1. Java解析
动态分派
源码:
public class DynamicDispatch {
public static class Human {
void sayHello() {
System.out.println("human say hello!");
}
}
static class Man extends Human {
@Override
void sayHello() {
System.out.println("man say hello!");
}
}
static class Woman extends Human {
@Override
void sayHello() {
System.out.println("human say hello!");
}
}
public static void main(String[] args) {
Human human = new Human();
Human man = new Man();
Human woman = new Woman();
human.sayHello();
man.sayHello();
woman.sayHello();
}
}
分析:
通过javap -v DynamicDispatch.class 获取编译后的二进制文件:
总结:
不是根据静态类型来确定,而是在运行时根据实际类型来决定函数的版本。
静态分派
源码:
public class StaticDispatch {
static class Human {
}
static class Man extends Human {
}
static class Woman extends Human {
}
void sayHello(Human human) {
System.out.println("human say hello!");
}
void sayHello(Man man) {
System.out.println("man say hello!");
}
void sayHello(Woman woman) {
System.out.println("woman say hello!");
}
public static void main(String[] args) {
StaticDispatch staticDispatch = new StaticDispatch();
Human human = new Human();
Human man = new Man();
Human woman = new Woman();
staticDispatch.sayHello(human);
staticDispatch.sayHello(man);
staticDispatch.sayHello(woman);
}
}
分析:
通过javap -v StaticDispatch.class 获取编译后的二进制文件:
通过这段编译,上述的三个方法都初始化了,但是最后再调用的都是human的sayHello方法。
总结:由于静态分派是在类编译的时候确定类的属性。
编译器在重载时是通过参数的静态类型而不是实际类型作为判定依据的。并且静态类型是编译期可知的,因此,在编译阶段,Javac编译器会根据参数的静态类型决定使用哪个重载版本。
Java语言是一门静态多分派、 动态单分派的语言。
类的初始化
-
<clinit> 类的初始化。静态变量,静态块的初始化。所有的类变量初始化语句和类型的静态初始化器。
Java在编译之后会在字节码文件中生成<clinit>方法,称之为类构造器,类构造器同实例构造器一样,也会对静态语句块,静态变量进行初始化
-
<init> 对象的初始化
Java在编译之后会在字节码文件中生成<init>方法,称之为实例构造器。该实例构造器会对语句块,变量进行初始化,并调用父类的构造器。
<clinit>方法是在类加载过程中执行的,而<init>是在对象实例化执行的,所以<clinit>一定比<init>先执行。所以整个顺序就是:
-
父类静态变量初始化
-
父类静态语句块
-
子类静态变量初始化
-
子类静态语句块
-
父类变量初始化
-
父类语句块
-
父类构造函数
-
子类变量初始化
-
子类语句块
-
子类构造函数
JDK1.8 默认的垃圾回收器是Parallel Scavenge 和Parallel Old。
JDK1.9默认的是G1
开启参数:
-XX:+Use<垃圾回收器的名称>GC