final关键字-单例模式-静态代码块

1.final关键字

下面简要介绍 final 关键字的一些主要用法:

1.修饰变量:

当一个变量被声明为 final 时,它的值不能被改变。对于基本类型来说,这意味着一旦给变量赋了值,就不能再改变这个值。对于引用类型(如对象),这意味着引用一旦指向一个对象后,就不能再指向另一个对象,但是对象的内容还是可以修改的,除非这个类的对象也是 final 的。

final int number = 10; // number 的值不能改变
final String str = "Hello"; // str 不能指向其他字符串,但原始字符串可以被修改(如果使用的是 StringBuilder 或 StringBuffer)

2.修饰方法:

如果一个方法被声明为 final,则该方法不能在子类中被覆写(override)。这通常用于确保某些关键操作不会被更改或覆盖。

public final void printMessage() { ... } // 此方法不能在子类中被覆写

3.修饰类:

如果一个类被声明为 final,则该类不能被继承。这通常用于防止一个类被继承的情况,确保其行为不会被子类修改。  

public final class UtilityClass { ... } // 此类不能被继承

2.单例模式

饿汉模式(Eager Initialization)和懒汉模式(Lazy Initialization)是单例模式中的两种实例化方式。它们主要应用于控制类的实例数量,确保某个类只有一个实例,并提供一个全局访问点。这两种模式的区别在于何时创建这个唯一的实例。

2.1饿汉模式:

饿汉模式是指在类加载的时候就完成了初始化,所以类一旦加载,就可以直接使用。

2.1.1特点:

  • 类加载时立即完成初始化。
  • 没有同步问题,因为是在类加载时就创建了实例。
  • 单例对象在系统中只存在一次,占用内存资源。

2.1.2优点:

  • 实例化保证在类加载时完成,因此可以保证线程安全。
  • 不需要每次调用getInstance()方法时进行同步操作,提高了性能。

2.1.3缺点:

  • 在任何地方都没有请求Singleton对象的情况下也完成了初始化,造成了资源浪费。
  • 如果实例化过程中发生错误,如资源未找到或配置错误等,会直接导致程序启动失败。

2.1.4实现步骤:

  1. 定义一个私有的构造方法,确保外界不能通过new关键字创建对象
  2. 创建一个私有的静态的属性,保存唯一对象
  3. 提供一个共有的静态getter方法,返回唯一对象

public class A {
    private A(){}
    private static A a= new A();
    public static A getInstance(){
        return a;
    }
}

 2.2懒汉模式(延迟加载式):

懒汉模式是指在第一次使用时才初始化,即延迟加载,只有当第一次调用getInstance方法时,才会真正创建实例。

2.2.1特点:

  • 第一次使用时完成初始化。
  • 可能会有同步问题,因为初始化可能发生在运行时的任何时刻。

2.2.2优点:

  • 资源利用率高,如果程序中根本没有用到这个单例,则不会创建它,节省了内存。
  • 实现简单。

2.2.3缺点:

  • 如果多线程同时访问,可能会产生多个实例,需要使用同步机制来保证单例性,而同步会带来一定的开销。

2.2.4实现步骤:

  1. 私有化构造方法
  2. 创建private,static的实例对象
  3. 提供一个静态的公共方法,在方法中判断对象是否为空,为空则创建对象,不为空则直接返回对象
public class B {
    private static B b;
    private B() {}
    public static B getInstance()
    {
        if (b == null)
        {
            b = new B();
        }
        return b;
    }
}

 2.3注意事项

在实际开发中,为了保证懒汉模式的线程安全性,通常会采用双重检查锁定(Double Checked Locking)或其他更高效的方式实现。

2.3.1双重检查锁定(DCL)

双重检查锁定是一种用于懒汉式单例模式的设计模式,目的是在保证线程安全的同时减少不必要的同步开销。DCL的核心思想是在进入同步块之前先检查实例是否已经被创建,如果尚未创建再进入同步块创建实例,从而避免了每次获取实例时都要进行同步所带来的性能损失。

2.3.2定义

双重检查锁定是一种优化技术,用于实现懒汉式的单例模式。其核心是在非线程安全的检查之后再加一个线程安全的检查,从而保证实例化过程的线程安全性和性能。

2.3.2下面是一个使用双重检查锁定实现懒汉式单例模式的例子:

public class Singleton {
    // 使用 volatile 关键字确保实例化时的可见性和有序性
    private volatile static Singleton instance;

    private Singleton() {}

    public static Singleton getInstance() {
        // 第一次检查:如果实例已经创建,则直接返回
        if (instance == null) {
            // 第二次检查:在同步块内再次检查,确保多线程环境下的唯一性
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

2.3.3作用

  1. 线程安全:通过两次检查确保在多线程环境下单例的唯一性。
  2. 性能优化:只有在第一次实例化时才需要同步,之后的调用不再需要同步,从而提高了性能。
  3. 懒加载:只有在第一次请求时才创建实例,实现了懒加载,节省了不必要的资源消耗。

2.3.4注意事项

虽然双重检查锁定能够有效地提高懒汉模式的性能,但在使用时需要注意以下几点:

  1. volatile关键字:使用volatile关键字标记单例对象,防止指令重排导致的问题。如果没有volatile,可能会出现由于JVM优化导致的对象创建不完整的情况。
  2. 构造函数私有化:确保外部不能直接通过new关键字创建对象。
  3. 序列化问题:如果单例对象支持序列化,还需要防止反序列化破坏单例性,可以通过覆盖readResolve方法解决。

总的来说,双重检查锁定是一种在保证线程安全的前提下,尽量减少同步开销的技术手段。在实际应用中,要根据具体情况选择合适的方法实现单例模式。

3.静态代码块和构造代码块:

3.1静态代码块:

  • 位置:在类中方法外

  • 格式:static{}

  • 作用:在类被加载的时候,静态代码块中的代码只会被执行一次

  • 特点:静态代码块只执行一次,且优先于main方法执行

3.2构造代码块:

  • 位置:在构造方法之前

  • 格式:{}

  • 作用:构造代码块中的代码,会被对象创建出来,且优先于构造方法执行;随着对象的创建而执行

3.3面试题:

public class Fu {
    static {
        System.out.println("父类静态代码块");//1
    }
    {
        System.out.println("父类构造代码块");//2
    }
    public Fu()
    {
        System.out.println("父类构造方法");//3
    }
}
public class Zi extends Fu{
    static {
        System.out.println("子类静态代码块");//4
    }
    {
        System.out.println("子类构造代码块");//5
    }
    public Zi(){
        System.out.println("子类构造方法");//6
    }
}
public class Test1 {
    public static void main(String[] args) {
        new Zi();
    }
}

代码执行结果:1-4-2-3-5-6

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值