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

目录
相关文章
|
存储 缓存 安全
除了变量,final还能修饰哪些Java元素
在Java中,final关键字不仅可以修饰变量,还可以用于修饰类、方法和参数。修饰类时,该类不能被继承;修饰方法时,方法不能被重写;修饰参数时,参数在方法体内不能被修改。
227 4
|
存储 Java
# 【Java全栈学习笔记-U1-day02】变量+数据类型+运算符
本篇笔记主要围绕Java全栈学习的第二天内容展开,涵盖了变量、数据类型、运算符以及Scanner类的应用。首先介绍了变量的概念与命名规范,以及如何定义和使用变量;接着详细讲解了Java中的基本数据类型,包括整型、浮点型、字符型、布尔型等,并通过实例演示了数据类型的运用。随后,深入探讨了各类运算符(赋值、算术、关系、逻辑)及其优先级,帮助理解表达式的构成。最后,介绍了如何利用Scanner类实现用户输入功能,并通过多个综合示例(如计算圆面积、购物打折、变量交换及银行利息计算)巩固所学知识。完成相关作业将进一步加深对这些基础概念的理解与实践能力。
241 13
|
存储 传感器 缓存
java变量与数据类型:整型、浮点型与字符类型
### Java数据类型全景表简介 本文详细介绍了Java的基本数据类型和引用数据类型,涵盖每种类型的存储空间、默认值、取值范围及使用场景。特别强调了`byte`、`int`、`long`、`float`、`double`等基本类型在不同应用场景中的选择与优化,如文件流处理、金融计算等。引用数据类型部分则解析了`String`、数组、类对象、接口和枚举的内存分配机制。
473 15
java构造方法,构造代码块,静态代码块的执行顺序
本文介绍了Java中构造方法、构造代码块和静态代码块的执行顺序。静态代码块用`static`声明,在JVM加载类时执行一次;构造代码块在每次创建对象时执行,先于构造方法;构造方法用于对象初始化,创建对象时调用。示例代码展示了这三者的输出顺序,并解释了它们的区别和应用场景。
386 1
|
Java Linux iOS开发
如何配置 Java 环境变量:设置 JAVA_HOME 和 PATH
本文详细介绍如何在Windows和Linux/macOS系统上配置Java环境变量。
18088 12
|
存储 Java C++
Java数组:静态初始化与动态初始化详解
本文介绍了Java中数组的定义、特点及初始化方式。
1137 12
|
存储 安全 Java
探索 Java 静态变量(static)的奥秘
本文深入探讨了Java中的静态变量(`static`),从初印象、使用场景、访问方式、初始化、线程安全、优缺点到最佳实践,全面解析其特性和应用场景。静态变量属于类而非实例,适用于共享数据、定义全局常量和工具类中的变量。它在类加载时初始化,生命周期贯穿整个程序运行。然而,多线程环境下需注意线程安全问题,可通过`synchronized`或原子类解决。优点包括共享数据方便和提高性能,但也存在线程安全和代码耦合度增高的缺点。最佳实践建议谨慎使用、保证线程安全、遵循命名规范并封装访问。掌握静态变量的正确用法,能让你的代码更加高效简洁。
955 11
java代码优化:判断内聚到实体对象中和构造上下文对象传递参数
通过两个常见的java后端实例场景探讨代码优化,代码不是优化出来的,而是设计出来的,我们永远不可能有专门的时间去做代码优化,优化和设计在平时
198 15
|
Java 编译器
Java重复定义变量详解
这段对话讨论了Java中变量作用域和重复定义的问题。学生提问为何不能重复定义变量导致编译错误,老师通过多个示例解释了编译器如何区分不同作用域内的变量,包括局部变量、成员变量和静态变量,并说明了使用`this`关键字和类名来区分变量的方法。最终,学生理解了编译器在逻辑层面检查变量定义的问题。
Java重复定义变量详解
|
Java 程序员 容器
Java中的变量和常量:数据的‘小盒子’和‘铁盒子’有啥不一样?
在Java中,变量是一个可以随时改变的数据容器,类似于一个可以反复打开的小盒子。定义变量时需指定数据类型和名称。例如:`int age = 25;` 表示定义一个整数类型的变量 `age`,初始值为25。 常量则是不可改变的数据容器,类似于一个锁死的铁盒子,定义时使用 `final` 关键字。例如:`final int MAX_SPEED = 120;` 表示定义一个名为 `MAX_SPEED` 的常量,值为120,且不能修改。 变量和常量的主要区别在于变量的数据可以随时修改,而常量的数据一旦确定就不能改变。常量主要用于防止意外修改、提高代码可读性和便于维护。
249 3