Java基础知识扫盲(三)——异常、断言、日志

异常

  • 所有异常都是由Throwable继承而来,分为两类:Error和Exception(RuntimeException程序错误和IOException其他异常)
  • 派生于 Error 类或 RuntimeException 类的所有异常称为非受查( unchecked ) 异常,其他的异常称为受查( checked) 异常
  • 编译器将核查是否为所有的受査异常提供了异常处理器

一个方法必须声明所有可能抛出的受查异常, 而非受查异常要么不可控制(Error),要么就应该避免发生 ( RuntimeException )。

Java 的内部错误, 即从 Error 继承的错误。任何程序代码都具有抛出那些异常的潜能, 而我们对其没有任何控制能力。

如果在子类中覆盖了超类的一个方法, 子类方法中声明的受查异常不能比超类方法中声明的异常更通用 (也就是说, 子类方法中可以抛出更特定的异常, 或者根本不抛出任何异常)。特别需要说明的是, 如果超类方法没有抛出任何受查异常, 子类也不能抛出任何受查异常。

try {...} catch(...){...}块中,如果在 try语句块中的任何代码抛出了一个在 catch 子句中说明的异常类,那么:

  • 程序将跳过 try 语句块的其余代码
  • 程序将执行 catch 子句中的处理器代码

如果想传递一个异常, 就必须在方法的首部添加一个 throws 说明符, 以便告知调用者这个方法可能会抛出异常。

如果编写一个覆盖超类的方法,而这个方法又没有抛出异常,那么这个方法就必须捕获方法代码中出现的每一个受查异常。

可以声明多个catch块儿。
可以合并多个异常类型:当捕获的异常类型彼此之间不存在子类关系时,才可以使用这个特性。

try{
  code that might throw exceptions
}catch (FileNotFoundException | UnknownHostException e){
  emergency action for missing files and unknown hosts
}

捕获多个异常时,异常变量隐含为 final 变量。不能为其赋不同的值。

异常链
在 catch 子句中可以抛出一个异常, 这样做的目的是改变异常的类型。

捕获异常并将它再次抛出的基本方法:

try{
  access the database
}catch (SQLException e){
  throw new ServletException("database error: " + e.getMessageO) ;
}

有一种更好的处理方法,将原始异常设置为新异常的“ 原因”:

try{
  access the database
}catch (SQLException e){
  Throwable se = new ServletException ("database error");
  se.initCause(e);
  throw se;
}

使用下面这条语句重新得到原始异常:

Throwable e = se.getCause();

可以让用户抛出子系统中的高级异常,而不会丢失原始异常的细节。

对于这种代码模式:

open a resource
try{
work with the resource
}finally{
close the
}

假设资源属于一个实现了 AutoCloseable 接口的类,AutoCloseable 接口有一个方法:void close() throws Exception

还有一个 Closeable 接口。 这是AutoCloseable 的子接口, 也包含一个 close方法。 不过, 这个方法声明为抛出一个 IOException。

带资源的 try 语句(try-with-resources) 的最简形式:try块退出时,会自动调用 res.close()

try (Resource res = . . .){
  work with res
}

要读取一个文件中的所有单词:

try (Scanner in = new Scanner(new FileInputStream("/usr/share/dict/words")), "UTF-8"){
  while (in.hasNext())
  System.out.println(in.next()) ;
}

这个块正常退出时, 或者存在一个异常时, 都会调用 in.close() 方法, 就好像使用了finally块一样。

也可以指定多个资源:

try (Scanner in = new Scanner(new FileInputStream("/usr/share/dict/words"). "UTF-8");
  PrintWriter out = new PrintWriter("out.txt")) {
  while (in.hasNextO)
  out.println(in.next().toUpperCase());
}

不论这个块如何退出, in 和 out 都会关闭。

如果 try 块抛出一个异常, 而且 close 方法也抛出一个异常:原来的异常会重新抛出,而 close方法抛出的异常会被抑制。这些异常将自动捕获,可以调用 getSuppressed 方法,它会得到从 close 方法抛出并被抑制的异常列表。

堆栈轨迹(stack trace)
是一个方法调用过程的列表,包含了程序执行过程中方法调用的特定位置

  public static int factorial(int n) {
    System.out.println("factorial(" + n + "):");
    Throwable t = new Throwable();
    StackTraceElement[] frames = t.getStackTrace();
    for (StackTraceElement f : frames)
      System.out.println(f);
    int r;
    if (n <= 1)
      r = 1;
    else
      r = n * factorial(n - 1);
    System.out.println("return " + r);
    return r;
  }

  public static void main(String[] args) {
    Scanner in = new Scanner(System.in);
    System.out.print("Enter n: ");
    int n = in.nextInt();
    factorial(n);
  }

结果:

Enter n: 3
factorial(3):
throwable.ThrowableTest.factorial(ThrowableTest.java:32)
throwable.ThrowableTest.main(ThrowableTest.java:49)
factorial(2):
throwable.ThrowableTest.factorial(ThrowableTest.java:32)
throwable.ThrowableTest.factorial(ThrowableTest.java:40)
throwable.ThrowableTest.main(ThrowableTest.java:49)
factorial(1):
throwable.ThrowableTest.factorial(ThrowableTest.java:32)
throwable.ThrowableTest.factorial(ThrowableTest.java:40)
throwable.ThrowableTest.factorial(ThrowableTest.java:40)
throwable.ThrowableTest.main(ThrowableTest.java:49)
return 1
return 2
return 6

上述即为递归阶乘函数的堆栈情况。

断言

Assert在默认情况下, 断言被禁用。可以在运行程序时用 -enableassertions 或 -ea 选项启用。

需要注意的是, 在启用或禁用断言时不必重新编译程序。启用或禁用断言是类加载器( class loader) 的功能。当断言被禁用时, 类加载器将跳过断言代码, 因此,不会降低程序运行的速度。

记录日志

生成日志:

Logger.getGlobal().info("");

获取日志记录器:

private final Logger logger = LoggerFactory.getLogger(this.getClass()); // LoggerFactory位于 org.slf4j中

有7个日志级别:

  • SEVERE
  • WARNING
  • INFO
  • CONFIG
  • FINE
  • FINER
  • FINEST

默认情况下,只记录前三个级别。

设置其他级别:

logger.setLevel(Level.FINE);

关闭所有级别的日志:

Logger.getGlobal().setLevel(Level.OFF); //在main里面调用可以

开启所有级别的日志:

Logger.getGlobal().setLevel(Level.ALL); //在main里面调用可以

记录方法:

logger.warning(message):
logger.fine(message):

或使用log方法:

logger.log(Level.FINE, message);

如果将记录级别设计为 INFO 或者更低, 则需要修改日志处理器的配置。 默认的日志处理器不会处理低于 INFO 级别的信息。

默认的日志记录将显示包含日志调用的类名和方法名, 如同堆栈所显示的那样。 但是,如果虚拟机对执行过程进行了优化,就得不到准确的调用信息。此时,可以调用 logp 方法获
得调用类和方法的确切位置, 这个方法的签名为:

void logp(Level l, String className, String methodName, String message)

以及跟踪执行流的方法:

void entering(String dassName, String methodName)
void entering(String className, String methodName , Object param)
void entering(String className, String methodName , Object[] params)
void exiting(String className, String methodName)
void exiting(String className, String methodName, Object result)

例如:

int read(String file, String pattern){
    logger.entering("com.mycompany.mylib.Reader", "read",
    new Object[] { file, pattern });
    logger.exiting("com.mycompany.mylib. Reader", "read" , count):
    return count;
}

生成 FINER 级别和以字符串 ENTRY 和 RETURN 开始的日志记录。

修改日志管理器配置

############################################################
#   Default Logging Configuration File
#
# You can use a different file by specifying a filename
# with the java.util.logging.config.file system property.  
# For example java -Djava.util.logging.config.file=myfile
############################################################

############################################################
#   Global properties
############################################################

# "handlers" specifies a comma separated list of log Handler 
# classes.  These handlers will be installed during VM startup.
# Note that these classes must be on the system classpath.
# By default we only configure a ConsoleHandler, which will only
# show messages at the INFO and above levels.
handlers= java.util.logging.ConsoleHandler

# To also add the FileHandler, use the following line instead.
#handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler

# Default global logging level.
# This specifies which kinds of events are logged across
# all loggers.  For any given facility this global level
# can be overriden by a facility specific level
# Note that the ConsoleHandler also has a separate level
# setting to limit messages printed to the console.
.level= INFO

############################################################
# Handler specific properties.
# Describes specific configuration info for Handlers.
############################################################

# default file output is in user's home directory.
java.util.logging.FileHandler.pattern = %h/java%u.log
java.util.logging.FileHandler.limit = 50000
java.util.logging.FileHandler.count = 1
java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter

# Limit the message that are printed on the console to INFO and above.
java.util.logging.ConsoleHandler.level = INFO
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter

# Example to customize the SimpleFormatter output format 
# to print one-line log message like this:
#     <level>: <log message> [<date/time>]
#
# java.util.logging.SimpleFormatter.format=%4$s: %5$s [%1$tc]%n

############################################################
# Facility specific properties.
# Provides extra control for each logger.
############################################################

# For example, set the com.xyz.foo logger to only log SEVERE
# messages:
com.xyz.foo.level = SEVERE
  • 在默认情况下, 配置文件存在于:jre/lib/logging.properties
  • 使用其它配置文件执行下列命令并启动应用程序:java -Djava.util.logging.config.file=configFile MainClass
  • 日志管理器在 VM 启动过程中初始化, 这在 main 执行之前完成。
    如果在 main中调用 System.setProperty("java.util_logging.config_file",file), 也会调用LogManager.readConfiguration() 来重新初始化曰志管理器
  • 默认日志记录级别 .level=INFO
  • 指定自己的日志记录级别com.yy.myapp.level=FINE
    即在日志记录器名后面添加后缀 .level
  • 控制台打印消息级别控制:java.util.logging.ConsoleHandler.level

关于日志消息本地化、日志处理器、过滤器、格式化器更详细的参考核心技术(I)7.5.4-7.5.7

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,527评论 5 470
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,314评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,535评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,006评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,961评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,220评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,664评论 3 392
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,351评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,481评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,397评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,443评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,123评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,713评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,801评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,010评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,494评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,075评论 2 341

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,537评论 18 399
  • 引言 在程序运行过程中(注意是运行阶段,程序可以通过编译),如果JVM检测出一个不可能执行的操作,就会出现运行时错...
    Steven1997阅读 2,388评论 1 6
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,566评论 18 139
  • 三重:代码、底层内存、源码 第一阶段:开发常用JavaSE基础、IDE、Maven、Gradle、SVN、Git、...
    guodd369阅读 16,167评论 1 44
  • 宝玉是好惹的,但又不是好惹的,分对什么人,分什么事。 平常,奴才们都不怕他的,丫鬟们也没拿他当主子。 在主子中间,...
    那座野山阅读 371评论 0 0