Java - 静态代码块、静态变量、普通变量、构造代码块、构造函数以及 Java 类初始化顺序

简介: Java - 静态代码块、静态变量、普通变量、构造代码块、构造函数以及 Java 类初始化顺序


静态代码块:用staitc声明,jvm加载类时执行,仅执行一次

构造代码块:类中直接用{}定义,每一次创建对象时执行。

执行顺序优先级:静态块,main(),构造块,构造方法。

 

构造函数

publicHelloA(){ // 构造函数}

关于构造函数,以下几点要注意:

  1. 对象一建立,就会调用与之相应的构造函数,也就是说,不建立对象,构造函数时不会运行的。
  2. 构造函数的作用是用于给对象进行初始化。
  3. 一个对象建立,构造函数只运行一次,而一般方法可以被该对象调用多次。

 

构造代码块

{ // 构造代码块    }

关于构造代码块,以下几点要注意:

  1. 构造代码块的作用是给对象进行初始化。
  2. 对象一建立就运行构造代码块了,而且优先于构造函数执行。这里要强调一下,有对象建立,才会运行构造代码块,类不能调用构造代码块的,而且构造代码块与构造函数的执行顺序是前者先于后者执行
  3. 构造代码块与构造函数的区别是:构造代码块是给所有对象进行统一初始化,而构造函数是给对应的对象初始化,因为构造函数是可以多个的,运行哪个构造函数就会建立什么样的对象,但无论建立哪个对象,都会先执行相同的构造代码块。也就是说,构造代码块中定义的是不同对象共性的初始化内容。

静态代码块

static { // 静态代码块    }

关于静态代码块,要注意的是:

  1. 它是随着类的加载而执行,只执行一次,并优先于主函数。具体说,静态代码块是由类调用的。类调用时,先执行静态代码块,然后才执行主函数的。
  2. 静态代码块其实就是给类初始化的,而构造代码块是给对象初始化的
  3. 静态代码块中的变量是局部变量,与普通函数中的局部变量性质没有区别。
  4. 一个类中可以有多个静态代码块。
publicclassTest{
staitcintcnt=6;
static {
cnt+=9;
    }
publicstaticvoidmain(String[] args) {
System.out.println(cnt);
    }
static {
cnt/=3;
    }
}
运行结果:

Java类初始化顺序

对于一个类的情况

例子1

publicclassHelloA {
publicHelloA(){ // 构造函数System.out.println("A的构造函数");    
    }
    { // 构造代码块System.out.println("A的构造代码块");    
    }
static { // 静态代码块System.out.println("A的静态代码块");        
    }
publicstaticvoidmain(String[] args) {
    }
}
运行结果:A的静态代码块

例子2

publicclassHelloA {
publicHelloA(){ // 构造函数System.out.println("A的构造函数");    
    }
    { // 构造代码块System.out.println("A的构造代码块");    
    }
static { // 静态代码块System.out.println("A的静态代码块");        
    }
publicstaticvoidmain(String[] args) {
HelloAa=newHelloA();    
    }
}
运行结果:A的静态代码块A的构造代码块A的构造函数

例子3

publicclassHelloA {
publicHelloA(){ // 构造函数System.out.println("A的构造函数");    
    }
    { // 构造代码块System.out.println("A的构造代码块");    
    }
static { // 静态代码块System.out.println("A的静态代码块");        
    }
publicstaticvoidmain(String[] args) {
HelloAa=newHelloA();
HelloAb=newHelloA();
    }
}
运行结果:A的静态代码块A的构造代码块A的构造函数A的构造代码块A的构造函数

对于一个类而言,按照如下顺序执行:

  1. 执行静态代码块
  2. 执行构造代码块
  3. 执行构造函数

对于静态变量、静态初始化块、变量、初始化块、构造器,它们的初始化顺序依次是(静态变量、静态初始化块)>(变量、初始化块)>构造器。

例子4

publicclassInitialOrderTest {
/* 静态变量 */publicstaticStringstaticField="静态变量";
/* 变量 */publicStringfield="变量";
/* 静态初始化块 */static {
System.out.println( staticField );
System.out.println( "静态初始化块" );
    }
/* 初始化块 */    {
System.out.println( field );
System.out.println( "初始化块" );
    }
/* 构造器 */publicInitialOrderTest()
    {
System.out.println( "构造器" );
    }
publicstaticvoidmain( String[] args )
    {
newInitialOrderTest();
    }
}

运行以上代码,我们会得到如下的输出结果:

  1. 静态变量
  2. 静态初始化块
  3. 变量
  4. 初始化块
  5. 构造器

对于继承情况

publicclassHelloA {
publicHelloA(){ // 构造函数System.out.println("A的构造函数");    
    }
    { // 构造代码块System.out.println("A的构造代码块");    
    }
static { // 静态代码块System.out.println("A的静态代码块");        
    }
}
publicclassHelloBextendsHelloA{
publicHelloB(){ // 构造函数System.out.println("B的构造函数");    
    }
    { // 构造代码块System.out.println("B的构造代码块");    
    }
static { // 静态代码块System.out.println("B的静态代码块");        
    }
publicstaticvoidmain(String[] args) {
HelloBb=newHelloB();        
    }
}
运行结果:A的静态代码块B的静态代码块A的构造代码块A的构造函数B的构造代码块B的构造函数

当涉及到继承时,按照如下顺序执行:

  1. 执行父类的静态代码块,并初始化父类静态成员变量
  2. 执行子类的静态代码块,并初始化子类静态成员变量
  3. 执行父类的构造代码块,执行父类的构造函数,并初始化父类普通成员变量
  4. 执行子类的构造代码块, 执行子类的构造函数,并初始化子类普通成员变量

Java初始化顺序如图image.png例子6

classParent {
/* 静态变量 */publicstaticStringp_StaticField="父类--静态变量";
/* 变量 */publicStringp_Field="父类--变量";
protectedinti=9;
protectedintj=0;
/* 静态初始化块 */static {
System.out.println( p_StaticField );
System.out.println( "父类--静态初始化块" );
    }
/* 初始化块 */    {
System.out.println( p_Field );
System.out.println( "父类--初始化块" );
    }
/* 构造器 */publicParent()
    {
System.out.println( "父类--构造器" );
System.out.println( "i="+i+", j="+j );
j=20;
    }
}
publicclassSubClassextendsParent {
/* 静态变量 */publicstaticStrings_StaticField="子类--静态变量";
/* 变量 */publicStrings_Field="子类--变量";
/* 静态初始化块 */static {
System.out.println( s_StaticField );
System.out.println( "子类--静态初始化块" );
    }
/* 初始化块 */    {
System.out.println( s_Field );
System.out.println( "子类--初始化块" );
    }
/* 构造器 */publicSubClass()
    {
System.out.println( "子类--构造器" );
System.out.println( "i="+i+",j="+j );
    }
/* 程序入口 */publicstaticvoidmain( String[] args )
    {
System.out.println( "子类main方法" );
newSubClass();
    }
}
结果:父类--静态变量父类--静态初始化块子类--静态变量子类--静态初始化块子类main方法父类--变量父类--初始化块父类--构造器i=9,j=0子类--变量子类--初始化块子类--构造器i=9,j=20

子类的静态变量和静态初始化块的初始化是在父类的变量、初始化块和构造器初始化之前就完成了。静态变量、静态初始化块,变量、初始化块初始化了顺序取决于它们在类中出现的先后顺序。

 

分析

(1) 访问SubClass.main(),(这是一个static方法),于是装载器就会为你寻找已经编译的SubClass类的代码(也就是SubClass.class文件)。在装载的过程中,装载器注意到它有一个基类(也就是extends所要表示的意思),于是它再装载基类。不管你创不创建基类对象,这个过程总会发生。如果基类还有基类,那么第二个基类也会被装载,依此类推。

(2) 执行根基类的static初始化,然后是下一个派生类的static初始化,依此类推。这个顺序非常重要,因为派生类的“static初始化”有可能要依赖基类成员的正确初始化。

(3) 当所有必要的类都已经装载结束,开始执行main()方法体,并用new SubClass()创建对象。

(4) 类SubClass存在父类,则调用父类的构造函数,你可以使用super来指定调用哪个构造函数。基类的构造过程以及构造顺序,同派生类的相同。首先基类中各个变量按照字面顺序进行初始化,然后执行基类的构造函数的其余部分。

(5) 对子类成员数据按照它们声明的顺序初始化,执行子类构造函数的其余部分。

目录
相关文章
|
4月前
|
存储 缓存 安全
除了变量,final还能修饰哪些Java元素
在Java中,final关键字不仅可以修饰变量,还可以用于修饰类、方法和参数。修饰类时,该类不能被继承;修饰方法时,方法不能被重写;修饰参数时,参数在方法体内不能被修改。
54 3
|
17天前
|
存储 传感器 缓存
java变量与数据类型:整型、浮点型与字符类型
### Java数据类型全景表简介 本文详细介绍了Java的基本数据类型和引用数据类型,涵盖每种类型的存储空间、默认值、取值范围及使用场景。特别强调了`byte`、`int`、`long`、`float`、`double`等基本类型在不同应用场景中的选择与优化,如文件流处理、金融计算等。引用数据类型部分则解析了`String`、数组、类对象、接口和枚举的内存分配机制。
48 15
|
2月前
|
Java Linux iOS开发
如何配置 Java 环境变量:设置 JAVA_HOME 和 PATH
本文详细介绍如何在Windows和Linux/macOS系统上配置Java环境变量。
2101 12
|
2月前
|
存储 Java C++
Java数组:静态初始化与动态初始化详解
本文介绍了Java中数组的定义、特点及初始化方式。
130 12
|
2月前
|
存储 安全 Java
探索 Java 静态变量(static)的奥秘
本文深入探讨了Java中的静态变量(`static`),从初印象、使用场景、访问方式、初始化、线程安全、优缺点到最佳实践,全面解析其特性和应用场景。静态变量属于类而非实例,适用于共享数据、定义全局常量和工具类中的变量。它在类加载时初始化,生命周期贯穿整个程序运行。然而,多线程环境下需注意线程安全问题,可通过`synchronized`或原子类解决。优点包括共享数据方便和提高性能,但也存在线程安全和代码耦合度增高的缺点。最佳实践建议谨慎使用、保证线程安全、遵循命名规范并封装访问。掌握静态变量的正确用法,能让你的代码更加高效简洁。
101 11
|
4月前
|
Java 编译器
Java重复定义变量详解
这段对话讨论了Java中变量作用域和重复定义的问题。学生提问为何不能重复定义变量导致编译错误,老师通过多个示例解释了编译器如何区分不同作用域内的变量,包括局部变量、成员变量和静态变量,并说明了使用`this`关键字和类名来区分变量的方法。最终,学生理解了编译器在逻辑层面检查变量定义的问题。
Java重复定义变量详解
|
4月前
|
Java 程序员 容器
Java中的变量和常量:数据的‘小盒子’和‘铁盒子’有啥不一样?
在Java中,变量是一个可以随时改变的数据容器,类似于一个可以反复打开的小盒子。定义变量时需指定数据类型和名称。例如:`int age = 25;` 表示定义一个整数类型的变量 `age`,初始值为25。 常量则是不可改变的数据容器,类似于一个锁死的铁盒子,定义时使用 `final` 关键字。例如:`final int MAX_SPEED = 120;` 表示定义一个名为 `MAX_SPEED` 的常量,值为120,且不能修改。 变量和常量的主要区别在于变量的数据可以随时修改,而常量的数据一旦确定就不能改变。常量主要用于防止意外修改、提高代码可读性和便于维护。
|
4月前
|
Java
Java 静态变量的初始化顺序
【10月更文挑战第15天】了解 Java 静态变量的初始化顺序对于正确编写和维护代码至关重要。通过深入理解初始化顺序的原理和细节,我们可以更好地避免潜在的问题,并提高代码的质量和可靠性。
|
24天前
|
存储 监控 Java
【Java并发】【线程池】带你从0-1入门线程池
欢迎来到我的技术博客!我是一名热爱编程的开发者,梦想是编写高端CRUD应用。2025年我正在沉淀中,博客更新速度加快,期待与你一起成长。 线程池是一种复用线程资源的机制,通过预先创建一定数量的线程并管理其生命周期,避免频繁创建/销毁线程带来的性能开销。它解决了线程创建成本高、资源耗尽风险、响应速度慢和任务执行缺乏管理等问题。
156 60
【Java并发】【线程池】带你从0-1入门线程池