异常类

异常

概述:异常是指在程序的运行过程中所发生的不正常的事件,它会中断正在运行的程序,简而言之就是程序出现了不正常的情况。异常的本质就是java当中对可能出现的问题进行描述的一种对象体现

常见的异常有:
1.ArithmeticException除数不能为0异常
2.NullPointException空指针异常
3.ArrayIndexOutOfBoundsException数组越界异常
4.ClassCastException类型转换异常

异常分类:
Error:错误,由java虚拟机生成并抛出,程序不对其做处理。在程序执行的整个阶段,都可发生错误,错误是无法解决的,可以避免 例如: StackOverflow
Exception:所有异常类的父类。其子类对应了各种各样可能出现的异常事件,一般需要用户显示的声明或者捕获。

RuntimeException:运行时期异常。又称非受检异常,RuntimeException及其所有子类都是运行时期异常。

编译时期异常:不是继承自RuntimeException的Exception的子类都成为编译时期异常。
在这里插入图片描述
处理异常:
目的:不是为了修复程序的错误,而是程序出现错误时,能够让程序继续执行下去

JVM默认处理异常的过程:
1、打印错误信息

a、异常名称,例如:java.lang.ArithmeticException
b、异常消息  / by zero
c、异常所发生的方法位置 at 包名.类名.方法名
d、异常所在java文件中 类名.java
e、异常发生行号  例如:20

2、终止程序
System.exit(0);

其实JVM处理异常的方式并不能满足我们实际开发的需求,实际需要我们自己处理异常

处理异常的步骤:
1、try…catch…finally
格式:

	try{
      //放置程序可能出现问题的代码
  	}catch(异常类  异常名){
   		//这里放置异常处理的代码
	 } finally{
	      //释放资源
   }

2、throws
这里需要注意的是:

1、try块的代码越少越好
2、try块中一旦发生异常,那么try块中发生异常,后面所有的代码都不会执行
3、多个catch块只会执行一个
4、Exception接收异常必须放在异常的最后面,满足先子类后父类

异常执行流程:
1、程序执行到错误行,系统会创建一个异常对象,并且抛给我们

ArithmeticException exp = new ArithmeticException("/by zero");
 			throw exp;

2、程序进入catch块进行逐个匹配,匹配成功,程序执行catch块代码

ArithmeticException ae = exp;
Exception e = exp;

匹配失败,交给jvm处理
处理异常的标准方式:
1.能够显示处理的尽量显示处理,提高程序的可读性
2.但是一定要在异常处理的最后加上 父类Exception处理
举例代码如下

public class ExceptionDemo02 {
	public static void main(String[] args) {
		System.out.println("Start Application");
		int a = 10;
		int b = 0;
		int[] arr = new int[3];
		Object obj = null;
		Object obj2 = new Integer(100);
		try {
//			System.out.println(a / b);
//			System.out.println(arr[4]);
//			obj.equals("ss");
			String s = (String) obj2;
		} catch (ArithmeticException ae) {
			System.out.println("算数异常,出问题1");	
		} catch (ArrayIndexOutOfBoundsException aioobe) {
			System.out.println("数组越界,出问题2");
		} catch (NullPointerException ne) {
			System.out.println("空指针异常,出问题3");
		} catch (Exception e) {
			System.out.println("出问题了");
		}
		
		System.out.println("End");
		
		
	}
}

Throwable类:

错误的相关信息都保存在这个类里面,它是所有异常的父类

观察Throwable的源码:

Throwable t = new NullPointerException("空指针异常");

 class NullPointerException extends RuntimeException {
  	public NullPointerException(String s) {
	        super(s); // "空指针异常"
	    }
  }
 
 class RuntimException extends Exception {
 	public RuntimeException(String message) {
	        super(message); // "空指针异常"
	    }
 }
 
 class Exception extends Throwable {
 	public RuntimeException(String message) {
	        super(message); // "空指针异常"
	    }
  }

	class Throwable {
 		private String detailMessage; "空指针异常"
		
		public String toString() {
	        String s = getClass().getName(); //  java.lang.NullPointerException
	        String message = getLocalizedMessage(); "空指针异常"
	        return (message != null) ? (s + ": " + message) : s; java.lang.NullPointerException:"空指针异常"
	    }
	    
	    public String getLocalizedMessage() {
	        return getMessage(); // "空指针异常"
	    }
	    
	    public String getMessage() {
	        return detailMessage; // "空指针异常"
	    }
	    				// "空指针异常"
	    public Throwable(String message) {
	        fillInStackTrace(); // 将错误信息填充到堆栈中
	        detailMessage = message; "空指针异常"
	    }
	    
	    public synchronized Throwable fillInStackTrace() {
        if (stackTrace != null ||
            backtrace != null  Out of protocol state  ) {
            fillInStackTrace(0);
            stackTrace = UNASSIGNED_STACK;
        }
        return this;
    }

    	private native Throwable fillInStackTrace(int dummy);
 }

如何来阅读异常?

  • 从下往上看,先看你自己写的
  • 再逐层往上查阅,
  • 如果不是你自己写的方法出问题,再看源码的方法是否出问题
  • 源码里面也有可能是本地方法出问题

举例代码如下

public class ExceptionDemo03 {
   public static void main(String[] args) {
   	// 利用多态来创建异常对象
//		Throwable t = new NullPointerException("空指针异常");
//		System.out.println(t); // java.lang.NullPointerException: 空指针异常
//		System.out.println(t.toString());
   	try {
   		method();
   	} catch (Exception e) {//  Exception e = new NullPointerException("空指针异常");
//			System.out.println("出问题了");
//			 StackTraceElement[] stackTrace = e.getStackTrace(); // 从堆栈中获取错误信息
//			/*
//			 *   String getClassName() 
//				          返回类的完全限定名,该类包含由该堆栈跟踪元素所表示的执行点。 
//				 String getFileName() 
//				          返回源文件名,该文件包含由该堆栈跟踪元素所表示的执行点。 
//				 int getLineNumber() 
//				          返回源行的行号,该行包含由该堆栈该跟踪元素所表示的执行点。 
//				 String getMethodName() 
//				 boolean isNativeMethod() 
//		          	如果包含由该堆栈跟踪元素所表示的执行点的方法是一个本机方法,则返回 true。 
//			 ExceptionDemo03.java|com.sxt.exceptiondemo.ExceptionDemo03|113|show|false
//			 ExceptionDemo03.java|com.sxt.exceptiondemo.ExceptionDemo03|107|method|false
//			 ExceptionDemo03.java|com.sxt.exceptiondemo.ExceptionDemo03|74|main|false
//			 */
//			for (StackTraceElement element : stackTrace) {
//				String fileName = element.getFileName();
//				String className = element.getClassName();
//				int lineNumber = element.getLineNumber();
//				String methodName = element.getMethodName();
//				boolean nativeMethod = element.isNativeMethod();
//				System.out.println(fileName + "|" + className 
//						+ "|" + lineNumber + "|" + methodName + "|" + nativeMethod) ;
				ExceptionDemo03.java|com.sxt.exceptiondemo.ExceptionDemo03|64|main|false
//			}
   		e.printStackTrace();
   	}
   	System.out.println("go on");
   	
   }
   
   public static void method() {
   	System.out.println("start Method");
   	show();
   	System.out.println("end Method");
   }

   public static void show() {
   	System.out.println("start show");
   	NullPointerException t = new NullPointerException("空指针异常");
   	throw t;
   }
}

处理编译时异常和运行时异常
编译时异常的处理: Ctrl + 1
运行时异常的处理: try…catch throws
举例代码如下

public class ExceptionDemo05 {
	public static void main(String[] args) {
		method1();
		try {
			method2();
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		System.out.println("go on");
	}

	private static void method2() {
		Object obj = null;
//		try {
//			
//		} catch (Exception e) {
//			e.printStackTrace();
//		}
		System.out.println(obj.equals("dada"));
		
	}

	private static void method1() {
		Student s = new Student();
		try {
			s.clone();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		
	}
	
}

class Student implements Cloneable {
	@Override
	protected Object clone() throws CloneNotSupportedException {
		// TODO Auto-generated method stub
		return super.clone();
	}
}

有了try…catch处理异常,为什么还需要throws处理方式?
在某些情况下,我们不会处理异常,或者我们没有权限异常,干脆我就不想处理异常
在这种情况下我们可以抛出异常,在方法上抛出
谁调用我有问题的方法就谁处理
千万不要交给虚拟机处理

throws处理异常的格式
[修饰符] 返回值类型 方法名(参数列表) [throws 异常类1,异常类2…]{
}
注意:
1.异常的声明只是一种可能性,可能方法中没有异常,但是也可以声明
2.子类重写方法抛出的异常不能够被扩大
举例代码如下

public class ExceptionDemo06 {
   public static void main(String[] args) {
   	System.out.println("start");
   	try {
   		method();
   	} catch (Exception e) {
   		e.printStackTrace();
   	}
   	
   	System.out.println("end");
   }

   private static void method() throws ArithmeticException{
   	method2();
   }

   private static void method2() throws ArithmeticException, NullPointerException {
   	int a =10;
   	int b = 0;
   	System.out.println(a / b);
   }
   
   
}

class Fu {
   public void show() throws Exception {
   	
   }
}

class Zi extends Fu {
   @Override
   public void show() throws NullPointerException {
   }
}

有了throws为什么还需要throw?
格式:
throw 异常对象;
谁调用就将对象抛给谁
当抛出的是编译时异常的时候,必须要在方法体上声明异常
当是运行时异常的时候,可以不用声明,但是建议声明
throw和throws的区别(重点)
1.throws出现在方法的声明上,throw关键出现在方法内
2.throws表示一种异常的可能性,throw表示程序的必然性
3.throws可以声明多个异常类,throw只能够抛出一个异常
4.throws声明的是异常类,throw抛出的是异常对象

举例代码如下

public class ExceptionDemo07 {
	public static void main(String[] args) {
		String s = new String(new byte[] {11,22,33,44}, -1, 3);
		System.out.println(s);
		try {
			calc();
		} catch (Exception e) {
			e.printStackTrace();
		}
		System.out.println("go on");
	}

	private static void calc() throws CloneNotSupportedException {
		int a = 10;
		int b = 0;
		// 方式一处理异常:
//		if (b != 0) {
//			System.out.println(a / b);
//		}
		
		// 方式二处理异常:
		if (b == 0) {
			throw new CloneNotSupportedException("除数不能为0");
		}
		System.out.println(a / b);
		
	}
}

finally关键字
try {

} catch() {

} finally {

}

finally关键字
1.finally修饰代码块一定会被执行,除非碰到程序退出
System.exit(0);
Runtime.getRuntime().exit(0);
2.一般用来释放内存资源,比如关闭IO流的输入输出对象,数据库连接对象等等…
举例代码如下

public class ExceptionDemo08 {
	public static void main(String[] args) throws FileNotFoundException {

		System.out.println("start");
		// try {
		// System.out.println(10/0);
		// } finally {
		// System.out.println("finally代码块执行了");
		// }

//		Scanner input = new Scanner(System.in);
//		InputStream is = new FileInputStream("a.txt");
//
//		try {
//			System.out.println(10 / 0);
//		} catch (Exception e) {
//			e.printStackTrace();
//			// return;
//			// System.exit(0);
//		} finally {
//			System.out.println("finally代码块执行了");
//			if (input != null) {
//				input.close();
//			}
//
//			if (is != null) {
//				try {
//					is.close();
//				} catch (IOException e) {
//					// TODO Auto-generated catch block
//					e.printStackTrace();
//				}
//			}
//		}
		

		try (Scanner in = new Scanner(System.in); 
				InputStream inputStream = new FileInputStream("a.txt");) {
			System.out.println(10/0);
		} catch (Exception e) {
			e.printStackTrace();
		}
		System.out.println("end");
	}
}

finally碰到return
finally一定会执行
执行顺序:
在try语句中,在执行return语句时,要返回的结果已经准备好了,就在此时,程序转到finally执行了。
在转去之前,try中先把要返回的结果存放到不同于x的局部变量中去,执行完finally之后,再从中取出返回结果,
因此,即使finally中对变量x进行了改变,但是不会影响返回结果。它应该使用栈保存返回值。
finally final finallize的区别:
final:
在java中,final可以修饰类,成员方法,成员变量
final修饰类时,不能被子类继承
final修饰成员方法时,不能被子类重写
final修饰的成员变量变为常量,只能赋值一次

finallize:
finallize()方法在java.lang.Object类定义,即所有类里面都有这个方法,这个方法在gc启动,该对象被回收的时候被调用。其实gc可以回收大部分的对象(凡是new出来的对象,gc都能搞定,一般情况下我们又不会用new以外的方式去创建对象),所以一般是不需要程序员去实现finalize的。
特殊情况下,需要程序员实现finalize,当对象被回收的时候释放一些资源,比如:一个socket链接,在对象初始化时创建,整个生命周期内有效,那么就需要实现finalize,关闭这个链接。 使用finalize还需要注意一个事,调用super.finalize();
一个对象的finalize()方法只会被调用一次,而且finalize()被调用不意味着gc会立即回收该对象,所以有可能调用finalize()后,该对象又不需要被回收了,然后到了真正要被回收的时候,因为前面调用过一次,所以不会调用finalize(),产生问题。 所以,推荐不要使用finalize()方法,它跟析构函数不一样。

finally:
在try语句中,在执行return语句时,要返回的结果已经准备好了,就在此时,程序转到finally执行了。
在转去之前,try中先把要返回的结果存放到不同于x的局部变量中去,执行完finally之后,再从中取出返回结果,
因此,即使finally中对变量x进行了改变,但是不会影响返回结果。它应该使用栈保存返回值。

举例代码如下

public class ExceptionDemo09 {
	public static void main(String[] args) {

		System.out.println(test()); // 4 3
	}

	public static int test() {
		int x = 1;
		try {
			System.out.println(x++ / 0); // 2
		} catch (Exception e) {
			x++; //
			return x; // return 3;
		} finally {
			++x; // 4
			System.out.println(x); // 4
		}
		return x;
	}
}

自定义异常
异常的本质就是Java中对错误问题的描述的一种对象体现
java提供的异常不够我们自己使用,我们需要自定义异常,自己应用在我们的开发中
例如:
自定义分数异常: 0~100之间
自定义异常的步骤
1.创建一个异常类继承 Exception或者RuntimeException
2.在自定义异常类中书写构造方法即可
a.无参构造
b.带错误信息的构造方法

代码实现

public class ExceptionDemo10 {
	public static void main(String[] args) {
		Scanner input = new Scanner(System.in);
		System.out.print("请输入分数:");
		double score = input.nextDouble();
		
		Teacher t = new Teacher();
		try {
			System.out.println(t.isBetween0to100(score) ? "分数合法" : "分数不合法");
		} catch (ScoreException e) {
			e.printStackTrace();
		} finally {
			if (input != null) {
				input.close();
			}
		}
		
		System.out.println();
		System.out.println("分数修改完毕!!!");
	}
}

class Teacher {
	// 判断分数是否在0~100分之间
	public boolean isBetween0to100(double score) throws ScoreException {
		if (score < 0 || score > 100) {
			throw new ScoreException("分数必须在0~100分之间:" + score);
		} 
		return true;
	}
}

class ScoreException extends Exception {

	private static final long serialVersionUID = -3114508965246281767L;
	
	public ScoreException() {}
	
	public ScoreException(String message) {
		super(message);
	}
	
}

需求:自定义运行时异常: 身份证异常
代码实现


public class ExceptionDemo11 {
	public static void main(String[] args) {
		
		boolean checkIDCard = false;
		try (Scanner input = new Scanner(System.in);){
			String id = input.next();
			checkIDCard = CheckUtils.checkIDCard(id);
		} catch (Exception e) {
			e.printStackTrace();
		} 
		
		if (checkIDCard) {
			System.out.println("合法");
		} else {
			System.out.println("非法身份证");
		}
	}
}

class CheckUtils {
	
	private CheckUtils() {}
	
	public static boolean checkIDCard(String idCard) throws IDCardException {
		if (!idCard.matches("(\\d{14}[0-9a-zA-Z])|(\\d{17}[0-9a-zA-Z])")) {
			throw new IDCardException("身份证不满足要求!!!!" + idCard);
		}
		return true;
	}
}

class IDCardException extends RuntimeException {

	private static final long serialVersionUID = 392084157998377850L;
	
	public IDCardException() {}
	
	public IDCardException(String msg) {
		super(msg);
	}
}

断言assert

一、概述

在C和C++语言中都有assert关键,表示断言。
在Java中,同样也有assert关键字,表示断言,用法和含义都差不多。

二、语法

在Java中,assert关键字是从JAVA SE 1.4 引入的,为了避免和老版本的Java代码中使用了assert关键字导致错误,Java在执行的时候默认是不启动断言检查的(这个时候,所有的断言语句都将忽略!),如果要开启断言检查,则需要用开关-enableassertions或-ea来开启。

assert关键字语法很简单,有两种用法:

1、assert <boolean表达式>
如果<boolean表达式>为true,则程序继续执行。
如果为false,则程序抛出AssertionError,并终止执行。

2、assert <boolean表达式> : <错误信息表达式>
如果<boolean表达式>为true,则程序继续执行。
如果为false,则程序抛出java.lang.AssertionError,并输入<错误信息表达式>。

举例

public class AssertFoo {
    public static void main(String args[]) {
        //断言1结果为true,则继续往下执行
        assert true;
        System.out.println("断言1没有问题,Go!");
 
        System.out.println("\n-----------------\n");
 
        //断言2结果为false,程序终止
        assert false : "断言失败,此表达式的信息将会在抛出异常的时候输出!";
        System.out.println("断言2没有问题,Go!");
    }
}
 
/*保存代码到C:\AssertFoo.java,然后按照下面的方式执行,查看控制台输出结果:
 
1、编译程序:
C:\>javac AssertFoo.java
 
2、默认执行程序,没有开启-ea开关:
C:\>java AssertFoo
断言1没有问题,Go!
 
-----------------
 
断言2没有问题,Go!
 
3、开启-ea开关,执行程序:
C:\>java -ea AssertFoo
断言1没有问题,Go!
 
-----------------
 
Exception in thread "main" java.lang.AssertionError: 断言失败,此表达式的信息将
会在抛出异常的时候输出!
        at AssertFoo.main(AssertFoo.java:10)
*/

注意:
assert关键字用法简单,但是使用assert往往会让你陷入越来越深的陷阱中。应避免使用。笔者经过研究,总结了以下原因:

1、assert关键字需要在运行时候显式开启才能生效,否则你的断言就没有任何意义。而现在主流的Java IDE工具默认都没有开启-ea断言检查功能。这就意味着你如果使用IDE工具编码,调试运行时候会有一定的麻烦。并且,对于Java Web应用,程序代码都是部署在容器里面,你没法直接去控制程序的运行,如果一定要开启-ea的开关,则需要更改Web容器的运行配置参数。这对程序的移 植和部署都带来很大的不便。

2、用assert代替if是陷阱之二。assert的判断和if语句差不多,但两者的作用有着本质的区别:assert关键字本意上是为测试 调试程序时使用的,但如果不小心用assert来控制了程序的业务流程,那在测试调试结束后去掉assert关键字就意味着修改了程序的正常的逻辑。

3、assert断言失败将面临程序的退出。这在一个生产环境下的应用是绝不能容忍的。一般都是通过异常处理来解决程序中潜在的错误。但是使用断言就很危险,一旦失败系统就挂了。

assert的思考

assert既然是为了调试测试程序用,不在正式生产环境下用,那应该考虑更好的测试JUint来代替其做用,JUint相对assert关键的所提供的功能是有过之而无不及。当然完全可以通过IDE debug来进行调试测试。在此看来,assert的前途一片昏暗。

因此,应当避免在Java中使用assert关键字,除非哪一天Java默认支持开启-ea的开关,这时候可以考虑。对比一下,assert能给你带来多少好处,多少麻烦,这是我们选择是否使用的的原则。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值