java高频面试题-设计模式

1 你所知道的设计模式有哪些?

Java 中一般认为有 23 种设计模式,我们不需要所有的都会,但是其中常用的几种设计模式应该去掌握。下面列出了所有的设计模式。需要掌握的设计模式我单独列出来了,当然能掌握的越多越好。

总体来说设计模式分为三大类:

创建型模式,共5种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。

结构型模式,共7种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

行为型模式,共11种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

2 单例设计模式?

单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。这些应用都或多或少具有资源管理器的功能。每台计算机可以有若干个打印机,但只能有一个Printer Spooler,以避免两个打印作业同时输出到打印机中。每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。总之,选择单例模式就是为了避免不一致状态。

● 单例类只能有一个实例。

● 单例类必须自己创建自己的唯一实例。

● 单例类必须给所有其他对象提供这一实例。

单例模式保证了全局对象的唯一性,比如系统启动读取配置文件就需要单例保证配置的一致性。

● 构造器私有化

● 以静态方法或者枚举返回实例

● 确保实例只有一个,尤其是多线程环境

● 确保反序列化时不会重新构建对象

(1)饿汉式(立即加载):

饿汉式单例在类加载初始化时就创建好一个静态的对象供外部使用,除非系统重启,这个对象不会改变,所以本身就是线程安全的

Singleton通过将构造方法限定为private避免了类在外部被实例化,在同一个虚拟机范围内,Singleton的唯一实例只能通过getInstance()方法访问。(事实上,通过Java反射机制是能够实例化构造方法为private的类的,会使Java单例实现失效)

(2)懒汉式(延迟加载):

该示例虽然用延迟加载方式实现了懒汉式单例,但在多线程环境下会产生多个Singleton对象

(3)同步锁(解决线程安全问题):

在方法上加synchronized同步锁或是用同步代码块对类加同步锁,此种方式虽然解决了多个实例对象问题,但是该方式运行效率却很低下,下一个线程想要获取对象,就必须等待上一个线程释放锁之后,才可以继续运行。

(4)双重检查锁(提高同步锁的效率):

使用双重检查锁进一步做了优化,可以避免整个方法被锁,只对需要锁的代码部分加锁,可以提高执行效率。

(5) 静态内部类:

这种方式引入了一个内部静态类(static class),静态内部类只有在调用时才会加载,它保证了Singleton 实例的延迟初始化,又保证了实例的唯一性。它把singleton 的实例化操作放到一个静态内部类中,在第一次调用getInstance() 方法时,JVM才会去加载InnerObject类,同时初始化singleton 实例,所以能让getInstance() 方法线程安全。

特点是:即能延迟加载,也能保证线程安全。

静态内部类虽然保证了单例在多线程并发下的线程安全性,但是在遇到序列化对象时,默认的方式运行得到的结果就是多例的。

(6)内部枚举类实现(防止反射攻击)

事实上,通过Java反射机制是能够实例化构造方法为private的类的。这也就是我们现在需要引入的枚举单例模式。

3 工厂设计模式(Factory)

工厂设计模式,顾名思义,就是用来生产对象的,在java中,万物皆对象,这些对象都需要创建,如果创建的时候直接new该对象,就会对该对象耦合严重,假如我们要更换对象,所有new对象的地方都需要修改一遍,这显然违背了软件设计的开闭原则,如果我们使用工厂来生产对象,我们就只和工厂打交道就可以了,彻底和对象解耦,如果要更换对象,直接在工厂里更换该对象即可,达到了与对象解耦的目的;所以说,工厂模式最大的优点就是:解耦

定义:

一个工厂方法,依据传入的参数,生成对应的产品对象;角色:1、抽象产品2、具体产品3、具体工厂4、产品使用者使用说明:

先将产品类抽象出来,比如,苹果和梨都属于水果,抽象出来一个水果类Fruit,苹果和梨就是具体的产品类,然后创建一个水果工厂,分别用来创建苹果和梨。代码如下:

水果接口:

苹果类:

梨类:

水果工厂:

使用工厂生产产品:

以上的这种方式,每当添加一种水果,就必然要修改工厂类,违反了开闭原则;

所以简单工厂只适合于产品对象较少,且产品固定的需求,对于产品变化无常的需求来说显然不合适。

定义:

将工厂提取成一个接口或抽象类,具体生产什么产品由子类决定;角色:1、抽象产品2、具体产品3、抽象工厂4、具体工厂使用说明:

和上例中一样,产品类抽象出来,这次我们把工厂类也抽象出来,生产什么样的产品由子类来决定。代码如下:水果接口、苹果类和梨类:

代码和上例一样

抽象工厂接口:

苹果工厂:

梨工厂:

使用工厂生产产品:

以上这种方式,虽然解耦了,也遵循了开闭原则,但是如果我需要的产品很多的话,需要创建非常多的工厂,所以这种方式的缺点也很明显。

定义:

为创建一组相关或者是相互依赖的对象提供的一个接口,而不需要指定它们的具体类。角色:

  1. 1、抽象产品2、具体产品3、抽象工厂4、具体工厂

使用说明:

抽象工厂和工厂方法的模式基本一样,区别在于,工厂方法是生产一个具体的产品,而抽象工厂可以用来生产一组相同,有相对关系的产品;重点在于一组,一批,一系列;举个例子,假如生产小米手机,小米手机有很多系列,小米note、红米note等;假如小米note生产需要的配件有825的处理器,6英寸屏幕,而红米只需要650的处理器和5寸的屏幕就可以了。用抽象工厂来实现:

cpu接口和实现类:

屏幕接口和实现类:

抽象工厂接口:

小米手机工厂:

红米手机工厂:

使用工厂生产产品:

以上例子可以看出,抽象工厂可以解决一系列的产品生产的需求,对于大批量,多系列的产品,用抽象工厂可以更好地管理和扩展。

1、对于简单工厂和工厂方法来说,两者的使用方式实际上是一样的,如果对于产品的分类和名称是确定的,数量是相对固定的,推荐使用简单工厂模式;

2、抽象工厂用来解决相对复杂的问题,适用于一系列、大批量的对象生产。

4 代理模式(Proxy)

代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗地来讲代理模式就是我们生活中常见的中介。

举个例子来说明:假如说我现在想买一辆二手车,虽然我可以自己去找车源,做质量检测等一系列的车辆过户流程,但是这确实太浪费我得时间和精力了。我只是想买一辆车而已为什么我还要额外做这么多事呢?于是我就通过中介公司来买车,他们来给我找车源,帮我办理车辆过户流程,我只是负责选择自己喜欢的车,然后付钱就可以了。用图表示如下:

中介隔离作用:

在某些情况下,一个客户类不想或者不能直接引用一个委托对象,而代理类对象可以在客户类和委托对象之间起到中介的作用,其特征是代理类和委托类实现相同的接口

开闭原则,增加功能:

代理类除了是客户类和委托类的中介之外,我们还可以通过给代理类增加额外的功能来扩展委托类的功能,这样做我们只需要修改代理类而不需要再修改委托类,符合代码设计的开闭原则。代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后对返回结果的处理等。代理类本身并不真正实现服务,而是通过调用委托类的相关方法,来提供特定的服务。真正的业务功能还是由委托类来实现,但是可以在业务功能执行的前后加入一些公共的服务。例如我们想给项目加入缓存、日志这些功能,我们就可以使用代理类来完成,而没必要修改已经封装好的委托类。

我们有多种不同的方式来实现代理。

如果按照代理创建的时期来进行分类的话,可以分为两种:静态代理、动态代理。

  • ● 静态代理是由程序员创建或特定工具自动生成源代码,再对其编译。在程序员运行之前,代理类.class文件就已经被创建了。
  • ● 动态代理是在程序运行时通过反射机制动态创建的。

第一步:创建服务类接口

第二步:实现服务接口

第三步:创建代理类

第四步:编写测试类

静态代理总结:

优点:可以做到在符合开闭原则的情况下对目标对象进行功能扩展。

缺点:我们得为每一个服务创建代理类,工作量太大,不易管理。同时接口一旦发生改变,代理类也得相应修改。

在动态代理中我们不再需要再手动的创建代理类,我们只需要编写一个动态处理器就可以了。真正的代理对象由JDK在运行时为我们动态地来创建。

第一步:创建服务类接口

代码和上例一样

第二步:实现服务接口

代码和上例一样

第三步:编写动态处理器

第四步:编写测试类

Proxy是所有动态生成的代理的共同的父类,这个类有一个静态方法Proxy.newProxyInstance(),接收三个参数:

● ClassLoader loader:指定当前目标对象使用的类加载器,获取加载器的方法是固定的

● Class<?>[] interfaces:指定目标对象实现的接口的类型,使用泛型方式确认类型

● InvocationHandler:指定动态处理器,执行目标对象的方法时,会触发事件处理器的方法

JDK动态代理总结:

优点:相对于静态代理,动态代理大大减少了开发任务,同时减少了对业务接口的依赖,降低了耦合度。

缺点:Proxy是所有动态生成的代理的共同的父类,因此服务类必须是接口的形式,不能是普通类的形式,因为Java无法实现多继承。

JDK实现动态代理需要实现类通过接口定义业务方法,对于没有接口的类,如何实现动态代理呢,这就需要CGLib了。CGLib采用了底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。但因为采用的是继承,所以不能对final修饰的类进行代理。JDK动态代理与CGLib动态代理均是实现Spring AOP的基础。

Cglib子类代理实现方法:

(1)引入cglib的jar文件,asm的jar文件

(2)代理的类不能为final

(3)目标业务对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法

第一步:创建服务类

第二步:创建CGLIB代理类

第三步:创建测试类

CGLib代理总结:

CGLib创建的动态代理对象比JDK创建的动态代理对象的性能更高,但是CGLIB创建代理对象时所花费的时间却比JDK多得多。所以对于单例的对象,因为无需频繁创建对象,用CGLIB合适,反之使用JDK方式要更为合适一些。同时由于CGLib由于是采用动态创建子类的方法,对于final修饰的方法无法进行代理。

动态代理的原理: 使用一个代理将对象包装起来,然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。

代理对象决定是否以及何时将方法调 用转到原始对象上

动态代理的方式

基于接口实现动态代理: JDK动态代理

基于继承实现动态代理: Cglib、Javassist动态代理

2019最全的Java架构师面试120题解析(MySQL/Redis/架构/高并发等)

1.Java基础和高级:

  • 集合框架: List:ArrayList、LinkedList;Set:HashSet、TreeSet
  • Map:TreeMap/ConcurrentHashMap;Queue:ConcurrentLinkedQueue等
  • 泛型、反射、并发编程、JVM、AIO/BIO/NIO等领域。

2.Spring框架:注入方式、IOC/AOP原理、Spring事务实现、隔离、Spring boot等。

3.网络+Linux:操作系统原理、网络协议、Linux的常见排查故障方法等。

4.数据库Mysql:索引、查询优化、表优化,以及与NoSQL的区别和结合。

5.高并发:并发编程领域,以及对应的设计方案等

6.分布式系列:分布式缓存Redis、分布式架构设计系列等领域,更多如下:

一、Java基础和高级

  1. String类为什么是final的。
  2. HashMap的源码,实现原理,底层结构。
  3. 反射中,Class.forName和classloader的区别
  4. session和cookie的区别和联系,session的生命周期,多个服务部署时session管理。
  5. Java中的队列都有哪些,有什么区别。
  6. Java的内存模型以及GC算法
  7. Java7、Java8的新特性
  8. Java数组和链表两种结构的操作效率,在哪些情况下(从开头开始,从结尾开始,从中间开始),哪些操作(插入,查找,删除)的效率高
  9. Java内存泄露的问题调查定位: jmap, jstack的使用等等

二、spring框架

  1. spring框架中需要引用哪些jar包,以及这些jar包的用途
  2. springMVC的原理
  3. springMVC注解的意思
  4. spring中beanFactory和ApplicationContext的联系和区别
  5. spring注入的几种方式
  6. spring如何实现事物管理的
  7. springlOC和AOP的原理
  8. spring中循环注入的方式
  9. Spring AOP与IOC的实现原理
  10. Spring的beanFactory和factoryBean的区别
  11. Spring的事务隔离级别,实现原理
  12. 对Spring的理解,非单例注入的原理?它的生命周期?循环注入的原理,aop的实现原理,说说aop中的几
  13. 个术语,它们是怎么相互工作的?
  14. spring boot特性,优势,适用场景等

三、java多线程常见问题

  1. Java创建线程之后,直接调用start(方法和run()的区别
  2. 常用的线程池模式以及不同线程池的使用场景
  3. newFixedThreadPool此种线程池如果线程数达到最大值后会怎么办,底层原理。
  4. 多线程之间通信的同步问题,synchronized锁的是对象, 衍伸出和synchronized相关很多的具体问题,例如同一个类不同方法都有synchronized锁,一个对象是否可以同时访问。或者- -个类的static构造方法加上synchronized之后的锁的影响。
  5. 了解可重入锁的含义,以及ReentrantLock和synchronized的区别
  6. 同步的数据结构,例如concurrentHashMap的源码理解以及内部实现原理,为什么他是同步的且效率高
  7. atomicinteger和volatile等线程安全操作的关键字的理解和使用
  8. 线程间通信,wait和notify

四、网络通信

  1. http是无状态通信,http的请求方式有哪些,可以自己定义新的请求方式么。
  2. socket通信,以及长连接,分包,连接异常断开的处理。
  3. socket通信模型的使用,AIO和NIO。
  4. socket框架netty的使用,以及NIO的实现原理,为什么是异步非阻塞。
  5. 同步和异步,阻塞和非阻塞。

五、常用Linux命令

  1. 常用的linux下的命令
  2. 大的log文件中,统计异常出现的次数、排序,或者指定输出多少行多少列的内容。
  3. linux下的调查问题思路:内存、CPU、 句柄数、过滤、查找、模拟POST和GET请求等等场景
  4. shelI脚本

六、数据库MySqI

  1. MySql的存储引擎的不同
  2. 单个索引、联合索引、主键索引
  3. Mysq|怎么分表,以及分表后如果想按条件分页查询怎么办(如果不是按分表字段来查询的话,几乎效率低下,无解)
  4. 分表之后想让一-个id多个表是自增的,效率实现
  5. MySq|的主从实时备份同步的配置,以及原理(从库读主库的binlog),读写分离
  6. 事物的四个特性,以及各自的特点(原子、隔离)等等,项目怎么解决这些问题

七、设计模式(写代码)

  1. 单例模式:饱汉、饿汉。以及饿汉中的延迟加载
  2. 工厂模式、装饰者模式、观察者模式等

八、算法&数据结构&设计模式

  1. 使用随机算法产生-个数, 要求把1-1000W之间这些数全部生成。(考 察高效率,解决产生冲突的问题)
  2. 两个有序数组的合并排序
  3. 一个数组的倒序
  4. 计算一个正整数的正平方根
  5. 说白了就是常见的那些查找排序算法
  6. 数组和链表数据结构描述,各自的时间复杂度
  7. 二叉树遍历
  8. 快速排序
  9. BTree相关的操作
  10. 在工作中遇到过哪些设计模式,是如何应用的
  11. hash算法的有哪几种,优缺点,使用场景
  12. 什么是一致性hash
  13. paxos算法

九、分布式缓存

  1. 为什么用缓存,用过哪些缓存,redis和memcache的区别
  2. redis的数据结构
  3. redis的持久化方式,以及项目中用的哪种,为什么
  4. redis集群的理解,怎么动态增加或者删除一个节点, 而保证数据不丢失。

十、线程池、高并发、NIO

  1. 分析线程池的实现原理和线程的调度过程
  2. 线程池如何调优
  3. 线程池的最大线程数目根据什么确定
  4. 动态代理的几种方式
  5. HashMap的并发问题
  6. 了解LinkedHashMap的应用吗
  7. 反射的原理,反射创建类实例的三种方式是什么?
  8. cloneable接口实现原理,浅拷贝or深拷贝
  9. Java NIO使用
  10. hashtable和hashmap的区别及实现原理,hashmap会问到数组索引,hash碰撞怎么解决
  11. arraylist和linkedlist区别及实现原理
  12. 反射中,Class.forName和ClassLoader区别
  13. String, Stringbuffer, StringBuilder的区别?
  14. 有没有可能2个不相等的对象有相同的hashcode
  15. 简述NIO的最佳实践,比如netty, mina
  16. TreeMap的实现原理

十一、JVM相关(面试必考)

  1. JVM内存分代
  2. Java 8的内存分代改进
  3. JVM垃圾回收机制,何时触发MinorGC等操作
  4. jvm中- -次完整的GC流程(从ygC到fgc) 是怎样的,重点讲讲对象如何晋升到老年代,几种主要的jvm参数等
  5. 你知道哪几种垃圾收集器,各自的优缺点,重点讲下cms, g1
  6. 新生代和老生代的内存回收策略
  7. Eden和Survivor的比例分配等
  8. 深入分析了Classloader,双亲委派机制
  9. JVM的编译优化
  10. 对Java内存模型的理解,以及其在并发中的应用
  11. 指令重排序,内存栅栏等
  12. 00M错误,stackoverflow错误, permgen space错误
  13. JVM常用参数

十二、分布式相关

  1. Dubbo的底层实现原理和机制
  2. 描述一一个服务从发布到被消费的详细过程
  3. 分布式系统怎么做服务治理
  4. 接口的幂等性的概念
  5. 消息中间件如何解决消息丢失问题
  6. Dubbo的服务请求失败怎么处理
  7. 重连机制会不会造成错误
  8. 对分布式事务的理解
  9. 如何实现负载均衡,有哪些算法可以实现?
  10. Zookeeper的用途,选举的原理是什么?
  11. 数据的垂直拆分水平拆分。
  12. zookeeper原理和适用场景
  13. zookeeper watch机制
  14. redis/zk节点宕机如何处理
  15. 分布式集群下如何做到唯一-序列号
  16. 如何做一个分布式锁
  17. 用过哪些MQ,怎么用的,和其他mq比较有什么优缺点,MQ的连接是线程安全的吗
  18. MQ系统的数据如何保证不丢失
  19. 列举出你能想到的数据库分库分表策略;分库分表后,如何解决全表查询的问题。

十三、数据库

  1. MySQL InnoDB存储的文件结构
  2. 索引树是如何维护的?
  3. 数据库自增主键可能的问题
  4. MySQL的几种优化
  5. mysq|索引为什么使用B +树
  6. 数据库锁表的相关处理
  7. 索引失效场景
  8. 高并发下如何做到安全的修改同一行数据,乐观锁和悲观锁是什么,INNODB的行级锁有哪2种,解释其含义
  9. 数据库会死锁吗,举-个死锁的例子,mysq|怎么解决死锁

十四、Redis&缓存相关

  1. Redis的并发竞争问题如何解决了解Redis事务的CAS操作吗
  2. 缓存机器增删如何对系统影响最小,-致性哈希的实现
  3. Redis持久化的几种方式,优缺点是什么,怎么实现
  4. Redis的缓存失效策略
  5. 缓存穿透的解决办法
  6. redis集群,高可用,原理
  7. mySQL里有2000w数据,redis中只存20w的数据, 如何保证redis中的数据都是热点数据
  8. 用Redis和任意语言实现一段恶意登录保护的代码, 限制1小时内每用户ld最多只能登录5次
  9. redis的数据淘汰策略

针对上面的问题有整理一份Java面试解析文档,包含集合,JVM,并发编程、Spring,MyBatis,微服务,Redis,Dubbo,设计模式,数据结构,分布式等!,由于篇幅有限,为了方便大家观看,这里以图片的形式给大家展示每部分的目录和答案截图!

资料获取方式:转发+关注后私信回复我【面试】即可获取资料的免费领取方式!

JVM篇——JVM知识点体系笔记

JVM篇—JVM高频常问面试解析

Java集合

Java并发体系

框架篇——Spring

框架篇——MyBatis

框架篇—SpringMVC

框架篇—Netty与RPC知识点笔记及面试解析

微服务篇——微服务知识点笔记

微服务篇——SpringBoot Cloud面试解析

微服务篇—dubbo面试解析

分布式篇——zookeeper、kafka、RabbitMQ、MongDB等

zookeeper

kafka、RabbitMQ

MongDB

Redis篇—Redis知识点实战笔记及面试问题解析

设计模式篇——23种设计模式知识点

MySQL数据库篇——包含索引、B+ 树、SQL 优化等

本文作者及来源:Renderbus瑞云渲染农场https://www.renderbus.com

点赞 0
收藏 0

文章为作者独立观点不代本网立场,未经允许不得转载。