单例模式,可以认为是java的一种写代码的模板。单例模式:设计类的对象是单一的。
包括三种:饿汉式、懒汉式、静态内部类方式
饿汉式的代码示例:
public class Student {
//类加载时就创建实例
public static final Student stu = new Student();
//私有化构造方法
private Student(){
}
//全局访问点
public static Student getStu(){
return stu;
}
}
饿汉式是在加载类的时候就会创建实例,优点是:实现简单 缺点就是会一直占用资源、浪费资源。
注:全局访问点相当于一个统一的入口,程序中任何类、方法都能调用,并且最后确保返回同一个实例。
懒汉式的代码:只有真正需要实例的时候才会创建对象,而不是加载类时就创建
public class Student2 {
private static Student2 stu;
//私有化构造方法
private Student2(){
}
//获取对象
public static Student2 getStu(){
//Student2.class表示当前类的Class对象
// 每个类都有唯一的Class对象 这里面是synchronized需要的监听器必须是唯一的
if(stu == null){
synchronized (Student2.class){
if(stu == null){
stu = new Student2(); //调用时才创建
}
}
}
return stu;
}
}
注意:这里用到了双重检查锁定 可以提高效率
第一次检查 如果实例已经存在可以直接返回,避免不必要的加锁,如果不存在才会进入同步代码块来保证只有一个线程能创建实例。
第二次检查的目的是:防止多个线程同时通过第一次检查后,重复创建实例
静态内部类的方式:
public class Student3 {
//静态内部类
private static class Handle{
//在内部类中创建外部类的实例
static Student3 stu = new Student3();
}
//私有化构造方法
private Student3(){
}
public static Student3 getStu(){
return Handle.stu;//触发内部类的加载
}
}
只有调用getStu()才会创建实例。
静态内部类的进程:static Student3 stu = new Student3(); JVM的类加载机制本身就是线程安全的 不需要手动加锁。
假如线程A和线程B同时调用getStu(),线程A执行return Handle.stu ,JVM发现handle类还没加载,就会加载handle类,执行静态初始化。此时线程B执行return Handle.stu,JVM发现handle类正在被加载(线程A持有锁),线程B则会等待,只有A完成加载释放锁后,才会接着执行。