1. 代理模式
1.1 静态代理模式
/**
* @author fengweibo
* @version 1.0
* @date 2021/5/19 19:34
*/
public interface Wedding {
void happy();
}
/**
* @author fengweibo
* @version 1.0
* @date 2021/5/19 19:32
*/
//真实对象和代理对象都需要实现一个共同的接口,代理对象可以对真实对象做一个很大的增强
public class Proxy {
public static void main(String[] args) {
Person person = new Person();
Company company = new Company(person);
company.happy();
}
}
class Person implements Wedding{
@Override
public void happy() {
System.out.println("结婚ing");
}
}
//公司,帮助人结婚
class Company implements Wedding{
//被代理的人
private Person person;
public Company(Person person){
this.person = person;
}
@Override
public void happy() {
before();
person.happy();
after();
}
public void before() {
System.out.println("婚礼前事务");
}
public void after() {
System.out.println("婚礼后事务");
}
}
Thread相当于代理他底层实现了Runnable接口,被代理类(用户自定义的的那个实现了Runnable接口的类)相当于被代理类,将被代理类传入代理类,增强了被代理类。
//实现Runnable接口 重写run方法 将run所在类对象(Runnable实现类对象)传入Thread类 使用start方法启动
public class Test2 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 2000; i++) {
System.out.println("AAAAA");
}
}
public static void main(String[] args) {
//创建Runnable接口的实现类对象
Test2 test2 = new Test2();
//创建Thread对象,通过线程对象来开启线程,代理模式。
Thread thread = new Thread(test2);
thread.start();
new Thread(test2).start();
for (int i = 0; i < 2000; i++) {
System.out.println("B");
}
}
}
多线程用到Thread类,用户类共同实现Runnable,Thread为用户类的代理类。
1.2 动态代理
2. 静态代理与装饰者模式
3. 单例模式
3.1 饿汉式单例:
public class Singleton{
private static final Singleton singleton = new Singleton();
private Singleton(){}
public static Singleton getInstance() {
return singleton;
}
}
3.2 懒汉式单例:
class Single{
//构造方法私有化
private Single(){};
private static Single instance = null;
public static Single getInstance(){
if (instance == null){
instance = new Single();
}
return instance;
}
}
缺点:
当多线程时,有可能多个线程看到的instance都是null,所以就会导致创建多个new Single()。
优化:
加锁
class Single{
//构造方法私有化
private Single(){};
private static Single instance = null;
//当前所对象是Single.class对象
// public static synchronized Single getInstance(){
// if (instance == null){
// instance = new Single();
// }
// return instance;
// }
//同这种方式
public static Single getInstance(){
synchronized (Single.class) {
if (instance == null){
instance = new Single();
}
return instance;
}
}
}
缺点:
当使用这种方式创建时容易造成性能浪费的问题,当第一个线程进入代码块创建完对象后,其他的线程就没必须再在外部等着创建线程了。
优化:
class Single{
//构造方法私有化
private Single(){};
private static Single instance = null;
public static Single getInstance(){
if (instance == null) {
synchronized (Single.class) {
if (instance == null) {
instance = new Single();
}
}
}
return instance;
}
}
缺点:
对象实际上创建对象要经过如下几个步骤:
- 分配内存空间。
- 调用构造器,初始化实例。
- 将 instance 指向分配的内存地址
所以创建的过程是可能发生指令重排序的,有可能3操作在2之前,此时还没真正的初始化完对象,但是别的线程去判断instance!= null,直接拿去用了,其实这个对象是个半成品,那就有空指针异常了。
优化:
加上volatile关键字,禁止指令重排
class Single{
//构造方法私有化
private Single(){};
private volatile static Single instance = null;
public static Single getInstance(){
if (instance == null) {
synchronized (Single.class) {
if (instance == null) {
instance = new Single();
}
}
}
return instance;
}
}
3.3 枚举实现单例
4. 生产者消费者模式
package com.fwb.customerAndProducer;
/**
* @author fengweibo
* @version 1.0
* @date 2021/5/27 11:01
*/
public class Test {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Consumer consumer = new Consumer(clerk);
Producer producer = new Producer(clerk);
consumer.setName("消费者");
producer.setName("生产者");
consumer.start();
producer.start();
}
}
class Clerk extends Thread{
private int productCount = 0;
public synchronized void produceProducet() throws InterruptedException {
if (productCount < 20){
productCount++;
notify();
//生产完产品10后,此处阻塞,消费者开始消费productCount--(两个线程同时操作clerk对象),则生产第10个产品就变成了生产第9个产品
//此时需要给当前方法加上synchronized,这样即使当前线程阻塞了,有锁锁着也不会出现线程安全问题
System.out.println(Thread.currentThread().getName() + "生产第" + productCount + "个产品");
}else {
// 等待
wait();
}
}
public synchronized void consumeProducet() throws InterruptedException {
if (productCount > 0){
System.out.println(Thread.currentThread().getName() + "消费第" + productCount + "个产品");
// Thread.sleep(100);
productCount--;
notify();
}else {
//等待
wait();
}
}
}
class Producer extends Thread{
Clerk clerk;
public Producer(Clerk clerk){
this.clerk = clerk;
}
@Override
public void run(){
System.out.println(getName() + "开始生产产品");
while (true) {
try {
sleep(10);
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
clerk.produceProducet();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Consumer extends Thread{
public Consumer(Clerk clerk){
this.clerk = clerk;
}
Clerk clerk;
@Override
public void run(){
System.out.println(getName() + "开始消费产品");
while (true) {
try {
sleep(10);
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
clerk.consumeProducet();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
5. 工厂模式
5.1 简单工厂模式
package com.fwb.factoryTest;
/**
* @author fengweibo
* @version 1.0
* @date 2021/6/1 14:37
*/
interface IFruit1 {
public void eat() ;
}
class Apple1 implements IFruit1{
@Override
public void eat() {
System.out.println("Apple");
}
}
class Orange1 implements IFruit1{
@Override
public void eat() {
System.out.println("Orange");
}
}
class Factory1 {
private Factory1(){}
public static IFruit1 getInstance(String name) {
if ("Apple".equals(name)) {
return new Apple1();
}
if ("Orange".equals(name)) {
return new Orange1();
}else {
return null;
}
}
}
public class EasyFactory {
public static void main(String[] args) {
IFruit1 apple = Factory1.getInstance("Apple");
apple.eat();
}
}
5.2 反射与简单工厂模式
上面的简单工厂模式每增加一个接口的子类就需要修改工厂类,不实用。
如果要想解决关键字new带来的问题,最好的做法就是通过反射来完成处理,因为Class类可以使用newInstance()实例化对象,同时Class.forName()能够接收类名称。
package com.fwb.factoryTest;
/**
* @author fengweibo
* @version 1.0
* @date 2021/6/1 10:03
*/
interface IFruit2 {
public void eat() ;
}
class Apple2 implements IFruit2{
@Override
public void eat() {
System.out.println("Apple");
}
}
class Orange2 implements IFruit2{
@Override
public void eat() {
System.out.println("Orange");
}
}
class Factory2 {
private Factory2(){}
public static IFruit2 getInstance(String name) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
Class<?> aClass = Class.forName(name);
return (IFruit2) aClass.newInstance();
}
}
public class TestWithReflect {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
IFruit2 iFruit2 = Factory2.getInstance("com.fwb.factoryTest.Orange2");
iFruit2.eat();
}
}
引入反射后,每当新增接口子类,无需去修改工厂类代码就可以很方便的进行接口子类扩容。
6. 策略模式
实现某一个功能有多种算法或者策略,我们可以根据环境或者条件的不同选择不同的算法或者策略来完成该功能。
以自定义类实现java.util.Comparator:外部排序接口为例。
类本身不具备可比较的特性,专门有一个类来比较自定义类的大小,此类专门用于比较两个Person的大小,以后如果需要更换比较策略,只需再写一个新的类就可以,不需要再次更改原代码。
package fwb.collection.Set;
import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;
class Person2 {
private Integer age;
private String name;
public Person2(Integer age, String name) {
this.age = age;
this.name = name;
}
public Integer getAge() {
return age;
}
public String getName() {
return name;
}
public void setAge(Integer age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person2{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
/*
此类专门用于比较两个Person的大小,以后如果需要更换比较策略,
只需再写一个新的类就可以,不需要再次更改原代码
*/
class PersonAgeSec implements Comparator<Person2>{
@Override
public int compare(Person2 o1, Person2 o2) {
if(o1.getAge() < o2.getAge()){
return -1;
}
if (o1.getAge() > o2.getAge()){
return 1;
}
return 0;
}
}
//若以后需要更改比较策略,只需要重新写一个类即可
class PersonAgeDesc implements Comparator<Person2>{
@Override
public int compare(Person2 o1, Person2 o2) {
if(o1.getAge() < o2.getAge()){
return 1;
}
if (o1.getAge() > o2.getAge()){
return -1;
}
return 0;
}
}
public class SetTest2 {
public static void main(String[] args) {
PersonAgeSec personAgeSec = new PersonAgeSec();
//TreeSet有一个构造方法可以接收Comparator的子类
Set<Person2> set = new TreeSet<>(personAgeSec);
set.add(new Person2(25,"王五"));
set.add(new Person2(25,"李四"));
set.add(new Person2(20,"张三"));
System.out.println(set);
}
}
7. 模板设计模式
在servlet、aqs中用到。
例:
package com.fwb.duoxianchen.basics.template;
/**
* @author fengweibo
* @version 1.0
* @date 2022/3/22 12:26
*/
class Coffee {
/*
* 咖啡冲泡法(算法)
*/
void prepareRecipe() {
boilWater();
brewCoffeeGrings();
pourInCup();
addSugarAndMilk();
}
public void boilWater() {
System.out.println("将水煮沸");
}
public void brewCoffeeGrings() {
System.out.println("冲泡咖啡");
}
public void pourInCup() {
System.out.println("把咖啡倒进杯子中");
}
public void addSugarAndMilk() {
System.out.println("加糖和牛奶");
}
}
class Tea {
/*
* 冲泡茶法(算法)
*/
void prepareRecipe() {
boilWater();
steepTeaBag();
pourInCup();
addLemon();
}
public void boilWater() {
System.out.println("将水煮沸");
}
public void steepTeaBag() {
System.out.println("浸泡茶叶");
}
public void pourInCup() {
System.out.println("把茶倒进杯子中");
}
public void addLemon() {
System.out.println("加柠檬");
}
}
/*
其中煮沸水与把饮料倒进杯子子类都有就可以抽取出来放入父类
*/
class Test1 {
public static void main(String[] agrs) {
Coffee coffee = new Coffee();
Tea tea = new Tea();
coffee.prepareRecipe();
System.out.println("----------------");
tea.prepareRecipe();
}
}
结果:
问题:
其中煮沸水与把饮料倒进杯子,子类都有就可以抽取出来放入父类。
使用模板模式优化:
package com.fwb.duoxianchen.basics.template;
/**
* @author fengweibo
* @version 1.0
* @date 2022/3/22 12:29
*/
/**
* 咖啡因饮料是一个抽象类,总结公共方法父类实现,非公共方法父类声明为抽象类,由子类实现。
**/
abstract class CaffeineBeverage {
/**
* 现在用同一个prepareRecipe()方法处理茶和咖啡。
* 声明为final的原因是我们不希望子类覆盖这个方法!
**/
final void prepareRecipe() {
boilWater();
brew();
pourInCup();
addCondiments();
}
/**
* 咖啡和茶处理这些方法不同,因此这两个方法必须被声明为抽象,留给子类实现
**/
abstract void brew();
abstract void addCondiments();
void boilWater() {
System.out.println("将水煮沸");
}
void pourInCup() {
System.out.println("把饮料倒进杯子中");
}
}
class Coffee extends CaffeineBeverage {
@Override
public void brew() {
System.out.println("冲泡咖啡");
}
@Override
public void addCondiments() {
System.out.println("加糖和牛奶");
}
}
class Tea extends CaffeineBeverage {
@Override
public void brew() {
System.out.println("浸泡茶叶");
}
@Override
public void addCondiments() {
System.out.println("加柠檬");
}
}
class Test {
public static void main(String[] agrs) {
CaffeineBeverage coffee = new Coffee();
CaffeineBeverage tea = new Tea();
coffee.prepareRecipe();
System.out.println("-------");
tea.prepareRecipe();
}
}
结果:
继续优化:
可以选择性的加糖/奶
package com.fwb.duoxianchen.basics.template;
/**
* @author fengweibo
* @version 1.0
* @date 2022/3/22 15:44
*/
import java.util.Scanner;
/**
* 咖啡因饮料是一个抽象类
**/
abstract class CaffeineBeverage {
/**
* 现在用同一个prepareRecipe()方法处理茶和咖啡。
* 声明为final的原因是我们不希望子类覆盖这个方法!
**/
final void prepareRecipe() {
boilWater();
brew();
pourInCup();
if(customerWantsCondiments()){
addCondiments();
}
}
/**
* 咖啡和茶处理这些方法不同,因此这两个方法必须被声明为抽象,留给子类实现
**/
abstract void brew();
abstract void addCondiments();
void boilWater() {
System.out.println("将水煮沸");
}
void pourInCup() {
System.out.println("把饮料倒进杯子中");
}
boolean customerWantsCondiments() {
return true;
}
}
class Coffee extends CaffeineBeverage {
@Override
public void brew() {
System.out.println("冲泡咖啡");
}
@Override
public void addCondiments() {
System.out.println("加糖和牛奶");
}
/**
* 子类覆写了钩子函数,实现自定义功能
**/
@Override
boolean customerWantsCondiments() {
String result = getUserInput();
return "y".equals(result);
}
private String getUserInput() {
System.out.println("您想要在咖啡中加入牛奶或糖吗(y/n)?");
Scanner scanner = new Scanner(System.in);
return scanner.nextLine();
}
}
class Tea extends CaffeineBeverage {
@Override
public void brew() {
System.out.println("浸泡茶叶");
}
@Override
public void addCondiments() {
System.out.println("加柠檬");
}
}
class Test3 {
public static void main(String[] agrs) {
CaffeineBeverage coffee = new Coffee();
CaffeineBeverage tea = new Tea();
coffee.prepareRecipe();
System.out.println("------");
tea.prepareRecipe();
}
}
结果:
辅助理解以上用到的多态性质的代码:
class father{
public void all(){
out();
}
public void out(){
System.out.println("father");
}
}
class son extends father{
@Override
public void out() {
System.out.println("son");
}
}
public class Test4 {
public static void main(String[] args) {
father father = new father();
father.all();
System.out.println("-------");
father son = new son();
son.all();
}
}
结果: