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实现步骤:
- 定义一个私有的构造方法,确保外界不能通过new关键字创建对象
- 创建一个私有的静态的属性,保存唯一对象
- 提供一个共有的静态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实现步骤:
- 私有化构造方法
- 创建private,static的实例对象
- 提供一个静态的公共方法,在方法中判断对象是否为空,为空则创建对象,不为空则直接返回对象
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作用
- 线程安全:通过两次检查确保在多线程环境下单例的唯一性。
- 性能优化:只有在第一次实例化时才需要同步,之后的调用不再需要同步,从而提高了性能。
- 懒加载:只有在第一次请求时才创建实例,实现了懒加载,节省了不必要的资源消耗。
2.3.4注意事项
虽然双重检查锁定能够有效地提高懒汉模式的性能,但在使用时需要注意以下几点:
- volatile关键字:使用
volatile
关键字标记单例对象,防止指令重排导致的问题。如果没有volatile
,可能会出现由于JVM优化导致的对象创建不完整的情况。- 构造函数私有化:确保外部不能直接通过new关键字创建对象。
- 序列化问题:如果单例对象支持序列化,还需要防止反序列化破坏单例性,可以通过覆盖
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