目录
单列模式(Singleton Pattern)
单列模式是一种创建型设计模式,它确保类只有一个实例,并提供一个全局访问点来获取该实例。
在单列模式中,类的构造函数被设置为私有,这样外部无法直接实例化对象。通过类的静态方法或者静态属性,可以获取到唯一的实例对象。如果多次调用获取实例的方法,将始终返回同一个实例。总之,选择单例模式就是为了避免不一致状态,避免政出多头。我们给出了单例模式在单线程环境下的两种经典实现:饿汉式 和懒汉式,但是饿汉式是线程安全的,而懒汉式是非线程安全的。在多线程环境下,我们特别介绍了五种方式来在多线程环境下创建线程安全的单例,即分别使用synchronized方法、synchronized块、静态内部类、双重检查模式 和ThreadLocal 来实现懒汉式单例,并总结出实现效率高且线程安全的懒汉式单例所需要注意的事项。
单列模式的特点:
- 只有一个实例:单列模式确保类只有一个实例,并提供全局访问点。
- 延迟实例化:单例对象的实例化是延迟进行的,即在第一次使用时才进行实例化。
- 全局访问点:通过类的静态方法或者静态属性,可以获取到单例对象的全局访问点。
三要素:
-
私有的构造方法;
-
指向自己实例的私有静态引用;
-
以自己实例为返回值的静态的公有方法。
2、单线程环境下的两种经典实现
在介绍单线程环境中单例模式的两种经典实现之前,我们有必要先解释一下 立即加载 和延迟加载 两个概念。
立即加载 : 在类加载初始化的时候就主动创建实例;
延迟加载 : 等到真正使用的时候才去创建实例,不用时不去主动创建。
在单线程环境下,单例模式根据实例化对象时机的不同,有两种经典的实现:一种是 饿汉式单例(立即加载),一种是 懒汉式单例(延迟加载)。饿汉式单例在单例类被加载时候,就实例化一个对象并交给自己的引用;而懒汉式单例只有在真正使用的时候才会实例化一个对象并交给自己的引用。
user类
package com.cskt;
import lombok.Data;
@Data
public class User {
private int id;
/*@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", word='" + word + '\'' +
'}';
}*/
/* public User(String name, String word) {
this.name = name;
this.word = word;
}*/
private String name;
private String word;
public String add;
private void show1(int id){
System.out.println("私有无参方法"+id);
}
private void show(String name){
System.out.println("公有有参方法"+name);
}
}
package com.cskt.text;
public class Singleton {
/*饿汉式*/
public static Singleton singleton=new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return singleton;
}
}
懒汉式
package com.cskt.text;
public class SqlSeSion {
/*懒汉式*/
public static SqlSeSion sqlSeSion;
private SqlSeSion(){}
public static SqlSeSion getInstance(){
if (sqlSeSion==null){
sqlSeSion=new SqlSeSion();
}
return sqlSeSion;
}
}
双锁
package com.cskt.text;
public class Singleton1 {
/*双重锁机制*/
private volatile static Singleton1 singleton1;
private Singleton1(){}
public static Singleton1 getSingleton1(){
if (singleton1==null){
synchronized (Singleton1.class){
if (singleton1==null){
singleton1=new Singleton1();
}
}
}
return singleton1;
}
}
测试类
package com.cskt.text;
public class text {
public static void main(String[] args) {
/*双锁*/
Singleton1 singleton11=Singleton1.getSingleton1();
Singleton1 singleton22=Singleton1.getSingleton1();
System.out.println(singleton11==singleton22);
/*懒汉式*/
SqlSeSion sqlSeSion1=SqlSeSion.getInstance();
SqlSeSion sqlSeSion2=SqlSeSion.getInstance();
System.out.println(sqlSeSion1==sqlSeSion2);
/*饿汉式*/
Singleton singleton1=Singleton.getInstance();
Singleton singleton2=Singleton.getInstance();
System.out.println(singleton1==singleton2);
}
}
反射(Reflection)
反射是指程序在运行时动态地获取和操作其自身的信息的能力。通过反射,我们可以在运行时获取类的定义信息,包括类的字段、方法、构造函数以及其他成员的信息,并且可以动态地创建对象、调用方法和访问/修改字段的值。
反射提供了一种机制,使得我们能够在编译时无法确定类型的情况下,仍然能够操作对象。通过反射,我们可以在运行时动态地加载和使用类,扩展程序的灵活性和可扩展性。
反射主要包含以下几个概念:
- Class对象:Class是反射的核心类,它代表一个类的定义。通过Class对象,可以获取到类的各种信息,如类名、字段、方法等。
- 获取Class对象:可以通过类名、对象实例、类加载器等方式来获取Class对象。
- 创建对象:通过反射,可以在运行时动态地创建对象,即使不知道具体类的类型。
- 访问/修改字段:可以通过反射来获取和修改对象的字段的值。
- 调用方法:通过反射,可以在运行时调用对象的方法,包括私有方法。
单例模式的优势
- 全局唯一实例: 单例模式确保一个类只有一个实例,这样可以方便地访问该实例,避免了多个实例对资源的竞争与浪费。
- 节省系统资源: 由于单例模式只创建一个实例,可以减少系统内存占用和性能开销。
- 简化操作和维护: 通过单例模式,我们可以集中管理和调度资源,简化代码结构和逻辑。同时,也便于对单例对象进行统一的维护和更新。
反射的优势
- 动态加载和扩展: 反射提供了在运行时动态加载所需的类的能力,这为实现插件化架构和动态扩展功能提供了便利。
- 灵活修改程序行为: 通过反射,我们可以动态地获取和修改类的信息、调用方法和访问字段,从而实现灵活修改程序的行为和状态。
- 代码生成和自动化处理: 结合注解处理器,反射可以在编译时扫描和处理注解,并生成相关的代码,从而实现自动生成代码和自动化处理的目的。
单例模式与反射的结合应用
单例模式和反射可以结合应用,在某些场景下发挥更大的作用。例如:
- 使用反射创建单例对象: 通过反射,我们可以绕过单例模式的限制,强制创建多个实例。这在一些特殊情况下可能是有用的,但需要注意破坏了单例模式的初衷。
- 在单例类中使用反射操作: 通过反射,可以在单例类中获取私有构造方法、调用私有方法、访问私有字段等,从而对单例对象进行灵活的操作和修改。
然而,需要注意的是,反射操作可能会破坏单例模式的封装性,并且由于反射通常比直接调用方法和访问字段要慢,所以在性能敏感的场景下需要谨慎使用。
反射实列
package com.cskt.text;
import com.cskt.User;
import lombok.val;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class usertext {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException {
/*反射模式 全路径*/
Class c=Class.forName("com.cskt.User");
Constructor<User> constructor = c.getConstructor();
User user1 = constructor.newInstance();/*new 拿公有*/
user1.add="北京";
/*私有*/
Field name=c.getDeclaredField("name"); /*@CallerSensitive 别人已经封装好了拿过来用这个 这里放私有属性*/
name.setAccessible(true); /*开启私有*/
name.set(user1,"张三");
Field word=c.getDeclaredField("word");
word.setAccessible(true);
word.set(user1,"小符");
System.out.println(user1.toString());
/*反射 com.cskt.User项目里的全路径*/
Class c4=Class.forName("com.cskt.User");
Constructor<User> constructor1 = c4.getConstructor();
User user2 = constructor1.newInstance();
/* user2.show("萧炎");*/
Method shows=c4.getDeclaredMethod("show",String.class); /*你要String类型 就String.class*/
shows.setAccessible(true);
shows.invoke(user2,"萧炎");
Method show1=c4.getDeclaredMethod("show1",int.class);
show1.setAccessible(true);
show1.invoke(user2,99);
}
}
反射 + 单例 思维图
我个人很喜欢这些博主的资源,可以学到很多
博主介绍:✌全网粉丝30W+,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌
🍅文末获取源码联系🍅
👇🏻 精彩专栏推荐订阅👇🏻 不然下次找不到哟
基于Java+SpringBoot+Vue+echarts健身房管理系统设计和实现_java李杨勇的博客-CSDN博客【Spring(十一)】万字带你深入学习面向切面编程AOP_续写青春.的博客-CSDN博客
【K8S系列】深入解析k8s网络插件—Weave Net_颜淡慕潇的博客-CSDN博客
等