一.概述
上一篇讲了Java的异常机制,这个机制会让程序变得更加有健壮性。Java异常处理
这一篇讲Java的断言和用日志框架来查看程序的问题。最后讲一下调试的小技巧。
二.断言
1.基本概念
通俗来讲就是自己断定某个属性是否是我们想要的那个答案。比如希望一个方法调用的参数是一个非负数,可以使用抛异常的方式告诉调用这个参数不能为负数。因为异常存在多了,会使系统的效率降低,所以可以使用断言来它不可能是一个负数。当代码发布时,这些断言将会自动被移走。
/**
* 断言的使用
*/
public class AssertDemo {
public static void main(String[] args) {
int x = 1;
// 断言x一定是大于0
assert x > 0;
double y = Math.sqrt(x);
// 断言的另一种写法后面跟一个表达式
// 如果为false 则会抛出一个AssertionError异常。
// 这种形式表达式的值将被传入AssertionError的构造器,并转换成一个消息字符串,但AssertionError并不会存储字符串的值
assert x > 0 : Math.sqrt(x);
}
}
2.启用断言
在默认情况下,断言是被禁用的。可以在运行时输入 java -enableassertions xxx 来启用。输入java -disableassertions xxx关闭。如果系统没有类加载器,需要使用 -enablesystemassrtions 来开启
注意:断言不需要重新编译程序。这个是类加载器的功能。但有些类是又虚拟机加载而不是类加载器。
3.断言完成参数校验
在Java语言中,处理系统错误的3种:
- 抛出一个异常
- 日志
- 使用断言
在什么时候使用断言?
- 断言失败是致命的,不可恢复的错误。
- 断言检查只用于开发和测试阶段。
三.日志
1.记录日志的优点
在以前的实例中,通常都是使用System.out.println()方法来查看对象和变量的值。
- 通过取消日志的级别来打开或关闭所有的日志,不用在删除和添加打印方法来查看日志。
- 可以在控制台显示,也可以存储到指定的文件查看。
- 可以采用不同的方式格式化,例如纯文本或者xml。
- 日志的控制可以交由配置文件来控制。
2.基本日志
3.处理器
默认情况下,日志记录器将记录发送到ConsoleHandler处理器中,并送到System.err的流中,最后将记录发送到父处理器上。
注意:对于一个要被记录的日志,需要高于日志记录器的级别和处理器的级别,别忘了处理器!!
日志记录器是原始记录器的子类,而原始记录器会把所有等于或高于INFO级别的记录发送到控制台中。如果不想两次看到记录,应将useParentHandleers的属性设置为false。
4.过滤器
5.格式化器
import javax.swing.*;
import javax.swing.filechooser.FileFilter;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.util.logging.*;
/**
* 日志demo
* 功能:在页面上生成日志
* 主要关注日志管理的部分其余暂时不用考虑
*/
public class LogViewDemo {
public static void main(String[] args) {
// 判断系统是否修改了日志配置文件
if (System.getProperty("java.util.logging.config.class") == null
&& System.getProperty("java.util.logging.config.file") == null
) {
try {
Logger.getLogger("LogViewDemo").setLevel(Level.ALL);
final int LOG_ROTATION_COUNT = 10;
// 创建一个处理器 循环序列的文件数量为10
Handler handler = new FileHandler("%h/loggingImageViewer.log", 0, LOG_ROTATION_COUNT);
Logger.getLogger("LogViewDemo").addHandler(handler);
} catch (IOException e) {
Logger.getLogger("LogViewDemo").log(Level.SEVERE, "Can't create log file handler", e);
}
}
EventQueue.invokeLater(() -> {
Handler windowHandler = new WindowHandler();
windowHandler.setLevel(Level.ALL);
Logger.getLogger("LogViewDemo").addHandler(windowHandler);
JFrame frame = new ImageViewerFrame();
frame.setTitle("loggingImageViewer");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Logger.getLogger("LogViewDemo").fine("Showing frame");
frame.setVisible(true);
}
);
}
}
class ImageViewerFrame extends JFrame {
private static final int DEFAULT_WIDTH = 300;
private static final int DEFAULT_HEIGHT = 400;
private JLabel label;
/**
* 设置日志记录器的名称
*/
private static Logger logger = Logger.getLogger("LogViewDemo");
public ImageViewerFrame() {
// 跟踪方法进入时候的日志
logger.entering("ImageViewerFrame", "<init>");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
JMenuBar menuBar = new JMenuBar();
setJMenuBar(menuBar);
JMenu menu = new JMenu("File");
menuBar.add(menu);
JMenuItem item = new JMenuItem("Open");
menu.add(item);
item.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
logger.fine("Exiting");
System.exit(0);
}
});
label = new JLabel();
add(label);
logger.exiting("ImageViewerFrame", "<init>");
}
private class FileOpenListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
// 跟踪方法进入时候的日志
logger.entering("ImageViewFrame.FileOpenListener", "actionPerformed", e);
JFileChooser chooser = new JFileChooser();
chooser.setCurrentDirectory(new File("."));
chooser.setFileFilter(new FileFilter() {
@Override
public boolean accept(File f) {
return f.getName().toLowerCase().endsWith(".gif") || f.isDirectory();
}
@Override
public String getDescription() {
return "GIF Images";
}
});
int r = chooser.showOpenDialog(ImageViewerFrame.this);
if (r == JFileChooser.APPROVE_OPTION) {
String name = chooser.getSelectedFile().getPath();
logger.log(Level.FINE, "Reading file {0}", name);
} else {
logger.fine("File open dialog canceled");
// 跟踪退出方法退出时的日志
logger.exiting("ImageViewerFrame.FileOpenListener", "actionPerformed");
}
}
}
}
/**
* 创建日志处理器 在窗口中显示日志记录
*/
class WindowHandler extends StreamHandler {
private JFrame frame;
public WindowHandler() {
frame = new JFrame();
final JTextArea output = new JTextArea();
output.setEditable(false);
frame.setSize(200, 200);
frame.add(new JScrollPane(output));
frame.setFocusableWindowState(false);
frame.setVisible(true);
setOutputStream(new OutputStream() {
@Override
public void write(int b) throws IOException {
}
// 每次写入到缓存区,待缓存区满了之后才会显示
@Override
public void write(byte[] b, int off, int len) {
output.append(new String(b, off, len));
}
});
}
/**
* 获取每次记录之后就刷新缓存区
*
* @param record
*/
@Override
public void publish(LogRecord record) {
if (!frame.isVisible()) {
return;
}
super.publish(record);
flush();
}
}
四.调试技巧
1.在方法的入口处打印出方法的入口参数,方法的结束打印出结果和入口参数日志。
2.利用printStackTrace()方法打印出堆栈轨迹
五.总结
下一篇学习泛型设计。这个方法可以确保类型的正确性。
有些可能我理解的不够深刻,大家如果觉得我说的不够详细可以参考我的推荐书,详细的看一下。欢迎大家评论。第一时间我会回复大家。谢谢!