设计模式
单例singleton
应用程序在使用过程中每一次获取的实例都是同一个,主要是解决内存空间浪费的问题。有些对象全局公用一个即可
单例的的要求
1.构造方法私有
2.提供静态方法能够获得其实例
3.提供一个自身类型的静态成员变量
单例的实现
线程安全的立即加载–>静态成员变量实例化
public class MySingleton { // 静态成员变量直接实例化是什么时候执行的 → 类加载的时候执行的 // 就是在第一次返回实例之前 private static MySingleton mySingleton = new MySingleton(); private MySingleton() {} public static MySingleton getInstance() { //类中的静态方法里能否使用其私有的构造方法 → 可以 // 实例化的代码只执行一次,第一次返回实例之前 //MySingleton mySingleton = new MySingleton(); return mySingleton; } }
线程安全的立即加载–>静态代码块
public class MySingleton2 { // 静态成员变量直接实例化是什么时候执行的 → 类加载的时候执行的 // 就是在第一次返回实例之前 private static MySingleton2 mySingleton; // 静态代码块也是类加载的时候执行 --> 只执行一次 static { mySingleton = new MySingleton2(); } private MySingleton2() { } public static MySingleton2 getInstance() { //类中的静态方法里能否使用其私有的构造方法 → 可以 // 实例化的代码只执行一次,第一次返回实例之前 //MySingleton mySingleton = new MySingleton(); return mySingleton; } }
静态成员变量和静态代码块都是在类加载的时候立即加载,无论如何都只会加载一次,因此可以保证线程安全
线程安全的懒加载–>加锁
public class MySingleton4 { // 就是在第一次返回实例之前 private static MySingleton4 mySingleton; private MySingleton4() { } public static synchronized MySingleton4 getInstance() { //类中的静态方法里能否使用其私有的构造方法 → 可以 // 实例化的代码只执行一次,第一次返回实例之前 //MySingleton mySingleton = new MySingleton(); //我们要在第一次调用方法的时候完成实例化 // 我们如何知道这是第一次调用这个方法 → 有什么特征 if (mySingleton == null) { //它没有实例化 --> 第一次调用方法 mySingleton = new MySingleton4(); } return mySingleton; } }
线程安全的懒加载 --> 静态内部类
解决效率问题就需要不加锁,而保证线程安全需要使用静态的成员变量,因此可以使用静态内部类来完成实例化
public class MySingleton6 { private MySingleton6(){} public static MySingleton6 getInstance() { // 懒加载 → 实例化的代码要在getInstance方法里面 // 内部类提供一个方法让外部类能够获得其实例 return Inner.getInnerInstance(); } static class Inner{ private static MySingleton6 mySingleton6 = new MySingleton6(); /*static { mySingleton6 = new MySingleton6(); }*/ public static MySingleton6 getInnerInstance() { return mySingleton6; } } }
工厂
工厂类一般类名中有Factory
工厂的作用是隐藏实现的细节,获得实例的时候不需要关心实例化的细节过程
简单工厂
- 根据传入的参数生成不同的实例对象
public class Animal2Factory { public Animal2 create(String name) { if ("pig".equals(name)) { return new Pig(); } else if ("sheep".equals(name)) { return new Sheep(); } else if ("chicken".equals(name)) { return new Chicken(); } //开闭原则 return null; } }
工厂方法
将工厂定义为接口,如果需要返回新的类型的实例,去新增一个实现了工厂类接口的实体类
public interface AnimalFactory { //接口中提供规范,提供一个返回值为Animal的方法 public Animal create(); } /** * @author stone * @date 2021/12/23 11:33 */ public class PigFactory implements AnimalFactory{ @Override public Animal create() { return new Pig(); } }
工厂的分类
根据工厂中的生产方法是静态方法还非静态方法来分类
静态工厂里的生产方法是静态方法,实例工厂里的方法是非静态方法
public class MyTest { @Test public void mytest1() { // 要先创建一个工厂的实例 → 实例工厂 Animal2Factory factory = new Animal2Factory(); Animal2 chicken = factory.create("chicken"); System.out.println(chicken instanceof Chicken); //true } @Test public void mytest2() { // 可以直接调用静态方法 → 静态工厂 Animal pig = AnimalFactory.create("pig"); } }
建造者
建造者的侧重点在于提供方法,让你设计细节
public class MoneyBuilder { // 成员变量实例化,当前类执行实例化调用构造方法的时候执行 // 当我们去new了一个moneyBuilder的时候,同时new了一个money Money money = new Money(); // 在set方法和build方法之前执行的 // 如果使用的是同一个builder就意味着是对同一个money做修饰 public void setMoneyType(String type) { money.setType(type); } public void setMoneyFaceValue(int value) { money.setFaceValue(value); } public Money build() { // 要在build方法之前给实例设置参数 // 实例化过程要在build方法之前 return money; } } @Data public class Money { String type; int faceValue; }
代理
匿名内部类:定义一个类,并获得这个类的实例,一般用于直接获取抽象类或者接口
new Interface/AbstractClass(){ //实现里面的方法 }
委托方:需要执行实际方法的类
代理方:帮助委托方做本身要做的事,除此之外还进行了额外的增强
代理的主要作用就是对多个已有的功能做相同的扩展
将需要反复执行的繁琐的内容提取出来,对一个类中所有的方法进行增强
代理的分类
- 静态代理
- 动态代理
- JDK动态代理
- CGlib动态代理
静态代理:代理类需要我们自己写;
动态代理:代理类是动态生成的,不需要我们自己去写
静态代理
儿子给父亲买早餐
委托方:Father
代理方:Son
谁的方法需要被增强,谁就是委托方
代理类中需要包含委托类的成员变量,要么维护一个对象,要么通过继承
代理类中的方法与委托类中定义的方法需要相同:修饰符、返回值、方法名、参数
所以一般使用接口来制定规范
public class Father { public void buyBreakFast() { System.out.println("早餐"); } public void buyLunch() { System.out.println("中餐"); } public void buySupper() { System.out.println("晚餐"); } }
public class Son { //通过维护一个为委托类的对象 Father f = new Father(); public void buyBreakFast() { f.buyBreakFast(); Stronger(); } public void buyLunch() { f.buyLunch(); Stronger(); } public void buySupper() { f.buySupper(); Stronger(); } public void Stronger(){ System.out.println("加个蛋"); } }
动态代理
代理类的方法:
1.委托类的方法
2.增强的方法
动态代理通过InvocationHandler中的invoke方法来调用委托类中的方法,并可以在其中实现自定义的增强
JDK中的动态代理
获得代理对象的步骤:
1.类加载器
2.委托类所实现的接口数组–>委托类必须要实现接口–>代理类实现了和委托类相同的接口–>通过接口保证方法的规范性
3.获取InvocationHandler的实例
通过实现类来获取实例
匿名内部类
4.接收代理对象:使用委托类实现的接口来接收,因为代理类同样也实现了该接口
自定义的Invocation
public class CustomHandler implements InvocationHandler { Father f = new Father(); @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { method.invoke(f,args); System.out.println("来三斤牛肉"); return null; } }
@Test public void myTest1(){ FatherInterface f = (FatherInterface) Proxy.newProxyInstance(Father.class.getClassLoader(), Father.class.getInterfaces(), new CustomHandler()); f.buyBreakFast(); f.buyLunch(); f.buySupper(); f.buyFood("吃鸡"); f.buyFoods("可乐","雪碧"); }
使用代理实例化时需要3个参数:第一个是类加载器,第二个是委托类接口列表,第三个是handler
JDK中实现动态代理的过程:
package proxy.dynamic; import java.lang.reflect.Method; //动态代理类要和委托类实现一样的接口 //模仿jdk来实现自定义的动态代理对象 public class JdkProxy implements FatherInterface { Method m1; Method m2; Method m3; Method m4; Method m5; { try { m1 = Class.forName("proxy.dynamic.Father").getDeclaredMethod("buyBreakFast"); m2 = Class.forName("proxy.dynamic.Father").getDeclaredMethod("buyLunch"); m3 = Class.forName("proxy.dynamic.Father").getDeclaredMethod("buySupper"); m4 = Class.forName("proxy.dynamic.Father").getDeclaredMethod("buyFood",String.class); m5 = Class.forName("proxy.dynamic.Father").getDeclaredMethod("buyFoods",String.class,String.class); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } CustomHandler handler = new CustomHandler(); @Override public void buyBreakFast() { try { handler.invoke(this,m1,null); } catch (Throwable throwable) { throwable.printStackTrace(); } } @Override public void buyLunch() { try { handler.invoke(this,m2,null); } catch (Throwable throwable) { throwable.printStackTrace(); } } @Override public void buySupper() { try { handler.invoke(this,m3,null); } catch (Throwable throwable) { throwable.printStackTrace(); } } @Override public void buyFood(String food) { try { handler.invoke(this,m4,new Object[]{food}); } catch (Throwable throwable) { throwable.printStackTrace(); } } @Override public void buyFoods(String food1, String food2) { try { handler.invoke(this,m5,new Object[]{food1,food2}); } catch (Throwable throwable) { throwable.printStackTrace(); } } }
在使用debug的时候要注意debug会自动调用tostring方法
CGlib动态代理
仍然是使用InvocationHandler来生成代理对象
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.2.12</version> </dependency>
@Test public void test1(){ Father f = new Father(); FatherInterface fProxy = (FatherInterface) Enhancer.create(Father.class, new InvocationHandler() { @Override public Object invoke(Object o, Method method, Object[] objects) throws Throwable { method.invoke(f, objects); System.out.println("我是增强方法"); return null; } }); fProxy.buyFoods("苹果","香蕉"); }
第一个参数仍然是委托类的.class文件,第二个是InvocationHandler,使用匿名内部类来实现
在使用CGlib的使用,接收代理对象可以使用委托类本身,也可以使用接口
一般使用接口,兼容性会更好
代理类的字节码文件
jdk
// 使用main方法,执行过程中会有一个工作路径 public static void main(String[] args) { //通知应用程序把字节码保存起来,默认是不保存 → com.sun.proxy // ProxyGenerator System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");}
生成的代理类
public final class $Proxy0 extends Proxy implements JingTianInterface { private static Method m6; static { try { m6 = Class.forName("com.cskaoyan.bean.JingTianInterface").getMethod("buyDoubleFood", Class.forName("java.lang.String"), Class.forName("java.lang.String")); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } public final void buyDoubleFood(String var1, String var2) throws { try { super.h.invoke(this, m6, new Object[]{var1, var2}); } catch (RuntimeException | Error var4) { throw var4; } catch (Throwable var5) { throw new UndeclaredThrowableException(var5); } } }
cglib
public static void main(String[] args) { //字节码文件也是默认不保存 → value写的是路径 // 和委托类相同的包目录 System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\WorkSpace\\j36_workspace\\codes\\day03-proxy-spring\\demo4-proxy-save"); }
public class JingTian$$EnhancerByCGLIB$$75eacb68 extends JingTian implements Factory { private InvocationHandler CGLIB$CALLBACK_0; private static final Method CGLIB$buyFood$1; static void CGLIB$STATICHOOK1() { CGLIB$buyFood$1 = Class.forName("com.cskaoyan.bean.JingTian").getDeclaredMethod("buyFood", Class.forName("java.lang.String")); } public final void buyFood(String var1) { try { InvocationHandler var10000 = this.CGLIB$CALLBACK_0; if (var10000 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_0; } var10000.invoke(this, CGLIB$buyFood$1, new Object[]{var1}); } catch (Error | RuntimeException var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } }
java.lang.String"));
}
public final void buyFood(String var1) {
try {
InvocationHandler var10000 = this.CGLIBKaTeX parse error: Expected '}', got 'EOF' at end of input: … CGLIBBIND_CALLBACKS(this);
var10000 = this.CGLIBKaTeX parse error: Expected 'EOF', got '}' at position 25: …0; }̲ va…buyFood$1, new Object[]{var1});
} catch (Error | RuntimeException var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
}