详述try-catch-finally
一、什么是异常?
Java语言将程序运行过程中发生的不正常严重错误称为异常,对异常的处理称为异常处理。
二、异常的后果
它会中断正在运行的程序,正因如此,异常处理是程序设计中非常重要的方面,也是程序设计的一大难点。
public class Test {
public static void main(String[] args) {
System.out.println(111);
System.out.println(1/0);//Java语言将程序运行过程中所发生的不正常严重错误称为异常,对异常的处理称为异常处理。
System.out.println(222);//它会中断正在运行的程序,正因为如此异常处理是程序设计中一个非常重要的方面,也是程序设计的一大难点。
}
}
“System.out.println(1/0);”,0不能出现在分母,因此该语句出现异常。由程序运行结果可以看出,程序出现异常后,异常语句后面的程序“System.out.println(222);”不再执行。
三、异常的结构
1、Throwable类中有两个常用方法:public String getMessage():获取异常信息;public void printStackTrace():输出异常堆栈中的异常信息。又因为Throwable类是Exception类和Error类的父类,因此Exception类及其所有子类都能调用这两个方法。
2、Error : 指合理的应用程序在执行过程中发生的严重问题。当程序发生这种严重错误时,通常的做法是通知用户并中止程序的执行。当程序报Error时,程序员不用管,交给JVM处理。
3、异常又可分为运行时异常和检查时异常,RuntimeException:运行时异常,即程序运行时抛出的异常。这种异常在写代码时不进行处理,Java源文件也能编译通过。 RuntimeException异常类及其下面的子类均为运行时异常。CheckedException:检查时异常,又称为非运行时异常,这样的异常必须在编程时进行处理,否则就会编译不通过。Exception异常类及其子类(除去RuntimeException异常类及其子类)都是检查时异常。
如下例:
运行时异常:
public class Test {
public static void main(String[] args) {
System.out.println(1/0);
}
运行时异常:可以不显式对异常处理,Javac仍然可以编译。
检查时异常:
程序在编译时报错
public class Test {
public static void main(String[] args) {
try {
System.out.println(1/0);
} catch (Exception e) {
e.printStackTrace();
}
try {
Class.forName("");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
因此,对于检查时异常,必须显式对异常处理,否则该异常无法通过编译。
四、异常处理方式 try-cathc-fianlly
1、捕获异常语法结构:
try{
//可能抛出异常的语句块
}catch(SomeException1 e){ // SomeException1特指某些异常
//当捕获到SomeException1类型的异常时执行的语句块
} catch( SomeException2 e){
//当捕获到SomeException2类型的异常时执行的语句块
}finally{
//无论是否发生异常都会执行的代码
}
public class Test {
public static void main(String[] args) {
try {
String name = null;
System.out.println(name.length());
} catch (NullPointerException e) {
e.printStackTrace();
} finally {
System.out.println("必须执行");
}
}
}
即使try{}块中的语句没有发生异常,finally{}块中的语句仍会执行
public class Test {
public static void main(String[] args) {
try {
String name = "Tom";
System.out.println(name.length());
} catch (NullPointerException e) {
e.printStackTrace();
} finally {
System.out.println("必须执行");
}
}
}
2、try…catch…finally异常处理结构中,try语句块是必须的, catch和finally语句块至少出现一个。
注意:如果try语句块包含的是检查时异常,则在没有通过throws抛出该异常类的情况下,try必须和catch一起使用,当该行代码去掉或注销掉时,catch相应的异常语句块必须去掉,如下代码:
由于该行代码抛出检查时异常,所以该行代码去掉或注销掉时,catch相应的异常语句块必须去掉。
3、try语句块中的代码可能会引发多种类型的异常,当引发异常时,会按顺序查看每个 catch 语句,并执行第一个与异常类型匹配的catch语句,其后 catch 语句被忽略。在捕获异常的时候,应按照“从小到大”的顺序捕获异常,即先子类后父类。
public class Test {
public static void main(String[] args) {
try {
String name = null;
System.out.println(name.length());
System.out.println(1/0);
Integer.parseInt("QQQQQ");
} catch (NullPointerException e) {
System.out.println("null"+e);
} catch (ArithmeticException e) {
System.out.println("0"+e);
} catch (Exception e) {
System.out.println("other"+e);
}
}
}
try{}语句块中第一个异常是空指针异常,因此将会执行第一个与空指针异常匹配的catch语句。NullPointerException和ArithmeticException都是Exception的子类,因此在捕获异常时,应按照先子类后父类,“从小到大”的方式捕获异常。
再例如下面的程序:
先子类异常,后父类异常:ArrayIndexOutOfBoundsException异常类是RuntimeException的子类,而RuntimeException异常类是Exception的子类,他们的先后顺序不能颠倒。
4、finally关键字
①Java异常在try/catch块后加入finally块,可以确保无论是否发生异常 finally块中的代码总能被执行。
无论是否异常,都将执行finally块中语句。即使return也不例外。仅当程序调用System.exit(1)才不执行该块。
②final、finally和finalize区别
a、final—修饰符(关键字),修饰的类不能被继承,修饰的方法不能被重写,修饰的变量为常量。
b、finally—在异常处理时提供 finally 块来执行任何清除操作。
c、finalize—方法名,finalize() 方法在垃圾收集器将对象从内存中清除之前做必要的清理工作,如下代码:
class Student {
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("对象从内存中清除之前执行");
}
}
public class Test {
public static void main(String[] args) {
new Student();
System.gc();//执行该代码,垃圾收集器将回收未使用的内存空间。
}
}
五、Log4j
1、什么是Log4j?
Log4j是Apache的一个开源项目,通过使用Log4j,可以控制日志信息格式及其输送目的地(控制台、文件、数据库等),方便后期查找系统运行期间出现的问题,进而便于维护系统。
2、为什么要用Log4j?
public class Test {
public static void main(String[] args) {
int i = 1;
while(true) {
try {
System.out.println(1/0);
} catch (Exception e) {
e.printStackTrace();
System.out.println(i);
}
i++;
}
}
}
程序执行了不到十秒钟,控制台就出现七万多行异常报错,并且最初的异常还丢失了。
程序运行过程中如果出现异常,将会把异常信息打印控制台并存入一段缓存,缓存有大小,当缓存满了,但是异常信息源源不断,则会将最先进入缓存的异常信息移除。这就意味着无法全面的发现程序运行期间出现的问题,为了全面收集系统运行期间出现的所有异常信息就需要利用Log4j。Log4j将所有异常信息输出到一个文件中,因为文件在硬盘上可以认为无限大。
3、Log4j怎么用?
第一步:导入log4j-1.2.15.jar依赖包;
在你需要利用Log4j的Java项目中,新建一个lib文件夹,并将og4j-1.2.15.jar包拷贝到里面,然后Build Path->Add Build Path,导入log4j-1.2.15.jar依赖包。
第二步:在src根目录下创建名为log4j.properties的文件,文件内容如下:
#DEBUG设置输出日志级别,由于为DEBUG,所以ERROR、WARN和INFO 级别日志信息也会显示出来
log4j.rootLogger=DEBUG,Console,RollingFile
#将日志信息输出到控制台
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
#将日志信息输出到操作系统D盘根目录下的log.log文件中
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
第三步:src目录创建Test类,代码如下:
import org.apache.log4j.Logger;
public class Test {
private static final Logger logger = Logger.getLogger(Test.class);
public static void main(String[] args) {
try {
Class.forName("ErrorClassName");
} catch (ClassNotFoundException e) {
logger.debug(e.getMessage(),e);//详细日报信息
logger.info(e.getMessage(),e);//详细日报信息
logger.warn(e.getMessage());//简单日报信息
logger.error(e.getMessage());//简单日报信息
}
}
}
运行Test类方法,打开D盘根目录中log.log文件可以看到相应的日志信息,此时的日志信息都会显示出来,Log4j常用日志级别从高到低依次为:ERROR、WARN、INFO和DEBUG,由于上例所设置的Log4j日志级别为DEBUG,所以ERROR、WARN和INFO 级别的日志信息也会显示出来。
4、间歇性产生新日志文件
上例配置文件是将所有的日志信息都收集到了一个文件中,那么随着时间的推移,该文件会越来越大,内容也会越来越多,这不利于后期对日志文件进行分析,为了解决该问题可以这样配置log4j.properties文件:
#DEBUG设置输出日志级别,由于为DEBUG,所以ERROR、WARN和INFO 级别日志信息也会显示出来
log4j.rootLogger=DEBUG,RollingFile
#每天产生一个日志文件(RollingFile)
log4j.appender.RollingFile=org.apache.log4j.DailyRollingFileAppender
#当天的日志文件全路径
log4j.appender.RollingFile.File=d:/logs/sirius.log
#服务器启动日志是追加,false:服务器启动后会生成日志文件把老的覆盖掉
log4j.appender.RollingFile.Append=true
#日志文件格式
log4j.appender.RollingFile.layout=org.apache.log4j.PatternLayout
log4j.appender.RollingFile.layout.ConversionPattern=%d [%t] %-5p %-40.40c %X{traceId}-%m%n
log4j.appender.RollingFile.Threshold=DEBUG
#设置每天生成一个文件名后添加的名称,备份名称:sirius.log.年月日时分.log
log4j.appender.RollingFile.DatePattern=’.‘yyyy-MM-dd-HH-mm’.log’
DatePattern选项的有效值为:
'.'yyyy-MM,对应monthly(每月)
'.'yyyy-ww,对应weekly(每周)
'.'yyyy-MM-dd,对应daily(每天)
'.'yyyy-MM-dd-a,对应half-daily(每半天)
'.'yyyy-MM-dd-HH,对应hourly(每小时)
'.‘yyyy-MM-dd-HH-mm,对应minutely(每分钟)
DatePattern中不用处理的文字要放到单引号(’)中,如上面的(.)。
这样,每隔一个固定时间就会创建一个新的文件夹。