什么是异常:
Java语言将程序运行过程中所发生的不正常严重错误称为异常,对异常的处理称为异常处理。
异常的后果:
它会中断正在运行的程序。
异常的分类:
注意:
1、这个连接关系是父类与子类的关系:即Throwable是Exception和Error的父类,同理,Exception也是RuntimeException的父类等等。
2、Throwable有两个常用方法:
① public String getMessage():获取异常信息。
② public void printStackTrace():输出异常堆栈中的异常信息。
因为是父类子类关系,所以Throwable后面的异常都可以继承这两个方法。
3、Throwable有两个直接子类,Error类和Exception类。
① Error : 指合理的应用程序在执行过程中发生的严重问题。当程序发生这种严重错误时,通常的做法是通知用户并中止程序的执行。
② Exception:异常可分为运行时异常(RuntimeException)和检查时异常(CheckedException)两种:
(1)RuntimeException:运行时异常,即程序运行时抛出的异常。这种异常在写代码时不进行处理,Java源文件也能编译通过。 RuntimeException异常类及其下面的子类均为运行时异常。
public class Test {
public static void main(String[] args) {
System.out.println(6);
System.out.println(1/0); //如果程序出现异常,则同级代码不再运行。
System.out.println(8);
}
}
该代码可以编译运行,即便不对该代码进行处理,java源文件也能通过javac命令将该源文件编译成class文件。但是如果程序出现异常,则同级代码不再运行,所以输出结果为:
6
Exception in thread “main” java.lang.ArithmeticException: / by zero
at test.Test.main(Test.java:5)
此时,ArithmeticException是发生异常的名称,这个是算术异常。
(2)CheckedException:检查时异常,又称为非运行时异常,这样的异常必须在编程时进行处理,否则就会编译不通过。Exception异常类及其子类(除去RuntimeException异常类及其子类)都是检查时异常。
public class Test {
public static void main(String[] args) {
System.out.println(1/0);
Class.forName(""); //此行会报错。必须显式对异常的处理,不然javac不会对程序进行编译。
}
}
上面即为检查时异常,必须要在编程的时候更改,不然不能运行,改成:
public class Test {
public static void main(String[] args) {
System.out.println(1/0);
try {
Class.forName("");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
此时才可以运行,如果这时候有符合检查时异常的错误代码,Class.forName("");处会报错。
或者改成:
public class Test {
public static void main(String[] args) throws ClassNotFoundException {
System.out.println(1/0);
Class.forName("");
}
}
将错误代码传给ClassNotFoundException,才得以编译运行。
异常处理:
Java中对异常的处理有如下两种方式:
1、通过try、catch和finally关键字捕获异常;
2、通过throw或throws关键字抛出异常;
常见异常:
捕获异常:
1、捕获异常语法结构:
try{
//可能抛出异常的语句块
}catch(SomeException1 e){ // SomeException1特指某些异常
//当捕获到SomeException1类型的异常时执行的语句块
} catch( SomeException2 e){
//当捕获到SomeException2类型的异常时执行的语句块
}finally{
//无论是否发生异常都会执行的代码
}
例如:
public class Test {
public static void main(String[] args){
try {
System.out.println(6);
System.out.println(1/0);
System.out.println(8); //因为上一行代码出现异常,所以这行没有执行,输出没有8。
} catch (Exception e) {
e.printStackTrace();
}finally {
System.out.println(10); //无论是否发生异常该代码都会执行,一般用于释放资源(此处只是举例子)。
}
}
}
输出:
java.lang.ArithmeticException: / by zero
at test.Test.main(Test.java:7)
6
10
注意:
① try…catch…finally异常处理结构中,try语句块是必须的, catch和finally语句块至少出现一个,即try不能单独使用。
② 如果try语句块包含的是检查时异常,则在没有通过throws抛出该异常类的情况下,try必须和catch一起使用,当该行代码去掉或注销掉时,catch相应的异常语句块必须去掉,如下代码:
public class Test {
public static void main(String[] args){
try {
Class.forName("java.lang.Object"); //由于该行代码抛出检查时异常,所以该行代码去掉或注销掉时,catch相应的异常语句块必须去掉。
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
2、多重catch:try语句块中的代码可能会引发多种类型的异常,当引发异常时,会按顺序查看每个 catch 语句,并执行第一个与异常类型匹配的catch语句,其后 catch 语句被忽略。所以,在捕获异常的时候,应按照“从小到大”的顺序捕获异常,即先子类后父类。如果先父类后子类,会报错。
Log4j的作用和用法:
1、为什么要用Log4j:因为将异常穿到控制台的所占内存是有限的,当异常很多的时候,前面的异常会消失,这样我们就丢失了异常的数据,所以Log4j的作用就出来了。
2、Log4j的作用:Log4j是Apache的一个开源项目,通过使用Log4j,可以控制日志信息格式及其输送目的地(控制台、文件、数据库等),方便后期查找系统运行期间出现的问题,进而便于维护系统。这样我们就可以把异常传到一个文档里面,而我们硬盘的内存是很大的,相对于传到控制台的内存可以看成是无限大。
3、怎么配置Log4j:
① 第一步:导入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 // 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());//简单日报信息
}
}
}
④ 经过上述三步,最终Java工程结构如下:
运行Test类方法,打开D盘根目录中log.log文件可以看到相应的日志信息,此时的日志信息都会显示出来,Log4j常用日志级别从高到低依次为:ERROR、WARN、INFO和DEBUG,由于上例所设置的Log4j日志级别为DEBUG,所以ERROR、WARN和INFO 级别的日志信息也会显示出来。
例如我使用的形式如下:
import org.apache.log4j.Logger;
public class HelloWorld {
static Logger logger = Logger.getLogger(HelloWorld.class);
public static void main(String[] args) {
while (true) {
try {
System.out.println(1 / 0);
} catch (ArithmeticException e) {
logger.debug(e.getMessage(), e);
}
}
}
}
执行一段时间后停止,在D:有文件:
直接用软件打开可以看到错误代码信息。
间歇性产生新日志文件:
上例配置文件是将所有的日志信息都收集到了一个文件中,那么随着时间的推移,该文件会越来越大,内容也会越来越多,这不利于后期对日志文件进行分析,为了解决该问题可以这样配置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中不用处理的文字要放到单引号(’)中,如上面的(.)。
自定义properties的文件:
Log4j默认使用src根目录中名为log4j.properties的文件,实际开发中有可能需要使用特定目录中的特定名字的properties文件,如下工程:
其中,config包内log.properties文件保存了Log4j相关配置信息,那么此时如何使用该文件呢,如下代码:
import java.net.URL;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
public class Test {
static {
// 获取src目录config包中log.properties文件对应的URL对象
URL url = Test.class.getResource("/config/log.properties");
PropertyConfigurator.configure(url);
}
static Logger logger = Logger.getLogger(Test.class);
public static void main(String[] args) {
try {
System.out.println(1 / 0);
} catch (Exception e) {
logger.debug(e.getMessage(), e);
}
}
}
开发中不止一个Java类需要将某些日志信息写入指定位置,此时每个类中都会重复性地写入如下代码:
static {
// 获取src目录config包中log.properties文件对应的URL对象
URL url = Test.class.getResource("/config/log.properties");
PropertyConfigurator.configure(url);
}
为了尽可能减少重用代码,可以将该部分代码写入到一个Java类中(如BaseLog4j.java),需要输出日志的类只需继承该类即可,如下代码: