- 设计模式
- 一、设计模式
- 1.单例模式
- 1.1 饿汉式
- 1.2 懒汉式
- 1.3 双重检查
- 1.4 静态内部类
- 1.5 枚举
- 1.6 总结
- 1.7 JDK中单例模式的使用
- 2.工厂模式
- 2.1 简单工厂模式
- 2.2 工厂方法模式
- 2.3 抽象工厂模式
- 2.3.1 关系图
- 2.3.2 代码
- 3.代理模式
- 3.1 静态代理
- 3.1.1 关系图
- 3.1.2 代码
- 3.2 动态代理
- 3.2.1 关系图
- 3.2.1 代码
单例模式中保证了系统内存中该类只有一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以系统系统性能。
1.1 饿汉式饿汉式静态变量实现
【优点】
- 这种写法比较简单,在类装载的时候就完成实例化,避免了线程同步的问题。
【缺点】
- 在类装载的时候就完成了实例化,没有达到Lazy Loading(懒加载)的效果,如果从开始到结束都没有使用过这个类,那么会造成内存浪费。
- 这种方式基于classloader机制避免了线程的同步问题,不过,对象user在类装载的时候就实例化,在单例模式中都是调用getUser方法,但是导致类加载的原因有很多,因此不确定有没有其他的方式导致类加载,这时候初始化就没有达到懒加载的效果。
【结论】
- 这种单例模式中可用,可能会造成内存浪费。
public class singleTest01 {
public static void main(String[] args) {
//测试
Singleton singleton1 = Singleton.getSingleton();
Singleton singleton2 = Singleton.getSingleton();
System.out.println(singleton1==singleton2);//instance
}
}
//饿汉式第一种写法:静态变量直接new对象
class Singleton{
//1.设置私有构造器
private Singleton(){
}
//2.本类中创建对象实例
private final static Singleton singleton=new Singleton();
//3.创建一个返回实例对象的方法
public static Singleton getSingleton(){
return singleton;
}
}
饿汉式静态代码块实现
这种方式跟上面的静态变量的优缺点是一样的。
public class singleTest01 {
public static void main(String[] args) {
//测试
Singleton singleton1 = Singleton.getSingleton();
Singleton singleton2 = Singleton.getSingleton();
System.out.println(singleton1==singleton2);//instance
}
}
//饿汉式第二种写法:静态代码块中new对象
class Singleton{
//1.设置私有构造器
private Singleton(){}
//2.本类中创建对象引用
private static Singleton singleton;
//3.使用静态代码块new对象实例
static {
Singleton=new Singleton();
}
//4.创建一个返回实例对象的方法
public static Singleton getSingleton(){
return singleton;
}
}
1.2 懒汉式
懒汉式第一种写法,线程不安全
【优点】
- 起到了懒加载的效果,但只能在单线程下使用
【缺点】
- 如果在多线程下,一个线程进入了if(user==null),还没来得及往下进行,另外一个线程也进入了这个判断语句,那么会产生多个实例,所以在多线程环境下不可使用这种方式。
【结论】
- 在实际开发中,不要使用这种方式。
public class singleTest01 {
public static void main(String[] args) {
//1.通过User中的静态方法获取User对象
//当第一次调用gerUser()方法时,user没有被创建那么先会被创建
Singleton singleton1 = Singleton.getSingleton();
//当第二次调用getUser()方法时,user已经被创建,不为null,会将原先创建好的user返回
Singleton singleton2 = Singleton.getSingleton();
//两次的user1和user2所指向的实例对象是同一个
System.out.println(singleton1 == singleton2);//true
}
}
//懒汉式第一种写法,即当需要的时候才会创建实例对象
//会出现线程不安全问题
class Singleton{
//1.创建一个私有的构造方法
private Singleton(){}
//2.定义一个静态User引用
private static Singleton singleton;
//3.写一个公共静态方法,当需要时再创建
public static Singleton getSingleton(){
if (singleton==null){
singleton=new Singleton();
}
return singleton;
}
}
【证明懒汉式第一种写法是线程不安全的】
测试
public class singleTest01 {
public static void main(String[] args) {
//创建线程t1和t2
Thread t1=new Thread(new myThread01());
Thread t2=new Thread(new myThread02());
//改名
t1.setName("t1");
t2.setName("t2");
//启动线程
t1.start();
t2.start();
}
}
class Singleton{
private static Singleton singleton;
private Singleton(){}
public static Singleton getSingleton(){
if (singleton==null){
//为了使另外一个线程成功进入if语句中,在这里设置睡眠
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
singleton=new Singleton();
}
return singleton;
}
}
class myThread01 implements Runnable{
@Override
public void run() {
Singleton singleton = Singleton.getSingleton();
System.out.println(Thread.currentThread().getName()+"==>"+singleton.hashCode());
}
}
class myThread02 implements Runnable{
@Override
public void run() {
Singleton singleton = Singleton.getSingleton();
System.out.println(Thread.currentThread().getName()+"==>"+singleton.hashCode());
}
}
结果
懒汉式第二种写法,线程安全,同步方法
给getUser()方法加上"synchronized",使得这个方法线程同步,当有一个线程计入方法时,其他线程就无法进入这个方法。
【优点】
- 解决了第一种写法中线程不安全的问题
【缺点】
- 加入"synchronized"之后,效率会太低,每个线程在获得类的实例时候,执行getUser()方法都要进行同步,而其实这个方法只执行一次实例化代码就够了,后面的想获得该类实例,直接return就行
public class singleTest02 {
public static void main(String[] args) {
Singleton s1=new Singleton();
Singleton s2=new Singleton();
System.out.println(s1==s2);
}
}
//懒汉式的第二种写法,线程同步,没有线程安全问题
class Singleton{
//创建一个静态引用
private static Singleton singleton;
//创建一个私有的构造方法
private Singleton(){}
//get方法
public synchronized static Singleton getSingleton(){
if (singleton==null){
singleton=new Singleton();
}
return singleton;
}
}
【证明懒汉式第二种写法是线程安全的】
测试
public class singleTest02 {
public static void main(String[] args) {
//创建线程t1和t2
Thread t1=new Thread(new myThread03());
Thread t2=new Thread(new myThread04());
//改名
t1.setName("t1");
t2.setName("t2");
//开启线程
t1.start();
t2.start();
}
}
//懒汉式的第二种写法,线程同步,没有线程安全问题
class Singleton{
//创建一个静态引用
private static Singleton singleton;
//创建一个私有的构造方法
private Singleton(){}
//get方法
public synchronized static Singleton getSingleton(){
if (singleton==null){
singleton=new Singleton();
}
return singleton;
}
}
class myThread03 implements Runnable{
@Override
public void run() {
//调用Singleton的getSingleton方法
Singleton singleton = User1.getSingleton();
System.out.println(Thread.currentThread().getName()+"==>"+singleton.hashCode());
}
}
class myThread04 implements Runnable{
@Override
public void run() {
//调用Singleton的getSingleton方法
Singleton singleton = User1.getSingleton();
System.out.println(Thread.currentThread().getName()+"==>"+singleton.hashCode());
}
}
结果
【优点】
- 不会造成线程安全问题
- 解决懒加载问题
- 效率不会受影响
【结论】
- 在实际开发中,推荐双重加载实现单例模式
public class singleTest03 {
public static void main(String[] args) {
Singleton singleton1=Singleton.getSingleton();
Singleton singleton2=Singleton.getSingleton();
System.out.println(singleton1==singleton2);//true
}
}
//使用双重检查机制实现单例模式
class Singleton{
//1.创建一个私有静态Singleton引用
private volatile static Singleton singleton;
//2.创建一个私有构造方法
private Singleton(){}
//3.使用双重检查机制来实现单例模式
public static Singleton getSingleton(){
if (singleton==null){
synchronized(Singleton.class){
if (singleton==null){
singleton=new Singleton();
}
}
}
return singleton;
}
}
【疑问】:为什么要使用双判断呢?一次不可以吗?
答:在java中线程安全同步有两个办法,第一个就是使用同步代码块(synchronized)。第二种方式就是volatile,这种方式是轻量级的,不会引起线程上下文的切换和调度,但容易出错。经过实验,只进行一次判断会出现错误,而在里面进行两次就不会出现错误。
1.4 静态内部类【优点】
- 这种方式采用类加载的机制来保证初始化实例时只有一个线程
- 静态内部类在Singleton被装载的时候并不会立即实例化,而是在需要实例化,调用getSingleton()方法时,才会装载内部类
- 类的静态属性只会在第一次加载类的时候初始化,在类的初始化时,别的线程是无法进入的
【结论】
- 避免了线程不安全的问题,利用静态内部类特点实现延迟加载,效率高,推荐使用
public class singleTest04 {
public static void main(String[] args) {
Singleton singleton1=Singleton.getSingleton();
Singleton singleton2=Singleton.getSingleton();
System.out.println(singleton1==singleton2);
}
}
//通过静态内部类实现单例模式
class Singleton{
//1.创建一个私有静态引用,也可也不创建,对程序没有影响
private static Singleton singleton;
//2.创建一个私有构造方法
private Singleton(){}
//3.创建一个内部类
private static class SingletonInstance{
private static final Singleton INSTANCE=new Singleton();
}
//4.创建获取singleton的方法,直接返回SingletonInstance.INSTANCE
public static Singleton getSingleton(){
return SingletonInstance.INSTANCE;
}
}
1.5 枚举
【优点】
- 不仅能避免多线程同步问题,还能防止反序列化重新创建新的对象
public class singleTest05 {
public static void main(String[] args) {
Singleton singleton=Singleton.INSTANCE;
Singleton singleton1=Singleton.INSTANCE;
System.out.println(singleton==singleton1);
}
}
//使用枚举实现单例模式
enum Singleton{
INSTANCE;
public void method(){
System.out.println("枚举!");
}
}
1.6 总结
package com.drl.design.single.singleTest05;
public class singleTest05 {
public static void main(String[] args) {
Singleton singleton=Singleton.INSTANCE;
Singleton singleton1=Singleton.INSTANCE;
System.out.println(singleton==singleton1);
}
}
//饿汉式
//class Singleton{
// //1.直接在静态变量中new
// private static Singleton singleton=new Singleton();
// private Singleton(){}
// public static Singleton getSingleton(){
// return singleton;
// }
// //2.在静态代码块中
// private static Singleton singleton;
// private Singleton(){}
// static {
// singleton=new Singleton();
// }
// public static Singleton getSingleton(){
// return singleton;
// }
//}
//懒汉式
//class Singleton{
// //1.线程不安全,但是效率高
private static Singleton singleton;
private Singleton(){}
public static Singleton getSingleton(){
if (singleton==null){
singleton=new Singleton();
}
return singleton;
}
//
// //2.线程安全,但是效率不高
// private static Singleton singleton;
// private Singleton(){}
// public synchronized static Singleton getSingleton(){
// if (singleton==null){
// singleton=new Singleton();
// }
// return singleton;
// }
//}
//双重检查
//class Singleton{
// private static volatile Singleton singleton;
// private Singleton(){}
// public static Singleton getSingleton(){
// if (singleton==null){
// synchronized (Singleton.class){
// if (singleton==null){
// singleton=new Singleton();
// }
// }
// }
// return singleton;
// }
//}
//静态内部类
//class Singleton{
// private Singleton(){}
//
// private static class SingletonInstance{
// private static final Singleton INSTANCE=new Singleton();
// }
// public static Singleton getInstance(){
// return SingletonInstance.INSTANCE;
// }
//}
//枚举
enum Singleton{
INSTANCE;
}
1.7 JDK中单例模式的使用
在JDK中java.lang.Runtime中Runtime就使用了单例模式,并且是饿汉式实现的单例模式,如下图:
以顾客买车、品牌方造车为案例:
平常情况,顾客直接和品牌方打交道
//造汽车接口
interface Car{
public void makeCar();
}
//五菱宏光品牌方造车
class WuLing implements Car{
@Override
public void makeCar() {
System.out.println("五菱宏光造车!");
}
}
//吉利品牌方造车
class JiLi implements Car{
@Override
public void makeCar() {
System.out.println("吉利品牌方造车!");
}
}
//顾客
class Consumer{
public static void main(String[] args) {
//要买五菱宏光
Car car1=new WuLing();
car1.makeCar();
//要买吉利
Car car2=new JiLi();
car2.makeCar();
}
}
使用工厂模式,顾客和中介打交道
//造汽车接口
interface Car{
public void makeCar();
}
//五菱宏光品牌方造车
class WuLing implements Car{
@Override
public void makeCar() {
System.out.println("五菱宏光造车!");
}
}
//吉利品牌方造车
class JiLi implements Car{
@Override
public void makeCar() {
System.out.println("吉利品牌方造车!");
}
}
//工厂
class CarFactory {
//返回汽车
public static Car getCar(String car){
if (car.equals("五菱宏光")){
return new WuLing();
}else {
return new JiLi();
}
}
}
//顾客
class Consumer{
public static void main(String[] args) {
//使用工厂模式之后,顾客直接说想买什么车就可以
Car car = CarFactory.getCar("五菱宏光");
car.makeCar();
}
}
2.2 工厂方法模式
使用工厂方法模式会解耦合,当添加另外一个车品牌方时,不用修改代码,只需要添加即可,满足开闭原则。
//造汽车接口
interface Car{
public void makeCar();
}
//车工厂接口
interface CarFactory{
public Car getCar();
}
//五菱宏光品牌方造车
class WuLing implements Car{
@Override
public void makeCar() {
System.out.println("五菱宏光造车!");
}
}
//吉利品牌方造车
class JiLi implements Car{
@Override
public void makeCar() {
System.out.println("吉利品牌方造车!");
}
}
//五菱宏光工厂
class WuLingCarFactory implements CarFactory {
@Override
public Car getCar() {
return new WuLing();
}
}
//吉利工厂
class JiLiCarFactory implements CarFactory{
@Override
public Car getCar() {
return new JiLi();
}
}
//顾客
class Consumer{
public static void main(String[] args) {
//工厂方法模式
Car car1=new WuLingCarFactory().getCar();
car1.makeCar();
Car car2=new JiLiCarFactory().getCar();
car2.makeCar();
}
}
2.3 抽象工厂模式
围绕一个超级工厂创建其他工厂,该超级工厂又称为其他工厂的工厂。以下使用小米和华为品牌方的手机、路由器工厂为例子
2.3.1 关系图 2.3.2 代码超级抽象接口工厂
//超级抽象工厂,制造工厂的工厂
public interface IProductFactory {
//制造手机的工厂
public IphoneProduct iphoneProduct();
//制造路由器的工厂
public IrouterProduct irouterProduct();
}
小米厂商工厂,实现超级抽象接口工厂
//小米工厂,可以制造手机,也可以制造路由器
public class XiaomiFactory implements IProductFactory {
@Override
public IphoneProduct iphoneProduct() {
return new XiaomiPhone();
}
@Override
public IrouterProduct irouterProduct() {
return new XiaomiRouter();
}
}
华为厂商工厂,实现超级抽象接口工厂
//华为厂商工厂,实现超级抽象接口工厂,分为创建出华为手机工厂和华为路由器工厂
public class HuaweiFactory implements IProductFactory {
@Override
public IphoneProduct iphoneProduct() {
return new HuaweiPhone();
}
@Override
public IrouterProduct irouterProduct() {
return new HuaweiRouter();
}
}
小米手机工厂和小米路由器工厂
//小米手机工厂,实现手机规范接口
public class XiaomiPhone implements IphoneProduct {
@Override
public void start() {
System.out.println("小米手机开启!");
}
@Override
public void shutUp() {
System.out.println("小米手机关机!");
}
@Override
public void call() {
System.out.println("小米手机打电话!");
}
}
//小米路由器工厂,实现路由器规范接口
public class XiaomiRouter implements IrouterProduct {
@Override
public void startWifi() {
System.out.println("小米路由器开启wifi");
}
@Override
public void setting() {
System.out.println("小米路由器设置参数");
}
}
华为手机工厂和华为路由器工厂
//华为手机工厂,实现手机规范接口
public class HuaweiPhone implements IphoneProduct {
@Override
public void start() {
System.out.println("华为手机开启!");
}
@Override
public void shutUp() {
System.out.println("华为手机关机!");
}
@Override
public void call() {
System.out.println("华为手机打电话!");
}
}
//华为路由器工厂,实现路由器接口
public class HuaweiRouter implements IrouterProduct {
@Override
public void startWifi() {
System.out.println("华为路由器开启wifi");
}
@Override
public void setting() {
System.out.println("华为路由器设置参数");
}
}
手机规范接口和路由器规范接口
//手机规范接口
public interface IphoneProduct {
void start();
void shutUp();
void call();
}
//路由器规范接口
public interface IrouterProduct {
void startWifi();
void setting();
}
3.代理模式
为对象提供一个替身,以控制对这个对象的访问,即通过代理对象访问目标对象。这样做的好处是可以在目标对象实现的基础上增加额外的功能操作,即扩展目标对象的功能。
3.1 静态代理 静态代理在使用时,需要定义接口或父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或是继承相同父类。
【优点】
- 在不修改目标对象的功能前提下,能通过代理对象对目标功能扩展
【缺点】
- 因为目标对象和代理对象都要实现一样的接口,所以会有很多代理类
- 一旦增加方法,目标对象与代理对象都要维护
ITeacher接口
//接口
public interface ITeacherDao {
//教授方法
public void teach();
}
TeacherDao类,即目标对象,被代理对象
//目标对象(即被代理的对象)
public class TeacherDao implements ITeacherDao {
public TeacherDao(){}
@Override
public void teach() {
System.out.println("开始授课!");
}
}
TeacherDaoProxy类,即代理对象
//代理对象
public class TeacherDaoProxy implements ITeacherDao {
private ITeacherDao iTeacherDao;
public TeacherDaoProxy(ITeacherDao iTeacherDao) {
this.iTeacherDao = iTeacherDao;
}
@Override
public void teach() {
System.out.println("开始代理....");
iTeacherDao.teach();
System.out.println("代理提交....");
}
}
客户端
public class client {
public static void main(String[] args) {
//目标对象(被代理对象)
TeacherDao teacherDao=new TeacherDao();
//代理对象
TeacherDaoProxy teacherDaoProxy=new TeacherDaoProxy(teacherDao);
//通过代理对象调用方法
teacherDaoProxy.teach();
}
}
3.2 动态代理
代理对象不用实现接口,但是目标对象需要实现接口,否则不能用动态代理。代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象。
3.2.1 关系图 3.2.1 代码ITeacher接口
//接口
public interface ITeacher {
public void teach();
}
TeacherDao目标对象,被代理的对象
//目标对象,被代理的对象
public class TeacherDao implements ITeacher {
@Override
public void teach() {
System.out.println("开始授课!");
}
}
代理对象工厂
//获得代理对象工厂
public class TeacherDaoProxyFactory {
private ITeacher target;
public TeacherDaoProxyFactory(ITeacher iTeacher){
this.target=iTeacher;
}
public Object getProxy(){
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
return method.invoke(target,objects);
}
});
}
}
客户端
public static void main(String[] args) {
//创建一个目标对象
ITeacher teacherDao=new TeacherDao();
//通过代理工厂获取代理类
TeacherDaoProxyFactory proxyFactory=new TeacherDaoProxyFactory(teacherDao);
ITeacher proxy = (ITeacher)proxyFactory.getProxy();
//通过代理对象调用方法
proxy.teach();
}



