1. 代码块基本介绍
代码块又称为初始化块, 属于类中的成员, 即为类的额一部分,类似于方法,将逻辑语句封装在方法体中, 通过 {} 包围起来。
但代码块又和方法不同, 没有方法名,没有返回值, 没有参数,只有方法体,而且不通过对象或类显式调用,而是加载类时,或创建对象时隐式调用。
- 修饰符: 代码快如果使用修饰符那么只能写 static , 非必须
- 代码块可以分为两类, 即由static修饰的静态代码块与没有static修饰的 普通代码块
- 代码块中的逻辑语句可以为任意逻辑,输入、输出、方法调用, 循环、判断等
- 代码块结尾的分号 “ ; ” 可以省略
2. 代码块的好处与案例演示
代码块相当于另一种形式的构造器, 或者说是对构造器的补充, 可以用于初始化的操作
使用场景: 如果多个构造器中有重复的语句可以抽取到初始化快中,可以提高代码的复用性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| 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; } }
|
如上面的代码逻辑, 每个构造器中都要进行一些数据处理, 这些数据处理逻辑是一样的, 就可以将其提取出来, 放进代码块中,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| 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. 代码块的细节
static 代码块也叫静态代码块,作用是对类进行初始化, 它随着类的加载而执行, 并且只会执行一次, 如果是普通代码块,每创建一个对象执行一次。
类什么时候被加载?
- 创建对象时候 (new)
- 子类创建对象时, 父类也会被加载
- 使用对象的静态成员时,(静态属性,静态方法)
普通的代码在创建对象实例时,会被隐式调用, 被创建一次, 就会调用一次
如果只是实用类的静态成员,类的普通代码块并不会被执行
static 代码块是类加载时执行且只执行一次
普通代码块是在创建对象时调用的, 对象实例化一次就调用一次
4. 测试类什么时候被加载
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| public class BlockCode3_static_block_load { public static void main(String[] args) {
System.out.println(A03.a);
}
}
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. 普通代码块的调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| public class BlockCode04_block_load { public static void main(String[] args) {
System.out.println(A04.a);
}
}
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 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| public class BlockCode05 { public static void main(String[] args) { Block05 block05 = new Block05();
} }
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() 和调用普通代码块, 新写一个演示,静态相关的代码块,属性初始化, 类加载时,就执行完毕,因此是优先于构造器和普通代码块执行的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| public class BlockCode06 { public static void main(String[] args) { B06 b = new B06();
} }
class A06{ { System.out.println("A06 普通代码块"); } public A06() { System.out.println("A06 构造器被调用"); } }
class B06 extends A06{ { System.out.println("B06 普通代码块"); } public B06() { System.out.println("B06 构造器被调用"); } }
|
8. 继承中的代码块执行顺序
我们看一下创建一个子类对象时(继承关系)他们的静态代码块,静态属性初始化, 普通代码,普通属性初始化,构造方法的调用顺序:
父类的静态代码块和静态属性初始化(优先级一样,按定义顺序执行)
子类的静态代码块和静态属性初始化(优先级一样,按定义顺序执行)
父类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
父类构造方法
子类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
子类构造方法
静态代码块只能直接调用静态成员(静态代码块或静态属性),普通代码块则可以调用任意成员
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| public class BlockCode07 { public static void main(String[] args) { Son07 a = new 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. 单例模式
什么是设计模式?
- 静态方法和属性的经典使用
- 设计模式是大量的实践中总结和理论之后优选的代码结构、编程风格、以及解决问题的思考方式。涉及模式就像是经典的棋谱, 不同的棋局我们用不同的棋谱,免去我们再思考和摸索。
10. 单例设计模式
所谓 类的单例设计模式,就是采用一定的方法保证在整个软件系统中, 对某个类只能存在一个对象实例,并且该类只提供一个获取该对象实例的方法
单例模式有两种方式: 1. 饿汉模式。2.懒汉模式
10.1 饿汉模式
饿汉模式步骤如下:
- 构造器初始化 =》 防止直接new
- 在类的内部创建对象
- 向外暴露一个静态的公共方法。getInstance
- 代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| 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; private static Singleton01 singleton01 = new Singleton01("小明"); private Singleton01(String name) { this.name = name; }
public static Singleton01 getInstance(){ return singleton01; } }
|
饿汉式, 直接就创建了, 只要类加载就创建了, 就算还没使用就已经创建了
10.2 懒汉式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| 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 对比懒汉与饿汉模式
- 二者最主要的区别在于创建对象的时机不同,饿汉模式是在类加载的时候就创建了对象实例,懒汉模式是在使用对象时才会去创建
- 饿汉模式不存在线程安全问题, 懒汉模式存在线程安全问题
- 饿汉模式存在浪费资源的可能, 如果程序员一个对象实例都没有使用,那么饿汉模式的对象就浪费了,懒汉模式是使用时才创建,就不存在这个问题
- 我们在JavaSE标准类中 java.lang.Runtime 就是经典的单例模式