赞
踩
一个示例:
package exceptiontest;
public class ExceptionTest01 {
public static void main(String[] args) {
int a = 10;
int b = 0;
// int c = a / b; //运行后抛出异常:Exception in thread "main" java.lang.ArithmeticException: / by zero
// //at exceptiontest.ExceptionTest01.main(ExceptionTest01.java:7)
// //抛出这个异常的时候底层是new了一个ArithmeticException对象
// System.out.println(a + "/" + b + " = "+ c);
String s = null;
s.toString();//这里会发生空指针异常,程序执行到这里JVM会在底层new一个NullPointerException对象
}
}
注意:编译时异常并不是在编译阶段发生的异常,所有的异常发生都是在运行阶段的,因为每个异常发生都是会new异常对象的,new异常对象只能在运行阶段完成。那为什么叫做编译时异常呢?这是因为这种异常必须在编译阶段提前预处理,如果不处理编译器报错,因此而得名“编译时异常”。
package exceptiontest;
public class ExceptionTest02 {
public static void main(String[] args) {
// // 异常的发生需要经历2个阶段
// // 第一个阶段:创建异常对象
// NullPointerException e = new NullPointerException();
// // 第二个阶段:让异常发生(手动抛出异常)
// throw e;
//将以上两个阶段合并成一步
throw new NullPointerException();
}
}
运行结果:
第一步:编写异常类继承Exception或者RuntimeException
第二步:提供一个无参数构造方法,再提供一个带String msg参数的构造方法,在构造方法中调用父类的构造方法。
一个小示例:
①定义两个编译时异常:
②完成如下需求:
示例代码:
package exceptiontest.exception;
/**
* 自定义无效用户名异常:当用户名长度小于6位或者大于12位的,认为程序出现了异常
*/
public class IllegalNameException extends Exception {
public IllegalNameException() {
}
public IllegalNameException(String message) {
super(message);
}
}
package exceptiontest.exception;
/**
* 自定义无效年龄异常:当年龄小于18岁,认为程序出现了异常
*/
public class IllegalAgeException extends Exception {
public IllegalAgeException() {
}
public IllegalAgeException(String message) {
super(message);
}
}
package exceptiontest.exception; import javax.sound.midi.Soundbank; import java.util.Scanner; /** * 编写一个用户注册的方法,该方法接收两个参数,一个是用户名,一个是年龄。 * 如果用户名长度在[6 - 12]位,并且年龄大于18岁时,输出用户注册成功。 * 如果用户名长度不是[6 - 12]位时,让程序出现异常,让IllegalNameException异常发生! * 如果年龄小于18岁时,让程序出现异常,让IllegalAgeException异常发生! */ public class ExceptionTest03 { public static void main(String[] args) { System.out.println("欢迎来到用户管理系统,请先注册:"); Scanner scanner = new Scanner(System.in); System.out.println("请输入用户名:"); String name = scanner.next(); System.out.println("请输入年龄:"); int age = scanner.nextInt(); UserServise userServise = new UserServise(); userServise.register(name, age); } } /** *用户的业务类 */ class UserServise{ public void register(String name, int age){ System.out.println("正在注册[" + name + "],请稍后......"); UserDao userDao = new UserDao(); userDao(name, age); System.out.println("注册成功,欢迎[" + name + "]"); } } /** * 操作数据库的一个类 * 用户需要注册,其用户名和年龄是需要保存的 */ class UserDao{ public void save(String name, int age){ System.out.println("用户[" + name + "]的信息正在保存..."); if(name.length() < 6 || name.length()>12){ throw new IllegalNameException(); } if(age < 18){ throw new IllegalAgeException(); } System.out.println("用户[" + name + "]的信息保存成功!"); } }
但是以上代码在throw new IllegalNameException();
和IllegalAgeException
会报错,因为定义的异常继承的是Exception
,是编译时异常,需要对其进行处理,没有处理编译器自然会报错。
throws
关键字声明异常,告知调用者,调用这个方法可能会出现异常。这种处理方式的态度是:如果出现了异常则会抛给调用者来处理。try...catch
进行捕捉处理。这种处理方式的态度是:把异常抓住。其它方法如果调用这个方法,对于调用者来说是不知道这个异常发生的。因为这个异常被抓住并处理掉了。public void m() throws AException, BException... {}
AException
和BException
都继承了XException
,那么也可以这样写:public void m() throws XException{}
m()
方法时,编译器会检测到该方法上用throws
声明了异常,表示可能会抛出异常,编译器会继续检测该异常是否为编译时异常,如果为编译时异常则必须在编译阶段进行处理,如果不处理编译器就会报错。throws
,包括main方法的处理态度也是throws
,如果运行时出现了异常,最终异常是抛给了main方法的调用者(JVM),JVM则会终止程序的执行。因此为了保证程序在出现异常后不被中断,至少main方法不要再使用throws进行声明了。上述例子若使用throws
关键字:
package exceptiontest.exception; import java.util.Scanner; /** *第一种处理方式:声明异常 (throws关键字) */ public class ExceptionTest03 { public static void main(String[] args) throws IllegalAgeException, IllegalNameException { //向上throws异常,交给JVM处理,即程序中止,产生报错 System.out.println("欢迎来到用户管理系统,请先注册:"); Scanner scanner = new Scanner(System.in); System.out.println("请输入用户名:"); String name = scanner.next(); System.out.println("请输入年龄:"); int age = scanner.nextInt(); UserServise userServise = new UserServise(); userServise.register(name, age); //这里的代码有可能出现异常,出现了异常之后,后续程序不再执行 System.out.println("main over!"); } } /** *用户的业务类 */ class UserServise{ public void register(String name, int age) throws IllegalAgeException, IllegalNameException {//向上throws异常 System.out.println("正在注册[" + name + "],请稍后......"); UserDao userDao = new UserDao(); userDao.save(name, age); //这里的代码有可能出现异常,出现了异常之后,后续程序不再执行 System.out.println("注册成功,欢迎[" + name + "]"); } } /** * 操作数据库的一个类 * 用户需要注册,其用户名和年龄是需要保存的 */ class UserDao{ public void save(String name, int age) throws IllegalNameException, IllegalAgeException { //向上throws异常 System.out.println("用户[" + name + "]的信息正在保存..."); if(name.length() < 6 || name.length()>12){ throw new IllegalNameException(); //若程序执行到这产生了异常,则后续代码不再执行 //这里不能写任何代码,因为这里的代码永远不会执行 } if(age < 18){ throw new IllegalAgeException(); } System.out.println("用户[" + name + "]的信息保存成功!"); } }
运行结果:
name
无效:name
和Age
都无效(只抛出IllegalNameException异常,因为程序执行到throw new IllegalNameException();
就不再往下执行了):age
无效:如果一个异常发生后,不需要调用者知道,也不需要调用者来处理,选择使用捕捉方式处理。
try{
// 尝试执行可能会出现异常的代码
// try块中的代码如果执行出现异常,出现异常的位置往下的代码是不会执行的,直接进入catch块执行
}catch(AException e){
// 如果捕捉到AException类型的异常,在这里处理
}catch(BException e){
// 如果捕捉到BException类型的异常,在这里处理
}catch(XException e){
// 如果捕捉到XException类型的异常,在这里处理
}
// 当try..catch..将所有发生的异常捕捉后,这里的代码是会继续往下执行的。
上述例子若使用try...catch...
关键字:
package exceptiontest.exception.trycatchtest; import exceptiontest.exception.IllegalAgeException; import exceptiontest.exception.IllegalNameException; import java.util.Scanner; /** * 第二种处理方式:捕捉异常 (try...catch...关键字) */ public class ExceptionTest04 { public static void main(String[] args) { System.out.println("欢迎来到用户管理系统,请先注册:"); Scanner scanner = new Scanner(System.in); System.out.println("请输入用户名:"); String name = scanner.next(); System.out.println("请输入年龄:"); int age = scanner.nextInt(); UserServise userServise = new UserServise(); try { userServise.register(name, age); System.out.println("如果以上行的代码出现异常,这里的代码不会执行!"); } catch (IllegalAgeException e) { System.out.println("对不起,年龄不合法!"); } catch (IllegalNameException e) { System.out.println("对不起,用户名不合法!"); } System.out.println("main over!"); } } class UserServise{ public void register(String name, int age) throws IllegalAgeException, IllegalNameException { //这里可以try...catch,也可以throws,一般系统是要在前端有所提示,所以这里依旧选择throws System.out.println("正在注册[" + name + "],请稍后......"); UserDao userDao = new UserDao(); userDao.save(name, age); System.out.println("注册成功,欢迎[" + name + "]"); } } class UserDao{ public void save(String name, int age) throws IllegalNameException, IllegalAgeException { //throw new异常的位置一定都是搭配throws,因为产生的异常是需要传递出去的,不能自己产生自己就try...catch...捕捉处理了,这是没有意义的,所以这里使用throws向上抛 System.out.println("用户[" + name + "]的信息正在保存..."); if(name.length() < 6 || name.length()>12){ throw new IllegalNameException(); } if(age < 18){ throw new IllegalAgeException(); } System.out.println("用户[" + name + "]的信息保存成功!"); } }
运行结果:
没有异常(同上)
name
无效:
name
和Age
都无效(只提示用户名不合法,因为程序先判断的name,并throw new IllegalNameException();
,就直接被对应的catch分支捕捉处理了):
age
无效:
如果catch时从大到小:
try {
userServise.register(name, age);
System.out.println("如果以上行的代码出现异常,这里的代码不会执行!");
}catch (Exception e){
} catch (IllegalAgeException e) {
System.out.println("对不起,年龄不合法!");
} catch (IllegalNameException e) {
System.out.println("对不起,用户名不合法!");
}
直接编译报错:
try {
userServise.register(name, age);
System.out.println("如果以上行的代码出现异常,这里的代码不会执行!");
} catch (IllegalAgeException | IllegalNameException e) { //注意这里只有最后有一个e,|前面没有
System.out.println("对不起,年龄或名字不合法!");
}
运行结果:
当多个异常的处理方式一样的时候,推荐同时处理;若处理方式不一样(像上面),还是建议分开处理(细粒度更明确)!
同时注意:该用法所处理的异常必须是没有继承关系的,否则报错
package exceptiontest; import exceptiontest.exception.IllegalAgeException; public class ExceptionTest05 { public static void main(String[] args) { User user = new User(); user.setName("zhangsan"); try { //try...catch...捕捉异常 user.setAge(-1); } catch (IllegalAgeException e) { System.out.println("年龄不合法!"); } } } class User{ private String name; private int age; public User() { } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) throws IllegalAgeException { //产生异常的位置使用throw将异常抛出 if(age < 0 || age > 120){ //对于年龄进行异常处理 throw new IllegalAgeException(); } this.age = age; } }
运行结果:
2. JDK内置的异常的使用:
package exceptiontest; import java.io.FileInputStream; import java.io.FileNotFoundException; public class ExceptionTest06 { public static void main(String[] args) { m1(); } public static void m1(){ try { //try...catch...捕捉 m2(); } catch (FileNotFoundException e) { System.out.println("文件没有找到!"); } } public static void m2() throws FileNotFoundException { //向上抛出 FileInputStream in = new FileInputStream("D://Tools/a.txt"); //FileInputStream 类 throws FileNotFoundException,所以需要进行对应的异常处理 } }
exception.getMessage();
获取的message是通过构造方法创建异常对象时传递过去的String message
。exception.printStackTrace();
exception.getMessage();
示例代码:
package exceptiontest.exception.trycatchtest02; import exceptiontest.exception.IllegalAgeException; import exceptiontest.exception.IllegalNameException; import java.util.Scanner; /** * 异常的常用方法一:exception.getMessage() */ public class ExceptionTest07 { public static void main(String[] args) { System.out.println("欢迎来到用户管理系统,请先注册:"); Scanner scanner = new Scanner(System.in); System.out.println("请输入用户名:"); String name = scanner.next(); System.out.println("请输入年龄:"); int age = scanner.nextInt(); UserServise userServise = new UserServise(); try { userServise.register(name, age); } catch (IllegalAgeException e) { System.out.println(e.getMessage()); //e.getMessage()返回 《throw new IllegalAgeException("年龄不合法,应该大于18岁!");》中传入的信息 }catch (IllegalNameException e){ System.out.println(e.getMessage()); //同上理 } System.out.println("main over!"); } } class UserServise{ public void register(String name, int age) throws IllegalAgeException, IllegalNameException { //这里可以try...catch,也可以throws,一般系统是要在前端有所提示,所以这里依旧选择throws System.out.println("正在注册[" + name + "],请稍后......"); UserDao userDao = new UserDao(); userDao.save(name, age); System.out.println("注册成功,欢迎[" + name + "]"); } } class UserDao{ public void save(String name, int age) throws IllegalNameException, IllegalAgeException { //throw new异常的位置一定都是搭配throws,向上throws异常 System.out.println("用户[" + name + "]的信息正在保存..."); if(name.length() < 6 || name.length()>12){ throw new IllegalNameException("用户名不合法,长度应该在6-12位!"); } if(age < 18){ throw new IllegalAgeException("年龄不合法,应该大于18岁!"); } System.out.println("用户[" + name + "]的信息保存成功!"); } }
IllegalNameException
和IllegalAgeException
两个异常类同前面代码。
运行结果:
exception.printStackTrace();
示例代码:
package exceptiontest.exception.trycatchtest02; import exceptiontest.exception.IllegalAgeException; import exceptiontest.exception.IllegalNameException; import java.util.Scanner; /** * 异常的常用方法一:exception.printStackTrace() */ public class ExceptionTest07 { public static void main(String[] args) { System.out.println("欢迎来到用户管理系统,请先注册:"); Scanner scanner = new Scanner(System.in); System.out.println("请输入用户名:"); String name = scanner.next(); System.out.println("请输入年龄:"); int age = scanner.nextInt(); UserServise userServise = new UserServise(); try { userServise.register(name, age); } catch (IllegalAgeException e) { e.printStackTrace(); //打印异常堆栈信息 }catch (IllegalNameException e){ e.printStackTrace(); //打印异常堆栈信息 } //try...catch后依旧会执行 System.out.println("main over!"); } } class UserServise{ public void register(String name, int age) throws IllegalAgeException, IllegalNameException { //这里可以try...catch,也可以throws,一般系统是要在前端有所提示,所以这里依旧选择throws System.out.println("正在注册[" + name + "],请稍后......"); UserDao userDao = new UserDao(); userDao.save(name, age); System.out.println("注册成功,欢迎[" + name + "]"); } } class UserDao{ public void save(String name, int age) throws IllegalNameException, IllegalAgeException { //throw new异常的位置一定都是搭配throws,向上throws异常 System.out.println("用户[" + name + "]的信息正在保存..."); if(name.length() < 6 || name.length()>12){ throw new IllegalNameException("用户名不合法,长度应该在6-12位!"); } if(age < 18){ throw new IllegalAgeException("年龄不合法,应该大于18岁!"); } System.out.println("用户[" + name + "]的信息保存成功!"); } }
运行结果:
注意:出现main over!在不同位置输出是因为高版本的JDK使用了多线程并发,处理异常的是另一个线程。
package exceptiontest.finallytest;
public class ExceptionTest08 {
public static void main(String[] args) {
try{
String s = null;
s.toString();
}finally {
System.out.println("finally 中的 main over"); //即使上述程序发生异常,finally语句块中的代码都一定会执行!
}
System.out.println("main over"); //由于没有catch,上述程序发生异常后,直接中止,不会执行到这里
}
}
运行结果:
2. 使用IO流的示例代码(finally中释放资源):
package exceptiontest.finallytest; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; public class ExceptionTest09 { public static void main(String[] args) { FileInputStream in = null; try { in = new FileInputStream("D:\\workspace\\IDEAProjects\\Java_se\\src\\exceptiontest\\finallytest\\ExceptionTest08.java"); //开始读 byte[] bytes = new byte[1024]; //一次读取1KB int readCount = 0; while ((readCount =in.read(bytes))!=-1){ //in.read(bytes))获取到每次读取的长度,赋给readCount System.out.println(new String(bytes, 0, readCount)); //将每次读取到的内容输出 //in.close(); //要是关闭流的操作放在这里,一旦上述程序出现异常,则该行代码无法执行到,资源无法释放,会一直占用内存 } } catch (FileNotFoundException e) { e.printStackTrace(); }catch (IOException e){ e.printStackTrace(); }finally { //关闭流释放资源,为保证其一定执行,将其放到finally语句块中 if (in != null) { //先判断其是否为空 try { in.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
运行结果(输出读到的文件内容):
①final是一个关键字,修饰的类无法继承,修饰的方法无法覆盖,修饰的变量不能修改。
②finally是一个关键字,使用在异常处理机制中,必须联合try一起使用,finally语句块中的代码一定会执行。
③finalize是一个标识符,它是Object类中的一个方法,是由垃圾回收期GC负责调用的。
public static void main(String[] args) {
// 程序执行结果是?
System.out.println(get());
}
public static boolean get(){
try{
return true;
}finally {
return false;
}
}
结果为false
,finally语句块一定会执行,在try的 return true;
之前,执行finally代码块中的内容,返回false
。
注意:由于在上面已经return了,所以下面的位置不能写任何代码,否则编译报错:
public static void main(String[] args) {
// 程序执行结果是?
System.out.println(get());
}
public static int get(){
int i = 0;
try{
return i;
}finally {
i++;
System.out.println(i);
}
}
finally语句块中的代码一定会执行,所以i++
后先输出1,由于Java中遵循代码自上而下执行的规则,所以try中return i;
时输出的是i++
之前的状态,以下是反编译之后的效果:
public static int get(){
int i = 0;
int j = i;
i++;
return j;
}
所以最后返回的i
依旧为0,所以有结果:
public static void main(String[] args) {
// 程序执行结果是?
try{
System.out.println("try...");
return;
}finally {
System.out.println("finally...");
}
}
finally语句块中的代码一定会执行,在try中的return;
之前就执行了finally语句块中的代码,所以结果为:
public static void main(String[] args) {
// 程序执行结果是?
System.out.println("返回:" + get());
}
public static int get(){
try{
System.out.println("try...");
return 1;
}finally {
System.out.println("finally...");
}
}
同上,finally语句块中的代码一定会执行,在try中的return;
之前就执行了finally语句块中的代码,所以结果为:
package exceptiontest.finallytest;
public class ExceptionTest10 {
public static void main(String[] args) {
// 程序执行结果是?
try{
System.out.println("try...");
System.exit(0); //退出JVM
}finally {
System.out.println("finally...");
}
}
}
System.exit(0);
是退出JVM,此时finally语句块中代码无法执行了,所以结果为:
public static void main(String[] args) {
// 程序执行结果是?
System.out.println("返回:" + get());
}
public static int get(){
try{
System.out.println("try...");
System.exit(0); //退出JVM
}finally {
System.out.println("finally...");
}
return 1;
}
同上System.exit(0);
是退出JVM,所以 return 1;
虽然没有报错,但是实际上是永远执行不到的,所以结果为:
7. 补充一个s++
和++s
相关的
public static void main(String[] args) {
int s = 20;
s++;
System.out.println(s);
s = s++;
System.out.println(s);
s= ++s;
System.out.println(s);
}
输出结果为:
21
21
22
最开始 s++;
即s自增1,此时s=21
而后s = s++;
是先执行s赋值给s,此时s++
并没有让s自增1,此时s=21
,而最后的s= ++s;
是先让让s自增1后,再将新的值赋给s,所以此时s=22
。
原则:方法覆盖之后,不能比父类方法抛出更多的异常,可以更少。
注意:
对于运行时异常,可以不用遵循上述原则,父类不抛出异常,子类可以抛出RuntimeException
。但实际上开发不会说父类方法抛出的异常和子类覆盖的方法抛出不同异常!一般都会保持一致!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。