即将迎来春招的金三银四阶段,察觉到网络上诸多Java面试题不存在答案,于是耗费久远时间收集整理汇集成了这套Java面试题大全~。
此套面试文档涵盖诸多科目,它有Java 基础专项,这里面讲述了JVM特点,还涉及多线程与并发技术,又包含spring框架内容,也有mybatis相关要点,还有springboot的讲解,以及MySQL数据库知识,springcloud的介绍,Dubbo的阐述,Nginx的概述,MQ的说明,数据结构与算法部分,Linux系统相关,Zookeeper的篇章,Redis篇的内容,分布式方面的知识,网络篇相关要点,设计模式的讲解,maven篇的要点,ElasticSearch篇的介绍,tomcat篇的讲解,Git篇相关内容,软实力篇的要点,真可谓应有尽有,能够一网打尽!
一、基础篇
1、 Java语言有哪些特点?
2、面向对象和面向过程的区别
面向过程:是对解决问题的步骤予以剖析,接着借助函数将这些步骤逐个予以实现,随后在运用之际逐一进行调用便可。其具备较高性能,故而单片机、嵌入式开发等通常采用面向过程开发 。
存在这样一种情况:将构成问题的事务予以分解,使其形成各个对象,并且建立这些对象的目的并不是去完成一个个单独的步骤,而是为了描述某个事物在解决整个问题的进程里所出现的行为。存在面向对象这种概念,它具备封装、继承以及多态这些特性,从而容易进行维护、容易实现复用、容易实现扩展,能够设计出低耦合的系统。然而从性能方面来讲,它相较于面向过程是要低一些的。
3 、八种基本数据类型的大小,以及他们的封装类
注:
int属于基本数据类型,Integer是int的封装类,它是引用类型。int的默认值为0,然而Integer的默认值是null,所以Integer能够区分出0与null的情形。一旦java碰到null,就会知晓这个引用尚未指向某个对象,在任何引用被使用之前,必须为其指定一个对象,不然会报错。
声明基本数据类型时,系统会自动为其分配空间,引用类型声明时,仅分配引用空间,必须经实例化开辟数据空间后才可赋值,数组对象也是引用对象,将一个数组赋给另一个数组时,仅复制一个引用,所以通过某数组所做修改,在另一数组中也可见。
仅定义了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类型,要是不能够进行转换,那么就会直接报错出来,要是没办法确定类型,那么会通过编译,具体的情况要看运行的时候来确定。
int i = 0;
输出语句System.out.println(i instanceof Integer),编译无法通过,因为i必须是引用类型,而不能是基本类型 。
Java 程序里,使用“System.out.println”,对变量“i”,进行“instanceof Object”这个操作,然而,此语句出现编译不通过的情况 。
生成一个整型对象,其值为1,将其赋值给一个名为integer的整型变量 。
System.out.println,integer属于Integer这个类型吗,答案是true 。
根据JavaSE规范里对于instanceof运算符的规定,倘若obj是null,所得结果将会是false , 。
将 `System.out` 用于输出,输出 `null` 与 `Object` 存在实例关系的判断结果 ,。
6、Java自动装箱与拆箱
通过自动方式,把基本数据类型转化成包装器类型(int转变为Integer)的这个操作就被称作装箱,调用其方法时,需使用Integer的valueOf(int) 方法 。
自动把包装器类型转变为基本数据类型(Integer转变为int)这一行为称作拆箱,调用方式是Integer的intValue方法 。
在数值为10的Integer对象被生成之前,于Java SE5之前,是需要按照这样的方式来进行的:
Integer i = new Integer(10);
从 Java SE5 起便提供了自动装箱的特性,若要生成一个数值是 10 的 Integer 对象,仅仅只需 。
这样就可以了:
Integer i = 10;
面试题 1 : 以下代码会输出什么?
public class Main {
全局静态无效空参主体字符串化数组参数的包裹体里边该主体当中的内容为:
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 方法的具体实施情况:
公共的静态的整数类型返回值的叫做valueOf的方法,其参数是整型的i 。
if(i >= -128 && i <= IntegerCache.high)
return IntegerCache.cache[i + 128];
else
return new Integer(i);
}
其中 IntegerCache 类的实现为:
私有的静态内部类,叫做整数缓存类,它是这样定义的,即 IntegerCache 。
static final int high;
static final Integer cache[];
static {
final int low = -128;
int h = 127;
如果(整数缓存高属性值不为空),那么 ,接下来 (这里由于原句结构限制,拆分后表述稍显奇怪。若有更复杂的句子结构,便能。
在此处使用Long.decode,以避免调用那些方法,这些方法,会导致怎样的结果 ,有待进一步探讨 。
需求整数自动装箱缓存去被初始化 ,// 这里有个斜杠 ,这是一个要求 ,整数自动装箱缓存要被初始化 。
声明整型变量i,i的值为,将由长整型解码整型缓存高位属性值后,再经转换所得的整数值,。
i = Math.max(i, 127);
最大数组大小是整数类型的最大值, 此最大值是整数类型所能表示的最大数值 。
求最小值。将i赋值给h,前提是i要小于等于整型最大最小值与减去后又减去low这个结果的差值,这里减去后又减去 low 是指对low进行了。
}
high = h;
缓存,等于新的整型数组,其长度为(高值减去低值)再加上一;。
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
}
private IntegerCache() {}
}
单从这2段代码而言,能够瞧出,于借助valueOf方法去创建Integer对象之时,要是数值处于相应范围之内,那么就会返回指向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)
从字面上瞧,重写所表达的意思乃是重新书写一回 。实际上它指的是在子类里头把父类原本就存在的方法再次书写一遍 。子类继承了父类原有的那些方法 ,然而有时候子类并不想以原封不动的状态去继承父类当中的某一个方法 ,所以在方法名 ,参数列表 , 在返回类型(除去子类里方法的返回值是父类中-method返回值的子类这种情况时)均相同的情形下 ,对方法体展开修改或者重写 ,这便是重写 。但需要留意的是子类函数的访问修饰权限不能够比父类的少 。
public class Father {
public static void main(String[] args) {
这儿要注意,这是自动生成的方法存根内容呢,但具体所包含的意思还需要结合整个程序的逻辑来进一步明确,这可不是简单。
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
输出到系统控制台,打印出双引号括起来的内容,即“hello by ”,句号。
}
}
总结:其一,是发生在父类跟子类之间的情况。其二,方法名、参数列表以及返回类型(除了子类中方法的返回类型为父类中返回类型的子类这种特殊情况)必须得相同。其三,访问修饰符的限制务必要大于被重写方法的访问修饰符(具体为public>protected>default>private)。其四,重写方法绝对不可以抛出新的检查异常或者比被重写方法声明更为宽泛的检查型异常。
重载(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");
}
修饰符是公共的,返回类型为空,方法名为说你好,参数是字符串类型的名字 {。
把“Hello”,与空格,和变量name,进行拼接,然后使用System.out.println输出 。
}
}
将其改写为:总结关于重载。首先,类中发生的重载Overload属于多态性的一种呈现样式。其次,倘若拥有多个同名方法,那么这些同名方法的参数列表需要有所差异,这种差异涵盖在参数类型、参数个数以及参数顺序方面。最后,需要明确的是,在进行重载操作之际,返回值类型既能够保持一致,也能够有所不同,绝对不能把返回值类型当作区分重载函数的标准依据。
因为篇幅存在限制,所以接下来仅能够给大家呈现小册中的部分内容了。这份面试笔记涵盖了,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以下的情况。
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道面试题)
受限篇幅的范围界限,仅向诸位呈现部分大纲目录的具体内容!存有相关需求的一小批朋友,能够通过转发并且加以关注之后,来私信小编发送【学习】这一特定信息,如此便能够免费获取。