java设计模式
java设计模式类型
根据完成的工作类型设计模式分为创建型模式、结构型模式和行为型模式。
创建型模式:是用来创建对象的模式,特点是将对象的创建和使用分离。有单例、原型、工厂方法、抽象工厂、建造者5中创建型模型
结构型创建模式:是用来将类或对象按某种布局组成更大的结构,有代理、桥接、组合等7种结构性模式
行为型模式:是用来将类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,以及怎样分配职责。。提供了模板方法、策略、命令、职责链、状态、观察者、中介者、迭代器、访问者、备忘录、解释器 11 种行为型模式。
常用设计模式
单例模式
为了节省资源、保证数据内容的一致性,某些类只能创建一个实例,这就是单例模式
单例模式的特点:只有一个实例对象、该单例对象必须由单例类创建、单例类提供一个访问方法可以让外部类进行获取。
单例模式的两种创建方式
饿汉式单例
类一旦加载就会创建一个实例,不会存在线程安全问题
private static Demo demo=new Demo();
private Demo(){
}
public Demo getInstance(){
return demo;
}
懒汉式单例
类加载时没有创建单例对象,只有第一次调用getInstance方法时才会去创建这个单例
private static Demo demo;
private Demo(){
}
public Demo getInstance(){
if(demo==null){
demo=new Demo();
}
return demo;
}
这种方式存在线程安全问题,当多个线程同时访问时,会出现创建多个对象的情况。
优化:
//这种方式虽然安全,但是效率低
public synchronized Demo getInstance(){
if(demo==null){
demo=new Demo();
}
return demo;
}
//这种方式是线程安全的,效率也高,但是在执行过程中,可能会出现代码重排序的问题
public Demo getInstance(){
//这里使用双重判断,第一个判断语句,是为了判断当线程进来时是demo是否被创建。
if(demo==null){
synchronized (Demo.class){
//这个判断语句主要是判断对象未创建时第一次进来的请求进入时的判断。
if(demo==null){
demo=new Demo();
}
}
}
return demo;
}
//最终优化 使用volatile和双重检索
private static volatile Demo demo;
public Demo getInstance(){
if(demo==null){
synchronized (Demo.class){
if(demo==null){
demo=new Demo();
}
}
}
return demo;
}
Jdk 中的源码 Runtime 类就是一个单例类,利用 Runtime 类可以启动新的进程或进行相关运行时环境的操作。比如,取得内存空间以及释放垃圾空间。
工厂模式
工厂负责批量创建对象,使用对象时,只需要找到相对应的工厂
简单工厂模式
不符合开闭原则,适合长聘子类少的、创建操作简单的情况
该模式包含的角色:
工厂角色:负责创建所有实例的内部逻辑,提供静态方法,可以被外界调用,创建产品对象。
抽象产品角色:是所有对象的父类,可以时接口或抽象类
具体产品角色:是简单工厂模式的创建目标。
优点:实现的对象创建和调用的分离。
缺点:违背了开闭原则,产品子类过多会导致工厂类非常庞大,违背了高内聚原则‘
//汽车类 抽象产品角色
public interface Car {
void run();
}
//奥迪类 具体产品角色
public class Aodi implements Car{
@Override
public void run() {
System.out.println("奥迪汽车行驶");
}
}
//宝马类 具体产品角色
public class Bmw implements Car{
@Override
public void run() {
System.out.println("宝马汽车行驶");
}
}
// 汽车工厂 工厂角色
public class CarFactory {
public static Car createCar(String name){
if(name.equals("aodi")){
Aodi aodi = new Aodi();
//aodi.
return aodi;
}
if(name.equals("bmw")){
return new Bmw();
}
return null;
}
}
工厂方法模式
在简单工厂模式的基础上对工厂也进行了抽象,解决了违背开闭原则的问题
该模式中包含的角色及其职责.
抽象工厂角色:工厂方法模式的核心,与应用程序无关。任何在模式中创建的对
象的工厂类必须实现这个接口。
具体工厂角色:这是实现抽象工厂接口的具体工厂类,包含与应用程序密切相关
的逻辑,并且受到应用程序调用以创建产品对象。
抽象产品角色:工厂方法模式所创建的对象的父类型,也就是产品对象的共同父
类或共同拥有的接口。
具体产品角色:这个角色实现了抽象产品角色所定义的接口。某具体产品有专门
的具体工厂创建,它们之间往往——对应。
//抽象工厂类
public interface CarFactory {
Car createCar();
}
//具体工厂角色
public class AodiFactory implements CarFactory{
@Override
public Car createCar() {
return new AodiCar();
}
}
public class BCFactroy implements CarFactory{
@Override
public Car createCar() {
return new BC();
}
}
//抽象产品角色
public interface Car {
void run();
}
//具体产品角色
public class AodiCar implements Car{
@Override
public void run() {
System.out.println("奥迪汽车行驶");
}
}
public class BC implements Car{
@Override
public void run() {
System.out.println("奔驰");
}
}
抽象工厂模式
抽象工厂模式中,一个具体的工厂负责创建一系列相互关联的产品。
//抽象工厂类
public interface AbstractFactory {
Car getCar();
Phone getPhone();
}
//具体工厂角色
public class AodiFactory implements AbstractFactory{
@Override
public Car getCar() {
return new AodiCar();
}
@Override
public Phone getPhone() {
return new AodiPhone();
}
}
//抽象产品角色
public interface Phone {
void call();
}
public interface Car {
void run();
}
//具体产品角色
public class AodiCar implements Car{
@Override
public void run() {
System.out.println("奥迪汽车行驶");
}
}
public class AodiPhone implements Phone{
@Override
public void call() {
System.out.println("奥迪手机打电话");
}
}
优点:获取具体系列产品只需要通过具体系列工厂获取,无序关心创建的细节。
原型模式
原型模式是指创建一个对象,并且通过这个对象作为原型来创建新的对象,相当于复制多个具有相同信息的对象。
实现方式:使用对象克隆的方式创建新的对象。
代理模式
代理模式就是给对象提供一个代理,通过代理对象来对原对象进行控制。是的客户不能直接和对象进行交互,只能通过代理对象来进行操作。
案例:买火车票不一定要去火车站买,可以通过 12306 网站或者去火车票代售点
优点:保护目标对象、代理对象可以扩展目标对象的功能,代理模式将客户端与目标分离,降低系统的耦合度。
代理模式结构
1.抽象主题类:使用接口和抽象类声明真实主题类和代理对象的业务方法
2.真实主题类:实现抽象主题中的业务,是目标对象是最终要引用的对象。
3.代理类:提供与真实主题相同的结构,引用了真实主题,它可以访问、控制和扩展真实主题的功能
1.png)]
代理分为两种:动态代理和静态代理
静态代理
静态代理的特点。代理类接受一个抽象主题接口,实现任何该接口的对象,都可以通过代理类进行代理。
优点:可以对目标对象进行功能扩展
缺点:一个代理类只能代理一个接口,必须现有接口再有代理,接口发生改变,代理类也要修改
以买票为例
//抽象主题 买票
public interface Ticket {
void buy();
}
//真实主题 目标对象类
public class TrainTicket implements Ticket {
@Override
public void buy() {
System.out.println("买火车票");
}
}
public class PlaneTicket implements Ticket {
@Override
public void buy() {
System.out.println("买飞机票");
}
}
//代理类
public class MeiTuan implements Point {
Ticket ticket;
public MeiTuan(Ticket ticket) {
this.ticket= ticket;
}
@Override
public void buy() {
System.out.println("添加额外功能");
ticket.buy();
}
}
//测试类
public class Test {
public static void main(String[] args) {
//具体对象
Ticket trainTicket = new TrainTicket();
Ticket planeTicket = new PlaneTicket();
//创建用户代理对象
Ticket ticket = new Ticket(trainTicket);
ticket.buy();
Ticket ticket1 = new Ticket(planeTicket);
ticket1.buy();
}
}
动态代理
在动态代理中不需要手动的创建类,只需要编写一个动态的处理器,代理对象在运行时动态的来创建。
动态代理分为jdk动态代理和cglid动态代理
jdk代理
jdk动态代理是通过反射来实现的,借助java自带的java.lang.reflect.Proxy生成。
实现步骤:
1.编写一个委托类的接口
2.实现一个委托类
3.创建一个动态代理类,实现现 InvocationHandler 接口,并重写该 invoke
方法。
4.在测试类中,生成动态代理的对象。
实现步骤的前两步和静态代理没有区别。
/*
动态代理类
代理类不需要实现与目标类相同的接口,这样就可以代理任意的目标类
但是是有要求的,目标类必需实现接口,此种方式是动态代理的实现方式之一: jdk代理 是一种纯反射机制实现(动态获取目标类接口方法)
*/
public class DynamicProxy implements InvocationHandler {
Object object;//真实对象,接收任何的目标类对象
public DynamicProxy(Object object) {
this.object = object;
}
/*
在代理类中调用目标类中的具体方法,
动态的将代理动态对象,目标类中要调用的方法,及方法中的参数传递过来
Method method 就是动态获取的真正要执行的方法
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("之前开启事务");
method.invoke(object);
System.out.println("之后提交事务");
return proxy;
}
//真正意义上,运行时生成代理对象的方法
public Object getProxy(){
return Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),this);
}
}
//测试类
public class Test {
public static void main(String[] args) {
TrainTicket trainTicket = new TrainTicket();
DynamicProxy dtproxy = new DynamicProxy(trainTicket);//自己创建的代理类对象
//这才是真正的创建动态代理对象
Ticket ticket = (Train)dtproxy.getProxy();
userDao.saveUser();//使用代理对象调用接口中的方法,获取当前调用的方法,最终调用invoke方法
}
}
jdk动态代理,减少了对接口业务的依赖,降低了耦合度,但是jdk动态代理有一个缺点就是他必须要创建一个代理接口。
Cglib代理
Cglib代理解决了jdk动态代理的缺点。CGLIB(Code Generator Library)是一个强大的、高性能的代码生成库。CGLib 采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。
Cglib 子类代理实现方法:
1.需要引入 cglib 的 jar 文件,但是 Spring 的核心包中已经包括了 Cglib 功能,所以直接引入 spring-core-xxx.jar 即可
2.引入功能包后,就可以在内存中动态构建子类
3.代理的类不能为 final,否则报错
4.目标对象的方法如果为 final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法.
CGLIB 创建的动态代理对象比JDK 创建的动态代理对象的性能更高,但是 CGLIB创建代理对象时所花费的时间却比 JDK 多得多。所以对于单例的对象,因为无需频繁创建对象,用 CGLIB 合适,反之使用 JDK 方式要更为合适一些。同时由于 CGLib 由于是采用动态创建子类的方法,对于 final 修饰的方法无法进行代理。
/*
* 动态代理类
*/
public class CGLibProxy implements MethodInterceptor {
private Enhancer enhancer = new Enhancer();
public Object getProxy(Class<?> clazz){
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return enhancer.create();
}
/*
* 拦截所有目标类方法的调用
* 参数:
* obj 目标实例对象
* method 目标方法的反射对象
* args 方法的参数
* proxy 代理类的实例
*/
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
//代理类调用父类的方法
System.out.println("开始事务");
Object obj1 = proxy.invokeSuper(obj, args);
System.out.println("关闭事务");
return obj1;
}
}
//具体主题
public class UserDaoImpl{
public void save() {
System.out.println("UserDaoImpl:save()");
}
}
//测试类
public class Test {
public static void main(String[] args) {
CGLibProxy proxy = new CGLibProxy();
UserDaoImpl userDaoImpl = (UserDaoImpl) proxy.getProxy(UserDaoImpl.class);
userDaoImpl.save();
}
}