31道Java核心面试题,一次性打包送给你

  • 文本块,预览功能

  • 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<String>();,可以使用 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 之后最重要的版本更新,深受开发者的喜爱。

我强烈建议点开上面的链接阅读以下,以正确理解这些概念。

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),加载 jre/lib 包下面的 jar 文件,比如说常见的 rt.jar(包含了 Java 标准库下的所有类文件,比如说 java.lang 包下的类,java.net 包下的类,java.util 包下的类,java.io 包下的类,java.sql 包下的类)。

  • 扩展类加载器(Extension or Ext Class-Loader),加载 jre/lib/ext 包下面的 jar 文件。

  • 应用类加载器(Application or App Clas-Loader),根据程序的类路径(classpath)来加载 Java 类。

一般来说,Java 程序员并不需要直接同类加载器进行交互。JVM 默认的行为就已经足够满足大多数情况的需求了。不过,如果遇到了需要和类加载器进行交互的情况,而对类加载器的机制又不是很了解的话,就不得不花大量的时间去调试

ClassNotFoundExceptionNoClassDefFoundError 等异常。

对于任意一个类,都需要由它的类加载器和这个类本身一同确定其在 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@18b4aac2

sun.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 沉默王二 沉默王三

javac 命令用来编译程序,java 命令用来执行程序,HelloWorld 为这段程序的类名,沉默王二和沉默王三为字符串数组,中间通过空格隔开,然后就可以在 main() 方法中通过 args[0]args[1] 获取传递的参数值了。

public class HelloWorld {

public static void main(String[] args) {

if (“沉默王二”.equals(args[0])) {

}

if (“沉默王三”.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(),但一个有参数(书名),另外一个没有(只能读写死的一本书)。

来写一段测试代码。

  • 16
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值