【470-500】异常
2022-01-25 17:25:00 # JavaSE

异常以类和对象的形式存在

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class _471_Java中异常以类和对象的形式存在 {
public static void main(String[] args) {
// 实例化 异常对象
NumberFormatException nfe = new NumberFormatException("数字格式化异常");
System.out.println(nfe); // java.lang.NumberFormatException: 数字格式化异常

// 实例化 异常对象
NullPointerException npe = new NullPointerException("空指针异常");
System.out.println(npe); // java.lang.NullPointerException: 空指针异常

int t = 10/0; // JVM 在执行此处时, 会 new 一个 ArithmeticException 异常对象
/*
Exception in thread "main" java.lang.ArithmeticException: / by zero
at _471_Java中异常以类和对象的形式存在.main(_471_Java中异常以类和对象的形式存在.java:11)
*/


}
}

UML以及starUML

  1. java的异常处理机制
    异常在java中以类和对象的形式存在。那么异常的继承结构是怎样的?我们可以使用UML图来描述一下继承结构。
    画UML图有很多工具,例如:Rational Rose (收费的)、startMz等

  2. 什么是UML?有什么用?
    UML是一种统一建模语言-一种图标式语言(画图的)
    UML不是只有java中使用。只要是面向对象的编程语言,都有UML
    一般画UM图的都是软件架构师或者说是系统分析师。这些级别的人员使用的。软件设计人员使用UML。
    在UML图中可以描述类和类之间的关系,程序执行的流程,对象的状态等.

异常的继承结构

  1. object
  2. Object下有Throwable (可抛出的)
  3. Throwable下有两个分支:Error(不可处理,直接退出JvM)和Exception (可处理的)
  4. Exception下有两个分支:
    1. Exception的直接子类:编译时异常(要求程序员在编写程序阶段必须预先对这些异常处理)
    1. RuntimeException:运行时异常。(在编写程序阶段程序员可以预先处理,也可以不管)

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

  1. 编译时异常和运行时异常,都是发生在运行阶段。编译阶段异常是不会发生的。
  2. 编译时异常因为什么而得名?
    因为编译时异常必须在编译(编写)阶段预先处理,如果不处理编译器报错,因此得名。
  3. 所有异常都是在运行阶段发生的。因为只有程序运行阶段才可以new对象。因为异常的发生就是new异常对象。
  4. 编译时异常发生概率高,运行时异常概率低。
  5. 编译时异常又称为 受检异常 和 受控异常
  6. 运行时异常又称为 未受检异常 和 非受控异常

异常的两种处理方式

  1. 在方法声明的位置上使用 throws 关键字
  2. 使用 try catch 语句对异常捕捉
  3. 如果一直上抛,抛到 main,main抛给调用者JVM,JVM只有一种结果,终止java程序执行
  4. 上抛类似推卸责任,catch表示拦下,异常真正解决,调用者不知道
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class _482_异常处理的原理 {
// 第一种方式 在 调用doSome()方法的 main 方法中继续上抛
public static void main(String[] args) throws ClassNotFoundException{
doSome();

// 第二种方式 try catch 方式
try {
doSome();
}catch (ClassNotFoundException e){
e.printStackTrace();
}
}


// 表示该方法在执行过程中会出现 ClassCastException 异常,这个异常是编译时异常
// 所以要预先处理,否则直接调用doSome()会报错
public static void doSome() throws ClassNotFoundException{
System.out.println("doSome");
}
}


运行时异常编写程序时可以不处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class _480_运行时异常编写程序时可以不处理 {
public static void main(String[] args) {
/*
程序执行到此处发生了ArithmeticException异常,
底层new了一个ArithmeticException异常对象,然后抛出了,
由于是main方法调用了100 / e ,
所以这个异常ArithmeticException抛给了main方法,
main方法没有处理,
将这个异常自动抛给了JVN。
JVM最终终止程序的执行。
*/
System.out.println(100 / 0);

// 这里未执行
System.out.println("Hello");
}
}

异常捕捉和上抛的联合使用

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
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class _483_异常捕捉和上抛的联合使用 {
// 一般不建议在 main 方法上 throws
// 因为异常发生了,就会抛给JVM,JVM终止程序
// 所以一般 mian 方法中的异常 使用 try catch 捕捉
public static void main(String[] args) {
m1();
}

// 使用 try catch 方式
public static void m1(){
System.out.println("m1 begin");
try {
// 如果出现异常 这里不执行
// 进入catch语句块中处理
m2();
} catch (IOException e) {
// e.printStackTrace();
System.out.println("该文件被删除");
}
System.out.println("m1 end");
}

// 抛出 FileNotFoundException 的父类 IOException 是可行的
// 也可以写多个异常
public static void m2() throws FileNotFoundException, IOException {
System.out.println("m2 begin");
m3();
// 如果出现异常 这里不执行
System.out.println("m2 end");
}

// 由于 FileInputStream 构造方法中 throws 了 FileNotFoundException 异常
// 所以程序必须处理这个异常
public static void m3() throws FileNotFoundException {
System.out.println("m3 begin");
new FileInputStream("D:\\Workspace\\使用说明.docx");
// 如果出现异常 这里不执行
System.out.println("m3 end");
}
}

深入try catch

  1. 多个catch的时候,从上到下的catch需要从小到大的Exception
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class _485_try_catch深入 {
public static void main(String[] args) {
try {
// 创建输入流
FileInputStream fis = new FileInputStream("D:\\xxxxxx");
// 读文件
fis.read();
} catch (FileNotFoundException e) { // 对应 new FileInputStream
System.out.println("文件不存在");
} catch (IOException e){ // 对应 read()
System.out.println("read 失败");
}

}
}



jdk8新特性

  1. catch中加入 “|” 运算符
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;

    public class _486_Java8新特性 {
    public static void main(String[] args) {
    try {
    FileInputStream fis = new FileInputStream("D:\\xxxxx");
    } catch (FileNotFoundException | NullPointerException e) {
    System.out.println("文件不存在? 空指针异常?");

    }
    }
    }

上抛和捕捉怎么选择

如果希望调用者处理,使用throws

异常对象的常用方法

getMessage()

1
2
3
4
5
6
7
8
9
10
11
public class _488_异常对象的常用方法 {
public static void main(String[] args) {
// getMessage() 获取简单的描述信息
NullPointerException npe = new NullPointerException("空指针异常");
String msg = npe.getMessage();
System.out.println(msg);

System.out.println("======");
}
}

printStackTrace()

1
2
3
4
5
6
7
8
9
10
11
12
13
public class _488_异常对象的常用方法 {
public static void main(String[] args) {
NullPointerException npe = new NullPointerException("空指针异常");

// printStackTrace() 打印异常堆栈信息
// 异步线程方式输出
// 所以控制台输出时 "======" 在前
npe.printStackTrace();

System.out.println("======");
}
}

异常信息如何查看

异常追踪信息从上往下看…
SUM公司写的代码就不用看了…

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
import java.io.FileInputStream;
import java.io.FileNotFoundException;

public class _489_异常对象的常用方法 {
public static void main(String[] args) {
try {
m1();
} catch (FileNotFoundException e) {
e.printStackTrace();
/*
java.io.FileNotFoundException: D:\xxxxxx (系统找不到指定的文件。)
at java.io.FileInputStream.open0(Native Method)
at java.io.FileInputStream.open(FileInputStream.java:195)
at java.io.FileInputStream.<init>(FileInputStream.java:138)
at java.io.FileInputStream.<init>(FileInputStream.java:93)
at _489_异常对象的常用方法.m3(_489_异常对象的常用方法.java:22)
at _489_异常对象的常用方法.m2(_489_异常对象的常用方法.java:18)
at _489_异常对象的常用方法.m1(_489_异常对象的常用方法.java:14)
at _489_异常对象的常用方法.main(_489_异常对象的常用方法.java:7)
*/
}

System.out.println("hello");
}

public static void m1() throws FileNotFoundException {
m2();
}

private static void m2() throws FileNotFoundException {
m3();
}

private static void m3() throws FileNotFoundException {
new FileInputStream("D:\\xxxxxx");
}
}

finally子句的使用

  1. try中的语句就算发生异常 finally 中的语句可以正常运行
  2. 所以finally语句比较有保障,比如close文件
  3. try 可以直接和 finally 连用
  4. try 中就算 return,finally 内的语句也会执行,也就是先执行 try 后执行 finally 最后 return
  5. 退出JVM finally语句不执行
  6. java中有一条这样的规则: 方法体中的代码必须遵循自上而下顺序战次逐行执行(亘古不变的语法! )

123

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
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class _490_finally子句的使用 {
public static void main(String[] args) {
FileInputStream fis = null;
try {
// 创建输入流
fis = new FileInputStream("D:\\1Devc++ file\\00001.cpp");

// 这里空指针异常 会导致 fis 没有关闭
String s = null;
s.toString();

// 关闭 占用资源 的流对象
fis.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e){
e.printStackTrace();
} catch (NullPointerException e){
e.printStackTrace();
} finally {
// fis 可能是 null
// 所以 try catch
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("======");
}
}
}

4

1
2
3
4
5
6
7
8
9
10
11
public class _491_finally子句的使用 {
public static void main(String[] args) {
try{
System.out.println("try...");
return;
}finally {
System.out.println("finally...");
}
}
}

5

1
2
3
4
5
6
7
8
9
10
11
public class _492_退出JVMfinally语句不执行 {
public static void main(String[] args) {
try{
System.out.println("try...");
System.exit(0);
}finally {
System.out.println("finally...");
}
}
}

6

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
public class _493_finally的面试题 {
public static void main(String[] args) {
System.out.println(f());
/*
finally!
100
*/
}

public static int f(){
int i = 100;
try{
return i;
}finally {
++i;
System.out.println("finally!");
}
}

// 这是对f()反编译后的结果
// public static int f(){
// int i = 100;
// int j = i;
// ++i;
// System.out.println("finally!");
// return j;
// }

}


final_finally_finalize的区别

  1. final 是一个关键字,表示最终的,不变的
  2. finally 也是一个关键字,和try连用,finally语句块中的语句一定执行
  3. finalize 是 Object 中的一个方法,所以是标识符,是JVM的GC的垃圾回收器负责调用
1
2
3
4
5
6
7
8
9
public class _494_final_finally_finalize的区别 {
public static void main(String[] args) {
Object o = new Object();
/* 这是 Object 中的一个方法
是JVM的GC的垃圾回收器调用
protected void finalize() throws Throwable { }
*/
}
}

Java中怎么自定义异常

  1. 编写一个类继承Exception或者RuntimeException.
  2. 提供两个构造方法,一个无参数的,一个带有string参数的。
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
public class _495_java中如何自定义异常 {
public static void main(String[] args) {
MyException1 e1 = new MyException1("用户名不能为空");
e1.printStackTrace();
/*
MyException1: 用户名不能为空
at _495_java中如何自定义异常.main(_495_java中如何自定义异常.java:3)
*/
}
}

class MyException1 extends Exception{ // 用于编译时异常
public MyException1() {
}

public MyException1(String message) {
super(message);
}
}

class MyException2 extends RuntimeException{ // 用于运行时异常
public MyException2() {
}

public MyException2(String message) {
super(message);
}
}

异常在实际开发中的作用

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
public class _496_异常在实际开发中的作用 {
public static void main(String[] args){
MyStack sta = new MyStack();

try {
sta.pop();
} catch (MyStackOperatorException e) {
e.printStackTrace();
}
/*
MyStackOperatorException: 栈空了~
at MyStack.pop(_496_异常在实际开发中的作用.java:59)
at _496_异常在实际开发中的作用.main(_496_异常在实际开发中的作用.java:6)
*/
System.out.println(" =============== ");

try {
sta.push(1);
sta.push(2);
sta.push(3);
sta.push(4);
sta.push(5);
sta.push(6);
sta.push(7);
sta.push(8);
sta.push(9);
sta.push(10);
sta.push(11);
} catch (MyStackOperatorException e) {
e.printStackTrace();
}
/*
MyStackOperatorException: 栈已满~
at MyStack.push(_496_异常在实际开发中的作用.java:48)
at _496_异常在实际开发中的作用.main(_496_异常在实际开发中的作用.java:23)
*/

}
}

class MyStack{
private Object[] elements;
private int index;

// 默认初始化容量是 10
public MyStack() {
elements = new Object[10];
index = 0;
}

public void push(Object o) throws MyStackOperatorException {
if(index >= 10){
// MyStackOperatorException e = new MyStackOperatorException("栈已满~");
// throw e;

// 以上两行合并为一行
throw new MyStackOperatorException("栈已满~");
}
elements[index++] = o;
System.out.println("push " + o);
}

public void pop() throws MyStackOperatorException {
if(index <= 0){
// MyStackOperatorException e = new MyStackOperatorException("栈空了~");
// throw e;

throw new MyStackOperatorException("栈空了~");
}
--index;
}

// set get 也许用不上,但是必须写...
public Object[] getElements() {
return elements;
}

public void setElements(Object[] elements) {
this.elements = elements;
}

public int getIndex() {
return index;
}

public void setIndex(int index) {
this.index = index;
}
}

class MyStackOperatorException extends Exception{
public MyStackOperatorException() {
}

public MyStackOperatorException(String message) {
super(message);
}
}

异常与方法覆盖

重写之后的方法不能比重写之前更宽泛的编译时的异常,可以更少。

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
public class _497_异常与方法覆盖 {
public static void main(String[] args) {

}
}

class Animal{
public void m1(){

}
public void m2() throws Exception{

}
}

class Cat extends Animal{
/* 编译报错
@Override
public void m1() throws Exception{
}
*/

/* 编译成功
@Override
public void m1() throws RuntimeException{

}
*/

/* 编译成功
@Override
public void m2(){
}
*/

/* 编译成功
@Override
public void m2() throws Exception {
}
*/

/* 编译成功
@Override
public void m2() throws NullPointerException {
}
*/
}

异常中的关键字

  1. try
  2. catch
  3. finally
  4. throws 在方法上声明
  5. throw 手动抛出异常

异常作业

编写程序模拟用户注册:
1、程序开始执行时,提示用户输入”用户名”和”密码”信息。
2、输入信息之后,后台java程序模拟用户注册。
3、注册时用户名要求长度在[6-14]之间,小于或者大于都表示异常。
注意:
完成注册的方法放到一个单独的类中-异常类自定义即可-

1
2
3
4
5
class Userservice {  
public void register (string username ,string password){
//这个方法中完成注册!
}
)

编写main方法,在main方法中接收用户输入的信息,在main方法中调用Userservice的register方法完成注册

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
public class _498_异常作业 {
public static void main(String[] args) {
UserServices u = new UserServices();
try {
u.register(null,"123123123");
} catch (MyRegisterException e) {
e.printStackTrace();
}
}
}

class UserServices{
public void register(String username, String pasaword) throws MyRegisterException {
if(username != null && 6 <= username.length() && username.length() <= 14){
System.out.println("登陆成功~~");
}
else{
throw new MyRegisterException("用户名长度不对!!!");
}
}
}

class MyRegisterException extends Exception{
public MyRegisterException() {
}

public MyRegisterException(String message) {
super(message);
}
}


武器数组作业

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
127
128
129
130
131
132
133
134
135
136
137
138
public class _499_武器数组作业 {
public static void main(String[] args) {
Army army = new Army(3);
try {
army.addWeapon(new Tank());
army.addWeapon(new Radar());
army.addWeapon(new Missile());
army.addWeapon(new Tank());
} catch (addWeaponException e) {
e.printStackTrace();
}

System.out.println("=======");
army.moveAll();
System.out.println("=======");
army.attackAll();

/*
Tank加入成功~
Radar加入成功~
Missile加入成功~
=======
tank move~
Radar move~
=======
tank attack~
Missile attack~
addWeaponException: 武器数量已满~
at Army.addWeapon(_499_武器数组作业.java:36)
at _499_武器数组作业.main(_499_武器数组作业.java:10)
*/
}
}

class Army{
Weapon[] w;
int index;

public Army(int count) {
w = new Weapon[count];
index = 0;
}

public void addWeapon(Weapon o) throws addWeaponException {
if(index < w.length){
w[index++] = o;
System.out.println(o + "加入成功~");
}
else throw new addWeaponException("武器数量已满~");
}

public void moveAll(){
for (int i = 0; i < w.length; i++) {
if(w[i] instanceof Movable){
Movable o = (Movable) w[i];
o.move();
}
}
}

public void attackAll(){
for (int i = 0; i < w.length; i++) {
if(w[i] instanceof Attachable){
Attachable o = (Attachable) w[i];
o.attack();
}
}
}
}

// 武器类
class Weapon{
@Override
public String toString() {
return "一个武器";
}
}

class Missile extends Weapon implements Attachable{
@Override
public void attack() {
System.out.println("Missile attack~");
}

@Override
public String toString() {
return "Missile";
}
}

class Radar extends Weapon implements Movable{
@Override
public void move() {
System.out.println("Radar move~");
}

@Override
public String toString() {
return "Radar";
}
}

class Tank extends Weapon implements Movable, Attachable{
@Override
public void move() {
System.out.println("tank move~");
}

@Override
public void attack() {
System.out.println("tank attack~");
}

@Override
public String toString() {
return "Tank";
}
}

// 可移动接口
interface Movable{
void move();
}

// 可攻击接口
interface Attachable{
void attack();
}

// 自定义异常
class addWeaponException extends Exception{
public addWeaponException() {
}

public addWeaponException(String message) {
super(message);
}
}
Prev
2022-01-25 17:25:00 # JavaSE
Next