异常:Java运行时期发生的问题就是异常。
Java中运行时发生的除了异常Exception还要Error。
【Exception与Error】
异常:通常可以有针对性的处理方式。
错误:通常是系统级别的问题,都是Java所在系统发生的并反馈给Java的。无法针对处理,只能修正代码。
1. throw用在函数内,抛出异常对象,并可以结束函数。
public class ArrayException {
int getElement(int[] a,int index)
{
if(a==null)
{
throw new NullPointerException("该数组不存在");
}
if(index<0||index>=a.length)
{
throw new ArrayIndexOutOfBoundsException("错误的角标"+index+"越界!");
}
return a[index];
}
}
public class Demo {
public static void main(String[] args) {
ArrayException ex=new ArrayException();
int[] b= {23,12,54,232};
int num=ex.getElement(null, 4);
System.out.println(num);
}
}
(小练习:https://blog.csdn.net/weixin_43677405/article/details/104108362)
【自定义异常】
前面出现的几个异常都是Java定义好的异常,Java通过类进行的描述,并将问题封装成对象,这是符合面向对象的思想的。所以可以有自定义异常。
自定义异常被抛出,必须是继承Throwable,或者继承Throwable的子类。该对象才可以被throw抛出。
public class IllegalAgeException extends RuntimeException{
public IllegalAgeException()
{
super();
}
IllegalAgeException(String s)
{
super(s);//如果自定义异常需要异常信息,可以通过调用父类的带有字符串参数的构造函数即可。
}
}
public class Person {
private String name;
private int age;
Person(String name,int age) {
if(age<0||age>200)
{
throw new IllegalAgeException("输入的年龄非法!");
}
this.name=name;
this.age=age;
}
public String toString()
{
return "Person[name"+name+",age="+age+"]";
}
}
public class PersonDemo {
public static void main(String[] args) {
Person p=new Person("Tom", -13);
System.out.println(p);
}
}
上面这个例子,IllegalAgeException自定义异常到底继承Exception还是RuntimeException呢?
继承Exception,必须要throws声明,一声明就告知调用者进行捕获,一旦问题处理了调用者的程序会继续执行。
如果继承RuntimeException,不需要throws声明,因为调用者根本不知道问题所在,所以也不可能编写捕获代码。一旦发生了IllegalAgeException,调用者程序会停止,并Jvm将信息显示到屏幕,让调用者看到问题,修正代码。
【问题】
1. 继承Exception和继承RuntimeException有什么区别?
Exception会导致编译失败,因为编译器在检查语法时会发生错误。
RuntimeException不是功能本身的发生的异常,比如调用者传递参数错误而导致运行失败。这个异常不要声明出来。(声明的目的是为了让调用者进行处理。不声明的目的是不让调用者进行处理,就是为了让程序停止,让调用者看到现象,并进行代码的修正。)
2. 什么是捕获,什么是声明?(即要么把问题处理,要么把问题说出来让调用者知道。)
声明:将问题标识出来,报告给调用者。
如果函数内通过throw抛出编译异常,而捕获,那么必须通过throws进行声明,让调用者去处理。
捕获:Java对异常有针对性的语句进行捕获。
public class exception {
void show(int a) throws Exception
{
if(a>0)
throw new Exception();
else
System.out.println("show run");
}
}
public class ExceptionDemo {
public static void main(String[] args) {
exception d=new exception();
try
{
d.show(2);
}
catch(Exception ex)
{
System.out.println("发生异常!");
}
System.out.println("hello world!");
}
}
3. 异常分类
答:1.编译时异常,编译器会检测的异常。
2. 运行异常,编译器不会检测的异常,可声明亦可不声明。如果声明了,无外乎就是让调用者给出处理方式。
(运行时异常的小练习:https://blog.csdn.net/weixin_43677405/article/details/104114523)
(编译时的异常的小练习:https://blog.csdn.net/weixin_43677405/article/details/104115220)
【类型转换】
当我们抛出的异常对象处理不了的话,我们就需要将这个异常传换为接收异常可以处理的异常,这时就需要异常的转换了。
比如上面这个例子,
public void Educate()throws MaoYanException
对Teacher抛出电脑冒烟的问题,老师群体是处理不了的,所以我们就需要将MaoYanException转换成老师可以处理的异常,如NoPlanException。
public void Educate()throws NoPlanException
public class LanPingException extends Exception{
public LanPingException() {
super();
}
public LanPingException(String message)
{
super(message);
}
}
public class MaoYanException extends Exception {
public MaoYanException() {
super();
}
public MaoYanException(String message)
{
super(message);
}
}
public class NoPlanException extends Exception {
public NoPlanException() {
super();
}
public NoPlanException(String message)
{
super(message);
}
}
public class NoteBook {
private int state=2;
public void run()throws LanPingException,MaoYanException
{
if(state==1)
throw new LanPingException("电脑蓝屏了!");
if(state==2)
throw new MaoYanException("电脑冒烟了!");
System.out.println("电脑运行!");
}
public void reset()
{
state=0;
}
}
public class Teacher {
private String name;
private NoteBook notebook;
public Teacher(String name)
{
this.name=name;
notebook=new NoteBook();
}
public void Educate()throws NoPlanException
{
try
{
notebook.run();
}
catch(LanPingException ex)
{
System.out.println(ex.toString());
notebook.reset();
}
catch (MaoYanException e) {
System.out.println(e.toString());
throw new NoPlanException();//将MaoYanException转为为老师可以处理的NoPlanException
}
System.out.println("讲课!");
}
}
public class TeacherDemo {
public static void main(String[] args) {
Teacher t=new Teacher("Tom");
try
{
t.Educate();
}
catch(NoPlanException e)
{
System.out.println("放假!");
}
}
}
【throw和throws的区别】
1. throw用在函数内,throws用在函数上。
2. throw抛出的是异常对象,throws用于进行异常的声明,后面异常可以有多个,用逗号隔开。
public void run()throws LanPingException,MaoYanException
{
if(state==1)
throw new LanPingException("电脑蓝屏了!");
if(state==2)
throw new MaoYanException("电脑冒烟了!");
System.out.println("电脑运行!");
}
【finally的使用】
有一些特定的代码无论异常是否发生,都需要执行。但是,异常会引发程序的跳转,导致有些语句执行不到,无法满足这个需求。异常捕获处理时,Java提供解决方案:try catch finally,finally解决这个问题,这个代码块中存放的代码都是一定会被执行的。(但是,也有不执行的,System.exit(0))
public class FinException extends Exception{
public FinException() {
super();
}
public FinException(String message) {
super(message);
}
}
public class Fin {
void show (int a)throws FinException
{
if(a<0)
throw new FinException("数值非法");
System.out.println("number="+a);
}
}
public class FinDemo {
public static void main(String[] args) {
Fin f=new Fin();
try
{
f.show(-7);
System.out.println("***");//如果上一句有异常,那么这句话不执行,直接跳转到catch里
}
catch(FinException ex)
{
System.out.println(ex.getMessage());
//return;//这里使用return,在执行return之前会执行finally,最后的输出“over”不会执行
//System.exit(0);//如果这里使用System.exit(0)连finally都不会执行
}
finally
{
System.out.println("hello");
}
System.out.println("over");
}
}
如果finally里有结束语句的语句,那么catch如果有return,就不会执行catch的return语句了。
int show(int num)
{
try
{
if(num<0)
throw new Exception();
return 4;
}
catch(Exception ex)
{
System.out.println(ex.toString());
return 100;
}
finally
{
return 200;
}
}
如果我传的num=-1, int a=show(-1),a得到的值为200,因为在catch的return之前会执行return 200,而catch的return不会执行。
问题:finally什么时候用呢?
答:只要程序中使用到了具体的资源(数据库连接,IO资源,网络连接socker等)。需要释放,都必须定义在finally中。只要问题发生与否,指定程序都需要执行时,就定义在finally中。
【try catch finally的组合方式】
1. try catch: 可以对代码进行代码异常检测。并对检测的异常传递给catch处理。 异常捕获处理。
//这时不需要写throws,因为在catch里将已捕获了Exception
void show()
{
try
{
throw new Exception();
}
catch(Exception ex)
{
//...
}
}
//这时需要写throws
void show()throws Exception
{
try
{
throw new Exception();
}
catch(Exception ex)
{
try
{
throw ex;
}
catch(Exception e)
{
throw e;//最后在这里还是将异常往外抛,所以要在方法头写throws
}
}
}
2. try finally: 对代码进行异常检测,检测到异常后,因为没有catch,本方法无法处理该异常,所以一样会被默认jvm抛出。
异常时没有捕获处理,finally里可以完成一些关闭资源的操作。
//要写throws
void show()throws Exception
{
try
{
throw new Exception();
}
finally
{
//可以做一些关闭资源等操作
}
}
3. try catch finally: 检测异常,并传递给catch,并定义资源释放。
4. try catch(1) catch (2) ...
【异常在覆盖中的细节】
1. 子类覆盖父类方法时,如果父类的方法声明异常,子类只能证明父类异常或者异常的子类,或者不声明。
2. 当父类方法声明多个异常时,子类覆盖时只能声明多个异常的子集。
3. 当被覆盖的方法没有异常声明时,子类声明时也无法声明异常。(但是,可以声明RuntimeException)这种情况存在于继承和接口中。
问题:如果接口中没有声明异常,而实现的子类覆盖方法时发生了异常该怎么办?
答:无法进行throws声明,只能catch的捕获。如果问题处理不了,catch中继续throw抛出,但是只能将异常转换成RuntimeException子类抛出。
public interface Inter {
public void show();
}
public class InterDemo implements Inter{
public void show()throws RuntimeException
{
try
{
throw new Exception();//这个是无法声明的,只能通过catch捕获,将其转换为RuntimeException才可声明
}
catch(Exception e)
{
throw new RuntimeException();
}
}
}