设计模式-单列设计模式,多列设计模式,工厂设计模式,动态代理,Lombok
第一章 单例设计模式
1.1单例设计模式的概述
正常情况下一个类可以创建多个对象
public class Person(){
// 默认生成一个公共的空参构造方法
}
// 测试类
public static void main(String[] args) {
// 正常情况下一个类可以创建多个对象
Person p1 = new Person();
Person p2 = new Person();
Person p3 = new Person();
// ...
}
但是有些时候的某些类, 我们希望只能创建单一的一个对象, 这时候我们需要使用到单例设计模式, 下面我们来介绍一下单例设计模式.
单例设计模式的作用
单例模式,是一种常用的软件设计模式。通过单例模式可以保证系统中,应用该模式的这个类只有一个实例。即一个类只有一个对象实例。
单例设计模式实现步骤
- 将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
- 在该类内部产生一个唯一的实例化对象
- 定义一个静态方法返回这个唯一对象。
例设计模式的类型
根据实例化对象的时机单例设计模式又分为以下两种:
- 饿汉单例设计模式
- 懒汉单例设计模式
1.2饿汉式单例设计模式
饿汉单例设计模式就是使用类的时候已经将对象创建完毕,不管以后会不会使用到该实例化对象,先创建了再说。很着急的样子,故被称为“饿汉模式”。
代码如下:
public class Person {
// 使用饿汉式单例设计模式: 比较着急,不管要不要获取我这个类的对象,先创建了该对象再说
// 1. 将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
private Person(){
}
// 2. 在该类内部产生一个唯一的实例化对象
private static final Person p = new Person();
// 3. 定义一个静态方法返回这个唯一对象。
public static Person getInstance(){
return p;
}
// ....
}
public class Test {
public static void main(String[] args) {
Person p1 = Person.getInstance();
Person p2 = Person.getInstance();
Person p3 = Person.getInstance();
System.out.println(p1);
System.out.println(p2);
System.out.println(p3);
}
}
1.3懒汉式单例设计模式
懒汉单例设计模式就是调用getInstance()方法时实例才被创建,先不急着实例化出对象,等要用的时候才实例化出对象。不着急,故称为“懒汉模式”。
代码如下:
public class Person {
// 懒汉式单例: 不着急,只要当你调用了getInstance静态方法获取对象的时候,就创建,其他时候不创建
// 1. 将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
private Person(){
}
// 2. 在该类内部产生一个唯一的实例化对象
private static Person p ;// 默认值为null
// 3. 定义一个静态方法返回这个唯一对象。
public static synchronized Person getInstance(){
// 创建Person类的唯一对象
// 判断一下,如果p这个成语变量的值为null,就创建,不为null,说明该对象已经创建了,直接返回即可
if (p == null){
p = new Person();
}
return p;
}
// ...
}
public class Test {
public static void main(String[] args) {
Person p1 = Person.getInstance();
Person p2 = Person.getInstance();
Person p3 = Person.getInstance();
System.out.println(p1);
System.out.println(p2);
System.out.println(p3);
}
}
注意:懒汉单例设计模式在多线程环境下可能会实例化出多个对象,不能保证单例的状态,所以加上关键字:synchronized,保证其同步安全。
小结
单例模式可以保证系统中一个类只有一个对象实例。
实现单例模式的步骤:
- 将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
- 在该类内部产生一个唯一的实例化对象,并且将其封装为private static类型的成员变量。
- 定义一个静态方法返回这个唯一对象。
第二章 多例设计模式
多例设计模式的作用
多例模式,是一种常用的软件设计模式。通过多例模式可以保证系统中,应用该模式的类有固定数量的实例。多例类要自我创建并管理自己的实例,还要向外界提供获取本类实例的方法。
实现步骤
1.创建一个类, 将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
2.在该类内部产生固定数量的实例化对象 ----> 集合
3.提高一个静态方法来随机获取一个该了的实例化对象
实现代码
public class Person {
// 使用多例设计模式: 保证程序运行期间该类只有固定数量的对象产生
// 1.创建一个类, 将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
private Person(){
}
// 2.在该类内部产生固定数量的实例化对象 ----> 集合 只能产生依次固定数量的对象
// 2.1 定义一个存放该类对象的集合
private static ArrayList<Person> list = new ArrayList<>();
// 2.2 在静态代码块中,创建固定数量的对象,添加到集合中
static {
// 创建固定数量的该类对象
for (int i = 0; i < 3; i++) {
Person p = new Person();
list.add(p);
}
}
// 3.提高一个静态方法来随机获取一个该了的实例化对象
public static Person getInstance(){
// 创建一个Random对象
Random r = new Random();
// 随机产生一个list集合的索引
int index = r.nextInt(list.size());// [0,3) 0,1,2
// 根据索引获取对象
Person p = list.get(index);
// 返回对象
return p;
}
}
测试结果
public class Test {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
Person p1 = Person.getInstance();
System.out.println(p1);
}
}
}
小结
多例模式可以保证系统中一个类有固定个数的实例, 在实现需求的基础上, 能够提高实例的复用性.
实现多例模式的步骤:
- 创建一个类, 将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
- 在类中定义该类被创建的总数量
- 在类中定义存放类实例的list集合
- 在类中提供静态代码块,在静态代码块中创建类的实例
- 提供获取类实例的静态方法
第三章 工厂设计模式
工厂模式的介绍
工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。之前我们创建类对象时, 都是使用new 对象的形式创建, 除new 对象方式以外, 工厂模式也可以创建对象.
耦合度: 类与类之间的关系,如果关系比较强,高耦合, 如果关系比较弱,低耦合
10个类:
测试类: 创建这10个类的对象
以前: 直接通过new 来创建 测试类和10个类关系起来
工厂模式: 定义一个类,专门用来创建这10个类的对象, 并提供获取的方法,那这个时候测试类只需要跟测试类关联 低耦合
工厂模式的作用
将前端代码与要创建的对象分开,前端不需要直接创建对象,也就不需要关心创建对象时需要的数据。只需要通过工厂获取对象即可。
- 解决类与类之间的耦合问题
案例演示
需求
- 编写一个Car接口, 提供run方法
- 编写一个Falali类实现Car接口,重写run方法
- 编写一个Benchi类实现Car接口,重写run方法
提供一个工厂类,可以用来生产汽车对象
实现代码
1.编写一个Car接口, 提供run方法
public interface Car {
public void run();
}
2.编写一个Falali类实现Car接口,重写run方法
public class Falali implements Car {
@Override
public void run() {
System.out.println("法拉利以每小时500公里的速度在奔跑.....");
}
}
3.编写一个Benchi类实现Car接口
public class Benchi implements Car {
@Override
public void run() {
System.out.println("奔驰汽车以每秒1米的速度在挪动.....");
}
}
4.提供一个CarFactory(汽车工厂),用于生产汽车对象
public class CarFactory {
/**
* @param id : 车的标识
* benchi : 代表需要创建Benchi类对象
* falali : 代表需要创建Falali类对象
* 如果传入的车标识不正确,代表当前工厂生成不了当前车对象,则返回null
* @return
*/
public Car createCar(String id){
if("falali".equals(id)){
return new Falali();
}else if("benchi".equals(id)){
return new Benchi();
}
return null;
}
}
5.定义CarFactoryTest测试汽车工厂
public class CarFactoryTest {
public static void main(String[] args) {
CarFactory carFactory = new CarFactory();
Car benchi = carFactory.createCar("benchi");
benchi.run();
Car falali = carFactory.createCar("falali");
falali.run();
}
}
- 工厂模式的存在可以改变创建类的对象的方式,解决类与类之间的耦合.
第四章 Lombok【自学扩展】
lombok介绍
- lombok可以使用注解的方式让一些代码变的简洁 方便
- 实体类中有一些固定的代码:构造方法,getter/setter、equals、hashcode、toString方法都是固定的,写出来看着比较麻烦。而Lombok能通过注解的方式,在编译时自动为属性生成这些代码。
lombok使用
1. 添加lombox的jar包:
将lombok.jar(本例使用版本:1.18.10),添加到模块目录下,并添加到ClassPath
2. 为IDEA添加lombok插件(连接网络使用)
第二步:
第三步:
第四步:
4. 安装完毕后,重启IDEA。
- 新建一个类:Student
lombok常用注解
-
@Getter和@Setter
- 作用:生成成员变量的get和set方法。
- 写在成员变量上,指对当前成员变量有效。
- 写在类上,对所有成员变量有效。
- 注意:静态成员变量无效。
-
@ToString:
- 作用:生成toString()方法。
- 该注解只能写在类上。
-
@NoArgsConstructor和@AllArgsConstructor
- @NoArgsConstructor:无参数构造方法。
- @AllArgsConstructor:满参数构造方法。
- 注解只能写在类上。
-
@EqualsAndHashCode
- 作用:生成hashCode()和equals()方法。
- 注解只能写在类上。
-
@Data
-
作用: 生成setter/getter、equals、canEqual、hashCode、toString方法,如为final属性,则不会为该属性生成setter方法。
-
注解只能写在类上。
-
第五章 动态代理
代理模式概述
为什么要有“代理”?生活中就有很多代理的例子,例如,我现在需要出国,但是我不愿意自己去办签证、预定机票和酒店(觉得麻烦 ,那么就可以找旅行社去帮我办,这时候旅行社就是代理,而我自己就是被代理了。
代理模式的定义:被代理者没有能力或者不愿意去完成某件事情,那么就需要找个人代替自己去完成这件事,这个人就是代理者, 所以代理模式包含了3个角色: 被代理角色 代理角色 抽象角色(协议)
静态代理:
public interface Happy {// 协议,被代理者需要代理的方法,就定义在这里,然后让代理者和被代理者去实现
// 被代理者实现: 为了确保和代理者实现的方法一致
// 代理者实现: 为了增强被代理者的这些方法
public abstract void happy();
}
public class JinLian implements Happy {
public void happy(){
System.out.println("金莲在happy...");
}
}
public class WangPo implements Happy{
// 成员变量
JinLian jl;
// 构造方法
public WangPo(JinLian jl) {
this.jl = jl;
}
// 成员方法
@Override
public void happy() {
System.out.println("王婆以做衣服的名义开好房间,并把2人约到房间里...");
// 金莲happy
jl.happy();
System.out.println("王婆打扫战场...");
}
}
public class XiMen {
public static void main(String[] args) {
/*
案例: 金莲要找西门happy
代理模式的定义:被代理者没有能力或者不愿意去完成某件事情,那么就需要找个人代替自己去完成这件事,这个人就是代理者,
所以代理模式包含了3个角色: 被代理角色 代理角色 抽象角色(协议)
*/
// 不请代理: 金莲直接找西门happy
// 创建金莲对象
JinLian jl = new JinLian();
// happy
// jl.happy();
// 请代理: 静态代理,代理类真实存在
Happy wp = new WangPo(jl);// wp:代理对象 WangPo类: 代理类 Happy接口: 协议 JinLian: 被代理类
wp.happy();
}
}
动态代理介绍
-
概述 : 动态代理就是直接通过反射生成一个代理对象,代理对象所属的类是不需要存在的
-
动态代理的获取:
jdk提供一个Proxy类可以直接给实现接口类的对象直接生成代理对象
动态代理相关api介绍
Java.lang.reflect.Proxy类可以直接生成一个代理对象
- Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)生成一个代理对象
- 参数1:ClassLoader loader 被代理对象的类加载器
- 参数2:Class<?>[] interfaces 被代理对象的要实现的接口
- 参数3:InvocationHandler h (接口)执行处理类
- 返回值: 代理对象
- 前2个参数是为了帮助在jvm内部生成被代理对象的代理对象,第3个参数,用来监听代理对象调用方法,帮助我们调用方法
- InvocationHandler中的Object invoke(Object proxy, Method method, Object[] args)方法:调用代理类的任何方法,此方法都会执行
- 参数1:代理对象(慎用)
- 参数2:当前执行的方法
- 参数3:当前执行的方法运行时传递过来的参数
- 返回值:当前方法执行的返回值
案例演示
案例1: 代理方法无参数
public interface Happy {// 协议,被代理者需要代理的方法,就定义在这里,然后让代理者和被代理者去实现
// 被代理者实现: 为了确保和代理者实现的方法一致
// 代理者实现: 为了增强被代理者的这些方法
public abstract void happy();
}
public class JinLian implements Happy {
public void happy(){
System.out.println("金莲在happy...");
}
}
public class XiMen {
public static void main(String[] args) {
/*
案例: 金莲要找西门happy
代理模式的定义:被代理者没有能力或者不愿意去完成某件事情,那么就需要找个人代替自己去完成这件事,这个人就是代理者,
所以代理模式包含了3个角色: 被代理角色 代理角色 抽象角色(协议)
*/
// 不请代理: 金莲直接找西门happy
// 创建金莲对象
JinLian jl = new JinLian();
// happy
// jl.happy();
// 请代理: 静态代理,代理类真实存在
Happy wp = new WangPo(jl);// wp:代理对象 WangPo类: 代理类 Happy接口: 协议 JinLian: 被代理类
wp.happy();
/*
问题:
1.金莲不方便直接找西门happy
2.金莲的happy方法需要增强一下,例如:happy之前需要开房,happy之后需要打扫战场
静态代理: 代理类是真实存在的,通过代理类产生代理对象
动态代理: 代理类是不真实存在的,在程序运行中,直接产生代理对象
前提: 被代理类需要实现接口
动态代理实现获取代理对象:
jdk提供一个Proxy类可以直接给实现接口类的对象直接生成代理对象
Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)生成一个代理对象
参数1loader:被代理类的类加载器
参数2interfaces: 被代理类所有实现的接口的Class对象
参数3InvocationHandler: 执行处理类
前2个参数是为了帮助在jvm内部生成被代理类的代理对象,第3个参数用来监听代理对象调用的方法,帮助我们代理对象调用方法
*/
System.out.println("=========================================");
// 使用动态代理直接产生金莲的代理对象
// 动态代理: 代理类是不真实存在的,但代理类是一定实现了被代理类的接口的
// p:动态代理产生的代理对象 代理类是不真实存在的
Happy p = (Happy) Proxy.newProxyInstance(JinLian.class.getClassLoader(), JinLian.class.getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 回调方法: 当代理对象调用了方法,就会来执行该invoke方法, 在该方法中就可以增强被代理类的方法
// 参数1: 生成的代理对象 这里就是p这个代理对象 (慎用)
// 参数2: 当前代理对象执行的方法 这里method就是happy()方法对象
// 参数3: 当前代理对象执行的方法,传入的实际参数
// 返回值:当前代理对象执行的方法的返回值
// System.out.println("invoke");
// 王婆开房
System.out.println("王婆以做衣服的名义开好房间,把2人约到房间...");
// 金莲happy
method.invoke(jl);
// 王婆打扫
System.out.println("王婆打印战场...");
return null;
}
});
// 代理happy
p.happy();// 无参数
}
}
案例2: 代理方法有参数和代理多个方法
public interface Happy {// 协议,被代理者需要代理的方法,就定义在这里,然后让代理者和被代理者去实现
// 被代理者实现: 为了确保和代理者实现的方法一致
// 代理者实现: 为了增强被代理者的这些方法
public abstract void happy();
public abstract void sleep(int time);
}
public class JinLian implements Happy {
public void happy(){
System.out.println("金莲在happy...");
}
public void sleep(int time){// 单位分钟
System.out.println("金莲在睡觉...time:"+time);
}
}
public class XiMen {
public static void main(String[] args) {
/*
问题:
1.金莲不方便直接找西门happy
2.金莲的happy方法需要增强一下,例如:happy之前需要开房,happy之后需要打扫战场
静态代理: 代理类是真实存在的,通过代理类产生代理对象
动态代理: 代理类是不真实存在的,在程序运行中,直接产生代理对象
前提: 被代理类需要实现接口
动态代理实现获取代理对象:
jdk提供一个Proxy类可以直接给实现接口类的对象直接生成代理对象
Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)生成一个代理对象
参数1loader:被代理类的类加载器
参数2interfaces: 被代理类所有实现的接口的Class对象
参数3InvocationHandler: 执行处理类
前2个参数是为了帮助在jvm内部生成被代理类的代理对象,第3个参数用来监听代理对象调用的方法,帮助我们代理对象调用方法
*/
System.out.println("=========================================");
// 使用动态代理直接产生金莲的代理对象
// 动态代理: 代理类是不真实存在的,但代理类是一定实现了被代理类的接口的
// p:动态代理产生的代理对象 代理类是不真实存在的
Happy p = (Happy) Proxy.newProxyInstance(JinLian.class.getClassLoader(), JinLian.class.getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 回调方法: 当代理对象调用了方法,就会来执行该invoke方法, 在该方法中就可以增强被代理类的方法
// 参数1: 生成的代理对象 这里就是p这个代理对象 (慎用)
// 参数2: 当前代理对象执行的方法 这里method就是happy()方法对象
// 参数3: 当前代理对象执行的方法,传入的实际参数
// 返回值:当前代理对象执行的方法的返回值
// System.out.println("invoke");
// 王婆开房
System.out.println("王婆以做衣服的名义开好房间,把2人约到房间...");
// 访问被代理对象的方法的方式:
// 方式一:
// 如果代理对象调用的方法没有参数,那么args等于null
/*if (args == null) {
// 金莲happy
method.invoke(jl);
} else if (args.length == 1) {
// 金莲sleep
method.invoke(jl,args[0]);
}*/
// 方式二:
// method.invoke(jl,args);
// 方式三:
JinLian jl = JinLian.class.newInstance();
method.invoke(jl,args);
// 王婆打扫
System.out.println("王婆打印战场...");
return null;
}
});
// 代理happy
p.happy();// 无参数
System.out.println("=========================");
p.sleep(10);// 有参数
}
}
总结- 能够说出单例设计模式的好处
保证使用这种模式创建的类,在程序运行期间只会创建一个该类的对象
-
能够说出多例模式的好处
保证使用这种模式创建的类,在程序运行期间只会创建固定数量的该类对象 -
能够说出动态代理模式的作用
1.在程序运行期间,动态产生一个代理对象,代理类不会真实存在
2.代理对象可以帮助被代理对象完成功能(增强被代理对象的功能) -
能够使用Proxy的方法生成代理对象
Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)生成一个代理对象
参数1loader:被代理类的类加载器
参数2interfaces: 被代理类所有实现的接口的Class对象
参数3InvocationHandler: 执行处理类
前2个参数是为了帮助在jvm内部生成被代理类的代理对象,第3个参数用来监听代理对象调用的方法,帮助我们代理对象调用方法public Object invoke(Object proxy, Method method, Object[] args);
// 回调方法: 当代理对象调用了方法,就会来执行该invoke方法, 在该方法中就可以增强被代理类的方法
// 参数1: 生成的代理对象 这里就是p这个代理对象 (慎用)
// 参数2: 当前代理对象执行的方法 这里method就是happy()方法对象
// 参数3: 当前代理对象执行的方法,传入的实际参数
// 返回值:当前代理对象执行的方法的返回值 -
能够使用工厂模式编写java程序
提供一个工厂类,在工厂类中提供一个静态方法,用来获取对象
练习模块
对Collection接口进行代理,以前的remove(Object obj)方法是删除集合中第一次出现的元素(比如集合中有多个“abc”,调用remove(“abc”)后只会删除一个元素)。代理后,要求在调用remove(Object obj)方法后,能够删除集合中所有匹配的元素。【动态代理】
Collection<String> list = new ArrayList<>();
list.add("aaa");
list.add("aaa");
list.add("aaa");
list.add("bbb");
list.remove("aaa");
System.out.println(list);
Collection<String>pox=(Collection<String>)Proxy.newProxyInstance(list.getClass().getClassLoader(), list.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
if (method.getName().equals("remove")){
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String next = iterator.next();
if (objects[0].equals(next)){
iterator.remove();
}
}
}return method.invoke(list,objects);
}
});
pox.remove("aaa");
System.out.println(list);
}}