1. 每一个非基本类型的对象都有一个toString()方法,而且当编译器需要一个String而你却只有一个对象时,该方法便会被调用。
2. 构造方法的过程是从基类“向外”扩散的,所以基类在导出类构造方法可以访问它之前,就已经完成了初始化。
调用父类构造方法必须是你在导出类构造方法中要做的第一件事。
3. 子类要想覆盖基类的方法,必须方法名,参数和返回值3个都一直才可以覆盖,否则编译不通过。
4. 由导出类转型成基类,在继承图上是向上移动的,因此一般称为向上转型。由于向上转型是从一个较专用类型向较通用类型转换,所有总是很安全的,也就是说,导出类是基类的一个超级。它可能比基类含有更多的方法,但它必须至少具备基类中的所含有的方法。在向上转型的过程中,类接口中唯一可能发生的事情是丢失方法,而不是获取它们。这就是为什么编译器在“未曾明确表示转型”或“未曾指定特殊标记”的情况下,仍然允许向上转型。
5. 根据上下文环境,Java的关键字final的含义存在着细微的区别,但通常它指的是”这是无法改变的。”不想做改变可能出于两种理由:设计或效率,由于这两个原因相差很远,所以关键字final有可能被误用。使用到final的三种情况:数据、方法和类。
在Java中,当以关键字final修饰时,必须对其进行赋值(或者在构造方法中赋值),否则编译不通过。必须在域的定义处或者每个构造器中用表达式对final进行赋值,这正是final域在使用前总是被初始化的原因所在。
一个既是static又是final的域只占据一段不能改变的存储空间。
当对对象引用而不是基本类型运用final时,其含义会有一点令人迷惑。对于基本类型,final使数值恒定不变;而用于对象引用,final使引用恒定不变。一旦引用被初始化指向一个对象,就无法再把它改为指向另一个对象。然而,对象其自身却是可以被修改的,Java并未提供使任何对象恒定不变的途径(但可以自己编写类以取得使对象恒定不变的效果)。这一限制同样适用数组,它也是对象。
我们不能因为某数据是final的就认为在编译时可以知道它的值。在运行时使用随机生成的数值来初始化就说明了这一点。
public class TestFinal {
private static Random rand = new Random(47);
private String id;
public TestFinal(String id) {
this.id = id;
}
private final int valueOne = 9;
private static final int VALUE_TWO = 99;
public static final int VALUE_THREE = 39;
public static int i4 = rand.nextInt(20);
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 String toString() {
return id + ":" + "i4 = " + i4 + ",INT_5 = " + INT_5;
}
public static void main(String[] args) {
TestFinal tf1 = new TestFinal("tf1");
// fd1.valueOne++; // Error: can't change value
tf1.v2.i++; // Object isn't constant !
tf1.v1 = new Value(9); // ok -- not final
for (int i = 0; i < tf1.a.length; i++) {
tf1.a[i]++; // Object isn't constant !
}
// tf1.v2 = new Value(0); // Error: can't;
// tf1.VAL_3 = new Value(1); // change reference.
// tf1.a = new int[3];
System.out.println(tf1);
System.out.println("Creating new TestFinal");
TestFinal tf2 = new TestFinal("tf2");
System.out.println(tf1);
System.out.println(tf2);
}
}
class Value {
int i;
public Value(int i) {
this.i = i;
}
}
也可以传递null当参数给方法:
public class TestFinalMethod {
void with(Gizmo g) {
g = new Gizmo();
System.out.println("创建好了!" + g.i);
}
public static void main(String[] args) {
TestFinalMethod tfm = new TestFinalMethod();
tfm.with(null);
}
}
class Gizmo {
int i = 4;
}
如果with的参数是final Gizmo g,那么就会在方法第一行报错了。
使用final方法的原因有两个:第一个原因是把方法锁定,以防止任何继承类修改它的含义。过去使用final的第二个原因就是效率。
类中所有的private方法都隐式地指定为是final的,由于无法取用private方法,所以也就无法覆盖它。可以对private方法添加final修饰词,但这并不能给该方法增加任何额外的意义。
“覆盖”只有在某方法是基类接口的一部分时才会出现,即,必须能将一个对象向上转型为它的基本类型并调用相同的方法。如果某方法为private,它就不是基类的接口的一部分,它仅是一些隐藏于类中的程序代码,只不过是具有相同名称而已。如果在导出类中以相同的名称生成一个public、pretected或宝访问权限方法的话,该方法就不会产生在基类中出现的“仅具有相同名称”的情况。此时你并没有覆盖该方法,仅是生成了一个新的方法。
final类的域可以根据个人的意愿选择是或不是final,不论类是否被定义为final,相同的规则都适用于定义为final的域,然而,由于final类禁止继承,所以final类中所有的方法都是隐式指定为是final的。
6. 初始化过程
父类静态方法(父类静态代码块)-->子类静态方法(子类静态代码快)-->父类成员(父类代码块)-->父类构造方法-->子类成员(子类代码块)-->子类构造方法