欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 健康 > 养生 > Java常见问题(一)

Java常见问题(一)

2025/2/22 16:45:31 来源:https://blog.csdn.net/wjj20040809/article/details/145713174  浏览:    关键词:Java常见问题(一)

1.Java中的final、finally和finalize有什么区别?

1.1 final

final 是一个关键字,用于修饰类、方法和变量,表示“不可改变的”。

用法:

修饰变量:表示变量一旦赋值后,其值不能被修改(常量)。

final int x = 10;
// x = 20; // 编译错误,x 不可修改

修饰方法:表示方法不能被子类重写。

class Parent {final void display() {System.out.println("This is a final method.");}
}class Child extends Parent {// void display() { } // 编译错误,不能重写 final 方法
}

修饰类:表示类不能被继承。

final class FinalClass { }
// class SubClass extends FinalClass { } // 编译错误,不能继承 final 类
1.2 finally

finally 是一个关键字,用于异常处理中的 try-catch-finally 块。无论是否发生异常,finally 块中的代码都会执行。

用法:

try {// 可能抛出异常的代码int result = 10 / 0;
} catch (ArithmeticException e) {System.out.println("Exception caught: " + e.getMessage());
} finally {System.out.println("This will always execute.");
}//示例
public int testFinally() {try {return 1;} catch (Exception e) {return 2;} finally {return 3; // 最终返回 3}
}

特点

  • finally 块通常用于释放资源(如关闭文件、数据库连接等)。

  • 即使 try 或 catch 块中有 return 语句,finally 块也会执行。

  • 如果 finally 块中有 return 语句,它会覆盖 try 或 catch 中的 return

1.3 finalize

finalize 是 Object 类中的一个方法,用于在垃圾回收器回收对象之前执行一些清理操作。

用法:

class MyClass {@Overrideprotected void finalize() throws Throwable {try {System.out.println("Finalize method called.");} finally {super.finalize();}}
}public class Main {public static void main(String[] args) {MyClass obj = new MyClass();obj = null; // 使对象成为垃圾System.gc(); // 建议 JVM 进行垃圾回收}
}

特点

  • finalize 方法在对象被垃圾回收之前调用。

  • 它通常用于释放非内存资源(如文件句柄、网络连接等)。

  • finalize 的执行时间不确定,甚至可能永远不会执行,因此不推荐依赖它来释放关键资源。

注意事项

  • Java 9 开始,finalize 方法被标记为 deprecated,推荐使用 Cleaner 或 PhantomReference 替代。

总结
  • final:用于定义不可变的变量、方法或类。

  • finally:用于确保某些代码无论是否发生异常都会执行。

  • finalize:用于对象销毁前的清理操作,但不推荐使用。

2. Java中的== 和equals() 方法有什么区别?

2.1 ==

== 是一个比较运算符,用于比较两个变量的值。

行为:

对于基本数据类型(如 intcharboolean 等):比较的是它们的

int a = 10;
int b = 10;
System.out.println(a == b); // true,因为值相等

对于引用数据类型(如对象、数组等):比较的是它们的内存地址(即是否指向同一个对象)。

String s1 = new String("hello");
String s2 = new String("hello");
System.out.println(s1 == s2); // false,因为 s1 和 s2 指向不同的对象
2.2 equals() 方法

equals() 是 Object 类中的一个方法,用于比较两个对象的内容是否相等。

行为:

默认实现:在 Object 类中,equals() 方法的行为与 == 相同,比较的是内存地址。

public boolean equals(Object obj) {return (this == obj);
}

重写实现:大多数 Java 类(如 StringInteger 等)都重写了 equals() 方法,用于比较对象的内容

String s1 = new String("hello");
String s2 = new String("hello");
System.out.println(s1.equals(s2)); // true,因为内容相等

重写 equals() 的规则

  1. 自反性x.equals(x) 必须返回 true

  2. 对称性:如果 x.equals(y) 返回 true,那么 y.equals(x) 也必须返回 true

  3. 传递性:如果 x.equals(y) 和 y.equals(z) 都返回 true,那么 x.equals(z) 也必须返回 true

  4. 一致性:多次调用 x.equals(y) 应该始终返回相同的结果。

  5. 非空性x.equals(null) 必须返回 false

//示例
class Person {private String name;private int age;public Person(String name, int age) {this.name = name;this.age = age;}@Overridepublic boolean equals(Object obj) {if (this == obj) return true; // 如果是同一个对象if (obj == null || getClass() != obj.getClass()) return false; // 如果对象为空或类型不同Person person = (Person) obj;return age == person.age && name.equals(person.name); // 比较内容}
}
特性==equals()
类型运算符方法
比较对象基本类型和引用类型引用类型
比较内容基本类型:值;引用类型:地址对象的内容(需重写 equals()
默认行为比较值或地址比较地址(除非重写)

注意事项:

  1. 字符串比较

    • 使用 == 比较字符串时,可能会因为字符串常量池的优化导致意外的结果。

    • 推荐使用 equals() 比较字符串内容。

      String s1 = "hello";
      String s2 = "hello";
      System.out.println(s1 == s2); // true,因为字符串常量池优化
  2. 重写 equals() 时必须重写 hashCode()

    • 如果两个对象通过 equals() 比较相等,那么它们的 hashCode() 也必须相等。

    • 这是为了确保在使用哈希表(如 HashMap)时的一致性。

总结

  • ==:比较值(基本类型)或内存地址(引用类型)。

  • equals():比较对象的内容(需重写 equals() 方法)。

  • 在大多数情况下,引用类型的比较应该使用 equals(),而不是 ==

3.Java中的String为什么是不可变的?

        在 Java 中,String 是不可变的(immutable),这意味着一旦一个 String 对象被创建,它的值就不能被修改。这种设计是 Java 语言的核心特性之一,具有重要的优点和意义。

3.1 什么是不可变性?
  • 不可变对象:对象的状态(数据)在创建后不能被修改。

  • String 的不可变性:一旦一个 String 对象被创建,它的字符序列(内容)就不能被改变。任何对 String 的修改操作(如拼接、替换等)都会创建一个新的 String 对象。

3.2 String不可变的原因

(1)安全性

  • 字符串常量池:Java 使用字符串常量池(String Pool)来存储字符串字面量。如果 String 是可变的,那么多个引用指向同一个字符串时,修改一个引用会导致其他引用的值也被修改,从而引发安全问题。

String s1 = "hello";
String s2 = "hello"; // s1 和 s2 指向字符串常量池中的同一个对象
// 如果 String 是可变的,修改 s1 会影响 s2//安全性示例:
String username = "admin";
String query = "SELECT * FROM users WHERE username = '" + username + "'";
// 如果 String 是可变的,恶意代码可能会修改 username 的值,导致 SQL 注入

(2)线程安全

  • 不可变对象天生是线程安全的,因为它们的状态不能被修改。多个线程可以共享同一个 String 对象,而不需要额外的同步机制。

String s = "hello";
// 多个线程可以安全地共享 s,无需担心数据竞争
(3)哈希码缓存
  • String 类重写了 hashCode() 方法,用于计算字符串的哈希值。由于 String 是不可变的,它的哈希值在创建时就可以计算并缓存,后续调用 hashCode() 时可以直接返回缓存值,提高性能。

String s = "hello";
int hash = s.hashCode(); // 哈希值被缓存
(4)字符串常量池优化
  • Java 使用字符串常量池来存储字符串字面量。如果 String 是可变的,那么常量池中的字符串可能会被修改,导致其他引用该字符串的代码出现意外行为。

String s1 = "hello";
String s2 = "hello"; // s1 和 s2 指向常量池中的同一个对象
// 如果 String 是可变的,修改 s1 会影响 s2
(5)性能优化
  • 不可变性使得编译器可以进行一些优化,例如字符串字面量的复用。

  • 例如,String 的 substring()concat() 等方法可以共享原始字符串的字符数组,而不需要复制数据。

3.3 String不可变的实现
(1)String 类的定义

String 类的核心字段是一个 final 的字符数组 value,它在对象创建后不能被修改。

public final class String implements java.io.Serializable, Comparable<String>, CharSequence {private final char value[]; // 存储字符串内容的字符数组private int hash; // 缓存哈希值// 其他方法...
}
(2)final 关键字
  • String 类被声明为 final,防止被继承和重写。

  • value 数组被声明为 final,防止被重新赋值。

(3)修改操作创建新对象

任何对 String 的修改操作(如拼接、替换等)都会创建一个新的 String 对象,而不是修改原始对象。

String s1 = "hello";
String s2 = s1.concat(" world"); // 创建一个新的 String 对象
System.out.println(s1); // 输出 "hello"(原始对象未被修改)
System.out.println(s2); // 输出 "hello world"
总结
  • String 不可变的原因:安全性、线程安全、哈希码缓存、字符串常量池优化、性能优化。

  • 不可变性的优点:安全、线程安全、性能优化。

  • 不可变性的缺点:内存开销、性能问题(可通过 StringBuilder 或 StringBuffer 解决)。

4.Java中的 StringBuilder 和 StringBuffer 有什么区别?

  StringBuilder 和 StringBuffer 是 Java 中用于处理可变字符串的两个类,它们的主要区别在于线程安全性和性能。

4.1 线程安全性
  • StringBuffer:是线程安全的,它的方法大多使用 synchronized 关键字进行同步,因此多个线程可以安全地操作同一个 StringBuffer 对象。

  • StringBuilder:不是线程安全的,它的方法没有使用同步机制,因此在多线程环境下使用可能会导致数据不一致的问题。

4.2 性能
  • StringBuilder:由于没有同步开销,StringBuilder 的性能通常比 StringBuffer 更高。在单线程环境下,推荐使用 StringBuilder

  • StringBuffer:由于同步机制的存在,StringBuffer 的性能相对较低。只有在多线程环境下需要确保线程安全时,才推荐使用 StringBuffer

4.3 使用场景
  • 单线程环境:使用 StringBuilder,因为它性能更好。

  • 多线程环境:使用 StringBuffer,因为它提供了线程安全的操作。

4.4 共同点
  • 两者都继承自 AbstractStringBuilder 类。

  • 两者都提供了类似的方法,如 append()insert()delete()reverse() 等。

//示例代码
// 使用 StringBuilder
StringBuilder sb = new StringBuilder();
sb.append("Hello");
sb.append(" World");
System.out.println(sb.toString()); // 输出: Hello World// 使用 StringBuffer
StringBuffer sbf = new StringBuffer();
sbf.append("Hello");
sbf.append(" World");
System.out.println(sbf.toString()); // 输出: Hello World
总结
  • StringBuilder:适用于单线程环境,性能更高。

  • StringBuffer:适用于多线程环境,线程安全但性能较低。

5.Java中的 ArrayList 和 LinkedList 有什么区别?

        在Java中,ArrayList 和 LinkedList 是两种常用的集合类,它们都实现了 List 接口,但底层实现和性能特点有所不同。以下是它们的主要区别:

5.1 底层数据结构
  • ArrayList:基于动态数组实现。数组在内存中是连续存储的,因此可以通过索引快速访问元素。

  • LinkedList:基于双向链表实现。链表中的每个元素(节点)都包含数据和指向前后节点的指针,因此在内存中是非连续存储的。

5.2 访问性能
  • ArrayList:由于基于数组,支持随机访问,通过索引访问元素的时间复杂度为 O(1)

  • LinkedList:由于基于链表,访问元素需要从头或尾遍历链表,时间复杂度为 O(n)

5.3 插入和删除性能
  • ArrayList

    • 在末尾插入或删除元素的时间复杂度为 O(1)

    • 在中间或开头插入或删除元素时,需要移动后续元素,时间复杂度为 O(n)

  • LinkedList

    • 在任意位置插入或删除元素的时间复杂度为 O(1)(前提是已经定位到插入或删除的位置)。

    • 如果需要先找到插入或删除的位置,时间复杂度为 O(n)

5.4 内存占用
  • ArrayList:内存占用较少,因为只需要存储元素和数组的容量。

  • LinkedList:内存占用较多,因为每个节点都需要存储前后节点的指针。

5.5 适用场景
  • ArrayList

    • 适合频繁访问元素的场景。

    • 适合在列表末尾进行插入和删除操作。

  • LinkedList

    • 适合频繁在列表中间或开头进行插入和删除操作。

    • 适合实现队列或栈等数据结构。

5.6 其他特性
  • ArrayList

    • 支持动态扩容,默认初始容量为10,扩容时容量增加50%。

    • 可以使用 trimToSize() 方法将容量调整为当前元素数量。

  • LinkedList

    • 实现了 Deque 接口,可以用作双端队列。

    • 提供了更多的方法,如 addFirst()addLast()removeFirst()removeLast() 等。

总结
  • 如果需要频繁访问元素,选择 ArrayList

  • 如果需要频繁在列表中间或开头插入或删除元素,选择 LinkedList

6. Java中的 HashMap 和 Hashtable 有什么区别?

        在Java中,HashMap 和 Hashtable 都是用于存储键值对的集合类,它们都实现了 Map 接口,但在实现细节和特性上有一些重要区别。以下是它们的主要区别:

6.1 线程安全性
  • HashMap非线程安全。在多线程环境下,如果没有额外的同步措施,可能会导致数据不一致。如果需要线程安全,可以使用 Collections.synchronizedMap(new HashMap<>()) 或者使用 ConcurrentHashMap

  • Hashtable线程安全。它的方法都是同步的(使用 synchronized 关键字),因此在多线程环境下可以直接使用,但性能较低。

6.2 性能
  • HashMap:由于没有同步开销,性能通常比 Hashtable 高。

  • Hashtable:由于方法都是同步的,性能较低,尤其是在高并发环境下。

6.3 Null 键和 Null 值
  • HashMap:允许 一个 null 键和多个 null 值。

  • Hashtable不允许 null 键或 null 值,否则会抛出 NullPointerException

6.4 继承和实现
  • HashMap:继承自 AbstractMap 类,实现了 Map 接口。

  • Hashtable:继承自 Dictionary 类,实现了 Map 接口。

6.5 初始容量和扩容机制
  • HashMap:默认初始容量为 16,扩容时容量变为原来的 2 倍

  • Hashtable:默认初始容量为 11,扩容时容量变为原来的 2 倍加 1

6.6 迭代器
  • HashMap:使用 Iterator 进行遍历,并且是 fail-fast 的(如果在迭代过程中集合被修改,会抛出 ConcurrentModificationException)。

  • Hashtable:使用 Enumeration 进行遍历,不是 fail-fast 的。

6.7 使用场景
  • HashMap:适用于大多数场景,尤其是在单线程环境下。如果需要线程安全,可以使用 ConcurrentHashMap

  • Hashtable:由于其同步特性,适用于需要线程安全的场景,但通常推荐使用 ConcurrentHashMap,因为它的性能更好。

6.8 历史
  • Hashtable:是 Java 早期版本中的类,属于遗留类。

  • HashMap:是 Java 集合框架的一部分,设计更加现代和灵活。

总结
  • 如果需要线程安全的 Map,推荐使用 ConcurrentHashMap 而不是 Hashtable

  • 在单线程环境下,HashMap 是更好的选择,因为它性能更高且更灵活。

7. Java中的 HashSet 和 TreeSet 有什么区别?

        在Java中,HashSet 和 TreeSet 都是实现了 Set 接口的集合类,用于存储不重复的元素。它们的底层实现和特性有显著区别,以下是它们的主要区别:


7.1 底层数据结构
  • HashSet

    • 基于 哈希表HashMap)实现。

    • 使用哈希算法来存储元素,元素的存储顺序与插入顺序无关。

  • TreeSet

    • 基于 红黑树(一种自平衡的二叉搜索树)实现。

    • 元素按照自然顺序或自定义比较器排序。

7.2 元素顺序
  • HashSet

    • 不保证元素的存储顺序,元素的顺序可能与插入顺序不一致。

    • 由于基于哈希表,元素的顺序取决于哈希函数和哈希桶的分布。

  • TreeSet

    • 元素按照升序排序(自然顺序或自定义比较器)。

    • 支持有序遍历。

7.3 性能
  • HashSet

    • 添加、删除和查找操作的平均时间复杂度为 O(1)

    • 性能受哈希冲突的影响,但在理想情况下性能非常高。

  • TreeSet

    • 添加、删除和查找操作的平均时间复杂度为 O(log n),因为需要维护红黑树的平衡。

    • 性能比 HashSet 稍低,但支持有序操作。

7.4 Null 值支持
  • HashSet

    • 允许存储 一个 null 值。

  • TreeSet

    • 不允许存储 null 值,否则会抛出 NullPointerException(因为需要比较元素大小,而 null 无法比较)。

7.5 排序
  • HashSet

    • 不支持排序,元素存储顺序不确定。

  • TreeSet

    • 支持自然排序(元素必须实现 Comparable 接口)或通过自定义 Comparator 排序。

7.6 线程安全性
  • HashSet 和 TreeSet 都是非线程安全的。如果需要在多线程环境下使用,可以使用 Collections.synchronizedSet() 方法包装它们,或者使用 ConcurrentSkipListSetTreeSet 的线程安全替代)。

7.7 使用场景
  • HashSet

    • 适用于需要快速查找、插入和删除的场景。

    • 不关心元素的顺序。

  • TreeSet

    • 适用于需要元素有序的场景。

    • 支持范围查找(如 subSet()headSet()tailSet() 等方法)。

//HashSet示例
Set<String> hashSet = new HashSet<>();
hashSet.add("Apple");
hashSet.add("Banana");
hashSet.add("Cherry");
System.out.println(hashSet); // 输出顺序不确定,可能是 [Apple, Cherry, Banana]
//TreeSet示例
Set<String> treeSet = new TreeSet<>();
treeSet.add("Apple");
treeSet.add("Banana");
treeSet.add("Cherry");
System.out.println(treeSet); // 输出顺序为自然排序 [Apple, Banana, Cherry]
总结
特性HashSetTreeSet
底层数据结构哈希表红黑树
元素顺序无序有序(自然排序或自定义排序)
性能O(1)(平均)O(log n)
Null 值支持允许一个 null不允许 null
排序支持不支持支持
使用场景快速查找、插入、删除需要有序集合

8. Java中的 static 关键字有什么作用?

        在Java中,static 是一个非常重要的关键字,用于修饰类的成员(变量、方法、代码块和内部类)。它的主要作用是使被修饰的成员与类本身关联,而不是与类的实例关联。

8.1 静态变量(Static Variables)
  • 作用:

    • 静态变量属于类,而不是类的实例。

    • 所有实例共享同一个静态变量。

    • 静态变量在类加载时初始化,且在程序运行期间只有一份内存空间。

//语法
static 数据类型 变量名;//示例
class Counter {static int count = 0; // 静态变量Counter() {count++; // 每次创建实例时,count 增加}
}public class Main {public static void main(String[] args) {Counter c1 = new Counter();Counter c2 = new Counter();System.out.println(Counter.count); // 输出 2}
}
8.2 静态方法(Static Methods)
  • 作用

    • 静态方法属于类,而不是类的实例。

    • 可以直接通过类名调用,无需创建类的实例。

    • 静态方法中不能直接访问非静态成员(变量或方法),因为非静态成员依赖于实例。

//语法
static 返回类型 方法名(参数列表) {// 方法体
}//示例
class MathUtils {static int add(int a, int b) {return a + b;}
}public class Main {public static void main(String[] args) {int result = MathUtils.add(5, 3); // 直接通过类名调用System.out.println(result); // 输出 8}
}
8.3 静态代码块(Static Block)
  • 作用

    • 静态代码块在类加载时执行,且只执行一次。

    • 通常用于初始化静态变量或执行一些只需要运行一次的操作。

//语法
static {// 代码块
}//示例
class Test {static int num;static {num = 10; // 初始化静态变量System.out.println("静态代码块执行");}
}public class Main {public static void main(String[] args) {System.out.println(Test.num); // 输出 10}
}
8.4 静态内部类(Static Nested Class)
  • 作用

    • 静态内部类是类的一个静态成员。

    • 它不依赖于外部类的实例,可以直接创建。

    • 静态内部类不能直接访问外部类的非静态成员。

//语法
class OuterClass {static class StaticNestedClass {// 类体}
}//示例
class Outer {static class Inner {void display() {System.out.println("静态内部类方法");}}
}public class Main {public static void main(String[] args) {Outer.Inner inner = new Outer.Inner(); // 直接创建静态内部类实例inner.display(); // 输出 "静态内部类方法"}
}
8.5 静态导入(Static Import)
  • 作用

    • 允许直接使用静态成员(变量或方法)而无需通过类名引用。

    • 可以提高代码的可读性,但过度使用可能导致命名冲突。

//语法
import static 包名.类名.静态成员;//示例
import static java.lang.Math.PI;
import static java.lang.Math.pow;public class Main {public static void main(String[] args) {double radius = 5.0;double area = PI * pow(radius, 2); // 直接使用 PI 和 powSystem.out.println("面积: " + area);}
}
8.6 静态成员的特点
  1. 类级别:静态成员属于类,而不是实例。

  2. 共享性:所有实例共享同一个静态成员。

  3. 生命周期:静态成员在类加载时初始化,在程序结束时销毁。

  4. 访问限制

    • 静态方法中不能直接访问非静态成员。

    • 非静态方法可以访问静态成员。

8.7 使用场景
  • 静态变量:用于存储类级别的数据,例如计数器、配置信息等。

  • 静态方法:用于工具类或不需要实例化的操作,例如 Math 类中的方法。

  • 静态代码块:用于初始化静态变量或加载资源。

  • 静态内部类:用于与外部类解耦,或者当内部类不需要访问外部类实例时。

总结
特性静态成员(static)非静态成员(实例成员)
所属对象实例
内存分配类加载时分配实例创建时分配
共享性所有实例共享每个实例独立
访问方式通过类名访问通过实例访问
生命周期类加载到程序结束实例创建到实例销毁

9.Java中的 this 和 super 关键字有什么区别?

        在Java中,this 和 super 是两个常用的关键字,它们都与对象的引用相关,但用途和行为有所不同。

9.1 this 关键字
  • 作用

    • 用于引用当前对象的实例。

    • 可以访问当前类的成员变量、方法和构造器。

  • 主要用途

    1. 区分成员变量和局部变量
      当成员变量与局部变量同名时,使用 this 明确引用成员变量。

      class Person {String name;Person(String name) {this.name = name; // 使用 this 区分成员变量和参数}
      }
    2. 调用当前类的其他构造器
      在一个构造器中调用另一个构造器,使用 this()

      class Person {String name;int age;Person(String name) {this.name = name;}Person(String name, int age) {this(name); // 调用当前类的其他构造器this.age = age;}
      }
    3. 传递当前对象
      将当前对象作为参数传递给其他方法。

class Printer {void print(Person person) {System.out.println(person.name);}
}
class Person {String name;void display(Printer printer) {printer.print(this); // 传递当前对象}
}
9.2 super 关键字
  • 作用

    • 用于引用父类的实例。

    • 可以访问父类的成员变量、方法和构造器。

  • 主要用途

    1. 调用父类的构造器
      在子类构造器中调用父类的构造器,使用 super()

      class Animal {String name;Animal(String name) {this.name = name;}
      }
      class Dog extends Animal {Dog(String name) {super(name); // 调用父类构造器}
      }
    2. 访问父类的成员变量或方法
      当子类重写了父类的方法或隐藏了父类的成员变量时,使用 super 访问父类的成员。

      class Animal {void sound() {System.out.println("Animal sound");}
      }
      class Dog extends Animal {void sound() {super.sound(); // 调用父类方法System.out.println("Dog sound");}
      }
9.3 this 和 super 的区别
特性thissuper
引用对象当前对象父类对象
访问范围当前类的成员变量、方法和构造器父类的成员变量、方法和构造器
调用构造器使用 this() 调用当前类的其他构造器使用 super() 调用父类的构造器
使用场景区分成员变量和局部变量、传递当前对象调用父类构造器、访问父类成员
是否必须第一行调用 this() 时必须在构造器的第一行调用 super() 时必须在构造器的第一行
9.4 注意事项
  1. 构造器中的调用顺序

    • 在子类构造器中,super() 或 this() 必须是第一行代码。

    • 如果子类构造器中没有显式调用 super() 或 this(),编译器会自动插入 super()(调用父类的无参构造器)。

    • 如果父类没有无参构造器,子类必须显式调用父类的有参构造器。

  2. 不能同时使用 this() 和 super()

    • 在一个构造器中,this() 和 super() 不能同时出现,因为它们都必须在第一行。

  3. 静态上下文

    • this 和 super 不能在静态方法或静态代码块中使用,因为它们依赖于实例。

9.5 示例对比
使用 this
class Person {String name;Person(String name) {this.name = name; // 使用 this 区分成员变量和参数}void display() {System.out.println("Name: " + this.name); // 使用 this 引用当前对象}
}
使用 super
class Animal {void sound() {System.out.println("Animal sound");}
}
class Dog extends Animal {void sound() {super.sound(); // 调用父类方法System.out.println("Dog sound");}
}
总结
  • this:用于引用当前对象,主要解决变量名冲突、调用当前类的其他构造器或传递当前对象。

  • super:用于引用父类对象,主要解决子类调用父类构造器或访问父类成员的问题。

10. Java中的 try-catch-finally 块是如何工作的?

        在Java中,try-catch-finally 是用于异常处理的关键结构。它的主要作用是捕获和处理代码中可能发生的异常,同时确保无论是否发生异常,某些必要的清理操作都能执行。

10.1 基本语法
try {// 可能抛出异常的代码
} catch (ExceptionType1 e1) {// 处理 ExceptionType1 类型的异常
} catch (ExceptionType2 e2) {// 处理 ExceptionType2 类型的异常
} finally {// 无论是否发生异常,都会执行的代码
}
10.2 各部分的作用
(1)try 块
  • 包含可能抛出异常的代码。

  • 如果在 try 块中发生异常,程序会立即跳转到匹配的 catch 块。

(2)catch 块
  • 用于捕获并处理特定类型的异常。

  • 可以有多个 catch 块,每个 catch 块处理一种类型的异常。

  • 异常类型从上到下匹配,因此应该将更具体的异常类型放在前面,更通用的异常类型放在后面。

(3)finally 块
  • 无论是否发生异常,finally 块中的代码都会执行。

  • 通常用于释放资源(如关闭文件、数据库连接等),确保资源不会被遗漏。

10.3 执行流程
  1. 正常执行

    • 如果 try 块中的代码没有抛出异常,程序会执行完 try 块后跳过所有 catch 块,直接执行 finally 块。

  2. 发生异常

    • 如果 try 块中的代码抛出异常,程序会立即跳转到匹配的 catch 块。

    • 执行完 catch 块后,程序会继续执行 finally 块。

  3. finally 块的强制性

    • 即使 try 或 catch 块中有 returnbreak 或 continue 语句,finally 块仍然会执行。

    • 如果 finally 块中有 return 语句,它会覆盖 try 或 catch 块中的 return 语句。

10.4 示例代码

示例 1:基本用法

public class Main {public static void main(String[] args) {try {int result = 10 / 0; // 抛出 ArithmeticExceptionSystem.out.println("结果: " + result);} catch (ArithmeticException e) {System.out.println("捕获异常: " + e.getMessage());} finally {System.out.println("finally 块执行");}}
}//输出:
//捕获异常: / by zero
//finally 块执行

示例 2:多个 catch 块

public class Main {public static void main(String[] args) {try {int[] arr = {1, 2, 3};System.out.println(arr[5]); // 抛出 ArrayIndexOutOfBoundsException} catch (ArithmeticException e) {System.out.println("捕获算术异常: " + e.getMessage());} catch (ArrayIndexOutOfBoundsException e) {System.out.println("捕获数组越界异常: " + e.getMessage());} finally {System.out.println("finally 块执行");}}
}//输出:
//捕获数组越界异常: Index 5 out of bounds for length 3
//finally 块执行

示例 3:finally 块的强制性

public class Main {public static void main(String[] args) {System.out.println("返回值: " + test());}static int test() {try {return 1; // 返回 1,但 finally 块会覆盖返回值} finally {return 2; // finally 块中的 return 会覆盖 try 块中的 return}}
}//输出
//返回值: 2
10.5 注意事项
  1. catch 块的顺序

    • 如果有多个 catch 块,应该将更具体的异常类型放在前面,更通用的异常类型(如 Exception)放在后面。

    • 如果 catch 块的顺序不正确,编译器会报错。

  2. finally 块的作用

    • 即使 try 或 catch 块中有 return 语句,finally 块仍然会执行。

    • 如果 finally 块中有 return 语句,它会覆盖 try 或 catch 块中的返回值。

总结
特性try 块catch 块finally 块
作用包含可能抛出异常的代码捕获并处理异常无论是否发生异常,都会执行
执行顺序优先执行发生异常时执行最后执行
资源管理通常用于释放资源
强制性即使有 return 也会执行

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词