单例设计模式分为饿汉式与懒汉式
单例设计模式:即某个类在整个系统中只能有一个实例对象可以被获取和使用,例如JVM运行的Runtime类
要点分析
1、某各类只能有一个实例
构造器私有化
2、它必须只能由它自己创建这个实例
含有该类的静态变量来保存该类的唯一实例
3、必须提供该实例的获取方法
1、直接暴露(直接公开,静态变量设为public)
2、使用静态变量的get方法获取
饿汉式:在类初始化时就直接创建对象,不存在线程安全问题,但该方式会造成一定的内存浪费
1、直接实例化饿汉式(简洁直观)
/**
* @Author: yuanzhi...
* @Date: created in 2020/1/13 14:12
*
* 饿汉式:
* 直接创建这个对象,不管你需不需要(假如该类中有静态方法,
* 那么调用时就不需要此类的实例化对象了)
*/
public class SingleTon {
private static final SingleTon SINGLETON = new SingleTon();
private SingleTon(){
}
}
2、使用枚举(最简洁)
/**
* @Author: yuanzhi...
* @Date: created in 2020/1/13 17:44
*
* 枚举类型:表示该类型的对象是有限的几个
* 我们可以限定为一个,就成了单例模式
* 枚举类型的构造器都是私有化的,所以不需要再去处理
*/
public enum SingleTon2 {
SINGLETON
}
class test{
public static void main(String[] args) {
SingleTon2 singleTon2 = SingleTon2.SINGLETON;
System.out.println(singleTon2);//此处打印出来的并不是类名加hashcode码,而是常量
}
}
3、使用静态代码块的方式(适合复杂实例化)
single.yml文件(放在src下),或者用single.properties文件(两者区别此处不细说)
#key: value
info: yuanZhi
实现代码:
import java.io.IOException;
import java.util.Properties;
/**
* @Author: yuanzhi...
* @Date: created in 2020/1/13 17:52
*/
public class SingleTon3 {
public static final SingleTon3 SINGLETON;
private String info;
static {
try {
Properties properties = new Properties();
properties.load(SingleTon3.class.getClassLoader().getResourceAsStream("single.yml"));
SINGLETON = new SingleTon3(properties.getProperty("info"));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private SingleTon3(String info){
this.info = info;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
@Override
public String toString() {
return "SingleTon3{" +
"info='" + info + '\'' +
'}';
}
}
class test3{
public static void main(String[] args) {
SingleTon3 singleTon3 = SingleTon3.SINGLETON;
System.out.println(singleTon3);
}
}
懒汉式:延缓式创建对象,即在什么时候需要什么时候创建,所以并不会造成内存的浪费
1、线程不安全(适用于单线程)
import java.util.concurrent.*;
/**
* @Author: yuanzhi...
* @Date: created in 2020/1/13 18:20
*
* 提供一个获取实例对象的静态方法
*/
public class SingleTon4 {
private static SingleTon4 singleTon4;
private SingleTon4() {
}
public static SingleTon4 singleTon4(){
if (singleTon4 == null){
//就算没有一下线程休眠的代码。也有可能CPU时间到了
//但是是有概率的,所以可能会是同一个,也可能不是
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
singleTon4 = new SingleTon4();
}
return singleTon4;
}
}
class test4{
public static void main(String[] args) throws ExecutionException, InterruptedException {
/*SingleTon4 singleTon4 = SingleTon4.singleTon4();
SingleTon4 singleTon41 = SingleTon4.singleTon4();
System.out.println(singleTon4 == singleTon41);//true
System.out.println(singleTon4);//SingleTon4@4554617c
System.out.println(singleTon41);//SingleTon4@4554617c*/
//线程安全问题(多线程,下面以两个线程为例)
System.out.println("===========================线程安全问题分割线============================");
Callable<SingleTon4> callable = new Callable<SingleTon4>() {
@Override
public SingleTon4 call() throws Exception {
return SingleTon4.singleTon4();
}
};
ExecutorService es = Executors.newFixedThreadPool(2);
Future<SingleTon4>future1 = es.submit(callable);
Future<SingleTon4>future2 = es.submit(callable);
SingleTon4 singleTon42 = future1.get();
SingleTon4 singleTon43 = future2.get();
System.out.println(singleTon42 == singleTon43);//false
System.out.println(singleTon42);//SingleTon4@6d6f6e28
System.out.println(singleTon43);//SingleTon4@135fbaa4
es.shutdown();
}
}
2、线程安全(适用于多线程)(在上面的基础上以同步的方法加以改善)
import java.util.concurrent.*;
/**
* @Author: yuanzhi...
* @Date: created in 2020/1/13 18:20
*
* 提供一个获取实例对象的静态方法
*/
public class SingleTon5 {
private static SingleTon5 singleTon5;
private SingleTon5() {
}
public static SingleTon5 SingleTon5(){
if (singleTon5 == null) { //该句为了提高性能,即如果有了就不必在需要锁那些了
synchronized (SingleTon5.class) {
if (singleTon5 == null) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
singleTon5 = new SingleTon5();
}
}
}
return singleTon5;
}
}
class test5{
public static void main(String[] args) throws ExecutionException, InterruptedException {
Callable<SingleTon5> callable = new Callable<SingleTon5>() {
@Override
public SingleTon5 call() throws Exception {
return SingleTon5.SingleTon5();
}
};
ExecutorService es = Executors.newFixedThreadPool(2);
Future<SingleTon5>future1 = es.submit(callable);
Future<SingleTon5>future2 = es.submit(callable);
SingleTon5 SingleTon52 = future1.get();
SingleTon5 SingleTon53 = future2.get();
System.out.println(SingleTon52 == SingleTon53);//true
System.out.println(SingleTon52);//SingleTon5@6d6f6e28
System.out.println(SingleTon53);//SingleTon5@6d6f6e28
es.shutdown();
}
}
3、静态内部类实现(适用于多线程)
/**
* @Author: yuanzhi...
* @Date: created in 2020/1/13 19:27
*
* 在内部类被加载和初始化的时候才创建对象SINGLE_TON_6
* 静态内部类不会总动随着外部类的加载和初始化而初始化,它是要单独加载和初始化的
* 因为是在内部类加载和初始化时创建的,因此是线程安全的
*/
public class SingleTon6 {
private SingleTon6(){
}
private static class Inner{
private static final SingleTon6 SINGLE_TON_6 = new SingleTon6();
}
public static SingleTon6 getSingleTon6(){
return Inner.SINGLE_TON_6;
}
}
class test6{
public static void main(String[] args) {
SingleTon6 singleTon61 = SingleTon6.getSingleTon6();
SingleTon6 singleTon62 = SingleTon6.getSingleTon6();
System.out.println(singleTon61 == singleTon62);//true
System.out.println(singleTon61);//SingleTon6@4554617c
System.out.println(singleTon62);//SingleTon6@4554617c
}
}
小结:
1、如果是饿汉式,枚举形式最简单;
2、如果是懒汉式,静态内部类形式最简单;
作者君总结码字不易,留个赞在走吧