java设计模式初探
java反射机制
我们平常都是通过new一个类来实例化一个对象,但是我们同时可以通过反射机制来构建,并且动态获取类里面的信息 比如说构造函数,方法和属性。
常用的代码像是这个:
方法 | 功能 |
---|---|
Class<?> Class.forName("全类名") | 加载该类对象,并且通过这个方法可以获得类的信息 |
xxx.newInstance() | 实例化一个对象,这个我一般和上面的代码一起用来实例化对象 |
Class.forName("xx").getDeclaredxxx | 获得该类的信息 |
单例模式
- 单利模式的共同点就是将构造函数私有化。
饿汉式
public class UserEntity {
private String username;
private static final UserEntity user = new UserEntity();
static {
System.out.println("静态代码块");
}
private UserEntity() {
System.out.println("默认构造函数");
}
{
System.out.println("非静态代码块");
}
public static UserEntity getInstance(){
return user;
}
public void setUsername(String username){
this.username = username;
}
public String getUsername(){
return username;
}
}
- 通过对该类进行java反射可以知道,static代码块在Class.forName装载的时候开始执行,而实例化的时候非静态代码块和构造函数开始执行
懒汉式
- 实体类在需要的时候才创建。
public class UserEntity {
private String username;
private static UserEntity user;
static {
System.out.println("静态代码块");
}
private UserEntity() {
System.out.println("默认构造函数");
}
{
System.out.println("非静态代码块");
}
/*这里加锁保证线程安全*/
public synchronized static UserEntity getInstance(){
if(user == null){
user = new UserEntity();
}
return user;
}
public void setUsername(String username){
this.username = username;
}
public String getUsername(){
return username;
}
}
静态内部类
public class UserEntity2 {
private UserEntity2() {
}
public static class SingleInstance{
private static final UserEntity2 user = new UserEntity2();
}
public static UserEntity2 getInstance(){
return SingleInstance.user;
}
}
枚举
//使用枚举实现单例模式 优点:实现简单、枚举本身就是单例,由jvm从根本上提供保障!避免通过反射和反序列化的漏洞 缺点没有延迟加载
public class User {
public static User getInstance() {
return SingletonDemo04.INSTANCE.getInstance();
}
private static enum SingletonDemo04 {
INSTANCE;
// 枚举元素为单例
private User user;
private SingletonDemo04() {
System.out.println("SingletonDemo04");
user = new User();
}
public User getInstance() {
return user;
}
}
public static void main(String[] args) {
User u1 = User.getInstance();
User u2 = User.getInstance();
System.out.println(u1 == u2);
}
}
双重检验锁
可以看作是对懒汉式的一个改版
public class SingletonDemo04 {
private SingletonDemo04 singletonDemo04;
private SingletonDemo04() {
}
public SingletonDemo04 getInstance() {
if (singletonDemo04 == null) {
synchronized (this) {
if (singletonDemo04 == null) {
singletonDemo04 = new SingletonDemo04();
}
}
}
return singletonDemo04;
}
}
单例模式的选择
如果不需要延迟加载单例,可以使用枚举或者饿汉式,相对来说枚举性好于饿汉式。
如果需要延迟加载,可以使用静态内部类或者懒汉式,相对来说静态内部类好于懒韩式。
最好使用饿汉式
工厂模式
- 工厂模式将实例化的工作从客户手里夺了回来,Spring的依赖反转也是这个道理
简单工厂模式
- 简单工厂一般代码不会更改,拓展性差
工厂方法模式
- 工厂方法一般是如果产品非常多的情况下,派生出不同的工厂去实例化不同的产品。
抽象工厂
这里分清楚产品族和产品树的关联
在下面的图中,两个产品有关联的实体形成了产品族,一个工厂只生产一个产品族。
产品树可以理解为由一个接口派生出来的类。
模板方法模式
- 这个方法是平时用的蛮多的一个
- 其实模板方法就是将子类都利用的方法在父类中写好。
建造者模式
- Builder 可以根据客户提交的参数自定义产品的零件来产生不同的产品,这也是建造者模式和工厂模式的一个很大的区别。
建造者的应用场景
- 去肯德基,汉堡、可乐、薯条、炸鸡翅等是不变的,而其组合是经常变化的,生成出所谓的"套餐"。
- JAVA 中的 StringBuilder。
代理模式
通过代理控制对象的访问,可以详细访问某个对象的方法,在这个方法调用处理,或调用后处理。SpringAOP就是代理模式
静态代理
静态代理一般都是将代码写死了,在项目中用的也很少,理解即可。
动态代理
静态代理有很大的缺陷,就是如果像让很多类都就行切面,不可能都去写进Proxy里面,这样动态代理就可以解决这个问题。
JDK动态代理
// 每次生成动态代理类对象时,实现了InvocationHandler接口的调用处理器对象
public class InvocationHandlerImpl implements InvocationHandler {
private Object target;// 这其实业务实现类对象,用来调用具体的业务方法
// 通过构造函数传入目标对象
public InvocationHandlerImpl(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
System.out.println("调用开始处理");
result = method.invoke(target, args);
System.out.println("调用结束处理");
return result;
}
public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException,
IllegalAccessException, IllegalArgumentException, InvocationTargetException {
// 被代理对象
IUserDao userDao = new UserDao();
InvocationHandlerImpl invocationHandlerImpl = new InvocationHandlerImpl(userDao);
ClassLoader loader = userDao.getClass().getClassLoader();
Class<?>[] interfaces = userDao.getClass().getInterfaces();
// 主要装载器、一组接口及调用处理动态代理实例
IUserDao newProxyInstance = (IUserDao) Proxy.newProxyInstance(loader, interfaces, invocationHandlerImpl);
newProxyInstance.save();
}
}
CGLib动态代理
需要asm-all和cglib的jar包
public class CglibProxy implements MethodInterceptor {
private Object targetObject;
// 这里的目标类型为Object,则可以接受任意一种参数作为被代理类,实现了动态代理
public Object getInstance(Object target) {
// 设置需要创建子类的类
this.targetObject = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("开启事物");
Object result = proxy.invoke(targetObject, args);
System.out.println("关闭事物");
// 返回代理对象
return result;
}
public static void main(String[] args) {
CglibProxy cglibProxy = new CglibProxy();
UserDao userDao = (UserDao) cglibProxy.getInstance(new UserDao());
userDao.save();
}
}
门面(外观)模式
外观模式,其实就是将后面很复杂的方法封装成一个类里面,让客户直接调用封装类里面的方法即可。
适配器模式
将一个类的接口转换成客户希望的另一个接口。适配器模式让那些接口不兼容的类可以一起工作。
策略模式
现实开发中如果我们由一个根据不同情况创建不同实体的情况下,无论是用if-else或者是swith实现,代码是不符合开闭原则的,也就是他的拓展性并不高。
那么,策略模式,解决了这个问题。其实,他的代码是比之前多了不少,但是这样也同时隐藏后台的核心代码,只将Strategy接口交给了用户,保证了一定的安全性。