Java基础-知识点03(面试|学习)

Java基础-知识点03

    • String类
      • String类的作用及特性
      • String不可以改变的原因及好处
      • String、StringBuilder、StringBuffer的区别
      • String中的replace和replaceAll的区别
      • 字符串拼接使用+还是使用Stringbuilder
      • String中的equal()与Object方法中equals()区别
      • String a = new String("abc");创建了几个对象?
    • 成员变量
      • 实例变量
      • 静态变量
      • 静态变量与实例变量的内存分配有何不同?
      • 静态变量在多线程环境下的安全性如何保证?
      • 如何实现常量(不可变)类:
      • 成员变量的默认值
      • 成员变量的访问权限
    • hashcode
      • hashCode 方法的原理和实现
      • hashcode和equals()区别
      • 为什么重写 equals() 时必须重写 hashCode() 方法?
      • 为什么 JDK 还要同时提供这两个方法呢?
      • 为什么不同的对象可能会有相同的hashcode?什么是哈希冲突?
      • 如何解决哈希冲突?
      • 哈希表的负载因子
      • 为什么哈希表负载因子默认为0.75?
    • 异常
      • 异常的层次结构
      • 受检异常(非运行时异常)
      • 非受检异常(运行时异常)
      • Error类及其子类
      • Java 中的异常处理机制
      • try-catch-finally 中哪个部分可以省略
      • 异常的声明
      • 异常的抛出
      • 抛出异常的场景
      • 什么是异常链(Exception Chaining)?如何使用异常链?
      • 如何自定义异常类?

String类

String类是Java中用于表示字符串的类。在Java中,字符串是一系列字符的序列,用于表示文本数据。

String类的作用及特性

1、创建字符串: 可以通过字符串字面量或使用new关键字来创建字符串对象。

String str1 = "Hello"; // 使用字符串字面量创建
String str2 = new String("World"); // 使用new关键字创建

2、字符串长度: 可以使用length()方法获取字符串的长度。

String str = "Hello, World!";
int length = str.length(); // length = 13

3、字符串连接: 可以使用+运算符或concat()方法将字符串连接起来。

String str1 = "Hello";
String str2 = "World";
String result = str1 + " " + str2; // 使用+运算符连接
String result2 = str1.concat(" ").concat(str2); // 使用concat()方法连接

4、字符串比较: 可以使用equals()方法比较两个字符串是否相等。

String str1 = "Hello";
String str2 = "hello";
boolean isEqual = str1.equals(str2); // false,区分大小写

5、子串提取: 可以使用substring()方法从字符串中提取子串。

String str = "Hello, World!";
String sub = str.substring(7); // 提取从索引为7开始的子串:"World!"

6、字符串查找: 可以使用indexOf()方法查找指定字符或子串在字符串中的位置。

String str = "Hello, World!";
int index = str.indexOf("World"); // index = 7,子串"World"在字符串中的位置

7、字符串替换: 可以使用replace()方法替换字符串中的字符或子串。

String str = "Hello, World!";
String replaced = str.replace("World", "Java"); // 替换子串:"Hello, Java!"

8、字符串拆分: 可以使用split()方法根据指定的分隔符将字符串拆分为字符串数组。

String str = "apple,orange,banana";
String[] fruits = str.split(","); // fruits = ["apple", "orange", "banana"]

9、字符串转换: 可以使用toLowerCase()、toUpperCase()等方法将字符串转换为小写或大写形式。

String str = "Hello, World!";
String lowerCase = str.toLowerCase(); // 转换为小写:"hello, world!"
String upperCase = str.toUpperCase(); // 转换为大写:"HELLO, WORLD!"

String类还提供了许多其他方法,如去除空白字符、字符查找、字符替换等

String不可以改变的原因及好处

1、String类不该可变的原因

  • String内部使用 char数组 存储数据,该数组被声明为 final。所以数组初始化后就不能引用其他数组。
  • String内部没有提供更改 char数组 的方法。 因此保证 String 不可变。

2、String对象不可变的好处

  • 安全性和可靠性: 不可变性确保了字符串对象的安全性和可靠性。
  • 可以缓存 hash 值 (String 用做 HashMap 的 key)
  • String Pool 的需要,如果一个 String 对象已经被创建过了,那么就会从 String Pool 中取得引用。只有 String 是不可变的,才可能使用 String Pool。

String、StringBuilder、StringBuffer的区别

  String, StringBuffer, 和 StringBuilder 是 Java 中用于处理字符串的三个类,它们在功能和性能上有一些区别:

1、String:

  • String 是不可变的类,一旦创建后就不能修改其内容,线程安全
  • 每次对 String 进行修改(如拼接、替换等操作)都会生成一个新的 String 对象,原有的对象不变。
  • 适用于那些不需要频繁修改的字符串,如存储常量、配置信息等。

2、StringBuffer:

  • StringBuffer 是可变的类,可以动态修改其内容。
  • 提供了许多方法来进行字符串的拼接、插入、删除、替换等操作。
  • 线程安全的,所有的公共方法都使用了 synchronized 关键字进行同步。
  • 适用于多线程环境下需要频繁修改字符串的场景。

3、StringBuilder:

  • StringBuilder 是可变的类,和 StringBuffer 类似,但不保证线程安全
  • 没有对方法进行同步处理,因此在单线程环境下性能更好。
  • 适用于单线程环境下需要频繁修改字符串的场景,比如字符串拼接、动态生成文本等。

记忆表格

特点StringStringBufferStringBuilder
可变性不可变可变可变
线程安全性线程安全线程安全非线程安全
内容修改方式生成新的String对象直接修改原对象直接修改原对象
同步机制不需要使用synchronized关键字同步不需要
性能较低(频繁修改时)一般较高(频繁修改时)
适用场景不需要频繁修改的字符串多线程环境下需要频繁修改的字符串单线程环境下需要频繁修改的字符串

String中的replace和replaceAll的区别

  String类中的replace和replaceAll方法都用于替换字符串中的字符或子串。

1、replace方法
  replace方法接受两个参数:要替换的旧字符或子串,和替换成的新字符或子串。
它只会替换字符串中所有匹配的旧字符或子串为新字符或子串,不涉及正则表达式。

2、replaceAll方法
  replaceAll方法接受两个参数:要替换的正则表达式,和替换成的新字符串。
它使用正则表达式来匹配要替换的内容,可以实现更灵活的替换规则。

记忆replace方法是基于字符或子串的简单替换,而replaceAll方法则更加灵活,可以基于正则表达式进行替换,因此在需要复杂替换规则或匹配模式时,更适合使用replaceAll方法。

字符串拼接使用+还是使用Stringbuilder

  在进行字符串拼接时,推荐使用StringBuilder或StringBuffer而不是简单的使用+操作符,特别是在有大量字符串拼接的情况下。因为StringBuilder和StringBuffer可以提供更好的性能和内存利用率。

  使用+操作符进行字符串拼接时,会调用StringBuilder 的 append() 方法实现拼接,然后通过 toString() 方法将 StringBuilder 转换为最终的字符串对象。但是在循环体中,每循环一次就会创建一个新的对象,这样会导致大量的对象创建和内存消耗。

  如果是在单线程环境下进行字符串拼接,推荐使用StringBuilder,因为它的性能更高,不需要同步操作。如果在多线程环境下进行字符串拼接,应该使用StringBuffer,因为它是线程安全的,内部方法都使用了synchronized关键字进行同步。

String中的equal()与Object方法中equals()区别

1、String类中的equals()方法

  String类重写了Object类中的equals()方法,用于比较两个字符串对象的内容是否相同。String类的equals()方法比较的是字符串的内容,而不是引用地址。

String str1 = "hello";
String str2 = "hello";
boolean result = str1.equals(str2); // true,比较的是字符串内容

2、Object类中的equals()方法

  Object类中的equals()方法是用于比较两个对象的引用地址是否相同。如果一个类没有重写Object类的equals()方法,则默认使用Object类中的方法进行引用地址的比较。

Object obj1 = new Object();
Object obj2 = new Object();
boolean result = obj1.equals(obj2); // false,比较的是对象的引用地址

String a = new String(“abc”);创建了几个对象?

  创建了2个对象。
  1、使用new关键字,首先会在堆中创建一个实例对象,即创建了一个字符串常量 “abc” 的对象。这个对象存储在字符串常量池中,因为字符串常量是不可变的,所以它可以被多个字符串变量引用,并且在内存中只有一份拷贝。
  2、创建了一个新的字符串对象 a。这个对象是通过调用 new String(“abc”) 构造方法创建的,它在堆内存中独立存在,并且包含了字符串常量 “abc” 的内容。

成员变量

  Java 中的成员变量是指定义在类中的变量,包含实例变量和静态变量。用于存储对象的状态或类的共享状态。

实例变量

  • 实例变量是属于对象的变量,每个对象都有自己的一份实例变量,它们在内存中存储在对象的内部。
  • 实例变量在对象被创建时被初始化,并且每个对象的实例变量值是相互独立的。
  • 实例变量通常用于存储对象的状态信息,比如一个人的姓名、年龄、性别等。
  • 实例变量只能通过对象来访问,不能通过类名来访问。
public class MyClass {int instanceVar; // 实例变量
}

静态变量

  • 静态变量是属于类的变量,所有对象共享一份静态变量,它们在内存中只会有一份拷贝,无论该类被实例化多少次,该变量的值都是相同的。
  • 静态变量通常用于存储所有对象共享的数据,比如常量、全局计数器等。
  • 静态变量可以通过类名来访问,也可以通过对象来访问,但是建议使用类名来访问
public class MyClass {static int staticVar; // 静态变量
}

成员变量

静态变量与实例变量的内存分配有何不同?

  • 实例变量在每个对象创建时分配内存,每个对象都有自己的实例变量副本。
  • 静态变量在类加载时分配内存,只会被分配一次,所有类的实例共享同一个静态变量。

静态变量在多线程环境下的安全性如何保证?

  • 静态变量可以在多线程环境下共享,但是需要注意同步访问静态变量的操作,可以使用synchronized关键字或者 volatile 关键字来保证线程安全性。

如何实现常量(不可变)类:

  • 使用 final 关键字修饰类,防止类被继承。
  • 使用 final 关键字修饰变量,防止变量被修改。
  • 使用构造方法或静态代码块初始化不可变对象的值。
  • 提供只读访问方法,不提供修改方法。

成员变量的默认值

  • 对于基本数据类型的成员变量,默认值为0或false。
  • 对于对象引用类型的成员变量,默认值为null。

成员变量的访问权限

  成员变量的访问权限可以通过访问修饰符来控制,常用的访问修饰符包括public、private、protected和默认访问修饰符。

hashcode

  hashCode 是 Java 中用于获取对象哈希码的方法。哈希码是一个整数,用于快速定位对象在哈希表等数据结构中的位置,比如在集合类如 HashMap、HashSet 中的使用。hashCode 方法定义在 Object 类中,因此所有 Java 类都可以调用 hashCode 方法。

hashCode 方法的原理和实现

  • hashCode 方法返回的是对象的哈希码,可以理解为对象的逻辑地址。hashCode 方法的默认实现是根据对象的地址计算哈希码,即将对象的内存地址转换成一个整数值。。
  • 相同对象调用 hashCode 方法多次应该返回相同的结果。
  • 对于不同的对象,其哈希码一般应该是不同的,但不保证不同对象的哈希码绝对唯一。

hashcode和equals()区别

  equals() 是用来判断两个对象是否相等,即内容是否相相等。

  hashCode 方法用于获取对象的哈希码,用于快速定位对象在哈希表等数据结构中的位置。hashcode 也可以用来两个对象是否相等,但是判断结果不准确,因为哈希码相等的两个对象不一定是同一个对象。

为什么重写 equals() 时必须重写 hashCode() 方法?

  因为两个相等的对象的 hashCode 值必须是相等。如果 equals 方法判断两个对象是相等的,那这两个对象的 hashCode 值也要相等。如果重写 equals() 时没有重写 hashCode() 方法的话就可能会导致 equals 方法判断是相等的两个对象,但hashCode 值却不相等。

总结:

  • 如果两个对象的hashCode 值相等,那这两个对象不一定相等(哈希碰撞)。
  • 如果两个对象的hashCode 值相等并且equals()方法也返回 true,我们才认为这两个对象相等。
  • 如果两个对象的hashCode 值不相等,我们就可以直接认为这两个对象不相等。

为什么 JDK 还要同时提供这两个方法呢?

  为了提高效率。在一些容器(比如 HashMap、HashSet)中,有了 hashCode() 之后,判断元素是否在对应容器中的效率会更高。

  在Java的⼀些集合类的实现中,比较两个对象是否相等时,先调用对象的 hashCode()方法得到hashCode进行比较,如果hashCode不相同,就可以直接认为这两个对象不相同,如果hashCode相同,进⼀步调用equals()方法进行比较

  而equals()⽅法,就是⽤来最终确定两个对象是不是相等的,因为equals()方法的实现会比较重,逻辑⽐较多,⽽hashCode()主要就是得到⼀个哈希值,实际上就⼀个数字,效率较快,所以在比较两个对象时,通常都会先根据 hashCode先比较⼀下。

为什么不同的对象可能会有相同的hashcode?什么是哈希冲突?

  不同的对象可能会有相同的哈希码,这种情况通常称为哈希冲突。哈希冲突是指两个不同的对象经过哈希函数计算后得到相同的哈希码
这种情况可能发生的原因包括以下几点:

  • 哈希码范围有限: 哈希码是一个整数,在有限的范围内取值。如果两个不同的对象经过哈希函数计算后得到的哈希码在这个范围内是相同的,就会发生哈希冲突。
  • 对象属性相似性: 如果两个对象的属性在哈希计算中具有相似性或者相等性,那么它们的哈希码可能会相同。
  • 哈希码与内存地址无关: Java 中的哈希码通常与对象的内存地址无关,因此即使两个不同的对象在内存中存储位置不同,它们的哈希码也有可能相同。

如何解决哈希冲突?

  哈希冲突是指两个不同的对象经过哈希函数计算后得到相同的哈希码。为了解决哈希冲突,通常采用以下几种常见的方法:
1、开放寻址法:
  当发生哈希冲突时,使用一定的规则找到下一个可用的存储位置,直到找到一个空闲位置或者遍历整个表。常见的开放寻址法包括线性探测平方探测双重散列等。

  • 线性探测法:从当前占用的地址往后,逐一排查,有空的地址就占用。如果查到表尾还没有就返回到表头,直到查完。
  • 平方探测法:探测的主要思想是,如果在原始哈希码的位置发生冲突,就通过计算一个增量序列来寻找下一个空闲位置。这个增量序列通常采用二次方的增量。如从发生冲突的位置d[i],按照地址+平方往后找,即d[i]+12,d[i]+22,d[i]+32直到找到空地址。
  • 双重散列法:双重散列法的基本思想是,当发生哈希冲突时,通过第一个哈希函数计算原始位置,然后通过第二个哈希函数计算增量,不断尝试将冲突的位置加上增量直到找到空闲位置或者遍历整个哈希表。

2、链地址法
  在链地址法中,哈希表的每个桶(存储位置)都对应一个链表,发生哈希冲突时,新元素会直接添加到对应桶的链表末尾,形成一个链式结构。
  链地址法的基本思想是,将具有相同哈希码但不同的键值的元素存储在同一个桶中,通过链表将它们串联起来。这样,在发生哈希冲突时,不需要重新计算位置或者探测空闲位置,只需要在对应桶的链表中进行插入、查找或删除操作即可。
链地址法的具体过程:

  • 使用哈希函数计算键值的哈希码,确定存储位置(桶)。
  • 如果该桶为空,直接将元素插入到该桶中。
  • 如果该桶非空,遍历链表查找是否存在相同键值的元素:
    • 如果存在相同键值的元素,则更新该元素的值。
    • 如果不存在相同键值的元素,则将新元素添加到链表末尾。

3、链地址法的优化
  在链地址法中,当链表过长时可能会影响性能。可以采用链表长度超过阈值时,将链表转换为更高效的数据结构,如红黑树,来优化查找和操作性能。

  这些方法各有优缺点,选择合适的解决方法取决于具体的应用场景和性能需求。在实际开发中,常见的哈希表实现如 HashMap 就是通过链地址法来解决哈希冲突,并对桶的数量和链表长度等进行动态调整来保持性能。

哈希表的负载因子

  哈希表的负载因子是指哈希表中已存储元素个数与哈希表容量之比,通常用公式表示为负载因子 = 元素个数 / 哈希表容量。负载因子是衡量哈希表空间利用率的重要指标之一。

  负载因子反映了哈希表的密集程度。当负载因子较小时,哈希表中空槽较多,可能会浪费一些存储空间;当负载因子较大时,哈希表中的槽可能会被填满,导致哈希冲突的概率增加,查找、插入、删除等操作的时间复杂度可能会变高。

  在 Java 中,哈希表的默认负载因子通常是 0.75。这个默认值指的是当哈希表中的元素个数达到容量的 75% 时,会触发哈希表的扩容操作。

为什么哈希表负载因子默认为0.75?

  这个负载因子的选择是经过实践和考量的结果,旨在保持哈希表的性能和空间利用率的平衡。
1、空间利用率: 负载因子为 0.75 表示在哈希表容量的 75% 时触发扩容,这样可以保证哈希表的空间利用率比较高,减少了内存浪费。
2、性能平衡: 通过控制负载因子,可以在空间利用率和性能之间寻找一个平衡点。负载因子过小会导致频繁的扩容操作,影响性能;负载因子过大会增加哈希冲突的概率,也会影响性能。
3、哈希冲突管理: 适度的负载因子可以有效控制哈希冲突的发生,避免在元素插入、查找和删除等操作时频繁地发生冲突。

  在 Java 中,可以通过 HashMap 和 HashSet 等类的构造函数来自定义负载因子。例如,在创建一个 HashMap 实例时,可以使用指定负载因子的构造函数来设置自定义的负载因子值。

Map<String, Integer> map = new HashMap<>(16, 0.8f); // 设置负载因子为 0.8
Set<String> set = new HashSet<>(32, 0.6f); // 设置负载因子为 0.6

异常

  异常在 Java 中是一种用于处理程序运行时错误的机制。

异常的层次结构

  Java 的异常类主要分为两大类:Throwable 类及其子类和 Error 类及其子类。Throwable 类是所有 Java 异常类的根类,它有两个主要的子类:Exception 类和 Error 类。
  Exception 类: 是表示程序可以处理的异常情况的基类,它包括了各种受检异常(非运行时异常)和非受检异常(运行时异常)。
  Error 类: 表示严重的错误,通常由系统级问题引起,比如内存不足、虚拟机错误等。Error 类及其子类不需要程序员显式地处理或者抛出,通常是由虚拟机或系统级别的组件处理。
在这里插入图片描述

受检异常(非运行时异常)

  受检异常是指在编译时强制要求程序处理的异常。Exception 的子类中,除了 RuntimeException 及其子类之外的所有异常,都属于受检异常。这些异常通常是由外部因素引起的,比如文件不存在、网络连接失败等。受检异常必须在代码中显式地处理或者通过 throws 关键字声明抛出。

常见的受检异常:

  • IOException:输入输出异常,比如文件读写错误。
  • SQLException:数据库操作异常。
public void readFile() throws IOException {FileInputStream fis = new FileInputStream("file.txt");// 处理文件读取逻辑fis.close();
}

非受检异常(运行时异常)

  非受检异常也称为运行时异常,是指在运行时可能会发生但不需要强制处理的异常。RuntimeException 及其子类属于非受检异常,这些异常通常是由程序逻辑错误引起的,比如空指针异常、数组下标越界等。非受检异常不要求在代码中显式处理,但可以选择捕获并处理,也可以通过 throws 关键字声明抛出。

RuntimeException:运行时异常的基类

  • NullPointerException:空指针异常。
  • ArrayIndexOutOfBoundsException:数组索引越界异常。
  • ClassCastException:类转换异常等。
public void divide(int a, int b) {if (b == 0) {throw new ArithmeticException("除数不能为零");}int result = a / b;System.out.println("结果:" + result);
}

Error类及其子类

  Error 类及其子类:程序中无法处理的错误, 此类错误一般表示代码运行时 JVM 出现问题。此类错误发生时,JVM 将终止线程。这些错误是不受检异常,非代码性错误。
通常有:

  • Virtual MachineError(虚拟机运行错误)
  • NoClassDefFoundError(类定义错误)
  • OutOfMemoryError:内存不足错误
  • StackOverflowError:栈溢出错误

Java 中的异常处理机制

  Java 中的异常处理机制通过 try-catch-finally 块来实现。

  try 块: 在 try 块中编写可能会引发异常的代码。try 块是异常处理的起点,用于包裹可能出现异常的代码段。
  try 用于监听,其后可接零个或多个 catch 块,如果没有 catch 块,则必须跟一个 finally 块

try {// 可能会引发异常的代码块
} catch (异常类型1 变量名1) {// 处理异常的代码块
} catch (异常类型2 变量名2) {// 处理其他类型的异常
} finally {// 最终执行的代码块// 无论是否发生异常,都会执行
}

  catch 块: 在 catch 块中处理 try 块中可能抛出的异常。catch 块可以有多个,用于捕获不同类型的异常

//捕获特定类型的异常:
try {// 可能会引发异常的代码块
} catch (IOException e) {// 处理 IOException 异常
} catch (NullPointerException e) {// 处理 NullPointerException 异常
}//捕获通用异常(Exception 类的子类):
try {// 可能会引发异常的代码块
} catch (Exception e) {// 处理所有类型的异常
}

   finally 块finally 块中的代码无论是否发生异常都会执行,用于执行一些必须要完成的操作,比如释放资源、关闭连接等。

try {// 可能会引发异常的代码块
} catch (异常类型1 变量名1) {// 处理异常的代码块
} finally {// 最终执行的代码块// 无论是否发生异常,都会执行
}

异常处理机制的工作流程

  • 当程序执行到 try 块中的代码时,如果发生异常,则会立即跳转到与异常类型匹配的 catch 块中执行对应的处理代码。
  • 如果发生异常但没有匹配到对应的 catch 块,则异常会沿着方法调用链向上抛出,直到找到匹配的 catch 块或者到达方法的最外层(main 方法)。
  • 如果在 try 块中没有发生异常,则会跳过 catch 块,直接执行 finally 块中的代码,然后继续执行 try-catch-finally 结构之后的代码。

注意:不要在 finally 语句块中使用 return! 当 try 语句和 finally 语句中都有 return 语句时,try 语句块中的 return 语句会被忽略。这是因为 try 语句中的 return 返回值会先被暂存在一个本地变量中,当执行到 finally 语句中的 return 之后,这个本地变量的值就变为了 finally 语句中的 return 返回值。

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

  在使用 try-catch-finally 块时,可以省略 catchfinally 中的任何一个部分,但不能同时省略它们。这是因为 try 块中必须至少有一个 catch 块或一个 finally 块,用于处理可能出现的异常或资源释放。

  如果省略 catch 块,则表示将异常传播到调用方处理,而如果省略 finally 块,则表示不需要执行任何清理代码,例如关闭文件或数据库连接等。

  需要注意的是,try-catch 块是合法的,即省略了 finally 块。这种情况下,如果出现异常,catch 块会处理异常,并且程序将继续执行 try-catch 块之后的代码。如果没有异常,则程序也将继续执行 try-catch 块之后的代码。

异常的声明

  异常的声明主要涉及两个方面:方法声明可能会抛出的异常和自定义异常类的声明。
1、方法声明可能会抛出的异常: 在方法声明中可以使用 throws 关键字来声明方法可能会抛出的异常,需要在调用时进行异常处理或者继续向上抛出异常。

public void myMethod() throws IOException, SQLException {// 可能会引发 IOException 或 SQLException 的代码块
}

在上面的例子中,myMethod() 方法声明可能会抛出 IOException 和 SQLException 两种异常,调用者在调用这个方法时需要对这些异常进行处理或者继续向上抛出。

2、自定义异常类的声明:自定义异常类的声明主要包括定义异常类的结构和功能,以及异常的使用方式。自定义异常类一般需要继承自 Exception 类或其子类,并根据实际需求添加合适的构造方法和其他方法。

public class MyCustomException extends Exception {public MyCustomException(String message) {super(message);}
}

在这个例子中,声明了一个名为 MyCustomException 的自定义异常类,它继承自 Exception 类,并提供了一个带有异常信息的构造方法。

异常的抛出

  抛出异常的目的是告诉调用者或者上层代码发生了异常,并且中断当前的执行流程,让异常处理机制来处理这个异常。通常通过 throw 关键字来实现。

如果代码可能会引发某种错误,可以创建一个合适的异常类实例并抛出它。

public static double method(int value) {if(value == 0) {throw new ArithmeticException("参数不能为0"); //抛出一个运行时异常}return 5.0 / value;
}

有时我们会从 catch 中抛出一个异常,目的是为了改变异常的类型。多用于在多系统集成时,当某个子系统故障,异常类型可能有多种,可以用统一的异常类型向外暴露,不需暴露太多内部异常细节。

private static void readFile(String filePath) throws MyException {    try {// code} catch (IOException e) {MyException ex = new MyException("read file failed.");ex.initCause(e);throw ex;}
}

习惯上,定义一个异常类应包含两个构造函数,一个无参构造函数和一个带有详细描述信息的构造函数(Throwable 的 toString 方法会打印这些详细信息,调试时很有用), 比如下面用到的自定义MyException:

public class MyException extends Exception {public MyException(){ }public MyException(String msg){super(msg);}// ...
}

抛出异常的场景

  • 检测到错误情况: 当程序检测到某些错误情况时,可以抛出异常来中断程序的执行,并提供错误信息以便后续处理。
  • 业务逻辑异常: 在业务逻辑中,某些特定情况可能需要抛出异常来通知调用者或者上层代码。
  • 异常转换: 在某些情况下,需要将底层异常转换成更高层次的异常抛出,以便更好地管理和处理异常。

在使用 throw 抛出异常时,需要注意以下几点

  • 抛出的异常应该是合理的,能够准确描述错误的情况,并提供有用的异常信息。
  • 抛出的异常应该被上层代码捕获和处理,以避免未捕获的异常导致程序崩溃。
  • 自定义异常类时,可以继承自 Java 的 Exception 或其子类,或者直接实现 Throwable 接口来定义异常类。

什么是异常链(Exception Chaining)?如何使用异常链?

  异常链是指在捕获和处理异常时,将当前异常和导致当前异常的原因异常(根异常)链接起来,形成一个异常链。这种链式结构可以帮助调试和追踪异常的来源,提供更全面的异常信息,方便进行故障排查和修复。
  在 Java 中,可以通过在捕获异常时使用 initCause() 方法或者在抛出新异常时将原始异常作为参数传递来创建异常链。

如何自定义异常类?

  要自定义异常类,在 Java 中通常需要继承自 Exception 类或其子类,并提供合适的构造方法和其他必要的成员方法。步骤如下:

1、创建异常类: 创建一个新的 Java 类,并继承自 Exception 类或其子类。

public class MyCustomException extends Exception {// 可以在这里添加异常类的成员变量和方法
}

2、添加构造方法:在自定义异常类中添加构造方法,通常需要至少提供一个带有异常信息的构造方法。

public class MyCustomException extends Exception {// 构造方法,传入异常信息public MyCustomException(String message) {super(message);}
}

3、添加其他方法(可选): 根据需要,可以在自定义异常类中添加其他方法或成员变量,以满足特定的异常处理需求。

public class MyCustomException extends Exception {private int errorCode;// 构造方法,传入异常信息和错误代码public MyCustomException(String message, int errorCode) {super(message);this.errorCode = errorCode;}// 获取错误代码的方法public int getErrorCode() {return errorCode;}
}

4、在代码中使用自定义异常类: 在需要抛出异常的地方,使用 throw 关键字抛出自定义异常对象。

public class MyClass {public void myMethod() throws MyCustomException {// 某些条件满足时抛出自定义异常if (someCondition) {throw new MyCustomException("发生了自定义异常", 500);}}
}

使用自定义异常类时,需要注意:

  • 异常类通常需要提供带有异常信息的构造方法,以便在抛出异常时传递描述性的异常信息。
  • 可以根据需要添加其他方法或成员变量,来扩展自定义异常类的功能。
  • 在代码中抛出自定义异常时,需要使用 throw 关键字并创建异常对象。
  • 在捕获自定义异常时,可以根据异常类型来捕获并处理异常,以及获取异常对象中的信息。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/308199.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

「51媒体网」汽车类媒体有哪些?车展媒体宣传

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 汽车类媒体有很多&#xff0c;具体如下&#xff1a; 汽车之家&#xff1a;提供全面的汽车新闻、评测、导购等内容。 爱卡汽车&#xff1a;同样是一个综合性的汽车信息平台&#xff0c;涵…

WPS二次开发系列:Gradle版本、AGP插件与Java版本的对应关系

背景 最近有体验SDK的同学反馈接入SDK出现报错&#xff0c;最终定位到原因为接入的宿主app项目的gradle版本过低导致&#xff0c;SDK兼容支持了android11的特性&#xff0c;需要对应的gradle插件为支持android11的版本。 现象 解决方案 将gradle版本升级至支持android11的插件版…

009、Python+fastapi,第一个后台管理项目走向第9步:ubutun 20.04下安装vscode+git环境配置

一、说明 git是一定要配置的&#xff0c;由于是白嫖的无影云电脑&#xff0c;东西得保存在网上&#xff0c;就继续白嫖gitee吧&#xff0c;显然国内github是不太合适的了 二、安装git 直接安装sudo apt install -y git git --version git version 2.25.1 三、配置git 在git上…

使用Postman发送跨域请求实验

使用Postman发送跨域请求 1 跨域是什么&#xff1f;2 何为同源呢?3 跨域请求是如何被检测到的&#xff1f;4 Postman跨域请求测试4.1 后端准备4.2 测试用例4.2.1 后端未配置跨域请求(1) 前端不跨域&#xff08;2&#xff09;前端跨域 4.2.2 后端配置跨域信息&#xff08;1&…

项目5-博客系统3+接口完

1.实现显示用户信息 ⽬前⻚⾯的⽤⼾信息部分是写死的. 形如 我们期望这个信息可以随着用户登陆而发生改变. • 如果当前⻚⾯是博客列表⻚, 则显⽰当前登陆⽤⼾的信息. • 如果当前⻚⾯是博客详情⻚, 则显⽰该博客的作者⽤⼾信息. 注意: 当前我们只是实现了显⽰⽤⼾名, 没有…

MySQL-触发器:触发器概述、触发器的创建、查看删除触发器、 触发器的优缺点

触发器 触发器1. 触发器概述2. 触发器的创建2.1 创建触发器语法2.2 代码举例 3. 查看、删除触发器3.1 查看触发器3.2 删除触发器 4. 触发器的优缺点4.1 优点4.2 缺点4.3 注意点 注&#xff1a;此为笔者学习尚硅谷-宋红康MySQL的笔记&#xff0c;其中包含个人的笔记和理解&#…

Node Sass does not yet support your current environment

Node Sass does not yet support your current environment 报错是node-sass的版本出现问题了&#xff0c;node-sass的版本是由node版本决定。 查看node和node-sass的版本的版本&#xff0c;进行版本匹配

读所罗门的密码笔记19_治理模式

1. 解决方案 1.1. 全球人工智能的环境错综复杂&#xff0c;它严重依赖于价值观&#xff0c;且关系重大 1.2. 即使是与大家同仇敌忾的问题做斗争&#xff0c;也往往无法在国际社会中取得最佳效果 1.3. OPCW&#xff08;禁止化学武器组织&#xff09;已经帮助限制了化学武器的…

数字乡村发展新模式:科技创新引领农业现代化与乡村振兴协同发展

随着信息技术的飞速发展&#xff0c;数字乡村已成为新时代农业现代化与乡村振兴协同发展的新模式。科技创新作为推动这一模式的核心动力&#xff0c;正引领着乡村产业结构的优化升级&#xff0c;促进农村经济的全面振兴&#xff0c;让农民在现代化的进程中共享发展成果。 一、科…

浮点数的表示

王道考研ppt总结&#xff1a; 二、个人理解 浮点数解决的是定点数的位数局限&#xff0c;导致表示范围有限的问题 阶码&#xff1a;由阶符和数值部分组成&#xff0c;阶符为&#xff0c;小数点向左移动&#xff0c;否则向右移动&#xff1b;数值部分&#xff0c;是底数的几次幂…

机器人坐标系转换之从世界坐标系到局部坐标系

三角函数实现 下面是代码c和python实现&#xff1a; #include <iostream> #include <cmath>struct Point {double x;double y; };class RobotCoordinateTransform { private:Point origin; // 局部坐标系的原点在世界坐标系中的坐标public:RobotCoordinateTransfo…

开源项目one-api的k8s容器化部署(上)-- 制作镜像及部署准备

一、背景 最近需要对开源项目one-api进行k8s容器化部署&#xff0c;主要分以下几个步骤&#xff1a; 制作docker镜像申请mysql和redis数据库docker-compose部署方式k8s部署方式 整个的篇幅比较长&#xff0c;将会分成上下两篇来阐述。 二、制作docker镜像 开源项目one-api…

[目标检测] OCR: 文字检测、文字识别、text spotter

概述 OCR技术存在两个步骤&#xff1a;文字检测和文字识别&#xff0c;而end-to-end完成这两个步骤的方法就是text spotter。 文字检测数据集摘要 daaset语言体量特色MTWI中英文20k源于网络图像&#xff0c;主要由合成图像&#xff0c;产品描述&#xff0c;网络广告(淘宝)MS…

【C Hash Map from Redis】

将Redis源码中的哈希表底层逻辑提取&#xff0c;并进行最小demo级测试将对应文件抽出&#xff0c;通过宏替换等方式保证源码编译通过main.c编写测试demo &#xff0c;注册哈希函数和值比较函数&#xff08;必选项&#xff09; /* Hash Tables Implementation.** This file imp…

【spring】AOP切面注解学习(二)

文接上篇&#xff1a;【spring】AOP切面注解学习&#xff08;一&#xff09; AOP切面注解测试示例代码 示例代码 一 maven的pom文件导入 <dependency><groupId>org.springframework</groupId><artifactId>spring-aop</artifactId></depende…

某次众测的加解密对抗

前言 起源于某次众测中&#xff0c;遇到请求包响应包全密文的情况&#xff0c;最终实现burp中加解密。 用到的工具有 sekiro&#xff08;rpc转发&#xff09;flask&#xff08;autodecoder自定义接口&#xff09;autodecoder&#xff08;burp插件转发&#xff09; debug部分…

说说我理解的数据库中的Schema吧

一、SQL标准对schema如何定义&#xff1f; ISO/IEC 9075-1 SQL标准中将schema定义为描述符的持久命名集合&#xff08;a persistent, named collection of descriptors&#xff09;。 大部分的网上资料定义Schema如下&#xff1a; schema是用来组织和管理数据的一种方式。它…

【LAMMPS学习】八、基础知识(2.7)NEMD 模拟

8. 基础知识 此部分描述了如何使用 LAMMPS 为用户和开发人员执行各种任务。术语表页面还列出了 MD 术语&#xff0c;以及相应 LAMMPS 手册页的链接。 LAMMPS 源代码分发的 examples 目录中包含的示例输入脚本以及示例脚本页面上突出显示的示例输入脚本还展示了如何设置和运行各…

Linux的学习之路:9、冯诺依曼与进程(1)

摘要 本章主要是说一下冯诺依曼体系结构和进程的一部分东西。 目录 摘要 一、冯诺依曼体系结构 二、操作系统的概念 三、设计OS的目的 四、管理 五、进程的基本概念 六、PCB 七、在Linux环境下查看进程 八、使用代码创建进程 九、思维导图 一、冯诺依曼体系结构 如…

【每日一算】冒泡算法

冒泡算法就是给数据排序的意思。比如说升序&#xff0c;17&#xff0c;8&#xff0c;9&#xff0c;28&#xff0c;5.升序之后的结果就是5&#xff0c;8&#xff0c;9&#xff0c;17&#xff0c;28. 从我们的大脑思维来看&#xff0c;结果一眼就有了&#xff0c;可是机器要怎么才…