单例模式
1 单例模式 只允许创建一个对象,因此节省内存,加快对象访问速度,因此对象需要被公用的场合适合使用,如多个模块使用同一个数据源连接对象等等
2 单例的缺点 就是不适用于变化的对象,如果同一类型的对象总是要在不同的用例场景发生变化,单例就会引起数据的错误,不能保存彼此的状态。
用单例模式,就是在适用其优点的状态下使用
单例模式是非常常见的模式,因为是单例的所以不存在耦合问题,例如Struts2使用的就是单例模式
单例模式的含义:
通过单例模式可以保证系统中该模式的这个类永远只有一个实例,即一个类永远只有一个对象实例
单例是为了节约内存;
实现单例模式的方式
饿汉单例设计模式:
在类获取对象时,对象已经提前创建好;
设计步骤:
a、私有化构造器
b、定义一个静态变量存储一个对象并赋值
c、提供获取该类的方法
public class Singleton01 {
//定义一个静态变量存储一个对象
private static final Singleton01 INSTANCE = new Singleton01();
//私有化构造器
private Singleton01(){}
//提供返回该对象的方法
public static Singleton01 getInstance(){
return INSTANCE;
}
}
懒汉单例设计模式:
真正需要改对象时再去创建这个对象;
设计步骤
a、私有化构造器
b、定义一个静态变量存储一个对象不赋值;
c、提供获取该类的方法
public class Singleton02 {
//定义一个静态变量存储一个对象
private static Singleton02 instance02;
//私有化构造器
private Singleton02(){}
//提供返回该对象的方法
public static Singleton02 getInstance02(){
if (instance02 == null){
instance02 = new Singleton02();
}
return instance02;
}
}
动态代理
动态代理简单来说是:拦截对真实对象方法的直接访问,增强真实对象方法的功能
代理类:
java.lang.reflect.Proxy;
具体创建并返回代理对象的方法:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
- 参数一:类加载器,负责加载做好的代理对象返回!!
- 参数二:获取当前被代理对象实现的全部接口,以便知道有哪些功能需要被代理起来。方法
- 参数三:代理的核心处理功能。
动态代理非常的灵活,可以为任意接口和接口实现类对象做代理,
动态代理可以为被代理对象的所有接口的所有方法做代理,动态代理可以在不改变方法源码的情况下,实现对方法功能的增强,
动态代理类的字节码在程序运行时右Java发射机制动态生成,无需程序员手工编写他的源代码。
动态代理不仅简化了编程工作,而且提高了软件系统的可扩展性,因为Java反射机制可以生成任意类型的动态代理。
动态代理同时也提高了开发效率
缺点,只能针对接口或者接口的实现类做代理对象,普通类是不能做代理对象的。
动态代理工具类:
jdk动态代理:
public static <T> T getProxy(Object obj){
/**
* 参数一:类加载器,负责加载做好的代理对象返回!!
* 参数二:获取当前被代理对象实现的全部接口,以便知道有哪些功能需要被代理起来。方法
* 参数三:代理的核心处理功能。
*/
return (T) Proxy.newProxyInstance(obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// proxy :当前代理对象,我们不会用到。
// method:当前业务对象正在执行的方法,就是需要被代理的方法。
// args:当前业务对象正在执行方法的入参,就是需要被代理的方法的参数!
long startTimer = System.currentTimeMillis();
// 真正通知该方法执行。
Object rs = method.invoke(obj,args);
long endTimer = System.currentTimeMillis();
System.out.println(method.getName()+"方法耗时:"+(endTimer - startTimer)/1000.0 + "s"));
// 返回方法执行的结果!!
return rs;
}
});
}
cglib动态代理:
导入依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
官方API:
public static Object create(
// 1. 代理对象需要继承的类
java.lang.Class superclass,
// 2. 代理对象需要实现的接口
java.lang.Class[] interfaces,
// 3. 代理对象方法调用时的处理器 InvocationHandler
Callback[] callbacks
)
工具类:
public static <T> T getProxy(final Object obj){
/**
* 参数一:/需要继承的实现类!
* 参数二:代理类需要实现的接口数组 (可传)
* 参数三:代理对象方法调用的处理器。
*/
return (T) Enhancer.create(obj.getClass(),
new Class[]{/*obj.getClass().getInterfaces() */}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// proxy :当前代理对象,我们不会用到。
// method:当前业务对象正在执行的方法,就是需要被代理的方法。
// args:当前业务对象正在执行方法的入参,就是需要被代理的方法的参数!
long startTimer = System.currentTimeMillis();
// 真正通知该方法执行。
Object rs = method.invoke(obj,args);
long endTimer = System.currentTimeMillis();
System.out.println(method.getName()+"方法耗时:"+(endTimer - startTimer)/1000.0 +"s");
// 返回方法执行的结果!!
return rs;
}
});
}
两种动态代理的区别
JDK | CGLIB | |
---|---|---|
实现方式 | 利用 实现 接口方法来拦截目标方法 | 利用 继承 覆写方法来拦截目标方法 |
性能高低 | JDK1.6以前较Cglib慢; 1.6以及1.7大量调用时较慢 | 1.8时被JDK代理超越 |
适用场景 | 目标对象必须实现接口 | 目标方法需要允许覆写(不能用final/static) |
装饰模式(静态代理):
装饰模式就是指在不改变原类,动态地扩展一个类的功能
思想
- 创意一个新类,包装原始类,从而在新类中提升原来类的功能
例子:
BufferedInputStream,BufferedOutputStream,BufferedReader,BufferedWriter等使用的都是装饰模式;
包装类
public class BufferedInputStrem extends InputStream {
private InputStream is ;
public BufferedInputStrem(InputStream is){
this.is = is;
}
@Override
public void read() {
System.out.println("方法增强~");
is.read();
}
@Override
public void close() {
is.close();
}
}
public class Demo {
public static void main(String[] args) {
InputStream is = new BufferedInputStrem(new FileInputStream());
is.read();
is.close();
}
}
工厂模式:
Java中常见的设计模式,他提供了一种新的创建对象的方式——对象由工厂创建,不再需要new对象
作用
对象通过工厂的方法创建返回,工厂的方法可以为该对象进行加工和数据注入
可以实现类与类之间的解耦(核心思想)
例子
spring的IOC使用的就是工厂模式,spring中的ApplicationContext.xml实际上就是一个容器(也就是工厂)
public class FactoryPattern {
public static Animal getAnimal() {
Class c = Cat.class;
Animal animal = null;
try {
Constructor constructor = c.getDeclaredConstructor();
animal = (Animal) constructor.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return animal;
}
}
public class FactoryDemo {
public static void main(String[] args) {
Animal animal = FactoryPattern.getAnimal();
animal.run();
}
}