一、实验内容
掌握面向对象继承和多态。
1. 运行例 4-1 到 4-6。
例 4-1:定义两个类,一个是动物的类,另一个是人的类。
class Animal {
String nickName;
// 父类方法:动物发声
public void speak() {
System.out.println("Animal is speaking!");
}
}
// 子类:人类,使用 extends 继承 Animal
// 子类会自动继承父类非私有成员变量和方法
class People extends Animal {
// 子类空实现,直接继承父类功能
}
例 4-2:super 的用法。
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();
}
}运行结果:
一只动物被构造
一只狗被构造
Wang Wang!

例 4-3:通过 super 关键字来调用父类的成员。
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();
}
}运行结果:
一只动物被构造
Animal Speak!
一只狗被构造
Wang Wang!

例 4-4:编译时多态举例。
// 动物子类:狗
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
}
}运行结果
无参speak:没有小动物
调用Dog版本speak:小狗:汪汪汪
调用Cat版本speak:小猫:喵喵喵

例 4-5:运行时多态举例。
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);
}
}运行结果:
Wang Wang!
Meow Meow!
Animal Speak!

例 4-6:final 类的举例。
// 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);
}
}运行结果:
这是final类

2. 运行案例 4-1 到 4-3。
案例 4-1:设计一个类 Test,里面有三个静态的内部类 Animal、Dog 和 Cat,Dog 和 Cat 类从 Animal 类继承,并重写父类相关的方法实现多态性。
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();
}
}运行结果:
狗的睡觉方式
狗的吼叫方式
猫的睡觉方式
猫的吼叫方式

案例 4-2:编写两个类,GetToKnowConstructingOrder 类从 parent 继承,根据以下程序分析子类与父类程序执行的顺序。
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();
}
}运行结果:
static parent.x initialized.
getToKnowConstructingOrder.y initialized.
getToKnowConstructingOrder constructor.
i=9
j=39
getToKnowConstructingOrder.k initialized.
k=63
j=39

类加载 = 自动执行所有 static 代码
普通变量:要创建对象才会赋值。静态变量:程序一启动,自动赋值,自动运行。
静态的东西,永远最先跑。先跑父类静态,再跑子类静态。创建对象时,先跑父类,再跑子类。
先给变量赋值,再跑构造方法。
执行 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 继承,分析他们间的构造方法执行过程。
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("**==**");
}
}运行结果:
This is Grandfather!
This is Father!
This is Son!
******************************
This is Grandfather!
This is Father!
This is Son**==**

3. 问答题
(1)阅读下面的程序(或程序片段),回答问题 。
现有类说明如下:
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 方法,输出对象的描述信息。
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());
}
}运行结果:
圆柱信息:
半径 = 2.0
高 = 5.0
体积 = 62.83185307179586

二、课后习题
第四章:继承与多态
第四章 一、填空题
- 在 Java 程序语言中,它允许在一个 class 中有几个方法,都有相同的名字,这种用法称为 方法重载。
- java 中 构造 方法与类名相同,没有返回值,在创建对象实例时由 new 运算符自动调用。
- Java 中常量定义的修饰符是 final。
- 继承 是一种由已有的类创建新类的机制。
- Java 中由继承而得到的类成为 子类,被继承的类称为父类。
- Java 中不支持 多重 继承。
- 在类的声明中,通过使用关键字 extends 来创建一个类的子类。
- Java 中一个类可以有 多个间接的 父类。
- 子类自然地继承了其父类中不是 private 的成员变量作为自己的成员变量。
- 当子类中定义的成员变量和父类中的成员变量同名时,子类的成员变量 隐藏 了父类的成员变量。
- 子类通过成员变量的隐藏和方法的 覆盖 可以把父类的状态和行为改变为自身的状态和行为。
- 如果一个类的声明中没有使用 extends 关键字,这个类被系统默认为是 Object 的子类。
- final 类不能被继承,即不能有子类。
第四章 二、选择题
-
在某个类 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)(方法名不一致,不属于重载) -
为了区分重载多态中同名的不同方法,要求 (A)。
(A)采用不同的形式参数列表(区分重载的唯一依据,正确)
(B)返回值类型不同(返回值不能区分重载,错误)
(C)调用时用类名或对象名做前缀(调用方式不影响重载判断,错误)
(D)参数名不同(参数名称不参与重载判定,错误) -
下列选项中,用于在定义类头时声明父类名的关键字是 (C)。
(A)return(方法返回关键字,和继承无关)
(B)interface(定义接口的关键字)
(C)extends(用于类继承,声明父类,正确)
(D)class(定义普通类的关键字) -
下列说法哪个是正确的?(C)
(A)子类不能定义和父类同名同参数的方法(错误,子类可以重写同名同参方法)
(B)子类只能继承父类的方法,而不能重载(错误,子类可自定义重载方法)
(C)重载就是一个类中有多个同名但有不同形参和方法体的方法(正确,符合重载定义)
(D)子类只能覆盖父类的方法,而不能重载(错误,子类既能重写也能重载) -
关于类的继承以下说法错误的是 (D)。
(A)在 java 中类只允许单一继承(正确,Java 类仅单直接继承)
(B)在 java 中一个类可实现多个接口(正确,支持多实现接口)
(C)在 java 中一个类可以同时继承一个类和实现一个接口(正确,单继承 + 多实现可共存)
(D)java 允许多重继承(错误,Java 不支持类多重继承) -
Java 语言的类间的继承关系是 (B)。
(A)多重的(错误,Java 类不允许多重继承)
(B)单重的(正确,一个类只能直接继承一个父类)
(C)线程的(和继承无关,错误)
(D)不能继承(错误,Java 支持类继承) -
(与上面选择题的第 2 题重复!!!省略)
-
设有下面两个类的定义:
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)上述类定义有语法错误(代码语法完全合法,错误)
-
在 Java 中,一个类可同时定义许多同名的方法,这些方法的形式参数个数、类型或顺序各不相同,传回的值也可以不相同。这种面向对象程序的特性称为(C)。
(A)隐藏(指子类变量隐藏父类同名变量,不符合题意)
(B)覆盖(子类重写父类方法,要求参数列表完全一致,不符合题意)
(C)重载(同类中方法名相同、参数列表不同,符合题干描述,正确)
(D)Java 不支持此特性(Java 明确支持方法重载,错误) -
A 派生出子类 B,B 派生出子类 C,并且在 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 行的声明都是正确的(父类引用可以指向任意层级子类对象,三行语法全部合法,正确)
- 假设 A 类有如下定义,设 a 是 A 类的一个实例,下列语句调用哪个是错误的?(C)
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 静态方法,可直接用类名调用,合法)
第四章 三、程序输出题
- 阅读下面的程序:
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 类无法通过对象调用私有方法,编译器会报错。
- 阅读下面的程序:
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。
运行结果:
My Func
- 写出以下程序的运行结果。
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
运行结果:
60
- 阅读以下程序,写出输出结果。
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
运行结果:
in Second class
in Second class
- 阅读下面的程序,写出输出结果。
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() 拼接字符串,输出对应信息。
执行 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 打印输出运行结果:
The cylinder information:radius=2.0,height=5.0,volume=62.83180236816406
- 阅读下面的程序:
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
运行结果:
2
- 阅读下面的程序:
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。
执行 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
└─ 全部执行结束运行结果:
102099- 请写出下面程序的运行结果。
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():调用本类其他重载的构造方法
运行结果:
What a pleasure!
I am Tom
How do you do?
第四章 四、问答题
(与上面实验内容的 3. 问答题 重复!!!省略)
第四章 五、编程题
(与上面实验内容的 4. 编程题 重复!!!省略)
评论