Java异常

异常

什么是异常?

  • Java 中的错误大致可以分为两类:一类是编译时错误,一般指语法错误;另一类是运行时错误。
  • 编译时的错误在程序编译时就会暴露出来,会导致程序编译失败。IDE 集成开发环境都会对这种错误进行提示,我们在编写代码时就能看到错误。
  • 运行时错误在我们编写代码的过程中以及程序编译期间难以发现,甚至可以正常编译通过,但一旦运行就会报错,这类错误一般不容易发现。
  • Java 中有一组类专门来描述各种不同的运行时错误,叫做异常类,Java 结合异常类提供了处理错误的机制,具体步骤就是当程序出现错误时,会创建一个包含错误信息的异常类的实例化对象,并将该对象提交给系统,由系统装交给能处理该异常类的代码进行处理。
  • 异常分为两类,包括 Error 和 Exception。Error 指系统错误,由 Java 虚拟机生成,我们编写的程序无法处理。Exception 指程序运行期间出现的错误 ,我们编写的程序可以对其进行处理。

异常的使用

异常的使用需要用到两个关键字 try 和 catch ,并且这两个关键字需要结合起来使用,用 try 来监听可能会抛出异常的代码,一旦捕获到异常,生成异常对象并交给 catch 来处理,基本语法如下:

try{
    //可能抛出的异常
}catch(异常对象){
    //处理异常
}

比如:

public class Test{
    public static void main(String[] args){
        try{
            int num = 10/0;
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}

这段代码就会在运行时报错,因为除数为 0;所以程序在执行时会产生错误并自动生成一个 Exception 对象,在 catch 代码块中捕获 Exception 对象并进行处理,将错误信息打印出来。

  • 通常除了使用 try 和 catch 关键字,我们还需要用到 finally 关键字,finally 里面的代码无论程序是否抛出异常都会执行。基本语法如下:
try{
    //可能抛出异常
}catch(异常对象){
    //处理异常
}finally{
    //必须执行的代码
}

示例:

public class Test{
    public static void main(String[] args){
        System.out.println(test());
    }
    pulbic static int test(){
        try{
            System.out.println("try...");
            return 0;
        }catch(Exception e){
            
        }finally{
            System.out.println("finally...");
            return 20;
        }
    }
}

运行结果:

try…

finally…

20

通过结果可以看到,虽然 try 代码块中执行了 return 操作,但是 finally 代码块中的程序已然会执行,并且会覆盖 try 中 return 的结果,返回给外部调用者,正是因为 finally 的这个特性,一般在 finally 中进行释放资源的操作


异常类

​ Java 将运行时出现的错误全部封装成类,并且不是一个类,而是一组类。这些类之间是有层级关系的,有树状结构一层层向下分级,处在最顶端的类是 Throwable ,是所有异常类的根结点。Throwable 有两个直接子类:Error 和 Exception。Throwable、Error、Exception 都存放在 java.lang 包中。

​ Error 常见的子类有 VirtualMachineError、AWTError、IOError。更加具体的自己去了解。

​ Exception 常见的子类主要有 IOException 和 RuntimeException 。 IOException 存放在 java.io 包中,RuntimeException 存放在 java.lang 包中。Exception 类要重点关注,因为这部分异常是需要我们在编写代码的过程中手动进行处理的

一下这些类全部存放在 java.lang 包中

  • RuntimeException 的常用子类如下:
  • ArithmeticException :表示数字运算异常;
  • ClassNotFoundException : 表示类未定义异常;
  • IllegalArgumentException : 表示参数格式错误异常;
  • ArrayIndexOutOfBoundsException : 表示数组下标越界异常;
  • NullPointerException : 表示空指针异常;
  • NoSuchMethodError : 表示方法未定义异常;
  • NumberFormatException : 表示将其他数据类型转为数值类型时的不匹配异常。

throw 和 throws

​ throw 和 throws 是 Java 在处理异常时使用的两个关键字,都用来抛出异常,但是使用方法以及表示的含义完全不同。

Java 中抛出异常有 3 种方式:

  • 第 1 种是我们之前介绍过的使用 try-catch 代码块捕获异常。这种方式其实是一种防范机制,即代码中有可能会抛出异常。如果抛出,则捕获并进行处理;如果不抛出,则程序继续向后执行 。
  • 使用 throw 是开发者主动抛出异常,即读到 throw 代码就一定会抛出异常,基本语法:throw new Exception(),这是一种基于代码的逻辑判断而主动抛出的异常的方式。

示例:

public class Test{
    public static void main(String[] args){
        String str = "Java";
        if(str.equals("Java")){
            throw new NumberFormatException();
        }else{
            int num = Integer.parseInt(str);
        }
    }
}

运行结果:

在这里插入图片描述

分析:上述代码中,我们主动对 str 进行判断,如果 str 的值为 Java,则直接抛出 NumberFormatException 异常。所以 try-catch 是捕获可能抛出的异常,throw 是确定会抛出异常,这是二者的区别

  • throws 是作用于方法,用来描述该方法可能会抛出的异常

示例:

public class Test{
    public static void main(String[] args){
        try{
            test();
        }catch(NumberFormatException e){
            e.printStackTrace();
        }
    }
    
    public static void test() throws NumberFormatException{
        String str = "Java";
        int num = Integer.parseInt(str);
    }
}

分析: test() 方法在定义时通过 throws 关键字声明了该方法可能会抛出 NumberFormatException 异常,所以我们在调用该方法时,需要手动使用 try-catch 进行捕获。同时 catch 代码块中可以捕获 NumberFormatException,也可以捕获 Exception。着两种方法都是没有问题的,这就是 Java 的多态机制,Exception 是所有异常的父类,NumberFormatException 也可以理解成 Exception 的另一种表现形式。在调用 test() 方法时,可以使用 try-cathc 主动捕获,也可以不添加 try-catch,直接交给 Java 虚拟机来处理异常。所以可以不加 try-catch ,但是建议添加。既然我们在 catch 中可以使用多态来捕获异常,那么在定义方法时也可以使用多态来描述可能会发生的异常,把 NumberFormatException 换成 Exception 就可以了

但是在这种情况下,main 方法调用 test 方法时就必须手动进行捕获。这里需要注意,如果方法抛出 RunntimeException 异常或者其子类异常,在外部调用方法时可以不进行 try-catch 捕获。如果方法抛出的是 Exception 异常或者其子类,则外部调用时必须进行 try-catch 捕获,否则会报错;如果不添加 try-catch ,也可以通过让 main 方法抛出该异常的方式来解决这个错误

public class Test{
    public static void main(String args) throws Exception{
        test();
    }
    
    public static void test() throws Exception{
        String str = "Java";
        int num = Integer.parseInt(str);
    }
}

分析:test() 方法声明时会抛出 Exception ,主方法中的代码在调用 test() 方法时需要对异常进行处理,这里选择将异常抛出,抛出异常之后是交给 Java 虚拟机来处理了(不建议这种做法)


自定义异常类

在实际开发中,我们除了使用 Java 提供的异常类之外,也可以根据需求来自定义异常类,比如定义一个方法,对传入的参数进行 ++ 操作并返回,同时要求参数必须是整型,如果传入的参数不是整数类型则抛出自定义异常,具体实现代码如下:

class MyNumberException extends Exception {
	public MyNumberException(String error) {
		super(error);
	}
}

public class TestMyException {
	public int add(Object object) throws MyNumberException {
		//instanceof 是 Java 的保留关键字。它的作用是测试它左边的对象是否是它右边的类的实例,返回 boolean 的数据类型。
		if(!(object instanceof Integer)) {
			String error = "传入的参数不是整数类型";
			throw new MyNumberException(error);
		} else {
			int num = (int) object;
			return num++;
		}
	}
	
	public static void main(String[] args) {
		TestMyException test = new TestMyException();
		try {
			int num = test.add("hello");
		}catch (MyNumberException e) {
			e.printStackTrace();
		}
	}
}

运行结果:

在这里插入图片描述

分析:add() 方法定义时声明了可能会抛出 MyNumberException 异常,是 Exception 的子类,所以在 main 方法中 调用 add() 方法时需要手动处理。我们也可以让 main 方法抛出异常,推荐使用 try-catch 的方式

Java 中有些异常在 throw 之后,还需要在方法定义出添加 throws 声明,有些异常则不需要,直接 throw 即可。这是因为 Exception 的异常分 checked exception 和 runtime exception ,checked exception 表示需要强制去处理的异常,即 throw 异常之后需要立即处理,要么自己 try-catch,要么抛给上一层去处理,否则会报错;而 runtime exception 没有这个限制,throw 之后可以不处理。直接继承自 Exception 的类就是 checked exception ,继承自 RuntimeException 的类就是 runtime exception。我们自己定义的 MyNumberException 是直接继承 Exception 的,所以需要在 add() 方法定处声明 throws MyNumberException。


综合练习

使用面向对象章节所学的知识点,重点包括封装、继承、多态、抽象、接口来完成一个汽车查询系统。

需求描述:共有 3 中类型的汽车:小轿车、大巴车、卡车,期中小轿车座位数为 4 座,大巴车座位数为 53 座,卡车座位数为2 座,要求使用封装、继承、抽象来完成车辆的定义。

可以对车辆信息作出修改,卡车可以运货三十载重量不能超过 12 吨,使用自定义异常来处理错误,小轿车和大巴车没有此功能,要求使用接口来实现。

示例:

public interface Container {
	public int getweight();

}

public class CarException extends Exception {
	public CarException(String message) {
		super(message);
	}
}

//定义一个抽象类,车类
public abstract class Car {
	private String name;
	private String color;
	public String getName() {
		return name;
	}
	public String getColor() {
		return color;
	}
	public Car(String name, String color) {
		this.name = name;
		this.color = color;
	}
	
	public abstract String seatNum();
}

//小轿车
class Sedan extends Car{
	public Sedan(String name, String color) {
		super(name,color);
	}
	@Override
	public String seatNum() {
		return "4座";
	}
}

//大巴车
class Bus extends Car{
	public Bus(String name, String color) {
		super(name, color);
	}
	@Override
	public String seatNum() {
		return "53座";
	}
}

//卡车
class Truck extends Car implements Container{
	private int weight;
	public Truck(String name, String color, int weight) {
		super(name, color);
		this.weight = weight;
	}
	@Override
	public String seatNum() {
		return"2座";
	}
	public int getweight() {
		return this.weight;
	}
}

public class CarTest {
	private static Scanner scanner;
	private static Sedan sedan;
	private static Bus bus;
	private static Truck truck;
	private static Car[] cars;
	static {
		scanner = new Scanner(System.in);
		sedan = new Sedan("小轿车","黑色");
		bus = new Bus("大巴车","绿色");
		truck = new Truck("卡车","红色",2);
		cars = new Car[3];
		cars[0] = sedan;
		cars[1] = bus;
		cars[2] = truck;
	}
	public void showCars() throws CarException {
		System.out.println("车辆名称\t\t车辆颜色\t\t座位\t\t载重量");
		for(Car car : cars) {
			if(car instanceof Sedan) {
				Sedan sedan = (Sedan) car;
				System.out.println(sedan.getName()+"\t\t"+sedan.getColor()+"\t\t"+sedan.seatNum());
			}
			if(car instanceof Bus) {
				Bus bus = (Bus) car;
				System.out.println(bus.getName()+"\t\t"+bus.getColor()+"\t\t"+bus.seatNum());
			}
			if(car instanceof Truck) {
				Truck truck = (Truck) car;
				System.out.println(truck.getName()+"\t\t"+truck.getColor()+"\t\t"
				+truck.seatNum()+"\t\t"+truck.getweight());
			}
		}
		System.out.println("1.小轿车\t2.大巴车\t3.卡车");
		System.out.print("请选择要修改的车辆:");
		int num = scanner.nextInt();
		switch(num) {
		case 1:
			update("sedan");
			break;
		case 2:
			update("bus");
			break;
		case 3:
			update("truck");
			break;
		default:
			System.out.println("车辆不存在");
		}
	}
	public void update(String type) throws CarException {
		String name = null;
		String color = null;
		if(type.equals("sedan")) {
			System.out.print("请输入车辆的名称:");
			name = scanner.next();
			System.out.print("请输入车辆的颜色:");
			color = scanner.next();
			Sedan sedan = new Sedan(name,color);
			cars[0] = sedan;
		}
		if(type.equals("bus")) {
			System.out.print("请输入车辆的名称:");
			name = scanner.next();
			System.out.print("请输入车辆的颜色:");
			color = scanner.next();
			Bus bus = new Bus(name,color);
			cars[1] = bus;
		}
		if(type.equals("truck")) {
			System.out.print("请输入车辆的名称:");
			name = scanner.next();
			System.out.print("请输入车辆的颜色:");
			color = scanner.next();
			System.out.print("请输入载重量:");
			int weight = scanner.nextInt();
			if(weight > 12) {
				try {
					throw new CarException("卡车的最大载重量为 12吨");
				}catch(CarException e) {
					e.printStackTrace();
					return;
				}
			}
			Truck truck = new Truck(name,color,weight);
			cars[2] = truck;
		}
		showCars();
	}
	
	public static void main(String[] args) {
		CarTest carTest = new CarTest();
		try {
			carTest.showCars();
		} catch (CarException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}

运行结果:

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值