Java设计模式分为三大类
创建型模式:对象实例化的模式,创建型模式用于解耦对象的实例化过程。
结构型模式:把类或对象结合在一起形成一个更大的结构。
行为型模式:类和对象如何交互,及划分责任和算法。
工厂模式
工厂顾名思义就是创建产品,根据产品是具体产品还是具体工厂可分为简单工厂模式和工厂方法模式,根据工厂的抽象程度可分为工厂方法模式和抽象工厂模式。
工厂模式的主要解决的问题是,将原来分布在各个地方的对象创建过程单独抽离出来,交给工厂类负责创建。其他地方想要使用对象直接找工厂(即调用工厂的方法)获取对象
简单工厂模式
// Phone类:手机标准规范类(AbstractProduct)
public interface Phone {
void make();
}
// MiPhone类:制造小米手机(Product1)
public class MiPhone implements Phone {
public MiPhone() {
this.make();
}
@Override
public void make() {
// TODO Auto-generated method stub
System.out.println("make xiaomi phone!");
}
}
// IPhone类:制造苹果手机(Product2)
public class IPhone implements Phone {
public IPhone() {
this.make();
}
@Override
public void make() {
// TODO Auto-generated method stub
System.out.println("make iphone!");
}
}
// PhoneFactory类:手机代工厂(Factory)
public class PhoneFactory {
public Phone makePhone(String phoneType) {
if(phoneType.equalsIgnoreCase("MiPhone")){
return new MiPhone();
}
else if(phoneType.equalsIgnoreCase("iPhone")) {
return new IPhone();
}
return null;
}
}
// 演示:
public class Demo {
public static void main(String[] arg) {
PhoneFactory factory = new PhoneFactory();
Phone miPhone = factory.makePhone("MiPhone"); // make xiaomi phone!
IPhone iPhone = (IPhone)factory.makePhone("iPhone"); // make iphone!
}
}
工厂方法模式(Factory Method)
简单工厂
工厂方法模式
也就是定义一个抽象工厂,其定义了产品的生产接口,但不负责具体的产品,将生产任务交给不同的派生类工厂。这样不用通过指定类型来创建对象了。
继续使用生产手机的例子,其中和产品相关的Phone类、MiPhone类和IPhone类的定义不变。
// AbstractFactory类:生产不同产品的工厂的抽象类
public interface AbstractFactory {
Phone makePhone();
}
// XiaoMiFactory类:生产小米手机的工厂(ConcreteFactory1)
public class XiaoMiFactory implements AbstractFactory{
@Override
public Phone makePhone() {
return new MiPhone();
}
}
// AppleFactory类:生产苹果手机的工厂(ConcreteFactory2)
public class AppleFactory implements AbstractFactory {
@Override
public Phone makePhone() {
return new IPhone();
}
}
// 演示:
public class Demo {
public static void main(String[] arg) {
AbstractFactory miFactory = new XiaoMiFactory();
AbstractFactory appleFactory = new AppleFactory();
miFactory.makePhone(); // make xiaomi phone!
appleFactory.makePhone(); // make iphone!
}
}
抽象工厂模式(Abstract Factory)
上面两种模式不管工厂怎么拆分抽象,都只是针对一类产品Phone(AbstractProduct),如果要生成另一种产品PC,应该怎么表示呢?
最简单的方式是把2中介绍的工厂方法模式完全复制一份,不过这次生产的是PC。但同时也就意味着我们要完全复制和修改Phone生产管理的所有代码,显然这是一个笨办法,并不利于扩展和维护。
抽象工厂模式通过在AbstarctFactory中增加创建产品的接口,并在具体子工厂中实现新加产品的创建,当然前提是子工厂支持生产该产品。否则继承的这个接口可以什么也不干。
// PC类:定义PC产品的接口(AbstractPC)
public interface PC {
void make();
}
// MiPC类:定义小米电脑产品(MIPC)
public class MiPC implements PC {
public MiPC() {
this.make();
}
@Override
public void make() {
// TODO Auto-generated method stub
System.out.println("make xiaomi PC!");
}
}
// MAC类:定义苹果电脑产品(MAC)
public class MAC implements PC {
public MAC() {
this.make();
}
@Override
public void make() {
// TODO Auto-generated method stub
System.out.println("make MAC!");
}
}
//下面需要修改工厂相关的类的定义:
// AbstractFactory类:增加PC产品制造接口
public interface AbstractFactory {
Phone makePhone();
PC makePC();
}
// XiaoMiFactory类:增加小米PC的制造(ConcreteFactory1)
public class XiaoMiFactory implements AbstractFactory{
@Override
public Phone makePhone() {
return new MiPhone();
}
@Override
public PC makePC() {
return new MiPC();
}
}
// AppleFactory类:增加苹果PC的制造(ConcreteFactory2)
public class AppleFactory implements AbstractFactory {
@Override
public Phone makePhone() {
return new IPhone();
}
@Override
public PC makePC() {
return new MAC();
}
}
//演示:
public class Demo {
public static void main(String[] arg) {
AbstractFactory miFactory = new XiaoMiFactory();
AbstractFactory appleFactory = new AppleFactory();
miFactory.makePhone(); // make xiaomi phone!
miFactory.makePC(); // make xiaomi PC!
appleFactory.makePhone(); // make iphone!
appleFactory.makePC(); // make MAC!
}
}
单例模式
单例模式是创建对象的一种特殊方式,程序从始至终都只创建一个对象叫单例(单实例)
单例模式分为懒汉式和饿汉式
饿汉式
在类加载时就完成了初始化,所以类加载比较慢,但获取对象的速度快。
public class Student {
//1、 饿汉式单例模式, 在类加载时创建一个对象
private static Student student = new Student();
// 2 构造器私有化
private Student(){
}
// 3 提供返回类对象的静态方法
public static Student getInstance(){
if(student !=null){
return student;
}
return null;
}
}
优点:在类加载的时候就完成了实例化,避免了多线程的同步问题。
缺点:因为类加载时就实例化了,没有达到Lazy Loading (懒加载) 的效果,如果该实例没被使用,内存就浪费了。
懒汉式
线程不安全,不可用
public class Person{
//为了不让其他类直接访问该成员 懒汉式单例,在使用时创建对象
//1、私有静态变量
private static Person person=null;
//2、将构造器私有化
private Person(){
}
//3、提供一个静态方法,并返回该类的对象
public static Person getInstance(){
if(person==null){
//第一次访问
person=new Person();;
}
return person;
}
}
优点:只有在方法第一次被访问时才会实例化,达到了懒加载的效果
缺点:线程不安全,假设对象还没被实例化,然后有两个线程同时访问,那么就可能出现多次实例化的结果。
同步方法的懒汉式
可用
public class Singleton {
private static Singleton instance = null;
private Singleton() {
}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
优点:保证同一时刻只能有一个线程访问并获得实例
缺点:因为synchronized是修饰整个方法,每个线程访问都要进行同步,但这个方法只执行一次实例化代码就够了,每次都同步方法显然效率低下,
双重检查懒汉式
public class Singleton {
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
volatile 的主要作用有两点:- 保证变量的内存可见性 - 禁止指令重排序
静态内部类
(可用,推荐)
public class Singleton {
private Singleton() {}
private static class SingletonInstance {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonInstance.INSTANCE;
}
}
这种静态内部类方式在Singleton类被装载时并不会立即实例化,而是在需要实例化时,调用getInstance方法,才会装载SingletonInstance类,从而完成对象的实例化。
同时,因为类的静态属性只会在第一次加载类的时候初始化,也就保证了SingletonInstance中的对象只会被实例化一次,并且这个过程也是线程安全的。
枚举方法
public enum Singleton {
INSTANCE;
public void doSomething() {
System.out.println("doSomething");
}
}
调用方法:
public class Main {
public static void main(String[] args) {
Singleton.INSTANCE.doSomething();
}
}
直接通过Singleton.INSTANCE.doSomething()的方式调用即可。方便、简洁又安全。
代理模式
通俗的来讲代理模式就是我们生活中常见的中介。
中介隔离作用:在某些情况下,一个客户类不想或者不能直接引用一个委托对象,而代理类对象可以在客户类和委托对象之间起到中介的作用,其特征是代理类和委托类实现相同的接口。
开闭原则,增加功能:代理类除了是客户类和委托类的中介之外,我们还可以通过给代理类增加额外的功能来扩展委托类的功能,这样做我们只需要修改代理类而不需要再修改委托类,符合代码设计的开闭原则。代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后对返回结果的处理等。代理类本身并不真正实现服务,而是同过调用委托类的相关方法,来提供特定的服务。真正的业务功能还是由委托类来实现,但是可以在业务功能执行的前后加入一些公共的服务。例如我们想给项目加入缓存、日志这些功能,我们就可以使用代理类来完成,而没必要打开已经封装好的委托类。
静态代理
静态代理:由程序员创建或特定工具自动生成源代码;动态代理:程序运行时通过反射机制动态创建。
静态代理优点:可以做到在符合开闭原则的情况下对目标对象进行功能扩展。
静态代理缺点:我们得为每一个服务都得创建代理类,工作量太大,不易管理。同时接口一旦发生改变,代理类也得相应修改。
// 第一步:创建服务类接口
package main.java.proxy;
public interface BuyHouse {
void buyHosue();
}
// 第二步:实现服务接口
import main.java.proxy.BuyHouse;
public class BuyHouseImpl implements BuyHouse {
@Override
public void buyHosue() {
System.out.println("我要买房");
}
}
// 第三步:创建代理类
package main.java.proxy.impl;
import main.java.proxy.BuyHouse;
public class BuyHouseProxy implements BuyHouse {
private BuyHouse buyHouse;
public BuyHouseProxy(final BuyHouse buyHouse) {
this.buyHouse = buyHouse;
}
@Override
public void buyHosue() {
System.out.println("买房前准备");
buyHouse.buyHosue();
System.out.println("买房后装修");
}
}
// 第四步:编写测试类
import main.java.proxy.impl.BuyHouseImpl;
import main.java.proxy.impl.BuyHouseProxy;
public class ProxyTest {
public static void main(String[] args) {
BuyHouse buyHouse = new BuyHouseImpl();
buyHouse.buyHosue();
BuyHouseProxy buyHouseProxy = new BuyHouseProxy(buyHouse);
buyHouseProxy.buyHosue();
}
}
动态代理
// 第一步:编写动态处理器
package main.java.proxy.impl;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class DynamicProxyHandler implements InvocationHandler {
private Object object;
public DynamicProxyHandler(final Object object) {
this.object = object;
}
@Override
public Object invoke(Object object, Method method, Object[] args) throws Throwable {
System.out.println("买房前准备");
Object result = method.invoke(object, args);
System.out.println("买房后装修");
return result;
}
}
// 第二步:编写测试类
package main.java.proxy.test;
import main.java.proxy.BuyHouse;
import main.java.proxy.impl.BuyHouseImpl;
import main.java.proxy.impl.DynamicProxyHandler;
import java.lang.reflect.Proxy;
public class DynamicProxyTest {
public static void main(String[] args) {
BuyHouse buyHouse = new BuyHouseImpl();
BuyHouse proxyBuyHouse = (BuyHouse) Proxy.newProxyInstance(BuyHouse.class.getClassLoader(), new
Class[]{BuyHouse.class}, new DynamicProxyHandler(buyHouse));
proxyBuyHouse.buyHosue();
}
}
CGLIB代理
JDK实现动态代理需要实现类通过接口定义业务方法,对于没有接口的类,需要CGLib
CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。但因为采用的是继承,所以不能对final修饰的类进行代理。
JDK动态代理与CGLib动态代理均是实现Spring AOP的基础。
CGLIB代理总结: CGLIB创建的动态代理对象比JDK创建的动态代理对象的性能更高,但是CGLIB创建代理对象时所花费的时间却比JDK多得多。所以对于单例的对象,因为无需频繁创建对象,用CGLIB合适,反之使用JDK方式要更为合适一些。同时由于CGLib由于是采用动态创建子类的方法,对于final修饰的方法无法进行代理。
// 第一步:创建CGLIB代理类
package dan.proxy.impl;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CglibProxy implements MethodInterceptor {
private Object target;
public Object getInstance(final Object target) {
this.target = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("买房前准备");
Object result = methodProxy.invoke(object, args);
System.out.println("买房后装修");
return result;
}
}
// 第二步:创建测试类
package dan.proxy.test;
import dan.proxy.BuyHouse;
import dan.proxy.impl.BuyHouseImpl;
import dan.proxy.impl.CglibProxy;
public class CglibProxyTest {
public static void main(String[] args){
BuyHouse buyHouse = new BuyHouseImpl();
CglibProxy cglibProxy = new CglibProxy();
BuyHouseImpl buyHouseCglibProxy = (BuyHouseImpl) cglibProxy.getInstance(buyHouse);
buyHouseCglibProxy.buyHosue();
}
}
装饰模式
动态地给一个对象添加一些额外的功能,装饰模式比生成子类更灵活,是继承关系的一个替代方案。
模板方法
模板方法是一种行为模式,父类的一个方法定义完成这个方法的步骤,但不具体实现具体细节,由子类完成各个步骤的实现,在创建子类对象时,最终实现过程是子类的方法。
模板方法的准备:
1、继承关系
2、父类是抽象类:抽象类实现了模板方法,定义了算法的估计
3、子类继承抽象类:实现抽象方法,完成完整的算法
public abstract class AbstractPerson {
/**
* 定义一个模板方法,用于实现这个方法的基本“骨架”
* 每一步骤的具体实现由子类完成
*/
public void preparedSchool(){
getUp();
dressing();
eat();
}
//起床
public abstract void getUp();
//穿衣服
public abstract void dressing();
//吃早餐
public abstract void eat();
}
public class Student extends AbstractPerson {
@Override
public void getUp() {
System.out.println("学生起床,起不来,闹钟响3次");
}
@Override
public void dressing() {
System.out.println("学生穿衣服,找不到衣服");
}
@Override
public void eat() {
System.out.println("学生吃早餐,来不及吃早餐");
}
}
public class Teacher extends AbstractPerson {
@Override
public void getUp() {
System.out.println("老师起床,7点半起床");
}
@Override
public void dressing() {
System.out.println("老师要衣服得体,穿工装");
}
@Override
public void eat() {
System.out.println("老师吃早餐。");
}
}
public class Test1 {
public static void main(String[] args) {
Student stu = new Student();
stu.preparedSchool();
Teacher teacher = new Teacher();
teacher.preparedSchool();
}
}