1、什么叫异常
下面来看一段代码
public class Test {
public static void main(String[] args) {
System.out.println("111");
System.out.println(1/0);
}
}
我们知道在分式中0不能做分母,但编译时代码并没有报错,运行时出现了这样的提示
这就是异常——Java语言将程序运行过程中所发生的不正常严重错误称为异常
2、异常的后果
如果出现异常的话,会中断正在运行的程序,同级代码不再运行,例如
public class Test {
public static void main(String[] args) {
System.out.println("111");
System.out.println(1/0);
System.out.println("222");
}
}
结果显示:
可以看到“222”这行代码没有被运行,所以说“异常”中断了程序的运行
3、异常的结构
Throwable中常用方法: public String getMessage():获取异常信息;
public void printStackTrace():输出异常堆栈中的异常信息
(可以被其所有子类(Exception、Error及其子类)直接调用)
Exception中构造方法: public Exception():无参构造方法;
public Exception(String message):可以指定详细异常信息的有参构造方法;
(可以Exception及其子类直接调用)
Throwable是最大的一类,是所有异常和错误的父类。Throwable有两个直接子类Exception(异常)和Error(错误)。
Error : 指合理的应用程序在执行过程中发生的严重问题。当程序发生这种严重错误时,通常的做法是通知用户并中止程序的执行。
Exception:指异常。出现异常可以我们可以进行异常处理。
4、异常的分类
异常分为运行时异常(RuntimeException)和非运行时异常即检查时异常(CheckedException)。
运行时异常:可以不显式对异常的处理,javac依然可以编译程序,如下图(1/0),写代码时并没有出错,但运行时会出错
处理异常办法:选中异常那一行代码,然后右击——Surround with——try/catch Block
检查时异常:必须显式对异常的处理,不然javac不会对程序进行编译,如下图
怎么办呢?
将鼠标放到波浪线处,会出现
第一种方式就是添加throws declaration,直接点击就行,目的是将异常交给JVM去处理
第二种方式就是用try/catch,直接点击下面的Surround with try/catch即可
(需要注意的是:异常是代码正确的前提下异常,如果代码本身就错,那就是错误,如Class.fonam(),代码本身就写错了,这不是异常)
5、异常处理方式(try/catch/finally)
先看一段代码
public class Test {
public static void main(String[] args) {
String name=null;
System.out.println(name.length());
}
}
结果会显示空指针异常
所以如果再来一行代码的话,不会执行,因为程序已中断运行
public class Test {
public static void main(String[] args) {
String name=null;
System.out.println(name.length());
System.out.println("0");//不会执行
}
}
try/catch中try是尝试运行,如果出现异常,就会catch,捕捉异常,类似于if...else if...else if...else...
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("QQQQ");
}catch(NullPointerException e) {
System.out.println("NUll");
}catch(ArithmeticException e) {
System.out.println("0");
}catch(Exception e) {//Exception是所有异常的父类
System.out.println("other"+e);
}
}
}
catch(),括号里前面的单词的是异常类型,e是异常对象,如果不清楚异常类型就用Exception,因为它是所有异常的父类,类似于上转型。根据异常类型执行相应的语句,如
因为第一个name就异常了 ,所以name以下代码就中断运行了,而name是空指针异常,所以执行第一个catch里的语句。
再如最后那个异常,如图
注意:catch里的异常类型要按从小到大的顺序写,不能第一个就写Exception,因为Exception是所有异常父类,如果第一个就写Exception,那所有的异常都属于这一类,这以下的catch就没用了。当然第一个catch也不能是RuntimeException,因为它是所有运行时异常的父类,范围太大。
try/catch/finally
一个try可以有多个catch,但try不允许单独使用
try/catch上面已用过,不再赘述,下面看与finally搭配
finally 无论try中是否有异常发生,finlly中的代码总会执行,一般用于释放资源
try可以单独与finally搭配
try也可以同时与catch、finally搭配
6、异常处理方式的弊端及其解决方式:
先看以下代码
可以看到,无法找到1,异常信息太多
弊端是:将异常信息打印到控制台——>存入一段缓存,缓存有大小——>当缓存满了,但是异常信息源源不断,则会将最先进入缓存的异常信息移除(因为堆是先进先出)——>意味着无法全面的发现程序运行期间出现的问题——>为了全面收集系统运行期间出现的所有异常信息,log4j诞生了——>将所有异常信息输出到一个文件中——因为文件在硬盘上——>可以认为无限大
Log4j是Apache的一个开源项目,通过使用Log4j,可以控制日志信息格式及其输送目的地(控制台、文件、数据库等),方便后期查找系统运行期间出现的问题,进而便于维护系统。
步骤:
第一步:导入log4j-1.2.15.jar依赖包;
①在项目中右击——new——folder,创建一个名为 “lib” 的folder,
②将log4j-1.2.15.jar依赖包拷贝到lib中,然后选中log4j-1.2.15.jar依赖包,右击——Build Path——Add...(后面单词没记清),导入成功
第二步:在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());//简单日报信息
}
}
}
经过上述三步,最终Java工程结构如下:
运行Test类方法,打开D盘根目录中log.log文件可以看到相应的日志信息,此时的日志信息都会显示出来,Log4j常用日志级别从高到低依次为:ERROR、WARN、INFO和DEBUG,由于上例所设置的Log4j日志级别为DEBUG,所以ERROR、WARN和INFO 级别的日志信息也会显示出来。
间歇性产生新日志文件
上例配置文件是将所有的日志信息都收集到了一个文件中,那么随着时间的推移,该文件会越来越大,内容也会越来越多,这不利于后期对日志文件进行分析,为了解决该问题可以这样配置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中不用处理的文字要放到单引号(')中,如上面的(.)。