JAVA异常处理实战

举报
小高先生 发表于 2022/06/08 17:07:35 2022/06/08
【摘要】 整理了异常处理的知识

  JAVA异常处理实战这节课讲解了JAVA异常处理的内容,主要包括三方面,分布式异常的分类、异常的作用以及自定义异常类。

  章节目录为:

  1. 错误和异常
  2. 异常分类
  3. 异常处理机制
  4. finall块
  5. throws和throw关键字
  6. 子类和父类构造函数使用throws关键字
  7. 方法重载使用throws关键字
  8. RuntimeException和自定义异常类
  9. 垃圾回收机制

一.错误和异常

  错误和异常看着都不是什么好的词,都是我们在编程时不想遇见的,那他们分别是什么意思呢,有什么区别?

  错误我理解为程序中发生了严重问题,并且程序无法自己处理,只能在编程之前避免,比如内存溢出,或者我总犯的错误,少打了一个分号。那异常听着就比错误的级别要低一些,程序可以处理,所以我们称程序本身就可以处理的问题叫做异常,比如数组下标越界

  异常体系

  Error:错误类

  Exception:异常类

二.异常分类

  异常可以分为两类,分别是编译时异常和运行时异常

  编译时异常也可以称为检查异常,就是写完程序的时候,我们运行之前都要先编译,编译通过了才能运行,如果存在编译时异常,那就无法通过编译,必须显示处理,解决这个问题,否则程序就一直不能通过编译,无法运行。其实就是IDE会自动弹出那种红叉叉

  运行时异常也叫非检查异常,这种异常如果存在,程序是可以通过编译的,IDE不会弹出红叉叉,但是运行后就会报错,这种异常无需显示处理,当然也可以和编程时异常一样处理,也就是显示处理。

  RuntimeException中所有的类及其子类都是运行时异常,其他的异常都是编译时异常

  RuntimeException:在编译器不被检查,但是运行后发生错误,需要我们回来修改代码

  非RuntimeException:如果不修改都无法通过编译

三.异常处理机制

  异常处理机制需要用到try...catch块,我们把可能出现异常的语句放入try中,当程序执行到try中的语句,发现有异常,就会抛出异常,然后jvm虚拟机会去找和try配对的处理该异常的catch块,然后执行里面的代码,执行结束后就会离开try...catch块,不会再执行异常的语句。如果没有找到处理该异常的catch块,那程序报错。

   try..catch 块格式:

  执行流程:

  •   先执行try中的语句,发现异常会自动生成一个异常类对象,这个对象会被提交java运行时系统
  •   运行系统会根据接收到的异常类对象,寻找与之匹配的catch块
  •   最后执行相应的catch中的代码

  案例代码如下:

  初始代码,出现异常,ArrayIndexOutOfBoundsException,数组下标越界

public class ExceptionDemo1 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		int[] arr = {12,23,34};
		System.out.println(arr[3]);
	}

}

  修改代码:

public class ExceptionDemo1 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		int[] arr = {12,23,34};
		try {
			System.out.println(arr[3]);
		}catch(ArrayIndexOutOfBoundsException e) {
			System.out.println("数组下标越界");
		}
		
		
	}

}

   案例代码如下:


public class ExceptionDemo1 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		int[] arr = {12,23,34};
		//第一个catch是空指针异常,这不是程序中出现的异常,所以要往下面找
		//下一个catch是父类异常,是我们要的
		try {
			System.out.println(arr[3]);
		}catch(NullPointerException e) {
			System.out.println("空指针异常");
		}catch(RuntimeException e) {
			System.out.println("父类异常");
		}catch(Exception e) {
			System.out.println("总异常");
		}
		
		
	}

}

四.finally代码块

  finally代码块是紧跟着try...catch后的代码块,可以有也可以没有。finally代码块格式如下:

  使用finally代码块有几点需要注意:

  • finall中的语句为异常提供了一个统一的出口,执行完catch之后就从finally中出来,这样的好处就是在控制流程到程序其他部分之前,对程序的状态进行统一管理
  • finall中的语句必须执行,无论try中的语句是否有异常。
  • 通常finally语句可以是资源的释放工作,如关闭打开的文件

  案例代码如下:

public class ExceptionDemo1 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		int[] arr = {12,23,34};
		try {
			System.out.println(arr[2]);
		}catch(ArrayIndexOutOfBoundsException e) {
			System.out.println("数组下标越界异常");
		}finally {
			//不管try/catch是否执行,finally都会执行
			System.out.println("释放资源");
		}
	}

}

五.throws和throw关键字

  程序中会生命很多方法,在写方法时可能会因为某些错误而产生异常,正常我们可以通过之前学习的try...catch代码块解决异常,但是如果不想在方法中解决这个异常,那就可以调用异常的方法统一处理,需要在这个有异常的方法参数列表后面使用throws关键字,抛出异常。throws关键字修饰的方法,谁调用谁处理异常

  现在我们就有了两种处理异常的方法

  编译时异常:try...catch或者throws关键字

  运行时异常,可以不处理,运行后更改代码

  案例代码如下:

  代码一:运行时异常,在throwsDemo()方法后面使用throws

public class ExceptionDemo {
	public static void main(String[] args) {
//		try {
//			throwsDemo();
//		}catch(ArrayIndexOutOfBoundsException e) {
//			System.out.println("数组下标越界");
//		}
		throwsDemo();
		
	}
	
	public static void throwsDemo() throws ArrayIndexOutOfBoundsException{
		int[] arr = {12,23,45};
		System.out.println(arr[3]);
	}
}

  代码二:throwsDemo1()是一个编译是异常,要对这个方法进行处理,可以在方法中处理,添加try...catch代码块,也可以通过throws抛出异常。在调用这个方法时,还是有异常,在main方法中既可以try...catch或者throws,但是不介意throws,会增加jvm的运行压力

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class ExceptionDemo {
	public static void main(String[] args) {
//		try {
//			throwsDemo();
//		}catch(ArrayIndexOutOfBoundsException e) {
//			System.out.println("数组下标越界");
//		}
		//throwsDemo();
		
		try {
			throwsDemo1();
		} catch (ParseException e) {
			// TODO Auto-generated catch block
			System.out.println("编译时异常处理");
		}
		
	}
	
	public static void throwsDemo() throws ArrayIndexOutOfBoundsException{
		int[] arr = {12,23,45};
		System.out.println(arr[3]);
	}
	public static void throwsDemo1() throws ParseException {
		//将字符串时间data转换为Data时间
		String data = "2022-6-8";
		//用到这个类
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
		//Data时间
		Date date = sdf.parse(data);
		System.out.println(date);//这地方会出现编译时异常,可以在方法里处理,也可以throws抛出异常
	}
}

 throw关键字

  当程序发生错误而且无法处理时,需要抛出异常,除此之外在某些时候,想要自行抛出异常,此时需要用throw关键字,用于生成指定的异常对象并且抛出。就如下代码,我们不希望用户往方法里输入空数组,所以我们在里面主动抛出一个异常,并且生成异常对象抛出。

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class ExceptionDemo1 {

	public static void main(String[] args) {

		//throwsDemo();//ArrayIndexOutOfBoundsException
		
		
//		try {
//			throwsDemo2();
//		} catch (ParseException e) {
//			// TODO Auto-generated catch block
//			System.out.println("编译时异常处理");
//		}//还是会报错,出现异常,这时就可以try...catch..或者再一次throws
//		//但是再throws就会增加jvm的运行压力,不建议,可以用try...catch
		
		int[] arr = null;
		throwDemo(arr);
		
	}
	/*
	 * throws抛出运行时异常
	 */
	public static void throwsDemo() throws ArrayIndexOutOfBoundsException{
		//定义一个数组
		int[] arr = {12,34,45};
		System.out.println(arr[3]);//数组下标越界异常
	}
	
	/*
	 * throws抛出编译时异常
	 */
	public static void throwsDemo2() throws ParseException{
		//将字符串时间改为Date类型时间
		String data = "2048-1-1";
		//这里要用到一个类
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
		//将字符串时间转成Date类型时间
		Date date = sdf.parse(data);//这里会报出编译时异常,如果不想在方法里面修改,就可以throws
		System.out.println(date);
	}
	
	/*
	 * throw关键字
	 */
	public static void throwDemo(int[] arr){
		if(arr==null) {
			//告诉该用户此时数组为空
			throw new NullPointerException("数组对象为空");
		}
	}
}

  throws:

  • 用在方法名后面,throws后面是异常类名
  • 表示抛出异常,由该方法的调用者处理
  • 表示出有异常的可能性,并不一定会出现异常

  throw:

  • 用在方法体内,跟的是异常对象名
  • 表示抛出异常,在方法内的语句处理
  • 执行throw一定抛出异常

六.子类和父类构造函数中使用throws关键字

  第一种情况,父类构造函数使用throws关键字抛出编译时异常,子类构造函数也必须抛出异常,并且这个异常是有讲究的,要么是和父类构造函数相同的异常,要么是其的父类异常,还有要注意的是,子类构造函数不能捕获,也就是不能使用try...catch。

  案例代码如下:子类的构造函数也要用throws抛出异常,不能用try...catch

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Father {
	public Father() throws ParseException {
		String data = "2022-5-9";
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
		Date date = sdf.parse(data);
		System.out.println(date);
	}
}

import java.text.ParseException;

public class Son extends Father{
	public Son() throws ParseException {
		super();
	}
}

  第二种情况,父类构造函数抛出运行时异常,子类构造函数可以抛出可以不抛出,如果抛出,就和编译时异常一样处理就行

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Father {
//	public Father() throws ParseException {
//		String data = "2022-5-9";
//		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
//		Date date = sdf.parse(data);
//		System.out.println(date);
//	}
	public Father() throws NullPointerException{
		
	}
}
import java.text.ParseException;

public class Son extends Father{
	public Son() {
		super();
	}
}

七.方法重写中使用throws关键字

  使用继承时,如果父类的某个方法抛出了异常,子类重写这个方法,需要怎么做呢

  第一,可以不抛出异常,也就是不用throws

  案例代码如下:

public class Base {
	public void show() throws NullPointerException{
		
	}
}


public class Spu extends Base{
	public void show() {
		
	}
}

  第二,子类可以只throws父类的部分异常

import java.text.ParseException;

public class Base {
	public void show() throws ParseException,Exception{
		
	}
}

public class Spu extends Base{
	public void show() throws Exception{
		
	}
}

  第三,子类抛出的异常可以小于或等于父类throws抛出的异常

import java.text.ParseException;

public class Base {
	public void show() throws Exception{
		
	}
}
import java.text.ParseException;

public class Spu extends Base{
	public void show() throws ParseException{//或者throws Exception
		
	}
}

  第四,子类重写父类的方法时抛出了异常,那父类被重写的方法可以不抛出异常

  如果父类方法没有抛出异常,子类重写该方法时抛出了编译时异常,那么子类重写的父类方法会报出编译时异常,解决方法有两个

  1.   父类被重写的方法抛出的异常要大于或等于子类方法抛出的异常
  2.   子类不用throws抛出异常,而是在方法中捕获,也就是使用try....catch

           案例代码如下:

import java.text.ParseException;
import java.util.Date;

public class Base {
	public void show() throws ParseException{
		
	}
}

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Spu extends Base{
	public void show() throws ParseException{//或者throws Exception
		String data = "2022-3-3";
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
		Date date = sdf.parse(data);
		System.out.println(date);
	}
}

import java.text.ParseException;
import java.util.Date;

public class Base {
	public void show(){
		
	}
}
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Spu extends Base{
	public void show(){//或者throws Exception
		String data = "2022-3-3";
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
		Date date = null;
		try {
			date = sdf.parse(data);
		} catch (ParseException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println(date);
	}
}

  八.RuntimeException和自定义异常类

  RuntimeException类被称为运行时异常,也称为非检查异常,这种异常因为JVM操作引起,随时都可能发生,此类异常一般是有特定操作引发的,在程序中频发出现,所以不受编译器检查与处理或者声明规则的限制。

  常见RuntimeException类

  • NullPointerException:当程序试图在需要对象的地方使用null,抛出该异常
  • ArrayIndexOutOfBoundsException:当数组下标越界时,抛出该异常
  • ClassCastException:试图将对象强制转换为不是实例的子类时,抛出该异常

  案例代码如下:girl指向的是Girl,son指向的是Son,Girl和Son没有任何联系

import java.text.ParseException;
import java.util.Date;

public class Base {
	public void show(){
		
	}
}

public class Spu extends Base{
	
}
public class Girl extends Base{

}
public class Test {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Base girl = new Girl();
		Spu son = new Spu();
		son = (Spu)girl;
	}

}
  • NumberFormatException:当程序试图将字符串转换成一种数值类型,但该字符串不能转换成目标格式,抛出该异常

  案例代码如下:

public class Test {

	public static void main(String[] args) {
	
		
		String number = "12.3";
		int num = Integer.parseInt(number);//字符串类型转成int
		System.out.println(num);//NumberFormatException数据格式异常
		//String类型是12.3,要转也是转成double类型
	}

}
    printStackTrace:Throwable定义了这个方法可以输出错误信息,用来跟踪异常事件发生时执行堆栈的内容
public class Test {

	public static void main(String[] args) {	
		show();
	}

	public static void show() {
		int[] arr = {12,23,45};
		try {
			System.out.println(arr[3]);
		}catch(ArrayIndexOutOfBoundsException e) {
			e.printStackTrace();//输出执行堆栈信息
		}
		
	}
}

  • getMessage:Throwable定义了一个方法可以得到有关异常事件的信息

  案例代码如下:

public class Test {

	public static void main(String[] args) {	
		show();
	}

	public static void show() {
		int[] arr = {12,23,45};
		try {
			System.out.println(arr[3]);
		}catch(ArrayIndexOutOfBoundsException e) {
			//e.printStackTrace();//输出执行堆栈信息
			System.out.println(e.getMessage());
		}
		
	}
}

  自定义异常类:为了更加精准的捕获和处理异常,需要用户自定义异常

  继承Exception自定义异常格式:

  public class 自定义异常类名 extends Exception{

               无参构造;

               有参构造;

  }

public class ScoreException extends Exception{//既有编译时异常也有运行时异常
	public ScoreException() {
		//无参
	}
	public ScoreException(String message) {
		super(message);//有参  message表示错误信息
	}
}

public class Teacher {
	public void check(int score) throws ScoreException {
		if(score<0||score>100) {
			//抛出异常,这里调用有参构造,当然也可以调用无参
			throw new ScoreException("输入不对");
			//这个异常是要处理的异常,既可以throws也可以try...catch
		}else {
			System.out.println(score);
		}
	}
}

public class Test1 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Teacher teacher = new Teacher();
		try {
			teacher.check(110);
		} catch (ScoreException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}//这里面最好用try..catch而不要用throws
	}

}

九.垃圾回收机制

  垃圾回收机制:很好理解,就是一个垃圾站

  • 简称GC,就是JVM自带的一个线程,用于回收没有任何引用指向的对象
  • 程序员不用担心内存管理,垃圾回收机制会自行管理,所以我们并不用担心如何把不需要的对象方法垃圾站
  • 也可以通过System.gc()方法

  内存泄漏:

  • 不再使用的内存没有被及时收回,如果没被收回的内存太多了就会导致内存占用过多从而导致程序崩溃
  • 如何判断一个对象是不是垃圾呢?GC通过查看这个对象是否有引用指向,如果没有他就是垃圾,所以如果我们不再使用某个对象,就可以给他设置为null,垃圾回收机制自行回收

十.总结

  学完了本节课,我掌握了有关异常处理的知识

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。