Java 面试知识点【背诵版 240题 约7w字】 (35)

优点是内存中只有一个实例,减少了开销,尤其是频繁创建和销毁实例的情况下并且可以避免对资源的多重占用。缺点是没有抽象层,难以扩展,与单一职责原则冲突。

Spring 的 ApplicationContext 创建的 Bean 实例都是单例对象,还有 ServletContext、数据库连接池等也都是单例模式。

Q7:单例模式有哪些实现?

饿汉式:在类加载时就初始化创建单例对象,线程安全,但不管是否使用都创建对象可能会浪费内存。

123456789 public class HungrySingleton {`` ``private HungrySingleton(){} ``private static HungrySingleton instance = ``new HungrySingleton(); ``public static HungrySingleton getInstance() {`` ``return instance;`` ``}``}
   

懒汉式:在外部调用时才会加载,线程不安全,可以加锁保证线程安全但效率低。

123456789101112 public class LazySingleton {`` ``private LazySingleton(){} ``private static LazySingleton instance; ``public static LazySingleton getInstance() {`` ``if``(instance == ``null``) {`` ``instance = ``new LazySingleton();`` ``}`` ``return instance;`` ``}``}
   

双重检查锁:使用 volatile 以及多重检查来减小锁范围,提升效率。

12345678910111213141516 public class DoubleCheckSingleton {`` ``private DoubleCheckSingleton(){} ``private volatile static DoubleCheckSingleton instance; ``public static DoubleCheckSingleton getInstance() {`` ``if``(instance == ``null``) {`` ``synchronized (DoubleCheckSingleton.``class``) {`` ``if (instance == ``null``) {`` ``instance = ``new DoubleCheckSingleton();`` ``}`` ``}`` ``}`` ``return instance;`` ``}``}
   

静态内部类:同时解决饿汉式的内存浪费问题和懒汉式的线程安全问题。

1234567891011 public class StaticSingleton {`` ``private StaticSingleton(){} ``public static StaticSingleton getInstance() {`` ``return StaticClass.instance;`` ``} ``private static class StaticClass {`` ``private static final StaticSingleton instance = ``new StaticSingleton();`` ``}``}
   

枚举:《Effective Java》提倡的方式,不仅能避免线程安全问题,还能防止反序列化重新创建新的对象,绝对防止多次实例化,也能防止反射破解单例的问题。

123 public enum EnumSingleton {`` ``INSTANCE;``}
   
Q8:讲一讲代理模式

代理模式属于结构型模式,为其他对象提供一种代理以控制对这个对象的访问。优点是可以增强目标对象的功能,降低代码耦合度,扩展性好。缺点是在客户端和目标对象之间增加代理对象会导致请求处理速度变慢,增加系统复杂度。

Spring 利用动态代理实现 AOP,如果 Bean 实现了接口就使用 JDK 代理,否则使用 CGLib 代理。

静态代理:代理对象持有被代理对象的引用,调用代理对象方法时也会调用被代理对象的方法,但是会在被代理对象方法的前后增加其他逻辑。需要手动完成,在程序运行前就已经存在代理类的字节码文件,代理类和被代理类的关系在运行前就已经确定了。 缺点是一个代理类只能为一个目标服务,如果要服务多种类型会增加工作量。

动态代理:动态代理在程序运行时通过反射创建具体的代理类,代理类和被代理类的关系在运行前是不确定的。动态代理的适用性更强,主要分为 JDK 动态代理和 CGLib 动态代理。

JDK 动态代理:通过 Proxy 类的 newInstance 方法获取一个动态代理对象,需要传入三个参数,被代理对象的类加载器、被代理对象实现的接口,以及一个 InvocationHandler 调用处理器来指明具体的逻辑,相比静态代理的优势是接口中声明的所有方法都被转移到 InvocationHandler 的 invoke 方法集中处理。

CGLib 动态代理:JDK 动态代理要求实现被代理对象的接口,而 CGLib 要求继承被代理对象,如果一个类是 final 类则不能使用 CGLib 代理。两种代理都在运行期生成字节码,JDK 动态代理直接写字节码,而 CGLib 动态代理使用 ASM 框架写字节码,ASM 的目的是生成、转换和分析以字节数组表示的已编译 Java 类。 JDK 动态代理调用代理方法通过反射机制实现,而 GCLib 动态代理通过 FastClass 机制直接调用方法,它为代理类和被代理类各生成一个类,该类为代理类和被代理类的方法分配一个 int 参数,调用方法时可以直接定位,因此调用效率更高。

Q9:讲一讲装饰器模式

装饰器模式属于结构型模式,在不改变原有对象的基础上将功能附加到对象,相比继承可以更加灵活地扩展原有对象的功能。

装饰器模式适合的场景:在不想增加很多子类的前提下扩展一个类的功能。

java.io 包中,InputStream 字节输入流通过装饰器 BufferedInputStream 增强为缓冲字节输入流。

Q10:装饰器模式和动态代理的区别?

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zwpgxs.html