吴丽丽-201871010123 《面向对象程序设计(java)》第六、七周学习总结
吴丽丽-201871010123 《面向对象程序设计(java)》第六、七周学习总结
项目 | 内容 |
这个作业属于哪个课程 | https://www.cnblogs.com/nwnu-daizh/ |
这个作业要求在哪里 |
https://www.cnblogs.com/nwnu-daizh/p/11605051.html |
作业的学习目标 |
|
第一部分:理论知识部分
第五章 继承(inheritance)
5.1 类、超类和子类
5.2 Object:所有类的超类
5.3 泛型数组列表
5.4 对象包装器和自动打包
5.5 参数变量可变的方法
5.6 枚举类
5.7 继承设计的技巧
5.1 类、超类和子类
继承的特点:具有层次结构;子类继承了父类的域和方法。
a)类继承的格式:
class 新类名 extends 已有类名
b)已有类称为:超类(superclass)、基类(base class) 或父类(parent class)
新类称作:子类(subclass)、派生类(derived class)或孩子类(child class)
c)一般来说,子类比超类拥有的功能更丰富。
d)super是一个指示编译器调用超类方法的特有关键字,它不是一个对象的引用,不能将super赋给另一个对象变量。
e) super关键字一般有两个用途:一是调用超类的方法(格式:super.方法名()),二是调用超类的构造器(格式:super() )。
f)从一个超类扩展而来的类集合称为继承层次(inheritance hierarchy)。在继承层次中,某个类到其祖先的路径被称为该类的继承链(inheritance chain)。
注意:Java不支持多继承
g)多态性:多态性泛指在程序中同一个符号在不同的情况下具有不同解释的现象。
java中,对象变量是多态的
h) 不允许继承的类称为final类,在类的定义中用final修饰符加以说明,String类是final类的一个例子。不能扩展该类。
I)抽象类:包含一个或多个抽象方法的类必须声明为抽象类
抽象方法充当着占位的角色,它们的具体实现在子类中。
抽象类不能被实例化,即不能创建对象,只能产生子类。
继承小结
1)封装、继承、多态是面向对象的主要特征。
2)继承可以提高代码重用性,用extends关键字来实现。除构造方法之外,父类的所有方法和属性都被子类继承。
3)继承建立了类与类间的关系,同时是多态特征的前提。
4)java只支持单继承,不直接支持多继承(避免两个父类出现同名方法的调用选择困难)
5)abstract修饰的抽象类不能实例化为对象,只能扩展子类;抽象类中的抽象方法充当着占位的角色,它们的具体实现在子类中。
6)final类不允许继承;类中final方法不允许被子类重写。
5.2 Object:所有类的超类
a)Object类是Java中所有类的祖先——每一个类都由它扩展而来。在不给出超类的情况下,Java会自动把Object 作为要定义类的超类。
b)可以使用类型为Object的变量指向任意类型的对象。但要对它们进行专门的操作都要进行类型转换。
c)Object类中的equals方法用于测试某个对象是否同另一个对象相等。它在Object类中的实现是判断两个对象是否具有相同的引用。如果两个对象具有相同的引用,它们一定是相等的。
d)定义子类的equals方法时,可调用超类的equals方法。
super.equals(otherObject)
e) Object类中的hashCode方法导出某个对象的散列码。散列码是任意整数,表示对象的存储地址。
两个相等对象的散列码相等。
f)Object类的toString方法返回一个代表该对象域值的字符串。定义子类的toString方法时,可先调用超类的toString方法。如:super.toString()
5.3 泛型数组列表
a) Java中,利用ArrayList类,可允许程序在运行时确定数组的大小。
b)ArryList是一个采用类型参数的泛型类。为指定数组列表保存元素的对象类型,需要用一对尖括号将数组元素的对象类名括起来加在后面。
ArryList<Employee> staff=new ArrayList<Employee>();
c)没有<>的ArrayList将被认为是一个删去了类型参数的“原始”类型。
d)a.ArrayList定义
>ArrayList <T> 对象 = new ArrayList<T>();
>API : ArrayList的构造器
ArrayList<T>()构造一个空数组列表
ArrayList<T>(int initialCapacity)构造一个具有指定容量的空数组列表
5.4 对象包装器和自动打包
a)所有基本数据类型都有着与之对应的预定义类,它们被称为对象包装器。
b)对象包装器类是不可变的,即一旦构造了包装器,就不允许更改包装在其中的值。且对象包装器类还是final,因此不能定义它们的子类。
c)使用对象包装器的好处:
- 基本类型转化为对象
- 定义了一些有用的基本方法(static方法)
d)在JavaSE5.0中,可以自动的将基本数据类型转换为包装器类的对象,将这种变换称为自动打包
e)相反的,当对一个包装器类的对象进行赋值或算法运算时,将会自动地拆包。
f)打包和拆包是编译器认可的。
5.5 参数变量可变的方法
a)在Java SE 5.0以前的版本中,每个Java方法都有固定数量的参数。然而,现在的版本提供了可以用可变的参数数量调用的方法(称为“可变参 ”方法)。
b 用户自己可以定义可变参数的方法,并将参数指定为任意类型,甚至是基本类型。
5.6 枚举类
a)声明枚举类
public enum Grade{A,B,C,D,E};
它包括一个关键字enum,一个新枚举类型的名字 Grade以及为Grade定义的一组值,这里的值既非整型,亦非字符型。
b)枚举类是一个类,它的隐含超类是java.lang.Enum。
c)枚举值并不是整数或其它类型,是被声明的枚举类的自身实例,例如A是Grade的一个实例
d)枚举类不能有public修饰的构造函数,构造函数都是隐含private,编译器自动处理。
e)枚举值隐含都是由public、static、final修饰的,无须自己添加这些修饰符。
f)在比较两个枚举类型的值时i,永远不需要调用equals方法,直接使用“==”进行相等比较。
5.7 继承设计的技巧
a) 将公共操作和域放在超类。
b)不要使用受保护的域。
c)使用继承实现“is-a”关系。
d)除非所有继承的方法都有意义,否则就不要使用继承。
e)在覆盖方法时,不要改变预期的行为。
f)使用多态,而非类型信息。
第二部分 :实验部分
1、实验目的与要求
(1) 理解继承的定义;
(2) 掌握子类的定义要求
(3) 掌握多态性的概念及用法;
(4) 掌握抽象类的定义及用途。
2、实验内容和步骤
实验1: 导入第5章示例程序,测试并进行代码注释。
测试程序1:
在elipse IDE中编辑、调试、运行程序5-1 —5-3(教材152页-153页) ;
1)掌握子类的定义及用法;
2)结合程序运行结果,理解并总结OO风格程序构造特点,理解Employee和Manager类的关系子类的用途,并在代码中添加注释;
3)删除程序中Manager类、ManagerTest类,背录删除类的程序代码,在代码录入中理解父类与子类的关系和使用特点。
程序5-1代码如下:
package inheritance; /** * This program demonstrates inheritance. * @version 1.21 2004-02-21 * @author Cay Horstmann */ public class ManagerTest { public static void main(String[] args) { // 构建管理者对象 var boss = new Manager("Carl Cracker", 80000, 1987, 12, 15); boss.setBonus(5000); var staff = new Employee[3]; // 用管理者和雇员对象填充工作人员数组 staff[0] = boss; staff[1] = new Employee("Harry Hacker", 50000, 1989, 10, 1); staff[2] = new Employee("Tommy Tester", 40000, 1990, 3, 15); // 打印所有员工对象的信息 for (Employee e : staff) System.out.println("name=" + e.getName() + ",salary=" + e.getSalary()); } }
运行结果如下:
程序5-2Employee类代码如下:
package inheritance; import java.time.*; public class Employee { //构建三个私有对象 private String name; private double salary; private LocalDate hireDay; public Employee(String name, double salary, int year, int month, int day)//构造器 { this.name = name; this.salary = salary; hireDay = LocalDate.of(year, month, day); } public String getName() { return name; } public double getSalary() { return salary; } public LocalDate getHireDay() { return hireDay; } public void raiseSalary(double byPercent) { double raise = salary * byPercent / 100; salary += raise; } }
程序5-3代码如下:
package inheritance; public class Manager extends Employee //关键字extends表示继承。表明正在构造一个新类派生于一个已经存在的类。 { private double bonus; //Manager的私有域 /** * @param name the employee's name * @param salary the salary * @param year the hire year * @param month the hire month * @param day the hire day */ public Manager(String name, double salary, int year, int month, int day) { //super调用构造器的语句必须是子类构造器的第一条语句。 super(name, salary, year, month, day); //调用超类中含有name,salary,year,month,day参数的构造器。 bonus = 0; } public double getSalary() { //子类要想访问要想访问超类中的方法需要使用特定的关键字super double baseSalary = super.getSalary(); return baseSalary + bonus; } public void setBonus(double b) { bonus = b; } }
背录删除类override后的程序代码如下:
package inheritance; public class Manager extends Employee //关键字extends表示继承。表明正在构造一个新类派生于一个已经存在的类。 { private int bouns; public Manager(String name, double salary, int year, int month, int day) { super(name, salary, year, month, day); // TODO Auto-generated constructor stub bouns=0; } public int getBouns() { return bouns; } public void setBouns(int bouns) { this.bouns = bouns; } @Override public double getSalary() { // TODO Auto-generated method stub return super.getSalary(); } }
在构造类时,可以在声明的时候用父类,在具体的构造器时,用子类。新生成的对象是父类类型的对象,也就是说这个对象中目前只有父类的属性和方法,但是,如果子类重写,就应该用子类重写的方法。
对于面向对象程序构造特点,我觉得大体如下:
a)封装:就是把一部分或全部属性和部分功能(函数)对外界屏蔽。
封装有两方面的含义:一是将有关数据和操作代码封装在对象当中,形成一个基本单位,各个对象之间相对独立、互不干扰。
二是将对象中某些属性和操作私有化,以达到数据和操作信息隐蔽,有利于数据安全,防止无关人员修改。
b)继承:是为了代码复用,把重复使用的代码精简掉。至于如何精简,当一个类中已经有了相应的属性和操作的代码,而另一个类当中也需要写重复的代码,那么就用继承方法,把前面的类当成父类,后面的类当成子类,子类继承父类。用一个关键字extends就完成了代码的复用。
c)多态:没有继承就没有多态,继承是多态的前提。虽然继承自同一父类,但是相应的操作却各不相同,这叫多态。由继承而产生的不同的派生类,其对象对同一消息会做出不同的响应。
实验1:测试程序2
a)编辑、编译、调试运行教材PersonTest程序(教材163页-165页);
b)掌握超类的定义及其使用要求;
c)掌握利用超类扩展子类的要求;
d)在程序中相关代码处添加新知识的注释;
e) 删除程序中Person类、PersonTest类,背录删除类的程序代码,在代码录入中理解抽象类与子类的关系和使用特点。
程序5-2代码如下:
package abstractClasses; /** * This program demonstrates abstract classes. * @version 1.01 2004-02-21 * @author Cay Horstmann */ public class PersonTest { public static void main(String[] args) { var people = new Person[2]; // 抽象类的声明,但不能将抽象类实例化 ,实例化的是Person类的子类 // fill the people array with Student and Employee objects people[0] = new Employee("Harry Hacker", 50000, 1989, 10, 1); people[1] = new Student("Maria Morris", "computer science"); // print out names and descriptions of all Person objects for (Person p : people) //for each循环 System.out.println(p.getName() + ", " + p.getDescription()); } }
Person类代码如下:
package abstractClasses; public abstract class Person { //包含一个或多个抽象方法的类被称为抽象类,由abstract关键字修饰 //通用的作用域和方法也放到了这里,抽象类不能被实例化,但可以被声明 public abstract String getDescription(); private String name; public Person(String name) { this.name = name; } public String getName() { return name; } }
Student类的代码如下:
package abstractClasses; public class Student extends Person //子类Student继承超类Person { private String major; //Student类的私有域 /** * @param name the student's name * @param major the student's major */ public Student(String name, String major) //子类构造器 { // pass name to superclass constructor super(name); this.major = major; } public String getDescription() { return "a student majoring in " + major; } }
程序5-6Employee类代码如下:
package abstractClasses; import java.time.*; public class Employee extends Person //子类Employee继承父类Person { private double salary; //Employee的私有域 private LocalDate hireDay; public Employee(String name, double salary, int year, int month, int day) //子类构造器 { super(name); //调用父类构造器 this.salary = salary; hireDay = LocalDate.of(year, month, day); } public double getSalary() { return salary; } public LocalDate getHireDay() { return hireDay; } public String getDescription() { return String.format("an employee with a salary of $%.2f", salary); } public void raiseSalary(double byPercent) { double raise = salary * byPercent / 100; salary += raise; } }
程序结果运行如下:
删除后override的代码如下:
package abstractClasses; import java.time.*; public class Employee extends Person { public Employee(String name) { super(name); // TODO Auto-generated constructor stub } private double salary; private LocalDate hireDay; @Override public String getDescription() { // TODO Auto-generated method stub return String.format("an employee with a salary of $%.2f", salary); } @Override protected Object clone() throws CloneNotSupportedException { // TODO Auto-generated method stub return super.clone(); } @Override public boolean equals(Object obj) { // TODO Auto-generated method stub return super.equals(obj); } @Override protected void finalize() throws Throwable { // TODO Auto-generated method stub super.finalize(); } @Override public int hashCode() { // TODO Auto-generated method stub return super.hashCode(); } @Override public String toString() { // TODO Auto-generated method stub return super.toString(); } public double getSalary() { return salary; } public void setSalary(double salary) { this.salary = salary; } public LocalDate getHireDay() { return hireDay; } public void setHireDay(LocalDate hireDay) { this.hireDay = hireDay; } }
抽象类与子类的关系及使用特点:
a)abstract方法只能声明,不能实现
b)包含一个或多个抽象方法的类本身必须被声明为抽象类
c)抽象方法充当着占位的角色,它们的具体实现在子类中
d)抽象类不能被实例化,即不能创建对象,只能产生子类。
实验1 测试程序3:
a)编辑、编译、调试运行教材程序5-8、5-9、5-10,结合程序运行结果理解程序(教材174页-177页);
b)掌握Object类的定义及用法;
c)在程序中相关代码处添加新知识的注释。
程序5-8代码如下:
package equals; /** * This program demonstrates the equals method. * @version 1.12 2012-01-26 * @author Cay Horstmann */ public class EqualsTest { public static void main(String[] args) { var alice1 = new Employee("Alice Adams", 75000, 1987, 12, 15); //创建对象,并初始化 var alice2 = alice1; var alice3 = new Employee("Alice Adams", 75000, 1987, 12, 15); var bob = new Employee("Bob Brandson", 50000, 1989, 10, 1); System.out.println("alice1 == alice2: " + (alice1 == alice2)); System.out.println("alice1 == alice3: " + (alice1 == alice3)); System.out.println("alice1.equals(alice3): " + alice1.equals(alice3)); System.out.println("alice1.equals(bob): " + alice1.equals(bob)); System.out.println("bob.toString(): " + bob); var carl = new Manager("Carl Cracker", 80000, 1987, 12, 15); var boss = new Manager("Carl Cracker", 80000, 1987, 12, 15); boss.setBonus(5000); System.out.println("boss.toString(): " + boss); System.out.println("carl.equals(boss): " + carl.equals(boss)); System.out.println("alice1.hashCode(): " + alice1.hashCode()); System.out.println("alice3.hashCode(): " + alice3.hashCode()); System.out.println("bob.hashCode(): " + bob.hashCode()); System.out.println("carl.hashCode(): " + carl.hashCode()); } }
Employee类代码如下:
package equals; import java.time.*; import java.util.Objects; public class Employee { private String name; //实例域定义 private double salary; private LocalDate hireDay; public Employee(String name, double salary, int year, int month, int day)//构造器定义 { this.name = name; this.salary = salary; hireDay = LocalDate.of(year, month, day); } public String getName() { return name; } public double getSalary() { return salary; } public LocalDate getHireDay() { return hireDay; } public void raiseSalary(double byPercent) { double raise = salary * byPercent / 100; salary += raise; } public boolean equals(Object otherObject) { // 快速检查对象是否相同 //这里获得一个对象参数,第一个if语句判断两个引用是否是同一个,如果是那么这两个对象肯定相等 if (this == otherObject) return true; // 如果显式参数为空,则必须返回false if (otherObject == null) return false; // getClass()方法是得到对象的类,如果两个对象的类不一样,那么就不相等 if (getClass() != otherObject.getClass()) return false; //现在我们知道另一个对象是非空雇员 //在以上判断完成,再将得到的参数对象强制转换为该对象,考虑到父类引用子类的对象的出现,然后再判断对象的属性是否相同 var other = (Employee) otherObject; //测试字段是否具有相同的值 return Objects.equals(name, other.name) && salary == other.salary && Objects.equals(hireDay, other.hireDay); } public int hashCode() // 哈希散列 { return Objects.hash(name, salary, hireDay); } // toString()方法,可自动生成 public String toString() { return getClass().getName() + "[name=" + name + ",salary=" + salary + ",hireDay=" + hireDay + "]"; } }
Manager类代码如下:
package equals; public class Manager extends Employee { private double bonus; public Manager(String name, double salary, int year, int month, int day) { super(name, salary, year, month, day); bonus = 0; } public double getSalary() { double baseSalary = super.getSalary(); return baseSalary + bonus; } public void setBonus(double bonus) { this.bonus = bonus; } public boolean equals(Object otherObject) { if (!super.equals(otherObject)) return false; var other = (Manager) otherObject; //检查这个和其他属于同一个类 return bonus == other.bonus; } public int hashCode() { return java.util.Objects.hash(super.hashCode(), bonus); } public String toString() { return super.toString() + "[bonus=" + bonus + "]"; } }
程序运行结果如下:
实验2:编程练习
定义抽象类Shape:
属性:不可变常量double PI,值为3.14;
方法:public double getPerimeter();public double getArea())。
让Rectangle与Circle继承自Shape类。
编写double sumAllArea方法输出形状数组中的面积和和double sumAllPerimeter方法输出形状数组中的周长和。
main方法中
1)输入整型值n,然后建立n个不同的形状。如果输入rect,则再输入长和宽。如果输入cir,则再输入半径。
2) 然后输出所有的形状的周长之和,面积之和。并将所有的形状信息以样例的格式输出。
3) 最后输出每个形状的类型与父类型,使用类似shape.getClass()(获得类型),shape.getClass().getSuperclass()(获得父类型);
思考sumAllArea和sumAllPerimeter方法放在哪个类中更合适?
输入样例为:
3 rect 1 1 rect 2 2 cir 1
输出样例为:
18.28 8.14 [Rectangle [width=1, length=1], Rectangle [width=2, length=2], Circle [radius=1]] class Rectangle,class Shape class Rectangle,class Shape class Circle,class Shape
程序代码如下:
ShapeTest代码如下:
import java.util.Scanner; public class ShapeTest { public static void main(String[] args) { Scanner in = new Scanner(System.in); String rect = "rect"; String cir = "cir"; System.out.print("请输入形状个数:"); int n = in.nextInt(); Shape[] figure= new Shape[n]; for(int i=0;i<n;i++) { System.out.println("请输入形状类型 (rect or cir):"); String input = in.next(); if(input.equals(rect)) { double length = in.nextDouble(); double width = in.nextDouble(); System.out.println("Rectangle["+"length:"+length+" width:"+width+"]"); figure[i] = new Rectangle(width,length); } if(input.equals(cir)) { double radius = in.nextDouble(); System.out.println("Circle["+"radius:"+radius+"]"); figure[i] = new Circle(radius); } } System.out.println(sumAllPerimeter(figure)); System.out.println(sumAllArea(figure)); for(Shape s:figure) { System.out.println(s.getClass()+", "+s.getClass().getSuperclass()); } } public static double sumAllArea(Shape figure[]) { double sum = 0; for(int i = 0;i<figure.length;i++) sum+= figure[i].getArea(); return sum; } public static double sumAllPerimeter(Shape figure[]) { double sum = 0; for(int i = 0;i<figure.length;i++) sum+= figure[i].getPerimeter(); return sum; } }
Shape类代码如下:
public abstract class Shape { double PI = 3.14; public abstract double getPerimeter(); public abstract double getArea(); }
Rectangle类代码如下:
public class Rectangle extends Shape { private double width; private double length; public Rectangle(double w,double l) { this.width = w; this.length = l; } public double getPerimeter() { double Perimeter = (width+length)*2; return Perimeter; } public double getArea() { double Area = width*length; return Area; } public String toString() { return getClass().getName() + "[ width=" + width + "]"+ "[length=" + length + "]"; } }
Circle类代码如下:
public class Circle extends Shape { private double radius; public Circle(double r) { radius = r; } public double getPerimeter() { double Perimeter = 2*PI*radius; return Perimeter; } public double getArea() { double Area = PI*radius*radius; return Area; } public String toString() { return getClass().getName() + "[radius=" + radius + "]"; } }
程序运行结果如下:
第三部分:实验总结
在老师上课梳理脉络的前提下,我通过这周的自主学习,掌握了关于继承的相关知识和实验技能。在国庆这个假期我将以前所学知识和第五、六周所学结合起来,将自己的知识加以巩固,关于课本的实验并没有太大的的问题,但是在完成自主编写程序这一块,写程序的时候思维逻辑并不是很清晰,程序代码有点凌乱,不过勉强能完成了题目要求的内容。通过这一个多月Java的学习,自己对程序语言又有了更深的理解,能够理解程序,并且看得懂程序,而且通过老师的这种学习模式更好的培养了我最欠缺的自主学习能力,这对于我们来说确实是一个历练的过程