心得经验总结:浅谈Java类中的变量初始化顺序

简介: 心得经验总结:浅谈Java类中的变量初始化顺序

一、变量与构造器的初始化顺序

我们知道一个类中具有类变量、类方法和构造器(方法中的局部变量不讨论,他们是在方法调用时才被初始化),当我们初始化创建一个类对象时,其初始化的顺序为:先初始化类变量,再执行构造器方法。

代码验证:

public class Demo01 {

public int a1 = 1;

public String a2 = "initiaied!";

public Demo01() {

System.out.println(a1);

System.out.println(a2);

System.out.println("构造器方法被执行");

}

public static void main(String【】 args) {

Demo01 demo01 = new Demo01();

}//代码效果参考:http://www.ezhiqi.com/bx/art_6913.html

}

运行结果:

1

initiaied!

构造器方法被执行

可以看出,当我们创建一个Demo01对象时,先初始化了变量a1和a2,然后执行构造器方法。

二、静态变量与非静态变量的初始化顺序

静态变量是属于类本身,无论创建多少个对象,静态变量都只有一份存储区域,因此他会在类首次被访问或者首次创建类对象时被初始化,而且有且只能初始化一次。

而非静态变量是属于每个对象,他是在创建每个对象时都初始化一次。因此,静态变量要先于非静态进行初始化。例子:

public class Demo02 {

public Cup cup1 = new Cup(1);

public static Cup cup2 = new Cup(2);

public Demo02() {

System.out.println("Demo02构造器被执行!");

}

public static void main(String【】 args) {

Demo02 demo02 = new Demo02();

Demo02 demo02_01 = new Demo02();

}

}

class Cup {

public Cup(int i) {

System.out.println("Cup->" + i);

}

}

运行结果:

Cup->2

Cup->1

Demo02构造器被执行!

Cup->1

Demo02构造器被执行

当程序要执行main方法时,会先加载Demo02类文件,在加载时就会先初始化静态变量cup2,因此控制台输出"cup->2"。

类加载完后,开始执行main方法,创建demo02对象,这时就会初始化类中的非静态变量cup1,控制台输出"cup->1",然后执行构造器方法。创建第二个对象时,只初始化cup1,cup2为静态变量只初始化一次。

三、静态代码块与非静态代码块

静态代码块本质上就是一段静态变量的代码,其初始化和静态变量没有区别:当类首次被访问或者首次创建该类对象时被初始化,并且只初始化一次。

非静态代码块就是一段非静态变量的代码,他和非静态变量的初始化没有区别。

public class Demo03 {

static Table table1;

Table table2;

static {

table1 = new Table(1);

}

{

table2 = new Table(2);

}//代码效果参考:http://www.ezhiqi.com/zx/art_5018.html

public Demo03() {

System.out.println("Demo03构造器被执行");

}

public static void main(String【】 args) {

new Demo03();

}

}//代码效果参考:http://www.ezhiqi.com/bx/art_701.html

class Table {

public Table(int i) {

System.out.println("Table->" + i);

}

}

运行结果:

Table->1

Table->2

Demo03构造器被执行

四、父类与子类的初始化顺序

没有父类就没有子类,因此无论是类加载还是创建实例对象,父类都优先于子类进行初始化。

public class Demo04 extends Insect {

private int k = fun("Demo04中的k被初始化");

private static int x2 = fun("Demo04中的x2被初始化");

public Demo04() {

System.out.println("k=" + k);

System.out.println("j=" + j);

}

public static void main(String【】 args) {

Demo04 demo04 = new Demo04();

}

}

class Insect {

public int i = 9;

public int j;

public static int x1 = fun("Insect中的x1被初始化");

public Insect() {

System.out.println("i=" + i + ",j=" + j);

j = 39;

}

public static int fun(String s) {

System.out.println(s);

return 47;

}

}

运行结果:

Insect中的x1被初始化

Demo04中的x2被初始化

i=9,j=0

Demo04中的k被初始化

k=47

j=39

当执行main方法时,加载器开始加载Demo04类文件,在加载过程中,加载器会注意到他有一个父类Insect还没被加载,因此会先加载父类Insect文件。

类加载过程中会完成静态变量的初始化(此时并不执行构造器方法,构造器方法只有在创建对象时调用),Insect类加载完成后,接着加载Demo04类,都加载完成后,就开始执行main方法中的代码,创建Demo04对象。

由于继承关系,因此先创建父类Insect的实例对象,因此父类中的变量初始化和构造器先被执行,然后在初始化子类Demo04中的非静态变量和执行构造器方法。

五、总结

最后放一段代码,把前面所说情况都放在一起。

public class Son extends Father {

int m = fun("Son中的m 被初始化");

public Son() {

System.out.println("m = " + m);

System.out.println("j = " + j);

}

public static int x3 = fun("Son中的x3 被初始化");

public static void main(String【】 args) {

Son son = new Son();

}

}

class Father extends GrandFather {

public int k = fun("Father中的k被初始化");

public static int x2 = fun("Father中的x2被初始化");

public Father() {

System.out.println("k=" + k);

System.out.println("j=" + j);

}

}

class GrandFather {

public int i = 9;

public int j;

public static int x1 = fun("GrandFather中的x1被初始化");

public GrandFather() {

System.out.println("i=" + i + ",j=" + j);

j = 39;

}

public static int fun(String s) {

System.out.println(s);

return 47;

}

}

运行结果:

GrandFather中的x1被初始化

Father中的x2被初始化

Son中的x3 被初始化

i=9,j=0

Father中的k被初始化

k=47

j=39

Son中的m 被初始化

m = 47

j = 39

相关文章
|
2月前
|
存储 缓存 安全
除了变量,final还能修饰哪些Java元素
在Java中,final关键字不仅可以修饰变量,还可以用于修饰类、方法和参数。修饰类时,该类不能被继承;修饰方法时,方法不能被重写;修饰参数时,参数在方法体内不能被修改。
33 2
|
2月前
|
Java 开发者
在 Java 中,一个类可以实现多个接口吗?
这是 Java 面向对象编程的一个重要特性,它提供了极大的灵活性和扩展性。
160 57
|
12天前
|
JSON Java Apache
Java基础-常用API-Object类
继承是面向对象编程的重要特性,允许从已有类派生新类。Java采用单继承机制,默认所有类继承自Object类。Object类提供了多个常用方法,如`clone()`用于复制对象,`equals()`判断对象是否相等,`hashCode()`计算哈希码,`toString()`返回对象的字符串表示,`wait()`、`notify()`和`notifyAll()`用于线程同步,`finalize()`在对象被垃圾回收时调用。掌握这些方法有助于更好地理解和使用Java中的对象行为。
|
2月前
|
存储 缓存 安全
java 中操作字符串都有哪些类,它们之间有什么区别
Java中操作字符串的类主要有String、StringBuilder和StringBuffer。String是不可变的,每次操作都会生成新对象;StringBuilder和StringBuffer都是可变的,但StringBuilder是非线程安全的,而StringBuffer是线程安全的,因此性能略低。
65 8
|
2月前
|
Java 编译器
Java重复定义变量详解
这段对话讨论了Java中变量作用域和重复定义的问题。学生提问为何不能重复定义变量导致编译错误,老师通过多个示例解释了编译器如何区分不同作用域内的变量,包括局部变量、成员变量和静态变量,并说明了使用`this`关键字和类名来区分变量的方法。最终,学生理解了编译器在逻辑层面检查变量定义的问题。
Java重复定义变量详解
|
2月前
|
存储 安全 Java
java.util的Collections类
Collections 类位于 java.util 包下,提供了许多有用的对象和方法,来简化java中集合的创建、处理和多线程管理。掌握此类将非常有助于提升开发效率和维护代码的简洁性,同时对于程序的稳定性和安全性有大有帮助。
76 17
|
2月前
|
Java 程序员 容器
Java中的变量和常量:数据的‘小盒子’和‘铁盒子’有啥不一样?
在Java中,变量是一个可以随时改变的数据容器,类似于一个可以反复打开的小盒子。定义变量时需指定数据类型和名称。例如:`int age = 25;` 表示定义一个整数类型的变量 `age`,初始值为25。 常量则是不可改变的数据容器,类似于一个锁死的铁盒子,定义时使用 `final` 关键字。例如:`final int MAX_SPEED = 120;` 表示定义一个名为 `MAX_SPEED` 的常量,值为120,且不能修改。 变量和常量的主要区别在于变量的数据可以随时修改,而常量的数据一旦确定就不能改变。常量主要用于防止意外修改、提高代码可读性和便于维护。
|
2月前
|
安全 Java
Java多线程集合类
本文介绍了Java中线程安全的问题及解决方案。通过示例代码展示了使用`CopyOnWriteArrayList`、`CopyOnWriteArraySet`和`ConcurrentHashMap`来解决多线程环境下集合操作的线程安全问题。这些类通过不同的机制确保了线程安全,提高了并发性能。
|
2月前
|
存储 Java 程序员
Java基础的灵魂——Object类方法详解(社招面试不踩坑)
本文介绍了Java中`Object`类的几个重要方法,包括`toString`、`equals`、`hashCode`、`finalize`、`clone`、`getClass`、`notify`和`wait`。这些方法是面试中的常考点,掌握它们有助于理解Java对象的行为和实现多线程编程。作者通过具体示例和应用场景,详细解析了每个方法的作用和重写技巧,帮助读者更好地应对面试和技术开发。
137 4
|
2月前
|
Java 编译器 开发者
Java异常处理的最佳实践,涵盖理解异常类体系、选择合适的异常类型、提供详细异常信息、合理使用try-catch和finally语句、使用try-with-resources、记录异常信息等方面
本文探讨了Java异常处理的最佳实践,涵盖理解异常类体系、选择合适的异常类型、提供详细异常信息、合理使用try-catch和finally语句、使用try-with-resources、记录异常信息等方面,帮助开发者提高代码质量和程序的健壮性。
84 2