目录
1.异常处理
1.1异常概述、体系
什么是异常?
为什么要学习异常?
异常体系
编译时异常和运行时异常
总结:
1.3常见编译时异常
编译时异常
编译时异常示例
2.4异常的默认处理流程
2.6编译时异常的处理机制
编译时异常的处理形式有三种:
异常处理方式1 —— throws
异常处理方式2 —— try…catch…
异常处理方式3 —— 前两者结合
总结:
2.6运行时异常的处理机制
2.7异常处理使代码更稳健的案例
2.8自定义异常
自定义异常的分类
1、自定义编译时异常
2、自定义运行时异常
异常是程序在“编译”或者“执行”的过程中可能出现的问题,注意:语法错误不算在异常体系中。
比如:数组索引越界、空指针异常、 日期格式化异常,等…
异常一旦出现了,如果没有提前处理,程序就会退出JVM虚拟机而终止.
研究异常并且避免异常,然后提前处理异常,体现的是程序的安全, 健壮性。
Error:系统级别问题、JVM退出等,代码无法控制。
Exception:java.lang包下,称为异常类,它表示程序本身可以处理的问题
RuntimeException及其子类:运行时异常,编译阶段不会报错。 (空指针异常,数组索引越界异常)
除RuntimeException之外所有的异常:编译时异常,编译期必须处理的,否则程序不能通过编译。 (日期格式化异常)。
编译时异常,是在编译成class文件时必须要处理的异常,也称之为受检异常
运行时异常,在编译成class文件不需要处理,在运行字节码文件时可能出现的异常。
简单来说:
编译时异常就是在编译的时候出现的异常,运行时异常就是在运行时出现的异常。
1.异常是什么?
异常是代码在编译或者执行的过程中可能出现的错误。
2.异常分为几类?
编译时异常、运行时异常。
编译时异常:没有继承RuntimeExcpetion的异常,编译阶段就会出错。
运行时异常:继承自RuntimeException的异常或其子类,编译阶段不报错,运行可能报错。
3.学习异常的目的?
避免异常的出现,同时处理可能出现的异常,让代码更稳健。
1.2常见运行时异常
运行时异常
直接继承自RuntimeException或者其子类,编译阶段不会报错,运行时可能出现的错误。
运行时异常示例
数组索引越界异常: ArrayIndexOutOfBoundsException
空指针异常 : NullPointerException,直接输出没有问题,但是调用空指针的变量的功能就会报错。
数学操作异常:ArithmeticException
类型转换异常:ClassCastException
数字转换异常: NumberFormatException
运行时异常:一般是程序员业务没有考虑好或者是编程逻辑不严谨引起的程序错误,自己的水平有问题!
/**
拓展: 常见的运行时异常。(面试题)
运行时异常的概念:
继承自RuntimeException的异常或者其子类,
编译阶段是不会出错的,它是在运行时阶段可能出现的错误,
运行时异常编译阶段可以处理也可以不处理,代码编译都能通过!!
1.数组索引越界异常: ArrayIndexOutOfBoundsException。
2.空指针异常 : NullPointerException。
直接输出没有问题。但是调用空指针的变量的功能就会报错!!
3.类型转换异常:ClassCastException。
4.迭代器遍历没有此元素异常:NoSuchElementException。
5.数学操作异常:ArithmeticException。
6.数字转换异常: NumberFormatException。
小结:
运行时异常继承了RuntimeException ,编译阶段不报错,运行时才可能会出现错误!
*/
public class ExceptionDemo {
public static void main(String[] args) {
System.out.println("程序开始。。。。。。");
/** 1.数组索引越界异常: ArrayIndexOutOfBoundsException。*/
int[] arr = {1, 2, 3};
System.out.println(arr[2]);
// System.out.println(arr[3]); // 运行出错,程序终止
/** 2.空指针异常 : NullPointerException。直接输出没有问题。但是调用空指针的变量的功能就会报错!! */
String name = null;
System.out.println(name); // null
// System.out.println(name.length()); // 运行出错,程序终止
/** 3.类型转换异常:ClassCastException。 */
Object o = 23;
// String s = (String) o; // 运行出错,程序终止
/** 5.数学操作异常:ArithmeticException。 */
//int c = 10 / 0;
/** 6.数字转换异常: NumberFormatException。 */
//String number = "23";
String number = "23aabbc";
Integer it = Integer.valueOf(number); // 运行出错,程序终止
System.out.println(it + 1);
System.out.println("程序结束。。。。。");
}
}
总结:
运行时异常的特点:
运行时异常:继承自RuntimeException的异常或者其子类,编译阶段不报错,运行可能报错。
不是RuntimeException或者其子类的异常,编译阶就报错,必须处理,否则代码不通过。
编译时异常的作用是什么:
是担心程序员的技术不行,在编译阶段就爆出一个错误, 目的在于提醒不要出错!
编译时异常是可遇不可求。遇到了就遇到了呗。
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
目标:常见的编译时异常认识。
编译时异常:继承自Exception的异常或者其子类,没有继承RuntimeException
"编译时异常是编译阶段就会报错",
必须程序员编译阶段就处理的。否则代码编译就报错!!
编译时异常的作用是什么:
是担心程序员的技术不行,在编译阶段就爆出一个错误, 目的在于提醒!
提醒程序员这里很可能出错,请检查并注意不要出bug。
编译时异常是可遇不可求。遇到了就遇到了呗。
了解:
*/
public class ExceptionDemo {
public static void main(String[] args) throws ParseException {
String date = "2015-01-12 10:23:21";
// 创建一个简单日期格式化类:
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM-dd HH:mm:ss");
// 解析字符串时间成为日期对象
Date d = sdf.parse(date);
//
System.out.println(d);
}
}
总结:
编译时异常的特点
编译时异常:继承自Exception的异常或者其子类
编译阶段报错,必须处理,否则代码不通过。
1.默认会在出现异常的代码那里自动的创建一个异常对象:ArithmeticException。
2.异常会从方法中出现的点这里抛出给调用者,调用者最终抛出给JVM虚拟机。
3.虚拟机接收到异常对象后,先在控制台直接输出异常栈信息数据。
4.直接从当前执行的异常点干掉当前程序。
5.后续代码没有机会执行了,因为程序已经死亡。
/**
目标:异常的产生默认的处理过程解析。(自动处理的过程!)
(1)默认会在出现异常的代码那里自动的创建一个异常对象:ArithmeticException。
(2)异常会从方法中出现的点这里抛出给调用者,调用者最终抛出给JVM虚拟机。
(3)虚拟机接收到异常对象后,先在控制台直接输出异常栈信息数据。
(4)直接从当前执行的异常点干掉当前程序。
(5)后续代码没有机会执行了,因为程序已经死亡。
小结:
异常一旦出现,会自动创建异常对象,最终抛出给虚拟机,虚拟机
只要收到异常,就直接输出异常信息,干掉程序!!
默认的异常处理机制并不好,一旦真的出现异常,程序立即死亡!
*/
public class ExceptionDemo {
public static void main(String[] args) {
System.out.println("程序开始。。。。。。。。。。");
chu(10, 0);
System.out.println("程序结束。。。。。。。。。。");
}
public static void chu(int a , int b){
System.out.println(a);
System.out.println(b);
int c = a / b;
System.out.println(c);
}
}
总结:
默认异常处理机制。
默认的异常处理机制并不好,一旦真的出现异常,程序立即死亡!
出现异常直接抛出去给调用者,调用者也继续抛出去。
出现异常自己捕获处理,不麻烦别人。
前两者结合,出现异常直接抛出去给调用者,调用者捕获处理。
throws:用在方法上,可以将方法内部出现的异常抛出去给本方法的调用者处理。
这种方式并不好,发生异常的方法自己不处理异常,如果异常最终抛出去给虚拟机将引起程序死亡。
抛出异常格式:
规范做法:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
目标:编译时异常的处理方式一。
编译时异常:编译阶段就会报错,一定需要程序员处理的,否则代码无法通过!!
抛出异常格式:
方法 throws 异常1 , 异常2 , ..{
}
建议抛出异常的方式:代表可以抛出一切异常,
方法 throws Exception{
}
方式一:
在出现编译时异常的地方层层把异常抛出去给调用者,调用者最终抛出给JVM虚拟机。
JVM虚拟机输出异常信息,直接干掉程序,这种方式与默认方式是一样的。
虽然可以解决代码编译时的错误,但是一旦运行时真的出现异常,程序还是会立即死亡!
这种方式并不好!
小结:
方式一出现异常层层跑出给虚拟机,最终程序如果真的出现异常,程序还是立即死亡!这种方式不好!
*/
public class ExceptionDemo01 {
// public static void main(String[] args) throws ParseException, FileNotFoundException {
// System.out.println("程序开始。。。。。");
// parseTime("2011-11-11 11:11:11");
// System.out.println("程序结束。。。。。");
// }
//
// public static void parseTime(String date) throws ParseException, FileNotFoundException {
// SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM-dd HH:mm:ss");
// Date d = sdf.parse(date);
// System.out.println(d);
//
// InputStream is = new FileInputStream("E:/meinv.jpg");
// }
public static void main(String[] args) throws Exception {
System.out.println("程序开始。。。。。");
parseTime("2011-11-11 11:11:11");
System.out.println("程序结束。。。。。");
}
public static void parseTime(String date) throws Exception {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date d = sdf.parse(date);
System.out.println(d);
InputStream is = new FileInputStream("E:/meinv.jpg");
}
}
监视捕获异常,用在方法内部,可以将方法内部出现的异常直接捕获处理。
这种方式还可以,发生异常的方法自己独立完成异常的处理,程序可以继续往下执行。
格式:
建议格式:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
目标:编译时异常的处理方式二。
方式二:在出现异常的地方自己处理,谁出现谁处理。
自己捕获异常和处理异常的格式:捕获处理
try{
// 监视可能出现异常的代码!
}catch(异常类型1 变量){
// 处理异常
}catch(异常类型2 变量){
// 处理异常
}...
监视捕获处理异常企业级写法:
try{
// 可能出现异常的代码!
}catch (Exception e){
e.printStackTrace(); // 直接打印异常栈信息
}
Exception可以捕获处理一切异常类型!
小结:
第二种方式,可以处理异常,并且出现异常后代码也不会死亡。
这种方案还是可以的。
但是从理论上来说,这种方式不是最好的,上层调用者不能直接知道底层的执行情况!
*/
public class ExceptionDemo02 {
public static void main(String[] args) {
System.out.println("程序开始。。。。");
parseTime("2011-11-11 11:11:11");
System.out.println("程序结束。。。。");
}
public static void parseTime(String date) {
try {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM-dd HH:mm:ss");
Date d = sdf.parse(date);
System.out.println(d);
InputStream is = new FileInputStream("E:/meinv.jpg");
} catch (Exception e) {
e.printStackTrace(); // 打印异常栈信息
}
}
// public static void parseTime(String date) {
// try {
// SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM-dd HH:mm:ss");
// Date d = sdf.parse(date);
// System.out.println(d);
//
// InputStream is = new FileInputStream("E:/meinv.jpg");
// } catch (FileNotFoundException|ParseException e) {
// e.printStackTrace(); // 打印异常栈信息
// }
// }
// public static void parseTime(String date) {
// try {
// SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM-dd HH:mm:ss");
// Date d = sdf.parse(date);
// System.out.println(d);
//
// InputStream is = new FileInputStream("E:/meinv.jpg");
// } catch (FileNotFoundException e) {
// e.printStackTrace(); // 打印异常栈信息
// } catch (ParseException e) {
// e.printStackTrace();
// }
// }
// public static void parseTime(String date) {
// try {
// SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM-dd HH:mm:ss");
// Date d = sdf.parse(date);
// System.out.println(d);
// } catch (ParseException e) {
// // 解析出现问题
// System.out.println("出现了解析时间异常哦,走点心!!");
// }
//
// try {
// InputStream is = new FileInputStream("E:/meinv.jpg");
// } catch (FileNotFoundException e) {
// System.out.println("您的文件根本就没有啊,不要骗我哦!!");
// }
// }
}
方法直接将异通过throws抛出去给调用者
调用者收到异常后直接捕获处理。
import java.io.FileInputStream;
import java.io.InputStream;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
目标:编译时异常的处理方式三。
方式三: 在出现异常的地方把异常一层一层的抛出给最外层调用者,
最外层调用者集中捕获处理!!(规范做法)
小结:
编译时异常的处理方式三:底层出现的异常抛出给最外层调用者集中捕获处理。
这种方案最外层调用者可以知道底层执行的情况,同时程序在出现异常后也不会立即死亡,这是
理论上最好的方案。
虽然异常有三种处理方式,但是开发中只要能解决你的问题,每种方式都又可能用到!!
*/
public class ExceptionDemo03 {
public static void main(String[] args) {
System.out.println("程序开始。。。。");
try {
parseTime("2011-11-11 11:11:11");
System.out.println("功能操作成功~~~");
} catch (Exception e) {
e.printStackTrace();
System.out.println("功能操作失败~~~");
}
System.out.println("程序结束。。。。");
}
public static void parseTime(String date) throws Exception {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy、MM-dd HH:mm:ss");
Date d = sdf.parse(date);
System.out.println(d);
InputStream is = new FileInputStream("D:/meinv.jpg");
}
}
异常处理的总结
在开发中按照规范来说第三种方式是最好的:底层的异常抛出去给最外层,最外层集中捕获处理。
实际应用中,只要代码能够编译通过,并且功能能完成,那么每一种异常处理方式似乎也都是可以的。
运行时异常的处理形式
运行时异常编译阶段不会出错,是运行时才可能出错的,所以编译阶段不处理也可以。
按照规范建议还是处理:建议在最外层调用处集中捕获处理即可。
/**
目标:运行时异常的处理机制。
可以不处理,编译阶段又不报错。
按照理论规则:建议还是处理,只需要在最外层捕获处理即可
*/
public class Test {
public static void main(String[] args) {
System.out.println("程序开始。。。。。。。。。。");
try {
chu(10, 0);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("程序结束。。。。。。。。。。");
}
public static void chu(int a , int b) { // throws RuntimeException{
System.out.println(a);
System.out.println(b);
int c = a / b;
System.out.println(c);
}
}
案例:
需求:
键盘录入一个合理的价格为止(必须是数值,值必须大于0)。
分析:定义一个死循环,让用户不断的输入价格。
import java.util.Scanner;
/**
需求:需要输入一个合法的价格为止 要求价格大于 0
*/
public class Test2 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (true) {
try {
System.out.println("请您输入合法的价格:");
String priceStr = sc.nextLine();
// 转换成double类型的价格
double price = Double.valueOf(priceStr);
// 判断价格是否大于 0
if(price > 0) {
System.out.println("定价:" + price);
break;
}else {
System.out.println("价格必须是正数~~~");
}
} catch (Exception e) {
System.out.println("用户输入的数据有毛病,请您输入合法的数值,建议为正数~~");
}
}
}
}
自定义异常的必要?
Java无法为这个世界上全部的问题提供异常类。
如果企业想通过异常的方式来管理自己的某个业务问题,就需要自定义异常类了。
自定义异常的好处:
可以使用异常的机制管理业务问题,如提醒程序员注意。
同时一旦出现bug,可以用异常的形式清晰的指出出错的地方。
定义一个异常类继承Exception。
重写构造器。
在出现异常的地方用throw new 自定义对象抛出,
作用:编译时异常是编译阶段就报错,提醒更加强烈,一定需要处理!!
定义一个异常类继承RuntimeException.
重写构造器。
在出现异常的地方用throw new 自定义对象抛出!
作用:提醒不强烈,编译阶段不报错!!运行时才可能出现!!
/**
自定义的编译时异常
1、继承Exception
2、重写构造器
*/
public class ItheimaAgeIlleagalException extends Exception{
public ItheimaAgeIlleagalException() {
}
public ItheimaAgeIlleagalException(String message) {
super(message);
}
}
/**
自定义的编译时异常
1、继承RuntimeException
2、重写构造器
*/
public class ItheimaAgeIlleagalRuntimeException extends RuntimeException{
public ItheimaAgeIlleagalRuntimeException() {
}
public ItheimaAgeIlleagalRuntimeException(String message) {
super(message);
}
}
/**
目标:自定义异常(了解)
引入:Java已经为开发中可能出现的异常都设计了一个类来代表.
但是实际开发中,异常可能有无数种情况,Java无法为
这个世界上所有的异常都定义一个代表类。
假如一个企业如果想为自己认为的某种业务问题定义成一个异常
就需要自己来自定义异常类.
需求:认为年龄小于0岁,大于200岁就是一个异常。
自定义异常:
自定义编译时异常.
a.定义一个异常类继承Exception.
b.重写构造器。
c.在出现异常的地方用throw new 自定义对象抛出!
编译时异常是编译阶段就报错,提醒更加强烈,一定需要处理!!
自定义运行时异常.
a.定义一个异常类继承RuntimeException.
b.重写构造器。
c.在出现异常的地方用throw new 自定义对象抛出!
提醒不强烈,编译阶段不报错!!运行时才可能出现!!
*/
public class ExceptionDemo {
public static void main(String[] args) {
// try {
// checkAge(-34);
// } catch (ItheimaAgeIlleagalException e) {
// e.printStackTrace();
// }
try {
checkAge2(-23);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void checkAge2(int age) {
if(age < 0 || age > 200){
// 抛出去一个异常对象给调用者
// throw :在方法内部直接创建一个异常对象,并从此点抛出
// throws : 用在方法申明上的,抛出方法内部的异常
throw new ItheimaAgeIlleagalRuntimeException(age + " is illeagal!");
}else {
System.out.println("年龄合法:推荐商品给其购买~~");
}
}
public static void checkAge(int age) throws ItheimaAgeIlleagalException {
if(age < 0 || age > 200){
// 抛出去一个异常对象给调用者
// throw :在方法内部直接创建一个异常对象,并从此点抛出
// throws : 用在方法申明上的,抛出方法内部的异常
throw new ItheimaAgeIlleagalException(age + " is illeagal!")
}else {
System.out.println("年龄合法:推荐商品给其购买~~");
}
}