快捷搜索:

Java 所有异常的详解

一,异常体系图

现实生活中的很多病况从面向对象的角度考虑也是一类事物,可以定义为类。

java中可以通过类对这一类不正常的现象进行描述,并封装为对象。

1. java的异常体系包含在java.lang这个包默认不需要导入。

2. java异常体系图

|——Throwable (实现类描述java的错误和异常)

|——Error (错误)一般不通过代码去处理。

|——Exceprion (异常)

|——RuntimeException (运行时异常)

|——非运行时异常

常见的Error:代码如下:

public class MainTest {

	public static void main(String[] args) {
		//java虚拟机默认管理了64M的内存,下面的数组需要1G内存
		//这样子会造成内存溢出
		byte[]by=new byte[1024*1024*1024];//1k--1M--1G
		System.out.println(by);
	}
}

错误原因:内存溢出。需要的内存已经超出了java虚拟机管理的内存范围。

错误(Error):

它指的是一个合理的应用程序不能截获的严重的问题。大多数都是反常的情况。错误是JVM的一个故障(虽然它可以是任何系统级的服务)。所以,错误是很难处理的,一般的开发人员(当然不是你)是无法处理这些错误的。比如内存溢出.

Throwable类:

写个例子:

/*
 Throwable类

 printStackTrace() 打印栈信息

 肺炎
 上呼吸道感染
 发烧
 流鼻涕感冒
 小感冒	
 */
class MainTest{

	public static void main(String[] args) {

		// Throwable able=new Throwable();
		Throwable able = new Throwable("想吐。。。");
		System.out.println(able.toString()); // 输出该异常的类名
		System.out.println(able.getMessage()); // 输出异常的信息
		able.printStackTrace(); // 打印栈信息
	}
}
效果图:

二,程序中的异常处理

1. 当除数是非0,除法运算完毕,程序继续执行。

2.当除数是0,程序发生异常,并且除法运算之后的代码停止运行。因为程序发生异常需要进行处理。

看如下代码:

class MainTest {

		public static void main(String[] args) {
			div(2, 0);
			System.out.println("over");
		}

		public static void div(int x, int y) {
	        //该行代码的y值可能是0,程序会出现异常并停止
			System.out.println(x / y);
			System.out.println("除法运算");
		}
	}
效果图:

所以,出现异常改怎么处理解决呢??

第一种方式:自行处理

(1) try{//可能发生异常的代码 }catch(异常类 变量名){//处理}。

(2)案例除法运算的异常处理。

(3)如果没有进行try catch处理,出现异常程序就停止。进行处理后,程序会继续执行。

这个案例可以使用如下代码解决~

class MainTest {

		public static void main(String[] args) {
			div(2, 0);
			System.out.println("over");
		}

		public static void div(int x, int y) {

			try {
				System.out.println(x / y); // 可能出现异常的语句,放入try中。
			} catch (ArithmeticException e) { // 进行异常匹配,
	             //异常信息
				System.out.println(e.toString());
				    System.out.println(e.getMessage());	
			 e.printStackTrace();
				System.out.println("除数不能为0");
			}
			System.out.println("除法运算");
		}
	}
那出现多个异常该怎么解决呢~

案例print方法,形参中增加数组。

在print方法中操作数组会发生新的异常

ArrayIndexOutOfBoundsException,NullPointerException),如何处理?

1. 使用将可能发生异常的代码放入try语句中,添加多个catch语句即可。

2. 可以处理多种异常,但是同时只能处理一种异常。

3. try中除0异常和数组角标越界同时出现,只会处理一种。

class MainTest {

		public static void main(String[] args) {

			System.out.println();
			int[] arr = { 1, 2 };
			arr = null;
			// print (1, 0, arr);
			print (1, 2, arr);

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

		public static void print(int x, int y, int[] arr) {

			try {
				System.out.println(arr[1]);
				System.out.println(x / y);
			} catch (ArithmeticException e) {
				e.toString();
				e.getMessage();
				e.printStackTrace();
				System.out.println("算术异常。。。");
			} catch (ArrayIndexOutOfBoundsException e) {
				e.toString();
				e.getMessage();
				e.printStackTrace();
				System.out.println("数组角标越界。。。");
			} catch (NullPointerException e) {
				e.toString();
				e.getMessage();
				e.printStackTrace();
				System.out.println("空指针异常。。。");
			}
			System.out.println("函数执行完毕");
		}
	}
效果图:

总结:

(1) 程序中有多个语句可能发生异常,可以都放在try语句中。并匹配对个catch语句处理。

(2) 如果异常被catch匹配上,接着执行try{}catch(){} 后的语句。没有匹配上程序停止。

(3) try中多个异常同时出现,只会处理第一条出现异常的一句,剩余的异常不再处理。

(4) 使用多态机制处理异常。

1. 程序中多态语句出现不同异常,出现了多个catch语句。简化处理(相当于急诊)。

2. 使用多态,使用这些异常的父类进行接收。(父类引用接收子类对象)

(5)多个catch语句之间的执行顺序:

1. 是进行顺序执行,从上到下。

2. 如果多个catch 内的异常有子父类关系。

1. 子类异常在上,父类在最下。编译通过运行没有问题

2. 父类异常在上,子类在下,编译不通过。(因为父类可以将子类的异常处理,子类的catch处理不到)。

3. 多个异常要按照子类和父类顺序进行catch

class Father {

}

class Son extends Father {

}

public class MainTest {

	public static void main(String[] args) {

		System.out.println();
		int[] arr = { 1, 2 };
		arr = null;
		Father f = new Father();
		div(1, 0, arr, f);
		
		System.out.println("over");
	}

	public static void div(int x, int y, int[] arr, Father f) {

		try {
			System.out.println(arr[1]); 
			System.out.println(x / y);
			Son s = (Son) f;

		} catch (ArithmeticException e) {
			e.toString();
			e.getMessage();
			e.printStackTrace();
			System.out.println("算术异常。。。");
		} catch (ArrayIndexOutOfBoundsException e) {
			e.toString();
			e.getMessage();
			e.printStackTrace();
			System.out.println("数组角标越界。。。");
		} catch (NullPointerException e) {
			e.toString();
			e.getMessage();
			e.printStackTrace();
			System.out.println("空指针异常。。。");
		} catch (Exception e) {
			e.toString();
			e.getMessage();
			e.printStackTrace();
			System.out.println("出错啦");
		}
		System.out.println("函数执行完毕");
	}
}
效果图:

处理异常应该catch异常具体的子类,可以处理的更具体,不要为了简化代码使用异常的父类。

第二种方式:抛出处理

定义一个功能,进行除法运算例如(div(int x,int y))如果除数为0,进行处理。

功能内部不想处理,或者处理不了。就抛出使用throw new Exception("除数不能为0"); 进行抛出。抛出后需要在函数上进行声明,告知调用函数者,我有异常,你需要处理如果函数上不进行throws 声明,编译会报错。例如:未报告的异常 java.lang.Exception;必须对其进行捕捉或声明以便抛出throw new Exception("除数不能为0");

public static void div(int x, int y) throws Exception { // 声明异常,通知方法调用者。

		if (y == 0) {
	throw new Exception("除数为0"); // throw关键字后面接受的是具体的异常的对象
		}
		System.out.println(x / y);
		System.out.println("除法运算");
	}

main方法中调用除法功能,调用到了一个可能会出现异常的函数,需要进行处理。如果调用者没有处理会编译失败。

如何处理声明了异常的函数

1.try{}catch(){}

public class MainTest {

	public static void main(String[] args) {

		try {
			div(2, 0);
		} catch (Exception e) {
			e.printStackTrace();
		}
		System.out.println("over");

	}

	public static void div(int x, int y) throws Exception { // 声明异常,通知方法调用者。

		if (y == 0) {
	throw new Exception("除数为0"); // throw关键字后面接受的是具体的异常的对象
		}
		System.out.println(x / y);
		System.out.println("除法运算");
	}

}
效果图:

2.继续抛出throws

public class MainTest {

		public static void main(String[] args) throws Exception {
			div(2, 0);
			System.out.println("over");
		}

		public static void div(int x, int y) throws Exception { // 声明异常,通知方法调用者。
		if (y == 0) {
			throw new Exception("除数为0"); // throw关键字后面接受的是具体的异常的对象
		}

			System.out.println(x / y);
			System.out.println("除法运算");
		}
	

}
效果图:

throw和throws的区别:

不同点:

使用的位置: throws 使用在函数上,throw使用在函数内

后面接受的内容的个数不同:

throws 后跟的是异常类,可以跟多个,用逗号隔开。

throw 后跟异常对象。

总结

(1)try语句不能单独存在,可以和catch、finally组成 try...catch...finally、try...catch、try...finally三种结构。

(2) catch语句可以有一个或多个,finally语句最多一个,try、catch、finally这三个关键字均不能单独使用。

(3) try、catch、finally三个代码块中变量的作用域分别独立而不能相互访问。如果要在三个块中都可以访问,则需要将变量定义到这些块的外面。

(4)多个catch块时候,Java虚拟机会匹配其中一个异常类或其子类,就执行这个catch块,而不会再执行别的catch块。(子类在上,父类在下)。

(5) throw语句后不允许有紧跟其他语句,因为这些没有机会执行。

(6)如果一个方法调用了另外一个声明抛出异常的方法,那么这个方法要么处理异常,要么声明抛出。

第三种方式:自定义异常

问题:现实中会出现新的病,就需要新的描述。

分析: java的面向对象思想将程序中出现的特有问题进行封装。

案例: 定义功能模拟凌波登录。(例如:lb(String ip))需要接收ip地址

1. 当没有ip地址时,需要进行异常处理。

1. 当ip地址为null是需要throw new Exception("无法获取ip");

2. 但Exception是个上层父类,这里应该抛出更具体的子类。

3. 可以自定义异常

2. 自定义描述没有IP地址的异常(NoIpException)。

1. 和sun的异常体系产生关系。继承Exception类,自定义异常类名也要规范,结尾加上Exception,便于阅读

/*
自定义异常	
*/
class NoIpException extends Exception {

	NoIpException() {

	}

	NoIpException(String message) {
		super(message);
	}

}

class MainTest {

	public static void main(String[] args) throws NoIpException {

		System.out.println();
		String ip = "192.168.10.252";
		ip = null;
		try {
			Lb(ip);
		} catch (NoIpException e) {
			System.out.println("程序结束");
		}

	}

	/*
	 * 
	 * 凌波教学
	 */
	public static void Lb(String ip) throws NoIpException {
		if (ip == null) {
			// throw new Exception("没插网线吧,小白");
			throw new NoIpException("没插网线吧,小白");
		}

		System.out.println("醒醒了,开始上课了。");
	}
}
效果图:

案例:模拟吃饭没带钱的问题

1. 定义吃饭功能,需要钱。(例如:eat(double money))

2. 如果钱不够是不能吃饭,有异常。

3. 自定义NoMoneyException();继承Exception 提供有参无参构造,调用父类有参构造初始化。at 方法进行判断,小于10块,throw NoMoneyException("钱不够");

4. eat 方法进行声明,throws NoMoneyException

5. 如果钱不够老板要处理。调用者进行处理。try{}catch(){} 。

class NoMoneyException extends Exception {

	NoMoneyException() {

	}

	NoMoneyException(String message) {
		super(message);
	}
}

class MainTest {

	public static void main(String[] args) {

		System.out.println();
		try {
			eat(0);
		} catch (NoMoneyException e) {
			System.out.println("跟我干活吧。");
		}
	}

	
	public static void eat(double money) throws NoMoneyException {
		if (money < 10) {
			throw new NoMoneyException("钱不够");
		}
		System.out.println("吃桂林米粉");
	}
}
效果图:

第四种方式:运行时异常和非运行时异常

1.RunntimeException--运行时异常

RunntimeException的子类:

(1)ClassCastException

多态中,可以使用Instanceof 判断,进行规避

(2)ArithmeticException

进行if判断,如果除数为0,进行return

(3)NullPointerException

进行if判断,是否为null

(4)ArrayIndexOutOfBoundsException

使用数组length属性,避免越界

这些异常时可以通过程序员的良好编程习惯进行避免的

总结:

遇到运行时异常无需进行处理,直接找到出现问题的代码,进行规避。

就像人上火一样牙疼一样,找到原因,自行解决即可

该种异常编译器不会检查程序员是否处理该异常

如果是运行时异常,那么没有必要在函数上进行声明。

案例如下:

1:除法运算功能(div(int x,int y))

2:if判断如果除数为0,throw new ArithmeticException();

3:函数声明throws ArithmeticException

4:main方法调用div,不进行处理

5:编译通过,运行正常

6:如果除数为0,报异常,程序停止。

7:如果是运行时异常,那么没有必要在函数上进行声明。

Object类中的wait()方法,内部throw了2个异常 IllegalMonitorStateException InterruptedException

只声明了一个(throws) IllegalMonitorStateException是运行是异常没有声明。

class MainTest {

	public static void main(String[] args){
		div(2, 1);
	}

	public static void div(int x, int y) {
		if (y == 0) {
			throw new ArithmeticException();  
		}
		System.out.println(x / y);
	}
}
结果输出为:2

2.非运行时异常--受检异常

如果出现了非运行时异常必须进行处理throw或者try{}catch(){}处理,否则编译器报错。

(1)IOException 使用要导入包import java.io.IOException;

(2)ClassNotFoundException

例如人食物中毒,必须进行处理,要去医院进行处理。

案例

1:定义一测试方法抛出并声明ClassNotFoundException(test())

2:main方法调用test

3:编译报错

未报告的异常 java.lang.ClassNotFoundException;必须对其进行捕捉或声明以便抛出

public void isFile(String path){
		try
		{
			/*
			根据文件的路径生成一个文件对象,如果根据该路径找不到相应的文件,
			则没法生成文件对象。
			*/
			File file = new File(path);
			//读取文件的输入流
			FileInputStream input = new FileInputStream(file);
			//读取文件
			input.read();
		}
		catch (NullPointerException e)
		{
			System.out.println("读取默认的文件路径..");
		}
		
	}

4:Sun 的API文档中的函数上声明异常,那么该异常是非运行是异常,

调用者必须处理。

5:自定义异常一般情况下声明为非运行时异常

就函数的重写和重载来说明这俩种异常吧~

1:运行时异常

案例定义Father类,定义功能抛出运行是异常,例如(test() throwClassCastException)

定义Son类,继承Father类,定义test方法,没有声明异常

使用多态创建子类对象,调用test方法

执行子类方法

函数发生了重写,因为是运行时异常,在父类的test方法中,可以声明throws 也可以不声明throws

class Father {
	void test() throws ClassCastException { // 运行时异常
		System.out.println("父类");
		throw new ClassCastException();
	}
}

class Son extends Father {
	void test() {
		System.out.println("子类");
	}
}
class MainTest {

	public static void main(String[] args) {
		Father f = new Son();
		f.test();
	}
}
结果输出:子类

2:非运行时异常

1.定义父类的test2方法,抛出非运行时异常,例如抛出ClassNotFoundException

1.此时父类test2方法必须声明异常,因为是非运行时异常

2.Son类定义test2 方法,抛出和父类一样的异常,声明异常

3.使用多态创建子类对象,调用test方法,调用test2方法,

1.声明非运行时异常的方法,在调用时需要处理,所以在main方法调用时throws

2.实现了重写,执行子类的test2方法

3.总结子类重写父类方法可以抛出和父类一样的异常,或者不抛出异常。

//1 子类覆盖父类方法父类方法抛出异常,子类的覆盖方法可以不抛出异常
class Father {
void test() throws ClassNotFoundException { // 非运行时异常
	System.out.println("父类");
	throw new ClassNotFoundException();
}
}

class Son extends Father {
void test() {
	System.out.println("子类");
	// 父类方法有异常,子类没有。
}
}
class MainTest {

public static void main(String[] args) throws ClassNotFoundException  {
	Father f = new Son();
	f.test();

}
}
结果输出:子类

4:子类抛出并声明比父类大的异常例如子类test2方法抛出Exception

1:编译失败,无法覆盖

2:子类不能抛出父类异常的父类。

3:总结子类不能抛出比父类的异常更大的异常。

//2:子类覆盖父类方法不能比父类抛出更大异常
class Father {
	void test() throws Exception {
		// 非运行时异常
		System.out.println("父类");
		throw new Exception();
	}
}

class Son extends Father {
	void test() throws ClassNotFoundException { // 非运行时异常
		System.out.println("子类");
		throw new ClassNotFoundException();
	}
}
class MainTest {

	public static void main(String[] args) throws Exception {
		Father f = new Son();
		f.test();

	}
}
效果图:

总结

1:子类覆盖父类方法是,父类方法抛出异常,子类的覆盖方法可以不抛出异常,或者抛出父类方法的异常,或者该父类方法异常的子类。

2:父类方法抛出了多个异常,子类覆盖方法时,只能抛出父类异常的子集

3:父类没有抛出异常子类不可抛出异常

子类发生非运行时异常,需要进行try{}catch的(){}处理,不能抛出。

4:子类不能比父类抛出更多的异常

第五种方式:finally

1: 实现方式一:

try{ // 可能发生异常的代码 } catch( 异常类的类型 e ){ // 当发生指定异常的时候的处理代码 }catch...

比较适合用于专门的处理异常的代码,不适合释放资源的代码。

2:实现方式二:

try{ } catch(){} finally{ // 释放资源的代码 }

finally块是程序在正常情况下或异常情况下都会运行的。

比较适合用于既要处理异常又有资源释放的代码

3:实现方式三

try{ }finally{ // 释放资源 }

比较适合处理的都是运行时异常且有资源释放的代码。

4:finally:关键字主要用于释放系统资源。

1:在处理异常的时候该语句块只能有一个。

2:无论程序正常还是异常,都执行finally。

5:finally是否永远都执行?

1:只有一种情况,但是如果JVM退出了System.exit(0),finally就不执行。

2:return都不能停止finally的执行过程。

案例使用流

1:使用FileInputStream加载文件。

导包import java.io.FileInputStream;

2:FileNotFoundException

导入包import java.io.FileNotFoundException;

3:IOException

import java.io.IOException;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class MainTest {
	// 本例子使用finally 关闭系统资源。
	public static void main(String[] args) {

		FileInputStream fin = null;
		try {
			System.out.println("1创建io流可能出现异常");
			fin = new FileInputStream("aabc.txt"); // 加载硬盘的文本文件到内存,通过流
			// System.out.println(fin);
		} catch (FileNotFoundException e) {
			System.out.println("2没有找到abc.txt 文件");
			System.out.println("3catch 了");
			// System.exit(0);
			// return;
		}
		// finally
		finally {
			System.out.println("4fianlly执行");
			if (fin != null) { // 如果流对象为null 流对象就不存在,没有必要关闭资源
				try {
					fin.close();
				} catch (IOException e) {
					e.printStackTrace();
					System.out.println("close 异常");
				}

			}
			System.out.println("5finally over");
		}
		System.out.println("6mainover");
	}
}

// 2:无论程序正常还是异常,都执行finally。 但是遇到System.exit(0); jvm退出。
// finally用于必须执行的代码, try{} catch(){}finally{}
// try{}finally{}
效果图:
经验分享 程序员 职场和发展