1、设计模式的七大原则
(1)开闭原则:我们在设计软件系统时,考虑到后续功能的扩展和维护,需要使得设计的系统对扩展开放,对修改关闭,也就是在不修改原有软件的基础上可以轻松实现功能的扩展。
(2)接口隔离原则:我们在设计一个接口的时候,如果发现接口中的方法要么被同时使用,要么就同时不被使用,就需要考虑使用子接口去拆分这些接口,采用多个隔离的子接口去取代一个统一的接口,这样可以降低类与类之间的耦合度。
(3)单一职责原则:即一个类应该只负责一个功能职责,如果一个类A中有两种不同的职责,当一个职责需求改变的时候,可能会引起另外的职责的功能出错,因此需要将这个类进行细粒度的划分,拆解为两个类分别管理职责。
(4)依赖倒转:依赖倒转的核心思想就是面向接口编程,具体就是抽象的结构不应该依赖具体的细节,而是具体的细节依赖抽象,比如,我们在一个类中需要定义一个接口A的实现类的对象,可以定位为:A a = new A_implement();因为抽象的东西相比于细节的东西更稳定,以抽象为基础搭建的系统会比基于细节搭建的系统更加稳健。
(5)里氏替换原则:里氏替换原则说明,继承实际上会让两个类之间的耦合度增强,在适当的情况下使用聚合、组合、依赖等来解决问题。
(6)迪米特法则:迪米特法则又叫最少知道原则,也就是一个类A依赖于另外一个类B,那么类A对于类B的具体的实现细节应该知道的越少越好,不管被依赖的类有多复杂,都将逻辑封装在自己的类内部,不要对外暴露。
2、java中的设计模式总共有23种,可以分为三种类型。
(1)创建型模式:单例模式,工厂模式,原型模式,建造者模式
(2)结构型模式:代理模式,适配器模式,装饰器模式,外观模式,桥接模式,组合模式,享元模式
(3)行为型模式:观察者模式,命令模式,职责链模式,策略模式,访问者模式,中介者模式,解释器模式,备忘录模式,迭代器模式,模板方法模式
3、代理模式有哪些?
代理模式分为静态代理和动态代理,动态代理又可以分为JDK代理和CGLIB代理。
(1)静态代理
静态代理中,代理类和被代理类需要实现相同的接口,并去重写里面的方法。在静态代理中,代理类需要在内部维护一个被代理类的对象,实现对这个被代理类的管理。
代理类实现:
//代理对象,静态代理
public class TeacherDaoProxy implements ITeacherDao{
private ITeacherDao target; // 目标对象,通过接口来聚合
//构造器
public TeacherDaoProxy(ITeacherDao target) {
this.target = target; //在代理类构造器中对被代理类对象进行初始化操作
}
@Override
public void teach() { //代理类和被代理类实现了相同的接口,则重写相同的方法
// TODO Auto-generated method stub
System.out.println("开始代理 完成某些操作。。。。。 ");//方法
target.teach();
System.out.println("提交。。。。。");//方法
}
}
被代理类实现:
public class TeacherDao implements ITeacherDao {
@Override
public void teach() {
// TODO Auto-generated method stub
System.out.println(" 老师授课中 。。。。。");
}
}
(2)jdk代理
jdk代理和静态代理一样,都是基于接口实现进行代理的,只不过jdk动态代理不像静态代理一样,要求我们自己写一个实际的代理类并去实现一个确定的接口(因为每一个类都去写一个代理类的话会造成类泛滥),而是通过编写一个代理工厂(ProxyFactory)去根据你的需求返回一个定制的代理类对象,体现出动态性,也就是需要通过反射机制实现动态代理,在jdk代理中有两个关键的类/接口,Proxy类和InvocationHandler接口。
代理工厂实现:
public class ProxyFactory {
private Object target; //这个是我们需要维护的一个被代理类对象,不管是什么代理方法,都是需要维护一个被代理对象的
public ProxyFactory(Object target){
this.target = target; //在代理工厂构造器中对这个被代理类对象进行初始化
}
//用于获取代理对象的方法
public Object getProxyInstance(){
MyInvocationHandler handler = new MyInvocationHandler(); //这个就是关键的Invocationhandler接口的实现
handler.band(target);
//通过Proxy类中的newProxyInstance方法生成代理对象proxyInstance,该方法需要传入一个类加载器、被代理类所实现的接口以及一个处理器handler
Object proxyInstance = Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),handler);
return proxyInstance;
}
//这个handler类需要我们自己去编写并实现InvocationHandler接口
class MyInvocationHandler implements InvocationHandler{
private Object object; //同样,这个handler对象也需要持有对应的被代理类对象
public void band(Object obj){
object = obj; //这个方法用于设置被代理类
}
//重写接口中的invoke方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO 自动生成的方法存根
System.out.println("JDK动态代理开始");
Object returnVal = method.invoke(target, args); //这里的target是被代理类对象
System.out.println("后续处理");
return returnVal;
}
}
}
(3)cglib代理
cglib代理和jdk代理一样,都是动态代理方式,只不过cglib代理不要求被代理对象一定要实现某个接口,底层都是依靠反射机制完成对代理对象的创建并返回的。cglib代理依赖于两个重要的类/接口,一个是Enhancer类和MethodIntercepter接口。
代理工厂实现:
public class ProxyFactory implements MethodInterceptor {
//维护一个被代理的对象
private Object target;
public ProxyFactory(Object target){
this.target = target;
}
//如果想要使用cglib动态代理,就需要让生成代理对象的工厂实现方法拦截器,并且充写里面的intercepter方法
@Override
public Object intercept(Object arg0, Method method, Object[] args, MethodProxy arg3) throws Throwable {
// TODO 自动生成的方法存根
System.out.println("cglib动态代理开始。。。");
Object returnVal = method.invoke(target, args);
System.out.println("cglib动态代理结束");
return returnVal;
}
public Object getProxyInstance(){
//生成一个工具类对象
Enhancer enhancer = new Enhancer();
//使用工具类对象设置父类
enhancer.setSuperclass(target.getClass());
//使用工具设置回调函数
enhancer.setCallback(this);
//使用工具类对象创建代理对象
return enhancer.create();
}
}
4、单例模式
单例模式指的是在程序运行期间,始终只存在唯一一个当前类型的对象。单例模式的实现分为懒汉式和饿汉式两种方式,并且单例模式的类的构造器是private私有化的,也就是不允许在外部构造新的对象,对象的构造只允许在类的内部进行,并对外暴露一个静态的公有方法,返回在类内部创建的单例对象。
(1)饿汉式
饿汉式指的是在类的字节码文件进行加载的时候就创建一个对象,以后在需要使用到这个对象的时候,只需要调用静态的公有方法就可以返回这个对象。饿汉式属于线程安全的实现 方式,因为整个对象的创建过程只在类加载时进行,而jvm在进行类加载时,自动就是线程安全的。饿汉式的实现方式有两种:静态变量和静态代码块
静态变量实现:
//饿汉式(静态变量)
class Singleton {
//1. 构造器私有化, 外部能new
private Singleton() {
}
//2.本类内部创建对象实例
private final static Singleton instance = new Singleton();
//3. 提供一个公有的静态方法,返回实例对象
public static Singleton getInstance() {
return instance;
}
}
静态代码块实现:
class Singleton {
//1. 构造器私有化, 外部不能new
private Singleton() {
}
//2.本类内部创建对象实例
private static Singleton instance;
static { // 在静态代码块中,创建单例对象
instance = new Singleton();
}
//3. 提供一个公有的静态方法,返回实例对象
public static Singleton getInstance() {
return instance;
}
}
(2)懒汉式
懒汉式指的是在类加载的时候并不进行对象的创建,而是在需要用到这个单例对象的时候才去创建,因此在多线程的条件下,懒汉式需要解决多线程带来的问题,因为多个线程操作,可能造成这个单例模式设计的类并不是单例类。懒汉式实现线程安全的方式有:同步方法、双重检查
同步方法实现:
// 懒汉式(线程安全,同步方法)
class Singleton {
private static Singleton instance;
private Singleton() {}
//提供一个静态的公有方法,加入同步处理的代码,解决线程安全问题,但是将同步的机制放在返回单例对象的方法上面,造成多线程条件下并发效率低下的问题
//即懒汉式
public static synchronized Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}
双重检查方式实现:
// 懒汉式(线程安全,双重检查)
class Singleton {
private static volatile Singleton instance;
private Singleton() {}
//提供一个静态的公有方法,加入双重检查代码,解决线程安全问题, 同时解决懒加载问题
//同时保证了效率, 推荐使用
public static Singleton getInstance() {
//双重检查实现线程安全,并且保证懒加载和效率问题
if(instance==null){ //在第一次进来时,如果线程A,B都进入这个if判断,看谁先拿到锁,如果线程A拿到锁的话,线程B等待
//线程A进入同步代码块,判断当前instance是否等于null,此时等于null,完成instance的初始化,退出同步代码快并且返回instance,此时A
//的getInstance动作完成,由于A释放了锁此时B也可以进入同步代码块,由于我们将instance这个变量设置为volatile修饰(这个
//代表一旦变量被修改就马上保存到内存,即实时更新),所以B进入同步代码块中进行if判断的时候,发现这个instance不是null,就直接返回。
//当后续还有线程来的时候,第一个if判断就进不了,直接返回对象。
//这里使用Singleton.class这个Class对象充当锁的作用
synchronized(Singleton.class){
if(instance==null){
instance = new Singleton();
}
}
}
return instance;
}
}
静态内部类实现方式:
// 静态内部类完成, 推荐使用
class Singleton {
private Singleton(){
}
/*
* 采用静态内部类的方式之所以能够实现单例,就是因为在静态内部类中已经被创建好了,没有后续的new对象的动作
* 并且实现了懒加载,因为jvm在加载外部类的时候,不会加载静态内部类,只有当调用getInstance的时候 。jvm才会
* 去加载静态内部类并且完成里面静态常量的初始化,但是是如何实现线程安全的呢?这是利用了jvm加载的线程安全特性,就是
* 在jvm加载的时候,属于线程安全的,所以里面的instance也只能被new一次。
*
* */
//定义一个静态内部类,里面包含一个静态的Singleton常量
private static class SingletonInstance{
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance(){
return SingletonInstance.INSTANCE;
}
}
枚举类实现方式:
//使用枚举,可以实现单例, 推荐
enum Singleton{
INSTANCE;
}