Java类初始化执行顺序介绍

本文详细解析Java类的初始化顺序,包括静态变量、静态初始化块、构造代码块等关键概念及其执行顺序。并通过示例代码展示了单类和继承情况下的初始化流程。

以前粗糙地看看了,面试问到了,没想起来,还是基础不行啊,还得认真总结,学知识体系要全面!!!

java类的初始化顺序

 转载来自http://blog.sina.com.cn/s/blog_4cc16fc50100bjjp.htmlhttps://blog.youkuaiyun.com/luoyoub/article/details/82874993

public class InitialOrderTest {
// 静态变量
    public static String staticField = "静态变量";
// 变量
    public String field = "变量";
// 静态初始化块
   static {
        System.out.println(staticField);
        System.out.println("静态初始化块");
    }
// 初始化块
    {
        System.out.println(field);
        System.out.println("初始化块");
    }
// 构造器
    public InitialOrderTest() {
        System.out.println("构造器");
    }
    public static void main(String[] args) {
        new InitialOrderTest();
    }
}

运行以上代码,我们会得到如下的输出结果:
1. 静态变量
2. 静态初始化块
3. 变量
4. 构造代码块
5. 构造器

下面我们再考虑继承的情况:

class Parent {
 // 静态变量
     public static String p_StaticField = "父类--静态变量";
 // 变量
     public String p_Field = "父类--变量";
     protected int i = 9;
     protected int j = 0;
 // 静态初始化块
     static {
          System.out.println(p_StaticField);
          System.out.println("父类--静态初始化块");
     }
 // 初始化块
     {
          System.out.println(p_Field);
          System.out.println("父类--初始化块");
     }
// 构造器
     public Parent() {
          System.out.println("父类--构造器");
          System.out.println("i=" + i + ", j=" + j);
          j = 20;
     }
}

public class SubClass extends Parent {
 // 静态变量
     public static String s_StaticField = "子类--静态变量";
 // 变量
     public String s_Field = "子类--变量";
 // 静态初始化块
     static {
          System.out.println(s_StaticField);
          System.out.println("子类--静态初始化块");
     }
 // 初始化块
     {
          System.out.println(s_Field);
          System.out.println("子类--初始化块");
     }
// 构造器
     public SubClass() {
          System.out.println("子类--构造器");
          System.out.println("i=" + i + ",j=" + j);
     }
// 程序入口
     public static void main(String[] args) {
          System.out.println("子类main方法");
          new SubClass();
     }
}

最终输出结果如下
运行一下上面的代码,结果马上呈现在我们的眼前:
父类--静态变量
父类--静态初始化块
子类--静态变量
子类--静态初始化块
子类main方法
父类--变量
父类--初始化块
父类--构造器
i=9, j=0
子类--变量
子类--初始化块
子类--构造器

i=9,j=20

执行顺序总结:

1.父类–静态变量/父类–静态初始化块(静态变量、静态初始化块顺序取决于它们在类中出现的先后顺序)
2.子类–静态变量/子类–静态初始化块(静态变量、静态初始化块顺序取决于它们在类中出现的先后顺序)

3.子类main方法执行(执行main方法之前会进行类的加载,同时会向上加载基类也就是父类,因此相关的静态变量以及静态初始化块静态方法被载入)

4.父类–变量/父类–初始化块(变量与初始化代码块顺序取决于它们在类中出现的先后顺序)
5.父类–构造器

6.子类–变量/子类–初始化块(变量与初始化代码块顺序取决于它们在类中出现的先后顺序)

7.子类–构造器
注意:

初始化块也叫构造代码块,每次new对象时都执行

静态代码块和静态方法的区别:一般情况下,如果有些代码必须在项目启动的时候就执行的时候,需要使用静态代码块,这种代码是主动执行的;需要在项目启动的时候就主动执行;静态方法在类加载的时候就已经加载 可以用类名直接调用,也可以使用对象调用,静态方法时在被调用的时候才会执行,属于被动执行, 比如main方法就必须是静态的 这是程序入口

两者的区别就是:静态代码块是自动执行的; 静态方法是被调用的时候才执行的.

 

双亲委派模型

https://blog.youkuaiyun.com/yusimiao/article/details/99301293https://www.jianshu.com/p/9df9d318e838暂时没看懂

双亲委派模式是在Java 1.2后引入的,其工作原理的是,如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行,如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器,如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模式

双亲委派模式优势

  • 采用双亲委派模式的是好处是Java类随着它的类加载器一起具备了一种带有优先级的层次关系,通过这种层级关可以避免类的重复加载,当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次。
  • 其次是考虑到安全因素,java核心api中定义类型不会被随意替换,假设通过网络传递一个名为java.lang.Integer的类,通过双亲委托模式传递到启动类加载器,而启动类加载器在核心Java API发现这个名字的类,发现该类已被加载,并不会重新加载网络传递的过来的java.lang.Integer,而直接返回已加载过的Integer.class,这样便可以防止核心API库被随意篡改
  • 可能你会想,如果我们在classpath路径下自定义一个名为java.lang.SingleInterge类(该类是胡编的)呢?该类并不存在java.lang中,经过双亲委托模式,传递到启动类加载器中,由于父类加载器路径下并没有该类,所以不会加载,将反向委托给子类加载器加载,最终会通过系统类加载器加载该类。但是这样做是不允许,因为java.lang是核心API包,需要访问权限,强制加载将会报出如下异常


 

代码块介绍

在Java中,使用{}大括号括起来的代码被称为代码块。

根据其位置和声明的不同,代码块可以分为:

  1. 局部代码块(普通代码块):控制变量的生命周期,提高内存利用率
  2. 构造代码块:可以给所有对象进行初始化
  3. 静态代码块:对静态属性、类进行初始化,并且只执行一次。
  4. 同步代码块(多线程讲解)。

构造代码块

在类中直接定义没有任何修饰符、前缀、后缀的代码块即为构造代码块。

特点:

       new一个对象的时候总是先执行构造代码,再执行构造函数。 

       有一点需要注意构造代码不是在构造函数之前运行的,它是依托构造函数执行的。

       正是由于构造代码块有这几个特性,所以它常用于如下场景:

 作用:

         可以给所有对象进行初始化

代码示例:

 
  1. public class demo1 {

  2. public static void main(String[] args) {

  3. Test test = new Test(3); //构造代码块会在构造函数被调用时执行, 且在这个例子中比"this.id=id;"语句先执行,作用是给对象统一初始化数据;

  4. System.out.println(test);

  5. }

  6. }

  7. class Test{

  8. int id;

  9. String name;

  10. {

  11. this.id= 5;

  12. this.name = "测试";

  13. System.out.println("这是构造代码块");

  14. }

  15. public Test(int id)

  16. {

  17. this.id = id;

  18. }

  19.  
  20. public String toString()

  21. {

  22. return "name: "+this.name +" , "+"id: "+ this.id ;

  23. }

  24. }

运行结果:

静态代码块

静态代码块就是用static修饰的用{}括起来的代码段

特点:

  1. 它是随着类的加载而执行,只执行一次,优先于构造函数
  2. 静态代码块其实就是给类初始化的,而构造代码块是给对象初始化的。
  3. 一个类中可以有多个静态代码块

作用:

         对静态属性进行初始化

注意:

          1.静态代码块不能存在于任何方法体内。

          2 .静态代码块不能直接访问静态实例变量和实例方法,需要通过类的实例对象来访问。

代码示例:

 
  1. public class demo01 {

  2. public static void main(String[] args) {

  3.  
  4. Test t=new Test();

  5. }

  6. }

  7. class Test{

  8. {

  9. System.out.println("构造代码块");

  10. }

  11. public Test(){

  12. System.out.println("我是构造方法");

  13. }

  14. static {

  15. System.out.println("我是静态代码块");

  16. }

  17. }

运行结果:

局部代码块

 位置在方法内部

作用:

         用于限定变量的生命周期,及早释放变量,提高内存利用率。

 
  1. public void show(){

  2. {

  3. System.out.println("局部代码块运行!");

  4. }

  5.  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值