201871010128-杨丽霞《面向对象程序设计(java)》第十周学习总结
项目 |
内容 |
这个作业属于哪个课程 |
https://www.cnblogs.com/nwnu-daizh/ |
这个作业的要求在哪里 |
https://www.cnblogs.com/nwnu-daizh/p/11778090.html |
作业学习目标 |
1.掌握java异常处理技术; 2.了解断言的用法; 3.了解日志的用途; 4.掌握程序基础调试技巧。
|
第一部分:总结第七章关于异常处理相关理论知识
第七章 异常、断言和日志
7.1 处理错误
1)异常概念: 在程序运行时打断正常程序流程的任何不正常的情况称为错位或异常
2)错误类型: 一般性的错误: 输入数据在保存到数组过程中引起超界、除数为零数据不合法等
致命性的错误: 系统硬件故障、网络通讯中断等引起的无法恢复性错误
Java处理的异常大致分为3类:Error及其子类、RuntimeException及其他Exception类
3)Java对于不同的异常通常采取不同的策略
a.Error 意味着是很难恢复的严重错误,一般不由程序处理。
b. RuntimeException意味着程序设计或实现问题,如:数据使用越界,算术运算异常(如
除0运算),空指针异常即访问没有初始化的空指针等。对于这类异常,处理的策略是纠正错误
C.其他异常,通常是由环境因素引起的,例如文件不存在,无效URL等。这类异常通常是由用户的误操作引起的,可以在异常处理中处理,例如提示用户进行确操作
4)异常处理具体有如下两种方式:
a. 捕获并处理异常。
b. 将方法中产生的异常抛出
5)Java异常处理的总结
1、不要丢弃异常,捕获异常后需要进行相关处理。如果用户觉得不能很好地处理该异常,就让它继续传播,传到别的地方去处理,或者把一个低级的异常转换成应用级的异常,重新抛出
2、catch语句应该指定具体的异常类型。不能把不该捕获的异常也捕获了
3、在finally里面释放资源。如果finally里面也会抛出异常,也一样需要使用try..catch处理
4、不要把大量的代码塞在try...catch块里面,分离各个可能出现异常的语句并分别捕获异常
5、由于异常可能导致输出的数据不完整,因此用户需要作出相对应的处理,至少应该提示该数据的不完整
6)声明抛出异常:如果一个方法可能会生成一些异常,但是该方法并不确切知道如何对这些异常进行处理,此时,这个方法就声明抛出异常。
A:声明抛出异常在方法声明中用throws子句中来声明,throws子句可以同时指多个异常,说明该方法将不对这些异常进行处理,而是声明抛出它们。
B:以下四种情况用throws子句声明抛出异常:
—方法调用了一个抛出已检查异常的方法;
—程序运行过程中可能发生错误,并且利用throw语句抛出一个已检查异常对象
—程序出现错误
—Java虚拟机和运行时库出现的内部异常。
C:一个方法必须声明该方法所有可能抛出的已检查异常,而未检查异常要么不可控制(Error),要么应该避免发生(RuntimeException).
D:如果方法没有声明所有可能发生的已检查异常,编译器会给出一个错误消息
7.2 捕获异常
使用 try...catch....语句捕获异常,如下:
· try 语句中的代码是我们想要执行的逻辑代码,但是在执行的过程中可能会出错。一旦出错,出错行之后的代码不再执行,进入 catch 语句。
· catch 语句是用来捕获异常并处理,就像函数定义一样,但是它只有一个参数。
finally 是一定会执行的代码, 不论 try 中调用 return, break, continue 都会执行这部分的代码(try 中,如果 return, break, continue 之后还有程序,编译器报错)。
如果 try 语句中有返回语句,而返回的值在 finally 中被修改了。 如果运行的过程中没报错,会返回 try 中的返回值, finally 对最后的返回值没有影响
注意:
catch 语句会按照顺序匹配查找
一旦某个 catch 语句匹配上了,就进入此 catch,执行完后,下面的所有 catch 语句都不会再执行了。
catch 语句中,如果一个父类异常排在子类异常的前面,也会报错,因为这个子类异常不可抵达,相当于无用。
如果在 try 主体中确定不会抛出某一类型的异常,此异常就不能出现在 catch 语句中,否则会编译报错。
7.3使用日常机制的技巧
7.4 使用断言
1.断言概述
断言机制允许在测试期间向代码中插入一些检查语句,当代码发布时,这些插入的检测语句就会被自动地移走。assertion(断言)是Java1.4引入的一个新特性,该特性的引入的目的是为了辅助开发人员调试和测试,是一种比较常用的调试、测试方案。由于其会对程序的整体设计产生很大影响,目前很少投入到使用中,一般情况下使用的目的是为了调试和测试。断言表示为一些布尔表达式,程序员相信在程序中的某个特定点该表达式为真。
2.常见的断言特性
前置条件断言:代码执行之前必须具备的特性
后置条件断言:代码执行之后必须具备的特性
前后不变断言:代码执行前后不能变化的特性
3.断言使用格式
断言可以有两种形式:
1.assert 条件;
2.assert 条件:表达式;
使用第一种格式,当布尔类型表达式为false时,抛出AssertionError异常;如果是第二种格式,则输出错误消息
7.5 记录日志
日志的作用
主要作用包括:
1、出问题后定位当时问题
2、显示程序当前运行状态
日志级别介绍
level 定义简直就是一门艺术, 好的定义应该遵循以下原则:
debug:完整详细的记录流程的关键路径. 应该用于开发人员比较感兴趣的跟踪和调试信息, 生产环境中正常都不会打开debug状态
info:应该简洁明确让管理员确认状态。记录相当重要的,对于最终用户和系统管理员有意义的消息。关键系统参数的回显、后台服务的初始化状态、需要系统管理员知会确认的关键信息都需要使用INFO级别
warn:能清楚的告知所有人发生了什么情况.能引起人的重视,指示潜在问题,但不一定需要处理。
error:系统出现了异常或不期望出现的问题,希望及时得到关注的处理。需要注意的一个点,不是所有的异常都需要记录成error。
日志记录的一些最佳实践
以下可以根据项目实际情况当做规范使用:
1、tomcat配置access日志,记录每次http请求相关信息
2、dubbo配置access日志,记录dubbo consumer和provider的每次请求调用和处理
3、在系统里调用外部的地方,记录请求的接口、参数、返回结果、花费时间、遇到的异常等
4、在系统里出现异常的地方,记录异常的堆栈,如果可以,尽量通过异常的日志能还原当时的情景,比如当时受影响的是哪个用户、传入的变量是什么、处理哪些核心数据引发的异常等等
5、有些代码在编写时就知道很难被执行到或者不希望被执行到、以及一些基本不会走到的else块,这些地方需要记录下核心日志
6、禁止使用 System.out 或 System.error
第二部分:实验部分
实验1:用命令行与IDE两种环境下编辑调试运行源程序ExceptionDemo1、ExceptionDemo2,结合程序运行结果理解程序,掌握未检查异常和已检查异常的区别。
(5分)
//异常示例1 public class ExceptionDemo1 { public static void main(String args[]) { int a = 0; System.out.println(5 / a); } }
命令行和IDE两种环境下运行截图如下:
//异常示例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(); } }
命令行和IDE两种环境下运行截图如下:
实验2:测试程序1
l 在elipse IDE中编辑、编译、调试运行教材281页7-1,结合程序运行结果理解程序;
l 在程序中相关代码处添加新知识的注释;
l 掌握Throwable类的堆栈跟踪方法;
1 package stackTrace; 2 3 import java.util.*; 4 5 /** 6 * A program that displays a trace feature of a recursive method call. 7 * @version 1.10 2017-12-14 8 * @author Cay Horstmann 9 */ 10 public class StackTraceTest 11 { 12 /** 13 * Computes the factorial of a number 14 * @param n a non-negative integer 15 * @return n! = 1 * 2 * . . . * n 16 */ 17 public static int factorial(int n) //求阶乘 18 { //调用Throwable类的getStackTrace方法访问栈堆轨迹的文本描述信息 19 System.out.println("factorial(" + n + "):"); 20 var walker = StackWalker.getInstance(); //创建对象时调用堆栈的跟踪 21 walker.forEach(System.out::println); //调用对象walker的foreach循环 22 int r; 23 if (n <= 1) r = 1; 24 else r = n * factorial(n - 1); //计算n的阶乘需要去调用n-1的阶乘 25 System.out.println("return " + r); 26 return r; 27 } 28 29 public static void main(String[] args) 30 { 31 try (var in = new Scanner(System.in)) 32 { 33 System.out.print("Enter n: "); 34 int n = in.nextInt(); 35 factorial(n); 36 } 37 } 38 }
运行截图:
实验2:测试程序2
l Java语言的异常处理有积极处理方法和消极处理两种方式;
l 下列两个简单程序范例给出了两种异常处理的代码格式。在elipse IDE中编辑、调试运行源程序ExceptionTest.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"); } } |
积极处理方式代码:
1 import java.io.*; 2 3 class ExceptionTest { 4 public static void main (String args[]) 5 { 6 7 try{ 8 FileInputStream fis=new FileInputStream("D://身份证号.txt"); 9 BufferedReader in = new BufferedReader(new InputStreamReader(fis)); 10 String s = new String(); 11 while ((s = in.readLine()) != null) { 12 System.out.println(s); 13 } 14 in.close(); 15 fis.close(); 16 } 17 catch (FileNotFoundException e) { 18 System.out.println("文件未找到"); 19 e.printStackTrace(); 20 } catch (IOException e) { 21 System.out.println("文件读取错误"); 22 e.printStackTrace(); 23 } 24 } 25 }
消极处理方式代码:
1 import java.io.*; 2 3 public class ExceptionTest { 4 public static void main (String args[]) throws IOException 5 { 6 7 try{ 8 FileInputStream fis=new FileInputStream("D://身份证号.txt"); 9 BufferedReader in = new BufferedReader(new InputStreamReader(fis)); 10 String s = new String(); 11 while ((s = in.readLine()) != null) { 12 System.out.println(s); 13 } 14 in.close(); 15 fis.close(); 16 17 } 18 finally { 19 return ; 20 } 21 22 } 23 }
运行截图:
实验3:编程练习
l 编写一个计算器类,可以完成加、减、乘、除的操作;
l 利用计算机类,设计一个小学生100以内数的四则运算练习程序,由计算机随机产生10道加减乘除练习题,学生输入答案,由程序检查答案是否正确,每道题正确计10分,错误不计分,10道题测试结束后给出测试总分;
l 将程序中测试练习题及学生答题结果输出到文件,文件名为test.txt;
l 在以上程序适当位置加入异常捕获代码。
代码如下:
package test; import java.util.Random; import java.util.Scanner; public class test{ int sum; public static void main(String[] args) { test t=new test(); System.out.println("本次测试共十道题,每题十分,满分一百分"); t.sum=0; Random r=new Random(); for(int i=0;i<10;i++) { t.core(); } System.out.println("考试结束"); System.out.println("你的总分为"+t.sum); } private void core() { Random r=new Random(); int m,n; m=r.nextInt(11); n=m%4; switch(n) { case 0: int a ,b,c; a=r.nextInt(101); b=r.nextInt(101); System.out.println(a+"+"+"("+b+")"+"="); Scanner x=new Scanner(System.in); c=x.nextInt(); if(c!=a+b) System.out.println("回答错误"); else { System.out.println("回答正确"); sum=sum+10; } break; case 1: int h,g,f; h=r.nextInt(101); g=r.nextInt(101); System.out.println(h+"-"+"("+g+")"+"= "); Scanner u=new Scanner(System.in); f=u.nextInt(); if(f!=h-g) System.out.println("回答错误"); else { System.out.println("回答正确"); sum=sum+10; } break; case 2: int q,w,e; q=r.nextInt(101); w=r.nextInt(101); System.out.println(q+"*"+"("+w+")"+"= "); Scanner y=new Scanner(System.in); e=y.nextInt(); if(e!=q*w) System.out.println("回答错误"); else { System.out.println("回答正确"); sum=sum+10; } break; case 3: double j,k,l; j=r.nextInt(101); k=r.nextInt(101); if(k==0) k++; System.out.println(j+"/"+"("+k+")"+"= "); Scanner z=new Scanner(System.in); l=z.nextDouble(); if(l!=(j/k)/1.00) System.out.println("回答错误"); else { System.out.println("回答正常"); sum=sum+10; } break; } } }
运行截图:
实验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 掌握断言的使用特点及用法。
注释语句test(-5)后:
实验4:测试程序2
l 用JDK命令调试运行教材298页-300页程序7-2,结合程序运行结果理解程序;
l 并掌握Java日志系统的用途及用法。
package logging; import java.awt.*; import java.awt.event.*; import java.io.*; import java.util.logging.*; import javax.swing.*; /** * A modification of the image viewer program that logs various events. * @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); }); } } /** * The frame that shows the image. */ 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); // set up menu bar 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); } }); // use a label to display the images 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); // set up file chooser JFileChooser chooser = new JFileChooser(); chooser.setCurrentDirectory(new File(".")); // accept all files ending with .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"; } }); // show file chooser dialog int r = chooser.showOpenDialog(ImageViewerFrame.this); // if image file accepted, set it as icon of the label 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) { } // not called 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(); } }
运行截图:
实验4:测试程序3
l 用JDK命令调试运行教材298页-300页程序7-2,结合程序运行结果理解程序;
l 按课件66-77内容练习并掌握Elipse的常用调试技术。
实验总结:
这一周学习了程序运行过程中产生的异常和处理异常的方法,断言,日志。异常时在程序的执行过程中很常见。因此在编写代码时需要及时处理这些错误。异常的抛出,断言,刚接触时不太能理解,但是通过课后作业和mooc的学习,有了进一步的认识,并且在测试程序和自主编程的练习中体会新知识的应用,思考并且反思自身学到的东西和未掌握的,多总结和利用各种学习资源,通过多练习去提高自己的编程能力。