这次我花了一周的工夫,筹备了 31 道 Java 外围面试题,心愿可能帮忙到读者。01、请说出 Java 14 版本中更新的重要性能
Java 14 公布于 2020 年 3 月 17 日,更新的重要性能有:
- switch 表达式
- instanceof 加强表达式,预览性能
- 文本块,第二次预览
- Records,预览性能
02、请说出 Java 13 版本中更新的重要性能
Java 13 公布于 2019 年 9 月 17 日,更新的重要性能有:
- 文本块,预览性能
- switch 表达式,预览性能
- Java Socket 从新实现
- FileSystems.newFileSystem() 办法
- 反对 Unicode 12.1
- 可伸缩、低提早的垃圾收集器改良,用于返回未应用的内存
03、请说出 Java 12 版本中更新的重要性能
Java 12 公布于 2019 年 3 月 19 日,更新的重要性能有:
- JVM 更新
- File.mismatch() 办法
- 紧凑型数字格局
- String 类新增了一些办法,比如说 indent()
04、请说出 Java 11 版本中更新的重要性能
Java 11 是继 Java 8 之后的第二个商用版本,如果你下载的是 Oracle JDK,则须要进行付费;如果想持续应用收费版本,须要下载 Open JDK。
Oracle JDK 中会有一些 Open JDK 没有的、商用闭源的性能。
Java 11 更新的重要性能有:
- 能够间接应用 java 命令运行 Java 程序,源代码将会隐式编译和运行。
- String 类新增了一些办法,比如说 isBlank()、lines()、strip() 等等。
- Files 类新增了两个读写办法,readString() 和 writeString()。
- 能够在 Lambda 表达式中应用 var 作为变量类型。
05、请说出 Java 10 版本中更新的重要性能
Java 10 更新的重要性能有:
- 局部变量类型推断,举个例子,var list = new ArrayList();,能够应用 var 来作为变量类型,Java 编译器晓得 list 的类型为字符串的 ArrayList。
- 加强 java.util.Locale。
- 提供了一组默认的根证书颁发机构(CA)。
06、请说出 Java 9 版本中更新的重要性能
Java 9 更新的重要性能有:
- 模块零碎
- 不可变的 List、Set、Map 的工厂办法
- 接口中能够有公有办法
- 垃圾收集器改良
07、请说出 Java 8 版本中更新的重要性能
Java 8 公布于 2014 年 3 月份,能够说是 Java 6 之后最重要的版本更新,深受开发者的青睐。
- 函数式编程和 Lambda 表达式
- Stream 流
- Java Date Time API
- 接口中能够应用默认办法和静态方法
我强烈建议点开下面的链接浏览以下,以正确理解这些概念。
08、请说出 Java 面向对象编程中的一些重要概念
- 形象
- 封装
- 多态
- 继承
09、Java 宣称的平台独立性指的是什么?
常见的操作系统有 Windows、Linux、OS-X,那么平台独立性意味着咱们能够在任何操作系统中运行雷同源代码的 Java 程序,比如说咱们能够在 Windows 上编写 Java 程序,而后在 Linux 上运行它。
10、什么是 JVM?
JVM(Java Virtual Machine)俗称 Java 虚拟机。之所以称为虚拟机,是因为它实际上并不存在。它提供了一种运行环境,可供 Java 字节码在下面运行。
JVM 提供了以下操作:
- 加载字节码
- 验证字节码
- 执行字节码
- 提供运行时环境
JVM 定义了以下内容:
- 存储区
- 类文件格式
- 寄存器组
- 垃圾回收堆
- 致命错误报告等
咱们来尝试了解一下 JVM 的内部结构,它蕴含了类加载器(Class Loader)、运行时数据区(Runtime Data Areas)和执行引擎(Excution Engine)。
1)类加载器
类加载器是 JVM 的一个子系统,用于加载类文件。每当咱们运行一个 Java 程序,它都会由类加载器首先加载。Java 中有三个内置的类加载器:
- 启动类加载器(Bootstrap Class-Loader),加载 jrelib 包上面的 jar 文件,比如说常见的 rt.jar(蕴含了 Java 规范库下的所有类文件,比如说 java.lang 包下的类,java.net 包下的类,java.util 包下的类,java.io 包下的类,java.sql 包下的类)。
- 扩大类加载器(Extension or Ext Class-Loader),加载 jrelibext 包上面的 jar 文件。
- 利用类加载器(Application or App Clas-Loader),依据程序的类门路(classpath)来加载 Java 类。
一般来说,Java 程序员并不需要间接同类加载器进行交互。JVM 默认的行为就曾经足够满足大多数状况的需要了。不过,如果遇到了须要和类加载器进行交互的状况,而对类加载器的机制又不是很理解的话,就不得不花大量的工夫去调试ClassNotFoundException 和 NoClassDefFoundError 等异样。
对于任意一个类,都须要由它的类加载器和这个类自身一起确定其在 JVM 中的唯一性。也就是说,如果两个类的加载器不同,即便两个类来源于同一个字节码文件,那这两个类就必然不相等(比方两个类的 Class 对象不 equals)。
是不是有点晕,来来来,通过一段简略的代码理解下。
public class Test { public static void main(String[] args) { ClassLoader loader = Test.class.getClassLoader(); while (loader != null) { System.out.println(loader.toString()); loader = loader.getParent(); } }}
每个 Java 类都保护着一个指向定义它的类加载器的援用,通过 类名.class.getClassLoader() 能够获取到此援用;而后通过 loader.getParent() 能够获取类加载器的下层类加载器。
下面这段代码的输入后果如下:
sun.misc.Launcher$AppClassLoader@18b4aac2sun.misc.Launcher$ExtClassLoader@4617c264
第一行输入为 Test 的类加载器,即利用类加载器,它是 sun.misc.Launcher$AppClassLoader 类的实例;第二行输入为扩大类加载器,是 sun.misc.Launcher$ExtClassLoader 类的实例。那启动类加载器呢?
按理说,扩大类加载器的下层类加载器是启动类加载器,但在我这个版本的 JDK 中, 扩大类加载器的 getParent() 返回 null。所以没有输入。
2)运行时数据区
运行时数据区又蕴含以下内容。
- PC寄存器(PC Register),也叫程序计数器(Program Counter Register),是一块较小的内存空间,它的作用能够看做是以后线程所执行的字节码的信号指示器。
- JVM 栈(Java Virtual Machine Stack),与 PC 寄存器一样,JVM 栈也是线程公有的。每一个 JVM 线程都有本人的 JVM 栈,这个栈与线程同时创立,它的生命周期与线程雷同。
- 本地办法栈(Native Method Stack),JVM 可能会应用到传统的栈来反对 Native 办法(应用 Java 语言以外的其它语言[C语言]编写的办法)的执行,这个栈就是本地办法栈。
- 堆(Heap),在 JVM 中,堆是可供各条线程共享的运行时内存区域,也是供所有类实例和数据对象分配内存的区域。
- 办法区(Method area),在 JVM 中,被加载类型的信息都保留在办法区中。包含类型信息(Type Information)和办法列表(Method Tables)。办法区是所有线程共享的,所以拜访办法区信息的办法必须是线程平安的。
- 运行时常量池(Runtime Constant Pool),运行时常量池是每一个类或接口的常量池在运行时的表现形式,它包含了编译器可知的数值字面量,以及运行期解析后能力取得的办法或字段的援用。简而言之,当一个办法或者变量被援用时,JVM 通过运行时常量区来查找办法或者变量在内存里的理论地址。
3)执行引擎
执行引擎蕴含了:
- 解释器:读取字节码流,而后执行指令。因为它一条一条地解释和执行指令,所以它能够很快地解释字节码,然而执行起来会比较慢。
- 即时(Just-In-Time,JIT)编译器:即时编译器用来补救解释器的毛病,进步性能。执行引擎首先依照解释执行的形式来执行,而后在适合的时候,即时编译器把整段字节码编译成本地代码。而后,执行引擎就没有必要再去解释执行办法了,它能够间接通过本地代码去执行。执行本地代码比一条一条进行解释执行的速度快很多。编译后的代码能够执行的很快,因为本地代码是保留在缓存里的。
11、JDK 和 JVM 有什么区别?
JDK 是 Java Development Kit 的首字母缩写,是提供给 Java 开发人员的软件环境,蕴含 JRE 和一组开发工具。可分为以下版本:
- 标准版(大多数开发人员用的就是这个)
- 企业版
- 微型版
JDK 蕴含了一个公有的 JVM 和一些其余资源,比如说编译器(javac 命令)、解释器(java 命令)等,帮忙 Java 程序员实现开发工作。
12、JVM 和 JRE 有什么区别?
Java Runtime Environment(JRE)是 JVM 的实现。JRE 由 JVM 和 Java 二进制文件以及其余类组成,能够执行任何程序。JRE 不蕴含 Java 编译器,调试器等任何开发工具。
13、哪个类是所有类的超类?
java.lang.Object 是所有 Java 类的超类,咱们不须要继承它,因为是隐式继承的。
14、为什么 Java 不反对多重继承?
如果有两个类独特继承(extends)一个有特定办法的父类,那么该办法会被两个子类重写。而后,如果你决定同时继承这两个子类,那么在你调用该重写办法时,编译器不能辨认你要调用哪个子类的办法。这也正是驰名的菱形问题,见下图。
ClassC 同时继承了 ClassA 和 ClassB,ClassC 的对象在调用 ClassA 和 ClassB 中重载的办法时,就不晓得该调用 ClassA 的办法,还是 ClassB 的办法。
15、为什么 Java 不是纯正的面向对象编程语言?
之所以不能说 Java 是纯正的面向对象编程语言,是因为 Java 反对根本数据类型,比如说 int、short、long、double 等,只管它们有本人的包装器类型,但它们确实不能算是对象。
16、path 和 classpath 之间有什么区别?
path 是操作系统用来查找可执行文件的环境变量,我的电脑上就定义了下图这些 path 变量,比方 Java 和 Maven 的。
classpath 是针对 Java 而言的,用于指定 Java 虚拟机载入的字节码文件门路。
17、Java 中 `main()` 办法的重要性是什么?
每个程序都须要一个入口,对于 Java 程序来说,入口就是 main 办法。
public static void main(String[] args) {}
- public 关键字是另外一个拜访修饰符,除了能够申明办法和变量(所有类可见),还能够申明类。main() 办法必须申明为 public。
- static 关键字示意该变量或办法是动态变量或静态方法,能够间接通过类拜访,不须要实例化对象来拜访。
- void 关键字用于指定办法没有返回值。
另外,main 关键字为办法的名字,Java 虚拟机在执行程序时会寻找这个标识符;args 为 main() 办法的参数名,它的类型为一个 String 数组,也就是说,在应用 java 命令执行程序的时候,能够给 main() 办法传递字符串数组作为参数。
java HelloWorld xuanwo008
javac 命令用来编译程序,java 命令用来执行程序,HelloWorld 为这段程序的类名,缄默王二和缄默王三为字符串数组,两头通过空格隔开,而后就能够在 main() 办法中通过 args[0] 和 args[1] 获取传递的参数值了。
public class HelloWorld { public static void main(String[] args) { if ("xuanwo008".equals(args[0])) { } if ("xuanwo008".equals(args[1])) { } }}
main() 办法的写法并不是惟一的,还有其余几种变体,只管它们可能并不常见,能够简略来理解一下。
第二种,把方括号 [] 往 args 凑近而不是 String 凑近:
public static void main(String []args) { }
第三种,把方括号 [] 放在 args 的右侧:
public static void main(String args[]) { }
第四种,还能够把数组模式换成可变参数的模式:
public static void main(String...args) { }
第五种,在 main() 办法上增加另外一个修饰符 strictfp,用于强调在解决浮点数时的兼容性:
public strictfp static void main(String[] args) { }
也能够在 main() 办法上增加 final 关键字或者 synchronized 关键字。
第六种,还能够为 args 参数增加 final 关键字:
public static void main(final String[] args) { }
第七种,最简单的一种,所有能够增加的关键字通通增加上:
final static synchronized strictfp void main(final String[] args) { }
当然了,并不需要为了装逼特意把 main() 办法写成下面提到的这些模式,应用 IDE 提供的默认模式就能够了。
18、Java 的重写(Override)和重载(Overload)有什么区别?
先来看一段重写的代码吧。
class LaoWang{ public void write() { System.out.println("老王写了一本《基督山伯爵》"); }}public class XiaoWang extends LaoWang { @Override public void write() { System.out.println("小王写了一本《茶花女》"); }}
重写的两个办法名雷同,办法参数的个数也雷同;不过一个办法在父类中,另外一个在子类中。就如同父类 LaoWang 有一个 write() 办法(无参),办法体是写一本《基督山伯爵》;子类 XiaoWang 重写了父类的 write() 办法(无参),但办法体是写一本《茶花女》。
来写一段测试代码。
public class OverridingTest { public static void main(String[] args) { LaoWang wang = new XiaoWang(); wang.write(); }}
大家猜后果是什么?
小王写了一本《茶花女》
在下面的代码中,们申明了一个类型为 LaoWang 的变量 wang。在编译期间,编译器会查看 LaoWang 类是否蕴含了 write() 办法,发现 LaoWang 类有,于是编译通过。在运行期间,new 了一个 XiaoWang 对象,并将其赋值给 wang,此时 Java 虚拟机晓得 wang 援用的是 XiaoWang 对象,所以调用的是子类 XiaoWang 中的 write() 办法而不是父类 LaoWang 中的 write() 办法,因而输入后果为“小王写了一本《茶花女》”。
再来看一段重载的代码吧。
class LaoWang{ public void read() { System.out.println("老王读了一本《Web全栈开发进阶之路》"); } public void read(String bookname) { System.out.println("老王读了一本《" + bookname + "》"); }}
重载的两个办法名雷同,但办法参数的个数不同,另外也不波及到继承,两个办法在同一个类中。就如同类 LaoWang 有两个办法,名字都是 read(),但一个有参数(书名),另外一个没有(只能读写死的一本书)。
来写一段测试代码。
public class OverloadingTest { public static void main(String[] args) { LaoWang wang = new LaoWang(); wang.read(); wang.read("金瓶梅"); }}
这后果就不必猜了。变量 wang 的类型为 LaoWang,wang.read() 调用的是无参的 read() 办法,因而先输入“老王读了一本《Web全栈开发进阶之路》”;wang.read("金瓶") 调用的是有参的 read(bookname) 办法,因而后输入“老王读了一本《金瓶》”。在编译期间,编译器就晓得这两个 read() 办法时不同的,因为它们的办法签名(=办法名称+办法参数)不同。
简略的来总结一下:
1)编译器无奈决定调用哪个重写的办法,因为只从变量的类型上是无奈做出判断的,要在运行时能力决定;但编译器能够明确地晓得该调用哪个重载的办法,因为援用类型是确定的,参数个数决定了该调用哪个办法。
2)多态针对的是重写,而不是重载。
- 如果在一个类中有多个雷同名字的办法,但参数不同,则称为办法重载。
- 父类中有一个办法,子类中有另外一个和它有雷同签名(办法名雷同,参数雷同、修饰符雷同)的办法时,则称为办法重写。子类在重写父类办法的时候能够加一个 @Override 注解。
19、`main()` 办法能够重载吗?
能够,一个类中能够有多个名称为“main”的办法:
public class MainTest { public static void main(String[] args) { System.out.println("main(String[] args)"); } public static void main(String[] args,String arg) { System.out.println("(String[] args,String arg"); }}
但该类在运行的时候,只会找到一个入口,即 public static void main(String[] args)。
20、一个 Java 源文件中有多个 public 类吗?
一个 Java 源文件中不能有多个 public 类。
21、什么是 Java 的 package(包)?
在 Java 中,咱们应用 package(包)对相干的类、接口和子包进行分组。这样做的益处有:
- 使相干类型更容易查找
- 防止命名抵触,比如说 com.itwanger.Hello 和 com.itwangsan.Hello 不同
- 通过包和拜访权限控制符来限定类的可见性
能够应用 package 关键字来定义一个包名,须要留神的是,这行代码必须处于一个类中的第一行。强烈建议在包中申明类,不要缺省,否则就失去了包构造的带来的益处。
包的命名应该恪守以下规定:
- 应该全副是小写字母
- 能够蕴含多个单词,单词之间应用“.”连贯,比如说 java.lang
- 名称由公司名或者组织名确定,采纳倒序的形式,比如说,我集体博客的域名是 www.itwanger.com,所以我创立的包名是就是 com.itwanger.xxxx。
每个包或者子包都在磁盘上有本人的目录构造,如果 Java 文件时在 com.itwanger.xxxx 包下,那么该文件所在的目录构造就应该是 com->itwanger->xxxx。
默认状况下,java.lang 包是默认导入的,咱们不须要显式地导入该包下的任何类。
package com.cmower.bb;public class PackageTest { public static void main(String[] args) { Boolean.toString(true); }}
Boolean 类属于 java.lang 包,当应用它的时候并不需要显式导入。
22、什么是拜访权限修饰符?
拜访权限修饰符对于 Java 来说,十分重要,目前共有四种:public、private、protected 和 default(缺省)。
一个类只能应用 public 或者 default 润饰,public 润饰的类你之前曾经见到过了,当初我来定义一个缺省权限修饰符的类给你观赏一下。
class Dog {}
哈哈,其实也没啥能够观赏的。缺省意味着这个类能够被同一个包下的其余类进行拜访;而 public 意味着这个类能够被所有包下的类进行拜访。
如果硬要通过 private 和 protected 来润饰类的话,编译器会怄气的,它不批准。
private 能够用来润饰类的构造方法、字段和办法,只能被以后类进行拜访。protected 也能够用来润饰类的构造方法、字段和办法,但它的权限范畴更宽一些,能够被同一个包中的类进行拜访,或者以后类的子类。
能够通过上面这张图来比照一下四个权限修饰符之间的差异:
- 同一个类中,不论是哪种权限修饰符,都能够拜访;
- 同一个包下,private 润饰的无法访问;
- 子类能够拜访 public 和 protected 润饰的;
- public 修饰符面向世界,哈哈,能够被所有的中央拜访到。
23、什么是 final 关键字?
final 关键字润饰类的时候,示意该类无奈被继承。比方,String 类就是 final 的,无奈被继承。
final 关键字润饰办法的时候,示意子类无奈笼罩它。
final 关键字润饰变量的时候,示意该变量只能被赋值一次,只管变量的状态能够更改。
对于 final 更具体的内容,能够参照我之前写了另外一篇文章:
我去,你居然还不会用 final 关键字
24、什么是 static 关键字?
static 关键字能够用来润饰类变量,使其具备全局性,即所有对象将共享同一个变量。
static 关键字能够用来润饰办法,该办法称为静态方法,只能够拜访类的动态变量,并且只能调用类的静态方法。
对于 static 更具体的内容,能够参照我之前写了另外一篇文章:
面试官:兄弟,说说Java的static关键字吧
25、finally 和 finalize 有什么区别?
finally 通常与 try-catch 块一起应用,即便 try-catch 块引发了异样,finally 块中的代码也会被执行,用于开释 try 块中创立的资源。
finalize() 是 Object 类的一个非凡办法,当对象正在被垃圾回收时,垃圾收集器将会调用该办法。能够重写该办法用于开释系统资源。
26、能够将一个类申明为 static 的吗?
不能将一个外部类申明为 static 的,但能够将一个外部类申明为 static 的——称为动态外部类。
27、什么是动态导入?
如果必须在一个类中应用其余类的动态变量或者静态方法,通常咱们须要先导入该类,而后应用“类名.变量办法”的模式调用。
import java.lang.Math;double test = Math.PI * 5;
也能够通过动态导入的形式,就不须要再应用类名了。
import static java.lang.Math.PI;double test = PI * 5;
不过,动态导入容易引发凌乱(变量名或者办法名容易抵触),因而最好防止应用动态导入。
28、什么是 try-with-resources?
try-with-resources 是 Java 7 时引入的一个主动资源管理语句,在此之前,咱们必须通过 try-catch-finally 的形式手动敞开资源,当咱们遗记敞开资源的时候,就容易导致内存透露。
对于 try-with-resources 更具体的内容,能够参照我之前写了另外一篇文章:
我去,你居然还在用 try–catch-finally
29、什么是 multi-catch?
Java 7 改良的另外一个中央就是 multi-catch,能够在单个 catch 中捕捉多个异样,当一个 try 块抛出多个相似的异样时,这种写法更短,更清晰。
catch(IOException | SQLException ex){ logger.error(ex); throw new MyException(ex.getMessage());}
当有多个异样的时候,能够应用管道示意符“|”隔开。
30、什么是 static 块?
static 块是由 Java ClassLoader 将类加载到内存中时执行的代码块。通常用于初始化类的动态变量或者创立动态资源。
31、什么是接口?
接口是 Java 编程语言中的一个外围概念,不仅在 JDK 源码中应用很多,还在 Java 设计模式、框架和工具中应用很多。接口提供了一种在 Java 中实现形象的办法,用于定义子类的行为约定。
对于接口更具体的内容,能够参照我之前写了另外一篇文章:
可能是把 Java 接口讲得最艰深的一篇文章
鸣谢
说句实在话,这 31 道 Java 外围面试题在面试的过程中还是很常见的,值得好好温习一遍。
因为内容太多,答案我只写了一部分,剩下的已整顿成文档模式上传网盘了,增加VX:xuanwo008即可支付