动手动脑 3 类与对象
一、构造函数的调用
原因&解法:
如果类没有定义构造函数,Java编译器在编译时会自动给它提供一个没有参数的“默认构造方法”。构造函数未定义时,调用Object父类中的默认构造,若用户自己定义时了有参构造函数,Object父类中的默认构造失效,所以需要用户自己写无参构造函数。所以上面的foo类需再定义空参构造,才可成功调用。
如下代码:
package 课件程序二; public class Test { public static void main(String[] args) { Foo obj1=new Foo(); } } class Foo { int value; public Foo(int initValue) { value=initValue; } public Foo() {}//用户需自己定义无参构造,因为用户自定义的实参构造,导致系统的默认构造失效 }
二、初始化执行顺序
代码如下:
public class InitializeBlockDemo { public static void main(String[] args) { InitializeBlockClass obj=new InitializeBlockClass(); System.out.println(obj.field); obj=new InitializeBlockClass(300); System.out.println(obj.field);//输出当前field的值 } } class InitializeBlockClass{ //下面这句在初始化块之前与之后,会影响到field字段的初始值 //public int field=100; { field=200; } public int field=100; public InitializeBlockClass(int value){ this.field=value; } public InitializeBlockClass(){ } }
执行结果:
过程分析:
在上述代码中,变量值以最终所存的值做最终值,虽然刚开始在执行体中给field赋值200,之后又重新赋值100,所以obj.field的第一次输出field为后者100,而obj重新初始化,开辟新空间,储存新的值,调用实参构造,改变以前field,将参数赋给field,所以obj.field的第二次输出为300。
静态初始化块的执行顺序:
1.静态初始化块只执行一次。
2.创建子类型的对象时,也会导致父类型的静态初始化块的执行。
总结:静态代码块内容先执行,接着执行父类非静态代码块和构造方法,然后执行子类非静态代码块和构造方法。注意***子类的构造方法,不管这个构造方法带不带参数,默认的它都会先去寻找父类的不带参数的构造方法。如果父类没有不带参数的构造方法,那么子类必须用supper关键子来调用父类带参数的构造方法,否则编译不能通过。
三、类的静态方法只能访问类的静态成员!
1.提出问题:
静态方法中只允许访问静态数据,那么,如何在静态方法中访问类的实例成员(即没有附加static关键字的字段或方法)?
2.解决方法:
只需创建该类的对象,再以对象名.变量名/方法名访问即可。
3.检验代码如下:
package 课件程序二; public class MyTestClass { int value=1; int set(int n) { value=n; return value; } static void display() { MyTestClass Test1=new MyTestClass();//实例化对象,利用对象.变量名/方法名访问 System.out.println("成员(字段)调用 "+Test1.value); System.out.println("成员(方法)调用 "+Test1.set(10)); } public static void main(String[] args) { display(); } }
结果截图:
四、Integer的特异性
java中变量有两种类型
1.基本类型(int,double等)
基本数据类类型存的是数值本身。
2.引用类型(赋值,new,类的创建等)
引用类型变量在内存放的是数据的引用。
基本类型通过==比较的是他们的值大小,而引用类型比较的是他们的引用地址。
如:
public class StrangeIntegerBehavior { public static void main(String[] args) { Integer i=-128; //Integer.valueOf(128); Integer j=-128; System.out.println(i==j); Integer i1=100; Integer j1=100; System.out.println(i1==j1); Integer i2=129; Integer j2=129; System.out.println(i2==j2); } }
结果截图:
当我们给一个Integer赋予一个int类型的时候会调用Integer的静态方法valueOf。
Integer i1= Integer.valueOf(-128);
Integer i2 = Integer.valueOf(200);
Integer i3 = Integer.valueOf(15);
Integer i4 = Integer.valueOf(127);
思考:那么Integer.valueOf()返回的Integer是不是都是重新new Integer(num);来创建的呢?如果是这样的话,那么== 比较返回都是false,因为他们引用的堆地址不一样。
Jdk中Integer.valueOf的源码
public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); }
在IntegerCache中cache数组初始化如下,默认存入了-128 - 127的值
cache = new Integer[(high - low) + 1];int j = low;for( int k = 0; k < cache.length ; k ++) cache[k] = new Integer(j ++);
Integer i1=100 的时候,会调用 Integer 的 valueOf 方法,这个方法就是返回一个 Integer 对象,只是在返回之前,看作了一个判断,判断当前i 的值是否在 [-128,127] 区别,且 IntegerCache 中是否存在此对象,如果存在,则直接返回引用,否则,创建一个新的对象。
从上面我们可以知道给Interger 赋予的int数值在-128 - 127的时候,直接从cache中获取,这些cache引用对Integer对象地址是不变的,但是不在这个范围内的数字,则new Integer(i) 这个地址是新的地址,不会相同。
上述代码分析
I2= j2分析
Integer i2 = 129; 自动调用Integer.valueOf(129) 返回一个Integer的对象。 这个对象存放到cache中的(上面一段代码分析)。 而 Integer i2= new Integer(129)这里创建了一个新的对象Integer ,同理j1的操作相同,两者开创空间不同,地址不同,所以 i2== j2 返回的是false
i1==j1 分析
Integer i1=100 的时候,会调用 Integer 的 valueOf 方法,在这里的话,因为程序初次运行,没有 59 ,所以,直接创建了一个新的对象,判断59在-128~127中,返回引用,i2与i1操作相同,返回相同,所以结果为true。
注意Integer 对象与int 比较时,比较的不是地址,而是值。 Integer 会自动拆箱成int ,然后进行值的比较。 Integer i3 =Integer.valueOf(59);时 因为 IntegerCache 中已经存在此对象,所以,直接返回引用。