1单例模式
1.1为什么单例模式单例就线程可能不安全
因为一个东西很多用。servlet就是案例。 属性就是数据,有属性就不安全。
1.2使用场合
对象开销比较大的时候,所有创建一份,用同步解决多人使用同一份数据的问题。
单例设计模式案例:
说明:用于不能两个人new ,所以第一步是构造器私有化,除了该类的内部可以new。
所以需要一个方法,对外让别人创建对象。并且方法必须是static,不能是实例方
法都不能创建实例,怎么调用方法呢。
package danli;
public class putongdanli {
private static putongdanli a;
private putongdanli(){
System.out.println("A被创建了!");
}
public static putongdanli getInstance(){
if(a==null){
a=new putongdanli();
}
return a;
}
}
测试代码:
package danli;
public class text {
public static void main(String[] args) {
// 普通
putongdanli1.getInstance();
putongdanli1.getInstance();
// 懒汉
/*new Thread(){
public void run(){
putongdanli.getInstance();
}
}.start();
new Thread(){
public void run(){
putongdanli.getInstance();
}
}.start();*/
// 恶汉
/* ehan.getInstance();
ehan.getInstance();*/
// 优化恶汉
// youhuaehan.getInstance();
// youhuaehan.getInstance();
}
}
结果如下:
如图所示:putongdanli1只被实例化一次;
1.3在多线程新的情况下的单例模式(懒汉模式)
上述例子在多线程的情况下是吧适用的。所以下面的代码作出了改进。
代码如下:
package danli;
public class putongdanli {
private static putongdanli a;
private putongdanli(){
System.out.println("A被创建了!");
}
public synchronized static putongdanli getInstance(){
if(a==null){
a=new putongdanli();
}
return a;
}
}
测试代码如下:
package danli;
public class text {
public static void main(String[] args) {
// 普通
// putongdanli1.getInstance();
// putongdanli1.getInstance();
// 懒汉
new Thread(){
public void run(){
putongdanli.getInstance();
}
}.start();
new Thread(){
public void run(){
putongdanli.getInstance();
}
}.start();
// 恶汉
/* ehan.getInstance();
ehan.getInstance();*/
// 优化恶汉
// youhuaehan.getInstance();
// youhuaehan.getInstance();
}
}
上述案例的解决方案:
多线程单例:懒汉模式
synchronized: 每一个类都有一个唯一class,封装类信息。
持有类的class对象的监视器对象
先完成AA类的加载,完成初始化。
public synchronized AA getInstabce(){
//同步实例方法,拿到this的监视器
}
1.4饿汉模式
两个线程同时启动:第一个线程声明初始化时,第二个线程势必也来了,也想初始化类。
但是jvm中有自己的锁,
懒汉模式和饿汉模式的区别:饿汉是线程安全的,懒汉如果在创建实例对象的时,不加上
synchronized关键字会导致对象的访问不是线程安全的。
懒汉式演示加载,他实在需要的时候才创建对象,而饿汉在虚拟机启动的时候会创建,饿汉无需关注过线程问题,写法简单明了,能用则用。
问题:懒汉模式不仅是懒加载,后面使用的效率也不高,因为需要检查,需要开销。
又想保留懒加载特性,又要保证是单例。
代码如下:
package danli;
public class ehan {
private static ehan a =new ehan();
private ehan(){
System.out.println("初始化成功");
}
public static ehan getInstance(){
return a;
}
}
测试代码:
package danli;
public class text {
public static void main(String[] args) {
// 普通
// putongdanli1.getInstance();
// putongdanli1.getInstance();
// 懒汉
new Thread(){
public void run(){
putongdanli.getInstance();
}
}.start();
new Thread(){
public void run(){
putongdanli.getInstance();
}
}.start();
// 恶汉
/* ehan.getInstance();
ehan.getInstance();*/
// 优化恶汉
// youhuaehan.getInstance();
// youhuaehan.getInstance();
}
}
1.5优化懒汉模式
问题:懒汉模式不仅是懒加载,后面使用的效率也不高,因为需要检查,需要开销。
又想保留懒加载特性,又要保证是单例
//创建静态成员内部类,控制初始化,相当于在懒汉中加了个饿汉
//实现了延迟加载。用到X类,对象被初始化
//此处假如不给static,要使用X必须实例化AAAA
//次数就将X看做是一个属性,或者实例方法
代码如下:
package danli;
public class youhuaehan {
private static class X{//静态成员内部内类
static youhuaehan a=new youhuaehan();
}
private youhuaehan(){
System.out.println("A被创建了!");
}
public static youhuaehan getInstance(){
return X.a;
}
}
测试代码:
package danli;
public class text {
public static void main(String[] args) {
// 普通
// putongdanli1.getInstance();
// putongdanli1.getInstance();
// 懒汉
// new Thread(){
// public void run(){
// putongdanli.getInstance();
// }
// }.start();
//
// new Thread(){
// public void run(){
// putongdanli.getInstance();
// }
// }.start();
// 恶汉
/* ehan.getInstance();
ehan.getInstance();*/
// 优化恶汉
youhuaehan.getInstance();
youhuaehan.getInstance();
}
}
结果如下:
这里使用静态私有的内部类实现了优化的懒汉模式;
2代理模式
案例:租房子 租房中介就是代理 房子
代理的作用:中介---租房子+附加功能。
代理的好处:对象的基本功能(产品本应该需要做的事情)+附加功能
Equals:只能知道字符串是否一样,假如不一样他不知道哪里不一样。
使用代理去代理Equals,我再代理的基础之上去只写判断哪里不一样的算法。
前一百名八五折优惠+基本功能+附加功能 – 58同城(租房子三次,送一次免费搬家)
代理在程序中能做什么?
equals:方法判断两个字符串是否相等。
可以使用代理设计模式去代理equals
可以在用户的基本功能之前或之后添加一些附加功能。
2.1代理模式的目的在于
为其他对象(租房子的人)提供一种代理的方式控制被代理对象(房子)—。
控制访问:有些对象直接访问不太方便或不合理,此时通过中介访问。
2.2结构组成
代理模式主要涉及到三个角色:抽象角色、代理角色、真实角色(被代理的角色)。
抽象角色:声明真实对象和代理对象的共同接口。即真实对象和代理对象共同要实现的行为动作(好比房东和中介都要能够实现租房的行为,都能把房子租给你)。
代理角色:代理角色内部含有对真实角色的引用,从而可以去操作真实对象,同时代理对象提供与真实对象的接口,以便在任何时候都能代替真实对象。同时,代理对象在执行真实对象的操作时,也能附加它自己的操作,相当于对真实对象的封装(可以理解为中介在执行将房东的房子租给你这一操作时,可以向你收取中介费,还可以在退房的时候扣你押金这类房东不具有的操作)。
真实角色:代理角色所代理的对象,亦即我们最终要引用的对象。
2.3代理角色好处
1)实现基本功能+附加功能
2)分离了用户与真实角色之间的直接联系,降低了程序的复杂度,增加了程序的灵活性
3)代理是一种模式,提供了对目标对象的间接访问方式,即通过代理访问目标对象。如此便于在实现的基础上增加额外的功能操作,前拦截,后拦截等,以满足自身的业务需求,同时代理模式便于扩展目标对象功能的特点也为多人所用。
2.4代理模式的实现
在代理设计模式中,抽象角色是指代理类和真实对象(被代理对象)必须共同实现的接口或抽象类。
抽象角色定义了真实对象和代理对象之间的共同行为,通过这个共同的接口或抽象类,代理类可以在不改变真实对象的情况下对其进行访问和控制。
抽象角色的实现:
// 抽象角色接口或抽象类
public interface Subject {
void doSomething();
}
真实角色:
// 真实对象角色
public class RealSubject implements Subject {
@Override
public void doSomething() {
System.out.println("RealSubject is doing something.");
}
}
代理角色:
// 代理角色
public class Proxy implements Subject {
private RealSubject realSubject;
public Proxy(RealSubject realSubject) {
this.realSubject = realSubject;
}
@Override
public void doSomething() {
// 在调用真实对象之前进行一些操作
System.out.println("Proxy is doing something before calling RealSubject.");
// 调用真实对象的方法
realSubject.doSomething();
// 在调用真实对象之后进行一些操作
System.out.println("Proxy is doing something after calling RealSubject.");
}
}
在上述示例中,Subject
是代理类和真实对象必须共同实现的抽象角色。RealSubject
类是真实对象角色,实现了 Subject
接口的方法。Proxy
类是代理角色,也实现了 Subject
接口,并持有一个真实对象的引用。它通过调用真实对象的方法来实现抽象角色中定义的行为,并可以在调用前后进行额外的操作。
通过抽象角色的定义,代理类和真实对象之间可以进行松耦合的交互,代理类可以对真实对象进行控制和访问,同时还可以实现各种代理模式的特定功能,如远程代理、虚拟代理、保护代理等。
3工厂模式
代替我们自己创造东西:代替我们自己去创建对象
工厂的作用:就是将对象的创建专门交给工厂去完成。
DiskFileItemFactory
ServletFileUpload
为什么交给工厂:
原先的方式:new Student();
1、工厂产生对象的成本更低
2、当前:将对象的销毁完全交给垃圾回收机制,资源的集约是不利的。
3、工厂分离用户和对象本身。
3.1简单工厂设计的三大角色
工厂角色(Creator):这是简单工厂模式的核心,由它负责创建所有的类的内部逻辑。当然工厂类必须能够被外界调用,创建所需要的产品对象。
抽象(Product)产品角色:简单工厂模式所创建的所有对象的父类,注意,这里的父类可以是接口也可以是抽象类,它负责描述所有实例所共有的公共接口。
具体产品(Concrete Product)角色:简单工厂所创建的具体实例对象,这些具体的产品往往都拥有共同的父类。
去饭店吃饭:
工厂角色:饭店
抽象角色:food interface—abstract
具体产品:油渣大白菜
3.2工厂模式实例1
工厂模式是一种创建对象的设计模式,它提供了一种封装对象创建过程的方式,使得客户端代码与具体对象的创建逻辑解耦。
在工厂模式中,有一个共同的抽象工厂接口或抽象类,用于定义创建对象的方法。具体的对象创建由实现该接口或继承该抽象类的具体工厂类负责完成。客户端代码通过调用工厂方法来获取所需的对象实例,而无需关心具体的对象创建细节。
以下是工厂模式的示例代码:
// 抽象产品接口
public interface Product {
void doSomething();
}
// 具体产品A
public class ConcreteProductA implements Product {
@Override
public void doSomething() {
System.out.println("ConcreteProductA is doing something.");
}
}
// 具体产品B
public class ConcreteProductB implements Product {
@Override
public void doSomething() {
System.out.println("ConcreteProductB is doing something.");
}
}
// 抽象工厂接口
public interface Factory {
Product createProduct();
}
// 具体工厂A
public class ConcreteFactoryA implements Factory {
@Override
public Product createProduct() {
return new ConcreteProductA();
}
}
// 具体工厂B
public class ConcreteFactoryB implements Factory {
@Override
public Product createProduct() {
return new ConcreteProductB();
}
}
在上述示例中,Product
是抽象产品接口,定义了产品的行为。ConcreteProductA
和 ConcreteProductB
是具体产品类,实现了 Product
接口。
Factory
是抽象工厂接口,定义了创建产品的方法。ConcreteFactoryA
和 ConcreteFactoryB
是具体工厂类,分别实现了 Factory
接口,并负责创建 ConcreteProductA
和 ConcreteProductB
对象。
客户端代码使用具体工厂来创建产品,而不直接依赖具体产品的类。这样,客户端可以通过调用工厂的方法获取所需的产品对象,从而达到解耦的效果。
// 客户端代码
public class Client {
public static void main(String[] args) {
Factory factoryA = new ConcreteFactoryA();
Product productA = factoryA.createProduct();
productA.doSomething();
Factory factoryB = new ConcreteFactoryB();
Product productB = factoryB.createProduct();
productB.doSomething();
}
}
在以上客户端代码中,通过具体工厂来创建产品对象,并调用产品的方法进行操作。客户端不直接与具体产品类进行交互,而是通过工厂来创建产品,从而实现了对象的解耦。
3.3简单工厂的优点
优点:工厂类是整个模式的关键所在。它包含必要的判断逻辑,能够根据外界给定的信息,决定究竟应该创建哪个具体类的对象。用户在使用时可以直接根据工厂类去创建所需的实例,而无需了解这些对象是如何创建以及如何组织的。有利于整个软件体系结构的优化。
3.4简单工厂的缺点
种类单一的缺点:由于工厂类集中了所有实例的创建逻辑,这就直接导致一旦这个工厂出了问题,所有的客户端都会受到牵连;
种类增加的缺点:
而且由于简单工厂模式的产品室基于一个共同的抽象类或者接口,这样一来,当产品的种类增加的时候,即有不同的产品接口或者抽象类的时候,工厂类就需要判断何时创建何种种类的产品,这就和创建何种种类产品的产品相互混淆在了一起,违背了单一职责,导致系统丧失灵活性和可维护性。而且更重要的是,简单工厂模式违背了“开放封闭原则”,就是违背了“系统对扩展开放,对修改关闭”的原则,因为当我新增加一个产品的时候必须修改工厂类,相应的工厂类就需要重新编译一遍。
总结:当产品按照种类增加的时候,违背了单一原则和开闭原则。
缺点的前提:当产品的种类增加的时候。
好了今天就分享这些设计模式和对应的案例!!!!!