当前位置: 代码迷 >> 综合 >> java面试必备--JAVA基础篇(十一) 之 异常
  详细解决方案

java面试必备--JAVA基础篇(十一) 之 异常

热度:51   发布时间:2023-09-28 21:30:16.0

    相信很多同行小伙伴会因为许多原因想跳槽,不论是干得不开心还是想跳槽涨薪,在如此内卷的行业,我们都面临着“面试造火箭,上班拧螺丝”的局面,鉴于当前形势博主呕心沥血整理的干货满满的造火箭的技巧来了,本博主花费2个月时间,整理归纳java全生态知识体系常见面试题!总字数高达百万! 干货满满,每天更新,关注我,不迷路,用强大的归纳总结,全新全细致的讲解来留住各位猿友的关注,希望能够帮助各位猿友在应付面试笔试上!当然如有归纳总结错误之处请各位指出修正!如有侵权请联系博主QQ1062141499! 

目录

1 异常结构图

2 请写出你最常见的5个RuntimeException

3 Java中异常分为哪些种类

4 Error和Exception的区别

5 OutOfMemoryError的原因有哪些?怎么解决?

6 Unsupported major.minor version 52是什么造成的,如何解决?

7 异常的设计原则有哪些?

8 Java中异常处理机制

9 OOM你遇到过哪些情况,SOF你遇到过哪些情况

10 throw和throws的区别?

11 final、finally、finalize 有什么区别?

12 finally语句块一定执行吗?

13 return与finally的执行顺序对返回值的影响

14 try-catch-finally 中哪个部分可以省略? 

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


1 异常结构图

   异常非常多,Throwable 是异常的根类。

   Throwable 包含子类 错误-Error 和 异常-Exception 。

   Exception 又分为 一般异常和运行时异常 RuntimeException。

   运行时异常不需要代码显式捕获处理。

下图是常见异常类及其父子关系:

Throwable

|  ├ Error 

|  │ ├ IOError

|  │ ├ LinkageError

|  │ ├ ReflectionError

|  │ ├ ThreadDeath

|  │ └ VirtualMachineError

|  │

|  ├ Exception 

|  │ ├ CloneNotSupportedException 不支持克隆异常

|  │ ├ DataFormatException 数据格式异常

|  │ ├ InterruptedException 线程中断异常

|  │ ├ IOException IO异常

|  │     ├ FileNotFoundException 文件未找到异常

|  │     ├ SocketException Socket异常

|  │     ├ ConnectException 连接异常

|  │ ├ ReflectiveOperationException

|  │ ├ RuntimeException 运行时异常

|  │    ├ ArithmeticException 数学算术异常

|  │    ├ ClassCastException 数据类型转换异常

|  │    ├ ConcurrentModificationException 并发修改异常

|  │    ├ IllegalArgumentException 非法参数异常

|  │           ├NumberFormatException 字符串转换为数字异常

|  │    ├ IndexOutOfBoundsException 数组下标越界异常

|  │    ├ NoSuchElementException 方法不存在异常

|  │    ├ ClassNotFoundException 指定类不存在异常

|  │    ├ NullPointerException 空指针异常

|  │ └ SecurityException 安全异常

|  │ └  SQLException SQL异常

2 请写出你最常见的5个RuntimeException

下面列举几个常见的 RuntimeException。

     1)java.lang.NullPointerException  空指针异常;出现原因:调用了未经初始化的对象或者是不存在的对象。

    2)java.lang.ClassNotFoundException  指定的类找不到;出现原因:类的名称和路径加载错误;通常都是程序

试图通过字符串来加载某个类时可能引发异常。

   3)java.lang.NumberFormatException  字符串转换为数字异常;出现原因:字符型数据中包含非数字型字符。

   4)java.lang.IndexOutOfBoundsException  数组角标越界异常,常见于操作数组对象时发生。

   5)java.lang.IllegalArgumentException  方法传递参数错误。

   6)java.lang.ClassCastException  数据类型转换异常。

   7)java.lang.NoClassDefFoundException  未找到类定义错误。

   8SQLException SQL 异常,常见于操作数据库时的 SQL  语句错误。

   9)java.lang.InstantiationException  实例化异常。

  10)java.lang.NoSuchMethodException  方法不存在异常。

3 Java中异常分为哪些种类

    1按照异常需要处理的时机分为编译时异常(也叫强制性异常)也叫 CheckedException  和运行时异常(也叫非强制性异常)也叫 RuntimeException。只有 java 语言提供了 Checked 异常,Java 认为  Checked异常都是可以被处理的异常,所以 Java 程序必须显式处理  Checked 异常。如果程序没有处理 Checked  异常,该程序在编译时就会发生错误无法编译。这体现了 Java  的设计哲学:没有完善错误处理的代码根本没有机会被执行。对 Checked  异常处理方法有两种:

    1  当前方法知道如何处理该异常,则用 try...catch  块来处理该异常。

    2  当前方法不知道如何处理,则在定义该方法是声明抛出该异常。

运行时异常只有当代码在运行时才发行的异常,编译时不需要 try catch。Runtime 如除数是 0  和数组下标越

界等,其产生频繁,处理麻烦,若显示申明或者捕获将会对程序的可读性和运行效率影响很大。所以由系统自动检

测并将它们交给缺省的异常处理程序。当然如果你有处理要求也可以显示捕获它们。

  2).    调用下面的方法,得到的返回值是什么?

public int getNum() {try {int a = 1 / 0;return 1;} catch (Exception e) {return 2;} finally {return 3;}
}

     代码在走到第 3 行的时候遇到了一个 MathException,这时第四行的代码就不会执行了,代码直接跳转到  catch语句中,走到第 6 行的时候,异常机制有这么一个原则如果在 catch 中遇到了 return  或者异常等能使该函数终止的话那么有 finally 就必须先执行完 finally 代码块里面的代码然后再返回值。因此代码又跳到第 8 行,可惜第 8  行是一个return 语句,那么这个时候方法就结束了,因此第 6 行的返回结果就无法被真正返回。如果 finally  仅仅是处理了一个释放资源的操作,那么该道题最终返回的结果就是 2。因此上面返回值是 3。

4 Error和Exception的区别

     Throwable是Java语言中所有错误或异常的超类。Throwable包含两个子类:ErrorException。它们通常用于指示发生了异常情况。Throwable包含了其线程创建时线程执行堆栈的快照,它提供了printStackTrace()等接口用于获取堆栈跟踪数据等信息。

     Error类一般是指与虚拟机相关的问题,如系统崩溃,虚拟机错误,内存空间不足,方法调用栈溢出等。对于这类错误的导致的应用程序中断,仅靠程序本身无法恢复和和预防,遇到这样的错误,建议让程序终止。

      Exception类表示程序可以处理的异常,可以捕获且可能恢复。遇到这类异常,应该尽可能处理异常,使程序恢复运行,而不应该随意终止异常。

      Exception类又分为运行时异常(RuntimeException)和受检查的异常(CheckedException),运行时异常;ArithmaticException,IllegalArgumentException,编译能通过,但是一运行就终止了,程序不会处理运行时异常,出现这类异常,程序会终止。而受检查的异常,要么用try。。。catch捕获,要么用throws字句声明抛出,交给它的父类处理,否则编译不会通过。

    Java将可抛出(Throwable)的结构分为三种类型:

    被检查的异常(CheckedException)运行时异常(RuntimeException)错误(Error)

    运行时异常RuntimeException

    定义:RuntimeException及其子类都被称为运行时异常。特点:Java编译器不会检查它也就是说,当程序中可能出现这类异常时,倘若既"没有通过throws声明抛出它",也"没有用try-catch语句捕获它",还是会编译通过。

    例如,除数为零时产生的ArithmeticException异常,数组越界时产生的IndexOutOfBoundsException异常,fail-fail机制产生的ConcurrentModificationException异常等,都属于运行时异常。

5 OutOfMemoryError的原因有哪些?怎么解决?

    OutOfMemoryError 分为多种不同的错误:

  • Java heap space

    原因:JVM 中 heap(堆) 的最大值不满足需要

    解决

             调高 heap 的最大值,-Xmx 的值调大

             如果程序存在内存泄漏,增加 heap 空间也只是推迟该错误出现的时间而已,要检查程序是否存在内存泄漏

  • GC overhead limit exceeded

    原因:JVM 在 GC 时,对象过多,导致内存溢出

    解决:调整 GC 的策略,在一定比例下开始GC而不使用默认的策略,或将新代和老代设置合适的大小,可以微调存活率。如在老代 80% 时就是开始GC,并且将 -XX:SurvivorRatio(-XX:SurvivorRatio=8)和-XX:NewRatio(-XX:NewRatio=4)设置的更合理

  • Java perm space

     原因:JVM 中 perm 的最大值不满足需要,perm 一般是在 JVM 启动时加载类进来

     解决:调高 heap 的最大值,即 -XX:MaxPermSize 的值调大解决。如果 JVM 运行较长一段时间而不是刚启动后溢出的话,很有可能是由于运行时有类被动态加载,此时可以用 CMS 策略中的类卸载配置解决如:-XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled

  • unable to create new native thread

      原因:当 JVM 向系统请求创建一个新线程时,系统内存不足无法创建新的 native 线程

      解决:JVM 内存调的过大或者可利用率小于 20%,可以将 heap 及 perm 的最大值下调,并将线程栈内存 -Xss 调小,如:-Xss128k

  • Requested array size exceeds VM limit

      原因:应用程序试图分配一个大于堆大小的数组

       解决

                检查 heap 的 -Xmx 是不是设置的过小

                heap 的 -Xmx 已经足够大,检查应用程序是不是存在 bug 计算数组的大小时存在错误,导致数组的 length 很大,从而导致申请巨大的数组

  • request XXX bytes for XXX. Out of swap space

        原因:从 native 堆中分配内存失败,并且堆内存可能接近耗尽,操作系统配置了较小的交换区,其他进程消耗所有的内存

        解决:检查操作系统的 swap 是不是没有设置或者设置的过小;检查是否有其他进程在消耗大量的内存,导致 JVM 内存不够分配

6 Unsupported major.minor version 52是什么造成的,如何解决?

      造成的原因是工程中存在 jar 包编译时所用的 JDK 版本高于工程 build path 中 JDK 的版本。

这里的 version 52 对应 JDK 版本是 1.8,将项目的 build path 中 JDK 的版本调整为高于或等于 1.8 即可。

7 异常的设计原则有哪些?

  • 不要将异常处理用于正常的控制流
  • 对可以恢复的情况使用受检异常,对编程错误使用运行时异常
  • 避免不必要的使用受检异常
  • 优先使用标准的异常
  • 每个方法抛出的异常都要有文档
  • 保持异常的原子性
  • 不要在 catch 中忽略掉捕获到的异常

8 Java中异常处理机制

Java 异常的结构

    Throwable

--Error:是程序无法处理的错误,表示运行应用程序中较严重问题。大多数错误与代码编写者执行的操作无关,而表示代码运行时 JVM(Java 虚拟机)出现的问题

--Exception

    --RuntimeException:运行时异常,编译通过了,但运行时出现的异常

    --非 RuntimeException:编译时(受检)异常,编译器检测到某段代码可能会发生某些问题,需要程序员提前给代码做出错误的解决方案,否则编译不通过

异常产生的原理

  • java 对异常默认的处理方式,是将问题抛出给上一级
  • 抛出之前,java 会根据错误产生的异常类,创建出该类的对象,底层并通过 throw 关键字将异常抛出给上一级,不断向上抛出,直到抛给了JVM 虚拟机,虚拟机拿到异常之后,就会将错误的原因和所在的位置,打印在控制台

异常的处理方式

  • try catch 处理:自己将问题处理掉,不会影响到后续代码的继续执行
  • throw 抛出:问题自己无法处理,可以通过 throw 关键字,将异常对象抛出给调用者。如果抛出的对象是 RuntimeException 或 Error,则无需在方法上 throws 声明;其他异常,方法上面必须进行 throws 的声明,告知调用者此方法存在异常

9 OOM你遇到过哪些情况,SOF你遇到过哪些情况

堆内存溢出 OutOfMemoryError(OOM)

     除了程序计数器外,虚拟机内存的其他几个运行时区域都有发生OutOfMemoryError(OOM)异常的可能。

     Java Heap 溢出。 一般的异常信息:java.lang.OutOfMemoryError:Java heap spacess。 java堆用于存储对象实例,我们只要不断的创建对象,并且保证GC Roots到对象之间有可达路径来避免垃圾回收机制清除这些对象,就会在对象数量达到最大堆容量限制后产生内存溢出异常。

堆栈溢出 StackOverflow (SOF)

      StackOverflowError 的定义: 当应用程序递归太深而发生堆栈溢出时,抛出该错误。 因为栈一般默认为1-2m,一旦出现死循环或者是大量的递归调用,在不断的压栈过程中,造成栈容量超过1m而导致溢出。

     栈溢出的原因:

     递归调用。 大量循环或死循环。 全局变量是否过多。 数组、List、map数据过大

10 throw和throws的区别?

?       throw:是真实抛出一个异常。

?       throws:是声明可能会抛出一个异常。

        1)throw语句用在方法体内,表示抛出异常,由方法体内的语句处理。

        2)throw是具体向外抛出异常的动作,所以它抛出的是一个异常实例,执行throw一定是抛出了某种异常。

throws

       1)throws语句是用在方法声明后面,表示如果抛出异常,由该方法的调用者来进行异常的处理。

       2)throws主要是声明这个方法会抛出某种类型的异常,让它的使用者要知道需要捕获的异常的类型。

      3)throws表示出现异常的一种可能性,并不一定会发生这种异常。

11 final、finally、finalize 有什么区别?

  1. final 表示最终的、不可改变的。用于修饰类、方法和变量。final 变量必须在声明时给定初值,只能读取,不可修改。final 方法也同样只能使用,不能重写,但能够重载。final 修饰的对象,对象的引用地址不能变,但对象的属性值可以改变
  2. finally 异常处理的一部分,它只能用在 try/catch 语句中,表示希望 finally 语句块中的代码最后一定被执行(存在一些情况导致 finally 语句块不会被执行,如 jvm 结束)
  3. finalize() 是在 java.lang.Object 里定义的,Object 的 finalize() 方法什么都不做,对象被回收时 finalize() 方法会被调用。Java 技术允许使用 finalize() 方法在垃圾收集器将对象从内存中清除出去之前做必要清理工作,在垃圾收集器删除对象之前被调用的。一般情况下,此方法由JVM调用。特殊情况下,可重写 finalize() 方法,当对象被回收的时候释放一些资源,须调用 super.finalize() 。

12 finally语句块一定执行吗?

答案是不一定。存在很多特殊情况导致 finally 语句块不执行。如:

  1. 直接返回未执行到 try-finally 语句块
  2. 抛出异常未执行到 try-finally 语句块
  3. 系统退出未执行到 finally 语句块

等...

代码如下

public static String test() {String str = null;int i = 0;if (i == 0) {return str;//直接返回未执行到finally语句块}try {System.out.println("try...");return str;} finally {System.out.println("finally...");}
}
public static String test2() {String str = null;int i = 0;i = i / 0;//抛出异常未执行到finally语句块try {System.out.println("try...");return str;} finally {System.out.println("finally...");}
}
public static String test3() {String str = null;try {System.out.println("try...");System.exit(0);//系统退出未执行到finally语句块return str;} finally {System.out.println("finally...");}
}

13 return与finally的执行顺序对返回值的影响

对于 try 和 finally 至少一个语句块包含 return 语句的情况:

  1. finally 语句块会执行
  2. finally 没有 return,finally 对 return 变量的重新赋值修改无效
  3. try 和 finally 都包含return,return 值会以 finally 语句块 return 值为准

如下面的例子

打印

finally change return string to C
B

finally 语句块中新增 return 语句

  public static void main(String[] args) {System.out.println(getString());
}
public static String getString() {String str = "A";try {str = "B";return str;} finally {System.out.println("finally change return string to C");str = "C";return str;}
}

打印结果

finally change return string to C
C

14 try-catch-finally 中哪个部分可以省略? 

     try-catch-finally 其中 catch 和 finally 都可以被省略,但是不能同时省略,也就是说有 try 的时候,必须后面跟一个 catch 或者 finally。

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

      finally 一定会执行,即使是 catch 中 return 了,catch 中的 return 会等 finally 中的代码执行完之后,才会执行。