201871010119-帖佼佼《面向对象程序设计(java)》第六—七周学习总结
博文正文开头格式:(2分)
项目 |
内容 |
这个作业属于哪个课程 |
https://www.cnblogs.com/nwnu-daizh/ |
这个作业的要求在哪里 |
https://www.cnblogs.com/nwnu-daizh/p/11435127.html |
作业学习目标 |
|
第一部分:总结第五章理论知识(30分)
1、类、超类和子类
(1)继承:用已有类来构建新类的一种机制。当定义了一个新类继承了一个类时,一个新类就继承了这个类的方法和域,同时在新类中添加新的方法和域以适应新环境。
(2)继承是Java程序设计中的一项核心技术,也是面向对象特征之一。
(3)继承的特点:具有层次结构;子类继承父类的方法和域。
(4)继承的优点:代码可重用性;父类的域和方法可用于子类;设计应用程序变得更加简单;可以轻松定义子类。
类继承的格式:
class 新类名 extends 已有类名
(1)已有类成为: 超类(superclass),基类(base class)或父类(parent class);
————来自系统类库;
————用户自定义类;
(2)新类称作:子类(subclass)、派生类(derived class)或孩子类(child class);
(3)一般来说,子类比超类拥有的功能更加丰富。
注意:
(1)由于子类的构造器不能直接访问超类的私有域,必须调用超类构造器,而且必须是第一条语句;
(2)子类不能直接访问超类的构造器,必须和其他方法一样————使用公共接口;
(3)父类对象可以引用子类对象;
(4)多态性质。
1)通过扩展超类定义子类时,仅需要指出子类和超类的不同之处。在子类中可以增加域,增加方法或覆盖(override)超类的方法,但绝对不能删除超类的任何域和方法;
2)super是一个指示编译器调用超类方法的特有关键字,它不是一个对象的引用,不能将super赋值给另一个对象变量。
suer关键字有两个特殊的用途:
(1)调用超类的方法,(格式: super. 方法名() );
(2)调用超类的构造器(格式: super());
3)若子类的构造器没有显示地调用 超类的构造器,则将自动的调用超类默认构造器,若子类构造器没有显示地调用超类的构造器,则Java报告器将报告错误。
子类不继承超类的构造方法;
方法覆盖只能在不同的类中完成;
2、继承层次:
(1)从一个超类扩展而来的类集合称为层次继承。在继承层次中,从某个类到继承祖先的路径被称为该类的继承链。
(2)Java不支持多继承:即一个超类可以有多个子类,但一个子类只能有一个超类(父类)。
3、多态性:
(1)多态性的概念:多态性泛指在程序中同一个符号在不同情况下具有不同解释的现象;
(2)超类中定义的域或方法,被子类继承之后,可以具有不同的数据类型或表现出不同的行为;
(3)这使得在超类及其子类中同名的域或方法具有相同的语义;
(4)超类中的方法在子类中可方法重写。
(5)Java中,对象变量是多态的;
(6)不能把对超类的对象引用赋给子类对象变量;
(7)注意子类数组和超类数组的关系;
(8)继承层次结构中,每个子类对象也可视作是超类对象,因此,可以将子类对象赋给超类对象。
4、抽象类:
(1)观察类的层次结构,位于上层的类更具通用性,甚至更加抽象。从某种角度看,祖先类更加通用,人们只将它作为派生其他类的基类(超类),而不作为特定的实例类。
(2)抽象类的定义:
abstract class Person
{
public abstract String getDescription();
...... ↓
} abstract 方法:只能声明,不能实现;
注意:
(1)为了提高程序的清晰度,包含一个或多个抽象方法的类本身必须被声明为抽象类。除了抽象方法之外,抽象类还可以包含具体数据和具体方法;
(2)抽象方法充当着占位的角色,它们的具体实现在子类中。扩展抽象类可以用两种选择:一种是在子类中实现部分抽象方法,这样就必须将子类也标记为抽象类;另一种方法是实现全部抽象方法,这样子类就可以不是抽象类。此外,类即使不含抽象方法,也可以将类声明为抽象类。
(3)抽象类不能被实例化,即不能创建对象,只能产生子类。可以创建抽象类的对象变量,只是这个变量必须指向它的非抽象子类的对象。
5、动态绑定:
动态绑定的概念:又称为运行时绑定。即程序在运行时会自动选择调用哪个方法。
调用对象方法的执行过程:
(1)首先,编译器检查对象的声明类型和方法名,搜索相应类(Son)及其父类(Father)“方法表”,找出所有访问属性为public的method方法。
(2)接下来,编译器检查方法调用中提供的参数类型,找出一个完全匹配的方法,这个过程称为重载解析。
(3)如果方法是private、static、final修饰的,或者是构造器,那么编译器能准确地判断应该调用哪个方法,这称为静态绑定。
(4)程序运行时,如果子类SSon中定义了mothod()方法,则直接调用子类中的相应方法;如果子类Son中没有定义相应的方法,则在其父类中寻找method()的方法;
(5)动态绑定中每一次调用方法都要进行搜索,时间开销相当大。因此虚拟机预先为每个类创建了一个方法表,其中列出了所有方法的签名和实际调用的方法;
(6)方法的名称和参数列表称为方法的签名。
6、阻止继承:final类和方法
不允许继承的类称为final类,在类的定义中用final修饰符来加以说明;
类中的方法可定义为final的。这时子类就不能覆盖该方法;
如果一个类声明为final,属于它的方法会被自动设为final,但不包括域(如果域定义为final,在对象构造以后,final域就不能在更改了)。
private final int Max = 100;
String类是final类的一个例子,不能扩展该类。
7、强制类型转换:
(1)如果要把一个超类对象赋给一个子类对象变量,就必须进行强制类型转换。其格式为:
子类 对象= (子类) (超类对象)
(2)类型转换必须在继承层次内进行;而且在超类转换为子类之前,应先使用instanceof操作符进行继承链检查。
(3)注意:应该尽量减少类型转换和instanceof运算符的使用。
8、继承小结
(1)封装、继承你和多态性是面向对象的主要特征;
(2)继承可提高代码的重用性,用extends关键字来实现。除构造方法之外,父类的所有方法和属性都被子类继承;
(3)继承建立了类与类之间的关系,同时也是多态特征的前提;
(4)Java只支持单继承,不直接支持多继承(避免两个父类出现同名方法的调用选择困难);
(5)abstract修饰符的抽象类不能被实例化为对象,只能扩展子类;抽象类中的抽象方法充当着占位的角色,它们的具体实现在子类中;
(6)final类不允许被继承;类中final 方法不允许被子类重写。
9、受保护访问:
如果希望超类的某些方法或域允许被子类直接访问,就需要在超类定义时,将这些方法或域声明为protected。
10、Java的四个访问权限修饰符:
(1)private(只有该类可以访问)、protected(该类及其子类的成员可以访问,同一个包中的类也可以访问)、public(该类或非该类的都可以访问)和默认(相同包中的类均可以访问);
(2)使用访问修饰符的原因:实现受限信息隐藏;
(3)信息隐藏目的:
——对类中任何实现细节的更改不会影响使用该类的代码;
——防止用户意外删除数据;
——易于使用类;
11、所有类的超类:
Object 类是Java中所有类的祖先——每一个类都由它扩展而来。在不给出超累的情况下,Java会自动把Object作为要定义的超类;
可以使用类型为Object的变量指向任意类型的对象。但要对它们进行专门的操作,都要进行强制类型转换。
12、equals方法:
Object类中的equals方法用于检测某个对象是否同另一个对象相等。它在Object类中的实现是判断两个对象是否相等的引用。如果两个对象具有相同的引用,它们一定是相等的;
如果需要检测两个对象状态的相等性,就需要在新类的定义中需要覆盖equals方法;
定义子类的equals方法时,可调用超累的equals方法;
super. equals (otherObjecct)
13、hashCode方法:
Object类中的哈hashCode方法导出某个对象的散列码。散列码时任意整数,表示dui过的存储地址;
两个相等对象的散列码相等。
14、toString方法:
Object类中的toString方法返回一个代表该类对象域值的字符串;
定义子类的toString方法时,可先调用超类的toString方法;
super.toString()
toString方法是非常重要的调试工具。标准类库中,多数类定义了toString方法,以便用户获得对象状态的必要信息。
15、泛型数组列表:
Java中,利用ArrayList类,可允许程序在运行时确定数组的大小;
ArrayList是一个采用类型参数的泛型类。为指定数组列表保存元素的对象类型,需要用一对尖括号将数组元素的对象类名括起来加在后面;
没有<>的ArrayList类将被认为是一个删去了类型参数的“原始”类型。
16、数组列表的操作:
ArrayList的定义:ArrayList<T> 对象 = new ArrayList<T>();
API:
(1)ArrayList的构造器
——ArrayList<T>()构造一个空数组列表
——ArrayList<T>(int initialCapacity)构造一个具有指定容量的空数组列表;
(2)添加新元素:
API:boolean add(T obj)把元素obj追加到数组列表的结尾;
(3)统计个数:
API:int size() 返回数组列表中当前元素的个数;
(4)调整大小:
API:void trimToSize() 把数组列表的存储空间调整到当前的大小;
(5)访问:
API:void Set(int index, T obj) 将obj放入到数组列表index的位置,将覆盖这个位置原来的内容;
API:T get(int index) 将获得指定位置index的元素值;
(6)增加与删除:
API:boolean add(T obj) 向后移动元素,在第n个位置插入obj;
API:T remove(int index) 将第n个位置存放的对象删除,并将后面的元素向前移动;
17、对象包装器与自动打包:
所有基本数据类型都有与之对应的预定义类,它们被称为对象包装器。
对象包装器类是不可变的,即一旦构造了包装器,就不允许更改包装在其中的值。且对象包装器类还是final,因此不能定义它们的子类;
使用对象包装器的好处:(1)基本类型转换为对象;
(2)定义一些有用的基本方法(static方法)
将基本数据类型转换为包装器类对象,将这种变换称为自动打包;
当将一个包装器类的对象进行赋值或算法运算时,将会自动的拆包,打包和拆包时编译器认可的;
18、参数数量可变的方法:
用户自己可以定义可变参数的方法,并将参数指定为任意类型,甚至是基本类型;
19、枚举类:
(1)声明枚举类:public enum Grade{A、B、C、D、E};
(2)它包括一个关键字enum,一个新枚举类型的名字Grade定义的一组值,这里的值既不是整型,也不是字符型;
(3)枚举类说明:
1)枚举类是一个类,它的隐含超类是Java.lang.Enum。
2)枚举值并不是整型或其他类型,是被声明的枚举类本身实例;
3)枚举类不能有public 修饰的构造函数,构造函数都是隐含private,编译器自动处理;
4)枚举值隐含都是由public、static、final修饰的,无需自己添加这些修饰符;
5)在比较两个枚举类型的值时,永远不需要调用 equals方法,直接使用“==”进行比较;
(4)为枚举类添加构造函数
(5)Enum类的API:
20、继承设计的技巧:
(1)将公共操作和域放在超类;
(2)不要使用受保护的域;
(3)使用继承实现“is-a”关系;
(4)除非所有继承的方法都有意义,否则就不要使用继承;
(5)在覆盖方法时,不要改变预期的行为;
(6)使用多态,而非类型信息。
第二的部分:实验部分
1、实验目的与要求
(1) 理解继承的定义;
(2) 掌握子类的定义要求
(3) 掌握多态性的概念及用法;
(4) 掌握抽象类的定义及用途。
2、实验内容和步骤
实验1:测试程序1(10分)
代码如下:
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; } }
package inheritance; public class Manager extends Employee //Manager类继承Employee类 { private double bonus; /** * @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) //Manager构造器 //子类构造器,与类名同名 { super(name, salary, year, month, day); //调用父类构造器 bonus = 0; } public double getSalary() //访问器 { double baseSalary = super.getSalary(); //基本工资由父类getSalary获得 return baseSalary + bonus; } public void setBonus(double b) //更改器 { bonus = b; } }
package inheritance; /** * This program demonstrates inheritance. * @version 1.21 2004-02-21 * @author Cay Horstmann */ public class ManagerTest { public static void main(String[] args) //构造器,构造管理者对象 { // construct a Manager object Manager boss = new Manager("Carl Cracker", 80000, 1987, 12, 15); //创建Manger类对象,对象名为boss,引用数组创建,anager信息 boss.setBonus(5000); Employee[] staff = new Employee[3]; // fill the staff array with Manager and Employee objects //将雇员和经理全填充在数组中 staff[0] = boss; //父类对象变量可以引用子类对象 staff[1] = new Employee("Harry Hacker", 50000, 1989, 10, 1); staff[2] = new Employee("Tommy Tester", 40000, 1990, 3, 15); // print out information about all Employee objects for (Employee e : staff) //输出雇员对象的信息 System.out.println("name=" + e.getName() + ",salary=" + e.getSalary()); } }
运行结果如下:
实验1:测试程序2(10分)
代码如下:
package abstractClasses; import java.time.*; //为第15行LocalDate方法提供服务 public class Employee extends Person { private double salary; //private:仅对本类有用 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); //String.format() : 用于创建格式化的字符串以及连接多个字符串对象 } public void raiseSalary(double byPercent) { double raise = salary * byPercent / 100; salary += raise; } }
package abstractClasses; public abstract class Person //为了提高程序的清晰度,包含一个或多个抽象方法的类必须被声明为抽象的 { public abstract String getDescription(); //abstract方法,只能声明,不能实现 private String name; public Person(String name) //构造器。为子类构造器提供服务 { this.name = name; } public String getName() { return name; } }
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) { Person[] people = new Person[2]; // 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) //输出学生的姓名 System.out.println(p.getName() + ", " + p.getDescription()); // p.getDescription() 由于不能构造抽象类Person的对象,所以变量p永远不会引用Person对象,而Employee或Student这样的具体子类对象中都定义了getDescription方法 } }
package abstractClasses; //定义一个扩展抽象类Person的具体子类Student,这个类不是抽象类 public class Student extends Person//继承 { private String major; /** * @param nama the student's name * @param major the student's major */ public Student(String name, String major) { // pass n to superclass constructor super(name); //创建一个超类 this.major = major; //this指代当前对象,指向当前对象的父类调用父类成员 } public String getDescription() { return "a student majoring in " + major; } }
运行结果如下:
实验1:测试程序3(11分)
代码如下:
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指代当前对象 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) //Objects类中的equals方法用于检测一个对象是否等于另外一个对象 { // a quick test to see if the objects are identical if (this == otherObject) return true; // must return false if the explicit parameter is null if (otherObject == null) return false; //如果显示参数为空,则返回false // if the classes don't match, they can't be equal //如果不是相同类型的对象,则返回false if (getClass() != otherObject.getClass()) return false; //getClass方法将返回一个对象所属的类 // now we know otherObject is a non-null Employee Employee other = (Employee) otherObject; // test whether the fields have identical values return Objects.equals(name, other.name) && salary == other.salary && Objects.equals(hireDay, other.hireDay); } //测试字段是否有相同的值,是否在该域中 public int hashCode() { return Objects.hash(name, salary, hireDay); //Objects.hash() 这个类用于操作对象的静态实用方法这些工具包括用于计算对象的哈希代码的空安全或空容错方法,返回一个对象的字符串,并比较两个对象 //Object类中的hashCode方法导出某个对象的散列码。散列码世人以整数,表示对象的存储地址 //两个相等对象的散列码相等 } public String toString() //方法返回一个代表该对象域值的字符串 //toString() { return getClass().getName() + "[name=" + name + ",salary=" + salary + ",hireDay=" + hireDay + "]"; } }
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) { Employee alice1 = new Employee("Alice Adams", 75000, 1987, 12, 15); //添加各雇员对象的信息 Employee alice2 = alice1; Employee alice3 = new Employee("Alice Adams", 75000, 1987, 12, 15); Employee 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); //添加Manager的信息 Manager carl = new Manager("Carl Cracker", 80000, 1987, 12, 15); Manager 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()); } }
package equals; public class Manager extends Employee //Manager类继承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; //检查超类是否与此类相等 //在子类中定义equals 方法时,首先调用超类的equals,如果检测失败,对象就不可能像等,如果超类中的域都相等,就需要比较子类中的实例域 Manager other = (Manager) otherObject; // super.equals checked that this and other belong to the same class return bonus == other.bonus; } public int hashCode() { return java.util.Objects.hash(super.hashCode(), bonus); } public String toString() { return super.toString() + "[bonus=" + bonus + "]"; } }
运行结果如下:
实验2:编程练习(20分)
定义抽象类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
import java.util.Scanner; public class calculate { public static void main(String[] args) { int r; int x; int y; int i; int a=100; Scanner num; num = new Scanner(System.in); //System.out.println("Input the num"); i = num.nextInt(); for(i=0;i<a;i++) { Scanner kind; kind = new Scanner(System.in); // System.out.println("Input the shape"); String rect="rect"; String cir="cir"; String input=kind.next(); if(input.equals(rect)) { Scanner sc; sc = new Scanner(System.in); //System.out.println("Input the Rectangle length:"); x = sc.nextInt(); //System.out.println("Input the Rectangle width:"); y = sc.nextInt(); System.out.println("Rectangle:"+"\n\n" +"\t"+"Perimeter:"+rectangle.getPerimeter(x,y)+"\n\n"+"\t"+"Area:"+rectangle.getArea(x,y)+"\n"); } if(input.equals(cir)) { Scanner sc; sc = new Scanner(System.in); //System.out.println("Input the Circle radius:"); r = sc.nextInt(); System.out.println("Circlr:"+"\n\n"+"\t"+"Perimeter:"+circle.getPerimeter(r)+"\n\n"+"\t"+"Area"+circle.getArea(r)+"\n"); } } } } class rectangle extends Shape {//子类继承父类 public static double getArea(double width, double height) { return width*height; } public static double getPerimeter(double width, double height) { return 2*(width+height); } } class circle extends Shape {//子类继承父类 public static double getArea(int radius) { return radius*radius*pi; } public static double getPerimeter(int radius) { int pi = 0; return 2*pi*radius; } } class Shape {//父类 private static String rect; private static String cir; public double width;//成员变量 public double length; public double area; public double Perimeter; final static double pi=3.1415926; final static String n=rect; final static String m=cir; public double getArea() { return area; //成员方法15 } public double getPerimeter() { return Perimeter; } }
运行结果如下:
3. 实验总结:(10分)
通过本周实验课以及老师课堂上编程习题的训练,对基本知识有了较好的掌握。通过选择填空对程序的运行结果或者程序的内容进行完善,不断提升自己对继承的方法的使用。但是在实验课上,并没有达到老师的要求, 完成背录代码的任务。在课下,完成作业的过程中,通过看书上对程序的分析,更进一步的掌握了对继承方法的定义与使用。