Java 面试题及答案整理(金九银十突击版)
这套面试文档包含了:Java 基础、JVM、多线程 &并发、spring、mybatis、springboot、MySQL、springcloud、Dubbo、Nginx、MQ、数据结构与算法、Linux、Zookeeper、Redis 篇、分布式、网络篇、设计模式、maven 篇、ElasticSearch 篇、tomcat 篇、Git 篇、软实力篇,应有尽有、一网打尽!
一、基础篇
1、 Java 语言有哪些特点?
简单易学、有丰富的类库
面向对象(Java 最重要的特性,让程序耦合度更低,内聚性更高)
与平台无关性(JVM 是 Java 跨平台使用的根本)
可靠安全
支持多线程
2、面向对象和面向过程的区别
面向过程:是分析解决问题的步骤,然后用函数把这些步骤一步一步地实现,然后在使用的时候一 一调用则可。性能较高,所以单片机、嵌入式开发等一般采用面向过程开发
面向对象:是把构成问题的事务分解成各个对象,而建立对象的目的也不是为了完成一个个步骤, 而是为了描述某个事物在解决整个问题的过程中所发生的行为。面向对象有封装、继承、多态的特性,所以易维护、易复用、易扩展。可以设计出低耦合的系统。 但是性能上来说,比面向过程要 低。
3 、八种基本数据类型的大小,以及他们的封装类
注:

http://1.int是基本数据类型,Integer 是 int 的封装类,是引用类型。int 默认值是 0,而 Integer 默认值是 null,所以 Integer 能区分出 0 和 null 的情况。一旦 java 看到 null,就知道这个引用还没有指向某个对象,再任何引用使用前,必须为其指定一个对象,否则会报错。
2.基本数据类型在声明时系统会自动给它分配空间,而引用类型声明时只是分配了引用空间,必须通过实例化开辟数据空间之后才可以赋值。数组对象也是一个引用对象,将一个数组赋值给另一个数组时只是复制了一个引用,所以通过某一个数组所做的修改在另一个数组中也看的见。
虽然定义了 boolean 这种数据类型,但是只对它提供了非常有限的支持。在 Java 虚拟机中没有任何供 boolean 值专用的字节码指令,Java 语言表达式所操作的 boolean 值,在编译之后都使用 Java 虚拟机中的 int 数据类型来代替,而 boolean 数组将会被编码成 Java 虚拟机的 byte 数组,每个元素 boolean 元素占 8 位。这样我们可以得出 boolean 类型占了单独使用是 4 个字节,在数组中又是 1 个字 节。使用 int 的原因是,对于当下 32 位的处理器(CPU)来说,一次处理数据是 32 位(这里不是指的是 32/64 位系统,而是指 CPU 硬件层面),具有高效存取的特点。
4、标识符的命名规则。
标识符的含义: 是指在程序中,我们自己定义的内容,譬如,类的名字,方法名称以及变量名称等等,都是标识符。
命名规则:(硬性要求) 标识符可以包含英文字母,0-9 的数字,$以及_ 标识符不能以数字开头 标识符不是关键字
命名规范:(非硬性要求) 类名规范:首字符大写,后面每个单词首字母大写(大驼峰式)。 变量名规范:首字母小写,后面每个单词首字母大写(小驼峰式)。 方法名规范:同变量名。
5、instanceof 关键字的作用
instanceof 严格来说是 Java 中的一个双目运算符,用来测试一个对象是否为一个类的实例,用法 为:
其中 obj 为一个对象,Class 表示一个类或者一个接口,当 obj 为 Class 的对象,或者是其直接或间接子类,或者是其接口的实现类,结果 result 都返回 true,否则返回 false。
注意:编译器会检查 obj 是否能转换成右边的 class 类型,如果不能转换则直接报错,如果不能确定类型,则通过编译,具体看运行时定。
6、Java 自动装箱与拆箱
装箱就是自动将基本数据类型转换为包装器类型(int-->Integer);调用方法:Integer 的 valueOf(int) 方法
拆箱就是自动将包装器类型转换为基本数据类型(Integer-->int)。调用方法:Integer 的 intValue 方法
在 Java SE5 之前,如果要生成一个数值为 10 的 Integer 对象,必须这样进行:
而在从 Java SE5 开始就提供了自动装箱的特性,如果要生成一个数值为 10 的 Integer 对象,只需要
这样就可以了:
面试题 1 : 以下代码会输出什么?
运行结果:
为什么会出现这样的结果?输出结果表明 i1 和 i2 指向的是同一个对象,而 i3 和 i4 指向的是不同的对
象。此时只需一看源码便知究竟,下面这段代码是 Integer 的 valueOf 方法的具体实现:
其中 IntegerCache 类的实现为:
从这 2 段代码可以看出,在通过 valueOf 方法创建 Integer 对象的时候,如果数值在[-128,127]之间, 便返回指向 IntegerCache.cache 中已经存在的对象的引用;否则创建一个新的 Integer 对象。
上面的代码中 i1 和 i2 的数值为 100,因此会直接从 cache 中取已经存在的对象,所以 i1 和 i2 指向的是同一个对象,而 i3 和 i4 则是分别指向不同的对象。
面试题 2:以下代码输出什么
运行结果:
原因: 在某个范围内的整型数值的个数是有限的,而浮点数却不是。
7、 重载和重写的区别
重写(Override)
从字面上看,重写就是 重新写一遍的意思。其实就是在子类中把父类本身有的方法重新写一遍。子类继承了父类原有的方法,但有时子类并不想原封不动的继承父类中的某个方法,所以在方法名,参数列表,返回类型(除过子类中方法的返回值是父类中方法返回值的子类时)都相同的情况下, 对方法体进行修改或重写,这就是重写。但要注意子类函数的访问修饰权限不能少于父类的。
重写 总结: 1.发生在父类与子类之间 2.方法名,参数列表,返回类型(除过子类中方法的返回类型是父类中返回类型的子类)必须相同 3.访问修饰符的限制一定要大于被重写方法的访问修饰符 (public>protected>default>private) 4.重写方法一定不能抛出新的检查异常或者比被重写方法申 明更加宽泛的检查型异常
重载(Overload)
在一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同甚至是参数顺序不 同)则视为重载。同时,重载对返回类型没有要求,可以相同也可以不同,但不能通过返回类型是 否相同来判断重载。
重载 总结: 1.重载 Overload 是一个类中多态性的一种表现 2.重载要求同名方法的参数列表不同(参 数类型,参数个数甚至是参数顺序) 3.重载的时候,返回值类型可以相同也可以不相同。无法以返回型别作为重载函数的区分标准
篇幅限制下面就只能给大家展示小册部分内容了。这份面试笔记包括了:Java 基础、JVM、多线程 &并发、spring、mybatis、springboot、MySQL、springcloud、Dubbo、Nginx、MQ、数据结构与算法、Linux、Zookeeper、Redis 篇、分布式、网络篇、设计模式、maven 篇、ElasticSearch 篇、tomcat 篇、Git 篇、软实力篇 面试专题【领取/点击】

二、JVM 篇
1、知识点汇总
JVM 是 Java 运行基础,面试时一定会遇到 JVM 的有关问题,内容相对集中,但对只是深度要求较高.

其中内存模型,类加载机制,GC 是重点方面.性能调优部分更偏向应用,重点突出实践能力.编译器优化 和执行模式部分偏向于理论基础,重点掌握知识点.
需了解 内存模型各部分作用,保存哪些数据.
类加载双亲委派加载机制,常用加载器分别加载哪种类型的类.
GC分代回收的思想和依据以及不同垃圾回收算法的回收思路和适合场景.
性能调优常有 JVM 优化参数作用,参数调优的依据,常用的 JVM 分析工具能分析哪些问题以及使用方法.
执行模式解释/编译/混合模式的优缺点,Java7 提供的分层编译技术,JIT 即时编译技术,OSR 栈上替换,C1/C2 编译器针对的场景,C2 针对的是 server 模式,优化更激进.新技术方面 Java10 的 graal 编译器
编译器优化 javac 的编译过程,ast 抽象语法树,编译器优化和运行器优化.
2、知识点详解:
1)、JVM内存模型:
线程独占:栈,本地方法栈,程序计数器 线程共享:堆,方法区
2)、栈:
又称方法栈,线程私有的,线程执行方法是都会创建一个栈阵,用来存储局部变量表,操作栈,动态链接,方法出口等信息.调用方法时执行入栈,方法返回式执行出栈.
3)、本地方法栈
与栈类似,也是用来保存执行方法的信息.执行 Java 方法是使用栈,执行 Native 方法时使用本地方法栈.
4)、程序计数器
保存着当前线程执行的字节码位置,每个线程工作时都有独立的计数器,只为执行 Java 方法服务,执行 Native 方法时,程序计数器为空.
5)、堆
JVM 内存管理最大的一块,对被线程共享,目的是存放对象的实例,几乎所欲的对象实例都会放在这里, 当堆没有可用空间时,会抛出 OOM 异常.根据对象的存活周期不同,JVM 把对象进行分代管理,由垃圾回收器进行垃圾的回收管理
6)、方法区:
又称非堆区,用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译器优化后的代码等数据.1.7 的永久代和 1.8 的元空间都是方法区的一种实现
7)、JVM 内存可见性

JMM 是定义程序中变量的访问规则 , 线程对于变量的操作只能在自己的工作内存中进行 , 而不能直接对主内存操作. 由于指令重排序 , 读写的顺序会被打乱 , 因此 JMM 需要提供原子性 , 可见性 , 有序性保证 .

3、说说类加载与卸载
加载过程

其中验证,准备,解析合称链接
加载通过类的完全限定名,查找此类字节码文件,利用字节码文件创建 Class 对象.
验证确保 Class 文件符合当前虚拟机的要求,不会危害到虚拟机自身安全.
准备进行内存分配,为 static 修饰的类变量分配内存,并设置初始值(0 或 null).不包含 final 修饰的静态变 量,因为 final 变量在编译时分配.
解析将常量池中的符号引用替换为直接引用的过程.直接引用为直接指向目标的指针或者相对偏移量等.
初始化主要完成静态块执行以及静态变量的赋值.先初始化父类,再初始化当前类.只有对类主动使用 时才会初始化.
触发条件包括,创建类的实例时,访问类的静态方法或静态变量的时候,使用 Class.forName 反射类的时候,或者某个子类初始化的时候.
Java 自带的加载器加载的类,在虚拟机的生命周期中是不会被卸载的,只有用户自定义的加载器加载的类才可以被卸.
1)、加载机制-双亲委派模式

双亲委派模式,即加载器加载类时先把请求委托给自己的父类加载器执行,直到顶层的启动类加载器. 父类加载器能够完成加载则成功返回,不能则子类加载器才自己尝试加载.*
优点:
1. 避免类的重复加载
2. 避免 Java 的核心 API 被篡改
2)、分代回收
分代回收基于两个事实:大部分对象很快就不使用了,还有一部分不会立即无用,但也不会持续很长时 间.

年轻代->标记-复制 老年代->标记-清除
3、回收算法
a、G1算法
1.9 后默认的垃圾回收算法,特点保持高回收率的同时减少停顿.采用每次只清理一部分,而不是清理全部的增量式清理,以保证停顿时间不会过长
其取消了年轻代与老年代的物理划分,但仍属于分代收集器,算法将堆分为若干个逻辑区域(region),一部分用作年轻代,一部分用作老年代,还有用来存储巨型对象的分区.
同 CMS 相同,会遍历所有对象,标记引用情况,清除对象后会对区域进行复制移动,以整合碎片空间.
年轻代回收: 并行复制采用复制算法,并行收集,会 StopTheWorld.
老年代回收: 会对年轻代一并回收
初始标记完成堆 root 对象的标记,会 StopTheWorld. 并发标记 GC 线程和应用线程并发执行. 最终标记完成三色标记周期,会 StopTheWorld. 复制/清楚会优先对可回收空间加大的区域进行回收
b、ZGC算法
前面提供的高效垃圾回收算法,针对大堆内存设计,可以处理 TB 级别的堆,可以做到 10ms 以下的回收停顿时间.

着色指针
读屏障
并发处理
基于 region
内存压缩(整理)
roots 标记:标记 root 对象,会 StopTheWorld. 并发标记:利用读屏障与应用线程一起运行标记,可能 会发生 StopTheWorld. 清除会清理标记为不可用的对象. roots 重定位:是对存活的对象进行移动,以
腾出大块内存空间,减少碎片产生.重定位最开始会 StopTheWorld,却决于重定位集与对象总活动集的比例. 并发重定位与并发标记类似.
4、简述一下 JVM 的内存模型
1).JVM 内存模型简介
JVM 定义了不同运行时数据区,他们是用来执行应用程序的。某些区域随着 JVM 启动及销毁,另外一些区域的数据是线程性独立的,随着线程创建和销毁。jvm 内存模型总体架构图如下:(摘自 oracle 官方网站)

JVM 在执行 Java 程序时,会把它管理的内存划分为若干个的区域,每个区域都有自己的用途和创建 销毁时间。如下图所示,可以分为两大部分,线程私有区和共享区。下图是根据自己理解画的一个 JVM 内存模型架构图:

JVM 内存分为线程私有区和线程共享区
线程私有区
1)、程序计数器
当同时进行的线程数超过 CPU 数或其内核数时,就要通过时间片轮询分派 CPU 的时间资源,不免发生线程切换。这时,每个线程就需要一个属于自己的计数器来记录下一条要运行的指令。如果执行的是 JAVA 方法,计数器记录正在执行的 java 字节码地址,如果执行的是 native 方法,则计数器为 空。
2)、虚拟机栈
线程私有的,与线程在同一时间创建。管理 JAVA 方法执行的内存模型。每个方法执行时都会创建一个桢栈来存储方法的的变量表、操作数栈、动态链接方法、返回值、返回地址等信息。栈的大小决定了方法调用的可达深度(递归多少层次,或嵌套调用多少层其他方法,-Xss 参数可以设置虚拟机栈大小)。栈的大小可以是固定的,或者是动态扩展的。如果请求的栈深度大于最大可用深度,则抛出 stackOverflowError;如果栈是可动态扩展的,但没有内存空间支持扩展,则抛出 OutofMemoryError。 使用 jclasslib 工具可以查看 class 类文件的结构。下图为栈帧结构图:

3)、本地方法栈
与虚拟机栈作用相似。但它不是为 Java 方法服务的,而是本地方法(C 语言)。由于规范对这块没有强制要求,不同虚拟机实现方法不同。
线程共享区
1)、方法区
线程共享的,用于存放被虚拟机加载的类的元数据信息,如常量、静态变量和即时编译器编译后的 代码。若要分代,算是永久代(老年代),以前类大多“static”的,很少被卸载或收集,现回收废弃 常量和无用的类。其中运行时常量池存放编译生成的各种常量。(如果 hotspot 虚拟机确定一个类 的定义信息不会被使用,也会将其回收。回收的基本条件至少有:所有该类的实例被回收,而且装 载该类的 ClassLoader 被回收)
2)、堆
存放对象实例和数组,是垃圾回收的主要区域,分为新生代和老年代。刚创建的对象在新生代的 Eden 区中,经过 GC 后进入新生代的 S0 区中,再经过 GC 进入新生代的 S1 区中,15 次 GC 后仍存在就进入老年代。这是按照一种回收机制进行划分的,不是固定的。若堆的空间不够实例分配,则 OutOfMemoryError。


Java 面试题目录

多线程 &并发篇(46 道面试题)


评论