异常-自定义异常-枚举-单元测试

[[Study/JavaSE/html/mindmap-day04-java.html]]

day04 【API、异常】

第一章 单元测试

所谓单元测试,就是针对最小的功能单元,编写测试代码对其进行正确性测试。

Junit是第三方公司开源出来的,用于对代码进行单元测试的工具 (IDEA已经集成了junit框架)。相比于在main方法中测试有如下几个优点。
null

由于Junit是第三方提供的,所以我们需要把jar包导入到我们的项目中,才能使用

1.1 导入Jar包

1
2
3
4
5
6
7
步骤:
1.模块名称上右键/new/directory/输入名称lib确定
2.把junit的jar包复制到文件夹lib中
3.文件夹lib上右键/Add as Library/在对话框中输入以下内容后/ok
Name: 输入lib
Level:输入Module Library
Add to module: 输入当前模块名

1.2 单元测试基本使用

哪个方法想使用单元测试,就在方法上,添加注解: @Test
注意:
该方法的返回值类型,必须写为void
该方法必须没有参数列表
不能是静态方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/*
运行:
方法上右键运行,运行的是含有@Test注解的方法
类上右键运行,运行的是类当中含有@Test注解的所有方法
绿条: 正常运行
红条: 出现问题,异常了
*/
public class StringUtil{
public static void printNumber(String name){
System.out.println("名字长度:"+name.length());
}
}

public class StringUtilTest{
@Test
public void testPrintNumber(){
StringUtil.printNumber("admin");
StringUtil.printNumber(null);
}
}

1.3 单元测试常用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
public class A {

// ---- 普通业务方法(待测试的方法) ----

/**
* 普通方法a:无参数、无返回值,仅打印一句话
*/
public void a(){
System.out.println("a.........");
}

/**
* 普通方法b:包含除零错误(会抛出ArithmeticException),用于测试异常场景
*/
public void b(){
int i = 1/0; // 故意制造除零异常,测试时会报错
System.out.println("b........."); // 这行不会执行,因为上面已经抛出异常
}

/**
* 普通方法c:带int类型参数,打印参数值
* @param a 传入的整数参数
*/
public void c(int a){
System.out.println("c........."+a);
}

/**
* 普通方法d:无参数,有int返回值,先打印再返回100
* @return 返回整数100
*/
public int d(){
System.out.println("d.........");
return 100;
}


// ------ JUnit测试方法(用@Test标记) --------

/**
* 测试方法testA:测试普通方法a()
*/
@Test
public void testA(){
a(); // 调用待测试的a方法
}

/**
* 测试方法testB:测试普通方法b()(会触发异常)
*/
@Test
public void testB(){
b(); // 调用待测试的b方法,会抛出除零异常
}

/**
* 测试方法testC:测试普通方法c(int a),传入参数25
*/
@Test
public void testC(){
c(25); // 调用待测试的c方法,传入参数25
}

/**
* 测试方法testD:测试普通方法d(),接收返回值并打印
*/
@Test
public void testD(){
int d = d(); // 调用待测试的d方法,接收返回值
System.out.println(d); // 打印返回值100
}

// -------- JUnit生命周期注解方法 --------

/**
* @Before:每个@Test方法执行前都会执行一次
* 作用:比如初始化测试数据、打开资源等
*/
@Before
public void bef(){
System.out.println("before........");
}

/**
* @After:每个@Test方法执行后都会执行一次(即使测试方法抛出异常也会执行)
* 作用:比如清理测试数据、关闭资源等
*/
@After
public void af(){
System.out.println("After........");
}

/**
* @BeforeClass:在所有测试方法执行前只执行一次(必须是static方法)
* 作用:比如加载配置文件、初始化数据库连接等(只需要做一次的操作)
*/
@BeforeClass
public static void befc(){
System.out.println("BeforeClass........");
}

/**
* @AfterClass:在所有测试方法执行后只执行一次(必须是static方法)
* 作用:比如释放数据库连接、清理全局资源等(只需要做一次的操作)
*/
@AfterClass
public static void afc(){
System.out.println("AfterClass........");
}
}

BeforeClass........
before........
a.........
After........
before........
(这里会抛出ArithmeticException异常信息)
After........
before........
c.........25
After........
before........
d.........
100
After........
AfterClass........
注解 执行时机 方法要求 作用示例
@BeforeClass 所有测试方法执行前只执行一次 必须static 加载配置、初始化数据库连接
@Before 每个@Test方法执行前都执行一次 非静态 初始化测试数据、准备资源
@After 每个@Test方法执行后都执行一次(即使测试抛异常也执行) 非静态 清理数据、关闭资源
@AfterClass 所有测试方法执行后只执行一次 必须static 释放数据库连接、清理全局资源

1.4 断言

断言就是对测试的结果进行”鉴定”,如果符合预期结果,那么就断言成功,否则就是失败,常见方法如下:
image-20260208113023274

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class B {
public int getIndex(int[] arr,int key){
for (int i = 0; i < arr.length; i++) {
if(arr[i] == key){
return key;
}
}
return -1;
}

// 编写测试方法,测试上面的方法是否正确
@Test
public void testGetIndex(){
int[] arr = {3,6,9};
int index = getIndex(arr, 6);
// 需要使用断言,确保被测试的方法返回的结果,是我们想要的结果
Assert.assertEquals("逻辑不对哟",1,index);
System.out.println(index);
}
}

第二章 枚举

2.1 概述

枚举是引用数据类型,和类,接口是一个级别的;

一个类的多个(固定的且内容不可改变的)对象

枚举类A是用class定义的,说明枚举确实是一个类,而且X,Y,Z都是A类的对象

而且每一个枚举项都是被public static final 修饰,所以被可以类名调用,而且不能更改

1
2
3
4
5
枚举概念:
1.这种实例(对象)有限而且固定的类,在Java里被称为枚举类。
2.枚举是新增的引用数据类型,和类,接口是一个级别的,定义枚举的关键字为enum
3.java.lang.Enum类,是所有枚举的父类。所有的类的最终父类是java.lang.Object类
4.枚举的本质就是一个类的多个(固定的且内容不可改变的)对象。

枚举场景举例:

1
2
3
4
5
交通灯: Red(红)、Green(绿)、Yellow(黄)
星期: Monday(星期一)、....、Sunday(星期天)
性别: Man(男)、Women(女)
季节:Spring(春季)、....、Winter(冬季)
线程状态: 新建、运行、消亡、阻塞、限时等待、无限等待

枚举的应用场景是这样的:枚举一般表示一组信息,然后作为参数进行传输。

用户进入应用时,需要让用户选择是女生、还是男生,然后系统会根据用户选择的是男生,还是女生推荐不同的信息给用户观看。
null

1
2
3
public class Constant{
BOY,GRIL
}

再定义一个测试类,完成用户进入系统后的选择

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Test{
public static void main(String[] args){
//调用方法,传递男生
provideInfo(Constant.BOY);
}

public static void provideInfo(Constant c){
switch(c){
case BOY:
System.out.println("展示一些信息给男生看");
break;
case GRIL:
System.out.println("展示一些信息给女生看");
break;
}
}
}

2.2 枚举的定义

枚举是一种特殊的类,它的格式是:

1
2
3
public enum 枚举类名{
枚举项1,枚举项2,枚举项3;
}

对象在定义枚举类时就预先写好了,以后就只能用这几个固定的对象

定义一个枚举类A,在枚举类中定义三个枚举项X, Y, Z

1
2
3
public enum A{
X,Y,Z;
}

想要获取枚举类中的枚举项,只需要用类名调用就可以了

1
2
3
4
5
6
7
8
public class Test{
public static void main(String[] args){
//获取枚举A类的,枚举项
A a1 = A.X;
A a2 = A.Y;
A a3 = A.Z;
}
}

枚举项实际上是枚举类的对象,这一点其实可以通过反编译的形式来验证

null

枚举类A是用class定义的,说明枚举确实是一个类,而且X,Y,Z都是A类的对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
/*
定义类时,发现该类的对象只有固定的几个,且每个对象的内容不可改变时,需要定义成枚举类
定义交通灯:
1.红灯: 停
2.绿灯: 行
3.黄灯: 等一等
1.格式:
public enmu 枚举名{}
2.枚举常量定义
(1)枚举中的常量名字大写,多个常量之间逗号分开,最后一个常量可以写分号
(2)每一个常量,都表示这个类的对象。
(3)枚举常量修饰符为public static final(不需要自己写,也不能写)
(4)枚举中有默认的无参数的private修饰的构造方法,如果手写构造方法,也必须是私有修饰的
(5)构造方法必须写在常量的后面,这时最后一个常量就必须要写分号
*/
public enum Light01 {
//3.需要给该类提供3个固定的对象: 每个对象都有一个编号,从0开始
RED("红灯", "必须停"),//调用有参构造方法
GREEN("绿灯", "行"),
YELLOW("黄灯", "等一等"),
BLUE();//调用空参构造方法

//1.因为每个对象中都有对应的属性
private String COLOR;
private String INFO;

//2.通过构造方法给属性赋值,构造方法private修饰,不允许外部创建该类的其它对象
private Light01(String COLOR, String INFO) {
this.COLOR = COLOR;
this.INFO = INFO;
}

private Light01(){}

//4.方便查看数据,覆盖重写toString方法
@Override
public String toString() {
return "Light01{" +
"COLOR='" + COLOR + '\'' +
", INFO='" + INFO + '\'' +
'}';
}

//5.方法获取对象中的属性信息,提供get方法
//每个对象中的属性信息,不允许修改,不能提供set方法
public String getCOLOR() {
return COLOR;
}

public String getINFO() {
return INFO;
}
}

2.3 枚举的使用

枚举的常量为静态修饰可以直接枚举名.调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/*
枚举类的values方法是一个特殊方法(内置方法),api和源代码中都没有提供
但是我们可以直接使用,获取到的是该枚举类中的对象数组
*/
public class Demo01Light01 {
public static void main(String[] args) {
//每一个常量,都表示这个类的对象。
Light01 red = Light01.RED;
//利用反射证明该对象的所属枚举的名称就是Light01
//反射是指:在编译期不知道要操作的类是什么,而是在程序运行时(Runtime),动态地去获取类的信息、创建对象、调用方法或访问属性。
/*
Class clazz = red.getClass();
String className = clazz.getName();
System.out.println(className);
*/
System.out.println(red.getClass().getName());

//重写toString打印内容
//不重写toString打印"GREEN",说明枚举类默认继承java.lang.Enum类(而非直接继承Object);
//自定义枚举类继承Enum,Enum中有toString方法,返回枚举的名称
System.out.println(Light01.GREEN);

//Enum类中的ordinal方法 返回枚举对象的编号
System.out.println(Light01.RED.ordinal());//0
System.out.println(Light01.GREEN.ordinal());//1
System.out.println(Light01.YELLOW.ordinal());//2

//values方法获取枚举中的 所有对象数组
Light01[] values = Light01.values();
//遍历
for (Light01 value : values) {
System.out.println("编号: " + (value.ordinal() + 1) + "名称: " + value.name() + ", 颜色: " + value.getCOLOR() + ", 信息: " + value.getINFO());
}
}
}

第三章 异常

4.1 概念和体系

1
2
3
4
5
6
7
8
9
10
11
12
13
/*
异常:程序中的意外问题,处理异常,提供一种预防机制(预案),写字楼中的消防栓
异常的体系:
|--Object
|--Throwable 程序中可能会出现的所有的不正常的情况
|--Error 程序中的致命的问题,严重的错误
比如:绝症 一旦出现了Error,程序不能继续进行
|--Exception(编译时期的异常) 程序中的小的问题,小的错误,
比如:感冒,发烧 只要将问题处理了,程序可以继续运行
名称:
错误: XxxError
异常: XxxException
*/

null|697

4.2 异常的分类

1
2
3
4
5
6
7
8
9
10
11
12
/*
异常的分类
|-- 编译时期异常:checked异常(受检测异常)。
在编译时期,就会检查,如果没有处理异常,则编译失败。(如日期格式化异常)
直接继承Exception

|-- 运行时期异常:runtime异常。
在运行时期,检查异常.
在编译时期,运行异常编译器不会检测(不报错)。(如数学异常)
不影响编译,可以正常生成.class文件,但是运行时期可能会出现问题
直接继承RuntimeException
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/*
演示手动创建并抛出运行时异常
抛出 : 使用 throw 关键字,把一个 异常对象 交给方法的调用者,目的是让调用者知道我们这个方法内部没有成功完成功能,而是出现了"意外情况",让调用者自己决定如何处理这个意外情况;

*/
public class MyTest04 {
public static void main(String[] args) {
int[] arr = {2,5,8};
//int index = getValueByIndex(null, 0);
//int index = getValueByIndex(arr, 3);
int index = getValueByIndex(arr, 1);
System.out.println(index);
System.out.println("mian...........");
}
public static int getValueByIndex(int[] arr,int index){
// 对数据的合法性进行校验
if(arr == null){
// 告诉调用者,参数不能为null,可以通过"抛异常"的手段实现这个效果
throw new RuntimeException("客官不可以,参数不能null");
}
if(index <0 || index >= arr.length){
//// 告诉调用者,索引参数必须有效,可以通过"抛异常"的手段实现这个效果
throw new RuntimeException(index+"不是一个合法的索引,干不了活...");
}
return arr[index];
}
}

4.3 异常的产生和JVM处理异常的方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class A {
public static void main(String[] args) throws Exception {
int[] arr = {1,4,7};
//int index = getValueByIndex(null, 3);
int index = getValueByIndex(arr, 1);
System.out.println(index);

}
public static int getValueByIndex(int[] arr,int index) throws ArrayIndexOutOfBoundsException,Exception {
// 对数据的合法性进行校验
if(arr == null){
// 告诉调用者,参数不能为null,可以通过"抛异常"的手段实现这个效果
throw new Exception("客官不可以,参数不能null");
}
if(index <0 || index >= arr.length){
//// 告诉调用者,索引参数必须有效,可以通过"抛异常"的手段实现这个效果
throw new ArrayIndexOutOfBoundsException(index+"不是一个合法的索引,干不了活...");
}
return arr[index];
}
}

1600931895028

第四章 异常处理

5.1 throw的用法

1
2
3
4
5
6
7
8
9
10
11
12
13
/* 
Demo02Exception中异常对象是由JVM创建并自动抛出的
如果我们想自己创建异常对象并`手动抛出`
需要使用throw关键字: 扔,抛的意思

throw关键字使用格式:
throw 异常对象;
throw new 异常类(...);
注意:
1.throw必须使用在方法内部
2.throw后面必须写异常对象,而且只能写一个
3.throw表示把一个具体的异常对象抛出给该方法的调用者
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class B {
public static void main(String[] args) {
int[] arr = {1, 4, 7};
try {
int index = getValueByIndex(arr, 1);
System.out.println(index);
}catch (Exception e){
// 第1次调用失败了
// 更新参数,重新调用
System.out.println("再次调用...");
int index = getValueByIndex(arr, 2);
System.out.println(index);
}
//int index = getValueByIndex(arr, 1);
}

public static int getValueByIndex(int[] arr, int index) {
// 对数据的合法性进行校验
if (arr == null) {
// 告诉调用者,参数不能为null,可以通过"抛异常"的手段实现这个效果
try {
throw new Exception("客官不可以,参数不能null");
} catch (Exception e) {
// 面向 e 对象,就可以获取具体的异常信息
e.printStackTrace(); // 以红色的字体打印异常信息,并不会停止程序运行
System.out.println("小样,抓到你了...");
}
}
if (index < 0 || index >= arr.length) {
// 告诉调用者,索引参数必须有效,可以通过"抛异常"的手段实现这个效果
throw new ArrayIndexOutOfBoundsException(index + "不是一个合法的索引,干不了活...");
}
return arr[index];
}
}

5.2 throws声明抛出异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/*
异常处理方式一:
使用throws关键字: 声明抛出异常
告诉方法的调用者,你调用我的方法时,我方法内部有可能会产生什么样子的异常,
方法内部没有处理,谁调用我谁来处理
使用格式:
修饰符 返回值类型 方法名称(形式参数列表....) throws 异常类 {
方法体;
}
场景:
项目经理: JVM
项目组长: main方法
程序猿: sports方法
注意:
1.throws关键字必须写在方法声明的后面
2.throws关键字后面跟的是异常类,而且可以写多个
3.throw必须使用在方法内部
4.throw后面必须写异常对象,而且只能写一个
5.throw表示把一个具体的异常对象抛出给该方法的调用者
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class C {
public static void main(String[] args) {

}
public static void abc(int a) throws NullPointerException,ClassCastException,Exception{
if(a < 0){
throw new NullPointerException("人为创建的空指针异常...");
}
if(a < 10){
throw new ClassCastException("人为创建的类型转换异常...");
}
throw new Exception("其他异常");
}
}

5.3 try-catch处理异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
/*
异常处理方式二: 捕获处理
try - catch 捕获处理
使用格式:
try {
有可能产生异常的代码;
} catch(异常类 对象名) {
处理异常的代码;
}

try: {}中的内容,相当于教室,走廊
catch:()中的内容,烟雾报警器
catch:{}中的内容,消防栓,消防官兵

try-catch的执行流程:
1.try中没有产生异常,程序正常执行,catch中代码不执行
2.try中的产生异常,`try中产生异常的位置后续代码不再执行`
(1)有catch捕获
a.如果catch成功捕获,执行{}中处理异常的代码,处理完毕后,程序继续执行
b.如果catch没有成功捕获,继续抛出异常,抛给JVM,JVM采用中断处理
(2)没有catch捕获 继续抛出
场景:
项目经理: JVM
项目组长: main方法
程序猿: sports方法
*/
public class C {
public static void main(String[] args) {
// 调用方法,针对方法抛出的3个异常分别处理
try {
abc(45);
}catch (NullPointerException e){
System.out.println("抓到了一个空指针异常...");
e.printStackTrace();
}catch (ClassCastException e){
System.out.println("抓到了一个类型转换异常...");
e.printStackTrace();
}catch (Exception e){
System.out.println("抓到了一个其他异常...");
e.printStackTrace();
}
System.out.println("main正常走完了......");
}
public static void abc(int a) throws NullPointerException,ClassCastException,Exception{
if(a < 0){
throw new NullPointerException("人为创建的空指针异常...");
}
if(a < 10){
throw new ClassCastException("人为创建的类型转换异常...");
}
throw new Exception("其他异常");
}
}

5.4 try-catch处理异常流程

1600867579718

5.5 try-catch-finally的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/*
有一些特定的代码无论异常是否发生,无论异常是否被正确处理,都需要执行的代码,写在finally中
使用场景:
后面使用IO流对象/操作数据库的连接对象后,必须要释放资源,
而释放资源的代码就必须写在finally当中

使用格式:
try {
有可能产生异常的代码;
} catch(异常类 对象名) {
处理异常的代码;
} finally {
必须要执行的代码;
}
*/
public class Test07 {
public static void main(String[] args) {
try {
int a = 1;
int i = get(a);
System.out.println("i="+i);
}catch (Exception e){
e.printStackTrace();
System.out.println("出异常了....");
}finally {
System.out.println("最终无论如何都要执行的代码");
}
System.out.println("main.....");
}

public static int get(int a){
a++;
try {
a++; // 3
return a; // 先获取a此时的值(3),并做好返回的准备,此时会被 finally拦截下来
}catch (Exception e){
a++;
}finally {
System.out.println("finally执行了....");
a++; // 4 在finally代码中修改变量的值,不会影响前面的return已经获取的值!!!
//return a; // 4
}
return a;
}
}

5.6 Throwable类中提供的常用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/*
Throwable中定义的获取异常信息:
public String getMessage() :
获取异常的描述信息,原因(提示给用户的时候,就提示错误原因) ---不用
public String toString() :获取异常的类型和异常描述信息(不用)。---不用
public void printStackTrace() :
打印异常的跟踪栈信息并输出到控制台。 ---最常使用,JVM打印异常信息的默认方式
*/
public class Demo07ThrowableMethod {
public static void main(String[] args) {
try {
int[] arr = {100, 200, 300};
int value = arr[5];
System.out.println("value=" + value);
//Throwable --> Exception --> RuntimeException --> IndexOutOfBoundsException
} catch (ArrayIndexOutOfBoundsException e) {
//String message = e.getMessage();
//System.out.println(message);
//String s = e.toString();
//System.out.println(s);
e.printStackTrace();
}
}
}

5.7 编译时期异常和运行时期异常的区别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/*
编译时期异常(受检测异常)
特点:
只要写上,就会报红线,必须处理
要么throws进行处理
要么try-catch进行处理
【不能不管它】

运行时期异常(不受监测)
特点
写上,不会报红线,可以不处理
可以throws进行处理
可以try-catch进行处理
【还可以不管】
对于运行时期异常,不建议使用throws/try-catch处理
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
//演示编译时期异常
public class Demo08ExceptionDiff {
public static void main(String[] args) throws FileNotFoundException {
a();
b();
}

//定义方法,内部抛出编译时期异常
//必须处理: 此处用throws,告诉调用者处理
public static void a() throws FileNotFoundException {
throw new FileNotFoundException();
}

//定义方法,内部抛出编译时期异常
//必须处理: 此处方法内部try-catch处理,告诉调用者处理
public static void b() {
try {
throw new ParseException("解析异常",2);
} catch (ParseException e) {
e.printStackTrace();
}
}

//定义方法,内部抛出编译时期异常
//不能不管它,目前既没有throws,又没有try-catch,所以报错
/*public static void c() {
throw new DataFormatException();
}*/
}
//演示运行时期异常
public class Demo09ExceptionDiff {
public static void main(String[] args) {
a();
b();
}

//定义方法,内部抛出运行时期异常
//写上,不会报红线,可以不处理
public static void a() {
throw new NullPointerException();
}

//定义方法,内部抛出运行时期异常
//可以throws进行处理
public static void b() throws ArrayIndexOutOfBoundsException {
throw new ArrayIndexOutOfBoundsException();
}

//定义方法,内部抛出运行时期异常
//可以内部try-catch
public static void c() {
try {
throw new StringIndexOutOfBoundsException();
} catch (StringIndexOutOfBoundsException e) {
e.printStackTrace();
}
}
}

5.8 异常后方法重写注意事项

1
2
3
4
5
/*
异常注意事项
1.父类方法没有抛出异常,子类覆盖父类该方法时也不可抛出异常。---重点掌握
此时子类产生该异常,只能捕获处理,不能声明抛出
*/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//父类
public class Fu {
public void method() {
System.out.println("Fu...method....");
}
}

//子类
public class Zi extends Fu {
/*
父类method方法声明上,没有使用throws声明抛出异常
子类重写后的方法,内部有异常,不能使用throws
只能内部try-catch处理 ---使用场景: 在讲多线程的时候会用到
*/
@Override
public void method()/* throws Exception*/ {
System.out.println("Zi...method...");
try {
throw new Exception("子类重写后出问题了!!!!");
} catch (Exception e) {
e.printStackTrace();
}
}
}

第六章 自定义异常

6.1 自定义异常简单演示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/*
自定义异常
虽然jdk中提供了大量的异常类,但是和我们实际的业务开发场景无关
所以我们需要根据需求自定义异常类
步骤:
1.自定义类继承Exception(编译时期异常)或者RuntimeException(运行时期异常)
继承谁都可以,没有统一规定
2.根据父类生成空参/满参构造
3.代码中就可以使用throw关键字抛出异常对象
*/
public class Demo12MyException {
public static void main(String[] args) {
try {
show();
} catch (UserNameRegisterException e) {
e.printStackTrace();
}
}

public static void show() throws UserNameRegisterException {
throw new UserNameRegisterException("用户名已经被注册了");
}
}
1
2
3
4
5
6
7
8
9
10
11
//自定义编译时期异常: 用户名已经被注册的异常类
public class UserNameRegisterException extends Exception {
//空参构造
public UserNameRegisterException() {
}

//满参构造
public UserNameRegisterException(String message) {
super(message);
}
}

6.2 自定义异常练习

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
/*
在java的scanner的基础上,再次封装,解决键盘输入的数据不合法的问题,及nextLine丢失键盘输入的机会的问题
*/
public class MyScannerUtils {
private static Scanner sc = new Scanner(System.in);
/*public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("亲,请输入一个整数:");
int a = sc.nextInt();
System.out.println(a);
}*/
private MyScannerUtils(){

}
//private:私有构造方法,作用是禁止外部类通过new MyScannerUtils()创建该类的对象。
//设计原因:工具类通常只提供静态方法(通过类名直接调用),不需要实例化,私有化构造方法可以强制使用者遵循这一设计。

// 提供静态方法,让调用者直接使用
public static int getInt(){
// 让用户循环输入,直到输入整数为止
while (true){
try {
return sc.nextInt();
} catch (Exception e) {
// 把残留在内存中的不是整数的数据"吃掉"
System.out.println("请务必输入整数.....");
sc.nextLine();
}
}
}
public static String getString(){
return new Scanner(System.in).nextLine();
}
/*
这个方法用于读取一行字符串,解决原生Scanner中nextLine()可能 “丢失” 输入的问题:
原生Scanner的痛点:如果先调用nextInt()、nextDouble()等方法,它们只会读取数值,不会读取换行符;此时如果接着调用nextLine(),会直接读取到残留的换行符,导致用户还没输入就 “跳过” 了。
本方法的解决方案:每次调用都创建一个新的Scanner对象—— 新的Scanner会清空之前的缓冲区状态,直接读取用户的输入行,避免了残留数据的干扰。
return new Scanner(System.in).nextLine():创建新的Scanner,调用nextLine()读取一行输入并返回。*/
}

public class MyTest {
public static void main(String[] args) {

System.out.println("请输入字符串:");
String string = MyScannerUtils.getString();

System.out.println(string);
System.out.println("请输入整数:");

int anInt = MyScannerUtils.getInt();
System.out.println("输入的整数是:"+anInt);
System.out.println("请输入字符串:");

String string2 = MyScannerUtils.getString();
System.out.println(string2);
}
}