继承(inheritance)语法

本文介绍了Java中的继承概念,包括如何通过extends关键字实现类的继承,以及如何在子类中覆盖父类方法并通过super关键字调用父类方法。此外,还讨论了在不同类中设置main方法以便于单元测试的做法。
继承是所有 OOP 语言和 Java 语言的组成部分。当你在创建一个类时,你总是在继承,因
此,除非你已明确指出要从其他类中继承,否则你就是在隐式地从 Java 的标准根源类 object
进行继承。
组合的语法比较平实,但要执行继承,其语法却要采用截然不同的形式。在继承过程中,你
需要先声明:“新类与旧类相似。”通常,你首先给类确定一个名称,但在书写类主体的左边
花括号之前,应先写下关键字 extends,并在其后写下基类的名称。当你这么做时,会自动
得到基类中所有的数据成员和成员方法。例如:


//: c06:Detergent.java
// Inheritance syntax & properties.
import com.bruceeckel.simpletest.*; 


class Cleanser { 
protected static Test monitor = new Test();
private String s = new String("Cleanser");
public void append(String a) { s += a; } 
public void dilute() { append(" dilute()"); } 
public void apply() { append(" apply()"); } 
public void scrub() { append(" scrub()"); } 
public String toString() { return s; }
public static void main(String[] args) { 
    Cleanser x = new Cleanser();
    x.dilute(); x.apply(); x.scrub(); 
    System.out.println(x); 
    monitor.expect(new String[] { 
"Cleanser dilute() apply() scrub()"
    });
  }
}


public class Detergent extends Cleanser { 
// Change a method:
public void scrub() {
    append(" Detergent.scrub()");
super.scrub(); // Call base-class version
  }
// Add methods to the interface:
public void foam() { append(" foam()"); } 
// Test the new class:
public static void main(String[] args) { 
    Detergent x = new Detergent(); 
    x.dilute();
    x.apply();
    x.scrub();
    x.foam();
    System.out.println(x); 
    System.out.println("Testing base class:");
 
    monitor.expect(new String[] { 
"Cleanser dilute() apply() " + 
"Detergent.scrub() scrub() foam()",
"Testing base class:",
    });
    Cleanser.main(args); 
  }
} ///:~


这个程序示范了 Java 的许多特性。首先,在 Cleanser  的 append( )方法中,我们用“+=” 操
作符将几个 String 对象连接成 s,此操作符是 Java 设计者重载的用来处理 String 对象的两
个操作符之一(另一个是“+”)。


其次,Cleanser 和 Detergent 均含有 main( )方法。你可以为你的每个类都创建一个 main( )
方法。一般我们都建议以这种方式来编写程序代码,以使测试代码被包装在类中。即使是一
个程序中含有多个类,也只有命令行所调用的那个类的 main( )方法会被调用(只要这个
main( )是 public,其所属的类是否为 public 则不用考虑)。因此,在此例中,如果你的命令
行是 java Detergent,那么 Detergent.main( )将会被调用。但即使 Cleanser 不是一个 public 类,
如果你的命令行是 java Cleanser,那么 Cleanser.main( ) 仍然会被调用。这种在每个类中都设
置一个 main( )方法的技术可使每个类的单元测试都变得简便易行。而且你在完成单元测试
之后,也无需删除 main( ),你可以将其留待下次测试。


在此例中,你可以看到 Detergent.main ( )明确调用了 Cleanser.main( ),并将其从命令行获取
的参数传递给了它。当然,你也可以向其传递任意的 String  数组。Cleanser 中所有的方法
都必须是 public 的,这一点非常重要。请记住,如果你没有加任何访问权限修饰符,那么成
员缺省的访问权限是 package,它仅允许包内的成员访问。因此,在此包中,如果没有访问
权限修饰符,任何人都可以使用这些方法。例如,Detergent 就不成问题。但是,其他包中
的某个类若要从 Cleanser 中继承,则只能访问 public 成员。所以,为了继承,一般的规则是
将所有的数据成员都指定为 private,将所有的方法指定为 public(稍后将会学到,protected
成员也可以借助导出类来访问)。当然,在特殊情况下,你必须做出调整,但上述方法的确
是一个很有用的规则。


请注意,在 Cleanser 接口中有一组方法:append()、dilute()、apply()、scrub()和 toString()。
由于 Detergent 是由关键字 extends 从 Cleanser 导出的,所以它可以在其接口中自动获得这
些方法,尽管你并不能地看到这些方法在 Detergent 中的显式定义。因此,你可以将继承视
作是对类的复用。


正如我们在 scrub()中所见,对在基类中定义的方法进行修改是可行的。在此例中,你可能
想要在新版本中调用从基类继承而来的方法。但是在 scrub()中,你并不能直接调用 scrub(),
因为这样做将会产生递归,而这并不是你所期望的。为解决此问题,Java 提供了关键字 super。
关键字 super 指代的是当前类所继承的基类。为此,表达式 super.scrub()将调用基类版本的
scrub()方法。


在继承的过程中,你并不一定非得使用基类的方法。你也可以在导出类中添加新方法,其添
 
加方式与在类中添加任意方法一样,即对其加以定义即可。foam()方法即为一例。


在 Detergent.main( )中,你会发现,对于一个 Detegent 对象而言,你除了可以调用 Detergent

的方法(如 foam())之外,还可以调用 Cleanser 中所有可用的方法。


<think>我们正在讨论C#中的面向对象编程核心概念:封装、继承和多态。根据引用,我们可以总结如下: 1. 封装:通过访问修饰符(如public、private、protected等)控制对类成员的访问,将数据和行为捆绑在一起,并隐藏内部实现细节。 2. 继承:允许一个类(子类)继承另一个类(父类)的成员,从而实现代码重用。C#只支持单继承(一个类只能有一个直接父类)。 3. 多态:分为编译时多态(通过方法重载实现)和运行时多态(通过虚方法和重写实现)。 接下来,我们将分别给出这三个概念的语法和示例。 ### 1. 封装 封装通过访问修饰符来实现,常用的有: - `public`:任何地方都可以访问。 - `private`:只能在当前类内部访问。 - `protected`:在当前类内部和派生类中访问。 - `internal`:同一程序集内可以访问。 示例: ```csharp public class Person { private string name; // 私有字段,封装 public string Name // 公有属性,提供对name的访问 { get { return name; } set { name = value; } } public void Display() // 公有方法 { Console.WriteLine($"Name: {name}"); } } ``` 在这个例子中,字段`name`被封装在类`Person`中,外部只能通过属性`Name`来访问。 ### 2. 继承 使用`:`符号表示继承关系。 示例: ```csharp public class Student : Person // Student继承自Person { public int StudentId { get; set; } public void Study() { Console.WriteLine("I am studying."); } } ``` `Student`类继承了`Person`类的所有公有和受保护成员(如`Name`属性和`Display`方法),并添加了自己的属性`StudentId`和方法`Study`。 ### 3. 多态 #### 编译时多态(方法重载) 在同一个类中,方法名相同但参数列表不同(参数类型、个数或顺序不同)。 示例: ```csharp public class Calculator { public int Add(int a, int b) { return a + b; } public double Add(double a, double b) { return a + b; } } ``` #### 运行时多态(虚方法和重写) 在父类中声明虚方法(`virtual`),在子类中使用`override`关键字重写。 示例: ```csharp public class Shape { public virtual void Draw() { Console.WriteLine("Drawing a shape."); } } public class Circle : Shape { public override void Draw() // 重写Draw方法 { Console.WriteLine("Drawing a circle."); } } public class Rectangle : Shape { public override void Draw() // 重写Draw方法 { Console.WriteLine("Drawing a rectangle."); } } ``` 使用多态: ```csharp Shape[] shapes = new Shape[2]; shapes[0] = new Circle(); shapes[1] = new Rectangle(); foreach (Shape shape in shapes) { shape.Draw(); // 将根据实际对象类型调用相应的Draw方法 } ``` 输出: ``` Drawing a circle. Drawing a rectangle. ``` ### 总结 - 封装:隐藏实现细节,通过访问修饰符控制访问。 - 继承:子类继承父类,实现代码重用(单继承)。 - 多态:编译时多态(重载)和运行时多态(虚方法重写)。 以上示例展示了C#中面向对象编程的核心概念。 §§相关问题§§ 1. C#中除了虚方法,还有哪些方式可以实现运行时多态? 2. 在C#中,如何防止一个类被继承? 3. 封装中,属性(Property)和字段(Field)有什么区别? 4. 在继承中,子类如何调用父类的构造函数?</think>### C#面向对象编程核心概念 #### 1. 封装 (Encapsulation) 封装通过访问修饰符控制数据访问权限,将数据和行为捆绑在类中: ```csharp public class BankAccount { private double balance; // 私有字段封装数据 public void Deposit(double amount) // 公有方法封装行为 { if (amount > 0) balance += amount; } public double GetBalance() => balance; // 受控访问 } ``` - **访问修饰符**: - `private`:仅类内访问 - `public`:完全开放访问 - `protected`:类内及子类可访问 - `internal`:同一程序集内可访问[^4] #### 2. 继承 (Inheritance) 通过`:`实现单继承关系(C#不支持多继承): ```csharp public class Animal // 基类 { public string Name { get; set; } public void Eat() => Console.WriteLine("Eating..."); } public class Dog : Animal // 派生类 { public void Bark() => Console.WriteLine("Woof!"); } // 使用 var myDog = new Dog(); myDog.Eat(); // 继承基类方法 myDog.Bark(); // 派生类特有方法 ``` #### 3. 多态 (Polymorphism) 分为编译时多态和运行时多态: **编译时多态(重载)**: ```csharp public class Calculator { // 方法重载 public int Add(int a, int b) => a + b; public double Add(double a, double b) => a + b; // 参数类型不同 } ``` **运行时多态(重写虚方法)**: ```csharp public class Shape { public virtual void Draw() => Console.WriteLine("Drawing shape"); } public class Circle : Shape { public override void Draw() => Console.WriteLine("Drawing circle"); // 重写方法 } // 使用 Shape shape = new Circle(); shape.Draw(); // 输出"Drawing circle" (运行时决定调用) ``` - `virtual`:声明可重写的方法 - `override`:子类重写实现[^2] ### 完整示例 ```csharp public class Vehicle // 封装 { private int speed; // 私有字段 public int Speed // 属性封装 { get => speed; set => speed = value > 0 ? value : 0; } } public class Car : Vehicle // 继承 { public virtual void StartEngine() => Console.WriteLine("Engine started"); } public class ElectricCar : Car // 多态 { public override void StartEngine() => Console.WriteLine("Silent electric start"); } // 使用 Vehicle vehicle = new ElectricCar(); vehicle.Speed = 120; // 使用封装属性 ((Car)vehicle).StartEngine(); // 输出"Silent electric start" ``` ### 关键特性总结 | 概念 | 关键语法 | 作用 | |----------|-------------------|--------------------------------| | 封装 | `private/protected` | 数据隐藏,访问控制[^4] | | 继承 | `:` | 代码复用,扩展功能[^3] | | 多态 | `virtual/override` | 同一接口不同实现[^1][^2] |
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值