文章目录
重要的几种设计模式
1.1 单例模式
最重要的思想是构造器私有,保证其他人无法new这个对象
1.12 饿汉式
/*
* 单例模式学习-饿汉式
*/
public class SingleObj {
// 通过饿汉式创建对象,会将此类中所有方法和属性提前创建出来,会占用一定的空间,如果不使用这个对象,就会造成资源上的浪费
private String[] str = new String[1024];
private String[] str1 = new String[1024];
private String[] str2 = new String[1024];
// ...属性
/*public void show();
* ...方法
* */
private SingleObj(){}//构造器私有
private final static SingleObj singleObj = new SingleObj();//饿汉式,无论是否使用这个对象,先创建出来
public static SingleObj getInstance(){
return singleObj;
}
}
1.13 DCL懒汉式
1.131 普通懒汉式
package com.hr.singleInstance;
/*
* 单例模式学习-普通懒汉式
*/
public class SingleObj {
// 通过懒汉式创建对象,只会在需要的时候才会创建此类中所有方法和属性,相比饿汉式要节省资源
private String[] str = new String[1024];
private String[] str1 = new String[1024];
private String[] str2 = new String[1024];
// ...属性
/*public void show();
* ...方法
* */
private SingleObj(){}//构造器私有
private static SingleObj singleObj;//懒汉式,默认为null
// 缺点:在普通情况下可以符合“单例”的需求,但是在多线程下,就会创建多个对象
public static SingleObj getInstance(){
if(singleObj==null){
singleObj = new SingleObj();
}
return singleObj;
}
}
//启动5个线程,进行启动,并都去获得懒汉式的单例对象,会发现有不同的对象
class Test{
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
new Thread(new Runnable() {
@Override
public void run() {
SingleObj obj = SingleObj.getInstance();
System.out.println(obj);
}
}).start();
}
}
}
控制台显示结果:
com.hr.singleInstance.SingleObj@5ab10c2d
com.hr.singleInstance.SingleObj@20c6ebb
com.hr.singleInstance.SingleObj@20c6ebb
com.hr.singleInstance.SingleObj@2aafbcf4
com.hr.singleInstance.SingleObj@5ab10c2d
1.132 双重检测锁的懒汉模式
package com.hr.singleInstance;
/*
* 单例模式学习-DCL懒汉式(双重检测锁下的懒汉式)
*/
public class SingleObj {
// 通过懒汉式创建对象,只会在需要的时候才会创建此类中所有方法和属性,相比饿汉式要节省资源
private String[] str = new String[1024];
private String[] str1 = new String[1024];
private String[] str2 = new String[1024];
// ...属性
/*public void show();
* ...方法
* */
private SingleObj(){}//构造器私有
private static SingleObj singleObj;//懒汉式,默认为null
public static SingleObj getInstance(){
if(singleObj==null){//只有第一次调用时,才需要同步判断(此时instance未初始化,为null)。一旦instance初始化完成,就不需要了(重要!!!)
//将这个类对象作为锁,谁得到这个类对象谁就可以执行其中的代码
synchronized (SingleObj.class){
if(singleObj==null){
singleObj = new SingleObj();
}
}
}
return singleObj;
}
}
//启动5个线程,进行启动,并都去获得懒汉式的单例对象,会发现对象都相同
class Test{
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
new Thread(new Runnable() {
@Override
public void run() {
SingleObj obj = SingleObj.getInstance();
System.out.println(obj);
}
}).start();
}
}
}
控制台输出:
com.hr.singleInstance.SingleObj@8a284c4
com.hr.singleInstance.SingleObj@8a284c4
com.hr.singleInstance.SingleObj@8a284c4
com.hr.singleInstance.SingleObj@8a284c4
com.hr.singleInstance.SingleObj@8a284c4
原理:只要有一个线程创建好了SingleObj对象,那么其他的线程将不会执行创建对象代码,将会直接返回第一个线程创建好的对象
防止指令重排
在上述DCL懒汉式基础上进一步改进:
private static volatile SingleObj singleObj;//懒汉式,默认为null,防止指令重排
public static SingleObj getInstance(){
if(singleObj==null){
//将这个类对象作为锁,谁得到这个类对象谁就可以执行其中的代码
synchronized (SingleObj.class){
if(singleObj==null){
singleObj = new SingleObj();//不是一个原子性操作,而是三步操作
/*
执行上述new 构造器()底层会有三步实现:
1、分配内存空间
2、执行构造方法,初始化对象
3、将引用指向内存空间
步骤应该是从上至下的,但是也有可能不会按照上述顺序,假设线程一执行顺序为1- -->3--->2
并假设第二个线程在线程一执行到第3步时获得了锁,此时内存空间中还没有对象, 那么线程二直接返回的对象可能为null
这种现象就叫指令重排
为了防止指令重排,需要使用volatile关键字
volatile:在一个对象前加上此关键字,可以使第3步操作一定是最后执行
*/
}
}
}
return singleObj;
}
}
BUT!!!反射仍可以破环这种反射,通过反射将类的私有构造方法变成公开的,那么就可以无限进行创造新的对象,可以在构造方法中添加判断来解决这种破环
1.2 工厂模式
本质:实例化对象不用new,采用工厂中的方法代替
实现了实现对象和调用对象的分离
1.21 为什么要使用工厂模式
public class Test {
public static void main(String[] args) {
// 每次生产一辆汽车,需要自己创建相应的对象,需要知道对象的参数,然后才能调用其中的方法或属性。
// 可以通过工厂模式将创建对象的工程进行包装,然后直接从工厂中获取对象
Car c1 = new Car1();
Car c2 = new Car2();
c1.getName();
c2.getName();
}
}
1.22 简单工厂模式(静态工厂模式)
本质:将所有的车的构造方法全部放入一个CarFactory中,通过一个方法调用
public class CarFactory {
public Car getCar(String carName){
if(carName.equals("car1")){
return new Car1();
}else if (carName.equals("car2")){
return new Car2();
}else{
return null;
}
}
}
public class Test {
public static void main(String[] args) {
// 通过简单工厂模式将创建对象的工程进行包装,然后直接从工厂中获取对象
CarFactory carFactory = new CarFactory();
Car c1 = carFactory.getCar("car1");
Car c2 = carFactory.getCar("car2");
c1.getName();
c2.getName();
}
}
缺点:每次产生一种新的对象类型时,需要在工厂中添加相应的创建方式
1.23 工厂方法模式
本质:将工厂也变成一个接口,当新的车出现的时候,就专门给其建立一个工厂
简单工厂模式(所有车的工厂在一起) 工厂方法模式(所有车的工厂分开)
public class Car1Factory implements CarFactory{
@Override
public Car getCar() { return new Car1(); }
}
public class Car2Factory implements CarFactory{
@Override
public Car getCar() {
return new Car2();
}
}
public class Test {
public static void main(String[] args) {
// 通过简单工厂模式将创建对象的工程进行包装,然后直接从工厂中获取对象
CarFactory car1Factory = new Car1Factory();
CarFactory car2Factory = new Car2Factory();
Car car1 = car1Factory.getCar();
Car car2 = car2Factory.getCar();
car1.getName();
car2.getName();
}
}
这种方法使用的不多,因为增加了代码的冗余,推荐第一种
1.24 抽象工厂模式
本质:建立一个工厂(超级工厂),可以建造其他的工厂,当需要某一类汽车的工厂时,直接通过超级工厂来获取其他汽车的工厂,在通过其他汽车的工厂建造汽车,这个工厂模式使用的也不多,自行了解即可