201871010101-陈来弟《面相对象程序设计(java)》第十周学习总结

                                                                                                                   201871010101-陈来弟《面相对象程序设计(java)》第十周学习总结

实验八异常、断言与日志

实验时间 2019-11-1

 

1、实验目的与要求

(1) 掌握java异常处理技术;

(2) 了解断言的用法;

(3) 了解日志的用途;

(4) 掌握程序基础调试技巧;

第一部分:理论基础

       在第七章的内容讲述的是异常、断言与日志。主要分为处理错误、捕获异常、使用异常机制的技巧、使用断言、记录日志、调试技巧等小节。

1.Java异常处理
  i.异常的概念和Java里面的异常体系结构
  1)基本概念:
  程序中的异常,一般成为例外情况,可以理解为是非正常情况,其他编程语言里面也有这样的情况,Java里面同样存在这样一个体系结构,这里需要分清楚的是异常和错误不是一个概念。异常并非是真正的错误,因为他们是一些例外情况,这些情况有可能不会导致系统直接崩溃掉,但是它的存在只能说是程序的某种缺陷,或者说是非必然缺陷,而Java里面提供的异常体系结构就是为了解决这些缺陷而存在的。
  在异常处理机制诞生之前,传统的异常处理方式多数是为了采用返回值来标识程序出现异常的情况,这种方式都很熟悉,如同在调试过程即是有良好的调试工具,但是常用的手段就是System.out.println的方式,但是这样的方式隐含一定的缺点。
  [1]一个API可以返回任意的值,但是这些返回值本身不能解释返回值是否代表一个异常发生了,也不能描述异常的详细情况,若要知道该API出现异常的一些内容,还需要调用它的某些方法;
  [2]没有一种结构可以确保异常情况能够得到处理,如果使用第一种方法,就会使得代码的可读性很差,而且很多时候并不能因为某些情况的存在就终止程序,就程序本身而言是应该提供一定的反馈情况。假设这样一个场景,如果你去输入用户名和密码登陆,如果你的用户名和密码输入错误了,但是界面上没有任何反应,这种情况是不是很糟糕,当然这只是个比方,这里不一定是出现了异常。
  在开发过程中,当一个程序本身抛出了异常过后,程序会从程序导致异常的地方跳出来,在java语言里面,使用try和catch块来实现,当JVM碰到这个语句块的时候,它会监听整个Java程序,一旦出现了任何异常情况,它会将整个程序的执行权交给catch块来执行。
       先看一段简单的代码:

import java.io.File;
import java.io.FileReader;
/**
 *一个简单的文件操作
 **/
public class CustomerFileReader
{
    public static void main(String args[])
    {
        File file = new File("C:/read.txt");
        FileReader reader = new FileReader(file); //这句话不能通过JVM的编译器
    }
}
  上边这段代码如果使用的是javac的命令,那么编译器就会报错,可能错误信息如下:
unreported exception java.io.FileNotFoundException; must be caught or declared to be thrown
  那么如果要保证上边这句代码是能够通过编译的,如何简单修改呢,有两种方式:
  [1]加入try和catch块,使用JVM的异常体系结构去捕捉该异常;
  [2]直接throws这个异常,让程序显式抛出该异常。

 

2)Java里面的异常体系结构:
  先看下边的异常体系结构图:
 
           

 

Java里面的异常分类按照下边的结构来分:
  Throwable是所有异常的基类,程序中一般不会直接抛出Throwable对象,Throwable本身存在两个子类实例,一个是Error、一个是Exception;
  [1]Error:在Java里面Error表示程序在运行期间出现了十分严重的问题以及不可以恢复的错误,这种情况唯一的办法是中止程序运行,JVM一般不会检查Error是否被处理,而本身在程序中也不能捕获Error类型的异常,因为Error一旦产生,该程序基本会处于需要中止的状态。
  [2]Exception:在Java里面Exception指的就是在编程过程可能会遇到的异常的概念,也属于Java程序里面允许出现的例外的状况,而Exception本身分为以下两种:
  RuntimeException:该异常继承于Exception类,这种类型的异常可以这样理解,为不可估计的异常,一般称为运行时异常,从字面意思上讲就是在程序正式运行的时候会碰到的偶发性异常,这种异常因为是在运行时抛出一般情况下不需要进行捕获操作。
  CheckedException:该类异常不存在继承于Exception类的说法,因为Java里面没有CheckedException异常类,而之所以这样区分,因为CheckedException类的异常是在编程过程中经常会遇到的异常,可以翻译为“可检测异常”或者“捕获型异常”,该类异常往往属于编译期异常,一般开发过程会针对系统进行CheckedException的设计。

【*:JVM在处理Java程序的时候,Runtime Exception和Checked Exception两种类型的异常在运行机制上是不一样的,而仔细考虑两种异常的使用场合也会发现,其两种异常在设计上所提及的使用目的也大相径庭。从程序开发角度可以这样理解:Checked Exception的设计目的在于这个Exception是必须被处理的,在程序设计过程应该知道这类异常会出现,所以要针对这一类型的异常进行不同的处理操作,这些异常也可以认为是在程序设计之初可以考虑到的异常;而RuntimeException可能理解起来隐晦一点,不能说不能考虑到这种异常的存在,反而是即使能够考虑到,也不能进行良性的程序处理,它往往是暗示着程序可能会出现某种错误,这种错误有可能根程序本身无关,也有可能有关,是在设计程序之初是无法预知处理方式的,而有时候甚至会造成程序中止的情况。】

 3)深入了解Throwable类:
  Throwable类是Java语言中所有错误(Error)或异常(Exception)的超类,只有当某个对象是该类的子类实例的时候,才能通过JVM或者Java本身编写过程的throw语句抛出,按照这种逻辑区判断,只有此类或者它的子类才可以是catch子句中的参数类型。Throwable类有两个子类Error和Exception,上边已经简单介绍过这两种类型的区别了。Throwable类本身包含:
  [1]线程创建的时候执行堆栈的快照
  [2]有关错误的消息字符串,比如该异常出现的位置以及代码里面的哪一行
  [3]它指出了这个异常的原因:该异常是由哪个异常导致的或者说是由哪个异常抛出的Throwable导致的这个Throwable的产生
  从JDK 1.4开始,出现了一个异常处理的新概念:异常链(Cause机制)。异常链机制可以这样理解:如果某个程序出现了异常,那么该异常本身也会有个原因,这个原因可能是自身的,也可能是外界的,以此类推就形成了一个异常链,简单讲:每个异常都是由另外一个异常引起的。
  而什么内容导致了throwable cause呢,查阅官方的API文档有以下两种解释:
  [1]导致throwable cause的一个理由是,抛出它的类构建在低层抽象之中,而高层操作由于低层操作的失败而失败。让低层抛出的throwable向外传播是一种糟糕的设计方法,因为它通常与高层提供的抽象不相关。此外,这样做将高层API与其实现细节关联起来,假定低层异常是经过检查的异常。抛出“经过包装的异常”(即包含cause的异常)允许高层与其调用方交流失败详细信息,而不会招致上述任何一个缺点。这种方式保留了改变高层实现而不改变其 API 的灵活性
  [2]导致throwable cause的另一个cause是,抛出它的方法必须符合通用接口,而通用接口不允许方法直接抛出cause。例如,假定持久集合符合Collection接口,而其持久性在java.io的基础上实现。假定add方法的内部可以抛出IOException。实现可以与其调用方交流IOException的详细消息,同时通过以一种合适的未检查的异常来包装IOException,使其符合Collection接口。

ii.异常的基本语法
  前边介绍了Java异常体系结构、分类以及基本概念,这一小节需要介绍的就是Java里面异常的基本语法。在Java里面,异常处理机制的编程部分需要使用到几个关键字:try、catch、finally、throw、throws

try语句:
  该语句块属于代码的正常执行块,如果这段代码里面不会出现任何异常,那么try语句块会按照顺序一直执行下去
  catch语句:
  该语句块的参数类似于平时在代码书写中的方法声明,包含了一个异常类型以及一个异常对象。这里结合第一节讲到的,异常类型必须是Throwable的子类型,它指出了catch语句处理的异常类型,异常对象则有可能在try语句块里面生成,而且被对应的catch块里面的异常类型所捕获,大括号里面的内容就是当你捕获了对应的异常过后如何进行处理。
  在异常处理里面,catch语句块可以有多个,分别处理不同的异常。Java运行的时候一旦抛出了异常就从catch块从上往下检索,一旦匹配对应的类型就执行catch块里面的内容,所以这里有一点需要注意:
  catch块里面的异常类型的顺序,一般情况是从特殊到一般,然后是从子类到父类,否则会造成代码不可达的无用代码块
  finally语句:
  该语句块可以指定一个段代码块,不论try块也好、catch块也好,也不论异常是否抛出,最终都会执行finally块里面的内容,可以这样理解:finally块里面是异常处理机制的统一出口,只要存在这样的一段代码块最终出口都是执行完finally块里面的内容了再继续。【*:但是有一个特殊的情况,如果try块里面出现了return语句,那么finally块里面的内容是不会执行的,但是这种做法不提倡。】

throw关键字:
  throw关键字总是出现在函数体内部,用来抛出一个异常,程序会在throw语句后立即终止执行,也就是说位于throw语句之后的语句块不会执行,一旦它抛出了一个异常过后,JVM会在包含它的try块所对应的catch里面根据抛出的异常类型匹配操作,如果能匹配就直接被捕捉,一旦不能匹配就继续往执行体的外层抛出该异常。
  throws关键字:
  throws关键字总是出现在函数头部,用来表明该函数有可能会抛出某种异常,有几点需要注意:
  [1]函数可以抛出多个不同类型的异常,直接使用,将每种抛出的不同异常分开;
  [2]如果函数体里面存在throw语句,而且函数体本身没有进行捕捉的话,那么必须使用throws在函数头里面添加对应的异常抛出语句,否则无法通过编译
  [3]如果编写代码的时候需要明确抛出一个RuntimeException,那么必须显示使用throws语句来声明它的类型
  [4]以上的规则主要是针对CheckedException,针对Error和RuntimeException或者它们的子类,这些规则不起作用。

 

第二部分:实验内容和步骤

实验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();

      }

}

 

 

异常示例1

package testing;
public class ExceptionDemo1 {
    public static void main(String args[]) {
        int a = 0;
        System.out.println(5 / a);
    }
}


运行结果:

 

异常示例2

package testing;
public class ExceptionDemo1 {
    public static void main(String args[]) {
        int a = 0;
        if(a==0){
            System.out.println("程序异常!");
        }
        else{
        System.out.println(5 / a);
    }
}
}


运行结果:

 

实验2: 导入以下示例程序,测试程序并进行代码注释。

测试程序1:

l  在elipse IDE中编辑、编译、调试运行教材281页7-1,结合程序运行结果理解程序;

l  在程序中相关代码处添加新知识的注释;

l  掌握Throwable类的堆栈跟踪方法;

 7-1 代码:
package stackTrace;

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;
      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中编辑、调试运行源程序ExceptionTest.java,将程序中的text文件更换为身份证号.txt,要求将文件内容读入内容,并在控制台显示;

l  掌握两种异常处理技术的特点。

//积极处理方式  

import java.io.*;

 

class ExceptionTest {

              public static void main (string args[])

   {

       try{

                     FileInputStream fis=new FileInputStream("text.txt");

       }

       catchFileNotFoundExcption e)

            {   ……  }

              ……

    }

}

//消极处理方式

 

import java.io.*;

class ExceptionTest {

              public static void main (string args[]) throws  FileNotFoundExcption

     {

                  FileInputStream fis=new FileInputStream("text.txt");

     }

}

异常 1:

package cld;
  //积极处理方式  
import java.io.*;
  import java.io.BufferedReader;
  import java.io.FileReader; 
  public class ExceptionTest {
     public static void main (String args[])
  {
         File fis=new File("身份证号.txt");
      try{
          
 
          FileReader fr = new FileReader(fis);
        BufferedReader br = new BufferedReader(fr);
          try {
              String s, s2 = new String();
              while ((s = br.readLine()) != null) {
                s2 += s + "\n ";
            }
             br.close();              System.out.println(s2);
          } catch (IOException e) {
              // TODO Auto-generated catch block
              e.printStackTrace();
          }
      } catch (FileNotFoundException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
      }
 
   }
 }

运行结果:

 

异常 2:

package aaa;
  
  //消极处理方式
  
  import java.io.*;
  public class ExceptionTest {
      public static void main (String args[]) throws  IOException
        {
         File fis=new File("身份证号.txt");
         FileReader fr = new FileReader(fis);
         BufferedReader br = new BufferedReader(fr);
         String s, s2 = new String();
 
             while ((s = br.readLine()) != null) {
                 s2 += s + "\n ";
             }
             br.close();
            System.out.println(s2);
       }
 }

 

运行结果:

 

实验3: 编程练习

   编写一个计算器类,可以完成加、减、乘、除的操作;

l   利用计算机类,设计一个小学生100以内数的四则运算练习程序,由计算机随机产生10道加减乘除练习题,学生输入答案,由程序检查答案是否正确,每道题正确计10分,错误不计分,10道题测试结束后给出测试总分;

l   将程序中测试练习题及学生答题结果输出到文件,文件名为test.txt;

l   在以上程序适当位置加入异常捕获代码。

 

代码如下:
package aaa;
  import java.io.FileNotFoundException;
  import java.io.PrintWriter;
  import java.util.Scanner;
  
  
  public class b {
      public static void main(String[] args) {
  
         Scanner in = new Scanner(System.in);
         Student student=new Student();
         PrintWriter out = null;
         try {
             out = new PrintWriter("text.txt");
         } catch (FileNotFoundException e) {
             e.printStackTrace();
         }
         int sum = 0;
 
         
         
         for (int i = 1; i <=10; i++) {
             int a = (int) Math.round(Math.random() * 100);
             int b = (int) Math.round(Math.random() * 100);
             int c= (int) Math.round(Math.random() * 3);
 
             
            switch(c)
            {
            case 0:
                System.out.println(i+": "+a+"/"+b+"=");
               
                while(b==0)
                { 
                    b = (int) Math.round(Math.random() * 100);
             }
                
             int C = in.nextInt();
             out.println(a+"/"+b+"="+C);
             if (C == student.division(a, b)) {
                 sum += 10;
                 System.out.println("恭喜答案正确");
             }
             else {
                 System.out.println("抱歉,答案错误");
             }
             
             break;
             
            case 1:
                System.out.println(i+": "+a+"*"+b+"=");
                int D = in.nextInt();
                out.println(a+"*"+b+"="+D);
                if (D == student.multiplication(a, b)) {
                    sum += 10;
                    System.out.println("恭喜答案正确");
                }
                else {
                    System.out.println("抱歉,答案错误");
                }
                break;
            case 2:
                System.out.println(i+": "+a+"+"+b+"=");
                int E = in.nextInt();
                out.println(a+"+"+b+"="+E);
                if (E == student.add(a, b)) {
                      sum += 10;
                    System.out.println("恭喜答案正确");
                }
                else {
                    System.out.println("抱歉,答案错误");
                }                
                break ;
            case 3:
                System.out.println(i+": "+a+"-"+b+"=");
                int F = in.nextInt();
                out.println(a+"-"+b+"="+F);
                if (F == student.reduce(a, b)) {
                    sum += 10;
                    System.out.println("恭喜答案正确");
                }
                else {
                    System.out.println("抱歉,答案错误");
                }
                break ;
               } 
           }
        System.out.println("成绩"+sum);
         out.println("成绩:"+sum);
          out.close();        
    }
    }






package aaa;
public class Student {
   private int a;
   private int b;
    public int  add(int a,int b)
    {
        return a+b;
    }
    public int   reduce(int a,int b)
    {
        return a-b;
    }
    public int   multiplication(int a,int b)
    {
        return a*b;
    }
    public int   division(int a,int b)
    {
        if(b!=0)
        return a/b;
        else return 0;
    }

}

 

 运算结果:

 

 

      

 

 

注:以下实验课后完成

实验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  掌握断言的使用特点及用法。

修改后:

package duanyan;


public class AssertDemo {

    public static void main(String[] args) {       

       // test1(-5);

        test2(-3);

    }

   

    private static void test1(int a){

        assert a > 0;//assert宏的原型定义在<assert.h>中,作用是如果它的条件返回错误,则终止程序执行
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); } }

运行结果:


 

 

 

实验程序2:

l  用JDK命令调试运行教材298页-300页程序7-2,结合程序运行结果理解程序;

l  并掌握Java日志系统的用途及用法。

代码入下:

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);
            });
   }
}

/**
 * 显示图像的帧。
 */
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");
      }
   }
}

/**
 * 用于在窗口中显示日志记录的处理程序。
 */
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();
   }
}

其运行结果:

 

实验程序3:

l  用JDK命令调试运行教材298页-300页程序7-2,结合程序运行结果理解程序;

按课件66-77内容练习并掌握Elipse的常用调试技术。

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);
            });
   }
}

/**
 * 显示图像的帧。
 */
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");
      }
   }
}

/**
 * 用于在窗口中显示日志记录的处理程序。
 */
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();
   }
}

其运行结果:

实验总结:

       在本周的学习中,掌握了java异常处理的一些基础技术;通过调试测试书上的示例程序,以及老师和助教学长的讲解下使我初步的理解了这一章的知识。异常时在程序的执行过程中所发生的非正常事件,它中断指令的正常执行。因此在编写代码时需要及时处理这些错误。对于异常处理,在理论课上听的不是很明白,但在实验课上通过学长演示程序,用抛出异常和将可能出错的语句放入try子句中两种方法,基本理解了异常的产生的原因和解决方法。但对于断言以及日志等内容,还是不明白,自己在课后看了课本内容,但也还是不太理解。因此在运行后面几个相关程序时,对程序不是很理解。希望在之后的课堂上,老师能再讲解一下,自己也会多练习程序去了解这些知识。

 

posted @ 2019-11-04 21:41  201871010101-陈来弟  阅读(223)  评论(0编辑  收藏  举报