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) 对子类成员数据按照它们声明的顺序初始化,执行子类构造函数的其余部分。

目录
相关文章
|
2月前
|
存储 缓存 安全
除了变量,final还能修饰哪些Java元素
在Java中,final关键字不仅可以修饰变量,还可以用于修饰类、方法和参数。修饰类时,该类不能被继承;修饰方法时,方法不能被重写;修饰参数时,参数在方法体内不能被修改。
34 2
|
3月前
|
Java 编译器
java“变量 x 可能未被初始化”解决
在Java中,如果编译器检测到变量可能在使用前未被初始化,会报“变量 x 可能未被初始化”的错误。解决方法包括:1. 在声明变量时直接初始化;2. 确保所有可能的执行路径都能对变量进行初始化。
302 2
|
2月前
|
Java 编译器
Java重复定义变量详解
这段对话讨论了Java中变量作用域和重复定义的问题。学生提问为何不能重复定义变量导致编译错误,老师通过多个示例解释了编译器如何区分不同作用域内的变量,包括局部变量、成员变量和静态变量,并说明了使用`this`关键字和类名来区分变量的方法。最终,学生理解了编译器在逻辑层面检查变量定义的问题。
Java重复定义变量详解
|
2月前
|
Java 程序员 容器
Java中的变量和常量:数据的‘小盒子’和‘铁盒子’有啥不一样?
在Java中,变量是一个可以随时改变的数据容器,类似于一个可以反复打开的小盒子。定义变量时需指定数据类型和名称。例如:`int age = 25;` 表示定义一个整数类型的变量 `age`,初始值为25。 常量则是不可改变的数据容器,类似于一个锁死的铁盒子,定义时使用 `final` 关键字。例如:`final int MAX_SPEED = 120;` 表示定义一个名为 `MAX_SPEED` 的常量,值为120,且不能修改。 变量和常量的主要区别在于变量的数据可以随时修改,而常量的数据一旦确定就不能改变。常量主要用于防止意外修改、提高代码可读性和便于维护。
|
2月前
|
Java
Java 静态变量的初始化顺序
【10月更文挑战第15天】了解 Java 静态变量的初始化顺序对于正确编写和维护代码至关重要。通过深入理解初始化顺序的原理和细节,我们可以更好地避免潜在的问题,并提高代码的质量和可靠性。
|
3月前
|
Java
通过Java代码解释成员变量(实例变量)和局部变量的区别
本文通过一个Java示例,详细解释了成员变量(实例变量)和局部变量的区别。成员变量属于类的一部分,每个对象有独立的副本;局部变量则在方法或代码块内部声明,作用范围仅限于此。示例代码展示了如何在类中声明和使用这两种变量。
|
3月前
|
Java 编译器
【一步一步了解Java系列】:子类继承以及代码块的初始化
【一步一步了解Java系列】:子类继承以及代码块的初始化
146 3
|
4天前
|
监控 Java
java异步判断线程池所有任务是否执行完
通过上述步骤,您可以在Java中实现异步判断线程池所有任务是否执行完毕。这种方法使用了 `CompletionService`来监控任务的完成情况,并通过一个独立线程异步检查所有任务的执行状态。这种设计不仅简洁高效,还能确保在大量任务处理时程序的稳定性和可维护性。希望本文能为您的开发工作提供实用的指导和帮助。
41 17
|
14天前
|
Java
Java—多线程实现生产消费者
本文介绍了多线程实现生产消费者模式的三个版本。Version1包含四个类:`Producer`(生产者)、`Consumer`(消费者)、`Resource`(公共资源)和`TestMain`(测试类)。通过`synchronized`和`wait/notify`机制控制线程同步,但存在多个生产者或消费者时可能出现多次生产和消费的问题。 Version2将`if`改为`while`,解决了多次生产和消费的问题,但仍可能因`notify()`随机唤醒线程而导致死锁。因此,引入了`notifyAll()`来唤醒所有等待线程,但这会带来性能问题。
Java—多线程实现生产消费者
|
16天前
|
安全 Java Kotlin
Java多线程——synchronized、volatile 保障可见性
Java多线程中,`synchronized` 和 `volatile` 关键字用于保障可见性。`synchronized` 保证原子性、可见性和有序性,通过锁机制确保线程安全;`volatile` 仅保证可见性和有序性,不保证原子性。代码示例展示了如何使用 `synchronized` 和 `volatile` 解决主线程无法感知子线程修改共享变量的问题。总结:`volatile` 确保不同线程对共享变量操作的可见性,使一个线程修改后,其他线程能立即看到最新值。
下一篇
开通oss服务