java设计模式

Java 设计模式(java design patterns)

在前辈们长期开发的过程中,为解决某一类问题,总结出的一种较为优化的代码设计结构.

提高程序代码复用,扩展性,稳定性.

创建型模式、结构型模式和行为型模式 3 种

创建型模式: 主要负责创建对象 单例,工厂

单例模式

在整个程序中只允许有一个对象.

创建对象 使用构造方法. 将构造方法私有化,

只能在本类中创建,这样我们就可以控制数量.

向外界提供一个公共的访问方法.

饿汉式单例 一般又称为急切式单例
在类加载时,就会创建此单例对象,这种写法不会出现线程安全问题

/*
 * 饿汉式单例
 */
public class Singleton {
	
	   //创建 Singleton 的一个对象
	   private static Singleton instance = new Singleton();
	 
	   //让构造函数为 private
	   private Singleton(){}
	 
	   //获取唯一可用的对象
	   public static Singleton getInstance(){
	      return instance;
	   }
}

//测试
public class Test {
	 public static void main(String[] args) {
		 
	      //获取唯一可用的对象
		 Singleton object1 = Singleton.getInstance();
		 Singleton object2 = Singleton.getInstance();
		 Singleton object3 = Singleton.getInstance();
		 System.out.println(object1);
		 System.out.println(object2);
		 System.out.println(object3);
	}
}

懒汉式单例
在类加载时,不会创建单例对象, 在第一次访问时,才会去创建
懒汉式单例有线程安全问题,必须要加锁处理

/*
 * 懒汉式单例
 */
public class Singleton {
	
	//定义静态的
	private static Singleton instance;  
	
	//让构造函数为 private,这样该类就不会被实例化
    private Singleton (){}  
    
    //向外界提供获取实例的方法  加锁 synchronized 才能保证单例
    public static synchronized Singleton getInstance() {  
	    if (instance == null) {  
	        instance = new Singleton();  
	    }  
      return instance;  
    }
}




//测试
public class Test {
	 public static void main(String[] args) {
		 
	      //获取唯一可用的对象
		 Singleton object1 = Singleton.getInstance();
		 Singleton object2 = Singleton.getInstance();
		 Singleton object3 = Singleton.getInstance();
		 System.out.println(object1);
		 System.out.println(object2);
		 System.out.println(object3);
	}
}

工厂模式

批量创建对象, 将创建对象与使用对象分离

简单工厂模式的主要角色如下:

简单工厂(SimpleFactory):是简单工厂模式的核心,负责实现创建所有实例 的内部逻辑。工厂类的创建产品类的方法可以被外界直接调用,创建所需的产品 对象。

抽象产品(Product):是简单工厂创建的所有对象的父类,负责描述所有实例共有的公共接口。

具体产品(ConcreteProduct):是简单工厂模式的创建目标。

其结构图如下图所示。

在这里插入图片描述

​ 工厂 负责生产对象的

​ 抽象产品 接口/抽象类 定义

​ 具体产品 实现了抽象的接口/抽象类的具体实现类

​ 以抽象来表示具体.

//抽象产品
public interface Product {

    void show();

}


//具体产品1
public class ProductA implements Product {

    @Override
    public void show() {
        System.out.println("具体产品1显示...");
    }

}

//具体产品2
public class ProductB implements Product {

    @Override
    public void show() {
        System.out.println("具体产品2显示...");
    }

}

/*
 * 工厂,负责生产对象
 */
public class SimpleFactory {

	   public Product createProduct(String className){
		   if(className == null){
			   return null;
		   }else{
				try {
					return (Product) Class.forName(className).newInstance();
				} catch (InstantiationException e) {
					e.printStackTrace();
					return null;
				} catch (IllegalAccessException e) {
					e.printStackTrace();
					return null;
				} catch (ClassNotFoundException e) {
					e.printStackTrace();
					return null;
				}
		   }      
	   }
	   
	   
}

//测试
public class Test {
	 public static void main(String[] args) {
	      SimpleFactory simpleFactory = new SimpleFactory();

		 Product productA = simpleFactory.createProduct("com.ff.javadesign.simplefactory.ProductA");

		 Product productB = simpleFactory.createProduct("com.ff.javadesign.simplefactory.ProductB");
	 }
}

代理模式

​ 代理—>代理商 4s店,中介, 商城

用户 ----> 汽车厂

用户 —> 大众4s店 (代理) 买保险,上牌照 —> 汽车厂

在有些情况下,一个客户不能或者不想直接访问另一个对象,这时需要找一 个中介帮忙完成某项任务,这个中介就是代理对象。例如,购买火车票不一定要 去火车站买,可以通过 12306 网站或者去火车票代售点买。又如找保姆、找工 作,买东西等都可以通过找中介完成。

当用户不想或不能直接去访问目标对象, 可以通过一个中间代理商在用户和目标之间起到一个中间作用.

代理模式优点:

​ 保护目标对象

​ 对目标的功能进行扩展

​ 将用户和目标进行分类,降低了耦合度

模式的结构

  1. 抽象主题(Subject)类:通过接口或抽象类声明真实主题和代理对象实现

的业务方法。

  1. 真实主题(Real Subject)类:实现了抽象主题中的具体业务,是代理对象

所代表的真实对象,是最终要引用的对象。

  1. 代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题

的引用,它可以访问、控制或扩展真实主题的功能。

其结构图如图所示。

在这里插入图片描述

AOP 是什么?

面向切面编程(AOP是Aspect Oriented Program的首字母缩写) ,我们知道,面向对象的特点是继承、多态和封装。而封装就要求将功能分散到不同的对象中去,这在软件设计中往往称为职责分配。实际上也就是说,让不同的类设计不同的方法。这样代码就分散到一个个的类中去了。这样做的好处是降低了代码的复杂程度,使类可重用。

AOP的作用及优势:
作用:在程序运行期间,不修改源码对已有的方法进行增强。
优势:减少重复代码,提高开发效率,维护方便。
AOP相关术语:
Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在Spring中,这些点指的是方法,因为spring只支持方法类型的连接点。
Pointcut(切入点):所谓切入点是指我们要对哪些Joinpoint进行拦截的定义。
Advice(通知/增强):所谓通知是指拦截到Joinpoint之后要做的事情就是五种。通知的类型有五种:分别是前置通知,后置通知,异常通知,最终通知以及环绕通知。
Introduction(引介):引介是一种特殊的通知,是在不修改类代码的前提下,Introduction可以在运行期间为类动态地添加一些方法或Field。
Target(代理对象):代理的目标对象。
Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程,spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入。
Proxy(代理):一个类被AOp植入增强后,就产生一个结果代理类。
Apect(切面):是切入点和通知(引介)的结合。

AOP使用方法 xml 注解

基于XML的AOP配置
<aop:config></aop:config>:用于声明开始aop的配置,aop的相关配置都在这个标签之内。
<aop:aspect></aop:aspect>:用于配置切面信息,属性id用于给切面提供一个唯一的表示,属性ref用于引用配置好的通知类bean的id。
<aop:pointcut></aop:pointcut>:用于配置切入点表达式,就是指定对哪些类的那些方法进行增强。属性expression用于定义切入点表达式,id用于给切入点表达式提供一个唯一标识。
<aop:xxx></aop:xxx>:用于配置对应的通知类型,这个标签在<aop:aspect></aop:aspect>标签中使用。
<aop:before></aop:before>:用于配置前置通知,在切入点方法之前执行。
method:用于指定通知类中的增强方法的名称。
pointcut-ref:用于指定切入点的表达式的引用。
pointcut:用于指定切入点表达式。
<aop:after-returning></aop:after-returning> :用于配置后置通知,在切入点方法正常执行后再执行,它和异常通知只能有一个执行,该标签具有的属性同上。
<aop:after-throwing></aop:after-throwing>:用于配置异常通知,在切入点方法发生异常后执行,它和后置通知只能执行一个,该标签具有的属性同上。
<aop:after></aop:after>:用于配置最终通知,无论切入点执行时是否发生异常,它都会在后面执行,该标签具有的属性同上。
<aop:around></aop:around>:用于配置环绕通知,它是spring框架为我们提供的一种可以手动控制增强代码什么时候执行的方式,通常情况下,环绕通知都是独立使用的,该标签具有的属性同上。
execution:对匹配到的方法进行增强,后接表达式,表达式语法:execution([修饰符] 返回值类型 包名.类名.方法名(参数))。

AOP实现原理

代理模式

思想?

分为静态代理

​ 静态代理一般使用于关系是固定的,代理某类事务必须实现接口,

​ 如果需要代理多个目标对象,那么就需要实现更多的接口,后期维护比较麻烦.

/*
  Dao接口,定义保存功能
 */
public interface BaseDao {
	
	void save();
	
}


/*
 * 静态代理类
 */
public class StaticDaoProxy implements BaseDao {

	//接收所有实现BaseDao接口的实现类对象
	private BaseDao baseDao;
	
	// 将被代理者的实例传进动态代理类的构造函数中
	public StaticDaoProxy(BaseDao baseDao) {
        this.baseDao = baseDao;
    }

    //代理他们实现功能,可以在调用前,调用后额外添加功能.
	@Override
	public void save() {
		System.out.println("before");
		baseDao.save();
		System.out.println("after");
	}

}



/*
  实际功能实现类
 */
public class UserDaoImpl implements BaseDao {

	    @Override
	    public void save() {
	        System.out.println("UserDaoImpl:save()");
	    }

}


public class Test {

	 public static void main(String[] args) {

          //把实际执行者交给代理对象管理即可
		  StaticDaoProxy subject = new StaticDaoProxy(new UserDaoImpl());
	      subject.save();

     }
}

动态代理

​ 实现方式有两种

​ jdk代理

​ jdk代理实现原理使用的是java反射机制,可以动态获取目标类中代理的方法,

​ 不需要代理类指定的去实现某些抽象接口, 代理的扩展性好.

​ 动态生成代理对象, 要求目标类必需有实现接口.

/*
  Dao接口,定义保存功能
 */
public interface BaseDao {
	
	void save();
	
}


/*
 * 动态代理类
 */
public class DynamicDaoProxy implements InvocationHandler {

	// 被代理类的实例
	private Object object;

	// 将被代理者的实例传进动态代理类的构造函数中
	public DynamicDaoProxy(Object object) {
		this.object = object;
	}

	/*
	 * 覆盖InvocationHandler接口中的invoke()方法
	 *    Object proxy 表示代理对象
	 *    Method method 代理对象中的方法
	 *	  Object[] args  表示代理方法中的参数
	 * 更重要的是,动态代理模式可以使得我们在不改变原来已有的代码结构
	 * 的情况下,对原来的“真实方法”进行扩展、增强其功能,并且可以达到
	 * 控制被代理对象的行为,下面的before、after就是我们可以进行特殊
	 * 代码切入的扩展点了。
	 */
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.println("before");

		Object result = method.invoke(object, args);

		System.out.println("after");
		return result;
	}
}



/*
  实际功能实现类
 */
public class UserDaoImpl implements BaseDao {

	    @Override
	    public void save() {
	        System.out.println("UserDaoImpl:save()");
	    }

}


public class Test {

	 public static void main(String[] args) {

		 //我们要代理的真实对象
		 UserDaoImpl userDaoImpl = new UserDaoImpl();

		 //我们要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法的
		 InvocationHandler dynamicProxy = new DynamicDaoProxy(userDaoImpl);

		 /*
		  * 通过Proxy的newProxyInstance方法来创建我们的代理对象,我们来看看其三个参数
		  * 第一个参数 handler.getClass().getClassLoader() ,我们这里使用handler这个类的ClassLoader对象来加载我们的代理对象
		  * 第二个参数realSubject.getClass().getInterfaces(),我们这里为代理对象提供的接口是真实对象所实行的接口,表示我要代理的是该真实对象,这样我就能调用这组接口中的方法了
		  * 第三个参数dynamicProxy, 我们这里将这个代理对象关联到了上方的 InvocationHandler 这个对象上
		  */
		 BaseDao baseDao = (BaseDao) Proxy.newProxyInstance(dynamicProxy.getClass().getClassLoader(), userDaoImpl.getClass().getInterfaces(), dynamicProxy);

		 baseDao.save();

     }
}

​ cglib代理(了解)

​ 动态字节码技术

​ 可以在运行中为目标类动态生成一个子类, 进行方法拦截,从而添加增强功能.

​ 不能代理final所修饰的类, 目标类可以不实现任何接口.

spring中两种实现都支持 可以根据目标类是否实现接口,自动选择动态方式.

/*
 * 动态代理类
 */
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();
	}
}

java注解

Java 注解(Annotation)又称 Java 标注,为类,方法,属性,包进行标注, 与java文档注释中的标记不同.

可以被编译到字节码文件中 ,运行时可以通过反射机制获取到注解标签.

内置的注解

java中已经定义好的注解标记

@Override - 检查该方法是否是重写方法。如果发现其父类,或者是引用的接口中并没有

该方法时,会报编译错误。

@Deprecated - 标记过时方法。如果使用该方法,会报编译警告。

@SuppressWarnings - 指示编译器去忽略注解中声明的警告

元注解 (注解的注解)

@Retention - 标识这个注解怎么保存,是只在代码中,还是编入 class 文件中,或者是在

运行时可以通过反射访问。

@Target - 标记这个注解应该是哪种 Java 成员。

对象克隆

//从前端向后端发送的数据   从后端向前端返回的数据  我们通通都使用一个Model类来完成封装

实际中模型类可以分好几中, 例如专门用来接收前端数据的,可以加入验证. 向dao层传输数据的
这里就需要用到对象的克隆.
user1  前端接收的数据
user = new User()

user.setId(user1.getId())


误区
我们常见的 Student stu1 = new Student (); 
          Student stu2 = stu1 ;

实现方式:

1.在 Java 语言中,通过覆盖 Object 类的 clone()方法可以实现浅克隆。

2.在 spring 框架中提供 BeanUtils.copyProperties(source,target);

克隆又分为浅克隆 和 深克隆

浅克隆

​ 如果一个对象中关联了其他的引用变量, 浅克隆时,只会将关联的对象的引用地址复制出来,并没有创建一个新的对象.

深克隆

​ 如果一个对象中关联了其他的引用变量, 深克隆时,将此对象中所关联的对象也会进行克隆操作,也就是会创建一个新的关联对象

package objectClone.demo2;

public class Address  implements Cloneable{

     String  address;

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "Address{" +
                "address='" + address + '\'' +
                '}';
    }

    @Override
    protected Address clone() throws CloneNotSupportedException {
        return (Address)super.clone();
    }
}



package objectClone.demo2;

public class Person implements  Cloneable{

     int num;
     String name;
     Address address;

    public Person() {
    }

    public Person(int num, String name) {
        this.num = num;
        this.name = name;
    }

    public int getNum() {
        return num;
    }

    public void setNum(int num) {
        this.num = num;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    @Override
    protected Person clone() throws CloneNotSupportedException {
        Person person = (Person)super.clone();
        person.address = (Address)address.clone();   //深度复制  联同person中关联的对象也一同克隆.去掉就为浅克隆
        return person;
    }

    @Override
    public String toString() {
        return "Person{" +
                "num=" + num +
                ", name='" + name + '\'' +
                ", address=" + address +
                '}';
    }
}



package objectClone.demo2;


public class Test {

    public static void main(String[] args) throws CloneNotSupportedException {

        Address address = new Address();
                address.setAddress("汉中");

        Person p1 = new  Person(100,"jim");
               p1.setAddress(address);

        Person p2 =p1.clone();
               p2.setName("tom");
               address.setAddress("西安");

        System.out.println(p1);
        System.out.println(p2);
    }
}

如何实现深克隆

1.多层次克隆 在关联的类中继续克隆

​ 2.使用序列化和反序列化的方式实现

package objectClone.demo4;



import java.io.*;

public class Person implements Serializable {

     int num;
     String name;
     Address address;

    public Person() {
    }

    public Person(int num, String name) {
        this.num = num;
        this.name = name;
    }

    public int getNum() {
        return num;
    }

    public void setNum(int num) {
        this.num = num;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    /**
     * 自定义克隆方法
     * @return
     */
    public Person myclone() {
            Person person = null;
              try { // 将该对象序列化成流,因为写在流里的是对象的一个拷贝,而原对象仍然存在于JVM里面。所以利用这个特性可以实现对象的深拷贝
                     ByteArrayOutputStream baos = new ByteArrayOutputStream();
                      ObjectOutputStream oos = new ObjectOutputStream(baos);
                      oos.writeObject(this);
            // 将流序列化成对象
                    ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
                     ObjectInputStream ois = new ObjectInputStream(bais);
                     person = (Person) ois.readObject();
                  } catch (IOException e) {
                     e.printStackTrace();
                  } catch (ClassNotFoundException e) {
                     e.printStackTrace();
                 }
             return person;
          }


    @Override
    public String toString() {
        return "Person{" +
                "num=" + num +
                ", name='" + name + '\'' +
                ", address=" + address +
                '}';
    }
}


package objectClone.demo4;

import java.io.Serializable;

public class Address  implements Serializable {

     String  address;

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "Address{" +
                "address='" + address + '\'' +
                '}';
    }

}


package objectClone.demo4;



public class Test {

    public static void main(String[] args) throws CloneNotSupportedException {

        Address address = new Address();
                address.setAddress("汉中");

        Person p1 = new  Person(100,"jim");
        p1.setAddress(address);

        Person p2 =p1.myclone();
               p2.setName("tom");
               address.setAddress("西安");

        System.out.println(p1);
        System.out.println(p2);


    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值