金九银十Java面试的高峰期到了,今天给大家总结了一份Java常见面试题及答案,希望对大家有所帮助@mikechen
Java线程的生命周期
在线程的生命周期中,它要经过新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)5种状态。
Java实现线程有哪几种方式?
1、继承Thread类实现多线程
2、实现Runnable接口方式实现多线程
3、使用ExecutorService、Callable、Future实现有返回结果的多线程。
什么是并发与并行?
并行
多个cpu实例或者多台机器同时执行一段处理逻辑,是真正的同时。
并发
通过cpu调度算法,让用户看上去同时执行,实际上从cpu操作层面不是真正的同时。
HashMap1.7与1.8底层如何实现?
1.7底层实现基于数组+链表实现(Key和value封装成Entry对象)1.根据key的hash值,计算该key存放在数组的index位置
2.如果发生index冲突,则会使用单向链表存放
同一个链表中存放的都是hashCode值相同,但是内容值却不同
如果index发生冲突,采用链表存放查询的时间复杂度是为O(n),效率非常低,
所以在JDK1.8开始优化改为红黑树
3. 使用头插入法(并发下扩容可能会发生死循环问题)
1.8底层实现
基于数组+链表+红黑树实现(Key和value封装成Entry对象)
1.根据key的hash值,计算该key存放在数组的index位置
2.如果发生index冲突,则会使用单向链表存放
当数组的容量大于=64且链表长度大于8则会将链表转化成红黑树。
红黑树查询的时间复杂度是为O(logN)
当红黑树的节点个数<6则将红黑树转换成链表
3.使用尾插法 解决了HashMap1.7版本并发扩容引发扩容死循环问题。
HashMap和Hashtable的区别?
1. 线程同步,Hashtable线程安全,HashMap线程不安全
2. 效率问题,Hashtable效率低,HashMap效率高
3. HashMap可以使用null作为key,Hashtable不可以使用null为key
4. HashMap使用的是新实现,继承AbstractMap,而Hashtable是继承Dictionary类,实现比较老
5. Hash算不同,HashMap的hash算法比Hashtable的hash算法效率高
6. HashMap把Hashtable的contains方法去掉了,改成containsValue和containsKey。因为contains方法容易让人引起误解。
7. 取值不同,HashMap用的是Iterator接口,而Hashtable中还有使用Enumeration接口
ArrayList和LinkedList的区别
ArrayList使用的是数组结构,LinkedList使用的是链表结构,前者用在查询较多的场合,后者适用于插入较多的场合。
1)ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。 (LinkedList是双向链表,有next也有previous)
2)对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。
3)对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。
Java基本类型有哪些?
八大基本类型:
- boolean/1
- byte/8
- char/16
- short/16
- int/32
- float/32
- long/64
- double/64
String、StringBuffer、StringBuilder的区别?
String、StringBuffer、StringBuilder最大的不同是String不可变,后者可变。
StringBuffer是线程安全的,StringBuilder线程不安全速度较快。
Java多线程同步有哪几种方法?
Synchronized关键字,Lock锁实现,分布式锁等。
Java线程池核心参数有哪些?
public ThreadPoolExecutor( int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler ) { .... }
Java重载Overload和Override重写的区别?
Overload是重载的意思,Override是覆盖的意思,也就是重写。
重载和重写有共同之处,两个方法的方法名都必须相同,如果不同,既不构成重载,也不构成重写。
1. 重写必须发生在父子类之间,重载可以不在父子类之间
2. 重写的特点:
a) 参数列表完全相同:个数相同、类型相同、顺序相同
b) 子类的返回值不能比父类的返回值范围大
c) 子类方法抛出的异常不能比父类方法抛出的异常范围大
d) 修饰符只能为public、protected、friendly,不能为private
e) 父子类方法不能使用static修饰
3. 重载发生在同一个类或父子类之间,重写中参数列表至少满足个数不同、类型不同、顺序不同中的一个条件,不包含父子类之间的static方法
SpringBoot、Spring MVC和Spring有什么区别?
1)Spring
Spring最重要的特征是依赖注入。所有Spring Modules不是依赖注入就是IOC控制反转,当我们恰当的使用DI或者是IOC的时候,可以开发松耦合应用。
2)Spring MVC
Spring MVC提供了一种分离式的方法来开发Web应用。通过运用像DispatcherServelet,MoudlAndView 和 ViewResolver 等一些简单的概念,开发 Web 应用将会变的非常简单。
3)SpringBoot
Spring和Spring MVC的问题在于需要配置大量的参数,SpringBoot通过一个自动配置和启动的项来解决这个问题。
JVM内存模型
首先我们来了解一下JVM的内存模型的怎么样的:
1.堆:存放对象实例,几乎所有的对象实例都在这里分配内存
- 堆得内存由-Xms指定,默认是物理内存的1/64;最大的内存由-Xmx指定,默认是物理内存的1/4。
- 默认空余的堆内存小于40%时,就会增大,直到-Xmx设置的内存。具体的比例可以由-XX:MinHeapFreeRatio指定
- 空余的内存大于70%时,就会减少内存,直到-Xms设置的大小。具体由-XX:MaxHeapFreeRatio指定。
2.虚拟机栈
虚拟机栈描述的是Java方法执行的内存模型:每个方法被执行的时候都会同时创建一个栈帧(Stack Frame)用于存储局部变量表、操作栈、动态链接、方法出口等信息本地方法栈:本地方法栈则是为虚拟机使用到的Native方法服务。
3.方法区:存储已被虚拟机加载的类元数据信息(元空间)
1)有时候也成为永久代,在该区内很少发生垃圾回收,但是并不代表不发生GC,在这里进行的GC主要是对方法区里的常量池和对类型的卸载
2)方法区主要用来存储已被虚拟机加载的类的信息、常量、静态变量和即时编译器编译后的代码等数据。
该区域是被线程共享的。
3)方法区里有一个运行时常量池,用于存放静态编译产生的字面量和符号引用。该常量池具有动态性,也就是说常量并不一定是编译时确定,运行时生成的常量也会存在这个常量池中。
4.程序计数器:当前线程所执行的字节码的行号指示器
JVM垃圾回收算法
1.标记-清除: 这是垃圾收集算法中最基础的,根据名字就可以知道,它的思想就是标记哪些要被回收的对象,然后统一回收。这种方法很简单,但是会有两个主要问题:1.效率不高,标记和清除的效率都很低;2.会产生大量不连续的内存碎片,导致以后程序在分配较大的对象时,由于没有充足的连续内存而提前触发一次GC动作。
2.复制算法: 为了解决效率问题,复制算法将可用内存按容量划分为相等的两部分,然后每次只使用其中的一块,当一块内存用完时,就将还存活的对象复制到第二块内存上,然后一次性清楚完第一块内存,再将第二块上的对象复制到第一块。但是这种方式,内存的代价太高,每次基本上都要浪费一般的内存。 于是将该算法进行了改进,内存区域不再是按照1:1去划分,而是将内存划分为8:1:1三部分,较大那份内存交Eden区,其余是两块较小的内存区叫Survior区。每次都会优先使用Eden区,若Eden区满,就将对象复制到第二块内存区上,然后清除Eden区,如果此时存活的对象太多,以至于Survivor不够时,会将这些对象通过分配担保机制复制到老年代中。(java堆又分为新生代和老年代)
3. 标记-整理 该算法主要是为了解决标记-清除,产生大量内存碎片的问题;当对象存活率较高时,也解决了复制算法的效率问题。它的不同之处就是在清除对象的时候现将可回收对象移动到一端,然后清除掉端边界以外的对象,这样就不会产生内存碎片了。
4.分代收集 现在的虚拟机垃圾收集大多采用这种方式,它根据对象的生存周期,将堆分为新生代和老年代。在新生代中,由于对象生存期短,每次回收都会有大量对象死去,那么这时就采用复制算法。老年代里的对象存活率较高,没有额外的空间进行分配担保,所以可以使用标记-整理 或者 标记-清除。
Spring IOC原理?
IOC是Inversion of Control的缩写,多数书籍翻译成“控制反转”。
IOC不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。
传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试,有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,如下图所示:
上图引入了IOC容器,使得A、B、C、D这4个对象没有了耦合关系,齿轮之间的传动全部依靠“第三方”了,全部对象的控制权全部上缴给“第三方”IOC容器。
所以,IOC借助于“第三方”实现具有依赖关系的对象之间的解耦,使程序更优良。
Spring MVC的工作原理?
MVC即Model-View-Controller的缩写,它是web应用当中的常用的设计模式。
具体如下图所示:
Mybatis架构设计
我们把Mybatis的功能架构分为三层:
- API接口层
- 数据处理层
- 基础支撑层
Mybatis核心执行流程
mybatis的总体执行流程,总结如下:
1.MyBatis配置文件
config.xml:配置了全局配置文件,配置了MyBatis的运行环境等信息。
mapper,xml:sql的映射文件,配置了操作数据库的sql语句,此文件需在config.xml中加载。
2.SqlSessionFactory
通过MyBatis环境等配置信息构造SqlSessionFactory(会话工厂)。
3.SqlSession
通过会话工厂创建SqlSession(会话),对数据库进行增删改查操作。
4.Exector执行器
MyBatis底层自定义了Exector执行器接口来具体操作数据库,Exector接口有两个实现,一个基本执行器(默认),一个是缓存执行器,SqlSession底层是通过Exector接口操作数据库。
5.MappedStatement
MyBatis的一个底层封装对象,它包装了MyBatis配置信息与sql映射信息等。mapper.xml中的insert/select/update/delete标签对应一个MappedStatement对象。标签的id就是MappedStatement的id。
MappedStatement对sql执行输入参数进行定义,包括HashMap、基本类型、pojo、Executor通过MappedStatement在执行sql前将输入的Java对象映射至sql中,输入参数映射就是JDBC编程对preparedStatement设置参数。
MappedStatement对sql执行输出结果进行定义,包括HashMap、基本类型、pojo,Executor通过MappedStatement在执行sql后将输出结果映射至Java对象中,输出结果映射就是JDBC编程对结果的解析处理过程。
Maven常见的依赖范围有哪些?
maven的依赖范围包括: compile,provide,runtime,test,system。
- compile:表示编译范围,指A在编译时依赖B,该范围为默认依赖范围。编译范围的依赖会用在编译,测试,运行,由于运行时需要,所以编译范围的依赖会被打包。
- provided:provied依赖只有当jdk或者一个容器已提供该依赖之后才使用。provide依赖在编译和测试时需要,在运行时不需要。例如:servlet api被Tomcat容器提供了。
- runtime:runtime依赖在运行和测试系统时需要,但在编译时不需要。例如:jdbc的驱动包。由于运行时需要,所以runtime范围的依赖会被打包。
- test:test范围依赖在编译和运行时都不需要,只在测试编译和测试运行时需要。例如:Junit。由于运行时不需要,所以test范围依赖不会被打包。
- system:system范围依赖与provide类似,但是必须显示的提供一个对于本地系统中jar文件的路径。一般不推荐使用。
mikechen睿哥
mikechen睿哥,十余年BAT架构经验,资深技术专家,就职于阿里、淘宝、百度等一线互联网大厂。
关注「mikechen」公众号,获取更多技术干货!
后台回复【面试】即可获取《史上最全阿里Java面试题总结》,后台回复【架构】,即可获取《阿里架构师进阶专题全部合集》