Java23种设计模式
1、单例模式
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
注意:
- 单例类只能有一个实例。
- 单例类必须自己创建自己的唯一实例。
- 单例类必须给所有其他对象提供这一实例。
意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
主要解决:一个全局使用的类频繁地创建与销毁。
何时使用:当您想控制实例数目,节省系统资源的时候。
如何解决:判断系统是否已经有这个单例,如果有则返回,如果没有则创建。
关键代码:构造函数是私有的。
优点:
- 在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例(比如管理学院首页页面缓存)。
- 避免对资源的多重占用(比如写文件操作)。
缺点:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。
- 饿汉式
package com.liuxiang.Singleton;
//一上来就创建对象
public class TestSingleton {
//饿汉式 构造器私有 避免别人创建对象
private TestSingleton(){
}
// 饿汉式可能会造成浪费空间
private final static TestSingleton singleton = new TestSingleton();
public static TestSingleton getInstance(){
return singleton;
}
}
- 懒汉式
package com.liuxiang.Singleton;
public class TestSingleton {
//构造器私有 避免别人创建对象
private TestSingleton(){
}
// 懒汉式可能会造成浪费空间 volatile确保不会被指令重排
private volatile static TestSingleton singleton;
public static TestSingleton getInstance(){
if (singleton == null){
//多线程获取 需要使用synchronized 双重检测锁模式
synchronized (TestSingleton.class){
if (singleton == null){
singleton = new TestSingleton();
//DCL懒汉式双重检测机制,在极端情况下还是会出现问题
//new一个对象在正常情况下的执行流程应该是:
//1.分配内存空间
//2.执行构造方法,初始化对象
//3.把这个对象指向这个空间
//如果出现了指令重排123 变成了132 就会出现问题
//所以我们应该加上volatile确保不会发生指令重排
}
}
}
return singleton;
}
}
- 使用枚举确保单例模式不被反射破坏
package com.liuxiang.Singleton;
import java.lang.reflect.Constructor;
public class TestSingleton {
//构造器私有 避免别人创建对象
private TestSingleton(){
}
// 懒汉式可能会造成浪费空间 volatile确保不会被指令重排
private volatile static TestSingleton singleton;
public static TestSingleton getInstance(){
if (singleton == null){
//多线程获取 需要使用synchronized 双重检测锁模式
synchronized (TestSingleton.class){
if (singleton == null){
singleton = new TestSingleton();
}
}
}
return singleton;
}
//使用反射创建对象 破坏单例模式
public static void main(String[] args) throws Exception {
Constructor<TestSingleton> declaredConstructor = TestSingleton.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);//关闭安全检测
TestSingleton singleton1 = declaredConstructor.newInstance();//通过反射创建对象
TestSingleton singleton2 = declaredConstructor.newInstance();//通过反射创建对象
System.out.println(singleton1);
System.out.println(singleton2);
}
}
com.liuxiang.Singleton.TestSingleton@1b6d3586
com.liuxiang.Singleton.TestSingleton@4554617c
使用反射就可以绕过私有变量创建新的对象,我们查看反射的源码可以发现:反射机制不能操作枚举类
2、工厂模式
- 简单工厂模式
public interface Car {
String name();
}
public class WuLing implements Car{
@Override
public String name() {
return "五菱宏光!";
}
}
public class Tesla implements Car{
@Override
public String name() {
return "特斯拉!";
}
}
public class CarFactory {
//方法一
public static Car getCar(String car){
if (car.equals("五菱宏光")){
return new WuLing();
}else if (car.equals("特斯拉")){
return new Tesla();
}else {
return null;
}
}
//方法二
public static Car getWuLing(){
return new WuLing();
}
public static Car getTesla(){
return new Tesla();
}
}
public class Customer {
public static void main(String[] args) {
Car car1 = CarFactory.getWuLing();
Car car2 = CarFactory.getTesla();
System.out.println(car1.name());
System.out.println(car2.name());
}
}
- 工厂方法模式
public interface Car {
String name();
}
public class Tesla implements Car {
@Override
public String name() {
return "特斯拉!";
}
}
public class WuLing implements Car {
@Override
public String name() {
return "五菱宏光!";
}
}
public interface CarFactory {
Car getCar();
}
public class TeslaFactory implements CarFactory{
@Override
public Car getCar() {
return new Tesla();
}
}
public class WuLingFactory implements CarFactory{
@Override
public Car getCar() {
return new WuLing();
}
}
public class Customer {
public static void main(String[] args) {
Car car1 = new WuLingFactory().getCar();
Car car2 = new TeslaFactory().getCar();
System.out.println(car1.name());
System.out.println(car2.name());
}
}
3、抽象工厂模式
public interface PhoneProduct {
void start();
void shutdown();
void call();
void sms();
}
public class HuaweiPhone implements PhoneProduct {
@Override
public void start() {
System.out.println("开启华为手机");
}
@Override
public void shutdown() {
System.out.println("关闭华为手机");
}
@Override
public void call() {
System.out.println("华为打电话");
}
@Override
public void sms() {
System.out.println("华为发短信");
}
}
public class XiaomiPhone implements PhoneProduct {
@Override
public void start() {
System.out.println("开启小米手机");
}
@Override
public void shutdown() {
System.out.println("关闭小米手机");
}
@Override
public void call() {
System.out.println("小米打电话");
}
@Override
public void sms() {
System.out.println("小米发短信");
}
}
public interface RouteProduct {
void start();
void shutdown();
void Wifi();
void setting();
}
public class HuaweiRouter implements RouteProduct{
@Override
public void start() {
System.out.println("启动华为路由器");
}
@Override
public void shutdown() {
System.out.println("关闭华为路由器");
}
@Override
public void Wifi() {
System.out.println("打开华为Wifi");
}
@Override
public void setting() {
System.out.println("华为设置");
}
}
public class XiaomiRouter implements RouteProduct{
@Override
public void start() {
System.out.println("启动小米路由器");
}
@Override
public void shutdown() {
System.out.println("关闭小米路由器");
}
@Override
public void Wifi() {
System.out.println("打开小米Wifi");
}
@Override
public void setting() {
System.out.println("小米设置");
}
}
//抽象产品工厂
public interface ProductFactory {
//生产手机
PhoneProduct phoneProduct();
//生成路由器
RouteProduct routerProduct();
}
public class HuaweiFactory implements ProductFactory{
@Override
public PhoneProduct phoneProduct() {
return new HuaweiPhone();
}
@Override
public RouteProduct routerProduct() {
return new HuaweiRouter();
}
}
public class XiaomiFactory implements ProductFactory{
@Override
public PhoneProduct phoneProduct() {
return new XiaomiPhone();
}
@Override
public RouteProduct routerProduct() {
return new XiaomiRouter();
}
}
public class Customer {
public static void main(String[] args) {
System.out.println("==================小米系列产品=================");
XiaomiFactory xiaomiFactory = new XiaomiFactory();
PhoneProduct phoneProduct = xiaomiFactory.phoneProduct();
phoneProduct.call();
phoneProduct.sms();
RouteProduct routeProduct = xiaomiFactory.routerProduct();
routeProduct.start();
routeProduct.shutdown();
System.out.println("==================华为系列产品=================");
HuaweiFactory huaweiFactory = new HuaweiFactory();
PhoneProduct phoneProduct1 = huaweiFactory.phoneProduct();
phoneProduct1.call();
phoneProduct1.sms();
RouteProduct routeProduct1 = huaweiFactory.routerProduct();
routeProduct1.start();
routeProduct1.shutdown();
}
}
==================小米系列产品=================
小米打电话
小米发短信
启动小米路由器
关闭小米路由器
==================华为系列产品=================
华为打电话
华为发短信
启动华为路由器
关闭华为路由器
4、代理模式
代理模式是 Spring AOP 的底层实现
代理模式的定义:
为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
4.1、静态代理模式
抽象角色 : 一般使用接口或者抽象类来实现
真实角色 : 被代理的角色
代理角色 : 代理真实角色 ; 代理真实角色后 , 一般会做一些附属的操作。
客户 : 使用代理角色来进行一些操作。
- 代码实现
package com.liuxiang.demo;
// 抽象角色
public interface UserService {
public void add();
public void delete();
public void update();
public void query();
}
package com.liuxiang.demo;
// 真实对象
public class UserServiceImpl implements UserService{
@Override
public void add() {
System.out.println("增加了一个用户");
}
@Override
public void delete() {
System.out.println("删除了一个用户");
}
@Override
public void update() {
System.out.println("修改了一个用户");
}
@Override
public void query() {
System.out.println("查询了一个用户");
}
}
package com.liuxiang.demo;
// 客户
public class Client {
public static void main(String[] args) {
UserServiceImpl userService = new UserServiceImpl();
userService.add();
}
}
- 此时要增加一个日志功能,我们不可以去修改源代码(避免把源代码改崩),因此需要代理角色。
package com.liuxiang.demo;
// 代理角色
public class UserServiceProxy implements UserService{
private UserServiceImpl userService;
public void setUserService(UserServiceImpl userService) {
this.userService = userService;
}
@Override
public void add() {
log("add");
userService.add();
}
@Override
public void delete() {
log("delete");
userService.delete();
}
@Override
public void update() {
log("update");
userService.update();
}
@Override
public void query() {
log("query");
userService.query();
}
// 日志方法
public void log(String msg){
System.out.print("使用了"+msg+"方法:");
}
}
package com.liuxiang.demo;
// 客户
public class Client {
public static void main(String[] args) {
UserServiceProxy userServiceProxy = new UserServiceProxy();
userServiceProxy.setUserService(new UserServiceImpl());
userServiceProxy.add();
}
}
- 静态代理的好处:
- 可以使得我们的真实角色更加纯粹,不再去关注一些公共的事情。
- 公共的业务由代理来完成,实现了业务的分工。
- 公共业务发生扩展时变得更加集中和方便。
- 静态代理的缺点:
- 类多了,多了代理类,工作量变大了,开发效率降低。
4.2、动态代理
动态代理的角色和静态代理的一样
动态代理的代理类是动态生成的,静态代理的代理类是我们提前写好的
动态代理分为两类:
- 基于接口动态代理 ----- JDK动态代理(原则:真实对象和代理对象实现相同的接口)
- 基于类的动态代理 ----- cglib(原则:代理对象继承真实对象)
现在用的比较多的是 javasist 来生成动态代理
JDK的动态代理类位于 java.lang.reflect 包下,需要了解两个类:
- Proxy:代理
- InvocationHandler:调用处理程序
Proxy 提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类。
//生成代理类 public Object getProxy(){ return Proxy.newProxyInstance(this.getClass().getClassLoader(), rent.getClass().getInterfaces(),this); }
InvocationHandler是由代理实例的调用处理程序实现的接口 。 每个代理实例都有一个关联的调用处理程序。
当在代理实例上调用方法时,方法调用将被编码并分派到其调用处理程序的invoke方法。
该接口中仅定义了一个方法:
Object invoke(Object proxy, 方法 method, Object[] args);
参数详解:
proxy:调用该方法的代理实例。
method:所述方法对应于调用代理实例上的接口方法的实例。方法对象的声明类将是该方法声明的接口,它可以是代理类继承该方法的代理接口的超级接口。
args:包含的方法调用传递代理实例的参数值的对象的阵列,或null如果接口方法没有参数。原始类型的参数包含在适当的原始包装器类的实例中,例如 java.lang.Integer 或 java.lang.Boolean。
- 代码实现
动态代理步骤:
- 创建一个实现接口 InvocationHandler 的类,它必须实现invoke方法
- 创建被代理的类以及接口
- 通过Proxy的静态方法 new ProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h)创建一个代理
- 通过代理调用方法
package com.liuxiang.demo1;
//抽象角色:租房
public interface Rent {
public void rent();
}
package com.liuxiang.demo1;
// 真实角色
public class Host implements Rent{
@Override
public void rent() {
System.out.println("租房子");
}
}
package com.liuxiang.demo1;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyInvocationHandler implements InvocationHandler {
//被代理的接口
private Rent rent;
public void setRent(Rent rent) {
this.rent = rent;
}
//生成得到代理类
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
rent.getClass().getInterfaces(),this);
}
//处理代理实例,并返回结果 proxy :代理类 method : 代理类的调用处理程序的方法对象
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("找客户");
//执行目标方法获得结果 核心:本质利用反射实现!
Object result = method.invoke(rent, args);
System.out.println("结束订单");
return result;
}
}
package com.liuxiang.demo1;
public class Client {
public static void main(String[] args) {
//真实角色
Host host = new Host();
// 代理实例的调用处理程序
ProxyInvocationHandler pro = new ProxyInvocationHandler();
pro.setRent(host); //将真实角色放置进去!
Rent proxy = (Rent) pro.getProxy(); //动态生成对应的代理类!
proxy.rent();
}
}
- 动态代理的优点
- 可以使得我们的真实角色更加纯粹,不再去关注一些公共的事情。
- 公共的业务由代理来完成,实现了业务的分工。
- 公共业务发生扩展时变得更加集中和方便。
- 一个动态代理,一般代理某一类业务。
- 一个动态代理可以代理多个类,代理的是接口!