1.异常的基本概念
什么是异常:程序执行过程中的不正常情况
异常的作用:增强程序的健壮性
异常在java中以类的形式存在,每一个异常类都可以创建异常对象
2.异常的分类
2.1异常的层次结构
可以使用UML图来描述异常的继承结构
UML是一种统一建模语言,图标式语言,可在面向对象的编程语言中使用,用来描述类与类之间的关系,程序的执行流程,对象的状态等。Java软件开发中,软件设计师、系统分析师、架构师设计类,java软件开发人员必须能够看懂
常用软件:Rational Rose、startUML…
Object下有Throwable
Throwable下有两个分支:Error(不可处理,直接退出JVM)和Exception(可处理的)
2.2异常的分类
Exception下有两个分支:
(1)Exception的直接子类:编译时异常(受检异常、受控异常、CheckedException)
(2)RuntimeException:运行是异常(未受检异常、未受控异常、UncheckedException)
2.2.1编译时异常
要求程序员在编写阶段必须预先对这些异常预先进行处理,如果不处理编译器会报错
编译时异常一般发生的概率你较高
2.2.2运行时异常
在编写阶段程序员可以预先处理,也可以不管
运行时异常一般发生的概率比较低
2.2.3异常的处理方法
- 在方法声明的位置上,使用throws关键字(调用者会接收异常)
- 使用try…catch语句进行异常的捕获(调用者并不知道异常发生并被解决了)
注意:在java中异常发生后如果一直上抛,会抛给main方法,main方法抛给JVM,JVM只能终止程序的执行
public static void main(String[] args) throws ClassNotFoundException { //不建议在主方法上继续上抛
doSome();
}
public static void main(String[] args){
try{
doSome();
}catch (ClassNotFoundException e){
e.printStackTrace();
//e引用保存的内存地址是那个new出来的异常对象的内存地址
}
}
注意:
- 异常没有捕捉,而采取上抛的方式,异常发生后,该方法的后续代码不会执行
- try语句块中的,某一行出现异常,该行后面的代码不会执行
- try…catch捕捉异常后,后续代码可以执行
- 编译时异常和运行时异常都发生在运行阶段,因为只有程序再运行阶段才可以new对象
(异常的发生就是在new对象) - 如何选择上抛还是捕获:如果希望调用者来处理,选择throws上抛
2.3try、catch和finally
2.3.1深入try…catch
- catch后面的小括号中的类型可以是具体的异常类型,也可以是异常类型的父类
- catch可以写多个,建议精确的一个一个处理,这样有利于程序的调试
- catch写多个是,从上到下,必须遵守异常类从小到大的关系
try{
FileInputStream fis = new FileInputStream("D:\\scripts\\IdeaProjects")
fis.read();
}catch(FileNotFoundException e){
System.out.println("文件不存在");
}catch(IOException e){
e.printStackTrace();
}
JDK8新特性
try{
}catch(FileNotFoundException | IOException e){
e.printStackTrace();
}
2.3.2 finally关键字
通常在finally语句块中完成资源的释放、关闭
finally中的代码比较有保障,是“一定”会执行的
即使try语句块中的代码出现异常,finally中的代码也会正常执行
2.3.2.1 finally的使用
try不能单独使用,try、finally可以联合使用:以下代码先执行try…再执行finally…最后执行return(方法结束)
try{
System.out.println("try...");
return;
}finally{
System.out.println("finally...");
}
public class ExceptionTest{
public static void main(String[] args){
FileInputStream fis = null;//声明位置放在try外面,这样在finally中才能使用
try{
FileInputStream fis = new FileInputInputStream("D:\\stripts")
String s = null
s.toString();
//fis.close();流用完需要关闭,即使程序异常也需要关闭。但这里执行不到关闭流
}catch(FileNotFoundException e){
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
}finally{
//流的关闭放在这里比较保险
if(fis != null){
try{
fis.close();
}catch(IOException e){
e.printStackTrace();
}
}
}
}
try{
System.out.println("try...");
System.exit(0); //退出JVM后,finally语句中的代码将不再执行
}finally{
System.out.println("finally...");
}
2.3.2.2 finally面试题
public class ExceptionTest{
public static void main(String[] args){
int result = m();
System.out.println(result); //100
}
public static int m();
int i = 100;
try{
return i;
}finally{
i++;
}
}
通过反编译工具:DJ Java Decompiler
public static int m(){
int i =100;
int j = i;
i++;
return j;
}
Java语法规则结论:
- 方法体中的代码必须遵循自上而下的顺序依次逐行执行
- return语句一旦执行,整个方法必须结束
2.4getMessage和printStackTrace
try{
}catch(FileNotFoundException e){
//打印异常堆栈追踪信息。实际开发中,建议使用这个
e.printStackTrace();
//获取异常简单的描述信息
String msg = e.getMassage();
}
System.out.println("HelloWorld");//捕获异常后,程序不耽误执行,服务器不会遇到异常而宕机
2.4.1getMEssage()
public class ExceptionTest(){
public static void main(String[] args){
NullPointerException e = new NullPointerException("空指针异常");
String msg = e.getMessage();
System.out.println(msg);//空指针异常
//这里只是new了异常对象,并没有将异常对象抛出,JVM会认为只是一个普通的java对象
e.printStackTrace();
}
}
2.4.2printStackTrace()
如何查看异常的追踪信息,从而快速调试程序呢?
异常堆栈追踪信息从上往下一行一行的看,SUN写的代码不需要检查,问题出在自己编写的代码上
java后台打印异常堆栈追踪信息的时候,采用了异步线程的方式打印
3.如何自定义异常
SUN提供的JDK内置异常在实际开发中不够用,可以自定义异常类
步骤:
- 编写一个类继承Exception或者RuntimeException
- 提供两个构造方法,一个无参数的,一个带有String参数的
public class MyException extends Exception{//编译时异常
public MyException{
}
public MyException(String s){
super(s);
}
}
//public class MyException extends RunTimeException{}//运行时异常
public class ExceptionTest{
public static void main(String[] args){
//创建异常对象,灵位手动抛出
MyException e = new MyException("芜湖~~");
e.printStackTrace();//打印异常堆栈信息
Striong msg = e.getMessage();//获取简单异常信息
System.out.println(msg);
}
}
4.异常在实际开发中的作用
重点:自定义异常类,在程序中使用
4.1数组模拟压栈弹栈
public class MyStackOperationException extends Exception{
public MyStackOperationException{
}
public MyStackOperationException(String s){
super(s);
}
}
public class MyStack {
private Object[] elements;
private int index;
public MyStack() {
this.elements =new Object[10];
this.index = -1;
}
public void push(Object obj) throws MyStackOperationException{
if(index >=elements.length-1){
throw new MyStackOperationException("压栈失败,栈已满!")
}
index++;
elements[index]=obj;
System.out.println("压栈"+obj+"成功,栈帧指向"+index);
}
public void pop() throws MyStackOperationException{
if(index<0){
throw new MyStackOperationException("栈已空!")
}
System.out.println("弹栈"+elements[index]+"元素成功");
index--;
System.out.println("栈帧指向"+index);
}
public Object[] getElements() {
return elements;
}
public void setElements(Object[] elements) {
this.elements = elements;
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
}
public class MyStackTest {
public static void main(String[] args) {
MyStack stack = new MyStack();
// 压栈
try {
stack.push(new Object());
stack.push(new Object());
stack.push(new Object());
stack.push(new Object());
stack.push(new Object());
stack.push(new Object());
stack.push(new Object());
stack.push(new Object());
stack.push(new Object());
stack.push(new Object());
// 这里栈满了
stack.push(new Object());
} catch (MyStackOperationException e) {
// 输出异常的简单信息。
System.out.println(e.getMessage());
}
// 弹栈
try {
stack.pop();
stack.pop();
stack.pop();
stack.pop();
stack.pop();
stack.pop();
stack.pop();
stack.pop();
stack.pop();
stack.pop();
// 弹栈失败
stack.pop();
} catch (MyStackOperationException e) {
System.out.println(e.getMessage());
}
}
}
4.2用户注册
public class IllegalNameException extends Exception {
public IllegalNameException() {
}
public IllegalNameException(String message) {
super(message);
}
}
public class UserService {
/**
* 用户注册
* @param username 用户名
* @param password 密码
* @throws IllegalNameException 无效
*/
public void register(String username, String password) throws IllegalNameException {
if (null == username || username.length() < 6 || username.length() > 14) {
throw new IllegalNameException("用户名不合法");
}
System.out.println("注册成功,欢迎"+username);
}
}
public class Tset {
public static void main(String[] args) {
UserService userService = new UserService();
try {
userService.register("日天家的猫猫","1234578");
} catch (IllegalNameException e) {
System.out.println(e.getMessage());
}
}
}
5.方法覆盖与异常
重写之后的方法不能比重写之前的方法抛出更多的异常,可以更少
实际开发中方法覆盖时一般不做修改,直接复制
6.总结异常中的关键字
- try
- catch
- fianlly
- throws 在方法声明的位置上使用,表示上报异常信息给调用者
- throw 手动抛出异常
7.final、fianlly与finalize的区别
final 关键字
- fianl修饰的类无法继承
- final修饰的方法无法覆盖
- fianl修饰的变量无法重新复制
finally 关键字
- 与try一起联合使用
- finally语句块中的代码时不需执行的
finalize 标识符
- 是一个Object类中的方法名
- 有垃圾回收器GC负责调用