第四章 异常处理
1. 异常:认识异常
2. 异常:自定义异常、异常的作用
年龄非法异常类(运行时异常):
package com.itheima.exception;
/**
* @ClassName AgeIllegalRuntimeException
* @Description 年龄非法运行时异常
* @Author 孙克旭
* @Date 2025/3/25 18:50
*/
public class AgeIllegalRuntimeException extends RuntimeException {
public AgeIllegalRuntimeException() {
}
public AgeIllegalRuntimeException(String message) {
super(message);
}
}
年龄非法异常类(编译时异常):
package com.itheima.exception;
/**
* @ClassName AgeIllegalException
* @Description 年龄非法编译时异常
* @Author 孙克旭
* @Date 2025/3/25 18:50
*/
public class AgeIllegalException extends Exception {
public AgeIllegalException() {
}
public AgeIllegalException(String message) {
super(message);
}
}
测试类:
package com.itheima.exception;
public class Demo1 {
public static void main(String[] args) {
try {
saveAge(160);
} catch (Exception e) {
e.printStackTrace();
}
try {
saveAge2(160);
} catch (AgeIllegalException e) {
e.printStackTrace();
}
}
/**
* 保存年龄
*
* @param i
*/
private static void saveAge(int i) {
if (i >= 0 && i <= 150) {
System.out.println("年龄保存成功!");
} else { //抛出异常
throw new AgeIllegalRuntimeException("/age is illegal:" + i);
}
}
private static void saveAge2(int i) throws AgeIllegalException {
if (i >= 0 && i <= 150) {
System.out.println("年龄保存成功!");
} else { //抛出异常
throw new AgeIllegalException("/age is illegal:" + i);
}
}
}
3. 异常:异常的常见处理方式一
4. 异常:异常的常见处理方式二
5. 课后练习
5.1 问题1
- 请描述异常的继承体系
- 请描述你对错误(Error)的理解
- 请描述你对异常(Expection的理解)
- 请描述你对运行时异常(RuntimeException)的理解
1. 异常继承体系为:异常的根类是 java.lang.Throwable,其下有两个子类:
java.lang.Error 与 java.util.Exception 。而Exception又分为编译时期异常:checked异常,与运行时期异常:runtime异常。
2. Error:表示不可修复的恶性的错误,只能通过修改代码规避错误的产生,通常是系统级别的,所以很严重。
3. Exception:表示可修复的良性(相对于错误)的异常,异常产生后程序员可以并且应该通过代码的方式纠正,使程序继续运行,是必须要处理的。
4. 运行时期异常:runtime异常。在运行时期,检查异常.在编译时期,运行异常不会编译器检测(不报错)
5.2 问题2
- 请描述throw的使用位置,作用是什么?
- 请描述throws的使用位置,作用是什么?
1. throw关键字通常用在方法体中,并且抛出一个异常对象。程序在执行到throw语句时立即停止,它后面的语句都不执行。
2. throws关键字通常被应用在声明方法时,用来指定可能抛出的异常。多个异常可以使用逗号隔开。当在主函数中调用该方法时,如果发生异常,就会将异常对象抛给方法调用处。
5.3 问题3
- 异常处理方式有几种,分别是什么?
- 详细阐述每种方式对异常是如何处理的
1. 异常的处理方式有两种,分别是使用throws和try...catch
2. throws用在方法的声明上后接异常类名,是把异常抛给调用者进行处理
3. try...catch是捕获异常,自己处理,处理完毕后面的程序可以继续运行
a) try代码块中是可能出现异常的代码
b) catch代码块,是遇到异常,对异常进行处理的代码
5.4 问题4
请列举常见异常,并说明产生原因
NullPointerException:空指针异常。
当应用试图在要求使用对象的地方使用了null时,抛出该异常。譬如:调用null对象的实例方法、访问null对象的属性、计算null对象的长度等等。
ArrayIndexOutOfBoundsException:数组索引越界异常。
当对数组的索引值为负数或大于等于数组大小时抛出此异常。
ArithmeticException:算术运算异常。
程序中出现了除以零这样的运算就会出这样的异常,对这种异常,大家就要好好检查一下自己程序中涉及到数学运算的地方,公式是不是有不妥了。
NumberFormatException:数字格式异常。
当试图将一个String转换为指定的数字类型,而该字符串确不满足数字类型要求的格式时,抛出该异常。
5.5 问题5
根据给出的相应代码,分析可能产生的结果。
1.举例:
public static void main(String[]args){
String str=null;
System.out.println(str.length());
}
2.举例:
public static void main(String[]args){
int arr[]={1,2};
System.out.println(arr[2]);
}
3.举例:
public static void main(String[]args){
System.out.println(1/0);
}
4.举例:
public static void main(String[]args){
System.out.println(Integer.parseInt(“itcast”));
}
5.举例:
public static void main(String[] args) {
SimpleDateFormat format = new SimpleDateFormat(“yyyy-MM-dd”);
try {
Date date = format.parse("2018-04-03");
System.out.println("程序正常");
} catch (ParseException e) {
System.out.println("程序异常");
}
}
结合代码:1.举例:
public static void main(String[]args){
?String str=null;
System.out.println(str.length());
}
答:变量str的值为null,调用方法时,报空指针异常NullPointerException
2.举例:
public static void main(String[]args){
int arr[]={1,2};
System.out.println(arr[2]);
}
答:索引值2大于等于数组arr的长度时,报数组索引越界异常ArrayIndexOutOfBoundsException
3.举例:
public static void main(String[]args){
System.out.println(1/0);
}
答:整数0做了分母,报算术运算异常ArithmeticException:/by zero
4.举例:
public static void main(String[]args){
System.out.println(Integer.parseInt("itcast"));
}
答:把字符串“itcast”转换为Integer类型时,当然会报数字格式化异常啦NumberFormatException
5.举例:
public static void main(String[] args){
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
try {
Date date = format.parse("2018-04-03");
System.out.println("程序正常");
} catch (ParseException e) {
System.out.println("程序异常");
}
}
答: 打印结果“程序正常”.try代码块中并没有产生异常,catch代码块中的代码不会执行。date为2018年1月3日00点04分00秒。
5.6 问题6
请使用代码实现
每一个学生(Student)都有学号,姓名和分数,分数永远不能为负数
如果老师给学生赋值一个负数,抛出一个自定异常
1.定义异常类NoScoreException,继承RuntimeException
a)提供空参和有参构造方法
public class NoScoreException extends RuntimeException {
// 空参构造
public NoScoreException() {
super();
}
// 有参构造
public NoScoreException(String message) {
super(message);
}
}
2.定义学生类(Student)
a)属性:name,score
b)提供空参构造
c)提供有参构造;
i.使用setXxx方法给名称和score赋值
d)提供setter和getter方法
i.在setScore(int score)方法中
1.首先判断,如果score为负数,就抛出NoScoreException,异常信息为:分数不能为负数:xxx.
2.然后在给成员score赋值.
public class Student {
private String name;
private int score;
// 空参构造
public Student() {
super();
}
// c)提供有参构造;
// i.使用setXxx方法给名称和score赋值
public Student(String name,int score){
setName(name);
setScore(score);
}
// d)提供setter和getter方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getScore() {
return score;
}
// i.在setScore(int score)方法中
public void setScore(int score) {
// 1.首先判断,如果score为负数,就抛出NoScoreException,异常信息为:分数不能为负数:xxx.
if(score <0){
throw new NoScoreException(":分数不能为负数:"+score);
}
// 2.然后在给成员score赋值.
this.score = score;
}
}
3.定义测试类Test9
a)提供main方法,在main方法中
i.使用满参构造方法创建Student对象,分数传入一个负数,运行程序
ii.由于一旦遇到异常,后面的代码的将不在执行,所以需要注释掉上面的代码
iii.使用空参构造创建Student对象
iv.调用setScore(int score)方法,传入一个正数,运行程序
v.调用setScore(int score)方法,传入一个负数,运行程序
public class Test9 {
public static void main(String[] args) {
// i.使用满参构造方法创建Student对象,分数传入一个负数,运行程序
// Student s = new Student("景甜", -10);
// ii.由于一旦遇到异常,后面的代码的将不在执行,所以需要注释掉上面的代码
// iii.使用空参构造创建Student对象
Student s = new Student();
// iv.调用setScore(int score)方法,传入一个正数,运行程序
s.setScore(100);
// v.调用setScore(int score)方法,传入一个负数,运行程序
s.setScore(-5);
}
}
6. 单元练习14
6.1 什么是异常?
程序在运行过程中发生的意外情况,称为异常,如:除数为0、访问下标不存在的数组元素等
异常是一种信号,用于向调用者传递信息,表示程序发生了意外情况
程序运行时一旦出现了异常,将会导致程序立即终止,异常之后的代码将无法继续执行,所以需要对异常进行处理。
6.2 异常的作用是什么?
保证程序的稳定性和健壮性。异常是程序运行过程中的常态,可以及时发现并处理这些问题,防止程序崩溃或出现其他严重的后果。
提高代码的可读性和可维护性。通过异常的处理机制,可以将错误信息以更容易理解和维护的方式呈现给开发者,有利于代码的修改和优化。
提高程序的可靠性。异常可以被用来判断程序是否正常运行,以及记录程序中的错误信息,从而提高程序的可靠性和稳定性。
促进代码的重用和复用。通过捕获和处理异常,可以将程序中的错误信息和处理逻辑封装成一个独立的模块,方便其他代码重用和复用。
6.3 简述throw和throws的区别?
① throw用在方法内部,后面跟一个异常对象,用来抛出一个异常
② throws用在方法定义上,后面跟一个异常类型,用来给方法添加一个异常声明。告诉方法的调用者,该方法可能会出现异常。
6.4 编码题1
在一个程序中有1,2,3三个选项交给用户选择,用户输入其他选项时会给出友好提示,请设计一个程序模拟该过程
1.在测试类中,使用键盘录入让用户输入选项1,2,3,通过if语句判断分别给出提示:选项一;选项二;选项三;
2.若用户输入其他内容时抛出运行时异常,信息为:输入有误
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入你的选项:1 or 2 or 3");
String next = sc.next();
if (!(List.of("1", "2", "3").contains(next)))
throw new RuntimeException("你输入的选项有误。请重新输入");
if ("1".equals(next))
System.out.println("选项一");
if ("2".equals(next))
System.out.println("选项二");
if ("3".equals(next))
System.out.println("选项三");
}
6.5 编码题2
现在老师要统计学生信息,已知该班学生年龄在18-25之间,如果不在范围内,则输入错误,请编写代码模拟该过程
①创建一个学生类(name,age),对学生的年龄赋值进行限制,如果输入的学生年龄不在范围内,则抛出异常
②在测试类中完成创建学生对象,并对学生对象进行赋值,对学生的年龄输入一个非法数值,查看控制台输出
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if(age >= 18 && age <= 25){
this.age = age;
}else{
//当年龄不合法时,产生一个异常
throw new RuntimeException("年龄超出了范围");
}
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
6.6 编码题3
设计一个程序模拟计算机计算两个整数乘积的过程
①通过键盘录入让用户输入两个整数,如果用户输入的不是整数,则提示用户重新输入
②计算两个数的乘积,并将结果打印在控制台
(已知:如果我们使用nextInt方法让用户输入整数,但用户实际输入的不是整数的时候,会发生异常,而对于一个键盘输入对象来说,一旦发生异常,即使使用try{}catch(){}语句进行了处理,这个键盘输入对象也无法继续工作了,解决的办法就是重写创建新的键盘输入对象即可;)
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int i = 1;
int a, b;
while (true) {
try {
System.out.println("请输入第" + i + "个整数");
a = sc.nextInt();
i++;
break;
} catch (Exception e) {
System.out.println("输入有误,请务必输入整数!");
sc = new Scanner(System.in);
}
}
while (true) {
try {
System.out.println("请输入第" + i + "个整数");
b = sc.nextInt();
i++;
break;
} catch (Exception e) {
System.out.println("输入有误,请务必输入整数!");
sc = new Scanner(System.in);
}
}
int c = a * b;
System.out.println(a + "乘以" + b + "=" + c);
}
7. 章节测评-异常处理
7.1 Calendar类的作用是什么?它的常用方法有哪些?
Calendar类是Java中用于表示日期和时间的抽象类,它提供了一些方法来操作日期和时间。
Calendar类的常用方法包括以下几种:
getInstance():获取当前日历对象
get(int field):获取日历中的某个信息,例如获取年、月、日等
set(int field, int value):修改日历的某个信息,例如设置年、月、日等
add(int field, int amount):为某个信息增加/减少指定的值,例如将年加1、月减2等
getTimeInMillis():返回一个表示Calendar对象所表示时间的Date对象
getTime():获取日期对象
7.2 简述throw和throws的区别?
① throw用在方法内部,后面跟一个异常对象,用来抛出一个异常
② throws用在方法定义上,后面跟一个异常类型,用来给方法添加一个异常声明。告诉方法的调用者,该方法可能会出现异常。
7.3 Lambda表达式省略规则?
①在标准格式的基础上()中的参数类型可以直接省略
②如果{}总的语句只有一条语句,则{}可以省略、return关键字、以及最后的“;”都可以省略
③如果()里面只有一个参数,则()可以省略
7.4 编码题1
给一家超市管理系统编写一个程序
1.要求能够统计每种商品销售量的前N名,并输出其销售数量及商品名称
2.商品信息存储在一个数组中,每个元素包含商品名称和销售数量两个属性。
3.编写一个方法,能够接收一个商品信息数组和一个整数N,返回销售量前N名的商品信息。
提示:可以使用Arrays类的sort方法进行排序,使用Comparator接口自定义排序规则,然后使用Arrays类的copyOf方法进行结果拷贝
public class AppTest {
public static void main(String[] args) {
//1,准备商品信息数组
Product[] products = {
new Product("苹果", 1000),
new Product("香蕉", 800),
new Product("橙子", 925),
new Product("草莓", 1026),
new Product("西瓜", 765),
};
// 2,定义 N
int N = 3;
// 3,统计销售量排名前 N 的商品信息
Product[] p = topNProducts(products,N);
System.out.println(Arrays.toString(p));
}
private static Product[] topNProducts(Product[] products, int n) {
Arrays.sort(products, new Comparator<Product>() {
@Override
public int compare(Product o1, Product o2) {
return Integer.compare(o2.getSales(),o1.getSales());
}
});
if (n >= products.length) {
return products;
}
return Arrays.copyOf(products,n);
}
}
public class Product {
private String name;//商品名称
private int sales;//销售数量
public Product(String name, int sales) {
this.name = name;
this.sales = sales;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getSales() {
return sales;
}
public void setSales(int sales) {
this.sales = sales;
}
@Override
public String toString() {
return "Product{" +
"name='" + name + '\'' +
", sales=" + sales +
'}';
}
}
7.5 编码题2
设计一个成绩管理系统,
①该系统使用数组记录多个学生的成绩信息
②编写学生对象包含姓名、语文、数学、英语成绩,和查询总分的方法
③编写测试类实现查询学生排名等功能。要求使用Lambda表达式
⑥编写CompareByData类,在里面定义比较方法,使用实例方法引用简化lambda表达式。
import java.util.Arrays;
public class StudentRanking {
public static void main(String[] args) {
Student[] students = new Student[]{
new Student("张三", 90, 80, 70),
new Student("李四", 80, 70, 60),
new Student("王五", 70, 60, 50),
new Student("赵六", 60, 50, 40),
new Student("钱七", 50, 40, 30)
};
// 计算每个学生的总成绩,并排序输出
Arrays.sort(students, (s1, s2) -> s2.getTotalScore() - s1.getTotalScore());
System.out.println("lambda方式:");
for (int i = 0; i < students.length; i++) {
Student student = students[i];
System.out.println("第"+(i+1)+"名:"+ student.getName()+",总成绩:"+student.getTotalScore());
}
CompareByData compareByData = new CompareByData();
Arrays.sort(students, compareByData::compareByTotalScore);
System.out.println("实例方法引用方式:");
for (int i = 0; i < students.length; i++) {
Student student = students[i];
System.out.println("第"+(i+1)+"名:"+ student.getName()+",总成绩:"+student.getTotalScore());
}
}
}
public class CompareByData {
public int compareByTotalScore(Student s1, Student s2) {
return s2.getTotalScore() - s1.getTotalScore();
}
}
public class Student {
private String name;
private int mathScore;
private int englishScore;
private int chineseScore;
public Student(String name, int mathScore, int englishScore, int chineseScore) {
this.name = name;
this.mathScore = mathScore;
this.englishScore = englishScore;
this.chineseScore = chineseScore;
}
public String getName() {
return name;
}
public int getTotalScore() {
return mathScore + englishScore + chineseScore;
}
}
7.6 编码题3
设计一个程序模拟计算机计算两个整数乘积的过程
①通过键盘录入让用户输入两个整数,如果用户输入的不是整数,则提示用户重新输入
②计算两个数的乘积,并将结果打印在控制台
(已知:如果我们使用nextInt方法让用户输入整数,但用户实际输入的不是整数的时候,会发生异常,而对于一个键盘输入对象来说,一旦发生异常,即使使用try{}catch(){}语句进行了处理,这个键盘输入对象也无法继续工作了,解决的办法就是重写创建新的键盘输入对象即可;)
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int i = 1;
int a, b;
while (true) {
try {
System.out.println("请输入第" + i + "个整数");
a = sc.nextInt();
i++;
break;
} catch (Exception e) {
System.out.println("输入有误,请务必输入整数!");
sc = new Scanner(System.in);
}
}
while (true) {
try {
System.out.println("请输入第" + i + "个整数");
b = sc.nextInt();
i++;
break;
} catch (Exception e) {
System.out.println("输入有误,请务必输入整数!");
sc = new Scanner(System.in);
}
}
int c = a * b;
System.out.println(a + "乘以" + b + "=" + c);
}