第六章动手动脑

1.为什么子类的构造方法在运行之前,必须调用父类的构造方法?能不能反过来?为什么不能反过来?

构造函数用来在创建对象时初始化对象,与new运算符一起使用在创建对象的语句时。子类拥有父类的成员变量和成员方法,如果不调用,则从父类继承而来的成员变量和成员方法得不到正确的初始化。不可以反过来调用,父类不知道子类有什么变量,导致子类得不到正确的初始化,程序出错。

2.源代码:

public class ExplorationJDKSource {

public static void main(String[] args) {

System.out.println(new A());

}

}

class A{}

结果截图:

3.下列语句哪一个将引起编译错误?为什么?哪一个会引起运行时错误?为什么?

m=d;
d=m;
d=(Dog)m;
d=c;
c=(Cat)m;
先进行自我判断,得出结论后,运行TestCast.java实例代码,看看你的判断是否正确

编译错误d=m;d=c;

不正确 子类对象可以直接赋给基类变量。
基类对象要赋给子类对象变量,必须执行类型转换,
其语法是:子类对象变量=(子类名)基类对象名;

运行错误c=(Cat)m

不正确 转换混乱。如果类型转换失败Java会抛出以下这种异常:ClassCastException

4.下边的程序运行结果是什么? 2. 你如何解释会得到这样的输出? 3. 计算机是不会出错的,之所以得 到这样的运行结果也是有原因的, 那么从这些运行结果中,你能总 结出Java的哪些语法特性?

public class ParentChildTest {
public static void main(String[] args) {
Parent parent=new Parent();
parent.printValue();
Child child=new Child();
child.printValue();

parent=child;
parent.printValue();

parent.myValue++;
parent.printValue();

((Child)parent).myValue++;
parent.printValue();

}
}

class Parent{
public int myValue=100;
public void printValue() {
System.out.println("Parent.printValue(),myValue="+myValue);
}
}
class Child extends Parent{
public int myValue=200;
public void printValue() {
System.out.println("Child.printValue(),myValue="+myValue);
}
}
1)

Parent.printValue(),myValue=100

Child.printValue(),myValue=200

Child.printValue(),myValue=200

Child.printValue(),myValue=200

当子类与父类拥有一样的方法,并且让一个父类变量引用一个子类对象时,到底调用哪个方法,由对象自己的“真实”类型所决定,这就是说:对象是子类型的,它就调用子类型的方法,是父类型的,它就调用父类型的方法。如果子类与父类有相同的字段,则子类中的字段会代替或隐藏父类的字段,子类方法中访问的是子类中的字段(而不是父类中的字段)。如果子类方法确实想访问父类中被隐藏的同名字段,可以用super关键字来访问它。如果子类被当作父类使用,则通过子类访问的字段是父类的。

5. 多态含义和用途

让我们看一个开发场景:

某动物园有一饲养员小李,

每天需要给他所负责饲养的狮子、猴子和鸽子喂食。

请用一个程序来模拟他喂食的过程。

三种动物对应三个类,每个类定义一个eat()方法,表示吃饲养员给它们的食物。

再设计一个Feeder类代表饲养员,其name字段保存饲养员名字,三个方法分别代表喂养三种不同的动物,其参数分别引用三种动物

public class Zoo

{

public static void main(String args[])
{
Feeder f = new Feeder("小李");
// 饲养员小李喂养一只狮子
f.feedLion(new Lion());
// 饲养员小李喂养十只猴子
for (int i = 0; i < 10; i++)
{
f.feedMonkey(new Monkey());
}
// 饲养员小李喂养5只鸽子
for (int i = 0; i < 5; i++)
{
f.feedPigeon(new Pigeon());
}
}
}
class Feeder
{
public String name;
public Feeder(String name)
{
this.name = name;
}
public void feedLion(Lion l)
{
l.eat();
}
public void feedPigeon(Pigeon p)
{
p.eat();
}
public void feedMonkey(Monkey m)
{
m.eat();
}
}
class Lion
{
public void eat()
{
System.out.println("我不吃肉谁敢吃肉!");
}
}
class Monkey
{
public void eat()
{
System.out.println("我什么都吃,尤其喜欢香蕉。");
}
}
class Pigeon
{
public void eat()
{
System.out.println("我要减肥,所以每天只吃一点大米。");
}
}
这种编程方式有什么不合理的地方?

每次喂食都要创建一次类。重复步骤多。

引入继承

定义一个抽象基类Animal,其中定义一个抽象方法eat(),三个子类实现这个抽象方法。

Feeder类的三个喂养方法现在可以合并为一个feedAnimal()方法,注意它接收一个类型为Animal参数,而不是三个具体的动物类型。

依据多态特性,此方法将可以接收任何一个派生自Animal类的子类对象

public class Zoo
{
public static void main(String args[])
{
Feeder f = new Feeder("小李");
//饲养员小李喂养一只狮子
f.feedAnimal(new Lion());
//饲养员小李喂养十只猴子
for (int i = 0; i < 10; i++)
{
f.feedAnimal(new Monkey());
}
//饲养员小李喂养5只鸽子
for (int i = 0; i < 5; i++)
{
f.feedAnimal(new Pigeon());
}
}
}
class Feeder
{
public String name;
Feeder(String name)
{
this.name = name;
}
public void feedAnimal(Animal an)
{
an.eat();
}
}
abstract class Animal
{
public abstract void eat();
}
class Lion extends Animal
{
public void eat()
{
System.out.println("我不吃肉谁敢吃肉!");
}
}
class Monkey extends Animal
{
public void eat()
{
System.out.println("我什么都吃,尤其喜欢香蕉。");
}
}
class Pigeon extends Animal
{
public void eat()
{
System.out.println("我要减肥,所以每天只吃一点大米。");
}
}
进一步优化喂养一群动物

package zoo3;
public class Zoo
public static void main(String args[]) {
Feeder f = new Feeder("小李");
Animal[] ans = new Animal[16];
//饲养员小李喂养一只狮子
ans[0] = new Lion();
//饲养员小李喂养十只猴子
for (int i = 0; i < 10; i++) {
ans[1 + i] = new Monkey();
}
//饲养员小李喂养5只鸽子
for (int i = 0; i < 5; i++) {
ans[11 + i] = new Pigeon();
}
f.feedAnimals(ans);
}
}
class Feeder {
public String name;
Feeder(String name) {
this.name = name;
}
public void feedAnimals(Animal[] ans) {
for (Animal an : ans) {
an.eat();
}
}
}
abstract class Animal {
public abstract void eat();
}
class Lion extends Animal {
public void eat() {
System.out.println("我不吃肉谁敢吃肉!");
}
}
class Monkey extends Animal {
public void eat() {
System.out.println("我什么都吃,尤其喜欢香蕉。");
}
}
class Pigeon extends Animal {
public void eat() {
System.out.println("我要减肥,所以每天只吃一点大米。");
}
}
第二次重构之后,Feeder类的feedAnimals()方法接收的是一个Animal数组,这有一个限制,就是只能创建固定个数的数组,无法动态地增减动物个数。

想想以下场景:

(1)动物园新进了一些动物

(2)某动物生病不幸死亡

(3)……

我们的代码能否应付以上的场景

import java.util.Vector;
public class Zoo {
public static void main(String args[]) {
Feeder f = new Feeder("小李");
Vector<Animal> ans = new Vector<Animal>();
//饲养员小李喂养一只狮子
ans.add(new Lion());
//饲养员小李喂养十只猴子
for (int i = 0; i < 10; i++) {
ans.add(new Monkey());
}
//饲养员小李喂养5只鸽子
for (int i = 0; i < 5; i++) {
ans.add(new Pigeon());
}
f.feedAnimals(ans);
}
}
class Feeder {
public String name;
Feeder(String name) {
this.name = name;
}
//Vector<T>是JDK中提供的一个对象集合,可以随时向其中加入或移除对象
public void feedAnimals(Vector<Animal> ans) {
for (Animal an : ans) {
an.eat();
}
}
}
abstract class Animal {
public abstract void eat();
}
class Lion extends Animal {
public void eat() {
System.out.println("我不吃肉谁敢吃肉!");
}
}
class Monkey extends Animal {
public void eat() {
System.out.println("我什么都吃,尤其喜欢香蕉。");
}
}
class Pigeon extends Animal {
public void eat() {
System.out.println("我要减肥,所以每天只吃一点大米。");
}
}

多态编程有两种主要形式:

(1)继承多态:示例程序使用的方法

(2)接口多态:使用接口代替抽象基类。

使用多态最大的好处是,当你要修改程序并扩充系统时,你需要修改的地方较少,对其它部分代码的影响较小

时间: 2024-10-22 18:11:38

第六章动手动脑的相关文章

第九章动手动脑

[动手动脑一] 多层的异常捕获-1 阅读以下代码(CatchWho.java),写出程序运行结果: 程序运行结果: [动手动脑二] 多层的异常捕获-2 写出CatchWho2.java程序运行的结果 程序运行结果: [动手动脑三] 当有多个嵌套的try-catch-finally时,要特别注意finally的执行时机. 请先阅读 EmbedFinally.java示例,再运行它,观察其输出并进行总结. 特别注意: 当有多层嵌套的finally时,异常在不同的层次抛出 ,在不同的位置抛出,可能会导

第四章动手动脑与课后作业

java中String类里的String.equals()方法: 源代码: public boolean equals(Object anObject) { //如果是同一个对象 if (this == anObject) { return true; } //如果传递进来的参数是String类的实例 if (anObject instanceof String) { String anotherString = (String)anObject; int n = count;//字符串长度 i

java第三章动手动脑

public class InitializeBlockDemo { /** * @param args */ public static void main(String[] args) { InitializeBlockClass obj=new InitializeBlockClass(); System.out.println(obj.field); obj=new InitializeBlockClass(300); System.out.println(obj.field); } }

Java第二章动手动脑练习

1.编写一个程序,用户输入两个数,求出其加减乘除,并用消息框显示计算结果. import javax.swing.JOptionPane; public class Test{ public static void main(String[] args) { int n1=Integer.parseInt(JOptionPane.showInputDialog("Input number 1: ")); int n2=Integer.parseInt(JOptionPane.showI

《Python从入门到实践》第六章动手试一试

6-1 人 :使用一个字典来存储一个熟人的信息,包括名.姓.年龄和居住的城市.该字典应包含键first_name .last_name .age 和city .将存储在该字典中的每项信息都打印出来. 6-2 喜欢的数字 :使用一个字典来存储一些人喜欢的数字.请想出5个人的名字,并将这些名字用作字典中的键:想出每个人喜欢的一个数字,并将这些数字作为值存储在字典中.打印每个人的名字和喜欢的数字.为让这个程序更有趣,通过询问朋友确保数据是真实的. 6-3 词汇表 :Python字典可用于模拟现实生活中

【Spark亚太研究院系列丛书】Spark实战高手之路-第2章动手实战Scala第2小节(3)

5,动手实战Scala中的apply方法和单例对象 新建一个类: 额外提一点,放在object对象中的方法都是静态方法,如下所示: 接下来看一下apply方法的使用: 上面代码总当我们使用"val a = ApplyTest()"的使用会导致apply方法的调用并返回该方法调用的值,也就是ApplyTest的实例化对象. Class中也可以由apply方法,其使用方法如下所示: 由于object中的方法和属性都是静态的,所以就是单例对象的理想载体,实例代码如下所示: 换言之,objec

Java课程02-动手动脑

1.编写一个方法,生成一千个随机数,纯随机数发生器. package random; public class random { public static void main(String[] args) { // TODO Auto-generated method stub long seed = System.currentTimeMillis();//种子 int i; int count=0; long random=(16807 * seed) % Integer.MAX_VALU

Java02-动手动脑及实践性问题

Java字段初始化的规律: 源代码: public class InitializeBlockDemo { /** * @param args */ public static void main(String[] args) { InitializeBlockClass obj=new InitializeBlockClass(); System.out.println(obj.field); obj=new InitializeBlockClass(300); System.out.prin

Java03-动手动脑

继承条件下的构造方法调用: 源代码: package 继承构造方法调用; class Grandparent { public Grandparent() { System.out.println("GrandParent无参构造函数执行"); } public Grandparent(String string) { System.out.println("GrandParent有参构造函数执行" + string); } } class Parent exten