脚本宝典收集整理的这篇文章主要介绍了单例模式,脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。
单例模式,是一种常用的软件设计模式。通过单例模式可以保证系统中,应用该模式的这个类永远只有一个实例。即一个类永远只有一个对象实例。
代码示例
package cn.Guardwhy.singleDemo01;
/**
单例的应用场景:在实例开发中,有很多业务对象永远只需要一个,无论启动多少次
我们只需要一个对象,例如任务管理对象,只需要一个。节约内存和性能。
单例的设计方式:
(1)饿汉单例设计模式
在用类的时候,对象已经创建好了。
步骤:
a.定义一个对象,最好static和final修饰,这样这个对象永远是唯一不可变的对象了。
b.把构造器进行私有化,外面就不能创建新对象。
c.提供一个方法把唯一的单例对象返回出去。
*/
public class HungrySingle01 {
public static void main(String[] args) {
// 4.创建对象
MyHungrySingle01 single1 = MyHungrySingle01.getInstance();
MyHungrySingle01 single2 = MyHungrySingle01.getInstance();
// 5.输出结果
System.out.PRintln(single1 == single2); // true
}
}
class MyHungrySingle01{
// 1.定义一个对象
private static final MyHungrySingle01 instance = new MyHungrySingle01();
// 2.将构造器私有化
public MyHungrySingle01() {
}
// 3.返回唯一的对象
public static MyHungrySingle01 getInstance(){
return instance;
}
}
在类中定义static变量的对象,并且直接初始化。既保证了线程的安全性,同时又满足了懒加载。这是饿汉式的改进版本。
package cn.guardwhy.singleDemo01;
// 静态内部类
public class Holder {
// 1. 构造器
private Holder(){
}
public static Holder getInstance(){
return InnerClass.HOLDER;
}
public static class InnerClass{
private static final Holder HOLDER = new Holder();
}
}
正常的懒汉式单例
代码示例
package cn.guardwhy.singleDemo01;
/*
懒汉单例设计模式
真正需要的时候,才创建一个对象。
步骤:
a.定义一个对象变量用于后面存储一个对象,此时是没有创建对象的。
b.把构造器进行私有化,外面就不能创建新对象。
c.提供一个方法,等需要对象的时候判断是否有一个唯一对象,如果没有创建一个对象。
以后都是直接返回这个对象即可!!
*/
public class LazySingle02 {
public static void main(String[] args) {
MyLazySingle02 t1 = MyLazySingle02.getInstance();
MyLazySingle02 t2 = MyLazySingle02.getInstance();
// 输出结果
System.out.println(t1); // cn.guardwhy.singleDemo01.MyLazySingle02@49e4cb85
System.out.println(t1); // cn.guardwhy.singleDemo01.MyLazySingle02@49e4cb85
System.out.println(t1 == t2); // true
}
}
// 懒汉式单例模式
class MyLazySingle02{
// 1.定义一个对象变量用于保存一个对象,但是这里还没有创建对象
private static MyLazySingle02 instance = null;
// 2.把构造器私有起来
private MyLazySingle02(){
}
// 3.返回这个唯一的对象
public static MyLazySingle02 getInstance(){
// 3.1 懒汉模式是现在需要,才创建对象。
if(instance == null){
instance = new MyLazySingle02();
}
// 3.2 返回对象
return instance;
}
}
代码示例
package cn.guardwhy.singleDemo02;
/*
懒汉式单例
*/
public class LazySingle {
// 1.将构造器私有化
public LazySingle() {
System.out.println(Thread.currentThread().getName() + " ok");
}
// 2.定义lazySingle
private static LazySingle lazySingle;
// 3.双重检查锁模式 懒汉式单例 DCL懒汉式
public static LazySingle getInstance(){
// 3.1 条件判断
if(lazySingle == null){
synchronized (LazySingle.class){
if(lazySingle == null){
// 不是一个原子性操作
lazySingle = new LazySingle();
}
}
}
// 4.返回对象
return lazySingle;
}
// 多线程并发
public static void main(String[] args) throws Exception {
for (int i = 0; i < 10; i++) {
new Thread(()->{
LazySingle.getInstance();
}).start();
}
}
}
DCL懒汉式的单例,保证了线程的安全性,又符合了懒加载,只有在用到的时候,才会去初始化,调用效率也比较高,但是这种写法在极端情况还是可能会有一定的问题。因为不是原子性操作,至少会经过三个步骤:
lazySingle = new LazySingle();
注意点
由于指令重排,导致A线程执行 lazySingle = new LazySingle( );的时候,可能先执行了第三步(还没执行第二步),此时线程B又进来了,发现lazySingle已经不为空了,直接返回了lazySingle,并且后面使用了返回的lazySingle,由于线程A还没有执行第二步,导致此时lazySingle还不完整,可能会有一些意想不到的错误,这时候增加一个volatile关键字来避免指令重排。
代码示例
package cn.guardwhy.singleDemo02;
/*
懒汉式单例
*/
public class LazySingle {
// 1.将构造器私有化
public LazySingle() {
System.out.println(Thread.currentThread().getName() + " ok");
}
// 2. volatile关键字来避免指令重排
private volatile static LazySingle lazySingle;
// 3.双重检查锁模式 懒汉式单例 DCL懒汉式
public static LazySingle getInstance(){
// 3.1 条件判断
if(lazySingle == null){
synchronized (LazySingle.class){
if(lazySingle == null){
/*
1.分配对象内存空间
2. 执行构造方法初始化对象
3. 设置instance指向刚分配的内存地址,此时instance !=null;
*/
lazySingle = new LazySingle(); // 不是一个原子性操作
}
}
}
// 4.返回对象
return lazySingle;
}
// 多线程并发
public static void main(String[] args) throws Exception {
for (int i = 0; i < 10; i++) {
new Thread(()->{
LazySingle.getInstance();
}).start();
}
}
}
通过反射破坏单例模式,反射无视private修饰的构造方法,可以直接在外面newInstance
代码示例
package cn.guardwhy.singleDemo02;
import java.lang.reflect.Constructor;
/*
懒汉式单例
*/
public class LazySingle {
// 1.将构造器私有化
public LazySingle() {
System.out.println(Thread.currentThread().getName() + " ok");
}
// 2. volatile关键字来避免指令重排
private volatile static LazySingle lazySingle;
// 3.双重检查锁模式 懒汉式单例 DCL懒汉式
public static LazySingle getInstance(){
// 3.1 条件判断
if(lazySingle == null){
synchronized (LazySingle.class){
if(lazySingle == null){
lazySingle = new LazySingle(); // 不是一个原子性操作
}
}
}
// 4.返回对象
return lazySingle;
}
public static void main(String[] args) throws Exception {
// 1.创建对象instance1
LazySingle instance1 = LazySingle.getInstance();
// 2.获得反射对象
Constructor<LazySingle> declaredConstructor = LazySingle.class.getDeclaredConstructor(null);
// 3.无视私有构造器
declaredConstructor.setAccessible(true);
// 4.通过反射创建对象instance2
LazySingle instance2 = declaredConstructor.newInstance();
System.out.println(instance1.hashCode()); // 460141958
System.out.println(instance2.hashCode()); // 1163157884
// 3.判断是否相等
System.out.println(instance1 == instance2); // false
}
}
解决方案
// 1.将构造器私有化
public LazySingle() {
synchronized (LazySingle.class){
// 条件判断
if(lazySingle != null){
throw new RuntimeException("反射破坏失败!!!");
}
}
}
执行结果
先调用getInstance方法,一上来就直接用反射创建对象,判断就不生效了。
代码示例
package cn.guardwhy.singleDemo02;
import java.lang.reflect.Constructor;
/*
懒汉式单例
*/
public class LazySingle {
// 1.将构造器私有化
public LazySingle() {
synchronized (LazySingle.class){
// 条件判断
if(lazySingle != null){
throw new RuntimeException("反射破坏失败!!!");
}
}
}
// 2. volatile关键字来避免指令重排
private volatile static LazySingle lazySingle;
// 3.双重检查锁模式 懒汉式单例 DCL懒汉式
public static LazySingle getInstance(){
// 3.1 条件判断
if(lazySingle == null){
synchronized (LazySingle.class){
if(lazySingle == null){
lazySingle = new LazySingle(); // 不是一个原子性操作
}
}
}
// 4.返回对象
return lazySingle;
}
public static void main(String[] args) throws Exception {
// 1.获得反射对象
Constructor<LazySingle> declaredConstructor = LazySingle.class.getDeclaredConstructor(null);
// 2.无视私有构造器
declaredConstructor.setAccessible(true);
// 3.通过反射创建对象instance2
LazySingle instance2 = declaredConstructor.newInstance();
LazySingle instance1 = declaredConstructor.newInstance();
System.out.println(instance1.hashCode()); // 460141958
System.out.println(instance2.hashCode()); // 1163157884
// 4.判断是否相等
System.out.println(instance1 == instance2); // false
}
}
解决方案
// 0.定义标志符
private static boolean flag = false;
// 1.将构造器私有化
public LazySingle() {
synchronized (LazySingle.class){
// 条件判断
if(flag == false){
flag = true;
}else{
throw new RuntimeException("反射破坏失败!!!");
}
}
}
执行结果
定义一个boolean变量flag,初始值是false,私有构造函数里面做一个判断,如果flag=false,就把flag改为true,但是如果flag等于true,就说明有问题了,因为正常的调用是不会第二次跑到私有构造方法的,所以抛出异常。看起来很美好,但是还是不能完全防止反射破坏单例模式,因为可以利用反射修改flag的值。
代码示例
package cn.guardwhy.singleDemo02;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
/*
懒汉式单例
*/
public class LazySingle {
// 0.定义标志符
private static boolean flag = false;
// 1.将构造器私有化
public LazySingle() {
synchronized (LazySingle.class){
// 条件判断
if(flag == false){
flag = true;
}else{
throw new RuntimeException("反射破坏失败!!!");
}
}
}
// 2. volatile关键字来避免指令重排
private volatile static LazySingle lazySingle;
// 3.双重检查锁模式 懒汉式单例 DCL懒汉式
public static LazySingle getInstance(){
// 3.1 条件判断
if(lazySingle == null){
synchronized (LazySingle.class){
if(lazySingle == null){
lazySingle = new LazySingle(); // 3.2不是一个原子性操作
}
}
}
// 4.返回对象
return lazySingle;
}
public static void main(String[] args) {
try {
// 拿到隐藏字段
Field flag = LazySingle.class.getDeclareDField("flag");
// 破坏字段的私有权限
flag.setAccessible(true);
// 获得反射对象
Constructor<LazySingle> declaredConstructor = LazySingle.class.getDeclaredConstructor(null);
// 无视私有构造器
declaredConstructor.setAccessible(true);
LazySingle instance1 = declaredConstructor.newInstance();
// 修改对象1的值
flag.set(instance1, false);
// 通过反射创建对象instance2
LazySingle instance2 = declaredConstructor.newInstance();
System.out.println(instance1.hashCode()); // 1163157884
System.out.println(instance2.hashCode()); // 1956725890
// 判断是否相等
System.out.println(instance1 == instance2); // false
} catch (Exception e) {
e.printStackTrace();
}
}
}
枚举类型是Java 5中新增特性的一部分,它是一种特殊的数据类型,之所以特殊是因为它既是一种类(class)类型却又比类类型多了些特殊的约束,但是这些约束的存在也造就了枚举类型的简洁性、安全性以及便捷性。
package cn.guardwhy.singleDemo02;
public enum EnumSingleton {
// 定义一个单例对象
INSTANCE;
public EnumSingleton getInstance(){
// 返回对象
return INSTANCE;
}
public static void main(String[] args) {
EnumSingleton singleton1 = EnumSingleton.INSTANCE;
EnumSingleton singleton2 = EnumSingleton.INSTANCE;
// 结果判断
System.out.println("两个实例是否相等:" + (singleton1==singleton2)); // 两个实例是否相等:true
}
}
通过查看newInstance的源码可得知
通过 jad工具进行反编译
jad -s java EnumSingleton.class
# 会生成一个java文件
Parsing EnumSingleton.class... Generating EnumSingleton.java
查看源码
// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.COM/jad.htML
// Decompiler options: packimports(3)
// Source File Name: EnumSingleton.java
package cn.guardwhy.singleDemo02;
import java.io.PrintStream;
public final class EnumSingleton extends Enum
{
public static EnumSingleton[] values()
{
return (EnumSingleton[])$VALUES.clone();
}
public static EnumSingleton valueOf(String name)
{
return (EnumSingleton)Enum.valueOf(cn/guardwhy/singleDemo02/EnumSingleton, name);
}
private EnumSingleton(String s, int i)
{
suPEr(s, i);
}
public EnumSingleton getInstance()
{
return INSTANCE;
}
public static void main(String args[])
{
EnumSingleton singleton1 = INSTANCE;
EnumSingleton singleton2 = INSTANCE;
System.out.println((new StringBuilder()).append("u4E24u4E2Au5B9Eu4F8Bu662Fu5426u76F8u7B49:").append(singleton1 == singleton2).toString());
}
public static final EnumSingleton INSTANCE;
private static final EnumSingleton $VALUES[];
static
{
INSTANCE = new EnumSingleton("INSTANCE", 0);
$VALUES = (new EnumSingleton[] {
INSTANCE
});
}
}
代码示例
package cn.guardwhy.singleDemo02;
import java.lang.reflect.Constructor;
public enum EnumSingleton {
// 定义一个单例对象
INSTANCE;
public EnumSingleton getInstance(){
// 返回对象
return INSTANCE;
}
public static void main(String[] args) {
EnumSingleton singleton1 = EnumSingleton.INSTANCE;
try {
// 获得反射对象
Constructor<EnumSingleton> declaredConstructor = EnumSingleton.class.getDeclaredConstructor(String.class, int.class);
// 破解私有构造器
declaredConstructor.setAccessible(true);
// 通过反射创建对象singleton2
EnumSingleton singleton2 = declaredConstructor.newInstance();
// 输出结果
System.out.println(singleton1);
System.out.println(singleton2);
} catch (Exception e) {
e.printStackTrace();
}
}
}
执行结果
以上是脚本宝典为你收集整理的单例模式全部内容,希望文章能够帮你解决单例模式所遇到的问题。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。