目录
一、类的继承关系
1、B属于A
继承在Java中是一种很重要的手段来实现代码复用,即已经编写过的成员以及方法无需再次编写,只需要继承实现过的那个类。而继承的类称为派生类或基类,被继承的类则称为父类或基类。二者的关系则是子类属于父类(派生类属于基类)。类比于零食与薯片的关系,薯片属于零食,是零食的具体划分,薯片继承于零食。即薯片是零食的子类,零食是薯片的父类。
一个父类可以有很多个子类,但一个子类只能有一个父类(除了Object类)。Object类是所有类的父类!但不影响这个类对于其他类的继承。
2、继承及继承的控制
子类继承父类的部分或全部成员及方法,并且可以编写属于自己的成员与方法。由前一句话加粗词可得出,继承也是可以控制的,继承可以使用权限修饰符来控制是否被继承。权限修饰符分为private、public、protect以及无修饰符四种。如下图所示:
编写一个基类BaseClass,然后派生出包内与包外两个子类,观察继承到的成员。
private int privateMem;
public int publicMem;
protected int protectedMem;
int noneMem;
包内:
在基类的包内派生出的派生类可以继承到除了private修饰外的所有的成员。
包外:
在包外继承到的子类继承了父类的protect以及public修饰的成员。
由上得知:protect和public修饰的成员及方法都可以被继承,而无修饰符则取决于在不在包内,private修饰的成员及方法无法被继承。
3、权限修饰符总结
public和private属于两个极端,0与1。public可以在任何情况下调用或继承,而private则是在任何情况下都不可以在其它类中被使用,实至名归的私有财产。
protect可以被包内和包外的子类所继承,也可以用父类的实例化对象所调用。
无修饰符则只能在包内使用。(无修饰符的成员与方法是为了打包后的jar文件不被外包中的类引用)
总结可得:1、只允许在包内使用的成员与方法无需使用修饰符
2、允许继承的成员与方法可以使用protect修饰
二、继承关系中的构造方法
1、构造方法的执行顺序
构造方案的执行顺序为从子类得到父类的无参构造,执行父类的无参构造之后再执行子类的构造方法。即先执行父类的无参构造方法再执行子类的构造方法。注意:这里父类所执行的构造方法不是由子类执行的构造方法决定,即并不是子类调用的是多参构造,父类就会执行多参构造,JVM规定无论子类调用的是何种构造方法,追溯到父类执行的构造方法必须是无参构造!如果父类没有无参构造的话继承则会出现问题,继承的子类无法执行构造方法。
2、关于super()
当追溯到子类的父类没有无参构造时无法执行子类的构造方法时,就会super()来执行父类的其他构造方法。super()与this()的意思比较类似,this()指代此类的构造方法,而super()指代父类的构造方法。super()的作用即在子类执行构造方法时想执行父类的其他构造方法。
要求:1、super()只能出现在构造方法中
2、出现在构造方法时只能出现在第一行
三、沿袭传统还是锐意改革——成员、方法的覆盖
1、成员的覆盖——super前缀
public class SubClassinPackage extends BaseClass {
private int subMem;
private int protectedMem;
public SubClassinPackage(int i) {
this.protectedMem = 20;
}
public void fun() {
System.out.println("this.protectedMem : " + this.protectedMem);
System.out.println("自己的this.protectedMem : " + this.protectedMem);
System.out.println("老爹的super.protectedMem : " + super.protectedMem);
super.someFun();
}
在子类中定义一个成员为protectedMem,这个成员将会覆盖掉继承得到的protectedMem,此时建立一个测试类调用fun()方法得到此时的this.protectedMem值为20,可得父类的protectedMem成员被覆盖掉,但是并不是删除,而是暂时被遮挡住了,当使用super.protectedMem时依旧可以得到父类的protectedMem值。
当这个子类被他的子类继承时由于本类中的protectedMem为private修饰的,所以父类中的protectedMem成员又得以出现。
2、方法的覆盖——主要的覆盖手段
首先需要对方法的覆盖进行语义与规则进行说明:
1、方法的覆盖只存在于有继承关系的类之间
2、子类方法名称、参数类型、参数个数以及返回值必须与父类一致
3、子类方法的修饰符不能低于父类
下面用几个类的例子来说明:
首先建立一个基类Animal
package com.mec.inheritance.core;
public class Animal {
protected String name;
public Animal() {
}
public Animal(String name) {
this.name = name;
}
public String cry() {
return this.name + "动物在叫!";
}
}
然后建立一个它的子类Cat:
package com.mec.inheritance.core;
public class Cat extends Animal {
protected String tybe;
public Cat() {
super("喵");
}
public Cat(String tybe) {
this();
this.tybe = tybe;
}
public void someMethodInCat() {
System.out.println("Cat类专有的方法!");
}
@Override
public String cry() {
return this.tybe + this.name + "在喵喵 !";
}
}
上图中对于Animal类的Cry方法进行了覆盖,得到子类独有的方法。
四、基类与派生类的关系
1、关于instanceof
instanceof是鉴别一个对象是否属于一个类,用法为对象 instanceof 类名称,如果属于返回true,否则返回false。
System.out.println("cat instanceof Cat :" + (cat instanceof Cat));
System.out.println("dog instanceof Animal :" + (dog instanceof Animal));
System.out.println("animal instanceof Dog :" + (animal instanceof Dog));
结果如上,而且可得派生类的实例化对象也属于基类!
2、基类与派生类之间的强制类型转换
public static void main(String[] args) {
Animal animal = new Animal("哺乳");
System.out.println(animal.cry());
Animal someAnimal = new Cat("野");
System.out.println(someAnimal.cry());
}
这个测试类将Cat类的对象强制转换为Animal类,然后调用这个对象的cry方法。结果如下:得到一个神奇的答案,执行的cry方法时Cat类的方法,而不是Animal类的。这个结果的原因是:在C语言中说过函数的名称本质是该函数的首地址常量,Cat对象的cry方法指向“喵喵”,类型转换并不能改变指向,只能约束可以执行的方法。如下:
someMethodInCat方法在Animal类中没有,所以不能引用,如果存在同名方法的话依旧是引用类型转换之前的方法。
综上可得,对象的类型只能约束对象所能引用的成员与方法,但是无法改变成员与方法的实际内容。对于方法,无法改变其实际指向的代码的首地址。