类初始化
涉及子类初始化时的执行顺序
顺序如下
①父类静态变量和静态代码块(按照声明顺序);
②子类静态变量和静态代码块(按照声明顺序);
③父类成员变量和代码块(按照声明顺序);
④父类构造器;
⑤子类成员变量和代码块(按照声明顺序);
⑥子类构造器。
以下源码来自牛客中的一道题
class A {
public A() {
System.out.println("class A");
}
{
System.out.println("I'm A class");
}
static {
System.out.println("class A static");
}
}
public class B extends A {
public B() {
System.out.println("class B");
}
{
System.out.println("I'm B class");
}
static {
System.out.println("class B static");
}
public static void main(String[] args) {
new B();
}
}
运行结果
class A static
class B static
I'm A class
class A
I'm B class
class B
在子类初始化时子类涉及重写父类
在分析上,如果父类即将调用的方法被子类重写了,那么父类就会调用子类重写后的方法,而不会调用父类的方法。
因此牢记,子类重写父类方法后,调用时会调用子类重写后的方法。
以下源码来自牛客中的一道题
public class Base
{
private String baseName = "base";
public Base()
{
callName();
}
public void callName()
{
System.out.println(baseName);
}
static class Sub extends Base
{
private String baseName = "sub";
public void callName()
{
System.out.println(baseName);
}
}
public static void main(String[] args)
{
Base b = new Sub();
}
}
运行结果
null
子类实现的方法中调用的baseName为子类中的私有属性。
以下源码来自牛客中的一道题
public class Demo {
class Super
{
int flag = 1;
Super() {
test();
}
void test() {
System.out.println("Super.test() flag=" + flag);
}
}
class Sub extends Super
{
Sub(int i) {
flag = i;
System.out.println("Sub.Sub()flag=" + flag);
}
void test() {
System.out.println("Sub.test()flag=" + flag);
}
}
public static void main(String[] args)
{
new Demo().new Sub(5);
}
}
运行结果
Sub.test()flag=1
Sub.Sub()flag=5
主动引用和被动引用
主动引用
JVM规范严格规定了有且只有5种情况必须对类进行“初始化”
- 当读或写入一个类的静态变量的时候;当用new实例化对象的时候;当调用一个类的静态方法的时候
- 当使用
java.lang.reflect
对类进行反射调用的时候,若类未初始化,则需要先触发其初始化 - 初始化一个类的时候,若发现其直接父类没有被初始化,则去初始化其直接父类。若直接父类向上还有父类未初始化,则继续向上初始化...
- 当JVM启动时,用户需要指定一个要执行的主类(就是包含main()方法的那个类),虚拟机会先初始化这个类
- 使用JDK1.7动态语言支持的时候的一些情况。
被动引用
被动引用:除了以上五种之外,其他的所有引用类的方式都不会触发初始化。
以下为被动引用的情况
通过子类引用父类的的静态变量,不会导致子类初始化
/**
* @author mmengyiyu
*/
public class NoInitialization
{
public static void main(String[] args)
{
System.out.println(SubClass.baseStaticIntVar);
}
}
class Base
{
static final int baseStaticIntVar = 1;
static
{
System.out.println("Base类的静态初始化块...");
}
}
class SubClass extends Base
{
static
{
System.out.println("SubClass类的静态初始化块...");
}
}
运行结果
1
通过数组定义来引用类,不会触发此类的初始化
/**
* @author mmengyiyu
*/
public class NoInitialization
{
public static void main(String[] args)
{
Base[] bases = new Base[5];
SubClass[] subClasses = new SubClass[5];
}
}
class Base
{
static
{
System.out.println("Base类的静态初始化块...");
}
}
class SubClass extends Base
{
static
{
System.out.println("SubClass类的静态初始化块...");
}
}
无运行结果
不会触发类的初始化,自然不可能调用static代码块。
常量在编译阶段会存入调用类的常量池中,本质上没有直接引用到定义常量的类,因此不会触发定义常量的类的初始化
/**
* @author mmengyiyu
*/
public class NoInitialization
{
public static void main(String[] args)
{
System.out.println(A.constantIntVar);
}
}
class A
{
static
{
System.out.println("静态初始化块...");
}
static final int constantIntVar = 100;
}
运行结果
100
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。