目录
单例模式:
简单工厂模式
工厂方法模式
抽象工厂模式
策略模式
观察者模式
适配器模式
模板方法模式(模板模式)
装饰者模式
静态代理模式
动态代理模式
责任链模式
享元模式
迭代器模式
桥接模式
方式一:拿去吧!
如果一个对象被new了多次,但无法保证每个对象都会被使用,这时候就会很浪费空间,而且如果访问量巨大,服务器也要承担很大的压力。出现这种情况,一些Java大神们当然不会放任着不管,于是经过一番潜心研究,就出现了一种新的获得对象的方式——单例模式。
单例模式大概做的事情就是通过自己的设计,让当前的这个类只能产生一个对象(但是根据特定情况以及自己对于这一块的设计,不能一刀切的都去用这个方法,比如一个person类,每个person都有自己不同的属性,怎么能都用一个person来做事情呢)。
如果做?
构造方法:前面我们为了不能随便创建当前对象,已经给构造方法私有化,所以排除此方法。
代码块:代码块会在对象创建的时候就加载了,但是没有返回值,同样获取不到对象,排除。
普通方法:这个似乎和构造方法一样了,每次调用这个方法同样会创建一个对象出来,也无法做到对象时单例的。
属性: 似乎只能使用属性来new对象,然后通过方法来将对象传递。
public Singleton single = new Singleton();
上面这个写法很明显是不对的,而且在做单例上面,似乎没有任何存在的意义。
原因是啥呢?
首先,这样写上来就是一个StackOverflow Error。因为这是类里面的属性,类加载以后会加载自己里面的属性,方法等。但是当加载这个属性的时候,发现又new了一个自己,然后又去加载自己类,又遇到一个属性里面new了一个自己。。。陷入死循环导致栈溢出。
解决方法:保证是一份就OK啦。 public static Singleton single = new Singleton();
有些朋友可能看出还有一个问题:就是这样写我岂不是用类名就可以随便调用了,Singleton.single简直不要太简单。所以呢,为了让我的获取对象的方法更有存在感,必然是不能让您调用的嗷。
较为标准的写法:private static Singleton single = new Singleton();
好啦。属性对象创建好了,我怎么给你呢--->通过方法获得。 设计一个方法,将当前唯一的对象返回出去。
public static Singleton getInstance(){
//注意:这里return的是single属性中存储对象的地址引用
return single;
}
这种方式又叫做饿汉式,也就是:上来就创建一个对象,如同饿汉见到食物上来就吃。但是呢,这样在整个系统执行过程中,如果创建的对象不使用,就会一直占用着内存,造成了内存空间的浪费。
方式二:不用,我就不做!
这种方式呢,又叫做懒汉式,就是你可以要,但是我未必会创建,等到你用的时候我再创建,这样就很大程度上避免了内存空间浪费问题。写法和饿汉式几乎一致,代码如下:
private Singleton(){}
//我先不创建对象
private static Singleton single;
public static Singleton getInstance(){
//这个时候你要用了,但是我要看看是否真的没有创建好的对象吗,没有我再给你
if(single == null){
single = new Singleton();
}
return single;
}
有利肯定有弊:这时候要考虑到线程的问题了,比如A和B两个线程,同时需要这个对象,假如A先开辟了一块空间single,A正准备判断对象是否为空,这个时候时间片轮转,到B了,B也开辟了一块空间,然后A又创建了对象,但此时B判断对象已经不为空了,但是自己又开辟了一块空间,就比较冲突。当线程数更多的话,资源的争夺就会更明显。所以,请看下个分解。
方式三:我给锁住,让你抢我的东西!
在懒汉式的基础上加一个锁,就是我在用这个对象的时候,我要给他锁住,你们谁也不能动,直到我用完。代码实现如下:
private Singelton(){}
private static Singleton single;
//当前线程使用的时候不允许其他线程操作
public synchronized static Singleton getInstance(){
if(single == null){
single = new Singleton();
}
return single;
}
有利有弊:这种整个方法的执行过程中,不能有其他线程操作的方式,线程安全是解决了,万一你用很久,谁等的起啊。
方式四:双重判定!
就是将当前的类模板锁住,而不是锁住整个对象,这样其他线程进来如果对象不是空的,就可以直接返回了,而不需要去等待加锁的线程执行完毕,这样大大提高了执行效率。实现如下:
private Singelton(){}
private static Singleton single;
public static Singleton getInstance(){
//第一重判定,如果为空,加锁,创建对象
if(single == null){
synchronized(Singleton.class){
//第二重判定,如果为空 创建对象
if(single == null){
single = new Singleton();
}
}
}
return single;
}
有利有弊:这样在解决了加锁性能的问题的同时,在JVM虚拟机中,在对象的开辟和赋值的过程中,可能会产生指令重排序。本来时1 ——> 2 ——> 3的顺序,可能会变成 1 ——> 3 ——> 2。
instance
分配内存空间。instance
。instance
指向分配的内存地址。方式五:大哥来了!
属性上添加volatile,保证属性在加载和赋值的过程中,不会被JVM指令重排序。代码如下:
private Singleton single(){}
private static volatile Singleton single;
public static Singleton getInstance(){
if(single == null){
synchronized(Singleton.class){
if(single == null){
single = new Singleton();
}
}
}
}
大家所熟知的有工厂方法模式、抽象工厂模式,那么简单工厂模式又是什么呢?简单工厂模式不能算作一个标准的设计模式,但是呢,在开发之中又挺常用的,那么就当做一个热身吧!
举一个小案例:话说有一天晚上,博主突发饿疾,非要去附近的小作坊买一些关东煮吃吃。且附近几家都有卖。那么问题来了,我新来的这个地方,还不熟悉,我还没吃过这附近的关东煮呢,万一关东煮里面有其他东西,出了问题,我找谁去呢??找我买东西的那家店?万一人家无证经营跑路了呢。所以为了安全起见,我需要看到人家的营业执照才放心的买。那么这就要求这些小作坊需要被统一管理起来(颁发营业执照,你这家店可以卖关东煮),或者说的贴切代码一些(遵循一个规则)。
好,假设有三家小作坊店,为了方便理解,类名就用拼音了(其实是我不会英语)。那么:GuanDongZhu1.java GuanDongZhu2.java GuanDongZhu3.java。这是我附近的三家店。现在我要给他们指定一个规则,允许他们卖关东煮。AcceptGuanDongZhu.java。既然是指定规则的类,我们就可以将它指定为借口或者父类。此例中将其指定为借口。那么三个小作坊店都实现这个借口里面的方法(卖关东煮)。代码如下:
GuanDongZhu1.java
public class GuanDongZhu1 implements AcceptGuanDongZhu{
@Override
public void sale() {
System.out.println("GuanDongZhu1号店也售卖关东煮,博主快来吃啊");
}
}
GuanDongZhu2.java
public class GuanDongZhu2 implements AcceptGuanDongZhu{
@Override
public void sale() {
System.out.println("GuanDongZhu2号店售卖关东煮,博主快来吃啊");
}
}
GuanDongZhu3.java
public class GuanDongZhu3 implements AcceptGuanDongZhu{
@Override
public void sale() {
System.out.println("GuanDongZhu3号店关东煮最好吃,博主快来吃啊");
}
}
AcceptGuanDongZhu.java
public interface AcceptGuanDongZhu {
//售卖关东煮的接口方法(营业执照) 默认用 public abstract修饰
void sale();
}
TestMain.java
public static void main(String[] args) {
//多态来创建子类对象
AcceptGuanDongZhu sale = new GuanDongZhu1();
sale.sale();
}
好,营业执照给了,小作坊也遵循了,但是!新的问题来了。用户这样用,是不是知道的太多了。首先,他知道了我的接口规则(实现了AcceptGuanDongZhu这个接口),其次,我具体的实现类也让他知道了,这完全违背封装隔离的思想啊。那么我就需要把用户和店隔开,用户可以看见我的实现规则,但是呢,我不能让你知道我的具体实现类是哪个,你可以通过传参数的方式,告知我想去哪个店,我给你找。而中间这个隔离层呢,就是我们今天的重点了——Factory。就是你告诉我工厂,你要买关东煮了,如果你传给我一个参数(店名),那我就把这个店给到你,不传,我就随便给你一个。按照这种封装隔离的思想,把具体的实现类包装起来。代码实现如下:
GuanDongZhuFactory.java
//调用我工厂 我要给你返回一个店 你给我传一个店名 或者不传我就随机给你一个
public AcceptGuanDongZhu getGuanDongZhu(String name){
//下面就简单做个判断,重要的是思想
if(!"".equals(name) && "GuanDongZhu1".equals(name)){
return new GuanDongZhu1();
}else if(!"".equals(name) && "GuanDongZhu2".equals(name)){
return new GuanDongZhu2();
}else if(!"".equals(name) && "GuanDongZhu3".equals(name)){
return new GuanDongZhu3();
}else{
//啥也不传,我给你选一个
return new GuanDongZhu1();
}
}
那么这时候用户找店的方式就应该变成:
TestMain.java
public static void main(String[] args) {
//先创建工厂
GuanDongZhuFactory factory = new GuanDongZhuFactory();
AcceptGuanDongZhu sale = factory.getGuanDongZhu("GuanDongZhu1");
sale.sale();
}
引子:为什么会有工厂方法模式?
在上面的简单工厂模式中,用户通过创建工厂对象来获取目标对象(GuanDongZhu对象),那么由于关东煮不止一家,用户肯定有自己想去的一家,在简单工厂模式中依靠传参数来决定选择哪一家,那这就意味着用户需要了解参数的意义。而且啊,添加了参数,我工厂处理的时候岂不是多了很多的判断,如:下面就是简单工厂中的工厂要做的事情。
if(!"".equals(name) && "GuanDongZhu1".equals(name)){
return new GuanDongZhu1();
}else if(!"".equals(name) && "GuanDongZhu2".equals(name)){
return new GuanDongZhu2();
}else if(!"".equals(name) && "GuanDongZhu3".equals(name)){
return new GuanDongZhu3();
}else{
//啥也不传,我给你选一个
return new GuanDongZhu1();
}
这样,每新开一家店,大工厂就要多一个判断,是不是挺麻烦啊。可是不判断呢,我又不知道该创建哪个具体的对象,唉,好烦,不干了!对,不干了——无为