刘志梅 201771010115 《面向对象程序设计(java)》 第九周学习总结
实验九 异常、断言与日志
实验时间 2018-10-25
1、实验目的与要求
(1) 程序中会出现的错误:用户输入错误、设备错误、代码错误、物理限制。
在Java程序设计语言中,异常对象都是派生于Throwable类的一个实例;如果java中内置的异常类不能满足需求,客户可以创建自己的异常类(注意的一点是:所有的异常都是由Throwable继承而来,但在下一层次立即分解为两个分支:Error和Exception);Error类层次结构描述了Java运行时系统的内部错误和资源耗尽错误;Exception层次结构又分解为两个分支:一个分支派生于RuntimeException而另一个分支包含其他异常类。
Java语言规范将派生于Error类和RuntimeException类的所有异常称为非受查异常,所有其它的异常称为受查异常。
在以下四种情况下应该抛出异常:调用一个抛出受查异常的方法、程序运行过程中发现错误,并且利用throw语句抛出一个受查异常、程序出现错误、Java虚拟机和运行时库出现的内部错误。
在这种情况下:1.找到一个合适的异常类2.创建这个类的一个异常对象3.将对象抛出,一旦方法抛出了异常,这个方法就不能返回到调用者。
(2) 如果某个异常发生的时候没有在任何地方进行捕获,那程序就会终止执行,并在控制台上打印出异常信息,其中包括异常的类型和堆栈的内容。
如果在try语句块中的任何代码抛出了一个在catch子句中说明的异常类,那么1.程序将跳过try语句块的其他代码2.程序将执行catch子句中的处理器代码(如果在try语句块中的代码没有抛出任何异常,那么程序将跳过catch子句;如果方法中的任何代码抛出了一个在catch子句中没有声明的异常类,那么这个方法就会立刻退出)。
如果想传递一个异常,就必须在方法的首部添加一个throw是说明符,以便告知调用者这个方法可能会抛出异常。
如果编写一个覆盖超类的方法,而这个方法又没有抛出异常,那么这个方法就必须捕获方法代码中出现的每一个受查异常;不允许在子类的throws说明符中出现超过超类方法所列出的异常类范围。
在一个try语句块中可以捕获多个异常类型,并对不同类型的异常做出不同的处理(捕获多个异常时,异常变量隐含为final变量)。
在catch子句中可以抛出一个异常,这样做的目的是改变异常的类型。
(3) finally子句:当代码抛出一个异常时,就会终止方法中剩余代码的处理,并退出这个方法的执行;不管是否有异常被捕获,finally子句中的代码都会被执行。
(4) 堆栈轨迹:是一个方法调用过程的列表,它包含了程序执行过程中方法调用的特定位置。
静态的Thread.getAllStackTrace方法,它可以产生所有线程的堆栈轨迹。
(5)使用异常机制的几个技巧:1.异常处理不能代替简单的测试2.不要过分地细化异常3.利用异常层次结构4.不要压制异常5.在检测错误时,“苛刻”要比放任更好6.不要羞于传递异常。
(6)断言:假设确信某个属性符合要求,并且代码的执行依赖于这个属性。
断言机制允许在测试期间向代码插入一些检查语句,当代码发布时,这些插入的检查语句将会被自动地移走。
在默认情况下,断言被禁用;注意,在启用或禁用断言时不必重新编译程序;有些类不是由类加载器加载,而是直接由虚拟机加载(可以使用这些开关有选择的启用或禁用那些类中的断言)。
在Java语言中给出了3种处理系统错误的机制:抛出一个异常、日志、使用断言。
什么时候使用断言呢:断言失败是致命的、不可恢复的错误;断言检查只用于开发和测阶段。
有时候会抛出一个断言错误,有时候会产生一个null 指针异常,这完全取决于类加载器的配置。
(5)API的优点:可以很容易的取消全部日志记录,或者仅仅取消某个级别的日志,而且打开和关闭这个操作也很容易;可以很简单的禁止日志记录的输出,因此,将这些日志代码留在程序中的开销很小;日志记录可以被定向到不同的处理器,用于在控制台中显示,用于存储在文件中等;日志记录器和处理器都可以对记录进行过滤,过滤器可以根据过滤实现器制定的标准丢弃那些无用的记录项;日志记录可以采用不同的方法格式化;应用程序可以使用多个日志记录器,它们使用类似包名的这种具有层次结构的名字;在默认情况下,日志系统的配置由配置文件控制(如果需要的话,应用程序可以替换这个配置)。
与包名类似,日志记录器名也具有层次结构;通常,有以下7个日志记录器级别:SEVERE、WARNING、INFO、CONFIG、FINE、FINER、FINEST。
默认的日志记录将显示包含日志调用的类名和方法名,如同堆栈所显示的那样;记录日志的常见用途是记录那些不可预料的异常;可以通过编辑配置文件来修改日志系统的各种属性。
在运行的程序中,使用jconsole程序也可以改变日志记录的级别。
在默认情况下,日志记录器将记录发送到ConsoleHandler中;与日志记录器一样,处理器也有日志记录级别;在默认情况下,日志记录器将记录发送到自己的处理器和父处理器;要想将日志记录发送到其它地方,就要添加其他的处理器。
在默认情况下,过滤器根据日志记录的级别进行过滤;每个日志记录器和处理器都可以有一个可选的过滤器来完成附加的过滤;要想将一个过滤器安装到一个日志记录器或处理器中,只需要调用setfilter方法就可以了(注意:同一时刻最多只能有一个过滤器)。
ConsoleHandle类和FileHandle类可以生成文本和XML格式的日志记录。
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: 导入以下示例程序,测试程序并进行代码注释。
测试程序1:
l 在elipse IDE中编辑、编译、调试运行教材281页7-1,结合程序运行结果理解程序;
l 在程序中相关代码处添加新知识的注释;
l 掌握Throwable类的堆栈跟踪方法;
import java.util.*; /** * A program that displays a trace feature of a recursive method call. * @version 1.01 2004-05-10 * @author Cay Horstmann */ public class StackTraceTest { /** * Computes the factorial of a number * @param n a non-negative integer * @return n! = 1 * 2 * . . . * n */ public static int factorial(int n) { System.out.println("factorial(" + n + "):"); Throwable t = new Throwable();//实例化异常类Throwable StackTraceElement[] frames = t.getStackTrace(); for (StackTraceElement f : frames) System.out.println(f); int r; if (n <= 1) r = 1;//如果(n<=1)r=1; else r = n * factorial(n - 1);//否则r=n*!(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: ");//打印(“输入n”) int n = in.nextInt(); factorial(n);//阶乘(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 + "\n "; } in.close(); System.out.println(n); } catch (FileNotFoundException e) { System.out.println("学生信息文件找不到"); e.printStackTrace(); } catch (IOException e) { System.out.println("学生信息文件读取错误"); e.printStackTrace(); } } }
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 + "\n "; } in.close(); System.out.println(n); } }
实验3: 编程练习
练习1:
l 编制一个程序,将身份证号.txt 中的信息读入到内存中;
l 按姓名字典序输出人员信息;
l 查询最大年龄的人员信息;
l 查询最小年龄人员信息;
l 输入你的年龄,查询身份证号.txt中年龄与你最近人的姓名、身份证号、年龄、性别和出生地;
l 查询人员中是否有你的同乡;
l 在以上程序适当位置加入异常捕获代码。
package shen; 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.Arrays; import java.util.Collections; import java.util.Scanner; public class Main { private static ArrayList<Student> studentlist; public static void main(String[] args) { studentlist = new ArrayList<>(); Scanner scanner = new Scanner(System.in); File file = new File("C:\\Users\\ASUS\\Desktop\\新建文件夹\\身份证号.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(); Student student = new Student(); student.setName(name); student.setnumber(number); student.setsex(sex); int a = Integer.parseInt(age); student.setage(a); student.setprovince(province); studentlist.add(student); } } 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("A.字典排序"); System.out.println("B.输出年龄最大和年龄最小的人"); System.out.println("C.寻找老乡"); System.out.println("D.寻找年龄相近的人"); System.out.println("F.退出"); String m = scanner.next(); switch (m) { case "A": Collections.sort(studentlist); System.out.println(studentlist.toString()); break; case "B": int max = 0, min = 100; int j, k1 = 0, k2 = 0; for (int i = 1; i < studentlist.size(); i++) { j = studentlist.get(i).getage(); if (j > max) { max = j; k1 = i; } if (j < min) { min = j; k2 = i; } } System.out.println("年龄最大:" + studentlist.get(k1)); System.out.println("年龄最小:" + studentlist.get(k2)); break; case "C": System.out.println("老家?"); String find = scanner.next(); String place = find.substring(0, 3); for (int i = 0; i < studentlist.size(); i++) { if (studentlist.get(i).getprovince().substring(1, 4).equals(place)) System.out.println("老乡" + studentlist.get(i)); } break; case "D": System.out.println("年龄:"); int yourage = scanner.nextInt(); int near = agenear(yourage); int value = yourage - studentlist.get(near).getage(); System.out.println("" + studentlist.get(near)); break; case "F": 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 < studentlist.size(); i++) { value = studentlist.get(i).getage() - age; if (value < 0) value = -value; if (value < min) { min = value; k = i; } } return k; } } Main
public class Student implements Comparable<Student> { private String name; private String number ; private String sex ; private int age; private String province; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getnumber() { return number; } public void setnumber(String number) { this.number = number; } public String getsex() { return sex ; } public void setsex(String sex ) { this.sex =sex ; } public int getage() { return age; } public void setage(int age) { // int a = Integer.parseInt(age); this.age= age; } public String getprovince() { return province; } public void setprovince(String province) { this.province=province ; } public int compareTo(Student o) { return this.name.compareTo(o.getName()); } public String toString() { return name+"\t"+sex+"\t"+age+"\t"+number+"\t"+province+"\n"; } }
注:以下实验课后完成
练习2:
l 编写一个计算器类,可以完成加、减、乘、除的操作;
l 利用计算机类,设计一个小学生100以内数的四则运算练习程序,由计算机随机产生10道加减乘除练习题,学生输入答案,由程序检查答案是否正确,每道题正确计10分,错误不计分,10道题测试结束后给出测试总分;
l 将程序中测试练习题及学生答题结果输出到文件,文件名为test.txt;
l 在以上程序适当位置加入异常捕获代码。
package demo; import java.io.FileNotFoundException; import java.io.PrintWriter; import java.util.Random; import java.util.Scanner; public class demo { public static void main(String[] args) { Scanner in = new Scanner(System.in); yunsuan counter = new yunsuan(); PrintWriter out = null; try { out = new PrintWriter("text.txt"); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } int sum = 0; System.out.println("随机生成的四则运算类型"); System.out.println("类型1:除法"); System.out.println("类型2:乘法"); System.out.println("类型3:加法"); System.out.println("类型4:减法"); for (int i = 1; i <= 10; i++) { int a = (int) Math.round(Math.random() * 100); int b = (int) Math.round(Math.random() * 100); int m; Random rand = new Random(); m = (int) rand.nextInt(4) + 1; System.out.println("随机生成的四则运算类型:"+m); switch (m) { case 1: System.out.println(i + ": " + a + "/" + b + "="); while (b == 0) { b = (int) Math.round(Math.random() * 100); } double c0 = in.nextDouble(); out.println(a + "/" + b + "=" + c0); if (c0 == counter.division(a, b)) { sum += 10; System.out.println("right!"); } else { System.out.println("error!"); } break; case 2: System.out.println(i + ": " + a + "*" + b + "="); int c = in.nextInt(); out.println(a + "*" + b + "=" + c); if (c == counter.multiplication(a, b)) { sum += 10; System.out.println("right!"); } else { System.out.println("error!"); } break; case 3: System.out.println(i + ": " + a + "+" + b + "="); int c1 = in.nextInt(); out.println(a + "+" + b + "=" + c1); if (c1 == counter.add(a, b)) { sum += 10; System.out.println("right!"); } else { System.out.println("error!"); } break; case 4: System.out.println(i + ": " + a + "-" + b + "="); int c2 = in.nextInt(); out.println(a + "-" + b + "=" + c2); if (c2 == counter.reduce(a, b)) { sum += 10; System.out.println("right!"); } else { System.out.println("error!"); } break; } } System.out.println("成绩" + sum); out.println("成绩:" + sum); out.close(); } }
实验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,结合程序运行结果理解程序;
l 注释语句test1(-5);后重新运行程序,结合程序运行结果理解程序;
l 掌握断言的使用特点及用法。
实验程序2:
l 用JDK命令调试运行教材298页-300页程序7-2,结合程序运行结果理解程序;
l 并掌握Java日志系统的用途及用法。
实验程序3:
l 用JDK命令调试运行教材298页-300页程序7-2,结合程序运行结果理解程序;
l 按课件66-77内容练习并掌握Elipse的常用调试技术。
实验总结:通过这一周的实验,我学习和了解了异常、断言和日志,异常的处理方法及预防异常出现可以加入的语句,断言的概念和启用禁用断言等,以及日志和日志级别分类;对于本章内容的掌握主要为概念上的认识,在程序中加以练习,在运用上不熟练,多多练习,总的来说还需要多看代码。