今日目标
- 单例模式------------->重点掌握
- 饿汉式单例
- 懒汉式单列
- 多例模式------------->重点掌握
- 枚举------------->重点掌握
- 定义枚举
- 使用枚举
- 工厂模式------------->重点掌握
- lombok插件
第一章 单例设计模式
1.1 单例设计模式的概述
引入
public class Person{
}
public class Test{
public static void main(String[] args){
Person p1 = new Person();
Person p2 = new Person();
Person p3 = new Person();
//...
}
}
单例设计模式的作用
单例模式,是一种常用的软件设计模式。通过单例模式可以保证系统中,应用该模式的这个类只有一个实例。即一个类只有一个对象实例。
单例设计模式实现步骤
- 将构造方法私有化,使其不能在类的外部通过new关键字创建该类对象。
- 在该类内部创建一个唯一的对象
- 定义一个公共的静态方法返回这个唯一对象。
例设计模式的类型
根据实例化对象的时机单例设计模式又分为以下两种:
- 饿汉单例设计模式
- 懒汉单例设计模式
1.2 饿汉式单例设计模式
-
概述: 饿汉单例设计模式就是使用类的时候已经将对象创建完毕,不管以后会不会使用到该类的对象,先创建了再说。很着急的样子,故被称为“饿汉模式”。
-
代码如下:
/** * Created by PengZhiLin on 2021/8/17 9:10
*/
// 概述: 饿汉单例设计模式就是使用类的时候已经将对象创建完毕,
// 不管以后会不会使用到该类的对象,先创建了再说。
// 很着急的样子,故被称为“饿汉模式”。
public class Person {
// 1.将构造方法私有化,保证其不能在外面通过new调用构造方法创建对象
private Person() {
}
// 2.在该类的内部创建一个唯一的该类对象
private static Person p = new Person();
// 3.提供一个公共的静态方法用来获取该类的唯一对象
public static Person getInstance(){
return p;
}
public static void method(){
}
}
```java
/**
* Created by PengZhiLin on 2021/8/17 9:09
*/
public class Test {
public static void main(String[] args) {
Person.method();
System.out.println(Person.getInstance());
System.out.println(Person.getInstance());
System.out.println(Person.getInstance());
System.out.println(Person.getInstance());
}
}
结果:
com.itheima.demo1_饿汉式单例设计模式.Person@4554617c
com.itheima.demo1_饿汉式单例设计模式.Person@4554617c
com.itheima.demo1_饿汉式单例设计模式.Person@4554617c
com.itheima.demo1_饿汉式单例设计模式.Person@4554617c
1.3 懒汉式单例设计模式
-
概述: 懒汉单例设计模式就是调用getInstance()方法时对象才被创建,先不急着创建出对象,等要用的时候才创建对象。不着急,故称为“懒汉模式”。
-
代码如下:
/** * Created by PengZhiLin on 2021/8/17 9:17
*/
//概述: 懒汉单例设计模式就是调用getInstance()方法时对象才被创建,先不急着创建出对象,等要用的时候才创建对象。不着急,故称为“懒汉模式”。
public class Person {
// 1.将构造方法私有化,防止外界通过new调用构造方法创建该类的对象
private Person() {
}
// 2.定义一个Person类型的成员变量
private static Person p;
// 3.提供一个公共的静态方法用来获取该类的唯一对象
public static synchronized Person getInstance(){
// 如果是第一次调用getInstance方法,就创建该类的唯一对象
if (p == null){
p = new Person();
}
// 如果不是第一次调用,就直接返回第一次调用的时候创建的对象
return p;
}
public static void method(){
}
}
```java
/**
* Created by PengZhiLin on 2021/8/17 9:17
*/
public class Test {
public static void main(String[] args) {
Person.method();
System.out.println(Person.getInstance());
System.out.println(Person.getInstance());
System.out.println(Person.getInstance());
System.out.println(Person.getInstance());
}
}
结果:
com.itheima.demo2_懒汉式单例设计模式.Person@4554617c
com.itheima.demo2_懒汉式单例设计模式.Person@4554617c
com.itheima.demo2_懒汉式单例设计模式.Person@4554617c
com.itheima.demo2_懒汉式单例设计模式.Person@4554617c
- 注意: 懒汉式单列设计模式在多线程的情况下很容易出现创建多个对象,所以getInstance方法需要加锁
第二章 多例设计模式
2.1 多例设计模式
多例设计模式的作用
多例模式,是一种常用的软件设计模式。通过多例模式可以保证系统中,应用该模式的类有固定数量的对象产生。
说白了,多例设计模式就是保证使用该模式的类会有固定数量的该类对象产生
实现步骤
1.创建一个类, 将构造方法私有化,使其不能在类的外部通过new关键字创建该类对象。
2.在该类内部创建固定数量的对象
3.提供一个公共静态方法来随机获取一个该类的对象
实现代码
/**
* Created by PengZhiLin on 2021/8/17 9:44
*/
public class Person {
// 1.将构造方法私有化,使其不能在类的外部通过new关键字创建该类对象。
private Person(){
}
// 2.定义一个ArrayList成员变量,用来存储该类的对象
private static ArrayList<Person> list = new ArrayList<>();
//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();
// 生成随机数,范围:[0,list.size())
int index = r.nextInt(list.size());
// 根据随机数获取对象
Person p = list.get(index);
// 返回
return p;
}
}
/**
* Created by PengZhiLin on 2021/8/17 9:44
*/
public class Test {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
System.out.println(Person.getInstance());
}
}
}
第三章 枚举
3.1 枚举的定义和使用--重点
不使用枚举存在的问题
假设我们要定义一个人类,人类中包含姓名和性别。通常会将性别定义成字符串类型,效果如下:
public class Person {
private String name;
private String sex;
public Person() {
}
public Person(String name, String sex) {
this.name = name;
this.sex = sex;
}
// 省略get/set/toString方法
}
public class Demo01 {
public static void main(String[] args) {
Person p1 = new Person("张三", "男");
Person p2 = new Person("张三", "abc"); // 因为性别是字符串,所以我们可以传入任意字符串
}
}
不使用枚举存在的问题:可以给性别传入任意的字符串,导致性别是非法的数据,不安全。
枚举的概念
枚举是一种引用数据类型,java中枚举的底层是一个有固定个数对象的"特殊类"。所以如果某种类型的数据有固定个值,就可以定义为枚举类型。比如性别, 季节,方向。
定义枚举的格式
-
格式:
public enum 枚举名{ 枚举值,枚举值,枚举值,... // 枚举值全部字母大写,多个枚举值使用逗号隔开 }
-
案例:
public enum Sex { MAN, WOMAN, YAO } /** * Created by PengZhiLin on 2021/8/17 9:57 */ public enum Season { SPRING, SUMMER, AUTUMN, WINTER } /** * Created by PengZhiLin on 2021/8/17 9:58 */ public enum Direction { UP, DOWN, LEFT, RIGHT }
枚举的使用
-
格式:
枚举名.枚举值
-
案例:
/** * Created by PengZhiLin on 2021/8/17 9:56 */ public class Test { public static void main(String[] args) { // 定义性别枚举类型的变量并赋值 Sex s1 = Sex.YAO; System.out.println("s1:" + s1); // 定义季节枚举类型的变量并赋值 Season s2 = Season.AUTUMN; System.out.println("s2:" + s2); } }
3.2 枚举中的其他内容( 听听就好)
-
枚举的本质是一个使用了多例设计模式的类,所以枚举中还可以有成员变量,成员方法,构造方法等。
-
枚举的本质是一个类,我们刚才定义的Sex枚举最终效果如下:
/** * Created by PengZhiLin on 2021/8/17 9:56 */ public enum Sex { MAN("itcast",100), WOMAN, YAO; // 成员变量 public String name; public int age; // 构造方法 private Sex() { } private Sex(String name, int age) { this.name = name; this.age = age; } // 成员方法 public void method(){ System.out.println("method..."); } }
/** * Created by PengZhiLin on 2021/8/17 10:16 */ public class Test { public static void main(String[] args) { // 定义Sex枚举类型的变量并赋值 Sex s = Sex.MAN; // 访问成员变量 //s.name = "itheima"; //s.age = 100; System.out.println(s.name+','+s.age); // 访问成员方法 s.method(); } }
第四章 工厂设计模式
4.1 工厂模式的概述
工厂模式的介绍
工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。之前我们创建类对象时, 都是使用new 对象的形式创建, 除new 对象方式以外, 工厂模式也可以创建对象.
耦合度: 类与类之间的关系,如果关系比较强,高耦合, 如果关系比较弱,低耦合(开发)
需求: 有10个类,需要在10个测试类中,分别创建这10个类的对象
以前: 直接通过new 来创建 --->每一个测试类都需要和这10个类进行关联--(耦合度高)
工厂模式: 定义一个工厂类,专门用来创建这10个类的对象, 并提供获取对象的方法,那这个时候测试类只需要跟工厂类关联即可----> 低耦合
工厂模式的作用
将获取对象的代码与要创建对象的代码进行分开,获取对象的代码不需要直接创建对象,也就不需要关心创建对象时需要的数据。只需要通过工厂类获取对象即可。
- 解决类与类之间的耦合问题
案例演示
需求
-
编写一个Car接口, 提供run方法
-
编写一个Falali类实现Car接口,重写run方法
-
编写一个Benchi类实现Car接口,重写run方法
提供一个工厂类,可以用来生产汽车对象
实现代码
1.编写一个Car接口, 提供run方法
/**
* Created by PengZhiLin on 2021/8/17 10:37
*/
public interface Car {
void run();
}
2.编写一个Falali类实现Car接口,重写run方法
/**
* Created by PengZhiLin on 2021/8/17 10:38
*/
public class Falali implements Car {
@Override
public void run() {
System.out.println("法拉利正在以300迈的速度行驶...");
}
}
3.编写一个Benchi类实现Car接口
/**
* Created by PengZhiLin on 2021/8/17 10:38
*/
public class Benchi implements Car {
@Override
public void run() {
System.out.println("奔驰正在以200迈的速度行驶...");
}
}
4.提供一个CarFactory(汽车工厂),用于生产汽车对象
/**
* Created by PengZhiLin on 2021/8/17 10:40
*/
public class CarFactory {
/**
* 生产汽车对象
* @param carType
* @return 汽车对象
*/
public static Car createCar(String carType){
if ("Falali".equalsIgnoreCase(carType)){
// 创建法拉利对象
return new Falali();
}else if ("Benchi".equalsIgnoreCase(carType)){
// 创建奔驰对象
return new Benchi();
}else if ("BaoMa".equalsIgnoreCase(carType)){
// 创建宝马对象
return new BaoMa();
}else{
return null;
}
}
}
5.定义CarFactoryTest测试汽车工厂
/**
* Created by PengZhiLin on 2021/8/17 10:37
*/
public class Test {
public static void main(String[] args) {
/*Falali falali = new Falali();
Benchi benchi = new Benchi();
BaoMa bmw = new BaoMa();
falali.run();
benchi.run();
bmw.run();*/
// 创建法拉利对象
Car car1 = CarFactory.createCar("Falali");
// 创建奔驰对象
Car car2 = CarFactory.createCar("Benchi");
// 创建宝马对象
Car car3 = CarFactory.createCar("BaoMa");
// 调用run方法
car1.run();
car2.run();
car3.run();
}
}
第五章 Lombok【自学扩展】
5.1 Lombok的使用
lombok介绍
- lombok可以使用注解的方式让一些代码变的简洁 方便
- 实体类中有一些固定的代码:构造方法,getter/setter、equals、hashcode、toString方法都是固定的,写出来看着比较麻烦。而Lombok能通过注解的方式,在编译时自动为属性生成这些代码。
lombok使用
1. 添加lombox的jar包:
将lombok.jar(本例使用版本:1.18.10),添加到模块目录下,并添加到ClassPath
2. 为IDEA添加lombok插件(连接网络使用)
- 第一步
- 第二步:
- 第三步:
- 第四步:
-
安装完毕后,重启IDEA。
-
新建一个类:Student
lombok常用注解
-
@Getter和@Setter
- 作用:生成成员变量的get和set方法。
- 写在成员变量上,指对当前成员变量有效。
- 写在类上,对所有成员变量有效。
- 注意:静态成员变量无效。
-
@ToString:
- 作用:生成toString()方法。
- 该注解只能写在类上。
-
@NoArgsConstructor和@AllArgsConstructor
- @NoArgsConstructor:无参数构造方法。
- @AllArgsConstructor:满参数构造方法。
-
注解只能写在类上。
-
@EqualsAndHashCode
- 作用:生成hashCode()和equals()方法。
- 注解只能写在类上。
-
@Data
-
作用: 生成setter/getter、equals、hashCode、toString方法,如为final属性,则不会为该属性生成setter方法。
-
注解只能写在类上。
-
-
案例:
/** * Created by PengZhiLin on 2021/8/17 10:55 */ @AllArgsConstructor @NoArgsConstructor @Data /*@Setter @Getter @EqualsAndHashCode @ToString*/ public class Student { private String name; private int age; }
动态代理
对Collection接口进行代理,以前的remove(Object obj)
方法是删除集合中第一次出现的元素(比如集合中有多个“abc”,调用remove(“abc”)后只会删除第一个元素)。代理后,要求在调用remove(Object obj)方法后,能够删除集合中所有匹配的元素。【动态代理】
package com.itheima.demo8_动态代理;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
/**
* Created by PengZhiLin on 2021/8/17 11:18
*/
public class Test {
public static void main(String[] args) {
/*
对Collection接口进行代理,以前的`remove(Object obj)`方法是删除集合中第一次出现的元素
(比如集合中有多个“abc”,调用remove(“abc”)后只会删除第一个元素)。
代理后,要求在调用remove(Object obj)方法后,能够删除集合中所有匹配的元素。【动态代理】
固定套路:
1.判断是否可以使用动态代理
2.如果可以使用,就使用Proxy调用newProxyInstance方法,传入3个参数
3.对生成的代理对象进行类型转换----向下转型
4.使用代理对象调用方法
5.代理对象调用方法就会来到InvocationHandler接口的invoke方法,所以接下来就得把增强的代码写在invoke方法里面
*/
// 1.创建ArrayList集合,限制集合中元素的类型为String
ArrayList<String> list = new ArrayList<>();
// 2.添加元素
list.add("abc");
list.add("bac");
list.add("cba");
list.add("abc");
list.add("nba");
list.add("abc");
System.out.println("删除前list:" + list);
// 被代理类的类加载器
ClassLoader classLoader = list.getClass().getClassLoader();
// 被代理类实现的所有接口的Class对象
Class<?>[] interfaces = list.getClass().getInterfaces();
// 处理接口---监听代理对象调用方法
InvocationHandler iv = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 书写代理对象调用的方法需要执行的代码
// 参数1: 代理对象--慎用
// 参数2: 代理对象调用的方法
// 参数3: 代理对象调用方法时传入的实参
// 返回值: 代理对象调用方法的返回值
// 通过反射让代理对象调用的方法执行一次-----被代理对象---->目的:确定代理对象调用方法的返回值
Object res = method.invoke(list, args);
// 如果是调用remove方法
if ("remove".equals(method.getName())){
// 获得迭代器对象
Iterator<String> it = list.iterator();
// 迭代删除
while (it.hasNext()) {
String e = it.next();
// 判断迭代出来的元素是否是要删除的元素
if (e.equals(args[0])) {
it.remove();
}
}
}
return res;
}
};
// 动态生成一个代理对象
Collection<String> proxy = (Collection<String>) Proxy.newProxyInstance(classLoader, interfaces, iv);
// 3.请代理,调用remove方法
boolean res2 = proxy.remove("abc");
System.out.println("res2:" + res2);// true
System.out.println("删除后list:" + list);// [bac, cba, nba]
System.out.println("-------------");
boolean res3 = proxy.remove("bac");
System.out.println("res3:" + res3);// true
System.out.println("删除后list:" + list);// [cba, nba]
System.out.println("-------------");
boolean res4 = proxy.remove("aaa");
System.out.println("res4:" + res4);// false
System.out.println("删除后list:" + list);// [cba, nba]
System.out.println("-------------");
// 代理对象调用size()方法
int size = proxy.size();
System.out.println("集合元素个数:" + size);// 2
// 3.不请代理,调用remove方法
//boolean res1 = list.remove("abc");
//System.out.println("res1:"+res1);// true
//System.out.println("删除后list:"+list);// [bac, cba, abc, nba, abc]
}
}
总结
必须练习:
1.单例设计模式
2.多例设计模式
3.定义和使用枚举
4.工厂设计模式
- 能够说出单例设计模式的好处
保证使用该模式设计的类只有1个对象产生
步骤:
1.将构造方法私有化
2.在类的内部创建该类的唯一对象
3.提供公共静态方法用来获取该类的唯一对象
- 能够说出多例模式的好处
保证使用该模式设计的类只有固定数量对象产生
步骤:
1.将构造方法私有化
2.在类的内部创建该类固定数量的对象
3.提供公共静态方法用来获取该类中创建的任意对象
- 能够定义枚举
格式:
public enum 枚举名{
枚举值,枚举值,...
}
使用: 枚举名.枚举值
- 能够使用工厂模式编写java程序
定义一个类,提供一个静态方法,在静态方法中创建类的对象并返回