Java工程师面试题
前言:时间不负有心人,星光不问赶路人;
适宜阅读人群
- 准备跳槽的初/中/高级 Java 程序员
- 想要查漏补缺的人
- 想要不断完善和扩充自己 Java 技术栈的人
- Java 面试官
一、Java 基础
1.JDK和JRE的区别
- JDK:Java开发工具包;提供Java的开发环境和运行环境。
- JRE:Java运行环境;提供Java运行所需要的运行环境。
2.==和equals的区别
- ==解读:基本类型比较值,引用类型比较内存地址
- equals()解读:默认比较内存地址
3.两个对象的HodeCode()相同,则equals也一定相同么
- 不相同;两个对象的HashCode相同,equals()不一定相同。
4.final在Java中的作用
- 类:被修饰的类不能被继承
- 方法:被修饰的方法不能被重写
- 变量:被修饰的变量初始化后不能被修改
5.String字符串属于基本类型么
- String不属于基本类型,属于引用类型对象。
6.String字符串操作类
- String 和 StringBuffer、StringBuilder 的区别在于 String 声明的是不可变的对象,每次操作会生成新的String对象,然后指针指向新的String对象;StringBuilder和StringBuffer的区别,StringBuffer线程安全,Stringbulider非线程安全;
7.抽象类一定有抽象方法么
- 不需要,抽象类不一定有抽象方法;
8.普通类和抽象类的区别‘
- 普通类不能包含抽象方法,抽象类包含抽象方法
- 抽象类不能实例化,普通可以实例化
9.抽象类能够使用final修饰么
- 不能;因为抽象类需要被其他类继承;final修饰的类不能继承,容易矛盾。
10.接口和抽象类的区别
- 实现:抽象类的子类使用 extends 来继承;接口必须使用 implements 来实现接口。
- 构造函数:抽象类有构造函数;接口没有构造函数
- 实现数量:Java类只能单继承,接口能够多实现
11.Java中的IO种类
- 按功能来分:输入流、输出流。
- 按类型来分:字节流、字符流。
12.BIO和NIO、AIO区别是什么
- BIO:同步堵塞=一个连接一个线程
- NIO:同步非堵塞=一个请求一个线程
- AIO:异步非堵塞=一个有效请求一个线程
二、Java 容器
1.Java容器有哪些
- List:ArrayList、LinkedList、Vector。
- Set:HashSet、TreeSet。
- Map:HashMap 、TreeMap、ConcurrentMap、Hashtable。
2.Conllection和Conllections区别是什么
- Conllection:是一个集合接口。他提供对集合对象进行基础操作的通用接口方法。主要接口类:List、set
- Conllections:针对集合类的工具类,提供一系列静态方法实现对各种集合的搜索、排序、线程安全等操作;服务Conllection集合框架
3.List、Set、Map之间的区别是什么
4.HashMap和Hashtable的区别是什么
- 存储:HashMap允许键值NuLL;Hashtable不允许键值NULL
- 线程安全:HashMap非线程安全;Hatable线程安全
- 单线程情况下使用HashMap;多线程并发情况下使用ConurrentMap;
5.如何决定使用TreeMap还是HashMap
- Map中插入、删除、定位一个元素使用HashMap;对于一个Key集合进行有序遍历使用TreeMap 、
6.HashMap底层实现原理
- HashMap整体是个Entry数组,每个Entry包含一个key-value数组
- HashMap基于Hash算法实现:GET获取、PUT存储
- 传入Key时根据HashCode计算Hash值,根据Hash值将value保存入数组桶中
- HashCode计算出相同的Hash值称之为Hash冲突;用链表+红黑树存储相同Hash值的value "拉链法"
- 如若Hash冲突较少时,则使用链表否则使用红黑树;
7.ArrayList和LinkedList是区别什么
- 数据结构:ArrayList动态数组实现、LinkedList双向链表实现
- 随机访问:ArrayList效率高于LinkedList;LinkedList线性存储方式需要移动指针从前往后查找;
- 增加删除:LinkedList效率高于LinkedList;ArrayList增删操作影响数组下标位置,需要移动数据;
8.如何实现数组和集合之间的转换
- 数组转 List:使用 Arrays.asList(array) 进行转换。
- List 转数组:使用 List 自带的 toArray() 方法。
9.ArrayList和Vetor区别是什么
- 线程安全:Vector 使用了 Synchronized 来实现线程同步,是线程安全的,而 ArrayList 是非线程安全的。
- 性能:ArrayList 在性能方面要优于 Vector。
- 扩容:ArrayList 和 Vector 都会根据实际的需要动态的调整容量,只不过在 Vector 扩容每次会增加 1 倍,而 ArrayList 只会增加 50%。
10.Array和ArrayList区别是什么
- Array存储基本类型和对象;ArrayList只能存储对象
- Array数组固定大小;ArrayList动态扩容数组大小
- Array 内置方法没有 ArrayList 多,比如 addAll、removeAll、iteration 等方法只有 ArrayList 有。
11.那些集合类线程安全
- Vector、Hashtable、Stack 都是线程安全的,而像 HashMap 则是非线程安全的,不过在 jdk 1.5 之后随着 java.util.concurrent 并发包的出现,它们也有了自己对应的线程安全类,比如 HashMap 对应的线程安全类就是 ConcurrentHashMap。
12.迭代器是什么
- Iterator 接口提供遍历任何 Collection 的接口。
13.怎么样确保一个集合不被修改
- 使用 Collections.unmodifiableCollection(Collection c) 方法来创建一个只读集合
三、Java 多线程
1.并发和并行的区别
- 并行(同一时刻,两个线程执行)
- 并发(同一时刻,只有一个执行,但一个时间段,两个线程都执行了)
2.线程和进程的区别
- 一个程序至少有一个进程;一个进程至少有一个或多个线程。
3.守护线程是什么呢
- 独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。在 java 中垃圾回收线程就是特殊的守护线程。
4.创建线程方式几种
- 继承Thread类重写run方法
- 实现Runable接口
- 实现Callable接口
5.runable和callable区别
- runable没有返回值;callable有返回值
6.线程有哪些状态
- 创建
- 就绪
- 运行
- 堵塞
- 死亡
7.sleep和wait区别
- 类不同:sleep继承Thread;wait继承Object;
- 释放锁:sleep不释放锁;wait释放锁
- 用法不同;sleep时间到自动恢复;wait等待notify唤醒
8.notify()和notifyAll() 区别
- notifyAll() 会将全部线程由等待池移到锁池,然后参与锁的竞争,竞争成功则继续执行,如果不成功则留在锁池等待锁被释放后再次参与竞争。
- notify()只会唤醒一个线程,具体唤醒哪一个线程由虚拟机控制。
9.线程run和start区别
- start()方法用于启动线程;run()用于执行线程运行的代码;run可多次调用;start只能调用一次;
10.创建线程池方式
- newSingleThreadExecutor():它的特点在于工作线程数目被限制为 1,操作一个无界的工作队列,所以它保证了所有任务的都是被顺序执行,最多会有一个任务处于活动状态,并且不允许使用者改动线程池实例,因此可以避免其改变线程数目;
- newCachedThreadPool():它是一种用来处理大量短时间工作任务的线程池,具有几个鲜明特点:它会试图缓存线程并重用,当无缓存线程可用时,就会创建新的工作线程;如果线程闲置的时间超过 60 秒,则被终止并移出缓存;长时间闲置时,这种线程池,不会消耗什么资源。其内部使用 SynchronousQueue 作为工作队列;
- newFixedThreadPool(int nThreads):重用指定数目(nThreads)的线程,其背后使用的是无界的工作队列,任何时候最多有 nThreads 个工作线程是活动的。这意味着,如果任务数量超过了活动队列数目,将在工作队列中等待空闲线程出现;如果有工作线程退出,将会有新的工作线程被创建,以补足指定的数目 nThreads;
- newSingleThreadScheduledExecutor():创建单线程池,返回 ScheduledExecutorService,可以进行定时或周期性的工作调度;
- newScheduledThreadPool(int corePoolSize):和newSingleThreadScheduledExecutor()类似,创建的是个 ScheduledExecutorService,可以进行定时或周期性的工作调度,区别在于单一工作线程还是多个工作线程;
- newWorkStealingPool(int parallelism):这是一个经常被人忽略的线程池,Java 8 才加入这个创建方法,其内部会构建ForkJoinPool,利用Work-Stealing算法,并行地处理任务,不保证处理顺序;
- ThreadPoolExecutor():是最原始的线程池创建,上面1-3创建方式都是对ThreadPoolExecutor的封装。
11.线程池有那些状态
- 创建
- 就绪
- 运行
- 堵塞
- 死亡
12.Java怎么保证线程安全
- 方法一:使用安全类,比如 java.util.concurrent 下的类。
- 方法二:使用自动锁 synchronized。
- 方法三:使用手动锁 Lock。
13.多线程中synchronized锁升级
- synchronized 锁升级原理:在锁对象的对象头里面有一个 threadid 字段,在第一次访问的时候 threadid 为空,jvm 让其持有偏向锁,并将 threadid 设置为其线程 id,再次进入的时候会先判断 threadid 是否与其线程 id 一致,如果一致则可以直接使用此对象,如果不一致,则升级偏向锁为轻量级锁,通过自旋循环一定次数来获取锁,执行一定次数之后,如果还没有正常获取到要使用的对象,此时就会把锁从轻量级升级为重量级锁,此过程就构成了 synchronized 锁的升级。
14.什么是死锁
- 线程A持有独占锁A,并尝试去获取独占锁B ;线程B持有独占锁B,并尝试去获取独占锁A 。就会产生双方都想获取对方所需要的锁资源,而发生的阻塞现象,我们称为死锁。
15.怎么防止死锁
- 使用tryLoca方法;设置超时时间,超时可以退出防止死锁
- 使用安全类 java.util.concurrent 并发类代替自己手写锁
- 降低锁的使用粒度,尽量不要几个功能用同一把锁。
- 减少同步的代码块。
16.ThreadLocal是什么?使用场景
- 为每个使用线程的变量提供变量副本,每个线程都可以改变自身的变量副本,而不影响其他线程
17.synchronized实现原理
- synchronized 是由一对 monitorenter/monitorexit 指令实现的,monitor 对象是同步的基本实现单元。在 java 6 之前,monitor 的实现完全是依靠操作系统内部的互斥锁,因为需要进行用户态到内核态的切换,所以同步操作是一个无差别的重量级操作,性能也很低。但在 java 6 的时候,java 虚拟机 对此进行了大刀阔斧地改进,提供了三种不同的 monitor 实现,也就是常说的三种不同的锁:偏向锁(Biased Locking)、轻量级锁和重量级锁,大大改进了其性能。
18.synchronized和volatile区别
- volatile 是变量修饰符;synchronized 是修饰类、方法、代码段。
- volatile 仅能实现变量的修改可见性,不能保证原子性;而 synchronized 则可以保证变量的修改可见性和原子性。
- volatile 不会造成线程的阻塞;synchronized 可能会造成线程的阻塞。
19.synchronized和lock区别
- synchronized 可以给类、方法、代码块加锁;而 lock 只能给代码块加锁。
- synchronized 不需要手动获取锁和释放锁,使用简单,发生异常会自动释放锁,不会造成死锁;而 lock 需要自己加锁和释放锁,如果使用不当没有 unLock()去释放锁就会造成死锁。
- 通过 Lock 可以知道有没有成功获取锁,而 synchronized 却无法办到。
20.synchronized和ReentranLock区别
- ReentrantLock 使用起来比较灵活,但是必须有释放锁的配合动作;
- ReentrantLock 必须手动获取与释放锁,而 synchronized 不需要手动释放和开启锁;
- ReentrantLock 只适用于代码块锁,而 synchronized 可用于修饰方法、代码块等。
- volatile 标记的变量不会被编译器优化;synchronized 标记的变量可以被编译器优化。
四、Java 反射
1.什么是Java反射
- Java反射是在动态运行状态中可以通过动态的方式获取这个类的属性和方法;
2.什么是Java序列化
- Java序列化是为了保存各种对象的内存状态,并且可以读取出来
3.动态代理是什么:运行时动态生成代理对象
- 动态代理应用:Spring Aop 、hibernate 数据查询、Java对象注解获取
4.怎么实现动态代理
- JDK动态代理:基于已经实现过的接口,实现代理对象
- CGLIB动态代理:基于未实现过的接口,实现代理对象;效率优先使用JDK动态代理
五、Java对象拷贝
1.为什么使用克隆
- 克隆的对象可能包含一些已经修改过的属性,而 new 出来的对象的属性都还是初始化时候的值,所以当需要一个新的对象来保存当前对象的“状态”就靠克隆方法了。
2.如何实现克隆
- 实现 Cloneable 接口并重写 Object 类中的 clone()方法。
- 实现 Serializable 接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深度克隆。
3.浅拷贝、深拷贝区别
- 浅克隆:当对象被复制时只复制它本身和其中包含的值类型的成员变量,而引用类型的成员对象并没有复制。
- 深克隆:除了对象本身被复制外,对象所包含的所有成员变量也将复制。
六、Java Web
1.JSP九大内置对象
- Request
- Response
- PageConText
- Session
- Application
- out
- Config
- Page
- Excpetion
2.Session和Cookie区别是什么
- 存储位置:Session存储在服务器;Cookie存储在浏览器
- 安全性:cookie安全性一般,在浏览器中可以被修改
- 容量个数:cookie 有容量限制,每个站点下的 cookie 也有个数限制。
- 存储多样:cookie只能存储在浏览器;session可以在MySQL数据库、Redis数据库中
3.Session工作原理
- 客户端登录后,服务器会创建对应的sessionID,并把sessionID发送给客户端,客户端再存储到浏览器中。这样每次客户端访问服务器时,会带sessionID。 服务器拿到sessionID后,在内存中找到对应的session。这就是session的工作原理
4.如果客户端禁用cookie,SessionId还能使用么;
- 可以在网址url 中添加 sessionid 的方式保证 session 能正常使用。
七、Java 异常
1.throw和throws区别?
- throw:是真实抛出一个异常。
- throws:是声明可能会抛出一个异常。
2..final、finally、finalize 有什么区别?
- final:如果修饰类,此类不能被继承;如果修饰方法和变量,则表示此方法和此变量不能在被改变,只能使用。
- finally:是 try{} catch{} finally{} 最后一部分,表示不论发生任何情况都会执行,但如果 finally 部分存在,则一定会执行 finally 里面的代码。
- finalize: 是 Object 类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法。
3.常见的异常类有哪些?
- NullPointerException 空指针异常
- ClassNotFoundException 指定类不存在
- NumberFormatException 字符串转换为数字异常
- IndexOutOfBoundsException 数组下标越界异常
- ClassCastException 数据类型转换异常
- FileNotFoundException 文件未找到异常
- NoSuchMethodException 方法不存在异常
- IOException IO 异常
- SocketException Socket 异常
八、Java 网络
1.TCP和UDP区别
- TCP面向连接、UDP不用建立连接;
- TCP提供可靠数据传输、不保证;
- TCP字节流、UDP面向报文;
- TCP传输慢、UDP传输快;
2.TCP为什么三次握手?两次不行么?
- 采用两次握手,只要服务端发送确认数据包就会建立连接,客户端此时并未响应服务端,那么服务端就会一直等待客户端;浪费服务器资源
- 采用三次握手,服务端就会知道客户端没有建立连接请求;不会浪费服务器资源
3.TCP粘包是怎么产生的?
- 发送端需要缓冲区满了才发出去,造成脏包
- 接收方不及时接收缓冲区的包,造成脏包
4.get 和 post 请求有哪些区别?
- get 请求会被浏览器主动缓存,而 post 不会。
- get 传递参数有大小限制,而 post 没有。
- post 参数传输更安全,get 的参数会明文限制在 url 上,post 不会
5.TCP/IP-三次握手
6.TCP/IP-四次挥手
- 客户端-发送一个 关闭(FIN),用来关闭客户端到服务器的数据传送
- 服务器-收到这个 关闭(FIN),它发回一 个 确认(ACK),确认序号并设置序号加 1
- 服务器-关闭与客户端的连接,发送一个 关闭(FIN)给客户端
- 客户端-发回 确认(ACK) 报文确认,并将确认序号并设置收到序号加 1
九、设计模式
1.说一下你熟悉的设计模式?
- 单例模式:保证被创建一次,节省系统开销。
- 工厂模式(简单工厂、抽象工厂):解耦代码。
- 观察者模式:定义了对象之间的一对多的依赖,这样一来,当一个对象改变时,它的所有的依赖者都会收到通知并自动更新。
- 外观模式:提供一个统一的接口,用来访问子系统中的一群接口,外观定义了一个高层的接口,让子系统更容易使用。
- 模版方法模式:定义一个算法的骨架,而将一些步骤延迟到子类中,模版方法使得子类可以在不改变算法结构的情况下,重新定义算法的步骤。
- 状态模式:允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。
十、Spring 框架
1.为什么使用Spring
- Spring是一款轻量级的Java开发框架,提高开发者的开发效率及系统维护性
- Spring核心思想就是不重复造轮子,开箱即用;
- Spring核心功能IOC控制反转、AOP面向切面编
2.IOC 控制反转
- IOC是中设计思想,不是具体技术实现;IOC将我们程序本身创建对象的权力交给了Spring容器管理
- 控制:创建对象的权力;反转:Spring容器、IOC容器
- 将对象之间相互依赖的权力交给了IOC容器,并通过IOC容器注入。
- IOC就绪工厂,我们创建多个对象,只需考虑配置好配置文件和注解,不考虑对象是如何创建出来的;
3.IOC面向切面编程
- AOP将那些与业务无关的,为模块所调用的逻辑和责任进行模块化(事务、日志、权限)
- AOP减少代码复用、减低耦合、提高扩展性、维护性;
- AOP基于动态代理,如果需要代理对象通过JDK动态代理去创建代理对象;没有实现的接口AOP无法代理对象
4.Spring主要有那些模块
- Core
- AOP
- IOC
- Context
- Dao
- Web
- Web Mvc
5.Spring注入方式
- 构造器注入
- Set注入
- 注解注入
6.Spring中Bean是线程安全么?
- Spring Bean默认线程安全因为是单例模式,Spring框架中并没有单例Bean进行封装多线程
7.Spring五大作用域
- singlenton :Spring容器中只有一个实例,bean以单例模式存在。系统默认值
- prototype:每次容器调用都会创建新的示例
Web环境下作用域
- request:每次HTTP请求都会创建一个Bean
- Session:每次HTTP Session共享一个Bean
- global-session:用于 portlet 容器,因为每个 portlet 有单独的 session,globalsession 提供一个全局性的 http session。
8.Spring事务实现方式有哪些?
- 声明式事务:基于 xml 配置文件的方式和注解方式(在类上添加 @Transaction 注解)。
- 编程式事务:提供编码的形式管理和维护事务。
十一、Spring MVC 框架
1.Spring MVC 工作原理
- 用户发送请求到前端控制器
- 前端控制器收到请求,调用处理器映射器
- 处理器映射器根据URL地址并生成处理器拦截器对象,并返回给前端控制器
- 前端控制器调用处理器适配器
- 处理器适配器经过适配后调用后端控制器
- 后端控制器将执行结果返回给模型和视图
- 处理器适配器将后端控制器执行的结果返回给视图解析器
- 视图解析器解析后返回具体视图
- 前端控制器根据视图进行视图渲染,并响应用户
2.Spring mvc 有哪些组件?
- 前置控制器 DispatcherServlet。
- 映射控制器 HandlerMapping。
- 后端控制器 Controller。
- 模型和视图 ModelAndView。
- 视图解析器 ViewResolver。
十二、SpringBoot
1.什么是Springboot
- Springboot为Spring服务,主要简化项目开发、系统搭建难度。
2.为什么使用Springboot
- 独立运行
- 简化配置
- 容易上手
- 自动装配
- 无XML配置
3.SpringBoot核心配置文件
- bootstrap (.yml 或者 .properties):boostrap 由父 ApplicationContext 加载的,比 applicaton 优先加载,且 boostrap 里面的属性不能被覆盖;
- application (.yml 或者 .properties):用于 spring boot 项目的自动化配置。
4.spring boot 配置文件有哪几种类型?
- 配置文件有 .properties 格式和 .yml 格式,它们主要的区别是书法风格不同。
5.SpringBoot核心装配原理
SpringBoot核心注解:@SpringBootApplication类上有三个注解
- @EnableAutoConfiguration:启用Springboot自动配置机制
- @SpringbootConfiguration:它组合了 @Configuration 注解,实现配置文件的功能。
- @ComponentScan:扫描@Service、@Controller注解的Bean;默认扫描启动类所在包下的类
EnableAutoConfiguration实际上是个简单的类,自动装配核心功能是通过AutoConfigurationImportSelector类
SpringBoot自动转配原理
AutoConfigurationImportSelector类实现了ImportSelector接口也实现SelectImport方法,该方法主要将所有复合条件的类注入到IOC容器中;再通过@EnableAutoConfiguration注解开启自动配置,通过SpringFactoryLoader加载MATE-INF/Srping.factories中的自动配置类实现自动转配。自动装配类其实通过@Conditional加载配置类。
十三、MyBatis 半自动化对象关系映射框架
1.MyBatis之#{}和${}区别是什么?
- ${}是变量占位符,用于标签属性和sql内部,属于静态文本替换
- #{}是参数占位符,MyBatis会将#{}替换成?,SQL在编译前会使用PrepredStatement参数设置方法,按序给SQL的?号占位符设置参数值
2.MyBatis几种分页
- 逻辑分页:MyBatis自带RowBounds进行分页;一次查询很多数据,然后在数据中检索
- 物理分页:手写SQL分页或使用分页插件PageHelper
3.MyBatis是否支持延迟加载
- 设置 lazyLoadingEnabled=true
- 基于CGLIB创建代理对象,调用目标方法,进入拦截器方法
- 如a.getB().getName(),拦截器发现没有值;那么会将单独发送事先保存好的查询关联对象B的Sql,查询出来,然后调用a.getB(b),这样a类就有了b的值了;这就是延迟加载
4.MyBatis之三大执行器
SimpleExecutor
- 每次执行更新,就开启一个Statement对象,用完后关闭Statement对象
ReuseExecutor
- 每次执行更新,sql通过key查找Statemtn对象,没有则创建Statement对象,用完也不关闭Statement对象,放到Map中等下次使用
BatchExecutor
- 每次执行更新,将所有的sql批量处理,等待统一处理,缓存中有多个Statement,将多个Statement添加到AddBatch()等代逐个executorBathc()处理
5.MyBatis一级缓存、二级缓存
- 一级缓存:默认级别缓存,SqlSession级别缓存;第一次执行查询SQL语句后,MyBatis自动将其放入缓存中,后续再次查询时,如果没有声明需要刷新,且缓存没有超时,会直接取出此前缓存的数据;
- 二级缓存:手动开启缓存,Mapper级别缓存,当sqlSession级别缓存关闭后一级缓存会存储到二级缓存中