一、动手实验:继承条件下的构造方法调用
观察输出,注意总结父类与子类之间构造方法的调用关系修改Parent构造方法的代码,显式调用GrandParent的另一个构造函数,注意这句调用代码是否是第一句,影响重大!
结论:通过 super 调用基类构造方法,必须是子类构造方法中的第一个语句。
1 class Grandparent 2 { 3 public Grandparent() 4 { 5 System.out.println("GrandParent Created."); 6 7 } 8 public Grandparent(String string) 9 { 10 System.out.println("GrandParent Created.String:" + string); 11 12 } 13 } 14 class Parent extends Grandparent 15 { 16 public Parent() 17 { 18 //super("Hello.Grandparent."); 19 System.out.println("Parent Created"); 20 21 // super("Hello.Grandparent."); 22 } 23 } 24 class Child extends Parent 25 { 26 public Child() 27 { 28 29 System.out.println("Child Created"); 30 } 31 } 32 public class TestInherits 33 { 34 public static void main(String args[]) 35 { 36 Child c = new Child(); 37 38 } 39 }
结果截图:
当super("Hello.Grandparent")出现在子类构造方法非注释语句的第一行语句,否则报错。
思索:为什么子类的构造方法在运行之前,必须调用父类的构造方法?能不能反过来?为什么不能反过来?
因为在调用子类前会首先调用父类,即父类的构造函数会首先运行。
二、在子类中,若要调用父类中被覆盖的方法,可以使用super关键字。方法覆盖要求子类与父类的方法一模一样,否则就是方法重载(overload)。
class Monther { public Monther() { System.out.println("Monther created."); } public void display() { System.out.println("Monther is displayed"); } } class Child extends Monther { public Child() { System.out.println("Child created"); } public void display() { super.display(); System.out.println("Child displayed"); } } public class Same { public static void main(String []args) { Child c=new Child(); c.display(); } }
结果截图:
java覆盖的语法规则:
(1)覆盖方法的允许访问范围不能小于原方法。
(2)覆盖方法所抛出的异常不能比原方法更多。
(3)声明为final方法不允许覆盖。 例如,Object的getClass()方法不能覆盖。
(4)不能覆盖静态方法。
三、“类型转换”知识点考核
现在有三个类:
class Mammal{}
class Dog extends Mammal {}
class Cat extends Mammal{}
针对每个类定义三个变量并进行初始化
Mammal m=null ;
Dog d=new Dog();
Cat c=new Cat();
下列语句哪一个将引起编译错误?为什么?哪一个会引起运行时错误?为什么?
m=d; //true 子类赋值给父类
d=m; //false
d=(Dog)m; //true 已知m=d,故m为Dog型可以赋值给d而不能给c。
d=c; //false
c=(Cat)m;//false
四、在实践中理解把握复杂的知识
public class ParentChildTest { public static void main(String[] args) { Parent parent=new Parent(); parent.printValue(); Child child=new Child(); child.printValue(); parent=child; parent.printValue(); parent.myValue++;//调用的parent.myValue parent.printValue(); ((Child)parent).myValue++;//调用child.myValue parent.printValue(); } } class Parent{ public int myValue=100; public void printValue() { System.out.println("Parent.printValue(),myValue="+myValue); } } class Child extends Parent{ public int myValue=200; public void printValue() { System.out.println("Child.printValue(),myValue="+myValue); } }
1. 左边的程序运行结果是什么?你如何解释会得到这样的输出?
Parent.printValue(),myValue=100 //调用Parent
Child.printValue(),myValue=200 //调用Child
Child.printValue(),myValue=200 //调用child
Child.printValue(),myValue=200 //调用child
Child.printValue(),myValue=201 //调用child
2. 计算机是不会出错的,之所以得到这样的运行结果也是有原因的,那么从这些运行结果中,你能总结出Java的哪些语法特性?
1)当子类与父类拥有一样的方法,对象是子类型的,它就调用子类型的方法,是父类型的,它就调用父类型的方法。
2)如果子类与父类有相同的字段,则子类中的字段会代替或隐藏父类的字段,子类方法中访问的是子类中的字段(而不是父类中的字段)。如果子类方法确实想访问父类中被隐藏的同名字段,可以用super关键字来访问它。如果子类被当作父类使用,则通过子类访问的字段是父类的!若将子类赋值给父类,则通过父类引用的方法是子类的,而调用的变量是父类的。(子类对象赋值给父类对象时,重写了父类方法,则属性是父类的,方法是子类的)
五、.多态编程有两种主要形式:(1)继承多态(2)接口多态:使用接口代替抽象基类。
Java中“接口”的语法特性:
定义一个接口,采用关键字interface,实现一个接口,采用关键字implements
接口的成员函数自动成为public的,数据成员自动成为 static和final的。
如果接口不声明为public的,则自动变为package。
一个类可以同时实现多个接口。
接口类型 接口类型的变量=new 实现了接口的具体类型()
可以通过继承接口来扩充已有接口,并形成一个新的接口。
interface OneInterface {void f1();}interface TwoInterface extends OneInterface{void f2();}
实现子接口的类,必须实现“父”“子”接口所定义的所有方法,才能被实例化(即new出一个对象)。
public interface ArrayBound{ public static final int LOWBOUND=1;public static final int UPBOUND=100;}只要一个类声明实现了这个接口,就可以直接使用这些常量名。
在实际开发中,这种编程方式非常常见。
注意:定义在接口中的常量必须被初始化。
抽象类是一个不完全的类,而接口只是表明类应该具有哪些“外部”特征,不涉及任何实现细节。
接口基本上不具备继承的任何具体特点,它仅仅承诺了外界能够调用的方法。
一个类一次可以实现若干个接口,但一个类只能继承一个父类。
import static java.lang.System.out; public class Interface{ public static void main(String []args) { Shape shape=new Circle(5); out.println(shape.area()); } } interface Shape { double area(); } class Circle implements Shape { private double r; public Circle(double _r) { this.r=_r; } public double area() { return Math.PI*r*r; } }