1. 本章学习总结
2. 书面
Q1.注释的应用使用类的注释与方法的注释为前面编写的类与方法进行注释,并在Eclipse中查看。(截图)
Q2.面向对象设计
2.1 将在网上商城购物或者在班级博客进行学习这一过程,描述成一个故事。(不得少于50字,参考QQ群中PPT的范例)
罗列网上购物的步骤可为:
- 搜索关键字(如衣服)→ 在搜索结果中进行挑选 → 点击进入相应商品界面 → 确认商品信息 → 进入支付界面 → 填写地址及个人联系方式 → 付款完成 → 收到货物 → 确认收货
2.2 通过这个故事我们能发现谁在用这个系统,系统中包含的类及其属性方法,类与类之间的关系。尝试找到这些类与属性,并使用思维导图描述类、属性、方法及类与类之间的关系。**
通过思维导图描述相关行为:
Q3.ManagerTest.zip代码分析
分析ManagerTest.zip中的代码,回答几个问题:
3.1 在本例中哪里体现了使用继承实现代码复用?回答时要具体到哪个方法、哪个属性。
反向生成类图可知,在子类继承父类的方法后,重写了getSalary方法并新定义了setBonus方法。
for (Employee e : staff) System.out.println("name=" + e.getName() + ",salary=" + e.getSalary());
这样子类也可以调用属于父类的getName,getHireDay,raiseSalary,并同时拥有name,salary,hireDay成员变量以及set与get属性
3.2 Employee类及其子类Manager都有getSalary方法,那怎么区分这两个方法呢?
- 此处涉及多态问题,多态的方法调用存在四种情况(调用子类方法时)
由上图可知,当运行类型为子类时执行子类方法,为父类时则执行父类方法。
3.3 文件第26行e.getSalary(),到底是调用Manager类的getSalary方法还是Employee类的getSalary方法。
name=Carl Cracker,salary=85000.0
name=Harry Hacker,salary=50000.0
name=Tommy Tester,salary=40000.0
代码运行结果如上,可知boss调用的是子类方法,而数组另外两个成员调用的是父类方法,具体原因见3.2
3.4 Manager类的构造函数使用super调用父类的构造函数实现了代码复用,你觉得这样的有什么好处?为什么不把父类构造函数中的相关代码复制粘贴到Manager的构造函数中,这样看起来不是更直观吗?
父类如果存在自定义构造函数,那么子类就必须通过super进行调用,否则将报错。至于为什么不将父类的构造函数中的代码拷贝下来,既然能用super,那还去写冗余的代码,那实在是有些不够聪明了。
Q4.Object类
4.1 编写一个Fruit类及属性String name,如没有extends自任何类。使用System.out.println(new Fruit());是调用Fruit的什么方法呢?该方法的代码是从哪来的?尝试分析这些代码实现了什么功能?
运行结果为:Frult@74a14482
原因是出于println的调用,Fruit看似是我们的一个自定义类,但其默认继承Object类,所以调用的是println(Object o)的方法
从上图中不难发现,层层调用的背后其实是Object类的toString方法,其返回值为:类名+@+哈希码值
4.2 如果为Fruit类添加了toString()方法,那么使用System.out.println(new Fruit());调用了新增的toString方法。那么其父类中的toString方法的代码就没有了吗?如果同时想要复用其父类的toString方法,要怎么操作?(使用代码演示)
class Frult {
String name; @Override public String toString() { return "Frult{" + "name='" + name + ''' + '}'; }@Override public String toString_super() { return super.toString(); }
}
使用super关键字调用父类方法
4.3 Fruit类还继承了Object类的eqauls方法。尝试分析其功能?自己编写一个equals方法覆盖父类的相应方法,功能为当两个Fruit对象name相同时(忽略大小写),那么返回true。(使用代码证明你自己覆盖的eqauls方法是正确的)
public boolean equals(Object obj) { return (this == obj);}
查看Object.equals发现,其使用“==”比较两个Object的内存地址
public boolean equals(Fruit f)
{ if(this.name.equals(f.name))return true; else return false; }如上,重写Fruit类的equals方法,测试代码及结果如下*
Fruit f=new Fruit("111"); Fruit d=new Fruit("111"); System.out.println(f.equals(d));
true
- 证明重写后的equals方法满足要求
4.4 在4.3的基础上使用ArrayList fruitList存储多个fruit,要求如果fruitList中已有的fruit就不再添加,没有的就添加进去。请编写相关测试代码。并分析ArrayList的contatins方法是如何实现其功能的?
class Fruit { String name; public Fruit(String name) { this.name = name; } @Override public String toString() { return "Fruit{" + "name='" + name + '\'' + '}'; } public boolean equals(Object obj) { if (obj instanceof Fruit) { Fruit f = (Fruit) obj; return this.name.equals(f.name); } else return super.equals(obj); }}public class testDemo{ public static void main(String[] args) { ArrayListfruitList=new ArrayList (); Scanner sc=new Scanner(System.in); while(sc.hasNext()) { Fruit f=new Fruit(sc.nextLine()); if (fruitList.contains(f))continue; else fruitList.add(f); System.out.println(fruitList); } }}
代码如上,输出结果如下:
1[Fruit{name='1'}]2[Fruit{name='1'}, Fruit{name='2'}]3[Fruit{name='1'}, Fruit{name='2'}, Fruit{name='3'}]1已存在,请重试
对于contains方法的分析,同样寻找其调用方法:
public boolean contains(Object o) { return indexOf(o) >= 0;}public int indexOf(Object o) { if (o == null) { for (int i = 0; i < size; i++) if (elementData[i]==null) return i; } else { for (int i = 0; i < size; i++) if (o.equals(elementData[i])) return i; } return -1;}
可以看出,contains方法是藉由indexOf方法中逐个比对的方式完成比较,需要注意的是在调用此方法前需要用户重写自定义类的equals方法。
Q5.代码阅读:PersonTest.java(abstract、多态)
5.1 画出类的继承关系
如上,反向生成类图
5.2 读懂main函数,将自己推测的出代码运行结果与真正运行结果进行比较。尝试分析原因
运行结果如下:
Manager [bonus=12000.3, toString()=Employee [salary=90000.1, toString()=Person [name=Clark, adress=GE, phonenumber=111, email=111@mail.com, age=10, gender=mail]]]
Student [status=1, toString()=Person [name=wang, adress=110, phonenumber=15959, email=15959@163.com, age=18, gender=male]] Employee [salary=1000.0, toString()=Person [name=zhang, adress=136, phonenumber=1360, email=1360@mail.com, age=21, gender=female]] Programmer [allowance=50000.0, toString()=Employee [salary=100000.0, toString()=Person [name=Gates, adress=usa, phonenumber=911, email=911@com, age=59, gender=male]]]
从main函数中不难看出,采用冒泡法根据age对数组成员进行排序。
5.3 子类中里面使用了super构造函数,作用是什么?如果将子类中的super构造函数去掉,行不行?
- super是用来调用父类的构造函数。因为父类中存在有参的构造函数,那么子类就必须调用父类的构造函数进行初始化,否则报错。
5.4 PersonTest.java中的代码哪里体现了多态?你觉得多态有什么好处?多态和继承有什么关系吗?
peoples[0] = new Employee("zhang", "136", "1360", "1360@mail.com", 21, "female", 1000.0); peoples[1] = new Student( "wang", "110", "15959", "15959@163.com", 18, "male", "1"); peoples[2] = new Programmer("Gates", "usa", "911", "911@com", 59, "male", 100000.0, 50000.0); peoples[3] = new Manager("Clark", "GE", "111", "111@mail.com", 10, "mail", 90000.1, 12000)
- 在数组成员的初始化时体现了多态,编译类型与运行类型有所不同,把子类对象赋给父类变量,在运行时期会表现出具体的子类特征。
- 想要体现多态,那么就一定要有子类与父类之间的继承关系。
3. 码云代码提交记录
4.实验总结
对于本次实验,难点我认为不在于继承的使用,由于类之间的继承其实不太常用,因此了解继承后子类与父类之间的关系也并不复杂。但对于多态的理解是本次实验的难点,编译类型与运行类型的不同导致函数调用的差异,函数所在位置的不同导致执行效果的不同,这都是值得思考的方面。
藉由继承的使用,可以初步了解到面向对象的编程的方便与合理之处,对于JAVA项目的架构也有较好的初步认识,也给今后参与真正的JAVA项目提供一些经验。