跳转至

Java基础

1. Java平台

谈谈你对Java平台的理解?Java是解释执行吗?

Java本身是一种面向对象的编程语言,有两大显著特性: - write once, run anywhere,从而非常容易跨平台 - Java通过垃圾收集器(GC)回收分配内存, 因此程序员不需要关心内存的分配与回收

”Java是解释执行“这一说法不太准确,Java源代码首先通过 javac 编译成字节码,然后在运行的时候 通过 JVM内嵌的解释器将字节码转换为最终的机器码。

但是常见的 JVM 比如 Hotspot JVM 都提供了 JIT 编译器,即动态编译器,JIT能够在程序运行的时候将热点代码编译成机器码,这种情况下部分热点代码就属于编译执行。

Java为什么可以跨平台?

Java程序的执行分为编译期和运行期两个阶段,程序执行的时候,Java源代码首先通过 javac 编译生成 '.class' 文件,'.class'文件中存储的水字节码。Java通过字节码和Java虚拟机,屏蔽了操作系统和硬件的细节,从而实现 write cone, run anywhere.

Java虚拟机启动时,可以指定不同的参数对运行模式进行选择。

  • 指定“-Xint”,就是告诉JVM只进行解释执行,不对代码进行编译,这种 模式抛弃了JIT可能带来的性能优势。毕竟解释器(interpreter)是逐条 读入,逐条解释运行的
  • 相对应的,还有一个“-Xcomp”参数,这是 告诉JVM关闭解释器,不要进行解释执行,或者叫作最大优化级别。(这种方式未必最高效。因为“-Xcomp”会 导致JVM启动变慢非常多,同时有些JIT编译器优化方式,比如分支预 测,如果不进行profiling,往往并不能进行有效优化。)

还有一种新的编译方式,即 所谓的AOT(Ahead-of-Time Compilation),直接将字节码编译成机器 代码,这样就避免了JIT预热等各方面的开销。

JVM作为一个强大的平台,不仅仅只有Java语言可以运行在JVM 上,本质上合规的字节码都可以运行,Java语言自身也为此提供了便 利。

2. Exception 和 Error

对比 Exception 和 Error,分析它们的异同。

相同点:Exception和Error都是继承了Throwable类,在Java中只有Throwable类型 的实例才可以被抛出(throw)或者捕获(catch),它是异常处理机制 的基本组成类型。

不同点:Exception和Error体现了Java平台设计者对不同异常情况的分类。

  • Exception是程序正常运行中,可以预料的意外情况,可能并且应该被捕 获,进行相应处理。

  • Error是指在正常情况下,不大可能出现的情况,绝大部分的Error都会 导致程序(比如JVM自身)处于非正常的、不可恢复状态。既然是非正 常情况,所以不便于也不需要捕获,常见的比如OutOfMemoryError之 类,都是Error的子类。

  • Exception又分为可检查(checked)异常和不检查(unchecked)异常,

  • 可检查异常在源代码里必须显式地进行捕获处理,这是编译期检查的一 部分。(Exception又分为可检查(checked)异常和不检查(unchecked)异常, 可检查异常在源代码里必须显式地进行捕获处理,这是编译期检查的一 部分。(前面介绍的Error,是Throwable不是Exception)
  • 不检查异常就是所谓的运行时异常,类似 NullPointerException、 ArrayIndexOutOfBoundsException之类,通常是可以编码避免的逻辑错 误,具体根据需要来判断是否需要捕获,并不会在编译期强制要求。

NoClassDefFoundError和 ClassNotFoundException有什么区别?

Java中异常处理机制 开销比较大的地方:

  1. try-catch代码段会产生额外的性能开销,即它往往 会影响JVM对代码进行优化,所以建议仅捕获有必要的代码段,尽 量不要一个大的try包住整段的代码;与此同时,利用异常控制代码 流程,也不是一个好主意,远比我们通常意义上的条件语句 (if/else、switch)要低效。
  2. Java每实例化一个Exception,都会对当时的栈进行快照,这是一个 相对比较重的操作。如果发生的非常频繁,这个开销可就不能被忽 略了。

3. final finally finalize 有什么不同?

  1. final可以用来修饰类、方法、变量,分别有不同的意义,final修饰的 class代表不可以继承扩展,final的变量是不可以修改的,而final的方法 也是不可以重写的(override)。
  2. finally则是Java保证重点代码一定要被执行的一种机制。我们可以使用 try-finally或者try-catch-finally来进行类似关闭JDBC连接、保证unlock锁 等动作。
  3. finalize是基础类java.lang.Object的一个方法,它的设计目的是保证对象在被垃圾收集前完成特定资源的回收。finalize机制现在已经不推荐使用,并且在JDK 9开始被标记为deprecated。

扩展 final:

  • 使用final修饰参数或者变量,也可以清楚地避免意外赋值导致的编 程错误,甚至,有人明确推荐将所有方法参数、本地变量、成员变 量声明成final。
  • final变量产生了某种程度的不可变(immutable)的效果,所以,可以用于保护只读数据,尤其是在并发编程中,因为明确地不能再赋 值final变量,有利于减少额外的同步开销,也可以省去一些防御性拷贝的必要。

4. 强引用、软引用、弱引用和幻想引用有什么区别?

不同的引用类型,主要体现的是对象不同的可达性(reachable)状态和对垃圾收集的影响。

  • 强引用("Strong" Reference),就是我们最常见的普通对象引用, 只要还有强引用指向一个对象,就能表明对象还“活着”,垃圾收集器不 会碰这种对象。对于一个普通的对象,如果没有其他的引用关系,只要 超过了引用的作用域或者显式地将相应(强)引用赋值为null,就是可 以被垃圾收集的了,当然具体回收时机还是要看垃圾收集策略。
  • 软引用(SoftReference),是一种相对强引用弱化一些的引用,可以让 对象豁免一些垃圾收集,只有当JVM认为内存不足时,才会去试图回收软引用指向的对象。JVM会确保在抛出OutOfMemoryError之前,清理软引用指向的对象。软引用通常用来实现内存敏感的缓存,如果还有空闲 内存,就可以暂时保留缓存,当内存不足时清理掉,这样就保证了使用 缓存的同时,不会耗尽内存。

  • 弱引用(WeakReference)并不能使对象豁免垃圾收集,仅仅是提供一 种访问在弱引用状态下对象的途径。这就可以用来构建一种没有特定约 束的关系,比如,维护一种非强制性的映射关系,如果试图获取时 对象还在,就使用它,否则重现实例化。它同样是很多缓存实现的选择。

  • 对于幻象引用,有时候也翻译成虚引用,你不能通过它访问对象。幻象引用仅仅是提供了一种确保对象被finalize以后,做某些事情的机制,比如,通常用来做所谓的Post-Mortem清理机制,我在专栏上一讲中介绍 的Java平台自身Cleaner机制等,也有人利用幻象引用监控对象的创建和销毁。