实验九异常、断言与日志
实验时间2018-10-25
1、实验目的与要求
ava把程序运行时可能遇到的错误分为两类:非致命异常:通过某种修正后程序还能继续执行。这类错误叫作异常。如:文件不存在、无效的数组下标、空引用、网络断开、打印机脱机、磁盘满等。 Java中提供了一种独特的处理异常的机制,通过异常来处理程序设计中出现的错误。致命异常:程序遇到了非常严重的不正常状态,不能简单恢复执行,是致命性错误。如:内存耗尽、系统内部错误等。这种错误程序本身无法解决。
Java中的异常类可分为两大类:
a.Error Error类层次结构描述了Java运行时系统的内部错误 和资源耗尽错误。应用程序不应该捕获这类异常,也 不会抛出这种异常。
b.Exception Exception类:重点掌握的异常类。Exception层次结 构又分解为两个分支:一个分支派生于 RuntimeException;另一个分支包含其他异常。
2、实验内容和步骤
实验1:用命令行与IDE两种环境下编辑调试运行源程序ExceptionDemo1、ExceptionDemo2,结合程序运行结果理解程序,掌握未检查异常和已检查异常的区别。
//异常示例1 public class ExceptionDemo1 { public static void main(String args[]) { int a = 0; System.out.println(5 / a); } } |
//异常示例2 import java.io.*; public class ExceptionDemo2 { public static void main(String args[]) { FileInputStream fis=new FileInputStream("text.txt");//JVM自动生成异常对象 int b; while((b=fis.read())!=-1) { System.out.print(b); } fis.close(); } } |
修改到结果为:
示例2修改
代码:
import java.io.*;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
public class ExceptionDemo2 {
public static void main(String args[]) throws IOException
{
try {
File file = new File("text.txt");
FileInputStream fis = new FileInputStream(file);
BufferedReader in = new BufferedReader(new InputStreamReader(fis));
String b;
while((b=in.readLine())!=null)
{
System.out.print(b);
}
fis.close();
}catch (Exception e) {
e.printStackTrace();
}
}
}
ExceptionDemo2
实验2: 导入以下示例程序,测试程序并进行代码注释。
测试程序1:
l 在elipse IDE中编辑、编译、调试运行教材281页7-1,结合程序运行结果理解程序;
l 在程序中相关代码处添加新知识的注释;
l 掌握Throwable类的堆栈跟踪方法;
package 实验程序;
import java.util.*;
/**
* 一个程序,显示递归方法调用的追踪功能.
*/
public class StackTraceTest
{
/**
* 计算数字的阶乘
* @param n 是一个非负整数
* @返回 n! = 1 * 2 * . . . * n
*/
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);
}
}
测试程序2:
l Java语言的异常处理有积极处理方法和消极处理两种方式;
l 下列两个简答程序范例给出了两种异常处理的代码格式。在elipse IDE中编辑、调试运行源程序ExceptionalTest.java,将程序中的text文件更换为身份证号.txt,要求将文件内容读入内容,并在控制台显示;
l 掌握两种异常处理技术的特点。
//积极处理方式 import java.io.*; class ExceptionTest { public static void main (string args[]) { try{ FileInputStream fis=new FileInputStream("text.txt"); } catch(FileNotFoundExcption e) { …… } …… } } |
//消极处理方式 import java.io.*; class ExceptionTest { public static void main (string args[]) throws FileNotFoundExcption { FileInputStream fis=new FileInputStream("text.txt"); } } |
积极处理:
package demo;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
public class ExceptionTest {
public static void main(String[] args) throws IOException {
try {
FileInputStream fis = new FileInputStream("身份证号.txt");
BufferedReader in = new BufferedReader(new InputStreamReader(fis));
String m, n = new String();
while ((m = in.readLine()) != null) {
n += m + " ";
}
in.close();
System.out.println(n);
} catch (FileNotFoundException e) {
System.out.println("学生信息文件找不到");
e.printStackTrace();
} catch (IOException e) {
System.out.println("学生信息文件读取错误");
e.printStackTrace();
}
}
}
ExceptionTest
消极处理:
package demo;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
public class ExceptionTest {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("身份证号.txt");
BufferedReader in = new BufferedReader(new InputStreamReader(fis));
String m, n = new String();
while ((m = in.readLine()) != null) {
n += m + " ";
}
in.close();
System.out.println(n);
}
}
ExceptionTest
实验3: 编程练习
练习1:
l 编制一个程序,将身份证号.txt 中的信息读入到内存中;
l 按姓名字典序输出人员信息;
l 查询最大年龄的人员信息;
l 查询最小年龄人员信息;
l 输入你的年龄,查询身份证号.txt中年龄与你最近人的姓名、身份证号、年龄、性别和出生地;
l 查询人员中是否有你的同乡;
l 在以上程序适当位置加入异常捕获代码。
package 人员身份证;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Scanner;
public class Founctions {
private static ArrayList<People> peoplelist;
public static void main(String[] args) {
peoplelist = new ArrayList<>();
Scanner scanner = new Scanner(System.in);
File file = new File("/Users/eleanorliu/Desktop/学习/java文件/实验六/身份证号.txt");
try {
FileInputStream fis = new FileInputStream(file);
BufferedReader in = new BufferedReader(new InputStreamReader(fis));
String temp = null;
while ((temp = in.readLine()) != null) {
Scanner linescanner = new Scanner(temp);
linescanner.useDelimiter(" ");
String name = linescanner.next();
String number = linescanner.next();
String sex = linescanner.next();
String age = linescanner.next();
String province =linescanner.nextLine();
People people = new People();
people.setName(name);
people.setnumber(number);
people.setsex(sex);
int a = Integer.parseInt(age);
people.setage(a);
people.setprovince(province);
peoplelist.add(people);
}
} catch (FileNotFoundException e) {
System.out.println("找不到学生信息文件");
e.printStackTrace();
} catch (IOException e) {
System.out.println("学生信息文件读取错误");
e.printStackTrace();
}
boolean isTrue = true;
while (isTrue) {
System.out.println("选择你的操作,输入正确格式的选项");
System.out.println("1.字典排序");
System.out.println("2.输出年龄最大和年龄最小的人");
System.out.println("3.寻找老乡");
System.out.println("4.寻找年龄相近的人");
System.out.println("5.退出");
String m = scanner.next();
switch (m) {
case "1":
Collections.sort(peoplelist);
System.out.println(peoplelist.toString());
break;
case "2":
int max=0,min=100;
int j,k1 = 0,k2=0;
for(int i=1;i<peoplelist.size();i++)
{
j=peoplelist.get(i).getage();
if(j>max)
{
max=j;
k1=i;
}
if(j<min)
{
min=j;
k2=i;
}
}
System.out.println("年龄最大:"+peoplelist.get(k1));
System.out.println("年龄最小:"+peoplelist.get(k2));
break;
case "3":
System.out.println("老家?");
String find = scanner.next();
String place=find.substring(0,3);
for (int i = 0; i <peoplelist.size(); i++)
{
if(peoplelist.get(i).getprovince().substring(1,4).equals(place))
System.out.println("老乡"+peoplelist.get(i));
}
break;
case "4":
System.out.println("年龄:");
int yourage = scanner.nextInt();
int near=agenear(yourage);
int value=yourage-peoplelist.get(near).getage();
System.out.println(""+peoplelist.get(near));
break;
case "5":
isTrue = false;
System.out.println("退出程序!");
break;
default:
System.out.println("输入有误");
}
}
}
public static int agenear(int age) {
int j=0,min=53,value=0,k=0;
for (int i = 0; i < peoplelist.size(); i++)
{
value=peoplelist.get(i).getage()-age;
if(value<0) value=-value;
if (value<min)
{
min=value;
k=i;
}
}
return k;
}
}
注:以下实验课后完成
练习2:
l 编写一个计算器类,可以完成加、减、乘、除的操作;
l 利用计算机类,设计一个小学生100以内数的四则运算练习程序,由计算机随机产生10道加减乘除练习题,学生输入答案,由程序检查答案是否正确,每道题正确计10分,错误不计分,10道题测试结束后给出测试总分;
l 将程序中测试练习题及学生答题结果输出到文件,文件名为test.txt;
l 在以上程序适当位置加入异常捕获代码。
package 人员身份证;
import java.util.*;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
public class test{
public static void main (String[] args)
{
Scanner in=new Scanner(System.in);
PrintWriter output = null;
try {
output = new PrintWriter("ss.txt");
} catch (Exception e) {
//e.printStackTrace();
}
int score;
int a=(int)Math.round(Math.random()*100);
int b=(int)Math.round(Math.random()*100);
int Input = in.nextInt();
int i=(int)Math.round(Math.random()*10);
while(i<5)
{
for (int cnt=0;cnt<10;cnt++)
{
switch(i) {
case'1':
plus(a,b);
break;
case'2':
minus(a,b);
break;
case'3':
multiple(a,b);
break;
case'4':
division(a,b);
break;
}
}
break;
}
}
public static void plus(int a,int b)
{
Scanner in=new Scanner(System.in);
int out=a+b;
boolean correction;
int Input=in.nextInt();
System.out.println("请输入答案"+(a)+(b));
if(Input==a+b) {
correction=true;
System.out.println("答案正确");
}
else
{
System.out.println("答案错误");
}
}
public static void minus(int a,int b)
{
Scanner in=new Scanner(System.in);
boolean correction;
int Input=in.nextInt();
System.out.println("请输入答案"+a+"-"+b);
if(Input==a-b) {
correction=true;
System.out.println("答案正确");
}
else
{
System.out.println("答案错误");
}
}
public static void multiple(int a,int b)
{
Scanner in=new Scanner(System.in);
boolean correction;
int Input=in.nextInt();
System.out.println("请输入答案"+a+"*"+b);
if(Input==a*b) {
correction=true;
System.out.println("答案正确");
}
else
{
System.out.println("答案错误");
}
}
public static void division(int a,int b)
{
Scanner in=new Scanner(System.in);
boolean correction;
int Input=in.nextInt();
System.out.println("请输入答案"+a+"/"+b);
if(Input==a/b) {
correction=true;
System.out.println("答案正确");
}
else
{
System.out.println("答案错误");
}
}
}
l
实验4:断言、日志、程序调试技巧验证实验。
实验程序1:
//断言程序示例 public class AssertDemo { public static void main(String[] args) { test1(-5); test2(-3); }
private static void test1(int a){ assert a > 0; System.out.println(a); } private static void test2(int a){ assert a > 0 : "something goes wrong here, a cannot be less than 0"; System.out.println(a); } } |
l 在elipse下调试程序AssertDemo,结合程序运行结果理解程序;
注释语句test1(-5);后重新运行程序,结合程序运行结果理解程序;
l 掌握断言的使用特点及用法。
实验程序2:
l 用JDK命令调试运行教材298页-300页程序7-2,结合程序运行结果理解程序;
l 并掌握Java日志系统的用途及用法。
package 实验程序;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.logging.*;
import javax.swing.*;
/**
* 记录各种事件对图像查看器的修改.
* @version 1.03 2015-08-20
* @author Cay Horstmann
*/
public class LoggingImageViewer
{
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("com.horstmann.corejava").setLevel(Level.ALL);
final int LOG_ROTATION_COUNT = 10;
Handler handler = new FileHandler("%h/LoggingImageViewer.log", 0, LOG_ROTATION_COUNT);
Logger.getLogger("com.horstmann.corejava").addHandler(handler);
}
catch (IOException e)
{
Logger.getLogger("com.horstmann.corejava").log(Level.SEVERE,
"Can't create log file handler", e);
}
}
EventQueue.invokeLater(() ->
{
Handler windowHandler = new WindowHandler();
windowHandler.setLevel(Level.ALL);
Logger.getLogger("com.horstmann.corejava").addHandler(windowHandler);
JFrame frame = new ImageViewerFrame();
frame.setTitle("LoggingImageViewer");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Logger.getLogger("com.horstmann.corejava").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("com.horstmann.corejava");
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 openItem = new JMenuItem("Open");
menu.add(openItem);
openItem.addActionListener(new FileOpenListener());
JMenuItem exitItem = new JMenuItem("Exit");
menu.add(exitItem);
exitItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
logger.fine("Exiting.");
System.exit(0);
}
});
//使用标签显示图像
label = new JLabel();
add(label);
logger.exiting("ImageViewerFrame", "<init>");
}
private class FileOpenListener implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
logger.entering("ImageViewerFrame.FileOpenListener", "actionPerformed", event);
// 设置文件选择器
JFileChooser chooser = new JFileChooser();
chooser.setCurrentDirectory(new File("."));
// 接受所有以gif结尾的文件
chooser.setFileFilter(new javax.swing.filechooser.FileFilter()
{
public boolean accept(File f)
{
return f.getName().toLowerCase().endsWith(".gif") || f.isDirectory();
}
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);
label.setIcon(new ImageIcon(name));
}
else logger.fine("File open dialog canceled.");
logger.exiting("ImageViewerFrame.FileOpenListener", "actionPerformed");
}
}
}
/**
* A handler for displaying log records in a window.
*/
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()
{
public void write(int b)
{
} // 不调用
public void write(byte[] b, int off, int len)
{
output.append(new String(b, off, len));
}
});
}
public void publish(LogRecord record)
{
if (!frame.isVisible()) return;
super.publish(record);
flush();
}
}
实验程序3:
l 用JDK命令调试运行教材298页-300页程序7-2,结合程序运行结果理解程序;
l 按课件66-77内容练习并掌握Elipse的常用调试技术。
常用的调试技术为:条件断点 ,变量断点,方法断点,异常断点,重新调试,单步执行程序 ,检查变量,改变变量值
本周学习总结:
异常
异常处理的任务就是将控制权从错误产生的地方转移给能够处理这种情况的错误处理器
在java中,如果某个方法不能够采用正常的途径完成它的任务时,方法不会返回任何值,而是抛出一个封装了错误信息的对象,同时方法会立刻退出,调用这个方法的代码也将无法继续执行,这时候异常处理机制开始搜索能够处理这种异常状况的异常处理器
所有的异常都是由Throwable继承而来,下一层分解为Error和Exception,Exception又分解为IOException和RuntimeException
Error:描述了java运行时系统的内部错误和资源耗尽错误,这类错误很少出现,应用程序也不应该抛出这类对象
RuntimeException:由于程序本身业务逻辑错误导致的异常,此类异常多半是由于程序本身设计的问题导致的,比如错误的类型转换等
IOException:由于类似I/O错误的问题导致的异常
派生自Error类或者RuntimeException类的所有异常称为非受查异常(unchecked),将所有其他异常称为受查异常(checked)。对于非受查异常要么是不可控制(Error)要么就应该避免发生(RuntimeException);对于受查异常,是可以提前知道可能会出现的错误的(比如文件不存在的情况)。所以一个方法必须声明所有可能抛出的受查异常,而尽可能保证代码的正确性来避免非受查异常的出现,而非受查异常比如数组越界这种程序中存在的问题应该专注于核查程序本身而不是通过抛出异常来解决。
子类中覆盖超类的方法,子类方法中声明的受查异常不能比超类方法中声明的异常更通用
如果调用了一个抛出受查异常的方法,就必须对它进行处理,或者继续传递。通常应该捕获知道如何处理的异常而将不知道如何处理的异常进行传递。在方法的首部添加throws说明符以告知调用者这个方法可能会抛出异常。除非覆写一个超类中并没有抛出异常的方法时,要捕获方法代码中的每一个受查异常
当代码抛出一个异常时,就会终止方法中剩余代码的处理,并退出这个方法的执行,这样一些没有来得及回收的资源就没有成功回收,所以可以使用finally字句中进行资源回收,确保无论try语句是否抛出异常都可以及时做到资源回收。
可以使用带资源的try语句解决finally中也可能出现受查异常的情况
断言
断言是一种测试和调试阶段所使用的工具,断言机制允许在测试期间向代码中插入一些检查语句,当代码发布的时候这些插入的检测语句将会被自动移走
断言的语法规则如下,这两种方式都会对条件进行检测,如果为false就抛出一个AssertionError异常,在第二种形式中,表达式就作为一条字符串传入AssertionError构造器,转换成消息字符串
assert 条件;
assert 条件:表达式; —— 这里表达式就可以理解成字符串
运行程序时可以使用-enableassertions或-ea参数启用断言
日志
全局日志记录(global logger)
Logger.getGlobal().info("test");
Logger.getGlobal().serLevel(Level.OFF);
可以使用getLogger方法创建或获取记录器,未被任何变量引用的日志记录器可能会被垃圾回收,所以可以使用静态变量存储日志记录器的一个引用
private static final Logger myLogger = Logger.getLogger("com.mycompany.myapp");
日志记录器有7个级别
SERVER
WARNING
INFO
CONFIG
FINE
FINER
FINEST
可以使用Level.ALL开启所有级别的记录,使用Level.OFF关闭所有级别的记录