第一章:同步代码块

1. 什么是同步代码块??使用同步代码块的好处是什么??

1. 1 同步代码块

  • 代码块又称为初始化块,属于类中的成员[即是类的一部分],类似于方法,将逻辑语句分装在方法体中,通过{}包围起来。

  • 和方法不同,没有方法名,没有返回,没有参数,只有方法体,而且不通过对象或类显示调用,而是加载类时,或创建对象时隐形调用

  • 基本语法

     			[修饰符]{
     			代码
     			};
     注意:
     		1. 修饰符是可选的【可写可不写】,但要写的话只能写static
     		2. 代码块分为两类:
     				第一类:使用static修饰的叫做静态代码块
     				第二类:没有用static修饰的叫做普通代码块
     		3. 逻辑语句可以分为任何逻辑语句(如:输入,输出,方法调用,循环,判断等)
     		4.  ";"号可以写上也可以不写。
    

1.2 为什么要用同步代码块(好处)

优点:

  • 相当于另一种形式的构造器(对构造器的补充机制),可以做初始化的操作。
  • 应用场景:假设在类中多个构造器中有重复语句,可以抽取到初始化代码块中,提高代码的重用性。
  • 和设计模式中的"单例设计模型"有关(与static修饰符关联有奇效)。

注意:代码块与构造器的调用规则:代码块的执行是优先于构造器的。

使用代码块案例:

package 进阶.面向对象01.同步代码块;
/*
    为什么要用同步代码块??? 使用同步代码块的好处是什么??

    观察: 在类movie中的三个构造器中都有相同的语句,这样做比较啰嗦
    如何解决: 可以思考将相同的语句放入到一个代码块中即可。

    好处:使用同步代码块之后,不管调用哪个构造器,创建对象,都会先调用代码块中的内容

        注意:代码块的调用要优先于构造器
 */

public class CodeBlock01 {
    public static void main(String[] args) {
        Movie movie = new Movie("你好");
        System.out.println("=============");
        Movie movie2 = new Movie("唐探3", 100, "陈思诚");
    }
}
class Movie{
    private String name;
    private double price;//价格
    private String director;

    {
        System.out.println("电影屏幕打开...");
        System.out.println("广告开始...");
        System.out.println("电影正式开始...");
    }

    //构造器  3个构造器->>重载
    public Movie(String name){
//        System.out.println("电影屏幕打开...");
//        System.out.println("广告开始...");
//        System.out.println("电影正式开始...");
        System.out.println("Movie(String name) 被调用了。。。。");
        this.name = name;
    }

    public Movie(String name, double price) {
//        System.out.println("电影屏幕打开...");
//        System.out.println("广告开始...");
//        System.out.println("电影正式开始...");
        this.name = name;
        this.price = price;
    }

    public Movie(String name, double price, String director) {
//        System.out.println("电影屏幕打开...");
//        System.out.println("广告开始...");
//        System.out.println("电影正式开始...");
        System.out.println("Movie(String name, double price, String director) 被调用了。。。。");
        this.name = name;
        this.price = price;
        this.director = director;
    }
}

2. 普通代码块与静态代码块

  • 使用static修饰的叫做静态代码块
  • 没有用static修饰的叫做普通代码块

2.1 静态代码块

  1. static代码块也叫静态代码块,作用是对类进行初始化,而且他随着类的加载执行,且只会执行一次,如果是普通代码块,每创建一个对象就会执行一次。

  2. 类什么时候会被加载??
    1) 创建实例对象时(new)
    2) 创建子类对象实例,父类也会被加载
    3) 使用类的静态成员(静态属性,静态方法)

    注意:

    1. 在加载一个类的时候,会先加载与该类有继承关系的父类(从最上层开始加载),如:在加载子类时会先加载父类,在先加载父类时会先加载爷爷类,以此类推。
    2. 在一个类被加载时,该类的静态代码块一定会执行。
    3. static代码块在类加载时被执行,且只会执行一次。
  3. 普通代码块:只有在创建对象实例(构造器被执行)时,会被隐式调用,被创建一次就调用一次。

类被加载的案例:

package 进阶.面向对象01.同步代码块;

import jdk.swing.interop.SwingInterOpUtils;

public class CodeBlock02 {
    public static void main(String[] args) {
        //类被加载的情况举例
        //1. 当创建一个类的实例对象时,类被加载
//        AA aa = new AA();
        //2. 创建子类对象实例,父类会被加载,而且父类会被先加载,子类被后加载
//        AA aa2 = new AA();
        // 3. 使用类的静态成员时(静态属性,静态方法)类被加载
//        System.out.println(Cat.n1);
        
        //static在类加载时被执行,且只执行一次
        Cat cat1 = new Cat();
        Cat cat2 = new Cat();


    }
}
class BB{
    static {
        System.out.println("BB的静态代码块执行了......");
    }
}
class AA extends BB{
    static {
        System.out.println("AA的静态代码块执行了......");
    }
}
class Cat{
    public static int n1 = 999; //静态属性
    static {
        System.out.println("Cat的静态代码块执行了......");
    }
}

总结:
1. static代码块时是在类加载时执行,且只会执行一次。
2. 普通代码块实在创建对象时调用的(构造器执行),且每创建一个对象,执行一次。

2.2 创建一个对象时,类中代码的调用顺序。

  1. 调用静态代码块和静态属性初始化(注意:静态代码块和静态属性初始化调用的优先级一样,如果有多个静态代码块和多个静态变量初始化,则按照他们定义的顺序调用)
  2. 调用普通代码块和普通属性初始化(注意:普通代码块和普通属性初始化的调用的优先级一样,如果有多个普通代码块和多个普通属性初始化,则按定义顺序调用)
  3. 最后 调用构造器

实例:

package 进阶.面向对象01.同步代码块;

public class CodeBlock03 {
    public static void main(String[] args) {
        A a = new A();
    }
}
class A{
    {
        System.out.println("A 普通代码块执行了");
    }
    private int n2 = getN2();
    private static int n1 = getN1();
    public A(){
        System.out.println("空参构造被执行了");
    }

    static {
        System.out.println("A 静态代码块");
    }
    private static int getN1() {
        System.out.println("getN1被调用了");
        return 100;
    }
    private int getN2() {
        System.out.println("getN2被调用了");
        return 200;
    }
}

2.3 创建一个子类对象时,类中代码的调用顺序。

执行顺序如下

  1. 父类的静态代码块和静态属性
  2. 子类的静态代码块和静态属性
  3. 父类的普通代码块和普通属性初始化
  4. 父类的构造方法
  5. 子类的普通代码块和普通属性初始化
  6. 子类的构造方法

解释说明:假设有两个类A和B,其中A继承了B,创建A类对象。

  • 第一步:类加载
    1. 类加载时,会先加载父类,然后加载子类,故父类的静态代码块和静态属性先执行,然后子类的静态代码块和静态属性执行。
  • 第二步:创建对象,注意:重点构造器中默认隐藏两段代码,super和普通代码块的执行
    1. 创建对象时,会先去构造器中,由于构造器中隐藏了两个属性,super()和普通代码块,因此,在构造其中会先执行super(),调用super()时会去父类的构造器
    2. 在父类的构造其中也同时隐藏了两个属性super()和普通代码块,假设此时父类没有继承其他类,则执行父类的普通代码块,然后执行构造器中内容。
      3.父类构造器执行结束后,会执行子类的普通代码块,普通代码块执行完毕后才会执行构造器中内容。

实例:

package 进阶.面向对象01.同步代码块;

public class CodeBlock04 {
    public static void main(String[] args) {
        A01 a01 = new A01();
    }
}

class A02{
    private static int n1 = getVal01(); 
    public int m2 = getVal02();
    
    static {
        System.out.println("A02 的静态代码块");  // 2
    }
    
    {
        System.out.println("A02 的一个普通代码块");  //6
    }
    

    private static int getVal01() {
        System.out.println("A02 getVal01"); // 1
        return 10;
    }
    private  int getVal02() {
        System.out.println("A02 getVal02");  //5
        return 10;
    }
    public A02(){
        //super()
        //普通代码块
        System.out.println("A02的空参构造方法");  // 7
    }

}
class A01 extends A02{
    private static int n1 = getVal01();
    public int m2 = getVal02();

    static {
        System.out.println("A01 的静态代码块");  //4
    }

    {
        System.out.println("A01 的一个普通代码块");  //9
    }


    private static int getVal01() {
        System.out.println("A01 getVal01");  //3
        return 10;
    }
    private  int getVal02() {
        System.out.println("A01 getVal02");  //8
        return 10;
    }
    public A01(){
        //super()
        //普通代码块
        System.out.println("A01 的空参构造方法");  //10
    }
}

你可能感兴趣的:(JavaSE,java)