1.创建的类总是在继承
如果创建的类没有明确声明继承自哪个类,那么该类就会默认继承自Object
类。
2.用super可以调用从基类继承而来的方法
假如子类重写了父类的一个方法,而你又想调用父类的方法,就可以使用super
来调用。
3.初始化基类
Java仅有一种方法用来保证正确的初始化基类:在子类构造器中调用基类构造器。
注意:
- 子类的默认构造器也会默认调用基类的构造器,前提是基类的构造器是不带参的构造器。
- 如果基类的构造器是有参的,没有无参的,那么就必须显式的定义出子类的构造器并调用基类的带参构造。
- 基类在子类构造器可以访问它之前就已经完成了初始化,所以初始化的顺序是从基类再到派生类向外扩散。
- 因为Java没有多继承,所以在子类构造器中一定只使用到了一次super来调用基类构造器。
- 调用基类构造器一定是你在子类构造器中要做的第一件事。
- 应该在子类构造器中以
super(args)
的形式调用基类构造器,而不是以super.
的形式使用。
class Game {
Game(int i) {};
}
class BoardGame extends Game {
BoardGame(int i) {
super(i);
}
}
public class Chess extends BoardGame {
Chess(int i) {
super(11);
}
}
/*
* 由11行可知,因为Java没有多继承,就算出现了多层继承,也只需要一个super来调用上一层基类。
*/
4.Java在继承中的重载机制
假如基类中存在一个已被多次重载的方法,那么在子类中重新对其进行重载并不会屏蔽掉基类中的任何一个重载版本。
因此,在子类中对基类的同一个方法的任意重载版本进行重写是可行的,Java的重载机制非常自由。
5.@Override注解防止重载
@Override
注解表示重写方法,它可以防止你意外进行了重载,然后给出错误信息。
6.在组合与继承直接选择
组合是 has a 有一个的关系;
继承是 is a 是一个的关系。
7.使用继承的时机
其实在实际开发中继承并不常用,但你只要问一下自己:“我需要向上转型嘛?”如果需要,则可以使用继承。
8.final
final
使基本类型的值恒定不变。
final
使对象的引用恒定不变,但对象本身可以修改。(这一限制也适用于数组,数组也是对象)
class Value {
int i;
public Value(int i) {
this.i = i;
}
}
public class FinalData {
private static Random rand = new Random(47);
private string name;
public FinalData(String name) {
this.name = name;
}
//编译时常量:
private final int valueOne = 9;
private static final int VALUE_TWO = 99;
public static final int VALUE_THREE = 39;
//不是编译时常量:
//基本类型:
private final int i4 = rand.nextInt(20);
private static final int INT_5 = rand.nextInt(20);
//对象:
private Value v1 = new Value(11);
private final Value v2 = new Value(22);
private static final Value VAL_3 = new Value(33);
//数组:
private final int[] a = {1, 2, 3, 4, 5, 6};
public Sting toString() {
return name + ":" + "i4 = " + i4 + ", INT_5 = " + INT_5;
}
public static void main(String[] args) {
FinalData fd1 = new FinalData("fd1");
FinalData fd2 = new FinalData("fd2");
//fd1.valueOne++; //错误,被final修饰的基本类型数据无法修改
fd1.v1 = new Value(0); //正确,对象v1没有用final修饰,引用可以改变,指向新new出来的对象。
fd1.v2.i++; //正确,fonal只让对象v2的引用无法改变,但可以修改v2对象里的属性的值。
//fd1.v2 = new Value(0); //错误,final使对象v2的引用无法改变,也就是v2无法指向新new出来的对象。
//fd1.VAL_3 = new Value(0); //错误,final使对象VAL_3的引用无法改变,也就是VAL_3无法指向新new出来的对象。
for(int i =0; fd1.a.length; i++) {
fd1.a[i]++; //正确,final只让数组的引用不变,但可以修改数组的值。
}
fd1.a = new int[3]; //错误,final使数组的引用无法改变,数组a无法指向新new出来的数组。
System.out.println(fd1); //fd1: i4 = 15, INT_5 = 18
System.out.println(fd2); //fd2: i4 = 13, INT_5 = 18
}
}
9.final与static final
在上面的代码块中,可以看到:fd1
对象与fd2
对象的i4
的值是不同的,但INT_5
的值却是相同的,这就是final
与static final
的区别:
final
和static final
都能使基本类型的值恒定不变,不同的是:
- 当被修饰的成员是编译时常量时(如19、20行代码),二者没有本质区别,都是使值恒定不变;
- 当被修饰的成员是非编译时常量时(如25、26行代码),
i4
与INT_5
通过rand.nextInt()
实现了在运行时才得以确定数值。也就是说,当创建不同的对象时,有static
修饰的INT_5
,不管对象再多,它的值只有一个,是静态的final
,是共享的;而没有static
修饰的i4
,虽然它是final
的,但因为它不是编译时常量,所以这种“不变”的性质只存在于在运行时才得以确定的对象之中,每创建一个对象,这种非编译时常量就会因为没有static
修饰而在不同的对象中有所不同。
总结:
final
与static final
都能使值恒定不变,但如果被修饰的成员不是编译时常量,那么没有static
修饰的该成员在不同的对象中的值就会有所不同,但final
的这种“不变”的性质在每一个独立的对象内部都是成立的。
10.空白final(未给定初值的final)
空白final
成员可以相当于是非编译时常量,可以在创建不同对象的时候给予其不同的值。
public class BlankFinal {
private final j; //空白final成员
public BlankFinal() {
j = ;
}
public BlankFinal(int i) {
j = i;
}
public static void main(String[] args) {
BlankFinal b1 = new BlankFinal(); //j = 1
BlankFinal b2 = new BlankFinal(2); //j = 2
}
}
11.final参数
首先记住一点,当final
被用来修饰方法的参数时,不管该参数是基本类型也好,还是非基本类型也好,其作用都是使参数引用无法被改变。当传入方法的参数是final
的时,你只能“用”这个参数,而不能“修改”这个参数,因为如果你修改了这个参数,就变相地使参数引用去指向不同的参数对象。
void f(int i) {
i++; //可以
}
void f(final int i) {
//i++; //不可以
}
void f(final int i) {
System.out.println(i); //可以
}
12.final方法
使用final
方法的原因:使该方法无法被修改。也就是说,子类想要重写该方法,是不可奏效的。
13.pivate方法都是final方法
类中所有的private
方法都隐式的指定为是final
的。
13.你并不能在子类中重写private方法,就算写了,也是新的
由于private
方法都隐式的指定为是final
的,所以你并不能通过任何的途径去修改该方法,就算是在继承关系中,好像你在子类中“重写”了父类的private
方法,但事实上并没有:
- 被你“重写”的父类的
private
方法仍旧只有一个版本,只存在于父类。 - 而在子类中好像被你“重写”了的父类的
private
方法对于父类来说却是全新的东西,它只存在于子类中,二者并没有本质的联系。
总结:由于private
方法无法触及而且能有效隐藏,所以你只需要把它看成是它所归属的类的组织结构就行。
14.final类
当一个类被修饰为final
,就表明该类无法被继承,你不能重写该类的方法,因为final
类的方法都默认是final
方法。
15.大道理
如果将项目视作是一种有机的、进化着的生命体而去培养,而不是打算像盖摩天大楼一样快速见效,就会获得更多的成功和更迅速的回馈。继承与组合正是在面向对象程序设计中使得你可以执行这种实验的最基本的两个工具。
注:《Java编程思想》p147原话。