面霸篇:高频 Java 基础问题(核心卷一)(三)

简介: 接上文。

说下 Exception 与 Error 区别?


码老湿,他们的相同点是什么呀?


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


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


image.png


异常使用规范:


  • 尽量不要捕获类似 Exception 这样的通用异常,而是应该捕获特定异常


  • 不要生吞(swallow)异常。这是异常处理中要特别注意的事情,因为很可能会导致非常难以诊断的诡异情况。


Exception


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


就好比开车去洗桑拿,前方道路施工,禁止通行。但是我们换条路就可以解决。


Exception 又分为可检查(checked)异常和不检查(unchecked)异常,可检查异常在源代码里必须显式地进行捕获处理,这是编译期检查的一部分。


不检查异常就是所谓的运行时异常,类似 NullPointerException、ArrayIndexOutOfBoundsException 之类,通常是可以编码避免的逻辑错误,具体根据需要来判断是否需要捕获,并不会在编译期强制要求。


Checked Exception 的假设是我们捕获了异常,然后恢复程序。但是,其实我们大多数情况下,根本就不可能恢复。


Checked Exception 的使用,已经大大偏离了最初的设计目的。Checked Exception 不兼容 functional 编程,如果你写过 Lambda/Stream 代码,相信深有体会。


Error


此类错误一般表示代码运行时 JVM 出现问题。通常有 Virtual MachineError(虚拟机运行错误)、NoClassDefFoundError(类定义错误)等。


比如 OutOfMemoryError:内存不足错误;StackOverflowError:栈溢出错误。此类错误发生时,JVM 将终止线程。


绝大多数导致程序不可恢复,这些错误是不受检异常,非代码性错误。因此,当此类错误发生时,应用程序不应该去处理此类错误。按照Java惯例,我们是不应该实现任何新的Error子类的!


比如开车去洗桑拿,老王出车祸了。无法洗了,只能去医院。


JVM 如何处理异常?


在一个方法中如果发生异常,这个方法会创建一个异常对象,并转交给 JVM,该异常对象包含异常名称,异常描述以及异常发生时应用程序的状态。


创建异常对象并转交给 JVM 的过程称为抛出异常。可能有一系列的方法调用,最终才进入抛出异常的方法,这一系列方法调用的有序列表叫做调用栈。


JVM 会顺着调用栈去查找看是否有可以处理异常的代码,如果有,则调用异常处理代码。


当 JVM 发现可以处理异常的代码时,会把发生的异常传递给它。如果 JVM 没有找到可以处理该异常的代码块,JVM 就会将该异常转交给默认的异常处理器(默认处理器为 JVM 的一部分),默认异常处理器打印出异常信息并终止应用程序。


NoClassDefFoundError 和 ClassNotFoundException 区别?


NoClassDefFoundError 是一个 Error 类型的异常,是由 JVM 引起的,不应该尝试捕获这个异常。


引起该异常的原因是 JVM 或 ClassLoader 尝试加载某类时在内存中找不到该类的定义,该动作发生在运行期间,即编译时该类存在,但是在运行时却找不到了,可能是变异后被删除了等原因导致;


ClassNotFoundException 是一个受查异常,需要显式地使用 try-catch 对其进行捕获和处理,或在方法签名中用 throws 关键字进行声明。


当使用 Class.forName, ClassLoader.loadClass 或 ClassLoader.findSystemClass 动态加载类到内存的时候,通过传入的类路径参数没有找到该类,就会抛出该异常;


另一种抛出该异常的可能原因是某个类已经由一个类加载器加载至内存中,另一个加载器又尝试去加载它。


Java 常见异常有哪些?


java.lang.IllegalAccessError:违法访问错误。当一个应用试图访问、修改某个类的域(Field)或者调用其方法,但是又违反域或方法的可见性声明,则抛出该异常。


java.lang.InstantiationError:实例化错误。当一个应用试图通过Java的new操作符构造一个抽象类或者接口时抛出该异常.


java.lang.OutOfMemoryError:内存不足错误。当可用内存不足以让Java虚拟机分配给一个对象时抛出该错误。


java.lang.StackOverflowError:堆栈溢出错误。当一个应用递归调用的层次太深而导致堆栈溢出或者陷入死循环时抛出该错误。


java.lang.ClassCastException:类造型异常。假设有类A和B(A不是B的父类或子类),O是A的实例,那么当强制将O构造为类B的实例时抛出该异常。该异常经常被称为强制类型转换异常。


java.lang.ClassNotFoundException:找不到类异常。当应用试图根据字符串形式的类名构造类,而在遍历CLASSPAH之后找不到对应名称的class文件时,抛出该异常。


java.lang.ArithmeticException:算术条件异常。譬如:整数除零等。


java.lang.ArrayIndexOutOfBoundsException:数组索引越界异常。当对数组的索引值为负数或大于等于数组大小时抛出。


final、finally、finalize 有什么区别?


除了名字相似,他们毫无关系!!!


  • final可以修饰类、变量、方法,修饰类表示该类不能被继承、修饰方法表示该方法不能被重写、修饰变量表示该变量是一个常量不能被重新赋值。


  • finally一般作用在try-catch代码块中,在处理异常的时候,通常我们将一定要执行的代码方法finally代码块中,表示不管是否出现异常,该代码块都会执行,一般用来存放一些关闭资源的代码。


  • finalize是一个方法,属于Object类的一个方法,而Object类是所有类的父类,Java 中允许使用 finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。


final 有什么用?


用于修饰类、属性和方法;


  • 被final修饰的类不可以被继承


  • 被final修饰的方法不可以被重写


  • 被final修饰的变量不可以被改变,被final修饰不可变的是变量的引用,而不是引用指向的内容,引用指向的内容是可以改变的。


try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗?


答:会执行,在 return 前执行。


注意:在 finally 中改变返回值的做法是不好的,因为如果存在 finally 代码块,try中的 return 语句不会立马返回调用者,而是记录下返回值待 finally 代码块执行完毕之后再向调用者返回其值,然后如果在 finally 中修改了返回值,就会返回修改后的值。


显然,在 finally 中返回或者修改返回值会对程序造成很大的困扰,C#中直接用编译错误的方式来阻止程序员干这种龌龊的事情,Java 中也可以通过提升编译器的语法检查级别来产生警告或错误。


public static int getInt() {
    int a = 10;
    try {
        System.out.println(a / 0);
        a = 20;
    } catch (ArithmeticException e) {
        a = 30;
        return a;
        /*
         * return a 在程序执行到这一步的时候,这里不是return a 而是 return 30;这个返回路径就形成了
         * 但是呢,它发现后面还有finally,所以继续执行finally的内容,a=40
         * 再次回到以前的路径,继续走return 30,形成返回路径之后,这里的a就不是a变量了,而是常量30
         */
    } finally {
        a = 40;
    }
  return a;
}


执行结果:30。


public static int getInt() {
    int a = 10;
    try {
        System.out.println(a / 0);
        a = 20;
    } catch (ArithmeticException e) {
        a = 30;
        return a;
    } finally {
        a = 40;
        //如果这样,就又重新形成了一条返回路径,由于只能通过1个return返回,所以这里直接返回40
        return a; 
    }
}


执行结果:40。


强引用、软引用、弱引用、虚引用


强引用、软引用、弱引用、幻象引用有什么区别?具体使用场景是什么?


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


强引用


通过new 创建的对象就是强引用,强引用指向一个对象,就表示这个对象还活着,垃圾回收不会去收集。


软引用


是一种相对强引用弱化一些的引用,只有当 JVM 认为内存不足时,才会去试图回收软引用指向的对象。


JVM 会确保在抛出 OutOfMemoryError 之前,清理软引用指向的对象


软引用通常用来实现内存敏感的缓存,如果还有空闲内存,就可以暂时保留缓存,当内存不足时清理掉,这样就保证了使用缓存的同时,不会耗尽内存。


弱引用


ThreadlocalMap中的 key 就是用了弱引用,因为ThreadlocalMap 被thread 对象持有,所以如果是强引用的话,只有当thread结束时才能被回收,而弱引用则可以在使用完后立即回收,不必等待thread结束。


虚引用


“虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。


虚引用主要用来跟踪对象被垃圾回收器回收的活动。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列 (ReferenceQueue)联合使用。


当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之 关联的引用队列中。


相关文章
|
4月前
|
存储 Java
最新Java基础系列课程--Day10-IO流文件处理(一)
最新Java基础系列课程--Day10-IO流文件处理
|
5月前
|
存储 缓存 Java
最新Java基础系列课程--Day10-IO流文件处理
最新Java基础系列课程--Day10-IO流文件处理
|
1月前
|
搜索推荐 Java
Java基础(快速排序算法)
Java基础(快速排序算法)
25 4
|
3月前
|
缓存 分布式计算 Java
Java基础深化和提高-------IO流
Java基础深化和提高-------IO流
110 0
|
4月前
|
缓存 Java Apache
最新Java基础系列课程--Day10-IO流文件处理(三)
最新Java基础系列课程--Day10-IO流文件处理
|
4月前
|
Java
最新Java基础系列课程--Day10-IO流文件处理(二)
最新Java基础系列课程--Day10-IO流文件处理
|
9月前
|
设计模式 缓存 分布式计算
|
7月前
|
存储 缓存 Java
Java基础知识第二讲:Java开发手册/JVM/集合框架/异常体系/Java反射/语法知识/Java IO
Java基础知识第二讲:Java开发手册/JVM/集合框架/异常体系/Java反射/语法知识/Java IO
170 0
Java基础知识第二讲:Java开发手册/JVM/集合框架/异常体系/Java反射/语法知识/Java IO
|
8月前
|
存储 缓存 Java
【JAVA基础】 IO详解
【JAVA基础】 IO详解
38 1
|
9月前
|
存储 缓存 Java