Java04异常、断言、日志和调试

11 异常、断言、日志和调试

异常处理(exception handing

使用断言来启动检测

Java日志框架

调试技巧

11.1 处理错误

如果一个方法不能够采用正常的途径完成任务,就通过另外一个路径退出方法。

在这种情况下,方法不返回任何值,而是抛出一个封装了错误信息的对象。此外,调用这个方法的代码也将无法继续执行。

异常处理机制开始搜索能够处理这种异常情况的异常处理器(exception handler)。

异常处理的任务就是将控制权从错误产生的地方转移给能够处理这种情况的错误处理器。

 

异常对象是派生于Throwable类的一个实例。

可以创建自己的异常类。

 

异常的分类:

Throwable

Exception

RuntimeException

 

ArrayIndexOutOfBoundsException

NullPointerException

IOException

 

 

Error

 

 

 

Error类层次描述的是Java运行时系统的内部错误和资源耗尽。

应用程序不应抛出此类错误。

除了通告给用户并尽力使程序安全地中止之外,再也无能为力。

此种情况很少出现。

 

派生于Error类或RuntimeException类的所有异常称为未检查(unchecked)异常;

所有其他的异常称为已检查(checked)异常。

编译器将检查是否为所有已检查异常提供了异常处理器。

 

声明已检查异常:

public FileInputStream(String name) throws FileNotFoundException

 

一个方法要么返回其返回值,要么抛出一个异常。

一个方法必须声明所有可能抛出的已检查异常,而未检查异常要么不可控(Error),要么应避免(RuntimeException)。

如果子类中覆盖了超类的一个方法,子类方法中声明的已检查异常不能比超类方法中声明的异常更通用。

 

如何抛出异常:

1、找到一个合适的异常类;

2、创建其类对象;

3、将对象抛出。

如:throw new EOFException();

throw new EOFException(String);

 

一旦方法抛出异常,这个方法就不能返回到调用者。

C++中可以抛出任何类型的值,Java中只能抛出Throwable子类的对象。

 

创建异常类:

定义一个派生于Exception的类,或者派生于Exception子类的类。

包含两个构造器:默认构造器;带有详细描述信息的构造器。

class FileFormatException extends IOException
{public FileFormatException(){}public FileFormatException(String message){super(massage);}
}

11.2 捕获异常

如果某个异常发生的时候没有在任何地方进行捕获,那程序就会终止执行,并在控制台上打印出异常信息,其中包括异常的类型和堆栈信息。

捕获异常:

try
{code that might throw exception
}
catch(FileNotFoundException | UnknownHostException e) //捕获多个异常时,e隐含为final变量。
{emergency action for missing files and unknown hosts
}
catch(IOException e)
{emergency action for all other I/O problems
}

如果try语句块中的任何代码抛出了一个在catch子句中说明的异常类,则跳过try语句块的其余代码,执行catch子句中的处理器代码;

如果try语句块中抛出的异常没有在catch子句中说明,则立即退出这个方法;

如果try语句块没有抛出异常,则跳过catch子句。

 

通常,异常处理的最好选择是什么都不做,而是将异常传递给调用者。

如果调用了一个抛出已检查异常的方法,要么处理,要么传递。

通常,捕获那些知道如何处理的异常,传递那些不知怎样处理的异常。

 

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

如:

try
{access the database
}
catch (SQLException e)
{Throwable se = new ServletException(“database error”);se.initCause(e);throw se;
}
finally
{close resource;
}

Throwable e = se.getCause();

//可以得到原始异常,使用这种包装技术,可以让用户抛出子系统中的高级异常,而不会丢失原始异常的细节。


如果在一个方法中发生了一个已检查异常,而方法不允许抛出它,那么包装技术可以将这个已检查异常包装成一个运行时异常。

 

强烈建议使用try/catchtry/finally子句

finally子句一定会被执行。

 

带资源的try语句

try(Resource res = ...)
{work with res;
}
//try块退出时,自动调用res.close()方法。
//此时,try块抛出的异常被抛出,close方法抛出的异常被抑制。

带资源的try语句自身也可以有catch子句和一个finally子句。这些子句会在关闭资源后执行。

在实际中,一个try语句中加入这么多内容可能不是一个好主意。

 

堆栈跟踪(stack trace):

是一个方法调用过程的列表,包含了程序执行过程中方法调用的特定位置。

 
Throwable t = new Throwable();
StackTraceElement[] frames = t.getStackTrace();
for (StackTraceElement frame : frames)analyze frame;
//多线程堆栈跟踪
Map<Thread, StackTraceElement[]> map = Thread.getAllStackTraces();
for (Thread t : map.keySet())
{StachTraceElement[] frames = map.get(t);analyze frames
}


11.3 使用异常的技巧

1、异常处理不能代替简单的测试;

2、不要过分细化异常;

3、利用异常层次结构;

4、不要压制异常;对于不常发生的异常,catch语句块的内容可以先空着,待日后感觉需要处理时再填上。

5、不要羞于传递异常。

 

11.4 使用断言

断言机制允许在测试期间向代码中插入一些检查语句。

当代码发布时,这些插入的检测语句将会被自动移走。

 

assert 条件;

assert 条件:表达式;

 

这两种形式都会对条件进行检测,如果结果为false,则抛出一个AssertionError异常。

在第二种形式中,表达式将被传入AssertionError的构造器,并转换成一个消息字符串。

表达式的唯一目的就是产生一个消息字符串。

 

assert x >= 0;

assert x >=0 : x;

assert x >=0 “x >= 0”;

 

默认情况下,断言被禁用。

在运行时,用-enableassertions -ea 选项启用它。

java -enableassertions MyApp

在启用或禁用断言时不必重新编译程序。启用或禁用断言时类加载器(class loader)的功能。

当断言被禁用时,类加载器将跳过断言代码,因此不会降低程序运行的速度。

也可选用-disableassertions-da禁用某个特定类或包的断言。

 

Java中,给出了三种处理系统错误的机制:

异常;

日志;

断言。

 

断言是致命的,不可恢复的错误。只用于开发和测试阶段。

断言是一种测试和调试阶段所使用的战术性工具;

日志记录是一种在程序的整个生命都可以使用的策略性工具。


11.5 记录日志

优点:

·可以取消全部日志记录,或仅取消某个级别的日志,而且打开和关闭这个操作也很容易;

·可以很简单的禁止日志记录的输出,因此,这些日志代码留在程序中的开销很小;

·日志记录可以在控制台中显示,也可以在文件中存储;

·可以对日志记录进行过滤,只保留重要日志;

·可以采用不同格式:纯文本或XML

·可使用多个日志记录;

·默认情况下,日志系统的配置由配置文件控制;如果需要的话,应用程序可以替换这个配置。

 

基本日志:

日志系统管理着一个名为Logger.global的默认日志记录器,可通过info方法记录日志信息

Logger.getGlobal().info(“File->Open menu item selected”); //记录日志

Logger.getGlobal().setLevel(Level.OFF); //取消所有日志

 

高级日志:

在一个应用程序中,不要将所有的日志都记录到一个全局日志记录器中,而是自定义日志记录器。

调用getLogger方法可以创建或检索记录器:

private static final Logger myLogger = Logger.getLogger(“com.mycompany.myapp”);

日志记录具有层次结构,上下层之间将共享某些属性,例如对com.mycompany日志记录器设置了日志级别,它的子记录器也会继承这个级别。

SERVER  WARNING  INFO  CONFIG  FINE  FINER  FINEST

默认记录前三个级别。

 

级别设置:

logger.setLevel(Level.FINE); //Level.ALL开启所有级别 Level.OFF关闭所有级别

 

记录方法:

logger.waring(message);

logger.fine(message);

logger.log(level.FINE, message);

 

默认的日志配置记录了INFO或更高级别的所有记录。

应该使用CONFIGFINEFINEST级别来记录那些有助于诊断,但对于程序员又没有太大意义的调试信息。

 

默认的日志记录将显示包含日志调用的类名和方法名,如同堆栈所显示的那样。

但是,如果虚拟机对执行过程进行了优化,就得不到准确的调用信息。

此时可用logp方法获得调用类和方法的确切位置:

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

 

 

跟踪执行流的方法:

void entering(String className, 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级别和以字符串ENTRYRETURN开始的日志记录。

 

日志记录的常见用途是记录那些不可预料的异常。

两种方法:

void throwing(String className, String methodName, Throwable t)

void log(Level l, String message, Throwable t)

典型用法:

if(...)
{IOException exception = new IOException(“...”);logger.throwing(“com.mucompany.mylib.Reader”,”read”,exception);throw exception;
}
//调用throwing可以记录一条FINER级别的记录和一条以THROW开始的信息。try
{...
}
catch(IOException)
{Logger.getLogger(“com.mycompany.myapp”).log(Level.WARNING, “Reading image”, e);
}

修改日志管理器配置文件jre/lib/logging.properties

日志管理器在VM启动过程中被初始化,在main之前执行。

.level=INFO //修改默认的日志记录级别

com.mycompany.myapp.level=FINE 指定自己的日志记录级别

 

本地化:

本地化的应用程序包含资源包(resource bundle)中的本地特定信息。

资源包由各个地区的映射集合组成。如某个资源包可以将redingFile映射成英文的Reading file或者德文的Achtung! Datei wired eingelesen

一个程序可以包含多个资源包,一个用于菜单;其他用于日志消息。

每个资源包都有一个名字,如com.mycompany.logmessages

要想将映射添加到一个资源包中,需要为每个地区创建一个文件。

英文消息映射位于com/mycompany/logmessages_en.properties文件中;

德文消息映射位于com/mycompany/logmessages_de.properties文件中。

可以将这些文件与应用程序的类文件放在一起,以便ResourceBundle类自动对它们进行定位。

这些文件都是纯文本文件,内容如下:

readigFile=Achtung! Datei wird eingelesen

renamingFile=Datei wird umbenannt

 

在请求日志记录器时,可以指定一个资源包:

Logger logger= Logger.getLogger(loggerName, “com.mycompany.logmessages”);

然后,为日志消息指定资源包的关键字,而不是实际的日志消息字符串:

logger.info(“readingFile”);

 

通常需要在本地化的消息中增加一些参数,因此,消息应该包括占位符{0}{1}等。

例如,在日志消息中包含文件名,需要使用下列方式包含占位符:

readingFile=Reading file {0}

renamingFile=Change name file {0} to {1}

 

logger.log(Level.INFO, “readingFile”, fileName);

logger.log(Level.INFO, “renamingFile”, new Object[]{oldName, newName};

 

处理器:

默认情况下,日志记录器将记录发送到ConsoleHandler中,并由它输出到System.err流中。

日志记录器还会将记录发送到父处理中,最终处理器(命名为””)有一个ConsoleHandler

处理器也有一个日志记录级别。

 

安装自己的处理器:

Logger logger = Logger.getLogger(“com.mycompany.myapp”);

logger.setLevel(Level.FINE);

logger.setUseParentHandlers(false);

Handler handler = new ConsoleHandler();

handler.setLevel(Level.FINE);

logger.addHandler(handler);

 

其他处理器:

FileHandler 将记录发送到用户主目录的javan.log文件中,n是文件的唯一编号,默认XML格式。

SocketHandler 将记录发到特定的主机和端口。

 

 

过滤器:

默认情况下,过滤器根据日志记录的级别进行过滤。

每个日志记录器和处理器都可以有一个可选的过滤器来完成附加的过滤。

另外,可通过实现Filter接口的boolean isLoggable(LogRecord record)方法来自定义过滤器。

调用setFilter方法安装过滤器。

一个时刻最多只能有一个过滤器。

 

日志记录说明:日志记录器最好与主应用程序包名相同;



本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/387302.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

全双工与半双工的区别

1、全双工传输 (英文Full&#xff0d;Duplex &#xff09; 是指交换机在发送数据的同时也能够接收数据&#xff0c;两者同步进行&#xff0c;这好像我们平时打电话一样&#xff0c;说话的同时也能够听到对方的声音。目前的交换机都支持全双工。全双工的好处在于迟延小&#xff…

人脸识别经典算法一:特征脸方法(Eigenface)

这篇文章是撸主要介绍人脸识别经典方法的第一篇&#xff0c;后续会有其他方法更新。特征脸方法基本是将人脸识别推向真正可用的第一种方法&#xff0c;了解一下还是很有必要的。特征脸用到的理论基础PCA在另一篇博客里&#xff1a;特征脸(Eigenface)理论基础-PCA(主成分分析法)…

Java05泛型

12 泛型 12.1 为什么使用泛型 泛型程序设计&#xff08;Generic programming&#xff09;&#xff1a;意味着编写的代码可以被很多不同类型的对象所重用。 类型参数&#xff08;type parameters&#xff09; 通配符类型&#xff08;wildcard type&#xff09; 可以将Manage…

bitmap转换为drawable

Bitmap bitmap MediaStore.Images.Media.getBitmap(this, Uri.parse(string)); Drawable drawable new BitmapDrawable(bitmap);// 这样就转换成drawable格式&#xff0c;可以设置背景图片了转载于:https://www.cnblogs.com/Nigeria/p/10471028.html

numpy的下载与安装教程——(解决No module named numpy问题)

NumPy函数库是Python开发环境的一个独立模块&#xff0c;而且大多数Python发行版没有默认安装NumPy数据库&#xff0c;因此在安装Python之后必须单独安装NumPy数据库。 进入Python shell开发环境后输入 [python] view plaincopy from numpy import* 如果出现No module named …

Xception

The First ColumnThe Second Columnpadding 方式&#xff1a;same and valid The First ColumnThe Second ColumnSame 就是 增加一列相同的数 &#xff08;一般是0&#xff09;valid只保留有效的转载于:https://www.cnblogs.com/hugeng007/p/10477430.html

1.苍穹外卖-day01

苍穹外卖-day01 课程内容 软件开发整体介绍 苍穹外卖项目介绍 开发环境搭建 导入接口文档 Swagger 项目整体效果展示&#xff1a; 管理端-外卖商家使用 用户端-点餐用户使用 当我们完成该项目的学习&#xff0c;可以培养以下能力&#xff1a; 1. 软件开发整体介绍 作为一名…

7 用户和用户组

7.1 用户配置文件 7.1.1 用户信息文件 /etc/passwd man 5 passwd 查看配置文件功能 第1字段&#xff1a; 用户名称 第2字段&#xff1a; 密码标志 X代表有密码 如果用户没有密码&#xff0c;则只允许本地登录 第3字段&#xff1a; UID&#xff08;用户ID&#xff09; 0…

C#调用Python模块

编程&#xff1a;C#调用Python模块 当下&#xff0c;C#与Python都是比较热门的计算机编程语言&#xff0c;他们各有优缺点&#xff0c;如果能让他们互相配合工作&#xff0c;那是多么美好的事情&#xff0c;今天我来讲解一下如何利用C#来调用Python。 如果让C#支持调用Python模…

面试简单整理之克隆

61.为什么要使用克隆&#xff1f; 克隆获取对象的副本&#xff0c;直接用复制还是一个引用。 62.如何实现对象克隆&#xff1f; 1.被clone的类实现cloneable接口&#xff0c;重写object类的clone&#xff08;&#xff09;方法 2.如果深克隆则克隆对象的引用对象也要实现接口、重…

8 权限管理

8.1 ACL权限 8.1.1 简介与开启 用于解决身份不够用的问题 ACL(access control list) 访问控制表 ACL是存在于计算机中的一张表&#xff0c;它使操作系统明白每个用户对特定系统对象&#xff0c;例如文件目录或单个文件的存取权限。 这张表对于每个系统用户有拥有一个访问…

windows下GitHub的SSH Key 配置

https://www.jianshu.com/p/9317a927e844转载于:https://www.cnblogs.com/lishidefengchen/p/10481889.html

PyCharm调试错误

JetBrains PyCharm 2017.3.2 这就说明python.exe的环境没有配&#xff0c;点击蓝色的configure Python Interpreter&#xff0c;然后选择对应的路径就可以了

Java06集合

13 集合 实现方法时&#xff0c;不同的数据结构会导致性能有很大差异。 13.1 集合接口 Java集合类库将接口&#xff08;interface&#xff09;与实现&#xff08;implementation&#xff09;分离。 可以使用接口类型存放集合的应用&#xff0c;一旦改变了想法&#xff0c;可…

Tensorflow验证码识别应用

简单的Tensorflow验证码识别应用&#xff0c;供大家参考&#xff0c;具体内容如下 1.Tensorflow的安装方式简单,在此就不赘述了. 2.训练集训练集以及测试及如下(纯手工打造,所以数量不多): 3.实现代码部分(参考了网上的一些实现来完成的) main.py(主要的神经网络代码) ?123456…

9 文件系统管理

9.1 回顾分区和文件系统 分区类型 主分区&#xff1a;总共最多只能分四个 扩展分区&#xff1a;只能有一个&#xff0c;主分区加扩展分区最多有四个&#xff0c;必须再划分成逻辑分区才能使用。 逻辑分区&#xff1a;在扩展分区中划分的 IDE硬盘最多支持59个逻辑分区 SCSI…

Linux 桌面玩家指南:09. X Window 的奥秘

Linux 桌面玩家指南&#xff1a;09. X Window 的奥秘 原文:Linux 桌面玩家指南&#xff1a;09. X Window 的奥秘特别说明&#xff1a;要在我的随笔后写评论的小伙伴们请注意了&#xff0c;我的博客开启了 MathJax 数学公式支持&#xff0c;MathJax 使用$标记数学公式的开始和结…

Storm教程1理论介绍

流式计算的历史: 早在7、8年前诸如UC伯克利、斯坦福等大学就开始了对流式数据处理的研究&#xff0c;但是由于更多的关注于金融行业的业务场景或者互联网流量监控的业务场景&#xff0c;以及当时互联网数据场景的限制&#xff0c;造成了研究多是基于对传统数据库处理的流式化&…

梯度下降原理及Python实现

梯度下降算法是一个很基本的算法&#xff0c;在机器学习和优化中有着非常重要的作用&#xff0c;本文首先介绍了梯度下降的基本概念&#xff0c;然后使用python实现了一个基本的梯度下降算法。梯度下降有很多的变种&#xff0c;本文只介绍最基础的梯度下降&#xff0c;也就是批…

dagger2的初次使用

一、使用前准备 1、打开app的build.gradle文件&#xff1a; 顶部停用apt插件&#xff1a; //添加如下代码&#xff0c;应用apt插件 apply plugin: com.neenbedankt.android-apt dependencies中添加依赖&#xff1a; //Dagger2compile com.google.dagger:dagger:2.4apt com.goog…