Java核心技术卷梳理补充-类中字段初始化与类的加载顺序问题
默认字段初始化原则
如果写一个类时没有编写构造器,就会为你提供一个无参数构造器。这个构造器将所有的实例字段设置为默认值。于是,实例字段中的数值型数据设置为0
,布尔型数据设置为false
,所有对象变量将设置为null
。
示例类:
public class JavaInitTest {
private int id;
private String name;
private LocalDate constructTime;
}
使用无参构造器进行初始化
public JavaInitTest(){
id = 0;
name = "";
constructTime = null;
}
在类中进行显式字段初始化
public class JavaInitTest {
private int id = 0;
private String name = "";
private LocalDate constructTime = LocalDate.now();
}
使用字段初始化块(代码块)
在代码块中的内容将会在类实例化的时候先于构造器执行,并且使用类的静态字段时,此代码块并不会被执行
public class JavaInitTest {
private int id;
private String name;
private LocalDate constructTime;
{
id = 0;
name = "Kun's FeFan";
constructTime = LocalDate.now();
}
}
静态字段的三种初始化方式
显式字段初始化
public class JavaInitTestOfStatic {
public static int count = 0;
}
注意:Java中静态字段同样满足默认初始化原则
静态方法初始化
public class JavaInitTestOfStatic {
public static int count;
static void setStaticField(){
count = 233;
}
public static void main(String[] args){
JavaInitTestOfStatic.setStaticField();
System.out.println(JavaInitTestOfStatic.count);
}
}
也可以这样写:
package ClassVar;
public class JavaInitTestOfStatic {
public static int count = getVal();
public static int getVal(){
return 233;
}
public static void main(String[] args){
System.out.println(JavaInitTestOfStatic.count);
}
}
静态初始化块
在静态初始化代码块中的内容将会在类加载的时候被执行
public class JavaInitTestOfStatic {
public static int count;
static {
count = 233;
}
public static void main(String[] args){
System.out.println(JavaInitTestOfStatic.count);
}
}
Java中类会在哪些情况下被加载
调用类的静态方法时加载
package ClassVar;
public class MainClass {
public static int id;
static {
id = 233;
System.out.println("The field ID in the class is set to 233");
}
public static void main(String[] args) {
System.out.println("id = " + id);
}
}
类进行实例化时被加载
package ClassVar;
class MyTestClass{
public static int id;
static {
id = 233;
System.out.println("The field ID in the MyTestClass is set to 233");
}
}
public class MainClass {
public static void main(String[] args) {
MyTestClass test_obj = new MyTestClass();
System.out.println("id = " + test_obj.id);
}
}
父类在子类实例化时被加载
注意:父类会优先于子类加载
package ClassVar;
class MyTestClass{
public static int id;
static {
id = 233;
System.out.println("The field ID in the MyTestClass is set to 233");
}
}
class MySubClass extends MyTestClass{
static {
System.out.println("The class MySubClass has been loaded");
}
}
public class MainClass {
public static void main(String[] args) {
MyTestClass test_obj = new MySubClass();
System.out.println("id = " + test_obj.id);
}
}
创建对象时类中字段的加载顺序
- 首先被加载的是静态字段与静态初始化块,该两者处于同等地位,并且加载顺序取决于定义顺序
- 第二个被加载的是普通字段与普通初始化块,该两者也处于同等地位,并且加载顺序取决于定义顺序
- 其次被加载的是该类的构造器
package LoadTest;
class StaticContentLoad{
public static int num1 = getStaticVar();
static {
System.out.println("Static-Code-Block has been executed");
}
{
System.out.println("Normal-Code-Block has been executed");
}
public int num3 = getNormalVar();
public static int num2 = getStaticVar();
public static int getStaticVar(){
System.out.println("Method getStaticVar has been executed");
return 233;
}
public static int getNormalVar(){
System.out.println("Method getNormalVar has been executed");
return 2333;
}
public StaticContentLoad(){
System.out.println("Constructor has been executed");
}
}
public class EnterMain {
public static void main(String[] args) {
new StaticContentLoad();
}
}
得到以下运行结果:
Method getStaticVar has been executed
Static-Code-Block has been executed
Method getStaticVar has been executed
Normal-Code-Block has been executed
Method getNormalVar has been executed
Constructor has been executed
进程已结束,退出代码为 0
执行构造器前进行了哪些操作
从上一节的内容不难看出,在执行当前类的构造器中的逻辑前,实质上还进行了其他操作,已知Java中的所有类均隐式继承自Object超类,则实际上在构造器中的代码前面其实**隐式执行了父类的无参构造器(super)**和当前类的普通初始化块,如果有继承自的父类将会依次执行:
package ClassOrderTest;
class MyClassA{
public MyClassA(){
System.out.println("Constructor of MyClassA has been executed");
}
}
class MyClassB extends MyClassA{
{
System.out.println("Normal-Code-Block of MyClassB has been executed");
}
public MyClassB(){
System.out.println("Constructor of MyClassB has been executed");
}
}
public class EnterMain {
public static void main(String[] args) {
new MyClassB();
}
}
在进行继承的情况下的加载顺序
- 先加载父类的静态代码块与静态字段,然后加载子类的静态代码块与静态字段
- 第二步加载父类的普通字段与普通代码块
- 第三步执行父类的构造器
- 最后加载子类的普通字段与普通代码块,然后执行子类的构造器
package ClassOrderTest;
class MyClassA{
static {
System.out.println("Static-Code-Block of MyClassA has been executed");
}
{
System.out.println("Normal-Code-Block of MyClassA has been executed");
}
public MyClassA(){
System.out.println("Constructor of MyClassA has been executed");
}
}
class MyClassB extends MyClassA{
static {
System.out.println("Static-Code-Block of MyClassB has been executed");
}
{
System.out.println("Normal-Code-Block of MyClassB has been executed");
}
public MyClassB(){
System.out.println("Constructor of MyClassB has been executed");
}
}
public class EnterMain {
public static void main(String[] args) {
new MyClassB();
}
}
运行结果如下:
Static-Code-Block of MyClassA has been executed
Static-Code-Block of MyClassB has been executed
Normal-Code-Block of MyClassA has been executed
Constructor of MyClassA has been executed
Normal-Code-Block of MyClassB has been executed
Constructor of MyClassB has been executed
进程已结束,退出代码为 0
本文含有隐藏内容,请 开通VIP 后查看