面试中会被问到了解过什么设计模式吗,其实设计模式23种那么多,都了解一般水平的小伙伴做不到,想详细学习的可以看专门介绍设计模式的书籍,此篇文章主要介绍几种简单常用的,(单例、简单工厂、工厂)下篇将介绍(装饰、策略、观察者)[https://blog.csdn.net/liuguangxu1988/article/details/82055853]熟练描述,外加手写个小例子,代码实现,会加分不少。下面结合了网上的一些资料,部分仿照了刘望舒大神分享的例子。吃水不忘挖井人,大神的《Android进阶之光》推荐大家阅读购买。
主要用于开发环境中的工具类(ToastUtil,SharepreferenceUtil等)简单的懒汉饿汉不写了,主流的两种写法及分析如下
第一种 双重检查写法
public class Singleton {
private static volatile Singleton sInstance;
private Singleton() {}
public static Singleton getInstance() {
if (sInstance == null) {//第一次判空减少不必要的同步
synchronized (Singleton.class) {
if (sInstance == null) {//第二次判空,实例为空再创建对象
sInstance = new Singleton();
}
}
}
return sInstance;
}
}
这时面试官基本会问volatile 这个关键字有什么作用
1保证线程修改的可见性
Java语言编写的程序,有时为了提高运行效率,编译器会自动对其优化,把经常访问的变量缓存起来,程序在读取这个变量时有可能直接从缓存(例如寄存器)中读取,而不会去内存中读取。当多线程编程时,变量的值可能因为别的线程改变了,而该缓存的值不会相应的改变,从而造成读取的值与实变量的值不一致。
volatile 被设计用来修饰不同线程访问和修改的变量。被volatile 类型定义的变量,系统每次用到他时都直接从对应的内存中提取,而不会利用缓存。这样所有线程在任何时候拿到的变量的值都是相同的。
2禁止指令重排序
在Java内存模型(JMM)中,并不限制处理器的指令顺序,说白了就是在不影响结果的情况下,顺序可能会被打乱。
在执行sInstance = new Singleton();这条命令语句时,JMM并不是一下就执行完毕的,即不是原子性,实质上这句命令分为三大部分:
1. 为对象分配内存
2. 执行构造方法语句,初始化实例对象
3. 把sInstance的引用指向分配的内存空间
在JMM中这三个步骤中的2和3不一定是顺序执行的,如果线程A执行的顺序为1、3、2,在第2步执行完毕的时候,恰好线程B执行第一次判空语句,则会直接返回sInstance,那么此时获取到的sInstance仅仅只是不为null,实质上没有初始化,这样的对象肯定是有问题的!
而volatile关键字的存在意义就是保证了执行命令不会被重排序,也就避免了这种异常情况的发生,所以这种获取单例的方法才是真正的安全可靠!
第二种 静态内部类
public class Singleton {
private Singleton() {}
private static class SingletonHolder {
private static Singleton sInstance = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.sInstance;
}
}
当外部类Singleton被加载时,其静态内部类SingeletonHolder不会被加载,所以它的成员变量sInstance是不会被初始化的,只有当调用Singleton.getInstance()方法时,才会加载SingeletonHolder并且初始化其成员变量,而类加载时是线程安全的,这样既保证了延迟加载,也保证了线程安全,同时也简化了代码量,一举三得!
简单(静态)工厂模式 和 工厂模式(基本会问下这两个的区别)
1简单工厂模式,又叫静态工厂模式,其实并不属于23中GOF之一,为了方便后文的工厂模式。
涉及到
Factory 工厂类 IProduct 抽象产品类 Product 具体产品类 举例如下
一个手机代工厂,可以生产华为手机,业务发展,还要生产小米、OPPO手机,这样一个单独的类来生产手机,用到简单工厂模式。
1抽象产品类 抽象手机,抽象开机方法
public abstract class Cellphone{
public abstract void start();
}
2具体产品类 继承父类Cellphone,实现start方法
public class HuaweiPhone extends Cellphone{
@Override
public void start(){
System.out.println("华为手机开机");
}
}
public class MiPhone extends Cellphone{
@Override
public void start(){
System.out.println("小米手机开机");
}
}
public class OPPOPhone extends Cellphone{
@Override
public void start(){
System.out.println("OPPO手机开机");
}
}
3工厂类 提供静态方法createCellphone来生产手机
public class CellphoneFactory{
public static Cellphone createCellphone(String type){
Cellphone mCellphone = null;
switch(type){
case "Huawei":
mCellphone = new HuaweiPhone();
break;
case "Mi":
mCellphone = new MiPhone();
break;
case "OPPO":
mCellphone = new OPPOPhone();
break;
}
return mCellphone;
}
}
4客户端调用
public class createCellphone{
public static void main(String[] args){
CellphoneFactory.createCellphone("Mi").start();
}
}
使用场景及优缺点
场景
*工厂类负责创建的对象比较少
*客户只需要知道传入工厂的参数,而无需关心创建对象的逻辑
优点:根据参数获得对应的实例,避免了直接实例化类,降低了耦合性。
缺点:可实例化的类型在编译期已被确定,如果增加新类型(生产VIVO手机),则要修改工厂,违反了开放封闭原则。当子类过多或子类层次过多时不适合使用。
涉及到
Factory 抽象工厂类 ConcreteFactory 具体工厂类
Product 抽象产品类 ConcreteProduct 具体产品类
简单实现
1创建抽象工厂
public abstract class CellphoneFactory{
public abstract T createCellphone(Class clz)
}
2具体工厂 继承抽象工厂,通过反射来创建手机
public class MyCellphoneFactory extends CellphoneFactory{
@Override
public T createCellphone(Class clz){
Cellphone mCellphone = null;
String classname = clz.getName();
try{
mCellphone = (Cellphone)Class.forName(classname).newInstance();
}catch(Excepion e){
e.printStackTrace();
}
return (T) Cellphone;
}
}
3客户端调用
public class Client{
public static void main(String[] args){
CellphoneFactory cellphoneFactory = new MyCellphoneFactory();
HuaweiPhone huaweiPhone = cellphoneFactory.createCellphone(HuaweiPhone.class);
huaweiPhone.sart();
MiPhone miPhone = cellphoneFactory.createCellphone(MiPhone.class);
miPhone.sart();
OPPOPhone oppoPhone = cellphoneFactory.createCellphone(OPPOPhone.class);
oppoPhone.start();
}
}
简单工厂模式与工厂模式的对比
简单工厂模式:如果要增加产品,就需要在工厂类添加一个Case分支,违反了开放封闭原则,对修改也开放了。
工厂模式没有违反这个原则,如果要生产VIVO手机,无需修改工厂类,直接创建产品即可。
单例 简单(静态)工厂 工厂模式属于创建型设计模式,还有(建造者,原型模式),这篇博客写的比较简单,在于给读者一个简单的思路,在回答问题上可以清晰,有条理,同时代码示例也非常简洁,在面试官让手写时也可写一下,码字不易点个赞吧,谢谢大家!