设计模式
单例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);
}
}
}