JAVA设计模式
学习设计模式的好处:学习前辈们的解决办法,有助于代码的编写,让代码设计的更加标准,更加合理。
常见的设计模式
单例模式
JDK中 Runtime类就是一个单例类,运用饿汉式单例。
单例模式有 3 个特点:1. 单例类只有一个实例对象;2. 该单例对象必须由单例类自行创建;3. 单例类对外提供一个访问该单例的全局访问点;
饿汉式单例(急切式单例)
没有任何线程安全问题
//实现: 把构造方私有化,在单例类中,向外界提供一个方法访问到唯一的单例对象.
public class Window {
//饿汉式单例,静态的成员在类加载时就会初始化Window对象,由于是静态的所以只有一次
private static Window window = new Window();
private Window() {
}
public static Window getWindow() {
return window;
}
}
如下图可以看到我们创建10个线程调用getWindow方法,获取的对象都是同一个
懒汉式单例
先看一段代码,
public class Window {
private static Window window;
private Window() {
}
//懒汉式单例,在类加载时不创建,在使用时才创建。
public static Window getWindow() {
if(window==null){
window=new Window();
}
return window;
}
}
这段代码就是懒汉式单例的实现,但是该写法会存在线程安全问题
我们给线程加上休眠,用于测试,结果如下
可以看到在这次我们10个线程所获取的对象有所不同,这就不符合单例的要求,在开发中会出现安全问题,那么我们对以上代码进行改进:
public class Window {
private static Window window;
private Window() {
}
/*
懒汉式单例,在类加载时不创建,在使用时才创建。
懒汉式单例会出现线程安全问题:
在多线程访问时,可能会有多个线程同时通过if判断进入创建对象的步骤,从而创建多个对象
*/
public static synchronized Window getWindow() {
if(window==null){
window=new Window();
}
return window;
}
}
我们可以给getWindow方法加上锁,这样就可以解决线程安全问题,通过测试10个线程获取的对象都是同一个,但是加锁之后 每次只能有一个线程访问该方法 效率会很低,我们再对其进行优化。
public class Window {
private static Window window;
private Window() {
}
/*
懒汉式单例,在类加载时不创建,在使用时才创建。
懒汉式单例会出现线程安全问题:
在多线程访问时,可能会有多个线程同时通过if判断进入创建对象的步骤,从而创建多个对象
1.加锁,但效率低
2.给代码块加锁,双重检索
*/
public static synchronized Window getWindow() {
if(window==null){
synchronized (Window.class){
if (window==null){
window=new Window();
}
}
}
return window;
}
}
我们把锁加到代码块上,然后进行第二次判断就可以解决问题了。假设一次性有四个线程同时通过了第一个if判断,但是只能有一个线程可以获取到锁,并进入第二个if判断,通过判断后便可创建一个window对象,而后面三个线程依次获取到锁之后,由于window对象已经被创建,所以会直接执行 return这样既解决了线程安全问题,还增加了效率(可以多个线程同时访问getWindow方法)
工厂模式
工厂模式看名字就知道是 批量生产一类东西,在JAVA中就是批量生产对象;例如spring框架,把创建对象的权利反转给spring框架, 把创建对象和使用对象分离.有一个工厂,专门负责创建对象,使用对象时,只需要找对应的工厂,根据我们产生对象的场景,需求的不同,又分为 简单工厂模式,工厂方法模式,和抽象工厂模式。
简单工厂模式
工厂方法模式
优点:
抽象工厂模式
如上图,如果需要创建上面四种产品,使用工厂方法模式的话需要创建四个工厂类(奥迪汽车工厂,奥迪手机工厂,宝马汽车工厂,宝马手机工厂),如果使用抽象工厂模式只需要 创建两个工厂类(奥迪工厂和宝马工厂)他们都实现一个抽象工厂接口(该接口里定义两个抽象方法,造手机和造汽车)这样大大减少了工厂类的创建。
原型模式
代理模式
静态代理
优点:可以做到在符合开闭原则的情况下对目标对象进行功能扩展。缺点:一个代理类只能代理一个接口,工作量太大;代理类是运行前编码已经完成的;必须先有接口,再有代理;接口一旦发生变量,代理类也要修改,违反开闭原则