黑马程序员————Java基础日常笔记---对异常的理解

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

1,异常的由来:

一切事物皆对象, 问题也是现实生活中一个具体的事物,就像疾病,因此也可以通过java的类的形式进行描述,并对其进行封装成对象;

其实就是java对不正常情况进行描述后的对象的体现。

对于问题的划分:两种:

一种是严重的问题;一种非严重的问题;

对于严重的:java通过Error类进行描述。

对于Error一般不编写针对性的代码对其进行处理, 因为就像疾病,太严重了,就治愈不了了。

对于非严重的:java通过Exception类进行描述。对于Exception可以使用针对行的处理方法进行处理

但是无论Error或者Exception都具有一些共性的内容, 因此可以向上抽取出来形成异常的体系。

异常的体系:

Throwable

|---Error

|---Exception

|---RuntimeException

|---ArrayIndexOutOfBoundsException

异常体系的特点:

异常体系中的所有类以及建立的对象都具备可抛性,也就是说可以被throw和throws关键字所操作

只有异常体系具备这种特点;

其中throw和throws的用法区别:

throw定义在函数内,用于抛出异常的对象。

throws定义在函数上, 用于抛出异常类, 可以抛出多个,多个需要用逗号隔开;

如果需要具体的处理异常,就需要在调用者的函数中写上多个对应的catch(..Exception e)。


--------------------------------------------------------------------------------------,

2,异常的处理:

a:try处理语句————

java提供了特有的语句进行处理。

try{

需要被检测的代码;

}

catch(异常类 变量){

处理异常的代码;(处理方式)

}

finally{

一定会执行的语句;一般是关闭某种资源(像数据库)

}

异常处理执行的过程如图:


异常对象,抛给了d.div(4.0)这个位置,主函数中这个方法的位置上;

有try , 就尝试着去检测,有问题就检测到了,这个就是这个语句的功能;

Try 检测到了这个对象后,catch就捕捉到了, 定义的这个形参Exception e=new ArithmeticException();,这里体现多态的思想;

如果不写这个try的话, 那么主函数就没有检测, 说明主函数无法处理,那么他就最终抛给了JVM(java虚拟机),JVM就默认处理,调用默认的异常处理机制,具体调用了父类中的e.printStackTrace();这个方法,从而导致程序的停止。

b:声明异常————

如上图中, 这个除法的功能是别人编写的,主函数在调用的时候,try语句不一定会执行,因为不知道这个功能是否发生问题,那么这样就可处理,可不处理,那么在传参数的时候,就会出现程序异常, 因此这样的开发不是最优的;

传值的时候,值是不确定的, 所以b为0,就可能传递进来了, 就容易发生异常,因此在这个函数的后面加上一个标识,表示这个函数在调用的时候,可能出现问题,希望调用着处理,函数 函数名 throws 异常类名{}

那么声明标识和不声明有什么区别呢?

声明了, 就表明这个函数可能有问题,抛了的话, 那么调用这个函数中必须要有异常处理的语句, 或是再次抛出,最终jvm处理了。如果没有处理的话, 就会编译失败;

这样就提高了安全性。

如果不声明的话, 是没有问题的;

说明黄条部分:

一般不会再次抛出,会在调用的地方给处理;

3,多异常处理:

由来:在定义函数(功能)的时候,不止发生一个问题,那么函数的声明的问题就不止一个了,如果声明的越具体,处理的就会越具体,

这才是声明的特点;

声明多个具体,就声明多个具体的异常,

在try..catch语句中, 就需要对应有多个catch(异常类 变量);

如果抛的异常,不是指定的异常,那么就可以在最下面写上

catch(Exception e)

但是这样写有两点不好:

3_1:不具体, 不知道发生了什么事情,程序还在运行中,

如果发生了对方指定的异常以外的情况,这时应该让程序停止,需要知道那个程序出错, 错在哪里。

3-2如果catch(Exception e)放在最上面, 下面的两个异常处理的就不会执行了,这样就会失去意义。 

代码如下:

<pre name="code" class="java">class Demo
{
	int div(int a,int b) throws ArithmeticException,ArrayIndexOutOfBoundsException//表明这个程序在运行时可能发生异常,希望被处理
	{
		int[] arr=new int[a];
		System.out.println(arr[4]);//如果发生异常, 这里new ArrayIndexOutOfBoundsException();
		return a/b;//如果这里发生异常,这里new ArithmeticException();
	}
}
class ExceptionDemo5
{
	public static void main(String[] args)
	{
		Demo d=new Demo();
		try{
			int x =d.div(5,-1);
			System.out.println("X="+x);
		}
		catch(ArithmeticException e){
			System.out.println(e.getMessage());
			e.printStackTrace();
			System.out.println("出错了, 算术异常");
		}
		catch(ArrayIndexOutOfBoundsException e){
			System.out.println(e.getMessage());
			e.printStackTrace();
			System.out.println("数组角标越界了");
		}
		catch(Exception e){
			System.out.println("其他异常的处理");
			System.out.println(e);
		}
		System.out.println("over");
	}
}


 

真发生了问题,不会打印的,会用一个硬盘的文件记录下来,这个文件称为异常日志文件,那么网站的维护人员, 就经常的看日志;

3,对自定义异常的理解:

Java可以对问题进行封装, 我们自己是否可以对可能出现的问题进行特有的封装呢?

他是因为在异常体系中没有的异常, 就像是新出现的疾病,这些问题并未被java所描述和封装。

例如:在上述的程序中, 对于除数是负数, 也视为是错误的是无法进行运算的,那么久需要对这个问题进行自定义的描述。

首先想定义成异常类, 就首先需要继承Exception这个类, 这样就具备了异常类的可抛性,就可以使用throw和throws这两个关键字了。

但是自定义的异常类, java是不认识的, 这时 对出现异常的情况,就需要手动的判断,建立对象,并抛出。

这里有错误,

这时当在函数内部出现有throw抛出异常对象,那么必须给出对于的处理动作,

要么在函数的内部try ...catch 语句处理异常,

要么在函数上声明让调用者去处理,

一般情况下:在函数上声明让调用者去处理,

这时:

调用者主函数也有两种选择,一个是try catch , 一个是抛出异常给JVM

这里异常中没有信息,只有名称,

以前看的是java 在描述异常的时候, java自己定义的,

而这里是我们自己定义声明的,

父类中有getMessage()这个方法,

因此可以复写,

但是这样的写的话, 就比较繁琐,

可以优化,直接调用父类的构造方法,

也想拿到到底是哪里出现问题,

把负数的值赋给他,可以对自定义异常类进行更改:

代码如下:

//自定义异常,创建这个类,方便创建对象
/*异常体系中, 异常类和异常对象都需要被抛出,因为会导致程序跳转,
他们都具备可抛性, 这个可抛性是Throwable体系中的独有特点,
只有这个体系中的类和对象才可以被Throw和Throws操作,*/
class FuShuException extends Exception
{
	/*private String msg;
	FuShuException(String msg){
		this.msg=msg;
	}
	public String getMessage(){//可以重写这个getMessage()方法,
		return msg;
	}*/
	private int value;//看到底哪里出错
	FuShuException(){}
	FuShuException(String msg){//这样就可以优化了代码
		super(msg);
	}
	FuShuException(String msg,int value){//这样就可以优化了代码
		super(msg);
		this.value=value;
	}
	public int getValue(){
		return value;
	}
}
class Demo
{
	int div(int a,int b) throws FuShuException//表明这个程序在运行时可能发生异常,希望被处理
	{
		//int[] arr=new int[a];
		//System.out.println(arr[4]);//如果发生异常, 这里new ArrayIndexOutOfBoundsException();
		if(b<0){
			throw new FuShuException("出现了除数为零的情况.../by 负数",b); //自定义的异常, java是不认识的,这时就需要手动的,建立对象,并抛出,
		}
		return a/b;//如果这里发生异常,这里new ArithmeticException();
	}
}
class ExceptionDemo3 
{
	public static void main(String[] args)
	{
		Demo d=new Demo();
		try{
			int x =d.div(5,-1);
			System.out.println("X="+x);
		}
		catch(FuShuException e){
			System.out.println("haaa,出现异常了"+e.toString());//toString()方法中调用了getMessage()这个方法;
			System.out.println(e.getMessage()+"\n错误的值是"+e.getValue());
		}
		System.out.println("over");
	}
}

4,RuntimeException的理解

Exception中有一个特殊的子类异常RuntimeException运行时异常。

一般算术异常他是自动抛的, 那也可以手动抛出,可以调用这个类中的另一个构造方法,


代码如下:

class Demo
{
	int div(int a,int b) //throws ArithmeticException,ArrayIndexOutOfBoundsException//表明这个程序在运行时可能发生异常,希望被处理
	{
		if(b==0)
		{
			throw new ArithmeticException("被零除了")
		}
		return a/b;//如果这里发生异常,这里new ArithmeticException();
	}
}
class ExceptionDemo6
{
	public static void main(String[] args)
	{
		Demo d=new Demo();
		int x =d.div(5,-1);
		System.out.println("X="+x);
		System.out.println("over");
	}
}
结果:

这里函数内抛了, 但是在函数上并没有声明,而且调用函数出没有try ..catch 语句,

但是如果:


这里编译失败,那么就是函数内抛, 函数就需要标识,不标识, 就编译失败;

原因:

因为ArithmeticException类是RuntimeException类的子类, 

这就是RuntimeException类的特点:(运行中的异常类)

如果是RuntimeException及其子类在函数内抛出了,函数上不用声明,

还有:


也没有问题。

总结1:

如果在函数内容抛出改异常, 函数上可以不用声明,编译一样通过。

如果在函数上声明了该异常,调用者可以不用进行处理,编译一样通过

为什么在这里抛, 函数上不用声明呢?

如果b=0,下面的return就不能运行了,那么x就没有值了,

那么下面的程序都是失败的,这个时候, java虚拟机就认为这个不要处理,希望程序停掉;

因为:   

这里定义功能没有错, 而是使用的时候出错了,瞎传参数造成的,那么产生的问题有你来解决,只有把参数改掉,才可以,

那么在函数内抛了, 为什么不用声明呢?

如果声明了, 就希望调用者主函数处理,如果对方给出了处理方式 ,相当于这个问题就被隐藏了,不声明的话,就不知道这里有问题,  所以只要瞎传值, 程序就会结束,这样就迫使使用者改动;

为的是不让错误不清楚,尽早给出解决方案;


如果该异常的发生,无法再继续进行运算,就让自定义异常继承RuntimeException。

总结2:

异常有两种:

编译是被检测的异常:

该异常在编译时,如果没有处理(没有抛出也没有try),编译会失败。

该异常在标识,代表着可以被处理。

运行时异常(RuntimeException类)

在编译是, 不需要处理,编译器不检查。

该异常的发生,建议不处理,让程序停止,需要对代码进行修正。

5,finally的用处

什么时候用:

写程序去链接数据库, 数据库的服务器存储着很多的数据;

客户机拿到了数据库中的数据,需要断开链接,数据库的链接数一般是有限的,

数据库的服务器是一台主机,主机中的CPU处理性能是有限的

如果不断开, 在耗费数据库中的资源和在占用一个数据库的链接,那么第11个人就连不上(只能连10个),那么数据库的CPU就处理不了了, 所以需要连完一次,就关闭,

所以连接了数据库, 取了值后, 就断开,

所以不管是否在数据库中取到数据, 这个断开的动作都需要执行,


因此:

finally代码块:定义一定执行的语句,通常用于关闭资源;

举个例子,调用数据库中的数据,


抛给了调用者主函数,数据库异常和我(调用者)是没有关系的, 但是数据没有存成功, 这也是一个异常,

这个调用者就可以处理了,

这个是分层思想,定义分层, 模块式开发

6,异常的格式:

1,try{

别检测的语句

}

catch(异常类名 变量){

处理的语句

}

2,

try{

被检测的语句

}

catch(异常类名 变量){

处理的语句

}

finally{

一定执行的代码块, 一般用于关闭资源

}

3,

try{

别检测的语句

}

finally{

一定执行的代码块, 一般用于关闭资源

}

7,覆盖是的子父类的理解:

1,,子类在覆盖父类时,如果父类的方法抛出异常,那么子类的覆盖方法,只能抛出父类的异常或者是该异常的子类或者不抛。

2,如果子类中有新的异常问题, 需要直接在子类中进行处理,

为啥不能覆盖父类中没有的异常呢?

代码如下:

class CException extends Exception//C异常继承异常类
{

}
class AException extends Exception//A异常继承异常类
{
{
}
class BException extends AException//B异常继承异常类
{
{
}
class Fu
{
	void show()throws AException//在show()方法中继承的是AException
	{
		
	}
}
class Zi extends Fu
{
	void show()throws BException//子类只有继承BException
	{

	}
}
class Test 
{
	void Function(Fu f)
	{
		try
		{
			f.show();
		}
		catch (AException e)
		{
		}
	}
}

class FE_3
{
	public static void main(String[] args) 
	{
		new Test().Function(new Zi());
	}
}
如果子类继承CException , 那么就在子类的catch(AException e) ,=new CException();但是这两个异常类是没有继承关系的, 因此会报错。


最后:

异常的好处:

1,将问题进行封装;

2,将正常流程的代码和问题的代码相分离,方便于阅读。

异常的处理原则:

1,处理方式有两个:try 或throw.

2,调用到抛出异常的功能是, 抛出几个,就处理几个。

3,一个try可以对应多个catch, 多个catch,父类的catch放在最下面。

4,catch内,需要定义针对性的处理方式,不要简单的定义printStackTrace,输出语句,也不要不写。

5,当捕获到的异常,本功能处理不了的,可以继续在catch中抛出。然后再在catch 代码块中,try ..catch,在接收新的异常,并处理(汇款的例子)

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值