王之泰201771010131《面向对象程序设计(java)》第八周学习总结

第一部分:理论知识学习部分

第六章  

  第六章知识点主要分为1. 接口 2. lambda表达式 3. 内部类 4. 代理

    1. 接口
      1) Java为了克服单继承的缺点,Java使用了接口, 一个类可以实现一个或多个接口。

      2) 在Java程序设计语言中,接口不是类,而是对类 的一组需求描述,由常量和一组抽象方法组成。

      3) 接口中不包括变量和有具体实现的方法。

      4) 只要类实现了接口,则该类要遵从接口描述的统 一格式进行定义,并且可以在任何需要该接口的 地方使用这个类的对象。

      5)声明方式: public interface 接口名 { …… }   接口体中包含常量定义和方法定义,接口中只进 行方法的声明,不提供方法的实现。

      6)类似建立类的继承关系,接口也可以扩展。 接口的扩展技术使得从具有较高通用性的接口存在 多条链延伸到具有较高专用性的接口。

        扩展方法: public   interface  接口1   extends接口2 {    ……           }

      7)在类声明时用implements关键字声明使用一个或 多个接口

        class Employee implementsPrintable { …… } 

      一个类使用了某个接口,那么这个类必须实现该 接口的所有方法,即为这些方法提供方法体。 一个类可以实现多个接口,接口间应该用逗号分 隔开。

        class Employee implements Cloneable,Comparable

      8)接口不能构造接口对象,但可以声明接口变量以指向一个实现了该接口的类对象。

         Comparablex = new Comparable(…);       //ERROR

        Comparable  x= new Employee(…);     //OK

       可以用instanceof检查对象是否实现了某个接口。

         if  (anObjectinstanceofComparable) {   ……} 

     2. 接口与抽象类的区别: (1)接口不能实现任何方法,而抽象类可以。 (2)类可以实现许多接口,但只有一个父类。 (3)接口不是类分级结构的一部分,无任何联系的类可以实现相同的接口。

               3. 回调(callback):一种程序设计模式,在这种模式中,可指出某个特定事件发生时程序应该采取的动作。 在java.swing包中有一个Timer类,可以使用它在到达给定的时间间隔时触发一个事件。

                     Timer(intinterval, ActionListenerlistener)

        void start()

        void stop()

    4. Object类的Clone方法
         1)当拷贝一个对象变量时,原始变量与拷贝变量引用同一个对象。这样,改变一个变量所引用 的对象会对另一个变量产生影响。
         2)如果要创建一个对象新的copy,它的最初状态与 original一样,但以后可以各自改变状态,就需要使用Object类的clone方法。

    5. 浅层拷贝:被拷贝对象的所有常量成员和基本类 型属性都有与原来对象相同的拷贝值,而若成员域是一个对象,则被拷贝对象该对象域的对象引 用仍然指向原来的对象。

     深层拷贝:被拷贝对象的所有成员域都含有与原 来对象相同的值,且对象域将指向被复制过的新对 象,而不是原有对象被引用的对象。换言之,深 层拷贝将拷贝对象内引用的对象也拷贝一遍。

    6. Java Lambda 表达式是Java 8 引入的一个新的功能,主要用途是提供一个函数化的语法来简化编码。 Lambda表达式本质上是一个匿名方法。

       public intadd(intx, inty) { return x + y; } 转成Lambda表达式后是: (intx, inty) -> x + y; 

    7.内部类(inner class)是定义在一个类内部的类。 外层的类成为外部类(outer class).

       内部类主要用于事件处理。 使用内部类的原因有以下三个:

      1)内部类方法可以访问该类定义所在的作用域中的数据,包括私有数据。

      2)内部类能够隐藏起来,不为同一包中的其他类所见。

      3)想要定义一个回调函数且不想编写大量代码时, 使用匿名内部类比较便捷。 

第二部分:实验部分 — 接口的定义与使用

实验时间 2018-10-18

1、实验目的与要求

(1) 掌握接口定义方法;

(2) 掌握实现接口类的定义要求;

(3) 掌握实现了接口类的使用要求;

(4) 掌握程序回调设计模式;

(5) 掌握Comparator接口用法;

(6) 掌握对象浅层拷贝与深层拷贝方法;

(7) 掌握Lambda表达式语法;

(8) 了解内部类的用途及语法要求。

2、实验内容和步骤

实验1: 导入第6章示例程序,测试程序并进行代码注释。

测试程序1:

1.编辑、编译、调试运行阅读教材214页-215页程序6-1、6-2,理解程序并分析程序运行结果;

2.在程序中相关代码处添加新知识的注释。

3,掌握接口的实现用法;

4.掌握内置接口Compareable的用法。

EmployeeSortTest程序

 1 package interfaces;
 2 
 3 import java.util.*;
 4 
 5 /**
 6  * This program demonstrates the use of the Comparable interface.
 7  * @version 1.30 2004-02-27
 8  * @author Cay Horstmann
 9  */
10 public class EmployeeSortTest
11 {
12    public static void main(String[] args)
13    {
14       Employee[] staff = new Employee[3];
15 
16       staff[0] = new Employee("Harry Hacker", 35000);
17       staff[1] = new Employee("Carl Cracker", 75000);
18       staff[2] = new Employee("Tony Tester", 38000);
19 
20       Arrays.sort(staff);
21 
22       // 打印所有员工对象的信息
23       for (Employee e : staff)
24          System.out.println("name=" + e.getName() + ",salary=" + e.getSalary());
25    }
26 }

Employee程序

 1 package interfaces;
 2 
 3 //将类声明为实现为某个接口,使用implements关键字
 4 public class Employee implements Comparable<Employee>
 5 {
 6    private String name;
 7    private double salary;
 8 
 9    public Employee(String name, double salary)
10    {
11       this.name = name;
12       this.salary = salary;
13    }
14 
15    public String getName()
16    {
17       return name;
18    }
19 
20    public double getSalary()
21    {
22       return salary;
23    }
24 
25    public void raiseSalary(double byPercent)
26    {
27       double raise = salary * byPercent / 100;
28       salary += raise;
29    }
30 
31    /**
32     * Compares employees by salary
33     * @param other another Employee object
34     * @return a negative value if this employee has a lower salary than
35     * otherObject, 0 if the salaries are the same, a positive value otherwise
36     */
37    public int compareTo(Employee other)
38    {
39 //       这里使用了静态Double.compare方法,且为泛型Comparable提供了一个类型参数
40       return Double.compare(salary, other.salary);
41    }
42 }

 实验结果:

测试程序2:

编辑、编译、调试以下程序,结合程序运行结果理解程序

 1 interface  A
 2 {
 3   double g=9.8;
 4   void show( );
 5 }
 6 class C implements A
 7 {
 8   public void show( )
 9   {System.out.println("g="+g);}
10 }
11 
12 class InterfaceTest
13 {
14   public static void main(String[ ] args)
15   {
16        A a=new C( );
17        a.show( );
18        System.out.println("g="+C.g);
19   }
20 }

实验结果:

测试程序3:

1.在elipse IDE中调试运行教材223页6-3,结合程序运行结果理解程序;

2.26行、36行代码参阅224页,详细内容涉及教材12章。

3.在程序中相关代码处添加新知识的注释。

4.掌握回调程序设计模式;

6-3代码如下:

 1 /**
 2    @version 1.01 2015-05-12
 3    @author Cay Horstmann
 4 */
 5 
 6 import java.awt.*;
 7 import java.awt.event.*;
 8 import java.util.*;
 9 import javax.swing.*;
10 import javax.swing.Timer; 
11 // 用JavaUTIL计时器解决冲突
12 
13 public class TimerTest
14 {  
15    public static void main(String[] args)
16    {  
17       ActionListener listener = new TimePrinter();
18 
19       // 构建一个调用侦听器的计时器
20       // 每十秒一次
21       Timer t = new Timer(10000, listener);
22       t.start();
23 
24       JOptionPane.showMessageDialog(null, "Quit program?");
25       System.exit(0);
26    }
27 }
28 
29 class TimePrinter implements ActionListener
30 {  
31    public void actionPerformed(ActionEvent event)
32    {  
33       System.out.println("At the tone, the time is " + new Date());
34       Toolkit.getDefaultToolkit().beep();
35    }
36 }

实验结果如下:

测试程序4:

1.调试运行教材229页-231页程序6-4、6-5,结合程序运行结果理解程序;

2.在程序中相关代码处添加新知识的注释。

3.掌握对象克隆实现技术;

4.掌握浅拷贝和深拷贝的差别。

6-4程序如下:

 1 package clone;
 2 
 3 /**
 4  * This program demonstrates cloning.
 5  * @version 1.10 2002-07-01
 6  * @author Cay Horstmann
 7  */
 8 //克隆了employee中的一个实例,调用了两个更改器的方法
 9 public class CloneTest
10 {
11    public static void main(String[] args)
12    {
13       try
14       {
15          Employee original = new Employee("John Q. Public", 50000);
16          original.setHireDay(2000, 1, 1);
17          Employee copy = original.clone();
18          copy.raiseSalary(10);//raiseSalary方法改变salary域的值
19          copy.setHireDay(2002, 12, 31);//setHireDay改变hireday域的状态
20          System.out.println("original=" + original);
21          System.out.println("copy=" + copy);
22       }
23       catch (CloneNotSupportedException e)
24       {
25          e.printStackTrace();
26       }
27    }
28 }

6-5程序如下:

 1 package clone;
 2 
 3 import java.util.Date;
 4 import java.util.GregorianCalendar;
 5 
 6 public class Employee implements Cloneable
 7 {
 8    private String name;
 9    private double salary;
10    private Date hireDay;
11 
12    public Employee(String name, double salary)
13    {
14       this.name = name;
15       this.salary = salary;
16       hireDay = new Date();
17    }
18 
19    public Employee clone() throws CloneNotSupportedException
20    {
21       // 调用对象clone
22       Employee cloned = (Employee) super.clone();
23 
24       // 克隆可变字段
25       cloned.hireDay = (Date) hireDay.clone();
26 
27       return cloned;
28    }
29 
30    /**
31     * Set the hire day to a given date. 
32     * @param year the year of the hire day
33     * @param month the month of the hire day
34     * @param day the day of the hire day
35     */
36    public void setHireDay(int year, int month, int day)
37    {
38       Date newHireDay = new GregorianCalendar(year, month - 1, day).getTime();
39       
40       // 实例字段突变示例
41       hireDay.setTime(newHireDay.getTime());
42    }
43 
44    public void raiseSalary(double byPercent)
45    {
46       double raise = salary * byPercent / 100;
47       salary += raise;
48    }
49 
50    public String toString()
51    {
52       return "Employee[name=" + name + ",salary=" + salary + ",hireDay=" + hireDay + "]";
53    }
54 }

实验结果如下:

实验2: 导入第6章示例程序6-6,学习Lambda表达式用法。

1.调试运行教材233页-234页程序6-6,结合程序运行结果理解程序;

2.在程序中相关代码处添加新知识的注释。

将27-29行代码与教材223页程序对比,将27-29行代码与此程序对比,体会Lambda表达式的优点。

 6-6程序如下:

 1 package lambda;
 2 
 3 import java.util.*;
 4 
 5 import javax.swing.*;
 6 import javax.swing.Timer;
 7 
 8 /**
 9  * This program demonstrates the use of lambda expressions.
10  * @version 1.0 2015-05-12
11  * @author Cay Horstmann
12  */
13 public class LambdaTest
14 {
15    public static void main(String[] args)
16    {
17       String[] planets = new String[] { "Mercury", "Venus", "Earth", "Mars", 
18             "Jupiter", "Saturn", "Uranus", "Neptune" };
19       System.out.println(Arrays.toString(planets));
20       System.out.println("Sorted in dictionary order:");
21       Arrays.sort(planets);
22       System.out.println(Arrays.toString(planets));
23       System.out.println("Sorted by length:");
24       Arrays.sort(planets, (first, second) //先指定类型,再计算表达式
25               -> first.length() - second.length());
26       System.out.println(Arrays.toString(planets));
27             
28       Timer t = new Timer(1000, event 
29               ->System.out.println("The time is " + new Date()));
30       t.start();   
31          
32       // 保持程序运行直到用户选择“OK”
33       JOptionPane.showMessageDialog(null, "Quit program?");
34       System.exit(0);         
35    }
36 }

实验结果如下:

实验3: 编程练习

1.编制一个程序,将身份证号.txt 中的信息读入到内存中;

2.按姓名字典序输出人员信息;

3.查询最大年龄的人员信息;

4.查询最小年龄人员信息;

5.输入你的年龄,查询身份证号.txt中年龄与你最近人的姓名、身份证号、年龄、性别和出生地;

6.查询人员中是否有你的同乡。

实验代码如下:

  1 package IDcard;
  2 
  3 import java.io.BufferedReader;
  4 import java.io.File;
  5 import java.io.FileInputStream;
  6 import java.io.FileNotFoundException;
  7 import java.io.IOException;
  8 import java.io.InputStreamReader;
  9 import java.util.ArrayList;
 10 import java.util.Scanner;
 11 import java.util.Collections;
 12 
 13 public class ID {
 14 
 15     public static People findPeopleByname(String name) {
 16         People flag = null;
 17         for (People people : peoplelist) {
 18             if(people.getName().equals(name)) {
 19                 flag = people;
 20             }
 21         }
 22         return flag;
 23 
 24     }
 25 
 26     public static People findPeopleByid(String id) {
 27         People flag = null;
 28         for (People people : peoplelist) {
 29             if(people.getnumber().equals(id)) {
 30                 flag = people;
 31             }
 32         }
 33         return flag;
 34 
 35     }
 36      
 37     private static ArrayList<People> agenear(int yourage) {
 38         // TODO Auto-generated method stub
 39         int j=0,min=53,d_value=0,k = 0;
 40         ArrayList<People> plist = new ArrayList<People>();
 41         for (int i = 0; i < peoplelist.size(); i++) {
 42             d_value = peoplelist.get(i).getage() > yourage ? 
 43                     peoplelist.get(i).getage() - yourage : yourage - peoplelist.get(i).getage() ;
 44             k = d_value < min ? i : k;
 45             min = d_value < min ? d_value : min;
 46         }
 47         for(People people : peoplelist) {
 48             if(people.getage() == peoplelist.get(k).getage()) {
 49                 plist.add(people);
 50             }
 51         }
 52         return plist;
 53     }
 54 
 55     private static ArrayList<People> peoplelist; 
 56     
 57     public static void main(String[] args) {
 58         peoplelist = new ArrayList<People>();
 59         Scanner scanner = new Scanner(System.in);
 60         File file = new File("D:\身份证号.txt");
 61         try {
 62             FileInputStream files = new FileInputStream(file);
 63             BufferedReader in = new BufferedReader(new InputStreamReader(files));
 64             String temp = null;
 65             while ((temp = in.readLine()) != null) {
 66                 
 67                 String[] information = temp.split("[ ]+");
 68                 People people = new People();
 69                 people.setName(information[0]);
 70                 people.setnumber(information[1]);
 71                 int A = Integer.parseInt(information[3]);
 72                 people.setage(A);
 73                 people.setsex(information[2]);
 74                 for(int j = 4; j<information.length;j++) {
 75                     people.setplace(information[j]);
 76                 }
 77                 peoplelist.add(people);
 78 
 79             }
 80         } catch (FileNotFoundException e) {
 81             System.out.println("文件未找到");
 82             e.printStackTrace();
 83         } catch (IOException e) {
 84             System.out.println("文件读取错误");
 85             e.printStackTrace();
 86         }
 87         boolean isTrue = true;
 88         while (isTrue) {
 89 
 90             System.out.println("******************************************");
 91             System.out.println("   1.按姓名典序输出人员信息");
 92             System.out.println("   2.查询最大年龄人员信息");
 93             System.out.println("   3.查询最小年龄人员信息");
 94             System.out.println("   4.输入你的年龄,查询身份证号.txt中年龄与你最近的人");
 95             System.out.println("   5.查询人员中是否有你的同乡");
 96             System.out.println("   6.退出");
 97             System.out.println("******************************************");
 98             int nextInt = scanner.nextInt();
 99             switch (nextInt) {
100             case 1:
101                 Collections.sort(peoplelist);
102                 System.out.println(peoplelist.toString());
103                 break;
104             case 2:
105                 int max=0;
106                 int j,k1 = 0;
107                 for(int i=1;i<peoplelist.size();i++)
108                 {
109                     j = peoplelist.get(i).getage();
110                    if(j>max)
111                    {
112                        max = j; 
113                        k1 = i;
114                    }
115                   
116                 }  
117                 System.out.println("年龄最大:"+peoplelist.get(k1));
118                 break;
119             case 3:
120                 int min = 100;
121                 int j1,k2 = 0;
122                 for(int i=1;i<peoplelist.size();i++)
123                 {
124                     j1 = peoplelist.get(i).getage();
125                     if(j1<min)
126                     {
127                         min = j1; 
128                         k2 = i;
129                     }
130 
131                  } 
132                 System.out.println("年龄最小:"+peoplelist.get(k2));
133                 break;
134             case 4:
135                 System.out.println("年龄:");
136                 int input_age = scanner.nextInt();
137                 ArrayList<People> plist = new ArrayList<People>();
138                 plist = agenear(input_age);
139                 for(People people : plist) {
140                     System.out.println(people.toString());
141                 }
142                 break;
143             case 5:
144                 System.out.println("请输入省份");
145                 String find = scanner.next();        
146                 for (int i = 0; i <peoplelist.size(); i++) 
147                 {
148                     String [] place = peoplelist.get(i).getplace().split("	");
149                     for(String temp : place) {
150                         if(find.equals(temp)) {
151                             System.out.println("你的同乡是    "+peoplelist.get(i));
152                             break;
153                         }
154                     }
155                     
156                 } 
157                 break;
158             case 6:
159                 isTrue = false;
160                 System.out.println("byebye!");
161                 break;
162             default:
163                 System.out.println("输入有误");
164             }
165         }
166     }
167 
168 }
 1 package IDcard;
 2 
 3 public class People implements Comparable<People> {
 4 
 5     private    String name = null;
 6     private    String number = null;
 7     private    int age = 0;
 8     private    String sex = null;
 9     private    String place = null;
10 
11     public String getName()
12     {
13         return name;
14     }
15     public void setName(String name) 
16     {
17         this.name = name;
18     }
19     public String getnumber() 
20     {
21         return number;
22     }
23     public void setnumber(String number)
24     {
25         this.number = number;
26     }
27     public int getage() 
28     {
29         return age;
30     }
31     public void setage(int age ) 
32     {
33         this.age = age;
34     }
35     public String getsex() 
36     {
37         return sex;
38     }
39     public void setsex(String sex ) 
40     {
41         this.sex = sex;
42     }
43     public String getplace() 
44     {
45         return place;
46     }
47     public void setplace(String place) 
48     {
49         if(this.place == null) {
50             this.place = place;
51         }else {
52             this.place = this.place+ "	" +place;
53         }
54 
55     }
56     public int compareTo(People o)
57     {
58         return this.name.compareTo(o.getName());
59     }
60     public String toString() 
61     {
62         return  name+"	"+sex+"	"+age+"	"+number+"	"+place+"
";
63     }
64 }

实验结果如下:

 

实验4:内部类语法验证实验

实验程序1:

1.编辑、调试运行教材246页-247页程序6-7,结合程序运行结果理解程序;

2.了解内部类的基本用法。

6-7程序如下:

 1 package innerClass;
 2 
 3 import java.awt.*;
 4 import java.awt.event.*;
 5 import java.util.*;
 6 import javax.swing.*;
 7 import javax.swing.Timer;
 8 
 9 /**
10  * This program demonstrates the use of inner classes.
11  * @version 1.11 2015-05-12
12  * @author Cay Horstmann
13  */
14 public class InnerClassTest
15 {
16    public static void main(String[] args)
17    {
18       TalkingClock clock = new TalkingClock(1000, true);
19       clock.start();
20 
21       // keep program running until user selects "Ok"
22       JOptionPane.showMessageDialog(null, "Quit program?");
23       System.exit(0);
24    }
25 }
26 
27 /**
28  * A clock that prints the time in regular intervals.
29  */
30 class TalkingClock
31 {
32    private int interval;
33    private boolean beep;
34 
35    /**
36     * Constructs a talking clock
37     * @param interval the interval between messages (in milliseconds)
38     * @param beep true if the clock should beep
39     */
40    public TalkingClock(int interval, boolean beep)
41    {
42       this.interval = interval;
43       this.beep = beep;
44    }
45 
46    /**
47     * Starts the clock.
48     */
49    public void start()
50    {
51       ActionListener listener = new TimePrinter();
52       Timer t = new Timer(interval, listener);
53       t.start();
54    }
55 
56    public class TimePrinter implements ActionListener
57    {
58       public void actionPerformed(ActionEvent event)
59       {
60          System.out.println("At the tone, the time is " + new Date());
61          if (beep) Toolkit.getDefaultToolkit().beep();
62       }
63    }
64 }

实验结果如下:

实验程序2:

1.编辑、调试运行教材254页程序6-8,结合程序运行结果理解程序;

2.了解匿名内部类的用法。

6-8程序如下:

 1 package anonymousInnerClass;
 2 
 3 import java.awt.*;
 4 import java.awt.event.*;
 5 import java.util.*;
 6 import javax.swing.*;
 7 import javax.swing.Timer;
 8 
 9 /**
10  * This program demonstrates anonymous inner classes.
11  * @version 1.11 2015-05-12
12  * @author Cay Horstmann
13  */
14 public class AnonymousInnerClassTest
15 {
16    public static void main(String[] args)
17    {
18       TalkingClock clock = new TalkingClock();
19       clock.start(1000, true);
20 
21       // keep program running until user selects "Ok"
22       JOptionPane.showMessageDialog(null, "Quit program?");
23       System.exit(0);
24    }
25 }
26 
27 /**
28  * A clock that prints the time in regular intervals.
29  */
30 class TalkingClock
31 {
32    /**
33     * Starts the clock.
34     * @param interval the interval between messages (in milliseconds)
35     * @param beep true if the clock should beep
36     */
37    public void start(int interval, boolean beep)
38    {
39       ActionListener listener = new ActionListener()
40          {
41             public void actionPerformed(ActionEvent event)
42             {
43                System.out.println("At the tone, the time is " + new Date());
44                if (beep) Toolkit.getDefaultToolkit().beep();
45             }
46          };
47       Timer t = new Timer(interval, listener);
48       t.start();
49    }
50 }

实验结果如下:

 

实验程序3:

1.在elipse IDE中调试运行教材257页-258页程序6-9,结合程序运行结果理解程序;

2.了解静态内部类的用法。

6-9程序如下:

 1 package staticInnerClass;
 2 
 3 /**
 4  * This program demonstrates the use of static inner classes.
 5  * @version 1.02 2015-05-12
 6  * @author Cay Horstmann
 7  */
 8 public class StaticInnerClassTest
 9 {
10    public static void main(String[] args)
11    {
12       double[] d = new double[20];
13       for (int i = 0; i < d.length; i++)
14          d[i] = 100 * Math.random();
15       ArrayAlg.Pair p = ArrayAlg.minmax(d);
16       System.out.println("min = " + p.getFirst());
17       System.out.println("max = " + p.getSecond());
18    }
19 }
20 
21 class ArrayAlg
22 {
23    /**
24     * A pair of floating-point numbers
25     */
26    public static class Pair
27    {
28       private double first;
29       private double second;
30 
31       /**
32        * Constructs a pair from two floating-point numbers
33        * @param f the first number
34        * @param s the second number
35        */
36       public Pair(double f, double s)
37       {
38          first = f;
39          second = s;
40       }
41 
42       /**
43        * Returns the first number of the pair
44        * @return the first number
45        */
46       public double getFirst()
47       {
48          return first;
49       }
50 
51       /**
52        * Returns the second number of the pair
53        * @return the second number
54        */
55       public double getSecond()
56       {
57          return second;
58       }
59    }
60 
61    /**
62     * Computes both the minimum and the maximum of an array
63     * @param values an array of floating-point numbers
64     * @return a pair whose first element is the minimum and whose second element
65     * is the maximum
66     */
67    public static Pair minmax(double[] values)
68    {
69       double min = Double.POSITIVE_INFINITY;
70       double max = Double.NEGATIVE_INFINITY;
71       for (double v : values)
72       {
73          if (min > v) min = v;
74          if (max < v) max = v;
75       }
76       return new Pair(min, max);
77    }
78 }

实验结果如下:

 第三部分:总结

   通过本周的学习我收益很多,比如,知道了接口和继承之间的区别,object类的clone方法等,并在助教学长的指导下改善了上周测试题中的不足。本周的实验主要还是以理解课本中的代码案例为主,老师为了节省我们的时间,自主完成实验也只是在以前实验的基础上添加了新的知识内容在课后探讨学习中助教学长给我们详细讲解了不理解的地方,拓展了我们的视野,展示了Java做工程的强大能力。更加加深了我们对Java的学习兴趣。

原文地址:https://www.cnblogs.com/hackerZT-7/p/9811837.html