现在的位置: 首页 > 综合 > 正文

JavaSE 拾遗(6)——JavaSE 异常

2018年01月10日 ⁄ 综合 ⁄ 共 4227字 ⁄ 字号 评论关闭

异常是java里面专门对于问题这种概念抽象出来的东西。问题也是现实生活中的一种具体的事物,也可以通过 java 的类的形式进行描述,并封装成对象,这些对象就是 java 里面的异常的内容。

异常这节的内容主要包含对问题的描述和对问题解决方法的描述。对于问题解决方法,java特地用 try… catch… finally… 和 throws…throw这种结构来描述,用来区别程序正常情况下执行的代码,使正常情况下的业务逻辑更加清晰,提高代码的阅读性。对于问题,java 创建了 throwable 体系框架,专门用来描述问题,并配合实现 try.. catch… finally… 、throws 的功能,我们自定义异常只需要自定义 Throwable 体系框架的子类就行,特别是 Exception
下的子类。异常可以说是既有 java 语言内容,也有 java 类库体系框架内容。处理异常的部分属于 java 语言的内容,描述异常的部分属于java 类库体系框架的内容。

Java里面,问题或者异常,分为两种,一种是严重问题,不能解决的问题,出现这种问题程序必须停掉,这种问题都叫Error;另一种问题是非严重的问题,出现这种问题,意思是问题还可以解决,问题解决后,程序还可以运行,这种问题都叫做 Exception。

Throwable
|----Error
|----Exception
      |-----RuntimeException(运行时检测异常,也叫做不检测异常)
      |----编译时检测异常

自定义异常主要就是继承上面 Exception 类。

异常的定义

class ... extends Exception

class MyException extends Exception{
	MyException(String message){
		 super(message);
	}
}

Throwable 类的方法主要有

public String toString()

public void printStackTrace()

public String getMessage()

处理异常包括下面内容

  • 异常的声明 throws
  • 异常的抛出 throw
  • 异常的捕获 try .. catch(Exception e) ... finally ...
  • 异常处理实例——老师用电脑上课在覆盖时异常处理的使用情况

异常的声明 throws

方法有异常抛出的时候,需要 throws 声明,来提醒使用方法的对象,必须处理这个异常。如果是 RuntimeException 异常的时候,可以不用声明。

运行时异常 RuntimeException 

运行时异常又叫运行时发生异常,是运行的时候才会发生的异常,编译的时候不会检测,这种异常发生后,一般从设计逻辑上来说应用程序不能继续执行,必须马上停止,需要程序员对代码进行完善后才能运行,那么这种问题使用运行时异常描述。这是由两种情况,一种是这个问题本身就是伪问题,是代码实现的问题,比如数组越界、除数为0之类、类型转换异常等等,那么此时完善代码;另一个问题是,这个是个问题,但是由于设计缺陷,没有对象负责处理这个问题。那么此时就需要完善设计。比如,线程等待函数 wait(long timeout) 函数,会抛出
错误参数异常,如果 timeout 为负,这种情况下是需要程序员来解决,因为逻辑上此时程序有不能继续执行的问题。程序员调用 wait 函数,传了不符合逻辑的参数,所以此时,这个问题是调用者的问题,应该调用者负责处理。

编译时异常 非 RuntimeException

编译时异常又叫编译时检测异常,是编译的时候就能检测到的异常,这种异常发生后,一般程序从逻辑上来说可以继续执行。

异常的抛出 throw

throw 关键字是用来抛出异常对象的

throw new RuntimeException();

异常的捕获、处理

try{

}catch( Exception e ){

}finally{

}

根据 java 规范:在 try-catch-finally 中,如果 try-finally 或者 catch-finally 中都有 return,则两个 return 语句都执行并且最终返回到调用者那里的是 finally 中 return 的值;而如果finally 中没有 return,则理所当然的返回的是 try 或者 catch 中 return 的值,但是 finally 中的代码是必须要执行的,而且是在 return 前执行,但是会先执行 try 或者 catch 中的 return
语句中 return 后面跟的表达式,只是不执行 return,return 对应的 jvm 指令是 areturn ,等 finally 里面执行完了之后再执行 return ,除非碰到 System.exit(),只有代码中有system.exit() 这一种情况 才不会执行 finally  因为终止了虚拟机进程 。

/**
需求:验证 return 和 finally 语句执行的先后顺序

思路:

步骤:
*/
class ReturnFinally
{
	public String message(){
		//ReturnFinally f = (ReturnFinally)(new Object());
		System.out.println("returned");
		return "123";							//----  2
	}

	public String testFinal(){
		try{
			return message();					//----  1
		}catch(Exception e){
			e.printStackTrace();
			return "exception";
		}finally{
			System.out.println("do finally");	//----  3
			return "567";
		}
	}

	public static void main(String[] args){
		ReturnFinally test = new ReturnFinally();
		System.out.println(test.testFinal());
	}
}

这就是异常的处理,专门用一种结构来把异常的处理和程序正常逻辑分开。这样提高程序的可读性。

异常的处理,要么 catch,那么 throws,跟现实生活中一样,现实中的问题谁负责处理,异常就谁负责处理,现实生活什么时候会产生问题,程序就什么时候会产生异常,并且异常的处理有很强的分层的概念。

使用 try… catch(Exception e)… finally… 的时候需要注意,父类异常 catch 块放下面,否则下面的子类 catch 块是无用语句,那么编译器会报错。

异常处理实例——老师用电脑上课

写一个老师讲课的应用程序老师用电脑上课,电脑会有蓝屏和冒烟的问题。

此时老师和电脑是依赖关系,老师依赖电脑。对于蓝屏问题,是老师可以解决的问题,老师重新启动电脑就好,所以此时蓝屏问题定义为编译时异常,让老师必须具备解决蓝屏问题的能力。

对于冒烟问题,是老师解决不了的问题,老师必须通知电脑维修工程师来解决,或者老师必须上报这个问题,等电脑维修工程师解决而此时课就要停下来,整个应用程序也就需要停止,那么冒的问题就定义为运行时异常。

如果设计上,电脑冒烟了,课接着上老师给学生做测试,那么冒烟仍然可以设计为编译时异常。

class LanpingException extends Exception
{
	LanpingException(String message)
	{
		super(message);
	}
}
class MaoyanException extends Exception
{
	MaoyanException(String message)
	{
		super(message);
	}
}
class NoplanException extends Exception
{
	NoplanException(String message)
	{
		super(message);
	}
}

class Computer
{
	private int state=3;
	public void run()throws LanpingException,MaoyanException
	{
		if(state==2)
			throw new LanpingException("蓝屏");
		if(state==3)
			throw new MaoyanException("冒烟");
		System.out.println("运行");
	}
	public void reset()
	{
		state=1;
		System.out.println("重启");
	}
}
class Teacher
{
	private String name;
	private Computer c;
	Teacher(String name)
	{
		this.name=name;
		c=new Computer();
	}
	public void prelect()throws NoplanException
	{
		try
			{
				c.run();
			}
		catch (LanpingException e)
		{
			c.reset();
		}
		catch(MaoyanException e)
		{
			test();//test功能写在抛前边,因为抛出异常程序就停止了。
			throw new NoplanException("课时无法继续"+e.getMessage());
		}
		System.out.println("讲课");
	}

	public void test()
	{
	 System.out.println("练习");
	}
}
class ExceptionTest1
{
	public static void main(String[] args)
	{	
	Teacher t=new Teacher("毕老师");	
	try	
		{	
			t.prelect();	
		}	
	catch (NoplanException e)	
		{	
			System.out.println(e.toString());
			System.out.println("换老师");	
		}	
	}
}

在覆盖时异常处理的使用情况

函数的定义时涉及的内容,除了函数的访问修饰符、函数名、返回值、参数列表、函数体,还有函数的抛出的异常,throws ...,这也是函数属性的一种。

根据里氏替换原则:

覆盖时子类函数只能抛出父类函数抛出的异常或者子类异常
覆盖时子类函数只能抛出父类函数抛出的异常的子集的异常
如果父类或者接口的函数没有抛出异常,这说明,这个异常是底层的子类函数负责解决的,那么子类函数只能 try catch 自己处理,决不能抛,如果一个类的函数可以解决一个异常的父类,那么其子类异常这个类也一定要可以解决。所以异常的处理,其实也是接口里面功能的一种。

抱歉!评论已关闭.