最近发现网上很多Java面试题都没有答案,所以花了很长时间搜集整理出来了这套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 、八种基本数据类型的大小,以及他们的封装类
注:
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中的一个双目运算符,用来测试一个对象是否为一个类的实例,用法 为:
boolean result = obj instanceof Class
其中 obj 为一个对象,Class 表示一个类或者一个接口,当 obj 为 Class 的对象,或者是其直接或间接子类,或者是其接口的实现类,结果result 都返回 true,否则返回false。
注意:编译器会检查 obj 是否能转换成右边的class类型,如果不能转换则直接报错,如果不能确定类型,则通过编译,具体看运行时定。
int i = 0;
System.out.println(i instanceof Integer);//编译不通过 i必须是引用类型,不能是基本类型
System.out.println(i instanceof Object);//编译不通过
Integer integer = new Integer(1);
System.out.println(integer instanceof Integer);//true
//false ,在 JavaSE规范 中对 instanceof 运算符的规定就是:如果 obj 为 null,那么将返回 false。
System.out.println(null instanceof Object);
6、Java自动装箱与拆箱
装箱就是自动将基本数据类型转换为包装器类型(int-->Integer);调用方法:Integer的valueOf(int) 方法
拆箱就是自动将包装器类型转换为基本数据类型(Integer-->int)。调用方法:Integer的 intValue方法
在Java SE5之前,如果要生成一个数值为10的Integer对象,必须这样进行:
Integer i = new Integer(10);
而在从 Java SE5 开始就提供了自动装箱的特性,如果要生成一个数值为 10 的 Integer 对象,只需要
这样就可以了:
Integer i = 10;
面试题 1 : 以下代码会输出什么?
public class Main {
public static void main(String[] args) {
Integer i1 = 100;
Integer i2 = 100;
Integer i3 = 200;
Integer i4 = 200;
System.out.println(i1==i2);
System.out.println(i3==i4);
}
}
运行结果:
true
false
为什么会出现这样的结果?输出结果表明 i1 和 i2 指向的是同一个对象,而 i3 和 i4 指向的是不同的对
象。此时只需一看源码便知究竟,下面这段代码是 Integer 的 valueOf 方法的具体实现:
public static Integer valueOf(int i) {
if(i >= -128 && i <= IntegerCache.high)
return IntegerCache.cache[i + 128];
else
return new Integer(i);
}
其中 IntegerCache 类的实现为:
private static class IntegerCache {
static final int high;
static final Integer cache[];
static {
final int low = -128;
// high value may be configured by property
int h = 127;
if (integerCacheHighPropValue != null) {
// Use Long.decode here to avoid invoking methods that
// require Integer's autoboxing cache to be initialized
int i = Long.decode(integerCacheHighPropValue).intValue();
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - -low);
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
}
private IntegerCache() {}
}
从这2段代码可以看出,在通过valueOf方法创建Integer对象的时候,如果数值在[-128,127]之间, 便返回指向IntegerCache.cache中已经存在的对象的引用;否则创建一个新的Integer对象。
上面的代码中i1和i2的数值为100,因此会直接从cache中取已经存在的对象,所以i1和i2指向的是同一个对象,而i3和i4则是分别指向不同的对象。
面试题2:以下代码输出什么
public class Main {
public static void main(String[] args) {
Double i1 = 100.0;
Double i2 = 100.0;
Double i3 = 200.0;
Double i4 = 200.0;
System.out.println(i1==i2);
System.out.println(i3==i4);
}
}
运行结果:
false
false
原因: 在某个范围内的整型数值的个数是有限的,而浮点数却不是。
7、 重载和重写的区别
重写(Override)
从字面上看,重写就是 重新写一遍的意思。其实就是在子类中把父类本身有的方法重新写一遍。子类继承了父类原有的方法,但有时子类并不想原封不动的继承父类中的某个方法,所以在方法名,参数列表,返回类型(除过子类中方法的返回值是父类中方法返回值的子类时)都相同的情况下, 对方法体进行修改或重写,这就是重写。但要注意子类函数的访问修饰权限不能少于父类的。
public class Father {
public static void main(String[] args) {
// TODO Auto-generated method stub
Son s = new Son();
s.sayHello();
}
public void sayHello() {
System.out.println("Hello");
}
}
class Son extends Father{
@Override
public void sayHello() {
// TODO Auto-generated method stub
System.out.println("hello by ");
}
}
重写 总结: 1.发生在父类与子类之间 2.方法名,参数列表,返回类型(除过子类中方法的返回类型是父类中返回类型的子类)必须相同 3.访问修饰符的限制一定要大于被重写方法的访问修饰符 (public>protected>default>private) 4.重写方法一定不能抛出新的检查异常或者比被重写方法申 明更加宽泛的检查型异常
重载(Overload)
在一个类中,同名的方法如果有不同的参数列表(参数类型不同、参数个数不同甚至是参数顺序不 同)则视为重载。同时,重载对返回类型没有要求,可以相同也可以不同,但不能通过返回类型是 否相同来判断重载。
public class Father {
public static void main(String[] args) {
// TODO Auto-generated method stub
Father s = new Father();
s.sayHello();
s.sayHello("wintershii");
}
public void sayHello() {
System.out.println("Hello");
}
public void sayHello(String name) {
System.out.println("Hello" + " " + name);
}
}
重载 总结: 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道面试题)
注:篇幅有限,资料已整理成文档,查看下方名片免费获取!