静态代码块 构造代码块 构造函数 深入分析

柔光的暖阳◎ 2023-03-03 11:26 108阅读 0赞

文章目录

    • 静态代码块 构造代码块 构造函数 的执行顺序
      • 概念
        • 静态代码块
        • 构造代码块
        • 构造函数
        • 普通代码块
      • 执行顺序
        • 简单例子
        • 解释
        • 父子类之间的加载顺序(重要)
        • 解释
      • 参考链接

静态代码块 构造代码块 构造函数 的执行顺序

概念

静态代码块

  1. //在java类中(方法中不能存在静态代码块)使用static关键字和{}声明的代码块
  2. public class CodeBlock {
  3. static {
  4. System.out.println("静态代码块");
  5. }
  6. }
  • 执行时机
    静态代码块在类被加载的时候就运行了,而且只运行一次,并且优先于各种代码块以及构造函数。如果一个类中有多个静态代码块,会按照书写顺序依次执行。
  • 作用

    一般情况下,如果有些代码需要在项目启动的时候就执行,这时候就需要静态代码块。比如一个项目启动需要加载的很多配置文件等资源,我们就可以都放入静态代码块中。

构造代码块

  1. //在java类中使用{}声明的代码块(和静态代码块的区别是少了static关键字)
  2. public class CodeBlock {
  3. {
  4. System.out.println("构造代码块");
  5. }
  6. }
  • 执行时机

    构造代码块在创建对象时被调用,每次创建对象都会调用一次,但是优先于构造函数执行。

    这里对构造代码块跟构造函数做个测试:

    1. //Java源代码
    2. public class Opt_class {
    3. {
    4. System.out.println("构造代码块");
    5. }
    6. public Opt_class() {
    7. System.out.println("无参构造函数");
    8. }
    9. public Opt_class(String str) {
    10. System.out.println("有参构造函数" + str);
    11. }
    12. }
    13. //对生成的class文件进行反编译
    14. public class Opt_class {
    15. public Opt_class() {
    16. System.out.println("构造代码块");
    17. System.out.println("无参构造函数");
    18. }
    19. public Opt_class(String str) {
    20. System.out.println("构造代码块");
    21. System.out.println((new StringBuilder()).append("有参构造函数").append(str).toString());
    22. }
    23. }

    会发现,会将构造代码块加在每一个构造函数的函数体之前。

  • 作用

    和构造函数的作用类似,都能对对象进行初始化,并且只要创建一个对象,构造代码块都会执行一次。但是反过来,构造函数则不一定每个对象建立时都执行(多个构造函数情况下,建立对象时传入的参数不同则初始化使用对应的构造函数)。

    利用每次创建对象的时候都会提前调用一次构造代码块特性,我们可以做诸如统计创建对象的次数等功能。

构造函数

  1. public class CodeBlock {
  2. public CodeBlock(){
  3. System.out.println("构造函数");
  4. }
  5. }
  1. 构造函数的命名必须和类名完全相同。
  2. 构造函数的功能主要用于在类的对象创建时定义初始化的状态。它没有返回值,也不能用void来修饰。
  3. 构造函数不能被直接调用,必须通过new运算符在创建对象时才会自动调用;
  4. 当定义一个类的时候,通常情况下都会显示该类的构造函数,如果没有自定义构造函数,Java编译器会提供一个默认的无参构造函数.

普通代码块

  1. //普通代码块和构造代码块的区别是,构造代码块是在类中定义的,而普通代码块是在方法体中定义的。且普通代码块的执行顺序和书写顺序一致。
  2. public void sayHello(){
  3. {
  4. System.out.println("普通代码块");
  5. }
  6. }

执行顺序

简单例子

  1. public class CodeBlock {
  2. static {
  3. System.out.println("静态代码块");
  4. }
  5. {
  6. System.out.println("构造代码块");
  7. }
  8. public CodeBlock(){
  9. System.out.println("构造函数");
  10. }
  11. public void sayHello(){
  12. {
  13. System.out.println("普通代码块");
  14. }
  15. }
  16. public static void main(String[] args) {
  17. System.out.println("main方法————————————————");
  18. new CodeBlock().sayHello();
  19. System.out.println("---------------");
  20. new CodeBlock().sayHello();
  21. }
  22. }

output:

  1. 静态代码块
  2. main方法————————————————
  3. 构造代码块
  4. 构造函数
  5. 普通代码块
  6. ---------------
  7. 构造代码块
  8. 构造函数
  9. 普通代码块

解释

静态代码块先于主函数执行(这里不够准确,后文会写出我的看法),之后进入主函数,打印第一句,new出一个对象调用sayHello(),这里新建对象的时候,会依次调用构造代码块,构造函数,之后调用方法会进入普通代码块。

因此执行到这里的顺序为: 静态代码块,主函数第一句,构造代码块,构造函数,普通代码块。

之后又新建一个对象,调用sayHello(),所以也会依次调用构造代码块,构造函数,之后调用方法会进入普通代码块。这里不会再调用静态代码块,因为它在类加载的时候已经执行,且只执行一次,应该说它与类有关,与对象是没有关系的

父子类之间的加载顺序(重要)

  1. class SuperClass{
  2. static {
  3. System.out.println("父类静态代码块");
  4. }
  5. {
  6. System.out.println("父类构造代码块");
  7. }
  8. public SuperClass(){
  9. System.out.println("父类构造函数");
  10. }
  11. public void SayHello(){
  12. System.out.println("父类普通方法");
  13. }
  14. }
  15. class SubClass extends SuperClass{
  16. static {
  17. System.out.println("子类静态代码块");
  18. }
  19. {
  20. System.out.println("子类构造代码块");
  21. }
  22. public SubClass(){
  23. System.out.println("子类构造函数");
  24. }
  25. public void SayHello(){
  26. System.out.println("子类普通方法");
  27. }
  28. }
  29. public class exe_seq {
  30. static{
  31. System.out.println("测试主类的静态代码块优先于main函数执行");
  32. }
  33. public static void main(String[] args) {
  34. System.out.println("main方法------------------");
  35. new SubClass().SayHello();
  36. System.out.println("------------------------");
  37. new SubClass().SayHello();
  38. }
  39. }
  40. /* 测试主类的静态代码块优先于main函数执行 main方法------------------ 父类静态代码块 子类静态代码块 父类构造代码块 父类构造函数 子类构造代码块 子类构造函数 子类普通方法 ------------------------ 父类构造代码块 父类构造函数 子类构造代码块 子类构造函数 子类普通方法*/
  41. //执行顺序: 静态代码块内容先执行,接着执行父类构造代码块和构造方法,然后执行子类构造代码块和构造方法。

解释

  1. 这里关于static块在main函数之前执行,我说一下我的看法,不知是否正确。

    一个文件里只能有一个public类,但是main函数可以在public类中,也可以不在。main函数也算一个类中定义的一个静态方法吧,它作为函数的入口点,要被执行,应该是先加载一下main函数所在的类,然后再根据类去调用这个main方法。

  2. 这样就不难分析上述程序的执行结果,我在父类,子类和测试类中都定义了静态代码块,但是只有包含main函数的测试类先被加载,也就是只执行了测试类的静态代码块。进入main函数后,打印一句话,接着新建一个子类对象,调用它的一个sayHello()。
  3. 这里说明一下子类初始化的加载顺序

    父类静态代码块->子类静态代码块->父类构造代码块、父类构造函数->子类构造代码块、子类构造函数->子类的普通方法(先静态,后动态,先父类,后子类

参考链接

Java中静态代码块、构造代码块、构造函数、普通代码块

final、static、代码块、静态代码块、内部类、代码执行顺序

发表评论

表情:
评论列表 (有 0 条评论,108人围观)

还没有评论,来说两句吧...

相关阅读