java 异常

异常:•Java语言中,将程序执行中发生的不正常情况称为“异常”

 Java程序运行过程中所发生的异常事件可分为两类:

  • Error: (不可查异常) JVM系统内部错误、资源耗尽等严重情况.如果程序出现了Error那么将无法恢复,只能重新启动程序,最典型的Error的异常是:OutOfMemoryError
  • RuntimeException(不可差异) ,是RuntimeException类及其子类异常,如NullPointerException(空指针异常),被0除异常,此种异常可以不用显示的处理,java没有要求我们一定要处理。程序中可以选择捕获处理,也可以不处理.
  • 一般性异常(可查异常):出现了这种异常必须在程序里面显示的处理(try-catch语句捕获它,要么用throws子句声明抛出它),否则程序无法编译通过

 

异常一般分为Error程序本身可以处理的异常(RuntimeException和一般性异常),一般性异常指Exception子类中初RuntimeException类及其子类外的其他类及其子类.

//RuntimeError
public class ExceptionTest1 {
	public static void main(String[] args) {
		Person p=new Person("Li",15);
//        	p=null; //编译时没问题,运行时报错 java.lang.NullPointerException
		System.out.println(p.name);//异常发生后,后面的程序不再执行
		
		int a=19;
		int b=0;
	//	System.out.println(a/b);//编译通过,运行报错, java.lang.ArithmeticException: / by zero
	}
}
class Person{
	private int age;
	String name;
	public Person(String name,int age) {
		this.age=age;
		this.name=name;
	}
}

jvm处理异常的方式

如果在main方法中可以处理该异常信息,比如捕获或者抛出,然后继续运行,如果不处理该异常信息,则调用main的jvm有默认的异常处理机制,比如在控制台打印出异常信息.

可以在出现异常的地方进行处理,捕获或者向上抛出(向调用它的方法抛出),可以多层抛出,如果都没有处理则最终会抛出到main方法,main方法不处理,则调用main的jvm默认处理,(源码里定义了处理方式)

总结如下:Java程序的执行过程中如出现异常,会自动生成一个异常类对象,该异常对象将被提交给Java运行时系统,这个过程称为抛出(throw)异常如果一个方法内抛出异常,该异常会被抛到调用方法中。如果异常没有在调用方法中处理,它继续被抛给这个调用方法的调用者。这个过程将一直继续下去,直到异常被处理。这一过程称为捕获(catch)异常如果一个异常回到main()方法,并且main()也不处理,则程序运行终止。程序员通常只能处理Exception,而对Error无能为力。

异常的作用:通过异常信息,可以快速定位程序问题,以便完善程序

throws关键字抛出异常

throws 关键字声明 抛出异常,在方法声明的位置上使用throws关键字向上抛出异常的列表,可以是方法中产生的异常类型,也可以是其父类,也可以是多个异常,用逗号隔开.

如果一个方法(中的语句执行时)可能生成某种异常,但是并不能确定如何处理这种异常,则此方法应显式地声明抛出异常,使用throws关键字抛出异常并不是真正的处理异常,而由该方法的调用者去处理。

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

public class ExceptionTest2  {
//在方法名后面加throws关键字抛出异常,抛出异常的种类可以通过调用类的api查,也可以是其父类
	public static void main(String[] args) throws FileNotFoundException {
		FileInputStream f =new FileInputStream("test.txt");
	}
}
out:
Exception in thread "main" java.io.FileNotFoundException: test.txt (系统找不到指定的文件。)
	at java.base/java.io.FileInputStream.open0(Native Method)
	at java.base/java.io.FileInputStream.open(FileInputStream.java:219)
	at java.base/java.io.FileInputStream.<init>(FileInputStream.java:157)
	at java.base/java.io.FileInputStream.<init>(FileInputStream.java:112)

 抛出异常的父类:

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

public class ExceptionTest2  {
	public static void main(String[] args) throws IOException {
		FileInputStream f =new FileInputStream("test.txt");
	}
}
out:
Exception in thread "main" java.io.FileNotFoundException: test.txt (系统找不到指定的文件。)
	at java.base/java.io.FileInputStream.open0(Native Method)
	at java.base/java.io.FileInputStream.open(FileInputStream.java:219)
	at java.base/java.io.FileInputStream.<init>(FileInputStream.java:157)
	at java.base/java.io.FileInputStream.<init>(FileInputStream.java:112)

多层向上抛出

public class ExceptionTest2  {
	//main方法调用Student的test1方法,不处理,抛出,调用main的JVM去处理
	public static void main(String[] args) throws FileNotFoundException {
		
		Student s=new Student();
		s.test1();
	}
	
}
class Student{
	//test1方法调用test2方法,test1也不处理,抛出
	public static void test1() throws FileNotFoundException {
		test2();
	}
	//test2方法调用test3方法,test2方法不处理,向上抛出
	public static void test2() throws FileNotFoundException {
		test3();
	}
	//在test3方法中出现异常,暂不处理,抛出
	public static void test3() throws FileNotFoundException {
		FileInputStream f=new FileInputStream("test.txt");
	}
}
out:
Exception in thread "main" java.io.FileNotFoundException: test.txt (系统找不到指定的文件。)
	at java.base/java.io.FileInputStream.open0(Native Method)
	at java.base/java.io.FileInputStream.open(FileInputStream.java:219)
	at java.base/java.io.FileInputStream.<init>(FileInputStream.java:157)
	at java.base/java.io.FileInputStream.<init>(FileInputStream.java:112)
	at com.chen.day6.Student.test3(ExceptionTest2.java:27)
	at com.chen.day6.Student.test2(ExceptionTest2.java:23)
	at com.chen.day6.Student.test1(ExceptionTest2.java:19)
	at com.chen.day6.ExceptionTest2.main(ExceptionTest2.java:12)

try-catch捕获异常

try捕获异常的首先用try{…}语句块选定捕获异常的范围,将可能出现异常的代码放在try语句块中。

catch (Exceptiontype e)当try语句块出现异常时,catch语句块才执行,在catch语句块中是对异常对象进行处理的代码。catch中的异常类型也要跟产生异常的类型相同,或者是其父类,如果是无关的异常类型,则不执行

try语句块后可以跟多个catch语句块,用于处理可能产生的不同类型的异常对象,但是catch异常类型只能从小到大.

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

public class ExceptionTest3 {
	public static void main(String[] args) {
		//try后面可以有多个catch(异常种类 e),异常种类的顺序要从小到大,先子类后父类
		try {
			FileInputStream f=new FileInputStream("test.txt");
			f.read();
		} catch (FileNotFoundException e) {
			//catch后面括号的内容是系统创建了一个FileNotFoundException对象,
			//相当于FileNotFoundException e=new FileNotFoundException();
			e.printStackTrace();
			String msg=e.getMessage();
//			System.out.println(msg);
		} catch (IOException e) {
			e.printStackTrace();
		}catch(Exception e) {
			e.printStackTrace();
		}
	}
}
out:
java.io.FileNotFoundException: test.txt (系统找不到指定的文件。)
	at java.base/java.io.FileInputStream.open0(Native Method)
	at java.base/java.io.FileInputStream.open(FileInputStream.java:219)
	at java.base/java.io.FileInputStream.<init>(FileInputStream.java:157)
	at java.base/java.io.FileInputStream.<init>(FileInputStream.java:112)
	at com.chen.day6.ExceptionTest3.main(ExceptionTest3.java:10)

catch异常后,创建了一个异常对象,可以访问异常对象的成员变量或调用它的方法。getMessage( ) 方法,用来得到有关异常事件的信息,printStackTrace( )用来跟踪异常事件发生时执行堆栈的内容。

 finally

 finally语句块使用方法:try….finally…(不常见),try…catch….finally,finally语句块可以不写,但写了之后不管try语句块是否发生异常,finally块中的语句一定会被执行。为异常处理提供一个统一的出口

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

public class ExceptionTest3 {
	public static void main(String[] args) {
		//try后面可以有多个catch(异常种类 e),异常种类的顺序要从小到大,先子类后父类
		try {
			FileInputStream f=new FileInputStream("test.txt");
			f.read();
		} catch (FileNotFoundException e) {
			//catch后面括号的内容是系统创建了一个FileNotFoundException对象,
			//相当于FileNotFoundException e=new FileNotFoundException();
			e.printStackTrace();
			String msg=e.getMessage();
//			System.out.println(msg);
		} catch (IOException e) {
			e.printStackTrace();
		}catch(Exception e) {
			e.printStackTrace();
		}finally {
			System.out.println("捕获到了异常,程序继续执行");
		}
	}
}
out:
java.io.FileNotFoundException: test.txt (系统找不到指定的文件。)
	at java.base/java.io.FileInputStream.open0(Native Method)
	at java.base/java.io.FileInputStream.open(FileInputStream.java:219)
	at java.base/java.io.FileInputStream.<init>(FileInputStream.java:157)
	at java.base/java.io.FileInputStream.<init>(FileInputStream.java:112)
捕获到了异常,程序继续执行

 try语句块中里面执行了return,finally中的代码也会执行,并且先执行finally语句块,才执行return.

 

public class FinallyTest {
	public static void main(String[] args) {
		Student1 person2=new Student1();
		int j=person2.test1();
		System.out.println(j);
	}
}
class Student1{
	public static int test1() {
		try{
			int i =10;//try里定义的变量在外部无法访问,相当于语句块
			return i;
		}catch (Exception e) {
			e.printStackTrace();
		}finally {

			System.out.println("finally 中语句块");
		}
			return 0;//当去掉catch语句块的时候可以直接执行,猜测有catch语句块的时候,
			//系统认为可能发生异常,执行不到return i这步,所以要求后面加个return 0,确保有返回值
	}
	public int test2() {
		try{
			int i =10;//try里定义的变量在外部无法访问,相当于语句块
			return i;
		}finally {
			System.out.println("finally 中语句块");
		}
//		return 0;//报错,不会被访问的语句,
	}
}

try-catch-finally的执行顺序

不管有没有出现异常,finally块中代码都会执行

如果try、catch中有return语句,finally中没有return,代码从try到finally顺序执行,因为finally语句块肯定执行,所以try里的语句并不会直接返回。在try语句的return语句中,return返回的引用变量并不是try语句外定义的引用变量i,而是系统重新定义了一个局部引用i’,这个引用指向了引用i对应的值,即使在finally语句中把引用i指向了新的值,但是return返回的引用是i',所以引用i的值和try语句中的返回值无关了。对于基本数据类型,静态变量,全局变量,引用数据类型,finally语句块不会改变其值,但是包装类型会改变其值,如ArrayList(下例3)

finally中有return语句,会屏蔽掉try - catch中的return,也会忽略掉try -catch中的异常,从finally中返回

如果finally中发生异常,代码执行将会抛出finally中的异常信息,try、catch中的异常将被忽略,所以finally尽量不要出现异常

public class FianllyTest2 {

	public static void main(String[] args) {
		Student2 student2=new Student2();
		int j=student2.test1();
		System.out.println(j);
	
	}
}
class Student2{
	public static int test1() {
		 int k=20;
		try{
			return k;
		}finally {
			k++;
			System.out.println("finally 中的"+k);
		}		
	}
}
out:
finally 中的21
20
分析:执行到try语句块的return k时,先将k赋值给一个变量temp,检查后面是否有finall语句,有的话先执行finally语句块内容,再执行将这个变量temp返回的操作,(也就是temp指针指向原来k指的地方,在finally中k指向了新的地方),如果finally语句中有return语句,那就不执行try中的finally语句了
public class FianllyTest2 {

	public static void main(String[] args) {
		Student2 student2=new Student2();
		int age=student2.test1();
		System.out.println(age);
	
	}
}
class Student2{
	public static int test1() {
		 Student3 s3=new Student3("zhang", 15);
		try{
			return s3.getAge();
		}finally {
			s3.setAge(28);
			System.out.println("finally 中的"+s3.getAge());
		}		
	}
}
class Student3{
	private int age;
	private String name;
	public Student3(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) {
		this.age = age;
	} 
}
out://说明引用对象类型,经过finally后也不会改变其值
finally 中的28
15

 例3,包装类型

public class FinallyTest4 {
	public static void main(String[] args) {
		ArrayList o=testWrap();
		System.out.println(o);
		
	}
	public static  ArrayList<Object> testWrap(){
        ArrayList<Object> list = new ArrayList<>();
        try{
            list.add("hello");
            System.out.println("try  hello ");
            return list;
        }catch(Exception e){
            list.add("catch");
            System.out.println("catch block");
            return list;
        }finally{
            list.add("world");
            System.out.println(" finally hello world  ");
        }
}
}
out:
try  hello 
 finally hello world  
[hello, world]

 自定义异常

当java里面的异常始终是有限的,无法满足开发者的需求时(比如没有用户不存在异常),可以自定义异常 

 通过查看异常类的源码,一般都是一个序列号和几个构造方法.

如果自定义异常是RuntimeException类型的,那就直接继承RuntimeException即可否则就继承Exception。继承之后一般提供两个构造方法.

package com.chen.day6;

import java.util.Arrays;

public class UserNotExistException extends RuntimeException {
	private static final long serialVersionUID = 1L;

	// 提供两个构造方法
	public UserNotExistException() {

	}

	public UserNotExistException(String msg) {
		super(msg);
	}

}
//提供一个类,作为返回值对象

class User {
}
测试类
class TestUserNotExistException {
	public static void main(String[] args) {
		String user = "AA";
		Object result = getUser(user);
		System.out.println(result);
	}

	public static User getUser(String user) {
		java.util.List<String> users = Arrays.asList("aa", "bb", "cc");
		if (users.contains(user)) {
			return new User();
		} else {
			throw new UserNotExistException("User is Not Exist");
		}
	}

}
out:
Exception in thread "main" com.chen.day6.UserNotExistException: User is Not Exist
	at com.chen.day6.TestUserNotExistException.getUser(UserNotExistException.java:34)
	at com.chen.day6.TestUserNotExistException.main(UserNotExistException.java:25)

 throw,在方法内部出现异常情况,程序不能继续执行,就用throw把异常对象抛出

throw和throws区别:

  • throws,写在方法声明后面,跟的是异常类型名字,如果有多个异常类型,用逗号隔开,本身不处理异常,由方法调用者处理
  • throw  用在方法体内,跟的是异常对象名字,只能抛出一个异常对象名,能抛出一个异常对象名,本身抛出异常,由方法体内的语句处理

 

例子:

编写应用程序EcmDef.java,接收命令行的两个参数,要求不能输入负数,计算两数相除。 对缺少命令行参数(ArrayIndexOutOfBoundsException)、 除0(ArithmeticException)及输入负数(EcDef 自定义的异常)进行异常处理。

提示:  (1)在主类(EcmDef)中定义异常方法(ecm)完成两数相除功能。  (2)main()方法中使用异常处理语句进行异常处理。  (3)在程序中,自定义对应输入负数的异常类(EcDef)  (4)运行时接受参数 java EcmDef 20 10   /args[0]=“20” args[1]=“10”

  (5)Interger类的static方法parseInt(String s)s转换成对应的int值。如int a=Interger.parseInt(“314”);  //a=314;

package com.chen.day6;

public class Ecmdef {

	public static void main(String[] args) {
		//多个异常只能catch一个
		try {
			int i = Integer.parseInt(args[0]);
			int j = Integer.parseInt(args[1]);
			int result = ecm(i, j);
			System.out.println("结果是: " + result);
		} catch (NumberFormatException e) {
			System.out.println("非法输入:输入参数不能转换成int型");
		} catch (ArrayIndexOutOfBoundsException e) {
			System.out.println("输入参数个数不足");
		} catch (ArithmeticException e) {
			System.out.println("不能被0除,除数不能是0");
		} catch (EcDef e) {
			System.out.println("不能输入负数");
			//e.printStackTrace();
		}

	}

	public static int ecm(int i, int j) {
		if (i < 0 || j < 0) {
			throw new EcDef(" 输入不能是负数");
		} else {
			return i / j;
		}
	}
}

class EcDef extends RuntimeException {
	//自定义异常
	public EcDef() {
	}

	public EcDef(String msg) {
		super(msg);
	}

}

 

 

参考http://www.atguigu.com/online.shtml

http://www.monkey1024.com/javase/429

https://www.cnblogs.com/superFish2016/p/6687549.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值