1.final、finally、finalize

  这三个词字面相似,易混。

  final是java中的关键字,是一个可选修饰符,用来修饰类时,该类不可被继承;用来修饰属性时,该属性为常量,定义时要初始化;用来修饰方法时,该方法不能被重写。

  finally也是java中的关键字,出现在try或者try-catch语句块之后,用于包含一个无论try语句块中是否出现异常都会被执行的语句块。此外还应注意若try块被执行,则finally块必会执行,除非java虚拟机停止运行:当try块中有break/continue/return等中断代码代码的的语句时,也会先执行finally后中断。特别地,若try中的return后有返回值,则在return前执行的finally块中的内容(哪怕操作了这个返回值)将不会改变return的返回结果。

  finalize不是关键字,它是java中所有类的根类Object类的一个方法名。它的作用是让java虚拟机调用,以销毁对象。

 

2.throw、throws

  这两个都是java中的关键字,他们出现的地方很相近:离不开方法;他们的作用也相似:跟操作或处理异常相关。

  throw出现在方法体中(包括构造方法),用来抛出一个异常对象给java虚拟机。单纯的new 一个异常对象虚拟机并不会检测该异常,而throw就是将其抛出,使其能够被检测到。

  throws则是出现方法的声明部分(在方法名及其圆括号后、花括号前),他可以出现在构造方法声明中,也可以出现在抽象方法声明中。throws后面跟的是异常的类名,可以有多个,用逗号分隔。它是一种异常的“处理”方式(另一种为try-catch),即向上抛出异常给方法的调用者,对于本方法而言,如果有编译时异常(当然没有异常或者有运行是异常也可以),则用throws则可以显式地将异常抛出,这样异常在本方法没有被真正的处理,编译也不会报错。特别指出的是,thows后面跟的异常类名必须大于或等于方法中所有的异常,即异常本类类名或父类类名。还应该指出的是,方法的调用者必须真正地try-catch处理该异常或者使用throws继续向调用者抛出,否则编译报错。

 

3.ArrayList与Vector HashMap与Hashtable

  这两组是数据java集合框架中的概念,放在一起说是因为他们的辨析也比较相似。

  ArrayList与Vector都是线性集合,他们有着相同的API,相同的底层存放数据方式(数组),区别就在于后者相对于前者来说是线程安全的(线程安全性问题是指在多个线程同时操作一个资源时,造成的数据不同步问题)。

  相似地,HashMap与Hashtable都是映射集合,他们也有着相同的API,相同的底层存放数据方式(键-值对,键不重复),区别就在于后者相对于前者来说是线程安全的。

 

4.Serializable/transient

  这两个词是与对象的序列化有关的,放出来是因为他们相对来说用的比较少,容易忘记。

  Serializable 是一个接口,类通过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。可序列化类的所有子类型本身都是可序列化的。序列化接口没有方法或字段,仅用于标识可序列化的语义。这个单词常要求拼写。

  transient是一个关键字,它是一个属性的修饰符。用于声明某个属性的值不被序列化。

 

5.实例初始化块、静态初始化块的执行时机

  java中实例初始化块的语法很特别,只有一对花括号:{}。它还有一个兄弟叫做静态初始化块,是static关键字加一对花括号:static{}。它们都出现在类中,他们的花括号中可以书写任何符合语法的代码。

  实例初始化块执行时机与构造方法的执行步骤有关,构造方法执行有4个步骤:

  (1)划分空间

  (2)划分属性

  (3)属性初始化

  (4)构造方法中的其他语句

  而实例子初始化块就是在第(2)步和第(3)步之间执行。另外,划分空间之前会先调用父类的构造方法。

  静态初始化块static{} 则是在类加载期间执行,多次使用该类时,该类也只会加载一次,静态初始化块static{} 只会在类第一次被加载时执行一次。此处要指出,类的加载实在主方法(或者其他线程)中,第一次执行到该类所在的语句时(比如new了一个该类的对象),就会先加载,而在该类所在的语句执行之前不会加载。

  如果类ClassB继承了ClassA,那么以下是个代码块的执行顺序。

public class ClassA {

ClassA() {
System.out.println("A的构造方法");
}
{
System.out.println("A的实例");
}

static{
System.out.println("A的静态");
}

}

 

public class ClassB extends ClassA{
ClassB() {
System.out.println("B的构造方法");
}
{
System.out.println("B的实例");
}
static{
System.out.println("B的静态");
}
}

 

public class TestMain {
public static void main(String[] args) {
System.out.println("主方法开始");
System.out.println("这是一条测试语句");
new ClassA();
System.out.println("主方法结束");
}
}

  执行效果:

主方法开始
这是一条测试语句
A的静态
A的实例
A的构造方法
主方法结束

 

public class TestMain {
public static void main(String[] args) {
System.out.println("主方法开始");
System.out.println("这是一条测试语句");
new ClassB();
System.out.println("主方法结束");
}
}

  执行效果:

主方法开始
这是一条测试语句
A的静态
B的静态
A的实例
A的构造方法
B的实例
B的构造方法
主方法结束

 

public class TestMain {
public static void main(String[] args) {
System.out.println("主方法开始");
System.out.println("这是一条测试语句");
new ClassA();
new ClassB();
System.out.println("主方法结束");
}
}

  执行效果:

主方法开始
这是一条测试语句
A的静态
A的实例
A的构造方法
B的静态
A的实例
A的构造方法
B的实例
B的构造方法
主方法结束

 

public class TestMain {
public static void main(String[] args) {
System.out.println("主方法开始");
System.out.println("这是一条测试语句");
new ClassB();
new ClassA();
System.out.println("主方法结束");
}
}

  执行效果:

主方法开始
这是一条测试语句
A的静态
B的静态
A的实例
A的构造方法
B的实例
B的构造方法
A的实例
A的构造方法
主方法结束

 

  以上的测试很清晰的反映了构造方法、静态初始化块、实例初始化块的执行顺序 以及父类子类构造方法的调用顺序,有兴趣研究的朋友可以自己进行测试。

  代码放上来,篇幅有点长了,先写这么多吧,其他后续再补。