课后作业3
一:使用自定义类
1 public class ClassAndObjectTest 2 { 3 4 public static void main(String[] args) 5 { 6 //创建类的实例,定义一个对象变量引用这一实例 7 MyClass obj = new MyClass(); 8 //通过对象变量调用类的公有方法 9 obj.myMethod("Hello"); 10 //给属性赋值 11 obj.setValue(100); 12 //输出属性的当前值 13 System.out.println(obj.getValue()); 14 //直接访问对象公有字段 15 obj.Information = "Information"; 16 //输出对象公有字段的当前值 17 System.out.println(obj.Information); 18 } 19 } 20 21 /** 22 * 自定义Java类的示例 23 */ 24 class MyClass 25 { 26 // 公有字段 27 public String Information = ""; 28 29 // 自定义公有Java实例方法 30 public void myMethod(String argu) 31 { 32 System.out.println(argu); 33 } 34 35 // 定义属性:私有字段+get方法+set方法 36 private int value; 37 public int getValue() 38 { 39 return value; 40 } 41 public void setValue(int value) 42 { 43 this.value = value; 44 } 45 46 }
运行结果:
小结:
需要定义一个对象变量 然后“创建(new)”一个对象,赋值给对象变量 现在就可以通过对象变量使用对象,主要方式有: (1)直接调用类的方法; (2)存取类的字段。
二、进行实验:
1 public class test{ 2 public static void main(String[] args){ 3 Foo obj1=new Foo(); 4 Foo obj2=new Foo(); 5 System.out.println(obj1==obj2); 6 } 7 8 } 9 class Foo{ 10 int value=100; 11 }
结果为:false
结论:
当“==”施加于原始数据类型变量时,是比较变量所保存的数据是否相等 当“==”施加于引用类型变量时,是比较这两个变量是否引用同一对象。 引用代表地址,所以“==”实际上相当于比较两个引用类型变量中保存的对象地址是否相同。
三、如何比较两个对象的“内容”是否一样?1 public class ObjectEquals 2 { 3 4 5 6 public static void main(String[] args) 7 { 8 9 MyTestClass obj1=new MyTestClass(100); 10 11 MyTestClass obj2=new MyTestClass(100); 12 System.out.println(obj1==obj2); 13 14 System.out.println(obj1.equals(obj2)); 15 16 } 17 18 19 } 20 21 class MyTestClass 22 23 { 24 25 public int Value; 26 27 //注意:只有参数类型为Object的,才是重写了Object的equals方法 28 29 //参数类型为MyTestClass的,仅仅是Overload了equals方法。 30 31 // @Override 32 33 // public boolean equals(Object obj) 34 35 // { 36 37 // return ((MyTestClass)obj).Value==this.Value; 38 39 // } 40 41 42 public boolean equals(MyTestClass obj) 43 44 { 45 46 return obj.Value==this.Value; 47 48 } 49 50 public MyTestClass(int initValue) 51 52 { 53 54 Value=initValue; 55 56 } 57 58 }
结果:
false
true
结论:两个对象的“内容”,其实是指它们在某一时刻的所有字段的值,“内容相等”,其实是就“对应字段值”一致。 在Java中要比对两个对象的字段值,可以 “重写(override)”基类的equals()方法
当你定义一个类时,如果不显式指明它的“父亲”类是哪个,则默认是Object。 Object是Java的最顶层基类,其中定义了equals( )方法
四、动手动脑:下面代码为何无法通过编译1 public class Test{ 2 public static void main(String[] args){ 3 Foo obj1=new Foo(); 4 } 5 } 6 class Foo{ 7 int value; 8 public Foo(int initValue){ 9 value=initValue; 10 } 11 }
结论:如果类提供了一个自定义的构造方法,将导致系统不再提供默认构造方法。
五、进行实验:如果一个类中既有初始化块,又有构造方法,同时还设定了字段的初始值,谁说了算?以下代码输出结果是什么?
1 public class Test { 2 public static void main(String[] args){ 3 InitializeBlockClass obj=new InitializeBlockClass(); 4 System.out.println(obj.field); 5 obj=new InitializeBlockClass(300); 6 System.out.println(obj.field); 7 } 8 } 9 class InitializeBlockClass{ 10 { 11 field=200; 12 } 13 public int field=100; 14 public InitializeBlockClass(int value){ 15 this.field=value; 16 } 17 public InitializeBlockClass(){} 18 }
结果:
100
300
结论:类字段的初始化顺序:1执行类成员定义时指定的默认值或类的初始化块,到底执行哪一个要看哪一个“排在前面”,排到后面的是最终执行的。 2执行类的构造函数
类的初始化块不接收任何的参数,而且只要一创建类的对象,它们就会被执行。因此,适合于封装那些“对象创建时必须执行的代码”
六、动手动脑:请运行TestStaticInitializeBlock.java示例,观察输出结果,总结出“静态初始化块的执行顺序”。1 class Root 2 { 3 static 4 { 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 { 19 System.out.println("Mid的静态初始化块"); 20 } 21 { 22 System.out.println("Mid的普通初始化块"); 23 } 24 public Mid() 25 { 26 System.out.println("Mid的无参数的构造器"); 27 } 28 public Mid(String msg) 29 { 30 //通过this调用同一类中重载的构造器 31 this(); 32 System.out.println("Mid的带参数构造器,其参数值:" + msg); 33 } 34 } 35 class Leaf extends Mid 36 { 37 static 38 { 39 System.out.println("Leaf的静态初始化块"); 40 } 41 { 42 System.out.println("Leaf的普通初始化块"); 43 } 44 public Leaf() 45 { 46 //通过super调用父类中有一个字符串参数的构造器 47 super("Java初始化顺序演示"); 48 System.out.println("执行Leaf的构造器"); 49 } 50 51 } 52 53 public class TestStaticInitializeBlock 54 { 55 public static void main(String[] args) 56 { 57 new Leaf(); 58 59 60 } 61 }
结果:
Root的静态初始化块
Mid的静态初始化块
Leaf的静态初始化块
Root的普通初始化块
Root的无参数的构造器
Mid的普通初始化块
Mid的无参数的构造器
Mid的带参数构造器,其参数值:Java初始化顺序演示
Leaf的普通初始化块
执行Leaf的构造器
结论:静态初始化的执行顺序:静态初始化块只执行一次。 创建子类型的对象时,也会导致父类型的静态初始化块的执行。
七、静态方法中只允许访问静态数据,那么,如何在静态方法中访问类的实例成员(即没有附加static关键字的字段或方法)?
解决方法:把静态方法的参数设置为类的实例,这样通过参数传递的方式就可以访问实例的成员了
1 public class Test 2 { 3 public static void main(String[] args) 4 { 5 Testgaga T=new Testgaga(); 6 Testgaga X=new Testgaga(); 7 T.show(X); 8 } 9 } 10 class Testgaga{ 11 int num=10; 12 static void show(Testgaga T){ 13 System.out.println(T.num); 14 } 15 }
结果:10
八、Integer的“诡异”特性”,上述神奇代码(StrangeIntegerBehavior.java)输出诡异的结果,原因何在?
1 public class StrangeIntegerBehavior 2 { 3 4 5 public static void main(String[] args) 6 { 7 8 9 Integer i1=100; 10 11 Integer j1=100; 12 13 System.out.println(i1==j1); 14 15 16 Integer i2=129; 17 18 Integer j2=129; 19 20 System.out.println(i2==j2); 21 22 } 23 24 25 }
结果:
true
false
结论:
当我们使用Integer类来包装整型数据时,Java会自动进行装箱操作,将整型数据封装为Integer对象。在这个过程中,对于较小的整数(范围在-128到127之间),Java会将它们缓存起来,以便提高性能和节省内存。
在你的代码中,i1和j1的值都是100,这个值在缓存范围内,因此当装箱操作发生时,Java会从缓存中返回同一个Integer对象。换句话说,i1和j1实际上引用的是同一个对象,因此它们比较相等,输出结果为true。
而对于i2和j2的值都是129,超出了缓存范围。当装箱操作发生时,Java会创建新的Integer对象来表示这个值。所以i2和j2实际上引用的是两个不同的对象,它们比较不相等,输出结果为false。
需要注意的是,这种缓存机制只适用于自动装箱,如果是使用new关键字手动创建Integer对象,不会应用缓存,即使数值相同,比较结果也会是false。