继承是为了避免重复的行为定义,但并不是所有出现重复行为的情境下都要使用继承来解决。如何正确判断使用继承的实际,及继承后如何活用多态,才是学习继承时的重点,也是我们看到优秀项目时需要思考的重点。
继承
1、private成员也可继承,但只可通过父类方法进行操作。
2、多态与is-a。多态就是,通过单一接口操作多种类型的对象。如下展示三两个代码:第一个编译不通过,第二个编译通过但执行报错。(Student、Teacher继承people)
People p1 = new Student();
Student s1 = p1;
Peopel p = new Teacher();
Student s = (Student)p;
3、abstract:这个解释蛮好记的,表示这个类有缺失,不可以直接被实例化。如果被继承,子类必须abstract或者实现父类的抽象方法。令注权限关键字与范围如下表,鉴于所有都可以在类内部获得,这里就不单独列出来了。(为了完美的三角形阵型)
关键字 | 相同包 | 不同包子类 | 不同包非子类 |
public | Y | Y | Y |
protected | Y | Y | N |
无修饰 | Y | N | N |
private | N | N | N |
如果继承,子类的修饰符的范围不可小于父类(粑粑同意做的事情儿子不同意,多没面子)。jdk5之前,如果子类继承方法返回类型是父类方法返回类型的子类,则报错,就是…嗯下面代码5之前会报错,5后就不会了。
public class Bird{
public Bird copy(){
return new Bird();
}
}
public class Chick extends Bird{
public Chick copy(){
return new Chick();
}
}
子类构造函数默认调用父类构造函数。之前说过,如果自定义带参构造函数,则系统不会自动为类构造无参函数。
4、子类如果想要调用父类的构造函数可以使用super,但this和super只能选择一个使用,且必须放在构造函数的第一行。
继承语法细节
1、所有类都是object的子类,子子类…。(默认继承)
2、继承中的final:如果修饰类,表示不可再被继承(String就是一个典型的final类,不可被继承);如果修饰方法,不可再被子类重新定义;如果修饰属性,则该属性需在定义时明确赋值或在其构造函数中明确赋值,且赋值后不会再改变
3、垃圾回收(GC):JVM自动识别无法通过变量参考的对象并回收,包括形成孤岛的对象们(互相调用,但未产生实际使用)。GC回收对象前调用对象的finalize()方法,但尽量避免使用,垃圾回收是java面试中常问到的一点,也内容也比较深,后面需要重点学习。
4、再看抽象类:抽象方法只能出现在抽象类中,书里用 模板方法模式(Template Method) 来展示抽象类的使用。如下例,当我们不确定使用什么方式获取猜测的数字,但是已经确定猜数的整个流程,就可以使用这种模式:
//抽象父类
public abstract class GuessGame {
//执行猜数过程
public void go(){
int secretNum = (int)(Math.random()*10);
int guess = 0;
do{
printPrompt();
guess = getGuessNum();
} while (guess != secretNum);
printSucessMessage(secretNum);
}
//通过不同的方式,获取输入猜测的数字
public abstract int getGuessNum();
//输出提示
public abstract void printPrompt();
//输出成功的提示消息
public abstract void printSucessMessage(int secretNum);
}
子类在继承父类的时候,必须实现抽象方法,在子类中确定通过什么方式获取猜测的数字。
public class ConsoleGame extends GuessGame{
private Scanner scanner = new Scanner(System.in);
@Override
public int getGuessNum() {
return scanner.nextInt();
}
@Override
public void printPrompt() {
System.out.println("请输入猜测的数字(1~10):");
}
@Override
public void printSucessMessage(int secretNum) {
System.out.println("猜对了,结果为:"+secretNum);
}
}
调用方式如下:
ConsoleGame consoleGame = new ConsoleGame();
consoleGame.go();
部分课后题:
解析:因为hashCode是Object的一个public方法,因为所有的类追究到其顶部,都是Object的子类,且子类继承父类的方法时权限关键字不得小于父类,所以这里会显示编译失败。
解析:若子类的构造函数不调用父类构造函数,默认调用父类的无参构造函数。而如果一个类已经有带参的构造函数,jvm不会再自动构建一个无参构造函数,需要编程人员指定无参构造函数。这种情况可以有两种解决办法:在Some中构造一个无参的构造函数;或者在Other构造函数的第一行通过“super(x)”来调用父类带参构造函数。