Java编程基础之异常处理
知识点1:异常的理解
在使用计算机语言进行项目开发的过程中,即使程序员把代码写得尽善尽美,在系统的运行过程中仍然会遇到一些问题,因为很多问题不是靠代码能够避免的,比如:客户输入数据的格式,读取文件是否存在,网络是否始终保持通畅等等。
//InputMismatchException:输入不匹配的异常
public static void main(String[] args) {
System.out.println("请输入一个整型数据:");
Scanner scanner = new Scanner(System.in);
int number = scanner.nextInt();
System.out.println(number);
scanner.close();
}
知识点2:异常的体系结构
1. 体系结构
* 一、异常的体系结构
* java.lang.Throwable
* |----java.lang.Error:错误。一般不编写针对性的代码进行处理。
* |----java.lang.Exception:异常。可以使用针对性的代码进行处理。
* |-----编译时异常:编译过程中可能出现的异常类型
* |-----运行时异常(RuntimeException):编译过程(javac.exe)可以正常通过,运行时报出的异常。
*
2. 关于Error的理解
/**
* @author shkstart
* @create 2020-08-08 10:16
*
* Error:一般不编写针对性的代码进行处理。
*/
public class ErrorTest {
public static void main(String[] args) {
// main(args);//StackOverflowError:栈溢出的错误
//OutOfMemoryError: Java heap space:堆空间溢出的错误
byte[] arr = new byte[1024 * 1024 * 1024];//1GB
}
}
知识点3:常见的异常类型及举例(掌握)
1. 运行时异常
//InputMismatchException:输入不匹配的异常
public static void main(String[] args) {
System.out.println("请输入一个整型数据:");
Scanner scanner = new Scanner(System.in);
int number = scanner.nextInt();
System.out.println(number);
scanner.close();
}
//ArrayIndexOutOfBoundsException
@Test
public void test1(){
int[] arr = new int[10];
System.out.println(arr[10]);
System.out.println("hello");
}
//NullPointerException
@Test
public void test2(){
// Date date = new Date();
// date = null;
// System.out.println(date.toString());
String str = null;
System.out.println(str.toString());
}
//ArithmeticException:算术异常
@Test
public void test3(){
int m = 10;
int n = 0;
System.out.println( m / n);
}
//ClassCastException
@Test
public void test4(){
Object obj = new Date();
String str = (String) obj;
}
//NumberFormatException:数值格式化的异常
@Test
public void test5(){
String str = "123a";
int num = Integer.parseInt(str);
System.out.println(num);
}
2. 编译时异常
//FileNotFoundException
//IOException
@Test
public void test6(){
// File file = new File("hello.txt");
// FileInputStream fis = new FileInputStream(file);
// int data = fis.read();
// while(data != -1){
// System.out.print((char)data);
// data = fis.read();
// }
//
// fis.close();
}
//ClassNotFoundException
@Test
public void test7(){
Class clazz = Class.forName("java.lang.String");
}
知识点4:异常处理的方式1:try-catch-finally
1. 理解
/* 一、java程序的异常处理:抓抛模型
*
* 过程一:抛 (生成异常对象、并抛出)
* java程序在执行过程中,一旦出现异常,就会在异常出现的位置生成一个相应异常类型的对象。
* 并将此对象抛出。
* > 一旦程序出现异常,就不再执行异常之后的代码了。
*
* > 生成异常对象有两种方式:① 系统自动生成 ② 使用throw + 异常对象
*
* 过程二:抓 (异常处理的过程)
* 可以理解为异常处理的方式。
* 异常处理有两种方式:
* > 方式一:try-catch-finally
* > 方式二:throws
*/
2. try-catch
/*二、try-catch-finally的使用
* try{
* 可能存在异常的代码
* }catch(异常类型1 变量名1){
* 异常的处理方式1
* }catch(异常类型2 变量名2){
* 异常的处理方式2
* }...
* finally{
* 一定会被执行的代码
* }
*
* 说明:
* 1. finally是可选的。
* 2. 将可能存在异常的代码声明在try中。一旦执行过程中出现异常,此异常对象就会抛出。进而匹配之后的catch结构
* 一旦与某一个catch结构匹配,进入catch的大括号中执行异常处理的代码。一旦执行完此catch结构,不再执行其后
* 的其他catch结构。
* 3. 一旦程序通过try-catch的方式处理了异常,则程序让可以继续向下执行。
* 4. 如果声明了多个catch,多个catch对应异常类型不存在子父类关系的话,则哪个类型声明在上面,哪个
* 类型声明在下面都可以。
* 如果多个catch对应异常类型满足子父类的关系,则必须将子类类型声明在父类类型的上面。
* 5. 在try中声明的变量,在出了try结构以后就失效了。
* 6. catch中常见的异常处理的方式:
* ① 通过打印,指名异常的类型信息
* ② getMessage()
* ③ printStackTrace() (推荐)
* 7. try-catch-finally结构是可以嵌套使用的
*/
- 代码演示
public class TryCatchFinallyTest {
@Test
public void test1() {
int[] arr = null;
try {
arr = new int[10];
System.out.println(arr[10]);//创建了一个ArrayIndexOutOfBoundsException类型的对象。
System.out.println("hello1");
} catch (NullPointerException e) {
System.out.println("出现空指针的异常了....");
} catch (ArrayIndexOutOfBoundsException e) {
// System.out.println("出现角标越界的异常了....");
// System.out.println(e.getMessage());
e.printStackTrace();
} catch (RuntimeException e) {
System.out.println("出现了运行时的异常了....");
}
System.out.println("hello2");
System.out.println(arr[1]);
}
@Test
public void test2() {
try {
Object obj = new Date();
String str = (String) obj;
} catch (ClassCastException e) {
e.printStackTrace();
}
System.out.println("hello");
}
//###############针对于编译时异常来说########################
@Test
public void test3() {
try {
File file = new File("hello1.txt");
FileInputStream fis = new FileInputStream(file);
int data = fis.read();
while (data != -1) {
System.out.print((char) data);
data = fis.read();
}
fis.close();
}catch(FileNotFoundException e){
// System.out.println("出现文件找不到的异常了....");
e.printStackTrace();
}catch(IOException e){
// System.out.println("出现了IO的异常了...");
e.printStackTrace();
}
}
@Test
public void test4(){
try{
Class clazz = Class.forName("java.lang.String");
//。。。。
}catch(ClassNotFoundException e){
e.printStackTrace();
}
}
}
3.结论
/* 1. 针对于编译时异常,我们必须要进行异常的处理,否则编译不通过。
* 我们针对编译时异常进行处理以后,相当于将一个编译时异常转变为在运行时才可能出现的异常。
*
* 2. 针对于运行时异常,其实我们可以不使用异常处理。
*/
4. finally的使用
package com.atguigu.java;
import org.junit.Test;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Scanner;
/**
* @author shkstart
* @create 2020-08-08 14:15
*
*
* finally的使用
*
* 1. finally是可选的。
* 2.将一定会被执行的代码声明在finally中。
* 即使try、catch中存在未被处理的异常,或try、catch有return返回值结构,我们说,finally也是一定要被执行的。
*
* 3. 开发中哪些代码会放在finally中?
* IO流、Socket、数据库连接等资源,都需要手动的关闭。那么需要保证此关闭操作一定要被执行。否则,会
* 出现内存泄漏。
*
*
* 面试题:final \ finally \ finalize的区别
*
*/
public class FinallyTest {
@Test
public void test1(){
try{
int[] arr = new int[10];
System.out.println(arr[10]);
int m = 10;
int n = 0;
System.out.println( m / n);
}catch(ArithmeticException e){
e.printStackTrace();
// int[] arr = new int[10];
// System.out.println(arr[10]);
}finally{
System.out.println("hello----1");
}
System.out.println("hello----2");
}
public int method1(){
try{
int m = 10;
int n = 0;
System.out.println( m / n);
return 1;
}catch(ArithmeticException e){
return 2;
}finally{
return 3;
}
}
@Test
public void test2(){
int num = method1();
System.out.println(num);
}
@Test
public void test3(){
FileInputStream fis = null;
try {
File file = new File("hello.txt");
fis = new FileInputStream(file);
int data = fis.read();
while (data != -1) {
System.out.print((char) data);
data = fis.read();
}
}catch(FileNotFoundException e){
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
}finally{
//必须手动关闭资源
try {
if(fis != null)
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Test
public void test4(){
Scanner scanner = null;
try{
System.out.println("请输入一个整型数据:");
scanner = new Scanner(System.in);
int number = scanner.nextInt();
System.out.println(number);
}finally{
if(scanner != null)
scanner.close();
}
}
}
知识点5:异常处理的方式2:throws + 异常类型
1. 使用
/* 1. 异常处理的方式二:在方法的声明处,使用:throws + 异常类型
* > 在方法内,执行过程中,一旦出现异常,就会生成一个指定异常类型的对象。使用“throws + 异常类型”
* 方式处理异常的话,就会将此异常对象抛给方法的调用者。 方法的调用者需要继续考虑如何处理异常?
* 比如:method1()中出现的异常,method1选择了“throws + 异常类型”的方式处理,则将异常抛给了其调用
* 者:method2()
*/
- 代码演示
public class ThrowsTest {
public static void main(String[] args) {
ThrowsTest test = new ThrowsTest();
test.method3();
}
public void method3(){
try{
method2();
}catch(Exception e){
e.printStackTrace();
// System.out.println("出现异常了");
}
System.out.println("hello");
}
public void method2() throws Exception {
method1();
}
public void method1() throws FileNotFoundException , IOException {
File file = new File("hello.txt");
FileInputStream fis = new FileInputStream(file);
int data = fis.read();
while(data != -1){
System.out.print((char)data);
data = fis.read();
}
fis.close();
}
}
2. try-catch-finally与throws的选择
/*在开发中,如何选择异常处理的方式?
*
* ① 如果程序中涉及到一定要被执行的代码(比如:流、Socket、数据库连接等),我们需要选择
* try-catch-finally方式处理异常。
*
* ② 如果父类被重写的方法声明时,没有使用throws的方式抛出异常。则子类重写的方法内部如果有异常,
* 只能使用try-catch-finally的方式处理,不能使用throws的方式了。
*
* ③ 如果一个方法method1()内,先后调用了另外了几个方法。比如:method2(),method3(),method4().
* 此时method2(),method3(),method4()是递进关系的调用。此时,method2(),method3(),method4()
* 中,我们选择使用throws的方式处理异常。而在method1()中统一的使用try-catch-finally的方式处理异常。
*/
知识点6:使用throw+异常对象,手动抛出
/**
* @author shkstart
* @create 2020-08-08 15:40
*
* throw的使用:
* 在方法内部,可以手动的生成一个异常类的对象,并将此对象抛出。使用“throw + 异常对象”
*
*
*/
public class ThrowTest {
public static void main(String[] args) {
try {
Student s1 = new Student();
s1.regist(-1001);
System.out.println(s1);
}catch(Exception e){
// e.printStackTrace();
System.out.println(e.getMessage());
}
}
}
class Student{
private int id;
public void regist(int id) throws Exception{
if(id > 0){
this.id = id;
}else{
// throw new RuntimeException("输入的学号不能为0或负数");
// throw new Exception("输入的学号不能为0或负数");
//编译不通过
// throw new String("输入的学号不能为0或负数");
throw new MyException("输入的学号不能为0或负数");
}
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
'}';
}
}
- throw与throws的对比
/* 面试题:throw 和 throws 的区别
*
* throw:在生成异常对象并抛出的环节使用。表示手动抛出一个new的异常对象。
* 写在方法内部
* throws:是针对于生成异常对象并抛出之后,考虑如何处理异常的一种方式。
* 写在方法的声明处。
*/
知识点7:自定义异常类型
/**
* @author shkstart
* @create 2020-08-08 15:51
*
* 自定义异常类
* 1. 需要继承于现有的异常体系结构。比如:继承于RuntimeException 、 Exception
* 2. 需要提供一个序列版本号:serialVersionUID
* 3. 提供重载的构造器
*/
public class MyException extends Exception {
static final long serialVersionUID = -338751694229948L;
public MyException(String message) {
super(message);
}
public MyException() {
}
}