ALPHA第四章 多态,接口,抽象类

发布于:2025-02-11 ⋅ 阅读:(42) ⋅ 点赞:(0)

在给出的选项中,错误的叙述是

子类可以继承父类的构造函数

详细分析:

1. 子类可以继承父类的构造函数

错误的
在 Java 中,子类不能继承父类的构造函数。构造函数是用来初始化对象的,因此构造函数是不能被继承的,但子类可以通过 super() 显式调用父类的构造函数。

2. 子类中调用父类构造函数不可以直接书写父类构造函数,而应该用 super();

正确的
子类的构造函数不能直接写父类的构造函数,而是应该使用 super() 来显式调用父类的构造函数。super() 必须是构造函数的第一行代码。通过 super() 可以调用父类的无参或带参构造函数。

示例:

class Parent {

Parent() {

System.out.println("Parent Constructor");

}

}

class Child extends Parent {

Child() {

super(); // 调用父类的构造函数 System.out.println("Child Constructor");

}

}

3. 用 new 创建子类的对象时,若子类没有带参构造函数,将先执行父类的无参构造函数,然后再执行自己的构造函数

正确的
如果子类没有定义带参构造函数,Java 会自动提供一个无参构造函数。在创建子类对象时,子类的构造函数会先执行,若没有显式调用父类构造函数,Java 会默认调用父类的无参构造函数。然后,子类构造函数的内容会被执行。

示例:

class Parent {
    Parent() {
        System.out.println("Parent Constructor");
    }
}

class Child extends Parent {
    Child() {
        super();  // 调用父类的构造函数
        System.out.println("Child Constructor");
    }
}
 

4. 子类的构造函数中可以调用其他函数

正确的
在子类的构造函数中,完全可以调用其他方法(包括父类的方法)。不过需要注意的是,方法调用应该是在构造函数的主体部分,而不能替代构造函数的调用逻辑。

示例:

class Parent {
    Parent() {
        System.out.println("Parent Constructor");
    }
}

class Child extends Parent {
    Child() {
        System.out.println("Child Constructor");
    }
}

public class Test {
    public static void main(String[] args) {
        new Child();  // 输出顺序是:Parent Constructor -> Child Constructor
    }
}

总结:

  • 错误的选项是:“子类可以继承父类的构造函数”。
  • 其他的选项都是正确的叙述。

1.extends只能接一个,继承据有传递性,但不具有多继承

2.implements可以多接口

多态

注意:

protected方法可以被子类所访问

在 Java 中,Person p = new Child(); 是一个非常常见的 向上转型(upcasting)操作。让我们详细解释这一行代码的含义和背后的概念。

1. 基本的类继承关系

首先,理解这段代码需要知道类 PersonChild 的继承关系:

public class Person {
    private String name = "Person";  // private:只能在当前类中访问
    int age = 0;  // 默认访问修饰符:包内可见
}

public class Child extends Person {
    public String grade;
}
  • Child 是 Person 的子类。
  • Person 类有两个字段:一个 private String name 和一个 int agename 是 private,只能在 Person 类内部访问;age 是包内可见(默认修饰符),可以在同一个包内的其他类中访问。
  • Child 类继承了 Person 类,并且可以拥有自己的字段和方法。当前例子中,Child 只有一个字段 grade

2. 向上转型(Upcasting)

这行代码:

Person p = new Child();

向上转型 的一个例子。具体来说,它是将 Child 类型的对象赋值给一个 Person 类型的变量 p

向上转型 的意思是:将子类的对象赋给父类类型的变量。因为 ChildPerson 的子类,所以 Child 类型的对象可以被赋给 Person 类型的变量。Java 中允许这种赋值操作,且它是隐式的,不需要显式转换。

3. 为什么可以这么做?

在面向对象编程(OOP)中,子类对象是父类对象的一种特殊类型。也就是说,ChildPerson 的一种类型,因此可以通过父类类型(Person)来引用子类对象(Child)。这是继承的多态特性。

  • Child 是 Person 的子类,因此 Child 对象不仅拥有 Child 类的成员(字段和方法),还继承了 Person 类的成员(字段和方法)。
  • 通过 Person p = new Child();,你可以在 p 变量中引用 Child 类型的对象,但只能访问 Person 类中定义的公共方法和字段。

4. 访问权限和方法

由于 p 的类型是 Person,所以你只能通过 p 访问 Person 类中的字段和方法。例如,在上面的代码中,Person 类有一个私有字段 name,它在 Child 类的对象中是不可访问的。即使 p 引用的是一个 Child 对象,你仍然只能访问 Person 类中 p 能访问的字段(如 age)。

举个例子:

public class Person {
    private String name = "Person";  // 只能在Person类中访问
    int age = 0;  // 在同一包内的类可以访问

    public String getName() {
        return name;
    }
}

public class Child extends Person {
    public String grade;

    public void printGrade() {
        System.out.println(grade);
    }
}

public class Test {
    public static void main(String[] args) {
        Person p = new Child();  // 向上转型,Child对象赋给Person类型的变量
        System.out.println(p.age);  // 可以访问父类的age字段
        // System.out.println(p.grade);  // 错误:p是Person类型,无法访问Child类的grade字段
    }
}

5. 总结

Person p = new Child(); 是一个合法的语法,它表示:

  • 创建了一个 Child 类型的对象;
  • 使用 Person 类型的变量 p 引用这个 Child 对象。

通过 p 这个 Person 类型的引用,我们只能访问 Person 类中定义的成员(比如 age),而不能直接访问 Child 类中特有的成员(比如 grade)。但是,我们可以通过向下转型(downcasting)将 p 强制转换回 Child 类型,从而访问 Child 中特有的成员。

例如:

Child c = (Child) p;  // 向下转型
c.grade = "A";  // 访问Child类的grade字段

在这个问题中,我们需要分析 Parent 类中的方法及其访问修饰符的权限,并判断子类能否继承和覆盖这些方法。下面是对每个选项的逐一分析:

1. 子类中一定能够继承和覆盖 Parent 类的 m1 方法

  • m1() 方法是 private 修饰的。private 修饰符的作用是限制方法的访问范围,表示该方法只能在当前类内部访问,子类无法访问。
  • 结论:子类不能继承和覆盖 m1 方法。

因此,这个选项是错误的

2. 子类中一定能够继承和覆盖 Parent 类的 m2 方法

  • m2() 方法是包级私有的(即没有修饰符,默认是 "default"),这意味着该方法在同一包内的类中是可以访问的,但在不同包中的类中不可访问。
  • 如果子类和父类在同一个包内,子类可以继承和覆盖 m2() 方法。
  • 如果子类与父类不在同一个包内,则子类无法访问 m2() 方法,无法继承和覆盖。
  • 结论:只有在子类与父类处于同一包时,子类才能继承和覆盖 m2 方法。

因此,这个选项是部分正确的(子类能继承和覆盖,前提是它们在同一个包中)。

3. 子类中一定能够继承和覆盖 Parent 类的 m3 方法

  • m3() 方法是 protected 修饰的。protected 修饰符的作用是,允许该方法在同一包中的类以及所有子类中访问。
  • 无论子类是否与父类处于同一包中,子类都可以继承和覆盖 m3() 方法。
  • 结论:子类一定能够继承和覆盖 m3 方法。

因此,这个选项是正确的

4. 子类中一定能够继承和覆盖 Parent 类的 m4 方法

  • m4() 方法是 public static 修饰的。public 表示该方法可以被任何类访问,而 static 表示该方法是静态的。
  • 静态方法是属于类的,而不是实例的,因此子类不能像实例方法一样重写静态方法。子类可以继承静态方法,但是不能覆盖静态方法(即不允许通过子类来改变静态方法的实现)。
  • 结论:子类能够继承 m4 方法,但不能覆盖它。

因此,这个选项是错误的,因为子类不能覆盖静态方法。

总结:

  • 子类一定能够继承和覆盖 Parent 类的 m1 方法:错误
  • 子类一定能够继承和覆盖 Parent 类的 m2 方法:部分正确(仅当子类与父类在同一包时)
  • 子类一定能够继承和覆盖 Parent 类的 m3 方法:正确
  • 子类一定能够继承和覆盖 Parent 类的 m4 方法:错误

在这个问题中,我们需要分析每个 System.out.println() 输出的结果,来确定输出 true 的个数。

首先,来看一下类的继承关系:

class A{}

class B extends A{}

class C extends A{}

class D extends B{}

  • A 是基类。
  • B 和 C 都继承自 A
  • D 继承自 B

接下来,看看 A obj = new D(); 这行代码的含义。它声明了一个类型为 A 的引用 obj,并将一个 D 类的实例赋值给它。由于 D 继承自 B,而 B 又继承自 A,所以 obj 实际上是指向一个 D 类型的对象。

分析 instanceof 的每个表达式

  1. obj instanceof B

    • obj 是一个 D 类的实例,而 D 继承自 B
    • 因此,obj instanceof B 返回 true,因为 obj 是 B 类型的一个实例(通过继承关系)。
  2. obj instanceof C

    • obj 是 D 类的实例,而 C 和 B 是平行的(即 C 不是 B 的子类,也不是 B 的父类)。
    • 所以 obj instanceof C 返回 false,因为 obj 既不是 C 类型,也不属于 C 的继承体系。
  3. obj instanceof D

    • obj 是 D 类的实例,因此 obj instanceof D 返回 true
  4. obj instanceof A

    • obj 是 D 类的实例,而 D 继承自 BB 继承自 A,因此 obj 也是 A 的一个实例。
    • 所以 obj instanceof A 返回 true

总结:

  • obj instanceof B 返回 true
  • obj instanceof C 返回 false
  • obj instanceof D 返回 true
  • obj instanceof A 返回 true

因此,输出 true 的个数是 3

答案:3

静态类不能被继承

举一个实际的例子,说明 super()this() 的使用,以及为什么某些描述是错误的。

例子 1:super() 和 this() 必须放在构造方法的第一行

class Animal {
    public Animal() {
        System.out.println("我是一只动物");
    }
}

class Dog extends Animal {
    public Dog() {
        // 错误:super() 必须是第一行
        System.out.println("我是一只狗");
        super();  // 编译错误
    }
}

public class Test {
    public static void main(String[] args) {
        Dog dog = new Dog();  // 编译错误
    }
}
解释:
  • super() 必须是构造方法中的第一行。如果你在构造方法里先执行了其他语句(如 System.out.println("我是一只狗")),然后再调用 super(),就会导致编译错误。

例子 2:super() 和 this() 不能同时出现在同一个构造函数中

class Animal {
    public Animal() {
        System.out.println("我是一只动物");
    }
}

class Dog extends Animal {
    public Dog() {
        // 错误:不能同时使用 super() 和 this()
        this("小狗");   // 错误
        super();       // 错误
        System.out.println("我是一只狗");
    }

    public Dog(String name) {
        System.out.println("狗的名字是:" + name);
    }
}

public class Test {
    public static void main(String[] args) {
        Dog dog = new Dog();  // 编译错误
    }
}
解释:
  • 在一个构造方法中,不能同时使用 super() 和 this()。构造方法中只能使用其中之一,且必须是第一行语句。Java 不允许 this() 和 super() 在同一个构造方法中同时出现。

例子 3:super() 和 this() 不能在 static 方法中使用

class Animal {
    public Animal() {
        System.out.println("我是一只动物");
    }
}

class Dog extends Animal {
    public Dog() {
        super();
        System.out.println("我是一只狗");
    }

    public static void staticMethod() {
        // 错误:不能在静态方法中使用 super() 或 this()
        super();  // 编译错误
        this();   // 编译错误
    }
}

public class Test {
    public static void main(String[] args) {
        Dog.staticMethod();  // 编译错误
    }
}
解释:
  • super() 和 this() 不能在 static 方法中使用。它们都依赖于实例对象,而 static 方法属于类级别的方法,不依赖于实例。因此,尝试在 static 方法中使用 super() 或 this() 会导致编译错误。

例子 4:super() 是构造方法的第一行

class Animal {
    public Animal() {
        System.out.println("我是一只动物");
    }
}

class Dog extends Animal {
    public Dog() {
        // 正确:super() 必须是第一行
        super();  // 父类构造方法必须是第一行
        System.out.println("我是一只狗");
    }
}

public class Test {
    public static void main(String[] args) {
        Dog dog = new Dog();  // 输出:我是一只动物\n我是一只狗
    }
}
解释:
  • 这是一个正确的例子。子类 Dog 的构造方法中,super() 被放在了第一行,并且成功调用了父类 Animal 的构造方法。

结论:

  • super() 必须是构造方法的第一行。
  • super() 和 this() 不能同时出现在同一个构造函数中。
  • super() 和 this() 不能在静态方法中使用。

方法重写(Method Overriding) 是面向对象编程中的一个重要概念,主要出现在继承的情境中。它指的是在子类中重新定义父类已经定义过的方法,以改变或者扩展父类方法的行为。

关键特点:

  1. 继承关系:方法重写是发生在子类与父类之间的。子类继承了父类的属性和方法,但有时子类希望对父类的方法进行不同的实现。
  2. 方法签名相同:重写的方法必须与父类中的方法有相同的签名,即方法名、参数列表和返回类型必须一致。否则,它就不是重写,而是方法的重载(Overloading)。
  3. 实现不同:子类重写父类的方法后,可以提供不同的实现,以便满足特定的需求。这样,子类可以修改或扩展父类的行为,而不需要改变父类本身的代码。
  4. 多态性:方法重写是实现多态性的基础。当父类引用指向子类对象时,调用的将是子类中重写的方法,而不是父类的方法。

1.父类中的同名成员方法被屏蔽

填空题

Java规定,如果子类中定义的成员方法与父类中定义的成员方法同名,并且参数的个数和类型以及(返回值)的类型也相同, 则父类中的同名成员方法被屏蔽。

2.所有类的父类Object

3.不可被继承的类

填空题

定义一个Java类时,如果前面使用(final)关键字修饰,那么该类不可以被继承。

4.

继承的定义

填空题

在Java语言中,允许使用已存在的类作为基础创建新的类,这种技术称为(继承)

5.创建一个人的类Student

程序填空

创建一个人的类Student,属性包括姓名和年龄,方法包括构造方法(初始化一个人的姓名和年龄)、显示姓名和年龄的方法;创建一个学生类Prog1,是从Student类继承而来,Prog1类比Student类多一个成员变量“所在学校”,Prog1的方法包括构造方法(借助父类的方法对学生的三个属性进行初始化)和显示学生的三个属性方法;最后创建一个学生对象并显示其自然信息。

形状的多态性

程序填空

下面的程序设计一个名为的超类Shape,它定义所有形状的公共接口(或行为)。所有形状都有一个称为的方法getArea(),该方法返回该特定形状的面积。

补全下面代码,使得父类的引用分别指向三角形和矩形。

class Shape {
   private String color;
   public Shape (String color) {
      this.color = color;
   }
   public String toString() {
      return "Shape[color=" + color + "]";
   }
   public double getArea() {
      System.err.println("Shape unknown! Cannot compute area!");
      return 0;
   }
}
class Rectangle extends Shape {
   private int length, width;
   public Rectangle(String color, int length, int width) {
      super(color);
      this.length = length;
      this.width = width;
   }
   public String toString() {
      return "Rectangle[length=" + length + ",width=" + width + "," + super.toString() + "]";
   }
   public double getArea() {
      return length*width;
   }
}
class Triangle extends Shape {
   private int base, height;
   public Triangle(String color, int base, int height) {
      super(color);
      this.base = base;
      this.height = height;
   }
   public String toString() {
      return "Triangle[base=" + base + ",height=" + height + "," + super.toString() + "]";
   }
   public double getArea() {
      return 0.5*base*height;
   }
}
public class Program{
    public static void main(String[] args){
        Shape s1 = new Rectangle("red", 4, 5);
        System.out.println(s1);
        Shape s2 = new Triangle ("blue", 4, 5);
        System.out.println(s2);  
    }
}

为了完成这个程序,我们需要判断 an1 的本质类型,并根据其类型执行相应的代码。由于 DogCat 都是实现了 Animal 接口的类,我们需要通过 instanceof 运算符来判断 an1 是否是 Cat 类型的实例。

解释:public class Program {
    public static void main(String[] args) {
        Animal an1 = new Dog(); // 创建一个Dog对象,并将其赋值给Animal类型的变量an1
        
        // 判断an1是否是Cat类型的实例
        if(an1 instanceof Cat){ 
            Cat cat = (Cat) an1; // 强制转换an1为Cat类型
            cat.shout(); // 调用Cat类的shout方法
            cat.catchMouse(); // 调用Cat类的catchMouse方法
        }else{
            System.out.println("该类型的对象不是Cat类型!"); // 如果an1不是Cat类型,输出提示信息
        }
    }
}

instanceof 运算符用于判断对象是否是某个类的实例。此处,我们判断 an1 是否是 Cat 类的实例。

  1. 如果 an1Cat 类型的对象(即 an1 instanceof Cattrue),则进行强制类型转换,将 an1 转换为 Cat 类型并调用 shout()catchMouse() 方法。

  2. 如果 an1 不是 Cat 类型的对象,则输出 "该类型的对象不是Cat类型!"。

注意:

  • 在你的代码中an1 被初始化为 new Dog(),所以它实际上是 Dog 类型的对象,因此 instanceof Cat 判断会返回 false,并且程序会输出 "该类型的对象不是Cat类型!"。
  • 如果你将 an1 初始化为 new Cat(),那么 instanceof Cat 判断会为 true,然后会调用 Cat 类的方法。

乐器的多态实现

程序填空

下面的代码,实现多态,乐器(Instrument )分为:钢琴( Piano )、小提琴( Violin ) ,各种乐器的弹奏( play )方法各不相同。

填空使得,依据乐器的不同,进行相应的弹奏。

动物类实现多态

程序填空

下面的代码编写 Animal 及其实现类。

定义了测试类 Program,请补全代码,使得程序输出 抓老鼠