1.继承条件下构造方法调用
(1)例子
package practice1; class Grandparent { public Grandparent() { System.out.println("GrandParent Created."); } public Grandparent(String string) { System.out.println("GrandParent Created.String:" + string); } } class Parent extends Grandparent { public Parent() { // super("Hello.Grandparent."); System.out.println("Parent Created"); //super("Hello.Grandparent."); } } class Child extends Parent { public Child() { System.out.println("Child Created"); } } public class Test { public static void main(String args[]) { Child c = new Child(); } }
运行现在的代码运行结果:
当把第一句注释删掉后的运行结果:
当把第二句注释删掉后运行时报错
(2)结论:
-
如果一个类中没有定义构造器时,编译器会在编译时自动加上默认无参构造器
-
每个类都直接或间接时object的子类,并且object只有一个无参构造方法
-
编译器会在每个构造函数第一行隐式添加父类的默认无参构造函数,即super()
-
在子类的构造方法中调用其父类的构造方法。如果父类无构造方法或者含有无参构造方法时,那么子类的构造方法中会自动调用super()
-
如果父类有含参数的构造方法,那么在子类的构造方法中,必须用super()调用父类的某个构造方法,而且必须是在构造方法的第一个语句中进行调用
-
子类构造时必须通过某些方式首先调用父类的构造方法,否则会报错
2.输出类的奥秘
1 class A{} 2 public class Test 3 { 4 public static void main(String[] args) { 5 System.out.println(new A()); 6 } 7 }
解释:
-
public void println(Object x),这一方法内部调用了String类的valueOf方法。
-
valueOf方法内部又调用Object.toString方法:
public String toString() {
return getClass().getName() +"@" +
Integer.toHexString(hashCode());
}
- hashCode方法是本地方法,由JVM设计者实现:
public native int hashCode();
3.方法覆盖
要点:
-
方法覆盖要求子类与父类的方法一模一样
-
在子类中,若要调用父类中被覆盖的方法,可以使用super关键字
-
覆盖方法的允许访问范围不能小于原方法
-
覆盖方法所抛出的异常不能比原方法更多
-
声明为final方法不允许覆盖
-
不能覆盖静态方法
1 class Animal{ 2 public void display() { 3 System.out.println("我是一只动物"); 4 } 5 } 6 class Dog extends Animal{ 7 public void display() { 8 super.display(); 9 System.out.println("我是一只狗"); 10 } 11 } 12 class Cat extends Animal{ 13 public void display() { 14 System.out.println("我是一只猫"); 15 } 16 } 17 public class Test 18 { 19 public static void main(String[] args) { 20 Animal a=new Animal(); 21 a.display(); 22 Dog d=new Dog(); 23 d.display(); 24 Cat c=new Cat(); 25 c.display(); 26 } 27 }
4.多态
1 public class Test { 2 public static void main(String[] args) { 3 Parent parent=new Parent(); 4 parent.printValue(); 5 Child child=new Child(); 6 child.printValue(); 7 8 parent=child; 9 parent.printValue(); 10 11 parent.myValue++; 12 parent.printValue(); 13 14 ((Child)parent).myValue++; 15 parent.printValue(); 16 17 } 18 } 19 20 class Parent{ 21 public int myValue=100; 22 public void printValue() { 23 System.out.println("Parent.printValue(),myValue="+myValue); 24 } 25 } 26 class Child extends Parent{ 27 public int myValue=200; 28 public void printValue() { 29 System.out.println("Child.printValue(),myValue="+myValue); 30 } 31 }
将main()代码修改后:
1 public class Test { 2 public static void main(String[] args) { 3 Parent parent=new Parent(); 4 parent.printValue(); 5 Child child=new Child(); 6 child.printValue(); 7 8 parent.myValue++; 9 System.out.println(parent.myValue); 10 parent.printValue(); 11 12 parent=child; 13 parent.printValue(); 14 15 parent.myValue++; 16 System.out.println(parent.myValue); 17 parent.printValue(); 18 19 20 ((Child)parent).myValue++; 21 parent.printValue(); 22 23 } 24 }
我们不难发现,在parent=child; 之前,父类parent调用的是父类的变量和父类的方法,而让父类对象变量引用子类对象后,parent调用的是父类的变量和子类的方法。
结论:
-
当子类与父类拥有一样的方法,并且让一个父类变量引用一个子类对象时,到底调用哪个方法,由对象自己的“真实”类型所决定,这就是说:对象是子类型的,它就调用子类型的方法,是父类型的,它就调用父类型的方法。
-
如果子类与父类有相同的字段,则子类中的字段会代替或隐藏父类的字段,子类方法中访问的是子类中的字段(而不是父类中的字段)。如果子类方法确实想访问父类中被隐藏的同名字段,可以用super关键字来访问它。
-
如果子类被当作父类使用,则通过子类访问的字段是父类的