Java类的初始化、变量的初始化

时间:2022-08-12 17:30:13

本文简单演示Java类的初始化、变量的初始化顺序,扎实Java 基础,编写高效的code哦


知识点

  • Java常量,final 修饰,值被设定后不能再被修改
  • 静态变量里,static 修饰,顾名思义,无须创建对象,便可在内存中申请一个存储空间进行存储
  • 成员变量,也称实例变量,它随着当前对象的建立而建立,随着对象的销毁而销毁,存在于对象所在的堆内存中
  • 构造器,创建class对象时执行
  • 静态初始化块,执行优先级高于非静态的初始化块,它会在对象装载到 jvm的时候执行一次,执行完成便销毁,只能初始化 static 修饰的变量
  • 非静态初始化块,执行的时候如果有静态初始化块,先执行静态初始化块再执行非静态初始化块,在每个对象生成时都会被执行一次,它可以初始化类的实例变量。但它会在构造函数执行之前被执行。

类的初始化顺序

Java中类初始化顺序,依次是(静态变量、静态初始化块)>(变量、初始化块)>构造器。。

实例代码

父类 Human

package com.lazy.pojos;

import com.lazy.interfaces.IBehaviour;

/**
* Created by system on 16/9/10.
*/

public class Human /*extends Object*/ implements IBehaviour { //default super class Object
String TAG = "Human";
public static String sTAG = "static Human";
protected long identifier = System.currentTimeMillis();//身份标识
protected int age;
protected int height;
protected String name;

/* 静态初始化块 */
static {
System.out.println("Human" + " 静态初始化块");
}

/* 初始化块 (实例初始化器)*/ {
System.out.println(TAG + " 初始化块");
System.out.println(TAG + " " + identifier);
}

public Human() {
System.out.println(TAG + " 构造器");
}

@Override
public void eat() {

}
}

子类Woman

package com.lazy.pojos;

/**
* Created by system on 16/9/10.
*/

public class Woman extends Human {

}

子类Man

package com.lazy.pojos;

/**
* Created by system on 16/9/10.
*/

public class Man extends Human {
/* 静态变量 */
public static String staticVar = "staticVar";

/* 成员变量 */
public String field = "field";

String TAG = "Man";

/* 静态初始化块 */
static {
System.out.println("Man 静态初始化块");
System.out.println("Man staticVar =" + staticVar);
}

/* 初始化块 (类实例初始化器)*/ {
System.out.println(TAG + " 初始化块");
System.out.println(TAG + " field = " + field);
}

/* 构造器 */
public Man() {
// super(); //隐式
System.out.println(TAG + " 构造器");
}
}

测试类:FinalVarTest

public class InitializeOrderTest {

public static void main(String[] args) {
new Man();
new Woman();
}
}

执行结果:如图
Java类的初始化、变量的初始化

Java 类的初始化,可以分为单一对象的初始化,已经有继承关系的对象初始化,然而java 所有类的基类都是Object。有基类的先初始化基类,再初始化自身。Woman 类之所以能调用基类构造器,皆因为Java 编译器,class 文件中生成了一个默认构造器。如下图

Java类的初始化、变量的初始化

IntelliJ IDEA 能直接查看class 文件,而且调试功能更为强大

变量初始化顺序

实例代码

package com.lazy.pojos;

public class FinalVar {
String mg = "msg";

static String msg = "static msg";

final static String msg2 = "final static msg";

public static final String MSG = "MSG";

public static final String MSG2 = "MSG" + 2; //MSG2 = "MSG2"

public static final String MSG3 = "MSG" + "3"; // MSG3 = "MSG3"

public static final String MSG4 = msg + "4"; //MSG4; 只 declare no value

public static final String MSG5 = msg2 + "5"; //MSG5 = "final static msg5";

public static String MSG6 = msg2 + "6"; //MSG6; 只 declare no value

public final String MSG7 = msg2 + "7"; ///MSG7 = "final static msg7";

public final String MSG8 = mg + "8"; //MSG8; 只 declare no value

public static final int NUM = 100 * 10; //NUM =1000;

public int num = 100 * 10; //NUM; 只 declare no value

final String MG = "MG";

static {
System.out.println("static");
}
}

对于变量我们如何测试变量的初始化顺序呢
- 查看Class 反编译后的代码
- Debug 变量

class 文件

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.lazy.pojos;

public class FinalVar {
String mg = "msg";
static String msg = "static msg";
static final String msg2 = "final static msg";
public static final String MSG = "MSG";
public static final String MSG2 = "MSG2";
public static final String MSG3 = "MSG3";
public static final String MSG4;
public static final String MSG5 = "final static msg5";
public static String MSG6;
public final String MSG7 = "final static msg7";
public final String MSG8;
public static final int NUM = 1000;
public int num;
final String MG;

public FinalVar() {
this.MSG8 = this.mg + "8";
this.num = 1000;
this.MG = "MG";
}

static {
MSG4 = msg + "4";
MSG6 = "final static msg6";
System.out.println("static");
}
}

测试代码

public class FinalVarTest {

public static void main(String[] args) {
System.out.println(FinalVar.MSG);
// System.out.println(FinalVar.MSG4);
// new FinalVar();
}
}

由此可得:
1.static final 变量在编译时,编译器已经为之初始化值,static 变量要比成员变量优先初始化,仅初始化一次。——若代码中引用到 一个 static 变量,便会初始化整个类的static 变量,static final 变量则不会。
看代码
①如 static final 变量 MSG2 , 编译器自动为它 拼接成:MSG2。②再看 MSG4 ,它由 static 变量 msg + “4” ,msg 值不是final ,常量值,编译器只好先declare MSG4 这个变量,在 static 代码块中进行赋值; ③而 MSG5 变量的默认值为 “final static msg5”,④即 static final 变量在赋值时,值必须编译前就明确了. 若采用 static 变量进行赋值,—— 只能static 代码块中进行赋值。

2.对于 成员变量 MSG8 ,以下的声明的成员变量 如 num 打开class 文件看到都只在class 构造器中被赋值,经我测试只有成员变量赋值时引用了其它变量时,都会在构造器中被赋值。

如果你还不明白变量初始化顺序,可以在变量上打变量断点
Java类的初始化、变量的初始化

debug运行代码便会挂起

最后

对于 变量的初始化来说,情况比较多,还得多实践。如果想了解jvm 类加载的请阅读 [ JVM 类加载的那些事 ]

对于本文的字符串拼接其细节的请参考 [ 浅谈Java String内幕 ], [ 什么是字符串常量池? ] 两篇文章。