201871010128-杨丽霞《面向对象程序设计(java)》第六-七周学习总结
项目 |
内容 |
这个作业属于哪个课程 |
|
这个作业的要求在哪里 |
https://www.cnblogs.com/nwnu-daizh/p/11605051.html
|
作业学习目标 |
· 深入理解程序设计中算法与程序的关系; · 深入理解java程序设计中类与对象的关系; · 理解OO程序设计的第2个特征:继承、多态; · 学会采用继承定义类设计程序(重点、难点); · 能够分析与设计至少包含3个自定义类的程序; · 掌握利用父类定义子类的语法规则及对象使用要求。
|
随笔博文正文内容包括:
第一部分:总结第五章理论知识(30分)
1.继承的概念:继承在本职上是特殊——一般的关系,即常说的is-a关系。子类继承父类,表明子类是一种特殊的父类,并且具有父类所不具有的一些属性或方法。
2. 继承中的初始化顺序:
从类的结构上而言,其内部可以有如下四种常见形态:属性(包括类属性和实例属性)、方法(包括类方法和实例方法)、构造器和初始化块(包括类的初始化块和实例的初始化块)。对于继承中的初始化顺序,又具体分为类的初始化和对象的初始化。
类初始化:在jvm装载类的准备阶段,首先为类的所有类属性和类初始化块分配内存空间。并在类首次初始化阶段中为其进行初始化,类属性和类初始化块之间的定义时的顺序决定了其初始化的顺序。若类存在父类,则首先初始化父类的类属性和类初始化块,一直上溯到Object类最先执行。
对象初始化:在new创建对象时,首先对对象属性和初始化块分配内存,并执行默认初始化。如果存在父类,则先为父类对象属和初始化块先分配内存并执行初始化。然后执行父类构造器中的初始化程序,接着才开始对子类的对象属性和初始化块执行初始化。
注:1. 在对象初始化阶段,属性和方法均针对子类可以从父类继承过来的属性和方法而言,一般而言,都是针对父类中非private而言的。因为private修饰的为父类所特有的,子类没有继承过来,当new子类时,无须为其分配空间并执行初始化。当然了,父类的构造器子类也是不继承过来的,但构造器另当别论类的初始化只执行一次,当对同一个类new多个对象时,类属性和类初始化块只初始化一次
3.继承中的隐藏:
隐藏含义:实际上存在,但是对外不可见。Java类具有三种访问控制符:private、protected和public,同时当不写这三个访问控制符时,表现为一种默认的访问控制状态。因此,一共具有四种访问控制级别。
具体访问控制表现如下:private修饰的属性或方法为该类所特有,在任何其他类中都不能直接访问;
default修饰的属性或方法具有包访问特性,同一个包中的其他类可以访问;
protected修饰的属性或方法在同一个中的其他类可以访问,同时对于不在同一个包中的子类中也可以访问;
public修饰的属性或方法外部类中都可以直接访问。
当子类继承父类,子类可以继承父类中具有访问控制权限的属性和方法(一般来说是非private修饰的),对于private修饰的父类所特有的属性和方法,子类是不继承过来的。
当子类需要改变继承过来的方法时,也就是常说的重写父类的方法。一旦重写后,父类的此方法对子类来说表现为隐藏。以后子类的对象调用此方法时,都是调用子类重写后的方法,但子类对象中想调用父类原来的此方法时,可以通过如下两种方式:
- 将子类对象类型强制转化为父类类型,进行调用;
- 2.通过super调用。同样的,如果在子类中定义父类中相同名称的属性时,父类属性在子类中表现为隐藏。
4.继承中的this和super:
构造器中的this表示当前正在初始化的对象引用,方法中的this表示当前正在调用此方法的对象引用。this具体用法表现在一下几个方面:
1.当具多个重载的构造器时,且一个构造器需要调用另外一个构造其,在其第一行使用this(param)形式调用,且只能在第一行;
2.当对象中一个方法需要调用本对象中其他方法时,使用this作为主调,也可以不写,实际上默认就是this作为主调;
3.当对象属性和方法中的局部变量名称相同时,在该方法中需要显式的使用this作为主调,以表示对象的属性,若不存在此问题,可以不显式的写this。
其实,其牵涉到的一个问题就是变量的查找规则:先局部变量 => 当前类中定义的变量 => 其父类中定义的可以被子类继承的变量 => 父类...
super表示调用父类中相应的属性和方法。在方法中,若需要调用父类的方法时,也一定要写在第一行
5.继承与组合:
- 从单纯的实现效果上看,继承和组合都能达到同样的目的。并且都是实现代码复用的有效方式。但在一般性的概念层次中,两者具有较为明显的差别。继承表现为一般——特殊的关系,子类是一个特殊的父类,是is-a的关系。父类具有所有子类的一般特性。组合表现为整体——部分关系,即has-a关系。在组合中,通过将“部分”单独抽取出来,形成自己的类定义,并且在“整体”这个类定义中,将部分定义为其中的一个属性,并通过get和set方法,以此可以调用“部分”类中的属性和方法。
6.多态的前提:
1) 必须有继承关系子类继承父类,存在一些特点
2) 必须有方法重写子类继承父类,方法重写的目的,举例:动物吃的方法,每一个具体动物吃的东西不一样,所有必须要方法覆盖
3) 就是必须有父类的引用指向子类对象 (向上转型)父类名 fu = new 子类名() ;通过父类对象的创建是通过子类在堆内存新建了了一个对象,由于子类又继承了父类
4) 多的前提就是继承和方法重写,所以访问成员变量和方法时会被子类方法覆盖掉.
5) 多态的好处:可以提供代码的复用性:继承保证可以提高的代码的扩展性:由多态保证... (父类的引用指向子类对象). 多态的弊端:父类引用指向子类对象,通过父类的引用调用子类特有功能,不能调用....解决多态的弊端可以向下转型不过可能会出现运行错
7.反射
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象.
反射就是把java类中的各种成分映射成一个个的Java对象
- Class 类
- Class 类的实例表示正在运行的 Java 应用程序中的类和接口。也就是jvm中有N多的实例每个类都有该Class对象。(包括基本数据类型)
Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的defineClass 方法自动构造的。也就是这不需要我们自己去处理创建,JVM已经帮我们创建好了
8.如何获取Class对象
Object ——> getClass();
任何数据类型(包括基本数据类型)都有一个 静态的class属性
通过Class类的静态方法:forName(String className)(常用)
- Object:所有类的超类
(1)toString方法
toString方法是Object中重要的方法之一,该方法将返回此对象的字符串表示,以便在实际运行或调试代码时可以获取字符串表示的对象信息,下面给出了该方法的定义形式。public String toString()Java中大多数类都重写了这个方法,通常的方式是将类名以及成员变量的状态信息组合转化为一个字符串返回
(2)equals方法都是来自Object类的,String类对其重写以满足比较字符串的内容要求。Object类中设计这个方法就是为了让它的类来重写,以满足比较不同类型对象是否等价的要求。
(3)hashCode方法
管理很多对象时,如果采用数组这一类线性表结构,在进行随机查找时效率将非常差,经常需要遍历整个线性表,随着被管对象的增多性能急剧下降。因此希望采取一种高效的管理方式,这时一般会用到哈希存储的方式。使用哈希时被存储的对象需要提供一个哈希码(hash code),一般是一个整数值。hashCode方法的功能就是用来提供所在对象的额哈希码,根据对象的不同哈希码的值有所不同。一般每定义一个新的类,都要为其重写一个hashCode的方法。进入哈希存储前,首先调用对象的hashcode方法获取哈希码,定位对象所在的哈希桶。在哈希桶内部,所有的哈希码相同的不同对象是按照线性表的方式存储的。
9.泛型数组列表-ArrayList类
在java中,数组的大小是不能改变的,为解决这一问题可使用ArrayList类,它在添加或删除元素时,具有自动调节数组容量的功能,而不需要为此编写任何代码。ArrayList是一个采用类型参数的泛型类,可指定数组列表保存的元素对象类型。声明和构造一个保存Cat对象的数组列表:
ArrayList<Cat>c = new ArrayList<>();(现版本采用第一种)
或ArrayList<Cat>c1 = new ArrayList<Cat>();
如果已经清楚或能够估计出数组可能存储的元素数量,就可以在填充数组之前调用ensureCapacity方法:
c.ensureCapacity(100);
这个方法调用将分配一个包含100个对象的内部数组。然后调用add添加数组元素。另外,还可以把初始容量传递给ArrayList构造器:
ArrayList<Cat>c = new ArrayList<>(100);
注意:
分配数组列表,如下所示:
new ArrayList<>(100); //capacity是100
new Cat[100]; //size是100
数组列表的容量与数组的大小有很大的区别。如果为数组分配100个元素的存储空间,数组就有100个空位置可以使用。而容量为100个元素的数组列表只是拥有保存100个元素的潜力。
size()方法返回数组列表中包含的实际元素的数目:c.size();将返回c数组列表的当前元素数量,等价于数组a.length。
(1)添加元素add
add方法为数组添加新的元素。示例:
c.add(new Cat("n1", "color1"));
c.add(new Cat("n2", "color2"));
向后移动元素,以便插入元素。示例:
c.add(0, new Cat("c", "yellow"));
(2)set方法
set方法设置数组列表指定位置的元素,这个操作将覆盖这个位置的原有内容。
(3)remove
删除一个元素,并将后面的元素向前移动。
(4)get
获取指定位置的元素值。
(5) trimToSize
将数组列表的存储容量削减到当前尺寸
第二部分:实验部分
1、实验目的与要求
(1) 理解继承的定义;
(2) 掌握子类的定义要求
(3) 掌握多态性的概念及用法;
(4) 掌握抽象类的定义及用途。
2、实验内容和步骤
实验一: 导入第5章示例程序,测试并进行代码注释。
测试程序1(10分)
(1) 在elipse IDE中编辑、调试、运行程序5-1 —5-3(教材152页-153页) ;
(2)掌握子类的定义及用法;
(3)结合程序运行结果,理解并总结OO风格程序构造特点,理解Employee和Manager类的关系子类的用途,并在代码中添加注释;
(4) 删除程序中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) { // construct a Manager object var boss = new Manager("Carl Cracker", 80000, 1987, 12, 15); boss.setBonus(5000);//创建一个新经理,并设置他的奖金,setBonus属于manager的特有方法 var 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()); //输出每个人的姓名及薪水 } }
例题5-2代码:
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()//取得name这个属性的值 { return name; } public double getSalary()//取得Salary这个属性的值 { 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 { 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) { super(name, salary, year, month, day);//调用超类构造器 bonus = 0; } public double getSalary() { 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(); } }
运行结果:
OO风格程序构造特点:oo方法即面向对象方法,而oo风格的特点是封装、继承、多态
测试程序2(10分)
(1) 编辑、编译、调试运行教材PersonTest程序(教材163页-165页);
(2)掌握超类的定义及其使用要求;
(3) 掌握利用超类扩展子类的要求;
(4) 在程序中相关代码处添加新知识的注释;
(5)删除程序中Person类、PersonTest类,背录删除类的程序代码,在代码录入中理解抽象类与子类的关系和使用特点。
例题5.4程序代码如下(Person Test):
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];//超类 // 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");//子类;将雇员和学生填充到Person引用数组 // print out names and descriptions of all Person objects for (Person p : people) System.out.println(p.getName() + ", " + p.getDescription());//输出对象的姓名和星描述 } }
因主类中有Person类,而Person类是一个抽象类,故需新建一个抽象类Person类,程序如下:
public abstract class Person//抽象类:Person { public abstract String getDescription(); private String name;//传建一个私有属性 public Person(String name)//构造器 { this.name = name; } public String getName()//访问器 { return name; } }
student类:
public class Student extends Person//子类:Student类继承Person类 { private String major;//创建一个私有属性major /** * @param nama the student's name * @param major the student's major */ public Student(String name, String major)//构造器 { //将n传递给超类构造函数 super(name);//子类直接调用超类中的name属性 this.major = major; } public String getDescription()//访问器 { return "a student majoring in " + major; }
Employee类:
import java.time.*; public class Employee extends Person//子类:Employee类继承Person类 { private double salary; private LocalDate hireDay; //构建两个私有属性 public Employee(String name, double salary, int year, int month, int day)//构造器 { super(name);//子类中不再重新定义,直接调用超类中的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; }//定义两个局部变量 }
运行结果如下:
删除类的程序代码:
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; } }
测试程序3(11分)
(1)编辑、编译、调试运行教材程序5-8、5-9、5-10,结合程序运行结果理解程序(教材174页-177页);
(2)掌握Object类的定义及用法;
(3)在程序中相关代码处添加新知识的注释。
例题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);//对alice1进行初始化 var alice2 = alice1;//将alice1的值赋给alice2 var alice3 = new Employee("Alice Adams", 75000, 1987, 12, 15);//对alice3进行初始化 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()); } }
例题5-9代码如下:
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()//取得name这个属性的值 { return name; } public double getSalary()//取得Salary这个属性的值 { return salary; } public LocalDate getHireDay() { return hireDay; } public void raiseSalary(double byPercent) { double raise = salary * byPercent / 100; salary += raise; } public boolean equals(Object otherObject) { // 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 if (getClass() != otherObject.getClass()) return false;//getClass():得到对象的类 // now we know otherObject is a non-null Employee var 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); } public String toString() { return getClass().getName() + "[name=" + name + ",salary=" + salary + ",hireDay=" + hireDay + "]"; } }
例题5-10代码如下:
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; // super.equals checked that this and other belong to the same classs //检查是否属于同一类 return bonus == other.bonus; } public int hashCode() { return java.util.Objects.hash(super.hashCode(), bonus); } public String toString() { return super.toString() + "[bonus=" + bonus + "]"; } }
运行结果:
实验二:编程练习(20分)
(1)定义抽象类Shape:
属性:不可变常量double PI,值为3.14;
方法:public double getPerimeter();public double getArea())。
(2)让Rectangle与Circle继承自Shape类。
编写double sumAllArea方法输出形状数组中的面积和和double sumAllPerimeter方法输出形状数组中的周长和。
(3)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 Work { 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[] count = new shape[n]; for(int i=0;i<n;i++) { System.out.println("请输入图形形状:"); String input = in.next(); if(input.equals(rect)) { double length = in.nextDouble(); double width = in.nextDouble(); System.out.println("长方形:"+"长:"+length+" 宽:"+width); count[i] = new Rect(length,width); } if(input.equals(cir)) { double radius = in.nextDouble(); System.out.println("圆:"+"半径:"+radius); count[i] = new Cir(radius); } } Work c = new Work(); System.out.println(c.sumAllPerimeter(count)); System.out.println(c.sumAllArea(count)); for(shape s:count) { System.out.println(s.getClass()+", "+s.getClass().getSuperclass()); } } public double sumAllArea(shape count[]) { double sum = 0; for(int i = 0;i<count.length;i++) sum+= count[i].getArea(); return sum; } public double sumAllPerimeter(shape count[]) { double sum = 0; for(int i = 0;i<count.length;i++) sum+= count[i].getPerimeter(); return sum; } }
public abstract class shape { double PI = 3.14; public abstract double getPerimeter(); public abstract double getArea(); }
public class Cir extends shape { private double radius; public Cir(double radius2) { // TODO Auto-generated constructor stub } public double getPerimeter() { double Perimeter=2*PI*radius; return Perimeter; } public double getArea() { double Area=PI*radius*radius; return Area; }
public class Rect extends shape { private double width; private double length; public Rect(double w,double l) { this.width = w; this.length = l; } public double getPerimeter() { double Perimeter = 2*(length+width); return Perimeter; } public double getArea() { double Area = length*width; return Area; } }
运行结果如下:
3. 实验总结:(10分)
这周学习了父类和子类的定义,而继承指的是子类继承父类的方法和域,以便于简化程序,在这个过程中常会用到关键字super。我也学会了如何去定义一个抽象类。但是在写程序的过程中,仍然有问题存在,虽然写出了实验二的程序,可是输出的格式和题目所要求的还是有出入,目前为止,这个问题依然没有想到解决的办法。并且运行过程也出现了很多错误,通过这次实验,我深深认识到,很多知识一定要看书,并在课后多敲代码练习。对于很多自己还不理解的知识,自己定会加强学习,希望之后的实验中,问题能够减少。