了解java异常

异常到底是什么?

相信你看了本篇博客,一定会对异常有一个大概的了解~~~


1.什么是异常

在程序的执行过程中,可能因为系统崩坏,虚拟机错误,或者程序编码错误,外在因素等都会使程序执行中断。而这些原因又分为两种:error(错误)和exception(异常)。
error是程序不可控的,比如说负责执行java程序的虚拟机出错了,java程序又怎么能够捕获到或者解决问题呢?因此这类错误我们不用去管,是不可改变的。
异常,就是剩下的那一部分,因程序编码错误或者外在因素导致的,我们可以去捕获到并且处理的,从而避免程序的非正常中断。


2.异常的分类

异常可分为两类:

非检查型异常:编译器不要求强制处理的,这种错误可以避免,RuntimeException及所有子类都属于非检查型异常。

检查型异常:编译器要求必须要进行处理的异常,Exception及其子类(除了RuntimeException及所有子类)都属于检查时异常。检查型异常也并不代表程序一定会出错,而是由于java语言的严谨性,编译器觉得出错的概率不小或者说如果出错,带来的后果很严重,就会要求你一定处理。


3.为什么要异常处理

我们可以想象一下程序的执行过程,如果程序执行到某一行出错,那么后面的程序都无法执行,这样带来的坏处是很大的,严重影响到了程序的稳定性。因此,如果是检查型异常,编译器一定会为我们指出并要求我们处理,如果我们不处理,程序将无法编译运行。而对于剩下的编译器没有提醒我们处理的,如果爆发未被处理的异常,将一定会是非检查型异常。如果我们能够提前意识到哪些地方可能会出现非检查型异常,并事先对它进行处理,那么即使程序爆发异常也会被处理掉,最终执行成功,这样程序的稳定性是不是会大大提高呢?


3.try-catch处理

		int a = 0;
		int b = 1;
		try {
			b = b/a;
			//在try中如果爆发异常会被下面捕获
		} catch (ArithmeticException e) {
			//当捕捉到这个异常时会执行语句
			e.printStackTrace();
		} catch (Exception e) {
			//当捕捉到这个异常时会执行语句
			e.printStackTrace();
		} finally {
			//最终一定会执行的,即使程序return
		}

在try-catch中的语句,如果运行时产生异常,将直接被捕获并与下面的catch比较,如果爆发的异常类型属于catch中异常类的子类,将会进入catch中,执行完语句后进入finally,(try-catch语句中可以没用finally语句)如果没有finally将跳出try-catch。

注意:
1.各个catch中上面的chatch语句中的异常类不能是下面的catch语句中的异常类的基类,因为如果异常属于上面catch中的子类,一定会进入上面catch语句,如果不是,那么也一定不是下面catch语句的子类,所以,下面这个catch语句将永远不会执行。
2.finally语句中的内容一定会执行,即使在上面catch语句中有return语句。


4.throws

public class Throw {

	public void say() throws ClassNotFoundException   {
		Class.forName("..");
	}
	
}

public class Test {

	public static void main(String[] args) {
		
		Throw throw1 = new Throw();
		try {
			throw1.say();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
	
}

上面已经说过了,对于检查型异常,编译器一定要求你处理,但try-catch并不是唯一的处理方法,throws是另外一种,throws的意思就是:我先不处理这个异常,抛出去,谁调用我这个方法就让谁去处理。

throws抛出检查时异常:
如果抛出检查型异常随之带来的结果就是,调用这个方法的方法一定要处理这个异常,而这个方法也可以选择把这个异常抛出去,或者直接try-catch处理,如果直接处理,将不用再抛。也就是说,检查型异常是一定要被处理的,不管抛了多少次,只要没处理就要继续抛,直到主函数,主函数也是可以选择是try-catch还是throws。

throws抛出不同异常:
throws也可以抛出非检查型异常,如果只抛非检查型异常,那么调用此方法的方法也无需处理该异常,也很容易理解,编译器不强制处理非检查型异常,因此这样做时很不规范的做法。

主函数选择throws:
那么我们想,如果主函数抛出检查型异常,那么程序确实可以编译运行了,但是它到底代表什么含义呢?没有一个地方去处理异常,那么如果爆发异常,后面的代码将无法执行,也就是说,这种方式仅仅是让程序得以运行,如果异常出现,是处理不了的。

public class Throw {

	public void say() throws ClassNotFoundException   {
		try {
			Class.forName("..");
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
}
public static void main(String[] args){
	
	Throw throw1 = new Throw();
	try {
		throw1.say();
	} catch (ClassNotFoundException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();	//永远不会执行
	}
	
	System.out.println("...");
	
}

无用的throws:
如果我们在方法中已经try-catch了抛出的异常,,但是又抛出异常,那么调用此方法的方法就必须得try-catch,那么,这个try-catch会执行吗?当然不会了,如果爆发异常,调用得方法里面已经处理过了,因此这个try-catch永远也不会执行。


5.throw

public class Test {

	public static void main(String[] args){
		
		try {
			throw new ClassNotFoundException();	//	throw
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("...");
		
	}
	
}

throw就比较简单了,这行代码的意思是抛出了一个异常对象。

throw特点:
也可以理解为,这行代码的位置一定会爆异常,与前面一样,如果为检查型异常必须处理,为非检查型异常则不需要,但是需要注意的是,由于这行代码一定会爆异常,那么我们按照正常代码执行顺序,如果后面的语句怎么都无法执行,将会出错,例如我们在方法中将它抛出,虽然在别的地方会把它try-catch,但是在它后面的语句都无法执行。


6.自定义异常类

我们可以自己定义一个异常类,这样的好处是什么呢?
当代码在执行到某一处,我们发现原本不该执行到这里,既然执行到了这里,说明一定有需要注意的地方,我们需要提醒别人这里需要注意,并且能够快速找到这里,还能够不影响后面代码的执行。这个时候就能用上自定义异常类了。

public class AgeException extends RuntimeException {

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

public class Student {

	private int age;

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		if(age<0 || age >20) {
			try {
				throw new AgeException("年龄不符合要求!!!");
			} catch (AgeException e) {
				e.printStackTrace();
			}
		}
		this.age = age;
	}
	
	public static void main(String[] args) {
		
		Student student = new Student();
		student.setAge(30);
		System.out.println("...");	//这里的语句是可以执行的
		
	}
	
}

输出:

T3.AgeException: 年龄不符合要求!!!
...
	at T3.Student.setAge(Student.java:14)
	at T3.Student.main(Student.java:25)

输出可以快速帮我们找到需要注意的信息,且该异常对于程序的正常执行完全没有影响。


6.log4j

有些时候,如果错误输出信息太多,然后控制台的输出空间又是有限的,将会有很多的错误信息被删除,这对于我们寻找错误信息很不好。

因此,可以使用log4j去帮助我们将错误信息存储在磁盘上,防止丢失。

1.导入log4j jar包

在这里插入图片描述
2.在src根目录新建文件,文件名为log4j.properties。

3.在文件中添加如下信息

# DEBUG\u8BBE\u7F6E\u8F93\u51FA\u65E5\u5FD7\u7EA7\u522B\uFF0C\u7531\u4E8E\u4E3ADEBUG\uFF0C\u6240\u4EE5ERROR\u3001WARN\u548CINFO \u7EA7\u522B\u65E5\u5FD7\u4FE1\u606F\u4E5F\u4F1A\u663E\u793A\u51FA\u6765
log4j.rootLogger=DEBUG,Console,RollingFile

#\u5C06\u65E5\u5FD7\u4FE1\u606F\u8F93\u51FA\u5230\u63A7\u5236\u53F0
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern= [%-5p]-[%d{yyyy-MM-dd HH:mm:ss}] -%l -%m%n
#\u5C06\u65E5\u5FD7\u4FE1\u606F\u8F93\u51FA\u5230\u64CD\u4F5C\u7CFB\u7EDFD\u76D8\u6839\u76EE\u5F55\u4E0B\u7684log.log\u6587\u4EF6\u4E2D
log4j.appender.RollingFile=org.apache.log4j.DailyRollingFileAppender
log4j.appender.RollingFile.File=D://log.log
log4j.appender.RollingFile.layout=org.apache.log4j.PatternLayout
log4j.appender.RollingFile.layout.ConversionPattern=%d [%t] %-5p %-40.40c %X{traceId}-%m%n

4.在测试类中检查是否成功

public class Test {
	public static void main(String[] args) {
			Logger logger = Logger.getLogger(Test.class);	
	
			while(true) {
				try {
					int x = 1/0;
				} catch (Exception e) {
					logger.debug(e.getMessage(), e);	//在文件夹中输出错误信息,必须有这一行代码
					e.printStackTrace();
				}
			}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值