Java 基础16 static 代码块介绍


1. 代码块基本介绍

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

但代码块又和方法不同, 没有方法名,没有返回值, 没有参数,只有方法体,而且不通过对象或类显式调用,而是加载类时,或创建对象时隐式调用。

  • 基本语法
[修饰符]{
    代码块
};
  • 注意事项
  1. 修饰符: 代码快如果使用修饰符那么只能写 static , 非必须
  2. 代码块可以分为两类, 即由static修饰的静态代码块与没有static修饰的 普通代码块
  3. 代码块中的逻辑语句可以为任意逻辑,输入、输出、方法调用, 循环、判断等
  4. 代码块结尾的分号 “ ; ” 可以省略

2. 代码块的好处与案例演示

代码块相当于另一种形式的构造器, 或者说是对构造器的补充, 可以用于初始化的操作

使用场景: 如果多个构造器中有重复的语句可以抽取到初始化快中,可以提高代码的复用性

  • 先看一个不使用代码块的逻辑
public class BlockCode01 {
    public static void main(String[] args) {
        Block01 b1 = new Block01("b1");
        Block01 b2 = new Block01("b1",12);
        Block01 b3 = new Block01("b1",12,'F');
    }
}

class Block01 {
    private String name;
    private int age;
    private char gander;

    public Block01(String name, int age, char gander) {
        System.out.println("处理数据逻辑1");
        System.out.println("处理数据逻辑2");
        System.out.println("处理数据逻辑3");
        System.out.println("构造器3 被调用");
        this.name = name;
        this.age = age;
        this.gander = gander;
    }

    public Block01(String name, int age) {
        System.out.println("处理数据逻辑1");
        System.out.println("处理数据逻辑2");
        System.out.println("处理数据逻辑3");
        System.out.println("构造器2 被调用");
        this.name = name;
        this.age = age;
    }

    public Block01(String name) {
        System.out.println("处理数据逻辑1");
        System.out.println("处理数据逻辑2");
        System.out.println("处理数据逻辑3");
        System.out.println("构造器1 被调用");
        this.name = name;
    }
}

如上面的代码逻辑, 每个构造器中都要进行一些数据处理, 这些数据处理逻辑是一样的, 就可以将其提取出来, 放进代码块中,

  • 将上面的逻辑使用代码块改写
public class BlockCode02 {
    public static void main(String[] args) {
        Block02 b1 = new Block02("b1");
        Block02 b2 = new Block02("b1",12);
        Block02 b3 = new Block02("b1",12,'F');
    }
}
class Block02 {
    private String name;
    private int age;
    private char gander;

//    下面三个代码都有相同的语句
//    这样代码看起来比较冗余
//    这时可以把相同的语句提取出来, 放到一个代码块中
//    这样不管调用哪个构造器创建对象, 都会先执行代码块中的逻辑
//    代码块的执行顺序优先于构造器
    {
        System.out.println("处理数据逻辑1");
        System.out.println("处理数据逻辑2");
        System.out.println("处理数据逻辑3");
    }
    public Block02(String name, int age, char gander) {
        System.out.println("构造器3 被调用");
        this.name = name;
        this.age = age;
        this.gander = gander;
    }

    public Block02(String name, int age) {
        System.out.println("构造器2 被调用");
        this.name = name;
        this.age = age;
    }

    public Block02(String name) {
        System.out.println("构造器1 被调用");
        this.name = name;
    }
}

3. 代码块的细节

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

  2. 类什么时候被加载?

    1. 创建对象时候 (new)
    2. 子类创建对象时, 父类也会被加载
    3. 使用对象的静态成员时,(静态属性,静态方法)
  3. 普通的代码在创建对象实例时,会被隐式调用, 被创建一次, 就会调用一次

  4. 如果只是实用类的静态成员,类的普通代码块并不会被执行

static 代码块是类加载时执行且只执行一次

普通代码块是在创建对象时调用的, 对象实例化一次就调用一次

4. 测试类什么时候被加载

public class BlockCode3_static_block_load {
    public static void main(String[] args) {
        // 1. 创建对象时候,类会被加载
//        B03 b = new B03();
//        B03 静态代码块被执行...

        // 2. 创建子类的时候, 子类与父类都会被加载, 先加载父类,再加载子类
//        A03 a = new A03();
//        B03 静态代码块被执行...
//        A03 静态代码块被执行...

        // 3. 调用静态成员时,类会被加载
//        System.out.println(B03.a);
//        B03 静态代码块被执行...
//        10

        // 4. 调用子类静态成员时, 子类与父类都会被加载, 先加载父类,再加载子类
        System.out.println(A03.a);
//        B03 静态代码块被执行...
//        A03 静态代码块被执行...
//        10
    }

}

class A03 extends B03{
    public static int a = 10;
    static{
        System.out.println("A03 静态代码块被执行...");
    }
}
class B03{
    public static int a = 10;
    static{
        System.out.println("B03 静态代码块被执行...");
    }
}

5. 普通代码块的调用

public class BlockCode04_block_load {
    public static void main(String[] args) {
        // 1. 创建对象时候,类会被加载
//        B04 b = new B04();
//        B04 静态代码块被执行...
//        B04 普通代码块被执行...
//        B04 构造方法被调用

        // 2. 创建子类的时候, 子类与父类都会被加载, 先加载父类,再加载子类
//        A04 a = new A04();
//        B04 静态代码块被执行...
//        A04 静态代码块被执行...
//        B04 普通代码块被执行...
//        B04 构造方法被调用
//        A04 普通代码块被执行...
//        A04 构造方法被调用

        // 3. 调用静态成员时,类会被加载, 只调用静态成员,类的普通代码块与构造方法不会被执行
//        System.out.println(B04.a);
//        B04 静态代码块被执行...
//        10

        // 4. 调用子类静态成员时, 子类与父类都会被加载, 先加载父类,再加载子类
        // 只调用静态成员,类的普通代码块与构造方法不会被执行
        System.out.println(A04.a);
//        B04 静态代码块被执行...
//        A04 静态代码块被执行...
//        10
    }

}


class A04 extends B04{
    public static int a = 10;
    static{
        System.out.println("A04 静态代码块被执行...");
    }
    {
        System.out.println("A04 普通代码块被执行...");
    }
    public A04() {
        System.out.println("A04 构造方法被调用");
    }
}
class B04{
    public static int a = 10;
    static{
        System.out.println("B04 静态代码块被执行...");
    }
    {
        System.out.println("B04 普通代码块被执行...");
    }
    public B04() {
        System.out.println("B04 构造方法被调用");
    }
}

6. 代码块的调用顺序

创建一个对象时, 在一个类的调用顺序是:

  1. 调用静态代码块和静态属性初始化,(静态代码块和静态属性初始化调用的优先级一样, 如果有多个静态代码块和静态变量初始化,则按照他们的定义顺序调用)
  2. 调用普通代码块和普通属性初始化,(普通代码块和普通属性初始化调用的优先级一样, 如果有多个普通代码块和普通变量初始化,则按照他们的定义顺序调用)
  3. 调用构造方法
public class BlockCode05 {
    public static void main(String[] args) {
        Block05 block05 = new Block05();
//        静态方法 getA 执行了
//        静态代码块执行了
//        普通方法 getB 执行了
//        普通代码块执行了
//        构造方法执行了
    }
}

class Block05{
    public static int a = getA();

    static {
        System.out.println("静态代码块执行了");
    }
    public static int getA(){
        System.out.println("静态方法 getA 执行了");
        return 10;
    }
    int b = getB();
    {
        System.out.println("普通代码块执行了");
    }

    public Block05() {
        System.out.println("构造方法执行了");
    }

    public int getB(){
        System.out.println("普通方法 getB 执行了");
        return 20;
    }
}

7. 关于 super

构造器的最前面其实隐含了, super() 和调用普通代码块, 新写一个演示,静态相关的代码块,属性初始化, 类加载时,就执行完毕,因此是优先于构造器和普通代码块执行的

public class BlockCode06 {
    public static void main(String[] args) {
        B06 b = new B06();
//        A06 普通代码块
//        A06 构造器被调用
//        B06 普通代码块
//        B06 构造器被调用
    }
}


class A06{
    {
        System.out.println("A06 普通代码块");
    }
    public A06() {
        // 这里隐藏了 super ()
        // 隐藏了普通代码块的调用
        System.out.println("A06 构造器被调用");
    }
}

class B06 extends A06{
    {
        System.out.println("B06 普通代码块");
    }
    public B06() {
        // 这里隐藏了 super ()
        // 隐藏了普通代码块的调用
        System.out.println("B06 构造器被调用");
    }
}

8. 继承中的代码块执行顺序

  1. 我们看一下创建一个子类对象时(继承关系)他们的静态代码块,静态属性初始化, 普通代码,普通属性初始化,构造方法的调用顺序:

  2. 父类的静态代码块和静态属性初始化(优先级一样,按定义顺序执行)

  3. 子类的静态代码块和静态属性初始化(优先级一样,按定义顺序执行)

  4. 父类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)

  5. 父类构造方法

  6. 子类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)

  7. 子类构造方法

  1. 静态代码块只能直接调用静态成员(静态代码块或静态属性),普通代码块则可以调用任意成员
public class BlockCode07 {
    public static void main(String[] args) {
        Son07 a = new Son07();
//        Father07 静态方法 getA 被调用
//        Father07 静态代码块被执行
//        Son07 静态方法 getA 被调用
//        Son07 静态代码块被执行
//        Son07 普通方法 getB 被调用
//        Father07 普通代码块被执行
//        Father07 构造方法被调用
//        Son07 普通方法 getB 被调用
//        Son07 普通代码块被执行
//        Son07 构造方法被调用
    }
}

class Son07 extends Father07 {
    public static int a = getA();

    static {
        System.out.println("Son07 静态代码块被执行");
    }
    public static int getA(){
        System.out.println("Son07 静态方法 getA 被调用");
        return 10;
    }
    public int b = getB();
    {
        System.out.println("Son07 普通代码块被执行");
    }
    public int getB(){
        System.out.println("Son07 普通方法 getB 被调用");
        return 20;
    }
    public Son07() {
        System.out.println("Son07 构造方法被调用");
    }
}

class Father07 {
    public static int a = getA();

    static {
        System.out.println("Father07 静态代码块被执行");
    }
    public static int getA(){
        System.out.println("Father07 静态方法 getA 被调用");
        return 10;
    }
    public int b = getB();
    {
        System.out.println("Father07 普通代码块被执行");
    }
    public int getB(){
        System.out.println("Father07 普通方法 getB 被调用");
        return 20;
    }
    public Father07() {
        System.out.println("Father07 构造方法被调用");
    }
}

9. 单例模式

什么是设计模式?

  1. 静态方法和属性的经典使用
  2. 设计模式是大量的实践中总结和理论之后优选的代码结构、编程风格、以及解决问题的思考方式。涉及模式就像是经典的棋谱, 不同的棋局我们用不同的棋谱,免去我们再思考和摸索。

10. 单例设计模式

所谓 类的单例设计模式,就是采用一定的方法保证在整个软件系统中, 对某个类只能存在一个对象实例,并且该类只提供一个获取该对象实例的方法

单例模式有两种方式: 1. 饿汉模式。2.懒汉模式

10.1 饿汉模式

饿汉模式步骤如下:

  1. 构造器初始化 =》 防止直接new
  2. 在类的内部创建对象
  3. 向外暴露一个静态的公共方法。getInstance
  4. 代码实现
public class BlockCode10_Singleton01 {
    public static void main(String[] args) {
        Singleton01 singleton01 = Singleton01.getInstance();
        Singleton01 singleton02 = Singleton01.getInstance();
        System.out.println(singleton01 == singleton02);
    }
}

class Singleton01{
    private String name;
    // 为了能在静态方法中访问 singleton01 需要将其设置为 static
    private static Singleton01 singleton01 = new Singleton01("小明");
    private Singleton01(String name) {
        this.name = name;
    }

    public static Singleton01 getInstance(){
        return singleton01;
    }
}

饿汉式, 直接就创建了, 只要类加载就创建了, 就算还没使用就已经创建了

10.2 懒汉式

public class BlockCode11_Singleton02 {
    public static void main(String[] args) {
        Singleton02 instance01 = Singleton02.getInstance();
        Singleton02 instance02 = Singleton02.getInstance();
        System.out.println(instance01 == instance02);

    }
}
class Singleton02{
    private String name;
    private static Singleton02  instange = null;
    private Singleton02(String name) {
        this.name = name;
    }
    public static Singleton02 getInstance(){
        if(null == instange){
            instange = new Singleton02("小明");
        }
        return instange;
    }
}

10.3 对比懒汉与饿汉模式

  1. 二者最主要的区别在于创建对象的时机不同,饿汉模式是在类加载的时候就创建了对象实例,懒汉模式是在使用对象时才会去创建
  2. 饿汉模式不存在线程安全问题, 懒汉模式存在线程安全问题
  3. 饿汉模式存在浪费资源的可能, 如果程序员一个对象实例都没有使用,那么饿汉模式的对象就浪费了,懒汉模式是使用时才创建,就不存在这个问题
  4. 我们在JavaSE标准类中 java.lang.Runtime 就是经典的单例模式

文章作者: hnbian
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 hnbian !
评论
 上一篇
Java 基础17 final 关键字介绍 Java 基础17 final 关键字介绍
1. Final介绍final 中文意思是最后的,最终的。 final可以修饰类、属性、方法和局部变量。 在某些情况下,程序员可能有一下需求,就会使用到final 2. final 修饰类使用 final 修饰类表示该类不能被继承。也就是说
2018-02-22
下一篇 
Java 基础 15 static 关键字介绍 Java 基础 15 static 关键字介绍
1. 介绍在Java中,static是一个关键字,用于修饰类、方法和变量。使用static关键字声明的成员属于类级别,不需要实例化对象就可以访问和使用。在类定义中使用static关键字声明的变量可以作为常量使用。 2. 为什么设计 stat
2018-02-16
  目录