java笔记3(动手动脑)

1.以下代码为何无法通过编译?哪儿出错了?

原因:已有的Foo()是带一个int型参数的构造方法,不存在无参的构造方法Foo()

 

"构造方法"

当创建一个对象时,它的构造方法会被自动调用。构造方法与类名相同,没有返回值

,它的作用是对类进行初始化,

如果类没有定义构造函数,Java编译器在编译时会自动给它提供一个没有参数的“默认构造方法”

但是如果已经有了一个有参数的构造方法,,即重写了构造方法,那么原来的默认的无参构造方法会被重写的构造方法所覆盖
 
2.根据下列代码的输出结果,总结java字段初始化的规律
 1 /**
 2  * 
 3  */
 4 
 5 /**
 6  * @author 信1605-3 20163471        吴鑫
 7  *
 8  */
 9 class InitializeBlockClass {
10     {
11         field=200;
12     }
13     public int field=100;
14     public InitializeBlockClass(int value) {
15         // TODO 自动生成的构造函数存根
16         this.field=value;
17     }
18     public InitializeBlockClass(){
19     }
20     /**
21      * @param args
22      */
23     public static void main(String[] args) {
24         // TODO 自动生成的方法存根
25         InitializeBlockClass obj=new InitializeBlockClass();
26         System.out.println(obj.field);
27         
28         obj=new InitializeBlockClass(300);
29         System.out.println(obj.field);
30     }
31 }

 

结果如下:

对此结果的分析:

  1. 类开始的  {  field=200;}是类的初始化块,用大括号"{","}"直接包裹的,是做为类的成员,这种“没有名字”的成员大多是初始化的字段。

  2. 后面的  public int field=100;是类在定义的时候进行的初始化,函数到这里的field=100.

        3.所以在主函数中的System.out.println(obj.field);输出的filed=100.

        4.obj=new InitializeBlockClass(300); System.out.println(obj.field);是调用了构造函数,使用构造函数进行了初始化,赋值300,故输出的filed=300.

对规律进行的总结:

  1. 执行类成员定义时指定的默认值或类的初始化块,到底执行哪一个要看哪一个“排在前面”。

  2. 执行类的构造函数。

  3. 类的初始化块不接受任何的参数,而且只要一创建类的对象,他们就会被执行。因此,适合于封装那些“对象创建时必须执行的代码”。

3.请运行以下程序,观察输出结果,总结出“静态初始化块的执行顺序”。

 1 package 课堂3;
 2 class Root
 3 {
 4     static{
 5         System.out.println("Root的静态初始化块");
 6     }
 7     {
 8         System.out.println("Root的普通初始化块");
 9     }
10     public Root()
11     {
12         System.out.println("Root的无参数的构造器");
13     }
14 }
15 class Mid extends Root
16 {
17     static{
18         System.out.println("Mid的静态初始化块");
19     }
20     {
21         System.out.println("Mid的普通初始化块");
22     }
23     public Mid()
24     {
25         System.out.println("Mid的无参数的构造器");
26     }
27     public Mid(String msg)
28     {
29         //通过this调用同一类中重载的构造器
30         this();
31         System.out.println("Mid的带参数构造器,其参数值:" + msg);
32     }
33 }
34 class Leaf extends Mid
35 {
36     static{
37         System.out.println("Leaf的静态初始化块");
38     }
39     {
40         System.out.println("Leaf的普通初始化块");
41     }    
42     public Leaf()
43     {
44         //通过super调用父类中有一个字符串参数的构造器
45         super("Java初始化顺序演示");
46         System.out.println("执行Leaf的构造器");
47     }
48 
49 }
50 
51 public class TestStaticInitializeBlock
52 {
53     public static void main(String[] args) 
54     {
55         new Leaf();
56         
57 
58     }
59 }

結果:

 

静态初始化块的执行顺序:

1.静态初始化块只执行一次。

2.创建子类型的对象时,也会导致父类型的静态初始化块的执行。

父类的静态初始化块

子类的静态初始化块

父类的初始化块

父类的构造函数

子类的初始化块

子类的构造函数

 

4.一个有趣的问题

静态方法中只允许访问静态数据,那么,如何在静态方法中访问类的是实例成员(即没有附加static关键字的字段方法)?

 

 1 /**
 2  * 
 3  */
 4 
 5 /**
 6  * @author 信1605-3 20163471 吴鑫
 7  *
 8  */
 9 public class Test {
10 
11     /**
12      * @param args
13      */
14     int value1=1;//实例变量
15     static int value2=2;//类的静态变量
16     public static void print()//静态方法
17     {
18         System.out.println("实例变量value1="+new Test().value1);
19         //在静态方法中访问类的实例变量需进行类的实例化
20         System.out.println("静态变量value2="+value2);
21         //在静态方法中课直接访问类的静态变量
22     }
23     public static void main(String[] args) {
24         // TODO 自动生成的方法存根
25         Test test=new Test();
26         Test.print();
27         System.out.println("结果是:实例变量="+test.value1);
28         //访问实例成员
29     }
30 
31 }

 运行结果:

 

 

Integer类的装箱和拆箱到底是怎样实现的?

让我们先来了解一下装箱和拆箱

装箱就是  自动将基本数据类型转换为包装器类型;拆箱就是  自动将包装器类型转换为基本数据类型。

下表是基本数据类型对应的包装器类型:

 1 public class BoxAndUnbox {
 2 
 3 /**
 4 * @param args
 5 */
 6       public static void main(String[] args)
 7 
 8     {
 9       int value=100;
10 
11       Integer obj=value; //装箱
12 
13       int result=obj*2; //拆箱
14       System.out.println(result);
15 
16       System.out.println(obj); 
17 
18   }
19 
20 }

反编译class文件之后得到如下内容

 

由此可见 在装箱的时候自动调用的是Integer的valueOf(int)方法。而在拆箱的时候自动调用的是Integer的intValue方法。

因此可以用一句话总结装箱和拆箱的实现过程:

  装箱过程是通过调用包装器的valueOf方法实现的,而拆箱过程是通过调用包装器的 intValue方法实现的.

 5.一段神奇的代码

 1 public class MagicCode {
 2 
 3     /**
 4      * @param args
 5      */
 6     public static void main(String[] args) {
 7         // TODO 自动生成的方法存根
 8         Integer i1=100;
 9         Integer j1=100;
10         System.out.println(i1=j1);//ture
11         
12         Integer i2=129;
13         Integer j2=129;
14         System.out.println(i2=j2);//false
15     }
16 
17 }

以上是一段神奇的代码,乍一看,是两个true,然而一运行却是一个true一个false,这是为什么?
我们首先来编译一下

没毛病啊,再来反编译一下

 

我们可以看到使用的Integer方法的路径,打开JDK,找到它

 

 打开它,用eclipse整理一下,然后其中有一段如下所示

 1     /**
 2      * Cache to support the object identity semantics of autoboxing for values between
 3      * -128 and 127 (inclusive) as required by JLS.
 4      *
 5      * The cache is initialized on first usage.  The size of the cache
 6      * may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option.
 7      * During VM initialization, java.lang.Integer.IntegerCache.high property
 8      * may be set and saved in the private system properties in the
 9      * sun.misc.VM class.
10      */
11 
12     private static class IntegerCache {
13         static final int low = -128;
14         static final int high;
15         static final Integer cache[];
16 
17         static {
18             // high value may be configured by property
19             int h = 127;
20             String integerCacheHighPropValue =
21                     sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
22             if (integerCacheHighPropValue != null) {
23                 try {
24                     int i = parseInt(integerCacheHighPropValue);
25                     i = Math.max(i, 127);
26                     // Maximum array size is Integer.MAX_VALUE
27                     h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
28                 } catch( NumberFormatException nfe) {
29                     // If the property cannot be parsed into an int, ignore it.
30                 }
31             }
32             high = h;
33 
34             cache = new Integer[(high - low) + 1];
35             int j = low;
36             for(int k = 0; k < cache.length; k++)
37                 cache[k] = new Integer(j++);
38 
39             // range [-128, 127] must be interned (JLS7 5.1.7)
40             assert IntegerCache.high >= 127;
41         }
42 
43         private IntegerCache() {}
44     }
45 
46     /**
47      * Returns an {@code Integer} instance representing the specified
48      * {@code int} value.  If a new {@code Integer} instance is not
49      * required, this method should generally be used in preference to
50      * the constructor {@link #Integer(int)}, as this method is likely
51      * to yield significantly better space and time performance by
52      * caching frequently requested values.
53      *
54      * This method will always cache values in the range -128 to 127,
55      * inclusive, and may cache other values outside of this range.
56      *
57      * @param  i an {@code int} value.
58      * @return an {@code Integer} instance representing {@code i}.
59      * @since  1.5
60      */
61     public static Integer valueOf(int i) {
62         if (i >= IntegerCache.low && i <= IntegerCache.high)
63             return IntegerCache.cache[i + (-IntegerCache.low)];
64         return new Integer(i);
65     }
66 
67     /**
68      * The value of the {@code Integer}.
69      *
70      * @serial
71      */

在以上代码的12行我们可以看到IntegerCache类,它定义了一个[-128,127]的数组

在类加载时就将-128 到 127 的Integer对象创建了,并保存在cache数组中

然后我们再来看看61行的ValueOf方法,有段很明显的

if (i >= IntegerCache.low && i <= IntegerCache.high)

  return IntegerCache.cache[i + (-IntegerCache.low)];

return new Integer(i);

也就是说,如果取的值是在-128 到 127 之间,

就直接在cache缓存数组中去取Integer对象,

然而超出范围,就得return new Integer(i);

也就是说随机的一个数,

这个值反正是[-128,127]之间,肯定不会是129就对了

 

posted @ 2017-10-20 15:48  山巅一寺一壶酒  阅读(166)  评论(0编辑  收藏  举报