JavaSE进阶20 - 异常、异常处理机制、异常处理原理、异常捕捉和上报的联合使用、异常对象的常用方法、finally子句、自定义异常、异常在实际开发中的应用

本文详细介绍了Java中的异常处理,包括异常的定义、作用、分类和处理机制。异常在Java中以类的形式存在,分为编译时异常和运行时异常。编译时异常需要在编译阶段处理,而运行时异常在程序运行时可以处理,也可以不处理。异常处理主要包括try...catch、finally语句和自定义异常。此外,文章还讨论了异常处理的策略,以及如何通过try...catch深入理解和使用finally子句。
摘要由CSDN通过智能技术生成

异常类

package com.bjpowernode.javase.exception;

/*
 * 1、什么是异常,异常机制有什么用?
 * 	  以下程序执行过程中发生了不正常的情况,而这种不正常的情况叫做:异常
 * java语言是很完善的语言,提供了异常的处理方式,以下程序执行过程中出现
 * 了不正常情况,java把该异常信息输出到控制台,供程序员参考。程序员看到
 * 异常信息之后,可以对程序进行修改,让程序更加的健壮。
 * 
 * 什么是异常:程序执行过程中的不正常情况
 * 异常的作用:增强程序的健壮性。
 * 
 * 2、以下程序执行控制台出现了:
 * 	Exception in thread "main" java.lang.ArithmeticException: / by zero
	at com.bjpowernode.javase.exception.ExceptionTest01.main(ExceptionTest01.java:19)
	这个信息被我们称为:异常信息。这个信息是JVM打印的。
	
 * */
public class ExceptionTest01 {

  
	public static void main(String[] args) {
		
		int a = 10;
		int b = 0;
		// 实际上JVM在执行到此处的时侯,会new异常对象:new ArithmeticException("/ by zero");
		// 并且JVM将new的异常对象抛出,打印输出信息到控制台了。
		int c = a / b; 
		System.out.println(a + "/" + b + "=" + c);
		
		// 此处运行也会创建一个:ArithmeticException类型的异常对象。
		// System.out.println(100 / 0);
		
//		int a = 10;
//		int b = 2;
//		if(b == 0) {
//			System.out.println("除数不能为0");
//			return;
//		}
//		// 程序执行此处说明除数一定不为0
//		int c = a / b;
//		System.out.println(a + "/" + b + "=" + c);
	}

}

package com.bjpowernode.javase.exception;

/*
	java语言中异常是以什么形式存在的呢?
		1、异常在java中以类的形式存在,每一个异常类都可以创建对象。
		2、异常对应的现实生活是怎样的?
			火灾(异常类):
				2008年8月8日,小明家着火了(异常对象)
				2008年8月9日,小刚家着火了(异常对象)
				2008年9月8日,小红家着火了(异常对象)
			类是:模板。
			对象是:实际存在的个体。
 * */
public class ExceptionTest02 {

	public static void main(String[] args) {
		
		// 通过"异常类"实例化"异常对象"
		NumberFormatException nfe = new NumberFormatException("数字格式化异常!");
		
		// java.lang.NumberFormatException: 数字格式化异常!
		System.out.println(nfe);
		
		// 通过"异常类"实例化"异常对象"
		NullPointerException npe = new NullPointerException("空指针异常发生了!");
		
		// java.lang.NullPointerException: 空指针异常发生了!
		System.out.println(npe);
		

	}

}

异常处理机制

  • 异常在java中以类和对象的形式存在。那么异常的继承结构是怎样的?
    • 我们可以使用UML图来描述一下结构。
    • 画UML图有很多工具,例如:Rational Rose(收费的)、 starUML等…
    • 异常的继承结构
Object
Object下有Throwable(可抛出的)
Throwable下有两个分支:Error(不可处理,直接退出JVM)和Exception(可处理的)
Exception下有两个分支:
	Exception的直接子类:编译时异常(要求程序员在编写程序阶段必须预先对这些异常进行处理,如果不处理编译器进行报错。)
	RunTimeException:运行时异常。(在编写程序阶段程序员可以预先处理,也可以不管。)
  • 编译时异常和运行时异常,都是发生在运行阶段。编译阶段异常是不会发生的。编译时异常因为什么而得名?

    • 因为编译时异常必须在编译(编写)阶段预先处理,如果不处理编译器报错,因此得名。
    • 所有异常都是在运行阶段发生的。因为只有程序运行阶段才可以new对象。因为异常的发生就是new异常对象。
  • 编译时异常和运行时异常的区别?

    • 编译时异常一般发生的概率比较高。
      • 举个例子:你看到外面下雨了,倾盆大雨的。
      • 你出门之前会预料到:如果不打伞,我可能会生病(生病是一种异常)。
      • 而且这个异常发生的概率很高,所以我们出门之前要拿一把伞。
      • “拿一把伞”就是对“生病异常”发生之前的一种处理方式。
      • 对于一些发生概率较高的异常,需要在运行之前对其进行预处理。
    • 运行时异常一般发生的概率比较低。
      • 举个例子:小明走在大街上,可能会被天上的飞机轮子砸到。
      • 被飞机轮子砸到也算是一种异常。
      • 但是这种异常发生概率较低。
      • 在出门之前你没必要提前对这种发生概率较低的异常进行预处理。
      • 如果你预处理这种异常,你将活得很累。
    • 假设java中没有对异常进行划分,没有分为:编译时异常和运行时异常,所有的异常都需要在编写程序阶段对其进行预处理,将是怎样的效果呢?
      • 首先,如果这样的话,程序肯定是绝对安全的。
      • 但是程序员编写程序太累,代码到处都是处理异常的代码。
    • 编译时异常又被称为:受检异常,受控异常。CheckedException
    • 运行时异常又被称为:未受检异常,非受控异常。UnCheckedException
    • 再次强调:所有异常发生在运行阶段的。
  • java语言中对异常的处理包括两种方式

    • 第一种方式:在方法声明的位置上,使用throws关键字,抛给上一级。谁调用我,我就抛给谁,抛给上一级。
    • 第二种方式:使用try…catch语句进行异常的捕捉。 这件事发生了,谁也不知道,因为我给抓住了。
      • 举个例子:我是某集团的一个职员,因为我的失误,导致公司损失了1000元。
      • “损失1000元”这可以看做是一个异常发生了,我有两种处理方式,
      • 第一种方式:我把这件事告诉我的领导【异常上抛】
      • 第二种方式:我自己掏腰包把这个钱补上。【异常的捕捉】
      • 张三 --> 李四 --> 王五 --> CEO
    • 思考:异常发生之后,如果我选择了上抛,抛给了我的调用者,调用者需要对这个异常继续处理,那么调用者处理这个异常同样有两种处理方式。
    • 注意:Java中异常发生之后如果一直上抛,最终抛给了mian方法,main方法继续向上抛,抛给了调用者JVM,JVM知道了这个异常的发生,只有一个结果,终止java程序的运行。
  • 什么是UML?有什么用?

    • UML是一种统一建模语言。
    • 一种图表式语言(画图的)
    • UML不是只有java中使用。只要是面向对象的编程语言,都有UML。
    • 一般画UML图的都是软件架构师或者说是系统分析师。这些级别的人员使用的。软件设计人员使用UML。
    • 在UML图中可以描述类和类之间的关系,程序执行的流程,对象的状态等。
    • 在java软件开发中,软件分析师/设计师负责设计类,java软件开发人员必须能看懂。

异常处理原理

package com.bjpowernode.javase.exception;

public class ExceptionTest03 {
	public static void main(String[] args) {
		
		System.out.println(100 / 0);
		/*
		 程序执行此处,发生了ArithmeticException异常。
		 底层new了一个ArithmeticException异常对象。
		 然后抛出了,由于是main调用了100 / 0,
		 所以这个异常ArithmeticException抛给了mian方法,
		 mian方法没有处理,将这个异常自动抛给了JVM,
		 JVM最终终止程序的运行。		  
		 
		 ArithmeticException 继承 RuntimeException,属于运行时异常,
		 在编写程序阶段不需要对程序进行处理。
		 */
		// 这里的Hello World没有输出,没有执行。
		System.out.println("Hello World!");
		
	}
}

package com.bjpowernode.javase.exception;

/*
以下代码报错的原因是:
	因为doSome()方法声明位置上使用了:throws ClassNotFoundException
而ClassNotFoundException是编译时异常,必须编写代码时处理,没有处理编译器报错。
 * */
public class ExceptionTest04 {

	public static void main(String[] args) {
		// main方法调用mian()方法
		// 因为doSome()方法声明位置上有:throws ClassNotFoundException
		// 我们在调用doSome()方法的时候必须对这种异常进行预先的处理
		// 如果不处理,编译器就会报错
		// 编译器报错信息:Unhandled exception type ClassNotFoundException
		// doSome();
	}
	
	/*
	 * doSome方法在方法声明的位置上使用了throws ClassNotFoundException
	 * 这个代码表示doSome()方法在执行过程中,有可能会出现ClassNotFoundException异常
	 * 叫做类没有找到异常。这个异常直接父类是:Exception,所以ClassNotFoundException属于编译时异常
	 * */
	public static void doSome() throws ClassNotFoundException{
		
	}

}

package com.bjpowernode.javase.exception;


public class ExceptionTest05 {

	// 第一种处理方式:在方法声明的位置上继续使用:throws,来完成异常的继续上抛。抛给调用者
	// 上抛等于推卸责任。(继续把异常传递给调用者。)
	/*
	public static void main(String[] args) throws ClassNotFoundException {

		doSome();
	}
	*/
	
	// 第二种方式:try...catch进行捕捉。
	// 捕捉等于把异常拦下了,异常真正的解决了。(调用者是不知道的)
	public static void main(String[] args) {
		try {
			doSome();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
	
	public static void doSome() throws ClassNotFoundException{
		
	}

}


异常捕捉和上报的联合使用

package com.bjpowernode.javase.exception;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

/*
处理异常的第一种方式:
	在方法声明的位置上使用关键字throws抛出,谁调用这个方法,我就抛给谁,抛给调用者来处理。
	这种异常处理的态度就是:上报。
处理异常的第二种方式:
	使用try...catch语句对异常进行捕捉。
	这个异常不会上报,自己直接处理。
	异常到此为止,不再上抛了。 
注意:
	只要异常没有捕捉,采用上报的方式,此方法的后续代码不会执行。
	另外需要注意,try语句块中的某一行出现错误,该行后面的代码不会执行。
	try...catch捕捉异常之后,后续的代码可以执行。
 * */
public class ExceptionTest06 {

	// 一般不建议在main方法上使用throws,因为这个异常如果真正的发生了,一定会抛给JVM,JVM只有终止。
	// 异常处理机制的作用就是增强程序的健壮性,怎么能做到,异常发生了也不影响程序的执行。所以
	// main方法中的异常建议使用try...catch进行捕捉,main方法就不要继续上抛了。
	// public static void main(String[] args) throws FileNotFoundException{

	public static void main(String[] args){
		
		// 100 / 0属于算术异常,就是运行时异常,在程序编写阶段可以处理,也可以不处理
		// System.out.println(100 / 0);
		try {
			System.out.println(100 / 0);
		}catch(ArithmeticException a) {
			System.out.println("算术异常了!");
		}
		
		
		System.out.println("main begin");
		try {
			// try尝试
			m1();
			// 以上代码出现异常,直接进入catch语句块中执行。
			System.out.println("Hello World!");
		}catch(FileNotFoundException e){// catch后面的好像一个方法的形参
			// 这个分支中可以使用e引用,e引用保存的内存地址是那个new出来异常对象的内存地址
			// catch是捕捉异常之后的分支。
			// 在catch分支中干什么?处理异常。
			System.out.println("文件找不到,可能路径存在错误,也可能文件被删除了");
			System.out.println(e);// java.io.FileNotFoundException
		}		 
		
		// try...catch把异常抓住之后,这里的代码会继续执行。
		System.out.println("main end");
	}
	
	private static void m1() throws FileNotFoundException {
		System.out.println("m1 begin");
		m2();
		// 以上代码出现异常,这里不会执行
		System.out.println("m1 end");
	}
	
	// 抛别的不行,抛ClassCastException说明你还是没有对FileNotFoundException进行处理
	// private static void m2() throws ClassCastException{
	// 抛FileNotFoundException父对象IOException,这样是可以的,因为IOException包括:FileNotFoundException
	//private static void m2() throws IOException{	
	// 这样也可以,因为Excetion包括所有异常
	// private static void m2() throws Exception{
	// throws后面也可以写多个异常,可以使用逗号隔开。
	// private static void m2() throws Exception ClassCastException, FileNotFoundException{
	// 系统自动纠正
	private static void m2() throws FileNotFoundException {
		System.out.println("m2 begin");
		// 编译器报错的原因是:m3()方法声明的位置上有:throws FileNotFoundException
		// 我们在这里调用m3()没有对异常进行预处理,所以编译报错。
		m3();
		// 以上如果出现异常,这里是无法执行的!
		System.out.println("m2 end");
	}
	
	private static void m3() throws FileNotFoundException{
		// 调用SUN jdk中某个类的构造方法。
		// 这个类还没有接触过,后期IO流的时候就知道了。
		// 我们只是借助这个类学习一些异常处理机制。
		// 创建一个输入流对象,该流指向一个文件。
		/*
		 编译报错的原因:
		 	第一:这里调用了一个构造方法:FileInputStream(String name)
		 	第二:这个构造方法的声明位置上有:throws FileNotFoundException
		 	第三:通过类的继承结构看到:FileNotFoundException的父类是IOException,IOException的父类是Exception
		 	最终知道FileNotFoundException是编译时异常。
		 * */
		// new FileInputStream("D:\\JAVA\\JavaProjects\\JavaSE\\day17\\1.txt");
		
		// 我们采用第一种处理方式:在方法声明的位置上使用throws继续上抛
		new FileInputStream("D:\\JAVA\\JavaProjects\\JavaSE\\day17\\2.txt");
		System.out.println("如果代码出现异常,这里会执行吗????????不会!!!");
	}
}

try…catch深入

package com.bjpowernode.javase.exception;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

/*
深入try...catch
 	1.catch后面的小括号中的类型可以写具体的异常类型,也可以是该异常类型的父类型。
 	2.catch可以写多个,建议catch的时候,精确的一个一个处理,这样有利于程序的调试。
 	3.catch写多个的时候,从上到下,必须遵循从小到大的原则。
 * */
public class ExceptionTest07 {

	/*
	public static void main(String[] args) throws Exception, FileNotFoundException, NullPointerException{
		
	}
	*/
	public static void main(String[] args) {
		
		// 编译报错
		// 因为没有正确处理异常FileNotFoundException
		/*
		try {
			FileInputStream fis = new FileInputStream("D:\\JAVA\\JavaProjects\\JavaSE\\day17\\1.txt");
		}catch(NullPointerException e){
			
		}
		*/
		
		/*
		try {
			FileInputStream fis = new FileInputStream("D:\\JAVA\\JavaProjects\\JavaSE\\day17\\2.txt");
			System.out.println("以上出现异常,这里无法执行!");
		}catch(FileNotFoundException e){
			System.out.println("文件不存在!");
		}
		*/
		
//		try {
//			FileInputStream fis = new FileInputStream("D:\\JAVA\\JavaProjects\\JavaSE\\day17\\2.txt");
//			System.out.println("以上出现异常,这里无法执行!");
//		}catch(IOException e){// 多态,IOEception e = new FileNotFoundException();(父类型引用指向子类型对象)
//			System.out.println("文件不存在!");
//		}
		
//		try {
//			FileInputStream fis = new FileInputStream("D:\\JAVA\\JavaProjects\\JavaSE\\day17\\2.txt");
//			System.out.println("以上出现异常,这里无法执行!");
//		}catch(Exception e){// 多态,Exception e = new FileNotFoundException();父类型引用指向子类型对象
//			System.out.println("文件不存在!");
//		}
		
//		try {
//			// 创建输入流
//			FileInputStream fis = new FileInputStream("D:\\JAVA\\JavaProjects\\JavaSE\\day17\\2.txt");
//			// 读文件
//			fis.read();
//		}catch(Exception e){// 所有的异常都走这个分支
//			System.out.println("文件不存在!");
//		}
		
//		try {
//			// 创建输入流
//			FileInputStream fis = new FileInputStream("D:\\JAVA\\JavaProjects\\JavaSE\\day17\\2.txt");
//			// 读文件
//			fis.read();
//		}catch(FileNotFoundException e){
//			System.out.println("文件不存在!");
//		}catch(IOException i) {
//			System.out.println("读文件报错了!");
//		}
		
		// 编译报错(catch(FileNotFoundException e)),It is already handled by the catch block for IOException
//		try {
//			// 创建输入流
//			FileInputStream fis = new FileInputStream("D:\\JAVA\\JavaProjects\\JavaSE\\day17\\2.txt");
//			// 读文件
//			fis.read();
//		}catch(IOException i) {
//			System.out.println("读文件报错了!");
//		}catch(FileNotFoundException e){
//			System.out.println("文件不存在!");
//		}
		
		System.out.println("Hello World!");
	}
}

JDK8的新特性

catch后面可以跟或运算符
		try {
			// 创建输入流
			FileInputStream fis = new FileInputStream("D:\\JAVA\\JavaProjects\\JavaSE\\day17\\2.txt");
			
			// 进行数学运算
			System.out.println(100 / 0);// 这个异常是运行时异常,编写程序时可以处理,也可以不处理。
			
		}catch(FileNotFoundException | ArithmeticException | NullPointerException e){
		// jdk8的新特性
			System.out.println("文件不存在?数学异常?空指针异常?");
		}

如何选择上报还是捕捉

  • 在以后的开发中,处理编译时异常,应该上报还是捕捉呢,怎么选
    • 如果希望调用者来处理,选择throws上报。
    • 其他情况选择捕捉的方式。

异常对象的常用方法

package com.bjpowernode.javase.exception;

/*
异常对象有两个常用的方法:

	获取异常简单的描述信息:
	String msg = exception.getMessage();
	
	获取异常追踪的堆栈信息:
	exception.printStackTrace();
 * */
public class ExceptionTest08 {

	public static void main(String[] args) {
		
		// 这里只是new了异常对象,但是没有将异常对象抛出,JVM会认为这是一个普通的java对象。
		// 对比FileInputStream类的构造方法
		NullPointerException n = new NullPointerException("空指针异常");
		
		//throw n;// 手动抛出异常
		
		// 获取异常简单的描述信息:这个信息实际上就是上面构造方法的String参数。
		String msg = n.getMessage();
		System.out.println(msg);// 空指针异常
		
		// 打印异常堆栈信息
		// java后台打印异常堆栈信息的时候,采用了异步线程的方式打印的。
		n.printStackTrace();
		
		System.out.println("Hello World!");
		
	}
	

}

package com.bjpowernode.javase.exception;

import java.io.FileInputStream;
import java.io.FileNotFoundException;

/*
异常对象的两个方法:
	String msg = n.getMessage();
	n.printStackTrace();

我们以后查看异常的追踪信息,我们应该怎么看,可以快速地调试程序呢?
	异常追踪信息,从上往下一行一行看。
	但是需要注意的是:SUN的代码就不用看了,主要的问题是出现在自己编写的代码上。
 * */
public class ExceptionTest09 {

	public static void main(String[] args) {
		try {
			m1();
		} catch (FileNotFoundException e) {
			
			// 获取异常的简单描述信息!!!
			String msg = e.getMessage();
			System.out.println(msg);
			
			// 打印异常堆栈追踪信息!!!
			// 在实际的开发中,建议使用这个,养成好习惯。
			// 这行代码要写上,否则出现问题也不知道。
			e.printStackTrace();
			/*
				java.io.FileNotFoundException: D:\JAVA\JavaProjects\JavaSE\day17\2.txt (系统找不到指定的文件。)
					at java.io.FileInputStream.open0(Native Method)
					at java.io.FileInputStream.open(FileInputStream.java:195)
					at java.io.FileInputStream.<init>(FileInputStream.java:138)
					at java.io.FileInputStream.<init>(FileInputStream.java:93)
					at com.bjpowernode.javase.exception.ExceptionTest09.m3(ExceptionTest09.java:32)
					at com.bjpowernode.javase.exception.ExceptionTest09.m2(ExceptionTest09.java:28)
					at com.bjpowernode.javase.exception.ExceptionTest09.m1(ExceptionTest09.java:24)
					at com.bjpowernode.javase.exception.ExceptionTest09.main(ExceptionTest09.java:15)
			 		因为32行出问题导致了28行
			 		28行出问题导致了24行
			 		24行出问题导致了15行
			 		应该先查看32行的代码,32行代码是错误的根源。
			 * */
		}
		// 这里程序不耽误执行,很健壮。(服务器不会因为遇到异常而宕机。)
		System.out.println("hello world!");
	}

	private static void m1() throws FileNotFoundException {
		m2();
	}

	private static void m2() throws FileNotFoundException {
		m3();
	}

	private static void m3() throws FileNotFoundException{
		new FileInputStream("D:\\JAVA\\JavaProjects\\JavaSE\\day17\\2.txt");
	}
	

}

finally子句的使用

package com.bjpowernode.javase.exception;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

/*
关于try...catch中的finally语句:
	1.在finally子句中的代码是最后执行的,并且是一定会执行的,即使try语句块中的代码出现了异常。
	 finally子句必须和try一起出现,不能单独编写。
	 
	2.finally语句通常使用在哪些情况下呢?
	  通常在finally语句块中完成资源的释放/关闭
	  因为finally中的代码比较有保障。
	  即使try语句块中的代码出现异常,finally中代码也会正常执行。


 * */

public class ExceptionTest10 {

	public static void main(String[] args) {
		FileInputStream fis = null;// 声明位置放在try外面,这样
		
		try {
			// 创建输入流对象
			fis = new FileInputStream("D:\\JAVA\\JavaProjects\\JavaSE\\day17\\1.txt");
			// 开始读文件,,,
			
			// ...
			String s = null;
			//这里一定会出现空指针异常;
			s.toString();
			System.out.println("Hello World!");
			
			// 流使用完需要关闭,因为流是占用资源的。
			// 即使以上程序出现异常也要关闭
			// 放在这里有可能关闭不了
			// fis.close();
			
		} catch(FileNotFoundException e) {
			e.printStackTrace();
			
		} catch(IOException e) {
			e.printStackTrace();
			
		} catch(NullPointerException e) {
			e.printStackTrace();
			
		} finally {
			
			System.out.println("hello haoke!");
			
			// 流的关闭放在这里比较保险。
			// finally中的代码是一定会执行的。
			// 即使try中出现了异常
			if(fis != null) {// 避免空指针异常
				// fis中close方法本身throws IOException,因此需要进行捕捉
				try {
					fis.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			
			
		}
		System.out.println("Hello Kitty!");
		
	}

}

package com.bjpowernode.javase.exception;

/*
finally语句:
	放在finally语句块中的代码是一定会执行的!!!
 * */
public class ExceptionTest11 {

	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...");
		}
		
		// 编译报错,这里不能写代码,因为这个代码永远不会执行到的。
		//System.out.println("Hello World!");
	}

}

package com.bjpowernode.javase.exception;

public class ExceptionTest12 {

	public static void main(String[] args) {
		
		try {
			System.out.println("try...");
			// 退出JVM
			System.exit(0);// 退出JVM之后,finally语句中的代码就不执行了!
		} finally {
			System.out.println("finally...");
		}	
	}
}

finally面试题

package com.bjpowernode.javase.exception;
/*
finally面试题
 * */
public class ExceptionTest13 {

	public static void main(String[] args) {
		int result = m();
		System.out.println(result);// 100
	}
	
	/*
	 java语法规则(有一些规则是不能破坏的,一旦这么说了,就必须这么做!)
	 	java中有一条这样的规则:
	 		方法体中的代码必须遵循自上而下顺序依次执行(亘古不变的语法!)
	 	java中还有一条规则:
	 		return语句一旦执行,整个方法必须结束(亘古不变的语法!)
	 * */
	public static int m() {
		int i = 100;
		try {
			// 这行代码出现在int i = 100的下面,所以最终的结果是返回100
			// return语句还必须保证是最后执行的,一旦执行,整个方法结束。
			return i;
		}finally {
			i++;//finally中的语句也一定会执行的。
		}
	}
	/*
	 反编译之后的效果:
  		int i = 100;
	    int j = i;
	    i++;
	    return j;
	 * */

}

final finally finalize的区别

package com.bjpowernode.javase.exception;

/*
final finally finalize有什么区别?
	final 关键字
		final修饰的类无法继承
		final修饰的方法无法覆盖
		final修饰的变量不能重新赋值
	finally 关键字
		和try一起联合使用
		finally语句块中的代码是必须执行的。
	finalize 标志符
		是一个Object类中的方法名。
		这个方法是由垃圾回收器GC负责调用的。
 * */
public class ExceptionTest14 {

	public static void main(String[] args) {
		
		// final是一个关键字。修饰的变量表示最终的。不变的。
		final int i = 100;
		//i = 200;
		
		// finally也是一个关键字,和try联合使用,使用在异常处理机制中
		// 在finally语句块中的代码是一定会执行的。
		try {
			
		}finally {
			System.out.println("finally...");
		}
		
		// finalize()是Object类中的一个方法。作为方法名出现。
		// 所以finalize是标识符。
		// finalize()是JVM的GC垃圾回收器负责调用
		Object obj;
	}

}

// final修饰的类无法继承
final class A{
	// 常量
	public static final double PI = 3.1415926;
}

class B{
	// final修饰的方法无法覆盖
	public final void doSome() {
		
	}
}

自定义异常

package com.bjpowernode.javase.exception;

/*
1、SUN提供的JFK内置的异常肯定是不够用的。在实际的开发中,有很多业务。
这些业务出现异常之后,JDK中都是没有的。和业务挂钩的。那么异常类我们
程序员可以自己定义吗?
	可以。
2、Java中怎么自定义异常呢?
	两步:
		第一步:编写一个类继承Exception或者RuntimeException。
		第二步:提供两个构造方法,一个无参数的,一个带有String参数的。
	死记硬背就可以了。
 * */
public class MyException extends Exception{ //编译时异常
	
	public MyException(){
		
	}
	
	public MyException(String s) {
		super(s);
	}
}

/*
public class MyException extends RuntimeException{ //运行时异常
	
	
}
*/
package com.bjpowernode.javase.exception;

public class ExceptionTest15 {

	public static void main(String[] args) {
		
		// 创建异常对象(只new了异常对象,并没有手动抛出)
		MyException c = new MyException("用户名不能为空");
		
		// 打印异常堆栈信息
		c.printStackTrace();
		
		// 获取异常简单描述信息
		System.out.println(c.getMessage());
	}

}

异常在实际开发中的应用

package com.bjpowernode.javase.exception;

/*
	栈操作异常,自定义异常
 */
public class MyStackOperationException extends Exception{// 编译时异常

	public MyStackOperationException() {
		super();
	}

	public MyStackOperationException(String message) {
		super(message);
	}
	
}

package com.bjpowernode.javase.exception;

/**
	编写程序:使用一维数组,模拟栈数据结构。
	要求:
		1、这个栈可以存储java中的任何引用类型的数据。
		2、在栈中提供push方法模拟压栈。(栈满了,要有提示信息。)
		3、在栈中提供pop方法模拟弹栈。(栈空了,也要有提示信息。)
		4、编写测试程序,new栈对象,调用push,pop方法来模拟压栈弹栈的动作。
		5、假设栈的默认初始化容量是10。(请注意无参数构造方法的编写方式。)
 */

public class MyStack {
	// 向栈当中存储元素,我们这里使用一维数组模拟。存到栈中,就表示存储到数组中。
	// 为什么选择Object类型数据数组?因为这个栈可以存储java中的任何引用类型的数据
	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[10];
		// index初始化
		this.index = -1;
	}		
	
	// 压栈方法
	public void push(Object obj) throws MyStackOperationException{//https://www.bilibili.com/video/BV1Rx411876f?p=659
		if(index >= elements.length - 1) {
			
			//-------------------改良之前----------------
//			System.out.println("压栈失败,栈已满!");
//			return;
			
			//-------------------改良之后----------------
			// 创建异常对象
			//MyStackOperationException e = new MyStackOperationException("压栈失败,栈已满!");
			// 手动抛出,出现异常之后,下面的程序不再执行!(相当于return;)
			//throw e;// 这里捕捉没有意义,自己new一个异常,自己捉,没有意义。栈已满这个信息你需要传递出去。
			
			//合并
			throw new MyStackOperationException("压栈失败,栈已满!");
		}
		
		// 程序走到这里说明,栈没满
		// 向栈中加1个元素,栈帧向上移动1个位置。
		index ++;
		elements[index] = obj;
		// 
		System.out.println("压栈" + obj + "成功,栈帧指向" + index);
	} 
	
	// 弹栈方法
	public Object pop() throws MyStackOperationException{
		if(index < 0) {
			
			//----------改良之前----------------
//			System.out.println("弹栈失败,栈已空!");
//			return null;
			
			//----------改良之后-----------------
			throw new MyStackOperationException("弹栈失败,栈已空!");
		}
		
		// 程序能执行到此处,说明栈没有空
		System.out.print("弹栈" + elements[index] + "元素成功,");
		Object obj = elements[index];// 弹出元素
		// 栈帧向下移动一位
		index --;
		System.out.println("栈帧指向" + index);
		return obj;
	}
	
	
	// set和get也许用不上,但是你必须写上,这是规矩。
	// 封装:第一步:属性私有化;第二步:对外部提供set和get方法。
	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;
	}
}


package com.bjpowernode.javase.exception;

/*
	测试改良之后的MyStack
	注意:最后这个例子,是异常最重要的案例。必须掌握。自定义异常在实际开发中的应用。
 */
public class ExceptionTest16 {

	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) {
			//e.printStackTrace();
			// 输出异常简单的描述信息
			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) {
			// e.printStackTrace();
			System.out.println(e.getMessage());
		}
	}

}

异常作业

package com.bjpowernode.javase.exception.homework;

/*
 用户业务类,处理用户相关的业务:例如登录,注册等功能。
 
	编写程序模拟用户注册:
		1、程序开始执行时,提示用户输入“用户名”和“密码”信息
		2、输入信息注册之后,后台java程序模拟用户注册
		3、注册用户名要求长度在[6-14]之间,小于或者大于都表示异常。
	注意:
		完成注册的方法放到一个单独的类中。
		异常类自定义即可。
		
	class UserService{
		public void register(String name, String password){
		
		}
	}
	编写main方法,在main方法中接收用户输入的信息,在main方法中调用UserService
	的register方法完成注册。

 */
public class UserService {

	public void register(String username, String password) throws IlegalNameException {
		
		// 如果username = null,则username.length()容易出现空指针异常
//		if(username.length() < 6 || username.length() > 14 || username == null) {
//			
//		}
		
		// 改进:引用等于null的这个判断最好放到所有条件的最前面
//		if(username == null || username.length() < 6 || username.length() > 14) {
//			
//		}
		
		// 再分享一个经验:username == null 不如写成 null == username,防止误写成username = null,则后续的username.length()容易出现空指针异常
		if(null == username || username.length() < 6 || username.length() > 14) {
//			System.out.println("用户名不合格,长度必须在[6-14]之间");
//			return;
			throw new IlegalNameException("用户名不合格,长度必须在[6-14]之间");
		}
		
		// 程序能执行到此处,说明用户名合法
		System.out.println("注册成功,欢迎" + username +"登录系统");
	}
}

package com.bjpowernode.javase.exception.homework;

public class IlegalNameException extends Exception{
	
	public IlegalNameException() {
		
	}
	
	public IlegalNameException(String s) {
		super(s);
	}
}

package com.bjpowernode.javase.exception.homework;

public class UserServiceTest {

	public static void main(String[] args) {
		
		// 创建UserService对象
		UserService us = new UserService();
		
		// 从控制台接收用户名和密码
		// ecliopse模拟控制台输入参数的步骤:Run --> Run Configurations --> 选中文件 --> Arguments --> Program arguments --> 空格分开输入参数
		String username = args[0];
		String password = args[1];
		
		
		try {
			us.register(username, password);
		} catch (IlegalNameException e) {
			 e.printStackTrace();
			//System.out.println(e.getMessage());
		}
	}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值