201871010123-吴丽丽《面向对象程序设计(Java)》第八周学习总结
项目 | 内容 |
这个作业属于哪个课程 | http://www.cnblogs.com/nwnu-daizh/ |
这个作业要求在哪里 | https://www.cnblogs.com/nwnu-daizh/p/11703678.html |
作业的学习目标 |
|
第一部分:理论部分
第六章 接口、lambda表达式与内部类
6.1.1接口
1) Java为了克服单继承的缺点,Java使用了接口,一个类可以实现一个或多个接口。
2) 在Java程序设计语言中,接口不是类,而是对类的一组需求描述,由常量和一组抽象方法组成。
3) 接口中不包括变量和有具体实现的方法。
4) 只要类实现了接口,则该类要遵从接口描述的统一格式进行定义,并且可以在任何需要该接口的地方使用这个类的对象.
5)声明方式: public interface 接口名 { …… } 接口体中包含常量定义和方法定义,接口中只进行方法的声明,不提供方法的实现。
6)类似建立类的继承关系,接口也可以扩展。 接口的扩展技术使得从具有较高通用性的接口存在多条链延伸到具有较高专用性的接口。
扩展方法: public interface 接口1 extends 接口2 { …… }
说明:
a)通常接口的名字以able或ible结尾;
b)可以使用extends来继承接口的常量和抽象方法,扩展形成新的接口;
c)接口中的所有常量必须是public static final,方法必须是public abstract,这是系统默认的,不管你在定义接口时,写不写修饰符都是一样的
7)在类声明时用implements关键字声明使用一个或多个接口
class Employee implements Printable { …… }
一个类使用了某个接口,那么这个类必须实现该接口的所有方法,即为这些方法提供方法体。
一个类可以实现多个接口,接口间应该用逗号分隔开。
class Employee implements Cloneable,Comparable
说明:
a)若实现接口的类不是抽象类,则必须实现所有接口的所有方法,即为所有抽象方法定义方法体
b)一个类在实现某接口抽象方法时,必须使用完全相同的方法名、参数列表和返回值类型
c)接口抽象方法的访问控制符已指定为public,所以在类的实现时,必须显示地使用public修饰符,否则被警告缩小了接口中定义的方法的访问控制范围。
8)接口不能构造接口对象,但可以声明接口变量以指向一个实现了该接口的类对象。
Comparablex = new Comparable(…); //ERROR
Comparable x= new Employee(…); //OK
9) 可以用instanceof检查对象是否实现了某个接口。
if (anObject instanceof Comparable) { ……}
6.1.2 接口与抽象类
1)抽象类:用abstract来声明,没有具体实例对象的类,不能用new来创建对象。可包含常规类所包含的任何东西。
抽象类必须由子类继承,如果abstract类的子类不是抽象类,那么子类必须重写父类中所有的abstract方法。
2)接口:用interfaces声明,是抽象方法和常量值定义的集合。从本质上讲,接口是一个特殊的抽象类,这种抽象类中只包含
常量和方法的定义,而没有变量和方法的定义。接口中只能定义抽象方法,而且这些方法默认为public的。只有类实
现了接口,就可以在任何需要该接口的地方使用这个类的对象。此外,一个类可以实现多个接口。
接口与抽象类的区别:
(1)接口不能实现任何方法,而抽象类可以。
(2)类可以实现许多接口,但只有一个父类。
(3)接口不是类分级结构的一部分,无任何联系的类可以实现相同的接口。
6.2.1 接口实例
1)回调(callback):一种程序设计模式,在这种模式中,可指出某个特定事件发生时程序应该采取的动作。 在java.swing包中有一个Timer类,
可以使用它在到达给定的时间间隔时触发一个事件。
-Timer(intinterval, ActionListenerlistener)
- void start()
-void stop()
2)Comparator接口
a)所在包:java.util.*
b)Comparator接口定义
public interface Comparator<T>{
int compare(T o1,T o2);
......
}
c)用途一:处理字符串按长度进行排序操作
3) Object类的Clone方法
a)当拷贝一个对象变量时,原始变量与拷贝变量引用同一个对象。这样,改变一个变量所引用的对象会对另一个变量产生影响。
b)如果要创建一个对象新的copy,它的最初状态与 original一样,但以后可以各自改变状态,就需要使用Object类的clone方法。
c)Object类的clone()方法是一个native方法。
d)Object类中的clone()方法被protected()修饰符修饰。这意味着在用户编写的代码中不能直接调用它。如果要直接应用clone()方法,
就需覆盖clone()方法,并要把clone()方法的属性设置为public。
e)Object.clone()方法返回一个Object对象。必须进行强制类型转换才能得到所需要的类型。
4)浅层拷贝:被拷贝对象的所有常量成员和基本类型属性都有与原来对象相同的拷贝值,而若成员域是一个对象,则被拷贝对象该对象域的对象引用仍然指向原来的对象。
5)深层拷贝:被拷贝对象的所有成员域都含有与原 来对象相同的值,且对象域将指向被复制过的新对 象,而不是原有对象被引用的对象。换言之,深层拷贝将拷贝对象内引用的对象也拷贝一遍。
6.2.2 Java中对象克隆的实现
1)在子类中实现Cloneable接口
2)为了获得对象的一份拷贝,可以利用Object类的clone方法。
3)在子类中覆盖超类的clone方法,声明为public。
4)在子类的clone方法中,调用super.clone()。
6.3 Lambda表达式
1)Java Lambda表达式是Java 8引入的一个新的功能,主要用途是提供一个函数化的语法来简化编码。
2)Lambda表达式本质上是一个匿名方法。
public intadd(intx, inty) {
return x + y; }
转成Lambda表达式后是:
(intx, inty) -> x + y;
参数类型也可以省略,java编译器会根据上下文推断出来
3)Lambda表达式的语法基本结构
(arguments)->body
有如下几种情况:
a)参数类型可推导时,不需要指定类型,如
(a)->System.out.println(a)
b)只有一个参数且类型可推导时,不强制写(),如
a->System.out.println(a)
c)参数指定类型时,必须有括号,如(int a)->System.out.println(a)
d)参数可以为空,如()->System.out.println("hello")
e)body需要用{}包含语句,当只有一条语句时{}可省略
4)函数式接口Functionallnterface
a)Java Lambda表达式以函数式接口为应用基础
b)函数式接口(Functionallnterface)
只有一个方法的接口,这类接口的目的是为了一个单一的操作。常见的接口如:ActionListener,Runnable,Comparator都是函数式接口,并且都
标注了注解@Functionallnterface.
c)函数式接口用作表示lambda表达式的类型。
6.4.1 内部类
1)内部类(inner class)是定义在一个类内部的类。
2)外层的类成为外部类(outer class).
3)内部类主要用于事件处理。 使用内部类的原因有以下三个:
a)内部类方法可以访问该类定义所在的作用域中的数据,包括私有数据。
b)内部类能够隐藏起来,不为同一包中的其他类所见。
c)想要定义一个回调函数且不想编写大量代码时, 使用匿名内部类比较便捷。
4)内部类的声明
内部类的声明格式如下:
[修饰符] class outerClass{
...
[修饰符]class innerClass{
...
}
...
}
5)内部类可以直接访问外部类的成员,包括private成员,但是内部类的成员却不能被外部类直接访问。内部类中加上修饰符访问外部类中的同名域。
6.4.2局部内部类
1)内部类并非只能在类内定义,也可以在程序块内定义局部内部类。例如,在方法中,甚至在for循环体内部。
2)局部内部类不能用public或private访问修饰符进行声明,它的作用域被限定在声明这个局部类的块中。
3)局部内部类可以访问方法中的final类型的局部变量。
6.4.3 匿名内部类
1)若只创建类的一个对象,则不该为该类命名,这种类称为匿名内部类。
2)由于匿名类没有类名,所以匿名类不能有构造器,取而代之的是将构造器参数传递给超类的构造器。
3)若匿名内部类实现接口时,则匿名内部类不能有任何构造参数。
4)如果构造参数的闭圆括号跟一个开花括号,表明正在定义的就是匿名内部类。
6.4.4 静态内部类
1)如果用static修饰一个内部类,这个类就相当于是一个外部定义的类,所以static的内部类中可以声明static成员,但非static的内部类中的成员不能声明为static的。
static的内部类不能再使用外部类的非static的成员变量。
2)static内部类很少使用。
第二部分:实验部分——接口的定义与使用
1、实验目的与要求
(1) 掌握接口定义方法;
(2)掌握实现接口类的定义要求;
(3)掌握实现了接口类的使用要求;
(4)掌握程序回调设计程序;
(5) 掌握Comparator接口用法;
(6) 掌握对象浅层拷贝与深层拷贝方法;
(7) 掌握Lambda表达式语法;
(8) 了解内部类的用途及语法要求。
2、实验内容和步骤
实验1: 导入第6章示例程序,测试程序并进行代码注释。
测试程序1:
a)编辑、编译、调试运行阅读教材214页-215页程序6-1、6-2,理解程序并分析程序运行结果;
b) 在程序中相关代码处添加新知识的注释。
c)掌握接口的实现用法;
d)掌握内置接口Compareable的用法。
EmployeeSortTest代码如下:
package interfaces; import java.util.*; /** * This program demonstrates the use of the Comparable interface. * @version 1.30 2004-02-27 * @author Cay Horstmann */ public class EmployeeSortTest { public static void main(String[] args) { var staff = new Employee[3]; staff[0] = new Employee("Harry Hacker", 35000); staff[1] = new Employee("Carl Cracker", 75000); staff[2] = new Employee("Tony Tester", 38000); Arrays.sort(staff); //使用Arrays中的sort方法对Employee对象数组进行排序 // print out information about all Employee objects for (Employee e : staff) System.out.println("name=" + e.getName() + ",salary=" + e.getSalary()); } }
Employee类代码如下:
package interfaces; public class Employee implements Comparable<Employee> // Employee类实现Comparable接口 { private String name; private double salary; public Employee(String name, double salary) { this.name = name; this.salary = salary; } public String getName() { return name; } public double getSalary() { return salary; } public void raiseSalary(double byPercent) { double raise = salary * byPercent / 100; salary += raise; } /** * Compares employees by salary * * @param other another Employee object * @return a negative value if this employee has a lower salary than * otherObject, 0 if the salaries are the same, a positive value * otherwise */ public int compareTo(Employee other) //重写接口的compareTo方法 { return Double.compare(salary, other.salary);//按照他们的工资高低进行排序 } }
程序运行结果如下:
实现小结:
1)任何实现Comparable接口的类都需要包含compareTo方法,并且这个方法的参数必须是一个Object对象,返回一个整型数值。
2)为了让类实现一个接口,通常需要以下两个步骤:
a)将类声明为实现给定的接口
b)对接口中的所有方法进行定义
测试程序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:
a)在elipse IDE中调试运行教材223页6-3,结合程序运行结果理解程序;
b)26行、36行代码参阅224页,详细内容涉及教材12章。
c)在程序中相关代码处添加新知识的注释。
d)掌握回调程序设计模式;
程序代码如下:
package timer; /** @version 1.02 2017-12-14 @author Cay Horstmann */ import java.awt.*; import java.awt.event.*; import java.time.*; import javax.swing.*; public class TimerTest { public static void main(String[] args) { var listener = new TimePrinter(); //构建一个新的对象 // construct a timer that calls the listener // once every second var timer = new Timer(1000, listener); //构建类的一个对象,并将它传递给Timer构造器 timer.start(); //调用timer的start方法 // keep program running until the user selects "OK" 保持一个对话框直到使用者选择OK JOptionPane.showMessageDialog(null, "Quit program?"); System.exit(0); } } class TimePrinter implements ActionListener //TimePrinter类实现ActionListener接口 { public void actionPerformed(ActionEvent event) //重写接口的方法 { System.out.println("At the tone, the time is " + Instant.ofEpochMilli(event.getWhen())); Toolkit.getDefaultToolkit().beep(); //获得默认的工具箱 } }
程序运行结果如下:
按确定后其程序才结束
实验小结:
1)回调(callback)是一种常见的程序设计模式。在这种模式中,可以指定某个特定事件发生时应该采取的动作。
2)在该实验中该程序构造定时器,其设置了一个时间间隔,并告知定时器,当到达时间间隔时需要做什么操作。
3)如何告知定时器做什么?在很多程序设计语言中,可以提供一个函数名,定时器周期性地调用它。但是,在java标准库中的类采用面向对象方法,它将某个类的对象传递给定时器,然后定时器调用这个对象的方法。传递一个对象比传递一个函数要灵活的多。
4)Timer构造器的第一个参数是发出通告的时间间隔,它的单位是毫秒。第二个参数是监听器对象。
测试程序4:
a)调试运行教材229页-231页程序6-4、6-5,结合程序运行结果理解程序;
b)在程序中相关代码处添加新知识的注释。
c)掌握对象克隆实现技术;
d)掌握浅拷贝和深拷贝的差别。
CloneTest代码如下:
package clone; /** * This program demonstrates cloning. * @version 1.11 2018-03-16 * @author Cay Horstmann */ public class CloneTest { public static void main(String[] args) throws CloneNotSupportedException { var original = new Employee("John Q. Public", 50000); //创建一个对象并进行初始化 original.setHireDay(2000, 1, 1); Employee copy = original.clone(); //对象克隆 copy.raiseSalary(10); copy.setHireDay(2002, 12, 31); System.out.println("original=" + original); System.out.println("copy=" + copy); } }
Employee类代码如下:
1 package clone; 2 3 import java.util.Date; 4 import java.util.GregorianCalendar; 5 //创建深拷贝的clone方法的一个例子 6 public class Employee implements Cloneable //Employee类实现接口 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 //允许所有方法克隆对象 20 public Employee clone() throws CloneNotSupportedException //将clone重新定义为public 21 { 22 // call Object.clone() 23 Employee cloned = (Employee) super.clone(); //为clone方法指定正确的返回类型 24 25 // clone mutable fields 26 cloned.hireDay = (Date) hireDay.clone(); //克隆对象中可变的实例域 27 28 return cloned; 29 } 30 31 /** 32 * Set the hire day to a given date. 33 * @param year the year of the hire day 34 * @param month the month of the hire day 35 * @param day the day of the hire day 36 */ 37 public void setHireDay(int year, int month, int day) 38 { 39 Date newHireDay = new GregorianCalendar(year, month - 1, day).getTime(); 40 41 // example of instance field mutation 42 hireDay.setTime(newHireDay.getTime()); 43 } 44 45 public void raiseSalary(double byPercent) 46 { 47 double raise = salary * byPercent / 100; 48 salary += raise; 49 } 50 51 public String toString() //toString方法 52 { 53 return "Employee[name=" + name + ",salary=" + salary + ",hireDay=" + hireDay + "]"; 54 } 55 }
运行结果如下:
实验小结:
克隆时,对于每一个类,需要确定:
1)默认的clone方法是否满足要求;
2)是否可以在可变的子对象上调用clone来修补默认的clone方法;
3)是否不该使用clone
实际上第3个选项是默认的选项。如果选择第1项或者第2项,类必须:
1)实现Cloneable接口;
2)重新定义clone方法,并指定public访问修饰符。
3)默认的克隆操作是“浅拷贝”,并没有克隆对象中引用的其他对象。
4)重新定义clone方法来建立一个深拷贝,同时克隆所有子对象。
实验2: 导入第6章示例程序6-6,学习Lambda表达式用法。
1) 调试运行教材233页-234页程序6-6,结合程序运行结果理解程序;
2)在程序中相关代码处添加新知识的注释。
3)将27-29行代码与教材223页程序对比,将27-29行代码与此程序对比,体会Lambda表达式的优点。
LambdaTest代码如下:
package lambda; import java.util.*; import javax.swing.*; import javax.swing.Timer; /** * This program demonstrates the use of lambda expressions. * @version 1.0 2015-05-12 * @author Cay Horstmann */ public class LambdaTest { public static void main(String[] args) { var planets = new String[] { "Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune" }; //构建一个数组并进行初始化 System.out.println(Arrays.toString(planets)); System.out.println("Sorted in dictionary order:"); Arrays.sort(planets); //调用Arrays的sort方法 System.out.println(Arrays.toString(planets)); System.out.println("Sorted by length:"); Arrays.sort(planets, (first, second) -> first.length() - second.length()); //Lambda表达式 System.out.println(Arrays.toString(planets)); //Lambda表达式转化为接口 var timer = new Timer(1000, event -> System.out.println("The time is " + new Date())); timer.start(); // keep program running until user selects "OK" JOptionPane.showMessageDialog(null, "Quit program?"); System.exit(0); } }
代码运行结果如下:
实验小结:
1)Lambda表达式是一个可传递的代码块,可以在以后执行一次或者多次
2)Lambda表达式可以转换为接口,使用Lambda表达式要比之前使用传统的内联类相比,高效的多。
实验3: 编程练习
1) 编制一个程序,将身份证号.txt 中的信息读入到内存中;
2)按姓名字典序输出人员信息;
3)查询最大年龄的人员信息;
4)查询最小年龄人员信息;
5)输入你的年龄,查询身份证号.txt中年龄与你最近人的姓名、身份证号、年龄、性别和出生地;
6)查询人员中是否有你的同乡。
所写的代码如下:
1 package peopleId; 2 3 4 import java.io.BufferedReader; 5 import java.io.File; 6 import java.io.FileInputStream; 7 import java.io.FileNotFoundException; 8 import java.io.IOException; 9 import java.io.InputStreamReader; 10 import java.util.ArrayList; 11 import java.util.Scanner; 12 import java.util.Collections; 13 14 public class Main { 15 16 public static People findPeopleByname(String name) { 17 People flag = null; 18 for (People people : peoplelist) { 19 if(people.getName().equals(name)) { 20 flag = people; 21 } 22 } 23 return flag; 24 25 } 26 27 public static People findPeopleByid(String id) { 28 People flag = null; 29 for (People people : peoplelist) { 30 if(people.getnumber().equals(id)) { 31 flag = people; 32 } 33 } 34 return flag; 35 36 } 37 38 private static ArrayList<People> agenear(int yourage) { 39 // TODO Auto-generated method stub 40 int j=0,min=53,d_value,k = 0; 41 ArrayList<People> plist = new ArrayList<People>(); 42 for (int i = 0; i < peoplelist.size(); i++) { 43 d_value = peoplelist.get(i).getage() > yourage ? 44 peoplelist.get(i).getage() - yourage : yourage - peoplelist.get(i).getage() ; 45 k = d_value < min ? i : k; 46 min = d_value < min ? d_value : min; 47 } 48 for(People people : peoplelist) { 49 if(people.getage() == peoplelist.get(k).getage()) { 50 plist.add(people); 51 } 52 } 53 return plist; 54 } 55 56 private static ArrayList<People> peoplelist; 57 58 public static void main(String[] args) { 59 peoplelist = new ArrayList<People>(); 60 Scanner scanner = new Scanner(System.in); 61 File file = new File("D:\\身份证号.txt"); 62 try { 63 FileInputStream files = new FileInputStream(file); 64 BufferedReader in = new BufferedReader(new InputStreamReader(files)); 65 String temp = null; 66 while ((temp = in.readLine()) != null) { 67 68 String[] information = temp.split("[ ]+"); 69 People people = new People(); 70 people.setName(information[0]); 71 people.setnumber(information[1]); 72 int A = Integer.parseInt(information[3]); 73 people.setage(A); 74 people.setsex(information[2]); 75 for(int j = 4; j<information.length;j++) { 76 people.setplace(information[j]); 77 } 78 peoplelist.add(people); 79 80 } 81 } catch (FileNotFoundException e) { 82 System.out.println("文件未找到"); 83 e.printStackTrace(); 84 } catch (IOException e) { 85 System.out.println("文件读取错误"); 86 e.printStackTrace(); 87 } 88 boolean isTrue = true; 89 while (isTrue) { 90 91 92 System.out.println(" 1.按姓名字典序输出人员信息"); 93 System.out.println(" 2.查询最大年龄人员信息"); 94 System.out.println(" 3.查询最小年龄人员信息"); 95 System.out.println(" 4.输入你的年龄,查询身份证号.txt中年龄与你最近的人的姓名、身份证号、年龄、性别和出生地"); 96 System.out.println(" 5.查询人员中是否有你的同乡"); 97 System.out.println(" 6.退出"); 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("\t"); 149 for(String temp : place) { 150 if(find.equals(temp)) { 151 System.out.println("你的同乡是 "+peoplelist.get(i)); 152 break; 153 } 154 155 156 157 158 } 159 160 } 161 break; 162 case 6: 163 isTrue = false; 164 System.out.println("Goodbye!"); 165 break; 166 default: 167 System.out.println("输入有误"); 168 } 169 } 170 } 171 172 }
People类实现接口的代码如下:
1 package peopleId; 2 3 public class People implements Comparable<People> { 4 5 private String name; 6 private String number; 7 private int age; 8 private String sex; 9 private String place; 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+ "\t" +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+"\t"+sex+"\t"+age+"\t"+number+"\t"+place+"\n"; 63 } 64 }
程序结果运行如下:
实验总结:通过本周的学习我收益很多,比如,知道了接口和继承之间的区别,object类的clone方法以及lambda表达式等。本周的实验主要还是以理解课本中的代码案例为主,老师为了节省我们的时间,自主完成实验虽然只是在以前实验的基础上添加了新的知识内容,但是自己在完成编程设计时还是不太熟悉,设计起来比较艰难。
原文地址:https://www.cnblogs.com/lily-2018/p/11700645.html