1、什么是异常,java提供异常处理机制有什么用?
程序在执行过程中发生了不正常的情况,而这种不正常的情况叫做:异常
java把该异常信息打印输出到控制台,让程序员看到异常信息后可以对程序进行修改,让程序更加健壮。
2、java语言安装异常是以什么形式存在的呢
异常在java中以类的形式存在,每一个异常类都可以创建异常对象
3、异常的继承结构
Throwable 不管是错误还是异常都是可以抛出的。
Error 所有的错误只要发生,java程序只有一个结果那就是终止程序的执行。退出JVM,错误是不能处理的。
ExceptionSubClass Exception的直接子类 所有Exception的直接子类都叫做编译时异常。
编译时异常是表示必须在编写程序的时候预先对这种异常进行处理,如果不处理,编译器报错。
所有的RuntimeException及子类都属于运行时异常。运行时异常在程序编写阶段可以选择处理也可以选择不处理。
Object
Object下有Throwable(可抛出的)
Throwable下有两个分支:Error(不可处理,直接退出JVM)和Exception(可处理的)
Exception下有两个分支:
Exception的直接子类:编译时异常
RuntimeException:运行时异常
4、编译时异常和运行时异常,都是发生在运行阶段。编译阶段异常是不会发生的
所有异常都是在运行阶段发生的。因为只有程序运行阶段才可以new对象。异常的发生就是在new异常对象。
5、编译时异常和运行时异常
编译时异常一般发生的概率比较高
运行时异常一般发生的概率比较低
假设java中没有对异常划分,没有分为:编译时异常和运行时异常,
所有的异常都需要在编写程序阶段对其进行预处理,将是怎样的效果?
首先,如果这样的话,程序肯定是绝对的安全。但是程序员编写程序太累,代码到处都是处理异常的代码。
编译时异常又叫做 受检异常 受控异常
运行时异常又叫做 未受检异常 非受控异常
7、java语言中对异常的处理包括两种方式:
第一种方式:在方法声明的位置上,使用throws关键字,抛给上一级。
第二种方式:使用try…catch语句进行异常的捕捉。
注意:java中异常发生之后如果一直上抛,最终抛给了main方法,main方法继续向上抛,抛给了调用者JVM,
JVM知道这个异常发生,只有一个结果,终止java程序的执行。
public class exceptionTest01 {
public static void main(String[] args) {
//通过"异常类"实例化"异常对象"
NumberFormatException nfe = new NumberFormatException("数组格式化异常!");
System.out.println(nfe);
//main方法中调用doSome()方法
//因为doSome()方法在声明的位置上有:throws ClassNotFoundException
//在调用doSome()方法的时候必须对这种异常进行预先的处理,如果不处理,编译器就会报错
//编译器报错信息:Unhandled exception: java.lang.ClassNotFoundException
//代码报错的原因:doSome()方法在声明位置上使用了:throws ClassNotFoundException,
//而ClassNotFoundException是编译时异常
//doSome();
}
/**
* doSome方法在声明的位置上使用了:throws ClassNotFoundException
* 这个代码表示doSome()方法在执行过程中,有可能会出现ClassNotFoundException异常。
* 叫做类没找到异常。这个异常的直接父类是:Exception,所以ClassNotFoundException属于编译时异常。
* @throws ClassNotFoundException
*/
public static void doSome() throws ClassNotFoundException{
System.out.println("doSome!!!");
}
}
处理异常的第一种方式:在方法声明的位置上使用throws关键字抛出,谁调用这个方法,就抛给谁。抛给调用者来处理。
注意:只要异常发生,采用上抛的方式,此方法的后续代码就不会执行。
另外注意,try语句块中的某一行出现异常,该行后面的代码不会执行。
try…catch捕捉异常之后,后续代码可以执行。
深入try…catch
1、catch后面的小括号中的类型可以是具体的异常类型,也可以是该异常类型的父类型。
2、catch可以写多个。建议catch的时候,精确的一个一个处理,这样有利于程序的调试。
3、catch写多个的时候,从上到下,必须遵守从小到大。
处理编译时异常,应该上抛还是捕捉,怎么选?
如果希望调用者来处理,选择throws上抛。
public class exceptionTest04 {
public static void main(String[] args) throws Exception {
try {
//创建输入流
FileInputStream fis= new FileInputStream("F:\\java\\课堂笔记\\day01-课程随笔.txt");
//读文件
// fis.read();
//System.out.println("以上出现异常,这里不法执行!");
//进行数学运算
System.out.println(100/0);//这个异常是运行时异常,编写程序时可以处理,也可以不处理
}catch (FileNotFoundException | ArithmeticException e ){
System.out.println("文件不存在?数字异常?都有可能!");
}catch(IOException e){
System.out.println("读文件报错!!!");
}
System.out.println("hello world!");
}
}
异常对象有两个非常重要的方法:
获取异常简单的描述信息
String msg =exception.getMessage();
打印异常追踪的堆栈信息
exception.printStackTrace();
如何查看异常的追踪信息,快速调试程序
异常追踪信息,从上往下一行一行看。
但需要注意的是:SUN写的代码就不用了看了。主要的问题是出现在自己编写的代码上。
catch中写上 e.printStackTrack()
public class exceptionTest05 {
public static void main(String[] args) {
//这里只是new了异常对象,但是没有将异常对象抛出。JVM会认为这是一个普通的java对象。
NullPointerException e = new NullPointerException("空指针异常");
NullPointerException e2 = new NullPointerException();
//获取异常简单描述信息,这个信息实际上就是构造方法上面的String参数。
String mse = e.getMessage();
System.out.println(mse);
String mse2 = e2.getMessage();
System.out.println(mse2);
//打印异常堆栈信息
//java后台打印异常堆栈追踪信息的时候,采用了异步线程的方式打印的。
/*
java.lang.NullPointerException: 空指针异常
at javase2.exception.exceptionTest05.main(exceptionTest05.java:13)
*/
e.printStackTrace();
System.out.println("hello world!");
}
}
关于try…catch中的finally子句:
1、在finally子句中的代码是最后执行的,并且是一定会执行的,即使try语句块中的代码出现了异常。
finally子句必须和try一起出现,不能单独编写。
2、finally语句通常使用在哪些情况下呢?
通常在finally语句块中完成资源的释放/关闭。
因为finally中的代码比较有保障。
即使try语句块中的代码出现异常,finally中代码也会正常执行。
public class exceptionTest06 {
public static void main(String[] args) {
FileInputStream fis = null;//声明位置放到try外面,这样在finally中才能用
try {
//创建输入流对象
fis= new FileInputStream("F:\\java\\课堂笔记\\day01-课程随笔.txt");
//...
String s = null;
//这里一定会出现空指针异常
s.toString();
//流使用完需要关闭,因为流是占用资源的。
//即使以上程序出现异常,流也必须要关闭
//放在这里有可能流关不了
//fis.close();
} catch (FileNotFoundException e) {
e.printStackTrace();//打印异常追踪的堆栈信息
}catch (IOException e){
e.printStackTrace();
}catch (NullPointerException e){
e.printStackTrace();
}finally {
//流的关闭放在这里比较保险
if (fis != null){ //避免空指针异常
try {
//close()方法有异常,采用捕捉的方式
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
public class ExceptionTest11 {
public static void main(String[] args) {
MyException e = new MyException("用户名不能为空!");
//打印异常堆栈信息
e.printStackTrace();//javase2.exception.MyException: 用户名不能为空!
//at javase2.exception.ExceptionTest11.main(ExceptionTest11.java:5)
//获取异常简单描述信息
String s = e.getMessage();
System.out.println(s);//用户名不能为空!
}
}
public class exceptionTest07 {
public static void main(String[] args) {
/*
try和finally,没有catch可以吗?可以
try不能单独使用。
try finally可以联合使用。
以下代码的执行顺序:
先执行try...
再执行finally...
最后执行 return (return语句只要执行,方法必然结束。)
*/
try{
System.out.println("try...");
return;
}finally {
System.out.println("finally...");
}
//这里不能写语句,无法执行到,return一旦执行,直接结束。如果去了return,这句就可以执行。
//System.out.println("hello world");
}
}
public class exceptionTest08 {
public static void main(String[] args) {
try{
System.out.println("try...");
//退出JVM
System.exit(0);//退出JVM之后,finally语句中的代码就不执行了
}finally {
System.out.println("finally...");
}
}
}
final finally finalize有什么区别?
final是一个关键字。表示最终的、不变的
final修饰的类无法继承
final修饰的方法无法覆盖
final修饰的变量能不能重新赋值
finally也是一个关键字,和try联合使用,使用在异常处理机制中。
finally语句块中的代码一定会执行的。
finalize()是Object类中的一个方法。作为方法名出现。所以finalize是标识符。
finalize()方法是JVM的GC垃圾回收器负责调用。
自定义异常在开发中的应用
java中如何自定义异常
1、编写一个类继承Exception或者RuntimeException。
2、提供两个构造方法,一个无参数的,一个带有String参数的。
栈操作中代码的改良,当栈满或者栈空时,不再只是return,而是采用异常处理机制的方法。
注意:throws 在方法声明位置上使用,表示上报异常信息给调用者。而throw 手动抛出异常
栈操作异常:自定义异常
public class MyStackOperationException extends Exception {
public MyStackOperationException(){
}
public MyStackOperationException(String s){
super(s);
}
}
栈类的定义
/*
编写程序,使用一维数组,模拟栈数据结构
1、这个栈可以存储java中的任何引用类型的数组。
2、在栈中提供push方法模拟压栈。(栈满了要有提示信息)
3、在栈中提供pop方法模拟弹栈。(栈空了要有提示信息)
4、假设栈的默认初始化容量是5。
*/
public class MyStack {//栈类
//提供一个数组来存储栈中的元素
private Object[] elements;
//栈帧,永远指向栈顶部元素
//注意:最初的栈是空的,一个元素都没有
//private int index = 0;//如果index采用0,表示栈帧指向了顶部元素的上方。
//private int index = -1;//如果index采用-1,表示栈帧指向了顶部元素。
private int index;
public MyStack() {
//一维数组动态初始化,默认初始化容量是10
this.elements = new Object[5];
this.index = -1;
}
/**
* 压栈的方法
* @param obj 被压入的元素
*/
public void push(Object obj) throws MyStackOperationException {
if(this.index>=this.elements.length-1){
//改良之前
//System.out.println("压栈失败,栈已满!");
//return;
//创建异常对象
//MyStackOperationException e = new MyStackOperationException("压栈失败,栈已满!");
//手动将异常抛出去
//throw e;
//合并
throw new MyStackOperationException("压栈失败,栈已满!");
}
//程序走到这里,说明栈没满
//向栈中加1个元素,栈帧向上移动一个位置
this.index++;
this.elements[index]=obj;
//elements[++index]=obj;
//所有的System.out.println()方法执行时,如果输出引用的话,自动调用引用的toString()方法
System.out.println("压栈"+obj +"元素成功,栈帧指向"+index);
}
/**
* 弹栈的方法,从数组中往外取元素。每取出一个元素,栈帧向下移动一位。
* @return
*/
public void pop() throws MyStackOperationException {
if(index < 0){
throw new MyStackOperationException("弹栈失败,栈已空!");
//System.out.println("弹栈失败,栈已空!");
}else{
System.out.print("弹栈"+this.elements[index] +"元素成功" );
//栈帧向下移动一位
index--;
System.out.println("栈帧指向"+index);
}
// return ;
}
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 exceptionTest12 {
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());
} catch (MyStackOperationException e) {
// e.printStackTrace();
//输出异常的简单信息
System.out.println(e.getMessage());
}
//弹栈
try {
stack.pop();
stack.pop();
stack.pop();
stack.pop();
stack.pop();
stack.pop();
} catch (MyStackOperationException e) {
//e.printStackTrace();
System.out.println(e.getMessage());
}
}
}