Java高级特性:异常处理与集合框架
4.1 异常处理
4.1.1 学习目标与重点提示
学习目标:理解异常的概念,掌握异常的处理方法(try-catch-finally、throw、throws),了解常见的异常类型。
重点:try-catch-finally的执行流程、自定义异常的实现。
4.1.2 异常的概念
在程序运行过程中,可能会出现各种错误,如文件不存在、数组下标越界、除数为零等。这些错误会导致程序无法正常运行,甚至崩溃。Java将这些错误分为两类:错误(Error)和异常(Exception)。
- 错误(Error):是程序无法处理的严重错误,如内存溢出、线程死锁等。这些错误通常由Java虚拟机(JVM)抛出,程序无法通过代码处理。
- 异常(Exception):是程序可以处理的错误,如文件未找到、数组下标越界、除数为零等。这些异常通常由程序抛出,可以通过代码进行处理。
异常类的继承关系:
Throwable(根类) ├─ Error(错误) │ ├─ VirtualMachineError(虚拟机错误) │ │ ├─ OutOfMemoryError(内存溢出错误) │ │ └─ StackOverflowError(栈溢出错误) │ └─ AWTError(AWT错误) └─ Exception(异常) ├─ RuntimeException(运行时异常) │ ├─ NullPointerException(空指针异常) │ ├─ ArrayIndexOutOfBoundsException(数组下标越界异常) │ ├─ ArithmeticException(算术异常,如除数为零) │ └─ ClassCastException(类转换异常) └─ CheckedException(受检异常) ├─ IOException(输入输出异常) │ ├─ FileNotFoundException(文件未找到异常) │ └─ EOFException(文件结束异常) └─ SQLException(数据库异常)💡运行时异常与受检异常的区别:
- 运行时异常(RuntimeException):编译时不检查,程序运行时可能抛出。例如,NullPointerException、ArrayIndexOutOfBoundsException等。
- 受检异常(CheckedException):编译时必须处理,否则会导致编译错误。例如,FileNotFoundException、SQLException等。
4.1.3 异常的处理方法
Java提供了多种异常处理方法,包括:
try-catch-finally语句:用于捕获和处理异常。
try块:包含可能抛出异常的代码。catch块:用于捕获和处理try块中抛出的异常。可以有多个catch块,每个catch块处理一种类型的异常。finally块:无论try块中是否抛出异常,都会执行。通常用于释放资源(如关闭文件、释放数据库连接等)。
示例:
import java.io.File; import java.io.FileNotFoundException; import java.util.Scanner; public class TestException { public static void main(String[] args) { File file = new File("test.txt"); Scanner scanner = null; try { scanner = new Scanner(file); while (scanner.hasNextLine()) { System.out.println(scanner.nextLine()); } } catch (FileNotFoundException e) { e.printStackTrace(); System.out.println("文件未找到"); } finally { if (scanner != null) { scanner.close(); System.out.println("资源已释放"); } } } }执行流程:
- 如果try块中的代码正常执行,没有抛出异常,则执行finally块。
- 如果try块中的代码抛出异常,并且被catch块捕获,则执行catch块中的代码,然后执行finally块。
- 如果try块中的代码抛出异常,但没有被catch块捕获,则执行finally块,然后程序终止。
throw语句:用于手动抛出异常。
示例:public class TestThrow { public static void main(String[] args) { try { checkAge(15); } catch (IllegalArgumentException e) { e.printStackTrace(); System.out.println(e.getMessage()); } } public static void checkAge(int age) { if (age < 18) { throw new IllegalArgumentException("年龄必须大于等于18岁"); } System.out.println("年龄符合要求"); } }输出结果:
java.lang.IllegalArgumentException: 年龄必须大于等于18岁 at TestThrow.checkAge(TestThrow.java:14) at TestThrow.main(TestThrow.java:6) 年龄必须大于等于18岁throws语句:用于声明方法可能抛出的异常。
示例:import java.io.FileNotFoundException; import java.util.Scanner; public class TestThrows { public static void main(String[] args) { try { readFile("test.txt"); } catch (FileNotFoundException e) { e.printStackTrace(); System.out.println("文件未找到"); } } public static void readFile(String fileName) throws FileNotFoundException { File file = new File(fileName); Scanner scanner = new Scanner(file); while (scanner.hasNextLine()) { System.out.println(scanner.nextLine()); } scanner.close(); } }
4.1.4 自定义异常
如果Java提供的异常类不能满足需求,可以自定义异常类。自定义异常类通常继承自Exception(受检异常)或RuntimeException(运行时异常)。
示例:自定义一个受检异常类
public class MyCheckedException extends Exception { public MyCheckedException() { super(); } public MyCheckedException(String message) { super(message); } public MyCheckedException(String message, Throwable cause) { super(message, cause); } public MyCheckedException(Throwable cause) { super(cause); } }使用自定义异常类:
public class TestMyCheckedException { public static void main(String[] args) { try { checkScore(85); checkScore(120); } catch (MyCheckedException e) { e.printStackTrace(); System.out.println(e.getMessage()); } } public static void checkScore(int score) throws MyCheckedException { if (score < 0 || score > 100) { throw new MyCheckedException("分数必须在0到100之间:" + score); } System.out.println("分数符合要求:" + score); } }输出结果:
分数符合要求:85 com.example.MyCheckedException: 分数必须在0到100之间:120 at TestMyCheckedException.checkScore(TestMyCheckedException.java:18) at TestMyCheckedException.main(TestMyCheckedException.java:8) 分数必须在0到100之间:1204.2 集合框架
4.2.1 学习目标与重点提示
学习目标:理解集合框架的基本概念,掌握常用集合类(List、Set、Map)的使用方法,了解集合的遍历和排序。
重点:List、Set、Map的区别、集合的遍历方法。
4.2.2 集合框架的基本概念
Java的集合框架(Collection Framework)是一组用于存储和操作数据的接口和类,位于java.util包中。集合框架的核心接口包括:
- Collection:用于存储一组对象,分为List和Set两个子接口。
- List:有序的集合,允许重复元素。
- Set:无序的集合,不允许重复元素。
- Map:用于存储键值对,键不允许重复,值可以重复。
集合框架的常用类:
- List:ArrayList、LinkedList、Vector
- Set:HashSet、TreeSet、LinkedHashSet
- Map:HashMap、TreeMap、LinkedHashMap、Hashtable
💡集合框架与数组的区别:
| 对比项 | 数组 | 集合框架 |
|---|---|---|
| 存储类型 | 可以存储基本数据类型和对象 | 只能存储对象 |
| 长度 | 固定长度 | 动态长度 |
| 操作方法 | 提供的操作方法较少,需要手动实现 | 提供了丰富的操作方法,如添加、删除、查找、排序等 |
4.2.3 List接口
List是有序的集合,允许重复元素。常用的List类包括ArrayList和LinkedList。
- ArrayList:基于动态数组实现,查询速度快,插入和删除速度慢。
- LinkedList:基于双向链表实现,查询速度慢,插入和删除速度快。
示例:使用ArrayList和LinkedList
import java.util.ArrayList; import java.util.LinkedList; import java.util.List; public class TestList { public static void main(String[] args) { // 使用ArrayList List<String> arrayList = new ArrayList<>(); arrayList.add("张三"); arrayList.add("李四"); arrayList.add("王五"); arrayList.add("张三"); // 允许重复元素 System.out.println("ArrayList的元素:" + arrayList); // 使用LinkedList List<String> linkedList = new LinkedList<>(); linkedList.add("张三"); linkedList.add("李四"); linkedList.add("王五"); linkedList.add("张三"); // 允许重复元素 System.out.println("LinkedList的元素:" + linkedList); // 遍历List System.out.println("\n遍历ArrayList的元素:"); for (String name : arrayList) { System.out.println(name); } System.out.println("\n遍历LinkedList的元素:"); for (int i = 0; i < linkedList.size(); i++) { System.out.println(linkedList.get(i)); } } }输出结果:
ArrayList的元素:[张三, 李四, 王五, 张三] LinkedList的元素:[张三, 李四, 王五, 张三] 遍历ArrayList的元素: 张三 李四 王五 张三 遍历LinkedList的元素: 张三 李四 王五 张三4.2.4 Set接口
Set是无序的集合,不允许重复元素。常用的Set类包括HashSet、TreeSet和LinkedHashSet。
- HashSet:基于哈希表实现,插入、删除和查询速度快,无序。
- TreeSet:基于红黑树实现,插入、删除和查询速度较慢,但有序(按自然顺序或自定义顺序排序)。
- LinkedHashSet:基于哈希表和链表实现,插入、删除和查询速度较快,有序(按插入顺序排序)。
示例:使用HashSet、TreeSet和LinkedHashSet
import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Set; import java.util.TreeSet; public class TestSet { public static void main(String[] args) { // 使用HashSet Set<String> hashSet = new HashSet<>(); hashSet.add("张三"); hashSet.add("李四"); hashSet.add("王五"); hashSet.add("张三"); // 不允许重复元素 System.out.println("HashSet的元素:" + hashSet); // 使用TreeSet Set<String> treeSet = new TreeSet<>(); treeSet.add("张三"); treeSet.add("李四"); treeSet.add("王五"); treeSet.add("张三"); // 不允许重复元素 System.out.println("TreeSet的元素:" + treeSet); // 使用LinkedHashSet Set<String> linkedHashSet = new LinkedHashSet<>(); linkedHashSet.add("张三"); linkedHashSet.add("李四"); linkedHashSet.add("王五"); linkedHashSet.add("张三"); // 不允许重复元素 System.out.println("LinkedHashSet的元素:" + linkedHashSet); // 遍历Set System.out.println("\n遍历HashSet的元素:"); for (String name : hashSet) { System.out.println(name); } } }输出结果:
HashSet的元素:[张三, 李四, 王五] TreeSet的元素:[张三, 李四, 王五] LinkedHashSet的元素:[张三, 李四, 王五] 遍历HashSet的元素: 张三 李四 王五⚠️ 注意:HashSet的输出顺序是无序的,TreeSet的输出顺序是按自然顺序(字符串的字典序)排序的,LinkedHashSet的输出顺序是按插入顺序排序的。
4.2.5 Map接口
Map是用于存储键值对的集合,键不允许重复,值可以重复。常用的Map类包括HashMap、TreeMap和LinkedHashMap。
- HashMap:基于哈希表实现,插入、删除和查询速度快,无序。
- TreeMap:基于红黑树实现,插入、删除和查询速度较慢,但有序(按键的自然顺序或自定义顺序排序)。
- LinkedHashMap:基于哈希表和链表实现,插入、删除和查询速度较快,有序(按插入顺序排序)。
示例:使用HashMap、TreeMap和LinkedHashMap
import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import java.util.TreeMap; public class TestMap { public static void main(String[] args) { // 使用HashMap Map<String, Integer> hashMap = new HashMap<>(); hashMap.put("张三", 18); hashMap.put("李四", 20); hashMap.put("王五", 22); hashMap.put("张三", 19); // 键重复,会覆盖原来的值 System.out.println("HashMap的元素:" + hashMap); // 使用TreeMap Map<String, Integer> treeMap = new TreeMap<>(); treeMap.put("张三", 18); treeMap.put("李四", 20); treeMap.put("王五", 22); treeMap.put("张三", 19); // 键重复,会覆盖原来的值 System.out.println("TreeMap的元素:" + treeMap); // 使用LinkedHashMap Map<String, Integer> linkedHashMap = new LinkedHashMap<>(); linkedHashMap.put("张三", 18); linkedHashMap.put("李四", 20); linkedHashMap.put("王五", 22); linkedHashMap.put("张三", 19); // 键重复,会覆盖原来的值 System.out.println("LinkedHashMap的元素:" + linkedHashMap); // 遍历Map System.out.println("\n遍历HashMap的元素:"); for (Map.Entry<String, Integer> entry : hashMap.entrySet()) { System.out.println("姓名:" + entry.getKey() + ",年龄:" + entry.getValue()); } System.out.println("\n遍历HashMap的键:"); for (String key : hashMap.keySet()) { System.out.println("姓名:" + key); } System.out.println("\n遍历HashMap的值:"); for (Integer value : hashMap.values()) { System.out.println("年龄:" + value); } } }输出结果:
HashMap的元素:{张三=19, 李四=20, 王五=22} TreeMap的元素:{张三=19, 李四=20, 王五=22} LinkedHashMap的元素:{张三=19, 李四=20, 王五=22} 遍历HashMap的元素: 姓名:张三,年龄:19 姓名:李四,年龄:20 姓名:王五,年龄:22 遍历HashMap的键: 姓名:张三 姓名:李四 姓名:王五 遍历HashMap的值: 年龄:19 年龄:20 年龄:224.2.6 集合的遍历与排序
遍历集合:
- List:可以使用for循环、增强for循环或迭代器(Iterator)遍历。
- Set:可以使用增强for循环或迭代器遍历。
- Map:可以遍历键值对(entrySet)、键(keySet)或值(values)。
排序集合:
- List:使用
Collections.sort()方法排序,该方法可以对实现了Comparable接口的对象进行排序,或者使用Comparator接口自定义排序。 - Set:TreeSet和LinkedHashSet可以自动排序,HashSet需要转换为List后排序。
- Map:TreeMap可以自动排序,HashMap和LinkedHashMap需要转换为List后排序。
- List:使用
示例:对List进行排序
import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; public class TestSort { public static void main(String[] args) { List<Integer> nums = new ArrayList<>(); nums.add(5); nums.add(2); nums.add(8); nums.add(1); nums.add(9); System.out.println("排序前:" + nums); // 自然排序(升序) Collections.sort(nums); System.out.println("自然排序(升序):" + nums); // 自定义排序(降序) Collections.sort(nums, new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return o2 - o1; } }); System.out.println("自定义排序(降序):" + nums); } }输出结果:
排序前:[5, 2, 8, 1, 9] 自然排序(升序):[1, 2, 5, 8, 9] 自定义排序(降序):[9, 8, 5, 2, 1]4.3 常见问题与解决方案
4.3.1 异常未捕获
问题:程序抛出异常但未捕获,导致程序崩溃。
解决方案:使用try-catch语句捕获异常,或者在方法声明中使用throws语句声明可能抛出的异常。例如:
public class TestUncheckedException { public static void main(String[] args) { int[] nums = {1, 2, 3}; System.out.println(nums[3]); // 数组下标越界异常,但未捕获 } }输出结果:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 3 at TestUncheckedException.main(TestUncheckedException.java:5)修改后的代码:
public class TestUncheckedException { public static void main(String[] args) { int[] nums = {1, 2, 3}; try { System.out.println(nums[3]); } catch (ArrayIndexOutOfBoundsException e) { e.printStackTrace(); System.out.println("数组下标越界"); } } }输出结果:
java.lang.ArrayIndexOutOfBoundsException: 3 at TestUncheckedException.main(TestUncheckedException.java:7) 数组下标越界4.3.2 集合中存储的对象没有重写equals()和hashCode()方法
问题:当集合中存储的是自定义对象时,如果没有重写equals()和hashCode()方法,可能会导致无法正确判断对象是否相等。
解决方案:重写equals()和hashCode()方法。例如:
public class Student { private String id; private String name; private int age; // 构造方法、getter和setter方法 @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Student student = (Student) o; if (age != student.age) return false; if (!id.equals(student.id)) return false; return name.equals(student.name); } @Override public int hashCode() { int result = id.hashCode(); result = 31 * result + name.hashCode(); result = 31 * result + age; return result; } }4.3.3 集合的并发修改异常
问题:在使用迭代器遍历集合时,同时修改集合的结构(如添加或删除元素),会导致ConcurrentModificationException异常。
解决方案:使用迭代器的remove()方法删除元素,或者使用ListIterator的add()方法添加元素。例如:
import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class TestConcurrentModificationException { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("张三"); list.add("李四"); list.add("王五"); // 错误的做法:使用for循环遍历并删除元素 for (String name : list) { if (name.equals("李四")) { list.remove(name); // 会导致ConcurrentModificationException } } // 正确的做法:使用迭代器遍历并删除元素 Iterator<String> iterator = list.iterator(); while (iterator.hasNext()) { String name = iterator.next(); if (name.equals("李四")) { iterator.remove(); } } System.out.println(list); } }输出结果:
[张三, 王五]总结
本章我们学习了Java的高级特性,包括异常处理和集合框架。其中,try-catch-finally的执行流程、自定义异常的实现、List/Set/Map的区别以及集合的遍历方法是本章的重点内容。从下一章开始,我们将学习Java的IO流、多线程等内容。
说真的,这两年看着身边一个个搞Java、C++、前端、数据、架构的开始卷大模型,挺唏嘘的。大家最开始都是写接口、搞Spring Boot、连数据库、配Redis,稳稳当当过日子。
结果GPT、DeepSeek火了之后,整条线上的人都开始有点慌了,大家都在想:“我是不是要学大模型,不然这饭碗还能保多久?”
先给出最直接的答案:一定要把现有的技术和大模型结合起来,而不是抛弃你们现有技术!掌握AI能力的Java工程师比纯Java岗要吃香的多。
即使现在裁员、降薪、团队解散的比比皆是……但后续的趋势一定是AI应用落地!大模型方向才是实现职业升级、提升薪资待遇的绝佳机遇!
如何学习AGI大模型?
作为一名热心肠的互联网老兵,我决定把宝贵的AI知识分享给大家。 至于能学习到多少就看你的学习毅力和能力了 。我已将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。
因篇幅有限,仅展示部分资料,需要点击下方链接即可前往获取
2025最新版CSDN大礼包:《AGI大模型学习资源包》免费分享**
一、2025最新大模型学习路线
一个明确的学习路线可以帮助新人了解从哪里开始,按照什么顺序学习,以及需要掌握哪些知识点。大模型领域涉及的知识点非常广泛,没有明确的学习路线可能会导致新人感到迷茫,不知道应该专注于哪些内容。
我们把学习路线分成L1到L4四个阶段,一步步带你从入门到进阶,从理论到实战。
L1级别:AI大模型时代的华丽登场
L1阶段:我们会去了解大模型的基础知识,以及大模型在各个行业的应用和分析;学习理解大模型的核心原理,关键技术,以及大模型应用场景;通过理论原理结合多个项目实战,从提示工程基础到提示工程进阶,掌握Prompt提示工程。
L2级别:AI大模型RAG应用开发工程
L2阶段是我们的AI大模型RAG应用开发工程,我们会去学习RAG检索增强生成:包括Naive RAG、Advanced-RAG以及RAG性能评估,还有GraphRAG在内的多个RAG热门项目的分析。
L3级别:大模型Agent应用架构进阶实践
L3阶段:大模型Agent应用架构进阶实现,我们会去学习LangChain、 LIamaIndex框架,也会学习到AutoGPT、 MetaGPT等多Agent系统,打造我们自己的Agent智能体;同时还可以学习到包括Coze、Dify在内的可视化工具的使用。
L4级别:大模型微调与私有化部署
L4阶段:大模型的微调和私有化部署,我们会更加深入的探讨Transformer架构,学习大模型的微调技术,利用DeepSpeed、Lamam Factory等工具快速进行模型微调;并通过Ollama、vLLM等推理部署框架,实现模型的快速部署。
整个大模型学习路线L1主要是对大模型的理论基础、生态以及提示词他的一个学习掌握;而L3 L4更多的是通过项目实战来掌握大模型的应用开发,针对以上大模型的学习路线我们也整理了对应的学习视频教程,和配套的学习资料。
二、大模型经典PDF书籍
书籍和学习文档资料是学习大模型过程中必不可少的,我们精选了一系列深入探讨大模型技术的书籍和学习文档,它们由领域内的顶尖专家撰写,内容全面、深入、详尽,为你学习大模型提供坚实的理论基础。(书籍含电子版PDF)
三、大模型视频教程
对于很多自学或者没有基础的同学来说,书籍这些纯文字类的学习教材会觉得比较晦涩难以理解,因此,我们提供了丰富的大模型视频教程,以动态、形象的方式展示技术概念,帮助你更快、更轻松地掌握核心知识。
四、大模型项目实战
学以致用,当你的理论知识积累到一定程度,就需要通过项目实战,在实际操作中检验和巩固你所学到的知识,同时为你找工作和职业发展打下坚实的基础。
五、大模型面试题
面试不仅是技术的较量,更需要充分的准备。
在你已经掌握了大模型技术之后,就需要开始准备面试,我们将提供精心整理的大模型面试题库,涵盖当前面试中可能遇到的各种技术问题,让你在面试中游刃有余。
因篇幅有限,仅展示部分资料,需要点击下方链接即可前往获取
2025最新版CSDN大礼包:《AGI大模型学习资源包》免费分享