1. 代码块基本介绍
代码块又称为初始化块, 属于类中的成员, 即为类的额一部分,类似于方法,将逻辑语句封装在方法体中, 通过 {} 包围起来。
但代码块又和方法不同, 没有方法名,没有返回值, 没有参数,只有方法体,而且不通过对象或类显式调用,而是加载类时,或创建对象时隐式调用。
- 基本语法
[修饰符]{
代码块
};
- 注意事项
- 修饰符: 代码快如果使用修饰符那么只能写 static , 非必须
- 代码块可以分为两类, 即由static修饰的静态代码块与没有static修饰的 普通代码块
- 代码块中的逻辑语句可以为任意逻辑,输入、输出、方法调用, 循环、判断等
- 代码块结尾的分号 “ ; ” 可以省略
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. 代码块的细节
static 代码块也叫静态代码块,作用是对类进行初始化, 它随着类的加载而执行, 并且只会执行一次, 如果是普通代码块,每创建一个对象执行一次。
类什么时候被加载?
- 创建对象时候 (new)
- 子类创建对象时, 父类也会被加载
- 使用对象的静态成员时,(静态属性,静态方法)
普通的代码在创建对象实例时,会被隐式调用, 被创建一次, 就会调用一次
如果只是实用类的静态成员,类的普通代码块并不会被执行
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. 代码块的调用顺序
创建一个对象时, 在一个类的调用顺序是:
- 调用静态代码块和静态属性初始化,(静态代码块和静态属性初始化调用的优先级一样, 如果有多个静态代码块和静态变量初始化,则按照他们的定义顺序调用)
- 调用普通代码块和普通属性初始化,(普通代码块和普通属性初始化调用的优先级一样, 如果有多个普通代码块和普通变量初始化,则按照他们的定义顺序调用)
- 调用构造方法
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. 继承中的代码块执行顺序
我们看一下创建一个子类对象时(继承关系)他们的静态代码块,静态属性初始化, 普通代码,普通属性初始化,构造方法的调用顺序:
父类的静态代码块和静态属性初始化(优先级一样,按定义顺序执行)
子类的静态代码块和静态属性初始化(优先级一样,按定义顺序执行)
父类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
父类构造方法
子类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
子类构造方法
- 静态代码块只能直接调用静态成员(静态代码块或静态属性),普通代码块则可以调用任意成员
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. 单例模式
什么是设计模式?
- 静态方法和属性的经典使用
- 设计模式是大量的实践中总结和理论之后优选的代码结构、编程风格、以及解决问题的思考方式。涉及模式就像是经典的棋谱, 不同的棋局我们用不同的棋谱,免去我们再思考和摸索。
10. 单例设计模式
所谓 类的单例设计模式,就是采用一定的方法保证在整个软件系统中, 对某个类只能存在一个对象实例,并且该类只提供一个获取该对象实例的方法
单例模式有两种方式: 1. 饿汉模式。2.懒汉模式
10.1 饿汉模式
饿汉模式步骤如下:
- 构造器初始化 =》 防止直接new
- 在类的内部创建对象
- 向外暴露一个静态的公共方法。getInstance
- 代码实现
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 对比懒汉与饿汉模式
- 二者最主要的区别在于创建对象的时机不同,饿汉模式是在类加载的时候就创建了对象实例,懒汉模式是在使用对象时才会去创建
- 饿汉模式不存在线程安全问题, 懒汉模式存在线程安全问题
- 饿汉模式存在浪费资源的可能, 如果程序员一个对象实例都没有使用,那么饿汉模式的对象就浪费了,懒汉模式是使用时才创建,就不存在这个问题
- 我们在JavaSE标准类中 java.lang.Runtime 就是经典的单例模式