(应用场景和思想:)
1.单例模式
2.抽象工厂方法
3.简单工厂方法
4.观察者模式(发布-订阅模式)
(1-3:创建型模式,省去new的过程,帮助我们更好的创建对象)
(类实现接口,接口写将要实现的方法,实现在类中写)
1.单例模式
· 单例思想:构造器私有+类内创建对象(getInstance()函数内),别人就不能new这个对象了,保证内存中只有一个对象
枚举能避免单例模式被破坏
java存在 反射,任何代码都不安全,任何private关键字都是纸老虎
· 饿汉式单例,一上来就把对象加载了,//饿有可能浪费内存
package DesignPatterns.Single;
public class Hungry {
private Hungry(){
}
private final static Hungry HUNGRY =new Hungry();//一上来就把对象加载了
public static Hungry getInstance(){
return HUNGRY;
}
}
· 懒汉式单例:想要用的时候,再创建对象 //双重检测锁(DLC懒汉式)+原子性操作
加锁:单例模式下,多线程并发,会生成多个实例(每次运行都不一样),破坏了单例模式,所以在调用实例化函数时,实例化函数内部要 加锁。
package DesignPatterns.Single;
public class LazyMan {
private LazyMan(){
System.out.println(Thread.currentThread().getName());
}
private volatile static LazyMan lazyMan;
//双重检测锁模式的 懒汉式单例 DLC懒汉式
public static LazyMan getInstance(){//提供给外部的函数
//加锁前可能会有两个线程,要做两次检测
if(lazyMan==null){
synchronized (LazyMan.class){
if(lazyMan==null){//在锁里面再判断一次
lazyMan=new LazyMan();//不是一个原子行操作,有几步操作
}
}
}
return lazyMan;
}
//多线程并发
public static void main(String[] args){
for (int i = 0; i <10 ; i++) {
new Thread(()->{LazyMan.getInstance();}).start();
}
}
}
要点:
lazyMan=new LazyMan(); 对象实例化过程 不是一个原子行操作,有几步操作
1.分配内存
2.执行构造方法,初始化对象
3.把这个对象指向这个空间
有可能出现 指令重排的现象,期望执行123,真是情况可能执行132
例如 132 线程A (先分配空间,把内存空间占用,再执行2)
线程B(由于lazyMan 对象已经指向一个空间了,双重检测的第一重,lazyMan!=null)
/此时 会直接执行 return lazyMan; 但lazyMan还没完成构造,空间内是虚无的
/为了安全 lazyMan避免指令重排 lazyMan定义的时候加volatile
·静态内部类(单例模式)//不安全,java存在 反射
的
package DesignPatterns.Single;
public class Holder {//外部类
private Holder(){} //单例模式构造器私有
public static Holder getInstance(){
return InnerClass.holder;
}
public static class InnerClass{//静态内部类
private static final Holder holder=new Holder();//内部类里示例化外部类
}
的
· 反射 ,破坏单例
declaredConstructor.setAccessible(true);//无视私有构造器,就可以通过反射构建对象
解决办法:在构造器里加锁,锁类后,再判断 类对象!=null ? 手动抛出异常打印字段“不可试图使用反射破坏单例”
package DesignPatterns.Single;
import java.lang.reflect.Constructor;
public class LazyMan {
private LazyMan(){
//解决 反射破坏单例的方法
synchronized (LazyMan.class){
if(lazyMan!=null){
throw new RuntimeException("不可试图使用反射破坏单例");
}
}
}
private volatile static LazyMan lazyMan;
//双重检测锁模式的 懒汉式单例 DLC懒汉式
public static LazyMan getInstance(){//提供给外部的函数
//加锁前可能会有两个线程,要做两次检测
if(lazyMan==null){
synchronized (LazyMan.class){
if(lazyMan==null){//在锁里面再判断一次
lazyMan=new LazyMan();//不是一个原子行操作,有几步操作
}
}
}
return lazyMan;
}
//上面代码与 懒汉式 一样
//反射
public static void main(String[] args) throws Exception {
LazyMan instance1 =LazyMan.getInstance();
Constructor<LazyMan> declaredConstructor=LazyMan.class.getDeclaredConstructor();//空参构造器(公有和私有),许抛出异常
declaredConstructor.setAccessible(true);//无视私有构造器,就可以通过反射构建对象
LazyMan instance2=declaredConstructor.newInstance();//通过反射构建对象,调用类的 无参构造方法
System.out.println(instance1);
System.out.println(instance2);
}
}
输出:DesignPatterns.Single.LazyMan@50cbc42f
DesignPatterns.Single.LazyMan@75412c2f //值不一样
(删掉 解决办法 代码块的输出)
两个实例都通过反射来创建,破坏单例,可以通过在构造函数加锁后设置标志位 解决单例破坏问题。
public class LazyMan {
//通过设置标志位判断 单例
private static boolean flag=false;
private LazyMan(){
synchronized (LazyMan.class){
if(flag==false) flag=true;
else{
throw new RuntimeException("不可试图使用反射破坏单例");
}
}
}
private volatile static LazyMan lazyMan;
public static LazyMan getInstance(){}
//反射
public static void main(String[] args) throws Exception {
//LazyMan instance1 =LazyMan.getInstance();
Constructor<LazyMan> declaredConstructor=LazyMan.class.getDeclaredConstructor();//空参构造器(公有和私有),许抛出异常
declaredConstructor.setAccessible(true);//无视私有构造器,就可以通过反射构建对象
LazyMan instance2=declaredConstructor.newInstance();//通过反射构建对象,调用类的 无参构造方法
LazyMan instance3=declaredConstructor.newInstance();//通过反射构建对象,调用类的 无参构造方法
}
}
在知道标志 后,可以通过反射 修改 标志值(类属性),flag.setAccessible(true);//破坏属性的私有权限,
import java.lang.reflect.Field;
public class LazyMan {
//通过设置标志位判断 单例
private static boolean flag=false;
private LazyMan(){
synchronized (LazyMan.class){
if(flag==false) flag=true;
else{
throw new RuntimeException("不可试图使用反射破坏单例");
}
}
}
private volatile static LazyMan lazyMan;
public static LazyMan getInstance(){}
//反射
public static void main(String[] args) throws Exception {
//LazyMan instance1 =LazyMan.getInstance();
Field flag=LazyMan.class.getDeclaredField("flag");//拿到这个标志,属性
flag.setAccessible(true);//破坏属性的私有权限
Constructor<LazyMan> declaredConstructor=LazyMan.class.getDeclaredConstructor();//空参构造器(公有和私有),许抛出异常
declaredConstructor.setAccessible(true);//无视私有构造器,就可以通过反射构建对象
LazyMan instance2=declaredConstructor.newInstance();//通过反射构建对象,调用类的 无参构造方法
flag.set(instance2,false);// 把 标志 设置回false
LazyMan instance3=declaredConstructor.newInstance();
}
}
· 反射不能破坏枚举的单例模式
package Reflection;
import java.lang.reflect.Constructor;
//enum 是一个Class类
public enum EnumSingle {//枚举类
INSTANCE; //这个对象默认就是单例的
public EnumSingle getInstance(){
return INSTANCE;
}
}
class test{
public static void main(String[] args) throws Exception {
EnumSingle instance1=EnumSingle.INSTANCE;
EnumSingle instance2=EnumSingle.INSTANCE;//他们是一样的
//尝试破坏 枚举单例
Constructor<EnumSingle> declaredConstructor=EnumSingle.class.getDeclaredConstructor(String.class,int.class);//有参构造器(公有和私有),许抛出异常
declaredConstructor.setAccessible(true);
EnumSingle instance3=declaredConstructor.newInstance();
}
}
//输出:
Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects
k
2.工厂模式:创建者和调用者分离
2.1 抽象工厂方法
围绕一个超级工厂创建其他工厂。该工厂又称为其他工厂的工厂。如果增加产品,只需在产品接口中增加方法,其他工厂类负责实现。
(类实现接口,接口写将要实现的方法,实现在类中写)
手机接口,华为,小米类实现手机接口。
工厂返回抽象的抽象
2.2 简单工厂方法(静态工厂方法)
用来生产同一等级结构中的任意产品。虽然某种程度上不符合设计原则,但实际使用最多
(对于增加新的产品,需要修改已有代码,例如修改工厂的代码。缺点:没有满足开闭原则)
通过工厂类来生成同一等级结构类的实例,通过编写工厂类的一个静态方法,接收不同的参数来返回不同的对象实例 or 编写工厂类的多个静态方法来生成实例。例子:一个Car工厂类,生产不同名字/类型的车类。消费者想购买车,不应该是消费者来生产new xxcar(new过程有很多参数,太麻烦了,无需关心生成实例过程的细节,方便消费者/调用者使用对象),而是告诉工厂类,需要买什么类型的车,由工厂来生产new。
public interface Car {
void name();
}
public class WuLing implements Car{
@Override
public void name() {
System.out.println("五菱宏光");
}
}
public class Tesla implements Car{
@Override
public void name() {
System.out.println("特斯拉");
}
}
public class CarFactory { //车工程只需要 车的名字 即可生产车
public static Car getCar(String car){
//方法一
if(car.equals("五菱")){
return new WuLing();
}else if(car.equals("特斯拉")){
return new Tesla();
}
return null;
}
//方法二
public static Car getWuling(){
return new WuLing();
}
public static Car getTesla(){
return new Tesla();
}
}
public class Consumer {
public static void main(String[] args) {
//1.接口,所有的实现类
//Car car1=new WuLing();//new一个对象,有很多参数,用工程不需要考虑这些
//Car car2=new Tesla();
//2.使用工厂创建
Car car1=CarFactory.getCar("五菱");
}
}
4.观察者模式(发布-订阅模式)
public class ObserverPattern {
public static void main(String[] args) {
Debit zhangSan=new ZhangSan();
zhangSan.brrow(new Wangwu());//找Wangwu借钱
zhangSan.brrow(new Zhaoyi());//找Zhaoyi借钱
//state改变
zhangSan.notifyCredits();//张三通知贷款方,找我要钱
}
}
interface Debit{//借款方接口
void brrow(Credit credit);//想别人借钱
void notifyCredits();//通知机制,通知借贷方 我要还款
}
interface Credit{//贷款方接口(观察者,他观察借款人的状态,比如有钱,没钱 )
void takeMone();//要钱
}
class ZhangSan implements Debit{
private List<Credit> allCredit =new ArrayList<>();//借了不止一家,定义数据结构来维护所有的贷款方
@Override
public void brrow(Credit credit) {
allCredit.add(credit);//添加一个观察者对象
}
@Override
public void notifyCredits() {//通知机制,一旦有钱了就通知贷款方
allCredit.forEach(credit -> credit.takeMone());//循环调用贷款方,找我要钱
}
}
class Wangwu implements Credit{
@Override
public void takeMone() {
System.out.println("Wangwu要钱");
}
}
class Zhaoyi implements Credit{
@Override
public void takeMone() {
System.out.println("Zhaoyi要钱");
}
}
5.工厂方法模式(不修改已有类的前提下,通过增加新的工厂类实现扩展)
满足开闭原则,不用修改已有代码,动态扩展,但是代码量多:每种类型的车有自己的工厂,没有改变原来的类(其他车的类和工厂的类),而是通过新增类来实现 增加新的产品(新增车类和车工厂)
结构复杂度,代码发杂度,客户端编程复杂度,管理复杂度来看 : 简单工厂模式 好
根据设计原则:工厂方法模式 好
根据实际业务:简单工厂模式
public interface Car {
void name();
}
public interface CarFactory {
Car getCar();
}
public class Tesla implements Car {
@Override
public void name() {
System.out.println("特斯拉");
}
}
public class TeslaFactory implements CarFactory{
@Override
public Car getCar() {
return new Tesla();
}
}
public class WuLing implements Car {
@Override
public void name() {
System.out.println("五菱宏光");
}
}
public class WuLingFactory implements CarFactory{
@Override
public Car getCar() {
return new WuLing();
}
}
public class Consumer {
public static void main(String[] args) {
Car car1=new WuLingFactory().getCar();//去找五菱的工厂,得到他的车
car1.name();
}
}