AI 摘要
AI
正在生成摘要...
本文内容可能已过时或不再适用,请结合实际情况谨慎参考。

一、实验内容

实验目的:

掌握面向对象继承和多态。

1. 运行例 4-1 到 4-6。

例 4-1:定义两个类,一个是动物的类,另一个是人的类。

JAVA
class Animal {
    String nickName;

    // 父类方法:动物发声
    public void speak() {
        System.out.println("Animal is speaking!");
    }
}

// 子类:人类,使用 extends 继承 Animal
// 子类会自动继承父类非私有成员变量和方法
class People extends Animal {
    // 子类空实现,直接继承父类功能
}

图片

例 4-2:super 的用法。

JAVA
class Animal {
    // 父类无参构造方法
    Animal() {
        System.out.println("一只动物被构造");
    }

    String name;

    public void speak() {
        System.out.println("Animal Speak!");
    }
}

class Dog extends Animal {
    // 子类无参构造方法
    Dog() {
        // 自动隐式调用 super(),执行父类构造
        System.out.println("一只狗被构造");
    }

    // 子类与父类同名成员变量:变量隐藏
    String name;

    // 重写父类方法
    @Override
    public void speak() {
        System.out.println("Wang Wang!");
    }
}

public class Test {
    public static void main(String[] args) {
        Dog g = new Dog();
        // 调用重写后的方法,执行子类实现
        g.speak();
    }
}

运行结果:

PLAINTEXT
一只动物被构造
一只狗被构造
Wang Wang!

图片

例 4-3:通过 super 关键字来调用父类的成员。

JAVA
class Animal {
    String nickName;
    String name;

    Animal() {
        System.out.println("一只动物被构造");
    }

    public void speak() {
        System.out.println("Animal Speak!");
    }
}

class Dog extends Animal {
    // 子类与父类同名变量,形成变量隐藏
    String name;

    // 子类无参构造方法
    Dog() {
        // super() 调用父类构造,必须放在第一行
        super();
        // super.访问父类成员变量
        super.nickName = "Kelly";
        // super.调用父类的成员方法
        super.speak();

        System.out.println("一只狗被构造");
    }

    // 重写父类方法
    @Override
    public void speak() {
        System.out.println("Wang Wang!");
    }
}

public class Test {
    public static void main(String[] args) {
        // 创建子类对象,先执行父类构造,再执行子类构造
        Dog g = new Dog();
        // 调用子类重写后的方法
        g.speak();
    }
}

运行结果:

PLAINTEXT
一只动物被构造
Animal Speak!
一只狗被构造
Wang Wang!

图片

例 4-4:编译时多态举例。

JAVA
// 动物子类:狗
class Dog {
    public void bark() {
        System.out.println("小狗:汪汪汪");
    }
}

// 动物子类:猫
class Cat {
    public void meow() {
        System.out.println("小猫:喵喵喵");
    }
}

// 测试类,演示编译时多态(方法重载)
class Polymorphism {
    // 重载1:接收 Dog 参数
    public void speak(Dog dog) {
        System.out.print("调用 Dog 版本speak:");
        dog.bark();
    }

    // 重载2:接收 Cat 参数
    public void speak(Cat cat) {
        System.out.print("调用 Cat 版本speak:");
        cat.meow();
    }

    // 拓展重载:无参版本,展示参数个数不同也构成重载
    public void speak() {
        System.out.println("无参 speak:没有小动物");
    }

    public static void main(String[] args) {
        Polymorphism poly = new Polymorphism();
        Dog dog = new Dog();
        Cat cat = new Cat();

        // 编译期就能确定调用哪个 speak 方法
        poly.speak();      // 匹配无参speak()
        poly.speak(dog);   // 匹配参数为Dog的speak
        poly.speak(cat);   // 匹配参数为Cat的speak
    }
}

运行结果

PLAINTEXT
无参speak:没有小动物
调用Dog版本speak:小狗:汪汪汪
调用Cat版本speak:小猫:喵喵喵

图片

例 4-5:运行时多态举例。

JAVA
class Animal {
    // 父类发声方法
    public void speak() {
        System.out.println("Animal Speak!");
    }
}

class Dog extends Animal {
    // 重写父类方法
    @Override
    public void speak() {
        System.out.println("Wang Wang!");
    }
}

class Cat extends Animal {
    // 重写父类方法
    @Override
    public void speak() {
        System.out.println("Meow Meow!");
    }
}

// 测试类:演示多态
public class Test {
    // 通用方法:接收父类类型,可传入所有子类对象
    public static void doSpeak(Animal an) {
        // 多态:运行时根据实际对象类型调用对应方法
        an.speak();
    }

    public static void main(String[] args) {
        // 向上转型:父类引用指向子类对象
        Animal a1 = new Dog();
        Animal a2 = new Cat();
        Animal a3 = new Animal();

        // 同一个方法,传入不同对象,执行不同逻辑
        doSpeak(a1);
        doSpeak(a2);
        doSpeak(a3);
    }
}

运行结果:

PLAINTEXT
Wang Wang!
Meow Meow!
Animal Speak!

图片

例 4-6:final 类的举例。

JAVA
// final 修饰的类:最终类,不能被继承
public final class FinalDemo {
    private String msg = "这是final类";

    public static void main(String[] args) {
        // final 类可以正常创建对象
        FinalDemo fd = new FinalDemo();
        // 访问成员变量并输出
        System.out.println(fd.msg);
    }
}

运行结果:

PLAINTEXT
这是final类

图片

2. 运行案例 4-1 到 4-3。

案例 4-1:设计一个类 Test,里面有三个静态的内部类 Animal、Dog 和 Cat,Dog 和 Cat 类从 Animal 类继承,并重写父类相关的方法实现多态性。

JAVA
public class Test {
    // 静态内部类:动物父类
    static class Animal {
        // protected 成员:子类可直接访问
        protected String name;

        // 父类带参构造
        public Animal(String name) {
            this.name = name;
        }

        // 父类方法,供子类重写
        public void sleep() {
        }

        public void speak() {
        }
    }

    // 静态内部类:狗类,继承 Animal
    static class Dog extends Animal {
        // 子类构造,通过 super 调用父类构造
        public Dog(String name) {
            super(name);
        }

        // 重写父类 sleep 方法
        @Override
        public void sleep() {
            System.out.println("狗的睡觉方式");
        }

        // 重写父类 speak 方法
        @Override
        public void speak() {
            System.out.println("狗的吼叫方式");
        }
    }

    // 静态内部类:猫类,继承 Animal
    static class Cat extends Animal {
        public Cat(String name) {
            super(name);
        }

        // 重写 sleep 方法
        @Override
        public void sleep() {
            System.out.println("猫的睡觉方式");
        }

        // 重写 speak 方法
        @Override
        public void speak() {
            System.out.println("猫的吼叫方式");
        }
    }

    public static void main(String args[]) {
        // 向上转型:父类引用指向子类对象
        Animal a = new Dog("旺财");
        // 多态调用,执行子类重写后的方法
        a.sleep();
        a.speak();

        Animal a1 = new Cat("汤姆");
        a1.sleep();
        a1.speak();
    }
}

运行结果:

PLAINTEXT
狗的睡觉方式
狗的吼叫方式
猫的睡觉方式
猫的吼叫方式

图片

案例 4-2:编写两个类,GetToKnowConstructingOrder 类从 parent 继承,根据以下程序分析子类与父类程序执行的顺序。

JAVA
class Parent {
    // 普通成员变量
    int i = 9;
    int j;

    // 父类构造方法
    Parent() {
        System.out.println("i=" + i);
        j = 39;
        System.out.println("j=" + j);
    }

    // 父类静态变量
    static int x = prt("static parent.x initialized.");

    // 父类静态方法
    static int prt(String s) {
        System.out.println(s);
        return 47;
    }
}

public class GetToKnowConstructingOrder extends Parent {
    // 子类普通成员变量
    int k = prt("getToKnowConstructingOrder.k initialized.");

    // 子类构造方法
    GetToKnowConstructingOrder() {
        prt("k=" + k);
        prt("j=" + j);
    }

    // 子类静态变量
    static int y = prt("getToKnowConstructingOrder.y initialized.");

    // 子类静态方法
    static int prt(String s) {
        System.out.println(s);
        return 63;
    }

    public static void main(String[] args) {
        prt("getToKnowConstructingOrder constructor.");
        // 创建子类对象
        GetToKnowConstructingOrder s = new GetToKnowConstructingOrder();
    }
}

运行结果:

PLAINTEXT
static parent.x initialized.
getToKnowConstructingOrder.y initialized.
getToKnowConstructingOrder constructor.
i=9
j=39
getToKnowConstructingOrder.k initialized.
k=63
j=39

图片

类加载 = 自动执行所有 static 代码

普通变量:要创建对象才会赋值。静态变量:程序一启动,自动赋值,自动运行。

静态的东西,永远最先跑。先跑父类静态,再跑子类静态。创建对象时,先跑父类,再跑子类。

先给变量赋值,再跑构造方法。

PLAINTEXT
执行 new GetToKnowConstructingOrder();
├─ 第一步:隐式调用 super() 进入父类构造 Parent()
│   ├─ 父类实例变量加载 i=9,j默认0
│   ├─ 打印 i=9
│   ├─ j=39
│   └─ 打印 j=39
├─ ↓ 到这里,父类构造彻底执行完毕,跳出super()
├─ 第二步:开始初始化子类自己的实例变量 int k = prt(...)
│   └─ 打印 getToKnowConstructingOrder.k initialized.
└─ 第三步:执行子类构造剩余代码,打印 k=63、j=39

案例 4-3:设计两个类,类 Father 从 类 GrandFather 继承,类 Son 从 类 Father 继承,分析他们间的构造方法执行过程。

JAVA
class Grandfather {
    public Grandfather() {
        System.out.println("This is Grandfather!");
    }

    public Grandfather(String s) {
        System.out.println("This is Grandfather" + s);
    }
}

class Father extends Grandfather {
    public Father() {
        System.out.println("This is Father!");
    }

    public Father(String s) {
        System.out.println("This is Father!" + s);
    }
}

class Son extends Father {
    public Son() {
        System.out.println("This is Son!");
    }

    public Son(String s) {
        System.out.println("This is Son" + s);
    }
}

public class Construct {
    public static void main(String[] args) {
        Son son = new Son();
        System.out.println("******************************");
        Son son1 = new Son("**==**");
    }
}

运行结果:

PLAINTEXT
This is Grandfather!
This is Father!
This is Son!
******************************
This is Grandfather!
This is Father!
This is Son**==**

图片

3. 问答题

(1)阅读下面的程序(或程序片段),回答问题 。

现有类说明如下:

JAVA
class A {
    int x = 10;
    int GetA() {
        return x;
    }
}

class B extends A {
    int x = 100;
    int GetB() {
        return x;
    }
}

(1)类 A 与类 B 是什么关系?

答案:
  • 类 B 继承 类 A,B 是 A 的子类(派生类),A 是 B 的父类(基类)。

(2)类 B 是否能继承类 A 的属性 x?

答案:
  • 能。Java 中子类会继承父类的非私有成员变量和方法,本题中父类 A 的属性 x 没有私有修饰。

(3)若 b 是类 B 的对象,则 b.GetA() 的返回值是什么?

答案:
  • 核心关键点:方法属于哪个类,它里面访问的变量就属于哪个类
  • 返回值:10。原因:GetA() 是父类 A 的方法,方法中访问的 x 是父类自身的 x,不受子类同名变量影响。
  • 类比:爸爸写了一个函数 “看自己钱包”,儿子拿去调用这个函数,看到的永远是爸爸钱包里的 10,看不到儿子自己的 100。

(4)若 b 是类 B 的对象,则 b.GetB() 的返回值是什么?

答案:
  • 核心关键点:方法属于哪个类,它里面访问的变量就属于哪个类
  • 返回值:100。原因:GetB() 是子类 B 的方法,方法中访问的 x 是子类自身定义的 x。

(5)类 A 和类 B 都定义了 x 属性,这种现象称为什么?

答案:
  • 称为属性(成员变量)隐藏:子类定义了和父类同名的成员变量,子类的变量会隐藏父类的同名变量。
    • 子类重新定义了和父类名字一模一样的成员变量,不是覆盖父类的,而是自己另造一个,把父类的那个给 “藏起来” 看不见了。
    • 不是把父类 x 删掉,是两个 x 都存在:父类空间:x = 10;子类自己空间:x = 100。
    • 就近原则:在子类方法里直接写 x,默认用子类自己的。
(2)构造方法在类中的作用是什么?
答案:

创建对象时初始化对象的成员变量,为对象赋初始值;完成对象创建时的必要初始化操作(如资源加载、参数配置);是创建类对象的必须步骤,new 关键字创建对象时会自动调用构造方法。

(3)在创建派生类的对象的时候,基类与派生类中构造方法的调用顺序怎样?
答案:

默认情况下,子类构造方法会自动先调用父类的无参构造方法;如果父类没有无参构造,子类必须手动通过 super(参数) 调用父类有参构造,且必须放在子类构造方法第一行。

(4)什么是方法的重载?
答案:
  • 方法重载:在同一个类中,定义多个方法名相同,但参数列表不同的方法。
    • 方法名必须完全相同;
    • 参数列表必须不同(参数个数、参数类型、参数顺序任意一项不同);
    • 方法的返回值、访问修饰符不影响重载;
    • 编译器根据传入的参数自动匹配调用对应的方法。

4. 编程题

编写一个完整的 Java Application 程序。包含类 Circle,类 Cylinder, 类 Test,具体要求如下:

(1)类 Circle

① 属性:radius,double 型,表示圆的半径。

② 方法

  • Circle( double r):构造函数,将半径初始化为 r。
  • double findArea():返回圆的面积。
  • double getRadius():返回圆的半径。

(2)类 Cylinder,继承Circle类,并有以下属性和方法:

①属性:length,double 型,表示圆柱体的高。

②方法

  • Cylinder(double r,double l):构造函数,给圆柱体的半径和高赋初值。
  • double findVolume():返回圆柱体的体积。
  • toString():返回圆柱体的半径、高、体积等信息。

(3)主类Test

① 生成 Cylinder 对象。

② 调用对象的 toString 方法,输出对象的描述信息。

JAVA
class Circle {
    // 私有成员变量:半径
    private double radius;

    public Circle(double r) {
        radius = r;
    }

    // 计算圆的面积
    double findArea() {
        return Math.PI * radius * radius;
    }

    // 获取半径的getter方法
    double getRadius() {
        return radius;
    }
}

// 子类:圆柱类,继承圆类
class Cylinder extends Circle {
    // 子类私有成员变量:高度
    private double length;

    // 子类构造方法
    public Cylinder(double r, double l) {
        // 调用父类构造方法,必须放在第一行
        super(r);
        length = l;
    }

    // 计算圆柱体积
    public double findVolume() {
        return findArea() * length;
    }

    // 重写toString方法,自定义对象输出
    @Override
    public String toString() {
        return "圆柱信息:\n" +
                "半径 = " + getRadius() + "\n" +
                "高 = " + length + "\n" +
                "体积 = " + findVolume();
    }
}

public class Test {
    public static void main(String[] args) {
        // 创建圆柱对象
        Cylinder cylinder = new Cylinder(2.0, 5.0);
        // 打印对象信息
        System.out.println(cylinder.toString());
    }
}

运行结果:

PLAINTEXT
圆柱信息:
半径 = 2.0
高 = 5.0
体积 = 62.83185307179586

图片

二、课后习题

第四章:继承与多态

第四章 一、填空题
  1. 在 Java 程序语言中,它允许在一个 class 中有几个方法,都有相同的名字,这种用法称为 方法重载
  2. java 中 构造 方法与类名相同,没有返回值,在创建对象实例时由 new 运算符自动调用。
  3. Java 中常量定义的修饰符是 final
  4. 继承 是一种由已有的类创建新类的机制。
  5. Java 中由继承而得到的类成为 子类,被继承的类称为父类。
  6. Java 中不支持 多重 继承。
  7. 在类的声明中,通过使用关键字 extends 来创建一个类的子类。
  8. Java 中一个类可以有 多个间接的 父类。
  9. 子类自然地继承了其父类中不是 private 的成员变量作为自己的成员变量。
  10. 当子类中定义的成员变量和父类中的成员变量同名时,子类的成员变量 隐藏 了父类的成员变量。
  11. 子类通过成员变量的隐藏和方法的 覆盖 可以把父类的状态和行为改变为自身的状态和行为。
  12. 如果一个类的声明中没有使用 extends 关键字,这个类被系统默认为是 Object 的子类。
  13. final 类不能被继承,即不能有子类。
第四章 二、选择题
  1. 在某个类 A 中存在一个方法:void GetSort(int x),以下能作为这个方法的重载的声明的是 ( A )。
    (A)void GetSort (float x)(方法名相同,参数类型不同,构成合法重载)
    (B)int GetSort (int y)(参数列表完全一致,仅返回值不同,不能构成重载)
    (C)double GetSort (int x,int y)(虽参数个数不同,但不是本题最优选项,一般不改返回值类型)
    (D)void Get (int x,int y)(方法名不一致,不属于重载)

  2. 为了区分重载多态中同名的不同方法,要求 (A)。
    (A)采用不同的形式参数列表(区分重载的唯一依据,正确)
    (B)返回值类型不同(返回值不能区分重载,错误)
    (C)调用时用类名或对象名做前缀(调用方式不影响重载判断,错误)
    (D)参数名不同(参数名称不参与重载判定,错误)

  3. 下列选项中,用于在定义类头时声明父类名的关键字是 (C)。
    (A)return(方法返回关键字,和继承无关)
    (B)interface(定义接口的关键字)
    (C)extends(用于类继承,声明父类,正确)
    (D)class(定义普通类的关键字)

  4. 下列说法哪个是正确的?(C)
    (A)子类不能定义和父类同名同参数的方法(错误,子类可以重写同名同参方法)
    (B)子类只能继承父类的方法,而不能重载(错误,子类可自定义重载方法)
    (C)重载就是一个类中有多个同名但有不同形参和方法体的方法(正确,符合重载定义)
    (D)子类只能覆盖父类的方法,而不能重载(错误,子类既能重写也能重载)

  5. 关于类的继承以下说法错误的是 (D)。
    (A)在 java 中类只允许单一继承(正确,Java 类仅单直接继承)
    (B)在 java 中一个类可实现多个接口(正确,支持多实现接口)
    (C)在 java 中一个类可以同时继承一个类和实现一个接口(正确,单继承 + 多实现可共存)
    (D)java 允许多重继承(错误,Java 不支持类多重继承)

  6. Java 语言的类间的继承关系是 (B)。
    (A)多重的(错误,Java 类不允许多重继承)
    (B)单重的(正确,一个类只能直接继承一个父类)
    (C)线程的(和继承无关,错误)
    (D)不能继承(错误,Java 支持类继承)

  7. (与上面选择题的第 2 题重复!!!省略)

  8. 设有下面两个类的定义:

JAVA
class Person {
    long id; // 身份证号
    String name; // 姓名
}

class Student extends Person {
    int score; // 入学总分

    int getScore() {
        return score;
    }
}

则类 Person 和类 Student 的关系是 (B)。

(A)包含关系(代码无包含逻辑,错误)
(B)继承关系(extends 代表继承,Student 继承 Person,正确)
(C)关联关系(不符合代码逻辑,错误)
(D)上述类定义有语法错误(代码语法完全合法,错误)

  1. 在 Java 中,一个类可同时定义许多同名的方法,这些方法的形式参数个数、类型或顺序各不相同,传回的值也可以不相同。这种面向对象程序的特性称为(C)。
    (A)隐藏(指子类变量隐藏父类同名变量,不符合题意)
    (B)覆盖(子类重写父类方法,要求参数列表完全一致,不符合题意)
    (C)重载(同类中方法名相同、参数列表不同,符合题干描述,正确)
    (D)Java 不支持此特性(Java 明确支持方法重载,错误)

  2. A 派生出子类 B,B 派生出子类 C,并且在 Java 源代码中有如下声明:

JAVA
A a0 = new A();
A a1 = new B();
A a2 = new C();

问以下哪个说法是正确的?(D)

(A)只有第 1 行能通过编译(父类引用可以指向自身、直接子类、间接子类,2、3 行都合法,错误)
(B)第 1、2 行能通过编译,但第 3 行编译出错(A 是 C 的间接父类,父类引用可指向间接子类,3 行无编译错误,错误)
(C)第 1、2、3 行能通过编译,但第 2、3 行运行时出错(仅声明语句无方法调用,不会运行报错,错误)
(D)第 1 行、第 2 行和第 3 行的声明都是正确的(父类引用可以指向任意层级子类对象,三行语法全部合法,正确)

  1. 假设 A 类有如下定义,设 a 是 A 类的一个实例,下列语句调用哪个是错误的?(C)
JAVA
class A {
    int i;
    static String s;

    void method1() {
    }

    static void method2() {
    }
}

(A)System.out.println (a.i);(i 是实例变量,用对象 a 访问,合法)
(B)a.method1 ();(method1 是实例方法,通过对象调用,合法)
(C)A.method1 ();(method1 是非 static 实例方法,不能直接用类名调用,错误)
(D)A.method2 ();(method2 是 static 静态方法,可直接用类名调用,合法)

第四章 三、程序输出题
  1. 阅读下面的程序:
JAVA
public class Test {
    public static void main(String args[]) {
        Bird b = new Bird();
        b.Fly(3);
    }
}

class Bird {
    static int Type = 2;

    private void Fly(int an_Type) {
        Type = an_Type;
        System.out.println("Flying..." + Type);
    }
}

上面的程序编译是否成功?如果编译出错,指出哪行出错,并说明理由;如果编译正确,运行结果是什么?

答案:

编译不成功,错误出现在代码第 4 行 b.Fly(3);。Fly 方法使用 private 修饰,属于私有方法,私有方法只能在定义它的 Bird 类内部访问,外部的 Test 类无法通过对象调用私有方法,编译器会报错。

  1. 阅读下面的程序:
JAVA
abstract class Base {
    abstract public void myfunc();

    public void another() {
        System.out.println("Another method");
    }
}

public class Abs extends Base {
    public static void main(String argv[]) {
        Base b = new Abs();
        b.another();
    }

    public void myfunc() {
        System.out.println("My Func");
    }

    public void another() {
        myfunc();
    }
}

以上程序经编译后,运行结果是什么?

答案:
  • Abs 子类重写了父类 Base 的 another() 方法,属于方法覆盖;
  • 多态机制:父类引用 Base b 指向子类 Abs 对象,调用 b.another() 时,执行的是子类重写后的 another();
  • 子类 another() 内部调用 myfunc(),执行子类实现的 myfunc(),输出 My Func。

运行结果:

PLAINTEXT
My Func
  1. 写出以下程序的运行结果。
JAVA
public class Test_2 {
    public static void main(String[] args) {
        System.out.println(fun(3, 4, 5));
    }

    static int fun(int x, int y, int z) {
        return fun(x, fun(y, z));
    }

    static int fun(int x, int y) {
        return x * y;
    }
}
答案:

执行 fun(3,4,5),先计算内层 fun(4,5),两参数 fun 方法实现相乘:4 * 5 = 20;再执行 fun(3,20),3 * 20 = 60;最终打印输出 60

运行结果:

PLAINTEXT
60
  1. 阅读以下程序,写出输出结果。
JAVA
class First {
    public First() {
        aMethod();
    }

    public void aMethod() {
        System.out.println("in First class");
    }
}

public class Second extends First {
    public Second() {
        super();
        aMethod();
    }

    public void aMethod() {
        System.out.println("in Second class");
    }

    public static void main(String[] args) {
        new Second();
    }
}
答案:
  • 执行 new Second(),先执行子类构造里的 super(),调用父类 First 构造方法;
  • 父类构造中调用 aMethod(),由于子类重写了该方法,多态机制执行子类的 aMethod(),打印第一行 in Second class;
  • super() 执行完毕,回到子类构造,再次调用 aMethod(),依旧执行子类重写方法,打印第二行 in Second class。
  • 真相:实例方法调用遵循运行时多态,只看 new 出来的真实对象类型,和代码写在哪个类无关。
  • 如果子类没有重写该实例方法,JVM 向上找父类的实现,执行 First 的 aMethod(),输出:in First class

运行结果:

PLAINTEXT
in Second class
in Second class
  1. 阅读下面的程序,写出输出结果。
JAVA
import java.io.*;

public class Test {
    public static void main(String args[]) {
        Cylinder c = new Cylinder(2, 5);
        System.out.println(c.toString());
    }
}

class Circle {
    final float PI = 3.14159f;
    double radius;

    public Circle(double r) {
        radius = r;
    }

    public double getRadius() {
        return radius;
    }

    public double findArea() {
        return PI * getRadius() * getRadius();
    }
}

class Cylinder extends Circle {
    double height;

    public Cylinder(double r, double h) {
        super(r);
        height = h;
    }

    public double findvolume() {
        double volume;
        volume = this.findArea() * height;
        return volume;
    }

    public String toString() {
        String slt = "";
        slt = "The cylinder information:radius=" +
                String.valueOf(this.radius) + ",height=" +
                String.valueOf(this.height) + ",volume=" +
                String.valueOf(this.findvolume());
        return slt;
    }
}
答案:

创建圆柱对象 Cylinder(2,5),调用父类 Circle 构造,半径 radius=2.0,高 height=5.0;底面积 findArea() = 3.14159 * 2 * 2 = 12.56636;圆柱体积 findvolume() = 12.56636 * 5 = 62.8318;调用子类重写的 toString() 拼接字符串,输出对应信息。

PLAINTEXT
执行 new Cylinder(2,5);
├─ 第一步:隐式调用 super(2) 进入父类构造 Circle(double r)
│   ├─ 父类实例常量加载 PI=3.14159f
│   ├─ 父类实例变量 radius 默认0.0
│   ├─ radius = 2 给半径赋值
│   └─ 父类构造执行完毕,跳出 super()
├─ ↓ 父类构造彻底结束,回到子类构造
├─ 第二步:执行子类构造剩余代码 height = 5
└─ 第三步:Cylinder 对象创建完成,回到 main 执行 c.toString()
    ├─ 进入 toString 方法,拼接字符串
    │  ├─ 读取 this.radius=2.0
    │  ├─ 读取 this.height=5.0
    │  └─ 调用 this.findvolume() 计算体积
    │      ├─ 进入findvolume方法,执行 this.findArea()
    │      │   ├─ 进入父类 findArea()
    │      │   ├─ getRadius()返回 2.0
    │      │   └─ 算出底面积=3.14159*2*2=12.56636
    │      └─ volume = 12.56636 * 5 = 62.8318
    ├─ 拼接完整字符串
    └─ 返回字符串交给 println 打印输出

运行结果:

PLAINTEXT
The cylinder information:radius=2.0,height=5.0,volume=62.83180236816406
  1. 阅读下面的程序:
JAVA
class Super {
    public int i = 0;

    public Super() {
        i = 1;
    }
}

public class Sub extends Super {
    public Sub() {
        i = 2;
    }

    public static void main(String args[]) {
        Sub s = new Sub();
        System.out.println(s.i);
    }
}

上面的程序经编译后,运行结果是什么?

答案:

创建 Sub 对象时,子类构造默认先调用父类 Super 构造,父类构造将成员变量 i 赋值为 1;父类构造执行完毕,回到子类 Sub 构造,执行 i=2,覆盖原有值;打印对象 s 的成员变量 i,输出结果为 2

运行结果:

PLAINTEXT
2
  1. 阅读下面的程序:
JAVA
class ValHold {
    public int i = 10;
}

public class ObParm {
    public static void main(String argv[]) {
        ObParm o = new ObParm();
        o.amethod();
    }

    public void amethod() {
        int i = 99;
        ValHold v = new ValHold();
        v.i = 30;
        another(v, i);
        System.out.print(v.i);
        System.out.print(i);
    }

    public void another(ValHold v, int i) {
        i = 0;
        v.i = 20;
        ValHold vh = new ValHold();
        v = vh;
        System.out.print(v.i);
    }
}

上面程序编译是否成功?如果编译出错,指出哪行出错,并说明理由;如果编译正确,运行结果是什么?

答案:
  • 进入 amethod():局部变量 i = 99,创建 v 对象,v.i = 30,调用 another(v,i),执行 another(ValHold v, int i):形参 i 是值传递,修改 i = 0 不影响外部 amethod 里的 i;形参 v 是引用传递,先修改 v.i = 20;新建 vh(vh.i = 10),把局部形参 v 指向 vh;打印 v.i,输出 10。
  • 回到 amethod() 中 System.out.print(v.i):方法内的 v 引用没有被改变,指向原来对象,该对象 i 已被改为 20,输出 20。
  • 执行 System.out.print(i):打印 amethod 局部变量 i = 99,输出 99。
  • 拼接完整输出:102099。
PLAINTEXT
执行 main → new ObParm () → o.amethod ();
├─ 进入 amethod ()
│ ├─ 定义局部基本变量 int i = 99;
│ ├─ new ValHold (),堆中对象 v1:v1.i = 10
│ ├─ v.i = 30 → 堆内 v1 的 i 被改成 30
│ ├─ 调用 another (v, i),传入:引用 v(指向 v1)、数值 99
│ │ ├─ 进入 another 方法,形参:ValHold v(副本地址,仍指向 v1)、int i(副本 = 99)
│ │ ├─ i = 0; // 修改副本基本类型,外部 amethod 的 i 不受影响
│ │ ├─ v.i = 20; // 通过引用副本修改堆中 v1 对象属性,v1.i=20
│ │ ├─ new ValHold (),堆中对象 vh:vh.i=10
│ │ ├─ v = vh; // 仅修改方法内形参 v 的指向,外部 amethod 的 v 不变,依旧指向 v1
│ │ └─ System.out.print (v.i); // 打印当前形参 v 指向的 vh.i → 输出 10
│ └─ 回到 amethod () 继续执行
│ ├─ System.out.print (v.i); // 外部 v 仍指向 v1,v1.i=20 → 输出 20
│ └─ System.out.print (i); //amethod 局部 i 始终是 99 → 输出 99
└─ 全部执行结束

运行结果:

PLAINTEXT
102099
  1. 请写出下面程序的运行结果。
JAVA
public class Test extends TT {
    public static void main(String args[]) {
        Test t = new Test("Tom");
    }

    public Test(String s) {
        super(s);
        System.out.println("How do you do?");
    }

    public Test() {
        this("I am Tom");
    }
}

class TT {
    public TT() {
        System.out.println("What a pleasure!");
    }

    public TT(String s) {
        this();
        System.out.println("I am " + s);
    }
}
答案:
  • main 中执行 new Test("Tom"),进入带参构造 Test(String s);
  • 第一行执行 super("Tom"),调用父类 TT(String s);
  • TT(String s)内执行 this(),调用无参父类构造 TT(),打印:What a pleasure!;
  • 回到 TT(String s),执行打印:I am Tom;
  • 父类构造全部执行完毕,回到子类 Test(String s),执行打印:How do you do?。
  • 重点:
    • super():调用父类的构造方法
    • this():调用本类其他重载的构造方法

运行结果:

PLAINTEXT
What a pleasure!
I am Tom
How do you do?
第四章 四、问答题

(与上面实验内容的 3. 问答题 重复!!!省略)

第四章 五、编程题

(与上面实验内容的 4. 编程题 重复!!!省略)

评论