类图可以显示吗?

master
高宏宇 2 years ago
parent 107648bb18
commit 744ffc33f5

@ -0,0 +1,662 @@
## 1. Inheritance继承 and Polymorphism多态
> Suppose you will define classes to model circles, rectangles, and triangles. These classes have many common features. What is the best way to design these classes so to avoid redundancy? The answer is to use inheritance.
### 1.1. 如何降低描述荣誉
如何降低代码的冗余例如在C语言中通过什么方式降低代码的冗余。
在C语言中降低代码冗余增加代码复用性一般使用函数来实现但是函数是一个高纬度的抽象和我们日常生活联系并不紧密。那么我们考虑日常生活中的例子。
通常我们描述一猫,只用说明猫是:界:动物界;门:脊椎动亚门;纲:哺乳纲;目:食肉目;科:猫科;属:猫属;种:猫种。
![Alt text](img/%E7%94%9F%E7%89%A9%E5%88%86%E7%B1%BB.drawio.svg)
上述的图表示物种分类的一部分。这样描述一个物种的分类有很多好处:
1. 清楚表明了祖先与后代的关系(继承树);
2. 后代具备祖先的所有特性;
3. 明确不同种之间的关系。
如果大家都清楚上述的规则当我们讨论猫种的时候就默认猫种具备祖先的所有特性而不必再说明猫种是脊椎动物胎生等这些它祖先所具备的特性。这种特性在Java种的类种也存在这就是继承。
### 1.2. Java中的继承
例如要在平面上描述两个类圆和矩形那么这两个类好像有一些基本的特性是一致的。例如填充颜色color是否填充 filled对象创建时间 dateCreated等。另外需要对上述属性设置或者读取的方法getColor()setColor()isFilled()setFilled()等。
如果每个类都需要描述所有的这些特性好像有点浪费如何做到降低代码冗余我们使用到继承如下图表述GeometrocObject是父类保护所有子类共有的特性数据和方法Circle和Rectangle继承于GeometrocObject。这样在编写Circle和Rectangle的时候就只需要对其特性进行描述就可以了这两个类的共性在其父类中以及体现出来了子类完全继承父类的所有特性数据和方法
![image-20230311122706219](img/image-20230311122706219.png)
代码实现如下:
#### 1.2.1. 父类
SimpleGeometricObject
```java
public class SimpleGeometricObject {
private String color = "white";
private boolean filled;
private java.util.Date dateCreated;
/** Construct a default geometric object */
public SimpleGeometricObject() {
dateCreated = new java.util.Date();
}
/**
* Construct a geometric object with the specified color and filled value
*/
public SimpleGeometricObject(String color, boolean filled) {
dateCreated = new java.util.Date();
this.color = color;
this.filled = filled;
}
/** Return color */
public String getColor() {
return color;
}
/** Set a new color */
public void setColor(String color) {
this.color = color;
}
/**
* Return filled. Since filled is boolean, its get method is named isFilled
*/
public boolean isFilled() {
return filled;
}
/** Set a new filled */
public void setFilled(boolean filled) {
this.filled = filled;
}
/** Get dateCreated */
public java.util.Date getDateCreated() {
return dateCreated;
}
/** Return a string representation of this object */
public String toString() {
return "created on " + dateCreated + "\ncolor: " + color + " and filled: " + filled;
}
}
```
可以看到父类对共有的变量和方法进行了定义,虽然父类可以实例化,但是好像实例化父类并没有什么实际的意义。
**注意:这个类最好不能被实例化,因为没有意义。下一章将借用这个离职,并使其不能被实例化。**
#### 1.2.2. 子类Circle
CircleFromSimpleGeometricObject
```java
public class CircleFromSimpleGeometricObject extends SimpleGeometricObject {
private double radius;
public CircleFromSimpleGeometricObject() {
}
public CircleFromSimpleGeometricObject(double radius) {
this.radius = radius;
}
public CircleFromSimpleGeometricObject(double radius, String color, boolean filled) {
this.radius = radius;
setColor(color);
setFilled(filled);
}
/** Return radius */
public double getRadius() {
return radius;
}
/** Set a new radius */
public void setRadius(double radius) {
this.radius = radius;
}
/** Return area */
public double getArea() {
return radius * radius * Math.PI;
}
/** Return diameter */
public double getDiameter() {
return 2 * radius;
}
/** Return perimeter */
public double getPerimeter() {
return 2 * radius * Math.PI;
}
/* Print the circle info */
public void printCircle() {
System.out.println("The circle is created " + getDateCreated() + " and the radius is " + radius);
}
}
```
1. 子类Circle使用`extend SimpleGeometricObject`表明继承于`SimpleGeometricObject`那么子类拥有父类的所有特性(数据和方法);
2. 但是请注意,虽然子类拥有父类的所有特性,不意味子类就可以无限制的访问这些特性,例如`color``filled``dateCreated` 这些是父类的私有变量在子类中并不能访问而要通过对应的getter和setter函数访问这些函数是公开的
3. `CircleFromSimpleGeometricObject`增加了自己的特性`radius`并设置成私有以及对应的getter和setter函数
4. 子类中增加了自己的行为:`getArea()`、`getPerimeter`和`printCircle()`
5. 当然,子类型的构造函数也不一样了,这是另外一个话题,后面会讲解。
#### 1.2.3. 子类
RectangleFromSimpleGeometricObject
```java
public class RectangleFromSimpleGeometricObject extends SimpleGeometricObject {
private double width;
private double height;
public RectangleFromSimpleGeometricObject() {
}
public RectangleFromSimpleGeometricObject(double width, double height) {
this.width = width;
this.height = height;
}
public RectangleFromSimpleGeometricObject(double width, double height, String color, boolean filled) {
this.width = width;
this.height = height;
setColor(color);
setFilled(filled);
}
/** Return width */
public double getWidth() {
return width;
}
/** Set a new width */
public void setWidth(double width) {
this.width = width;
}
/** Return height */
public double getHeight() {
return height;
}
/** Set a new height */
public void setHeight(double height) {
this.height = height;
}
/** Return area */
public double getArea() {
return width * height;
}
/** Return perimeter */
public double getPerimeter() {
return 2 * (width + height);
}
}
```
RectangleFromSimpleGeometricObject也使用同样的方式继承于SimpleGeometricObject只是新增的变量和方法不太一样。
#### 1.2.4. 测试主类
TestCircleRectangle
```java
public class TestCircleRectangle {
public static void main(String[] args) {
CircleFromSimpleGeometricObject circle = new CircleFromSimpleGeometricObject(1);
System.out.println("A circle " + circle.toString());
System.out.println("The color is " + circle.getColor());
System.out.println("The radius is " + circle.getRadius());
System.out.println("The area is " + circle.getArea());
System.out.println("The diameter is " + circle.getDiameter());
RectangleFromSimpleGeometricObject rectangle = new RectangleFromSimpleGeometricObject(2, 4);
System.out.println("\nA rectangle " + rectangle.toString());
System.out.println("The area is " + rectangle.getArea());
System.out.println("The perimeter is " + rectangle.getPerimeter());
}
}
```
通过测试程序,我们发现子类的对象引用变量上可以调用`toString()``getColor()` 等方法(这些方法是在父类中定义的),说明子类的确继承了父类的所有特性。
**思考一个问题:`getArea()`函数和`getPerimeter()`函数既然在两个子类中存在,就说明是这两个子类的共性;那为什么不把这两个函数写在父类当中?对了其实是因为他们的算法不同,虽然函数签名是一样的。下一章将要学习如何把这个方法放到父类中去。**
另外一个是`toString()`这个函数非常特别,后面会讲到。
## 2. 子类的构造函数
### 2.1. 构造函数也被继承了吗?
> No. They are not inherited.They are invoked explicitly or implicitly. Explicitly using the super keyword.
>
> A constructor is used to construct an instance of a class. Unlike properties and methods, a superclass's constructors are not inherited in the subclass. They can only be invoked from the subclasses' constructors, using the keyword super. If the keyword super is not explicitly used, the superclass's no-arg constructor is automatically invoked.
构造函数并不会被子类型所继承子类型通过显式或者隐式的方式调用父类的构造函数显式调用使用super关键字。如果没有显式调用那么子类型将隐式调用父类型不带参数的构造函数。
**为了便于子类型调用父类的构造函数,建议在定义类型的时候,要么不写构造函数,否则建议写一个不带参数的构造函数。**
A constructor may invoke an overloaded constructor or its superclasss constructor. If none of them is invoked explicitly, the compiler puts super() as the first statement in the . For example
![image-20230311222741376](img/image-20230311222741376.png)
![image-20230311222819514](img/image-20230311222819514.png)
父类如果只定义了带参数的构造函数那么Java不会分配不带参数的构造函数没有不带参数的构造函数这时如果子类没有显式调用父类带参数的构造函数将导致构造函数找不到的问题。
例如:
```java
class Person {
int age;
public Person(int a) {
age = a;
}
}
class Man extends Person {
}
```
上诉例子中定义的 Man 类将出现编译错误,因为 Person 缺少不带参数的构造函数Person 又没有显式的调用Person带参数的构造函数。修改 Man 类如下就可以了:
```java
class Man extends Person {
public Man() {
super(10);
}
}
```
### 2.2. super关键字
The keyword super refers to the superclass of the class in which super appears. This keyword can be used in three ways:
1. To call a superclass constructor
2. To call a superclass method
3. To access superclass fields
super关键字调用父类构造方法还可以理解为什么还需要super关键字去调用父类的函数和字段`CircleFromSimpleGeometricObject` 不是可以直接调用父类`SimpleGeometricObject`的方法吗?例如:`getColor()`等方法?
这是因为子类可以覆盖override父类的方法后面的覆盖父类方法的小节会讲解。
### 2.3. 构造函数链
当使用new关键字构建一个对象的时候如果这个对象所属的类有父类通常情况下都有那么是否和一步一步构建的我们看看下面的例子。
```java
public class Faculty extends Employee {
public static void main(String[] args) {
new Faculty();
}
public Faculty() {
super();
System.out.println("(4) Faculty's no-arg constructor is invoked");
}
}
class Employee extends PPP {
public Employee() {
this("(2) Invoke Employees overloaded constructor");
System.out.println("(3) Employee's no-arg constructor is invoked");
}
public Employee(String s) {
super();
System.out.println(s);
}
}
class PPP {
public PPP() {
System.out.println("(1) Person's no-arg constructor is invoked");
}
}
```
main函数开起来比较奇怪为什么在Faculty内中又使用new 方法构造自己所在的类其实大家不用太关心main函数在那里。首先因为java虚拟机要访问到这个静态函数作为程序的入口其次对于 main 函数只要被构建的类对于main函数来说是可以访问就行了。上述所有类型都定义在一个文件间当中这样如同是一个包因此main函数可以访问该文件中的所有类进而也可以访问 Faculty 类,构建 Faculty 类的对象当然也是可以的。其实把main函数放在这个文件的任何类中都是可以的只不过我们习惯放在 Faculty 这个类中(这个文档的文件名也是和这个类一致的)。
我们接下来仔细分析一下发生了什么,运行的结果是:
```
(1) Person's no-arg constructor is invoked
(2) Invoke Employees overloaded constructor
(3) Employee's no-arg constructor is invoked
(4) Faculty's no-arg constructor is invoked
```
也就是说PPP的构造函数被最先执行完成注意不是最先被调用然后是 Employee 的构造函数被执行完成,最后是 Faculty 的构造函数被执行完成。
```mermaid
sequenceDiagram
activate Faculty()
Faculty()->>Employee(): 显式调用super()
deactivate Faculty()
activate Employee()
Employee()->>PPP(): 隐式调用 PPP()
deactivate Employee()
activate PPP()
Note over PPP():1
PPP()->>Employee(): PPP()调用返回
activate Employee()
deactivate PPP()
Note over Employee():2、3
deactivate Employee()
Employee()->>Faculty():Employee()调用返回
activate Faculty()
Note over Faculty():4
deactivate Faculty()
```
可以看到这是典型的函数调用栈的方式最先进入的函数最后退出。其实PPP类还隐式调用了Object()会在Object小节讲解。
## 3. Overriding 覆盖父类的方法
在很多时候,子类型可能需要改变父类型的一些行为(函数),这时子类型可以定义一个与父类型一样的函数,但是实现不一样,这样父类型的实现就被隐藏了。
### 3.1. 继承父类型的函数
先来看看子类型继承父类型,拥有父类型的全部能力的情况:
```java
class Fruit {
public String toString() {
return "This is fruit.";
}
}
class Apple extends Fruit {
}
public class Test {
public static void main(String[] args) {
Apple apple = new Apple();
System.out.println(apple.toString()); // 输出This is fruit.
}
}
```
Apple中没有toString()函数但是其父类型后因此Apple也得到了这样的能力。其实是间接调用父类型的toString()函数。
### 3.2. 覆盖父类型的函数
如果在Apple中也定义一个toString()函数会有什么样的结果?
```java
class Fruit {
public String toString() {
return "This is fruit.";
}
}
class Apple extends Fruit {
public String toString() {
return "This is an apple.";
}
}
public class Test {
public static void main(String[] args) {
Apple apple = new Apple();
System.out.println(apple.toString()); // 输出This is an apple.
}
}
```
Apple类型中的 toString() 函数覆盖了父类型的同名函数,这样的方式叫做 override。在Apple类型的对象上面调用toString()会出现Apple类自己toString()函数的行为而不会调用Fruit类型的toString()。换句话说,子类型如果定义了和父类型一样的函数,父类型的这个同名函数将不可见。
### 3.3. 使用super关键字调用父类型被隐藏的函数
子类如果覆盖了父类的某个函数,但是在子类中(只能在直接子类中)任然有办法调用父类被隐藏的函数。
```java
class Fruit {
public String toString() {
return "This is fruit.";
}
}
class Apple extends Fruit {
public String toString() {
return super.toString() + "\nThis is an apple.";
}
public void printFruitToString() {
System.out.println(super.toString());
}
}
public class Test {
public static void main(String[] args) {
Apple apple = new Apple();
System.out.println(apple.toString());
apple.printFruitToString();
}
}
```
结果是:
```
This is fruit.
This is an apple.
This is fruit.
```
1、2行输出是`System.out.println(apple.toString());` 的结果。可以看到第1行是Apple类中`toString()`函数使用`super.toString()`调用了Fruit类中`toString`的结果。第3行使用Apple的函数`printFruitToString()`,本质也是使用了`super.toString()`。
**注意super关键字只能在直接子类中使用表示其直接父类。**
### 3.4. Overriding vs. Overloading
> An instance method can be overridden only if it is accessible. Thus a private method cannot be overridden, because it is not accessible outside its own class. If a method defined in a subclass is private in its superclass, the two methods are completely unrelated.
>
> (了解)实例函数在子类中可以访问的情况下才可以被覆盖。如果父类中有一个私有函数,子类中定义了一个函数(与父类函数名,参数都一样),那么这两个函数没有任何关系。
>
> Like an instance method, a static method can be inherited. However, a static method cannot be overridden. If a static method defined in the superclass is redefined in a subclass, the method defined in the superclass is hidden.
>
> (了解)和实例函数一样,静态函数可以被子类继承;但是静态函数不能被覆盖;如果子类型重新定义了父类型的静态函数,父类型的静态函数将被一直隐藏。
![image-20230312150607935](img/image-20230312150607935.png)
覆盖Overriding只在父类型函数和字类型函数一样的情况下出现
重载Overloading函数签名不一样
上图中左边是覆盖,右边是重载。
## 4. Object类
如果在定义的时候不使用关键字extends指定父类那么默认其父类是Object。也就是说所有的引用类型都是从Object直接或者是间接继承扩展而来的。
```java
class Fruit{
}
// 与下面是一样的
class Fruit extends Object{
}
```
### 4.1. toString方法
这样以来所有引用类型都具备Object所具备的所有能力最常用的就是toString()方法。
toString()方法是在Object中定义的方法会打印一个该对象的描述。表述的大约格式是类名@对象地址
```java
class Fruit{
}
public class Test {
public static void main(String[] args) {
Fruit fruit = new Fruit();
System.out.println(fruit.toString());
}
}
```
试试上面的例子。
## 5. Polymorphism多态
> Polymorphism means that a variable of a supertype can refer to a subtype object.
>
> 多态是指直接或者间接的父类型引用变量可以用来引用字类型的对象。很难理解,有什么用?
>
> A class defines a type. A type defined by a subclass is called a subtype, and a type defined by its superclass is called a supertype. Therefore, you can say that Circle is a subtype of GeometricObject and GeometricObject is a supertype for Circle.
>
> 上面是说父类型和字类型的关系。例如借用本章开头的分类图:猫种可以说成是属于猫属、猫科、食肉动物目。。。就是这意思,这有什么用?
### 5.1. 简单的例子
```mermaid
classDiagram
Fruit <|-- Apple
Fruit <|-- Orange
Fruit : +toString()
class Apple{
+toString()
}
class Orange{
+toString()
}
```
有三个类
```Java
class Fruit {
public String toString() {
return "This is fruit.";
}
}
class Apple extends Fruit {
public String toString() {
return "This is an apple.";
}
}
class Orange extends Fruit {
public String toString() {
return "This is an orange.";
}
}
public class Test {
public static void main(String[] args) {
Fruit fruit = new Apple();
System.out.println(fruit.toString()); // 打印This is an apple.
fruit = new Orange();
System.out.println(fruit.toString()); // 打印This is an orange.
}
}
```
### 5.2. 复杂一点的例子
我们先来看看一个例子:
```java
class Fruit {
public String toString() {
return "This is fruit.";
}
}
class Apple extends Fruit {
public String toString() {
return "This is an apple.";
}
}
class Orange extends Fruit {
public String toString() {
return "This is an orange.";
}
}
public class Test {
public static void main(String[] args) {
Apple apple = new Apple();
Orange orange = new Orange();
printInfo(apple);
printInfo(orange);
}
static void printInfo(Fruit fruit) {
System.out.println(fruit.toString()); // 这里引用变量的类型其实是父类型
}
}
```
这里例子中
### 5.3. 动态函数绑定
## 6. 引用类型的类型转换
### 6.1. 子类型转换成父类型
### 6.2. 父类型转换成子类型
## 7. equals 方法
## 8. ArrayList类
## 9. protected 访问修饰
## 10. final 修饰

@ -1,608 +0,0 @@
## 1. Inheritance继承 and Polymorphism多态
> Suppose you will define classes to model circles, rectangles, and triangles. These classes have many common features. What is the best way to design these classes so to avoid redundancy? The answer is to use inheritance.
### 1.1. 如何降低描述荣誉
如何降低代码的冗余例如在C语言中通过什么方式降低代码的冗余。
在C语言中降低代码冗余增加代码复用性一般使用函数来实现但是函数是一个高纬度的抽象和我们日常生活联系并不紧密。那么我们考虑日常生活中的例子。
通常我们描述一猫,只用说明猫是:界:动物界;门:脊椎动亚门;纲:哺乳纲;目:食肉目;科:猫科;属:猫属;种:猫种。
![Alt text](img/%E7%94%9F%E7%89%A9%E5%88%86%E7%B1%BB.drawio.svg)
上述的图表示物种分类的一部分。这样描述一个物种的分类有很多好处:
1. 清楚表明了祖先与后代的关系(继承树);
2. 后代具备祖先的所有特性;
3. 明确不同种之间的关系。
如果大家都清楚上述的规则当我们讨论猫种的时候就默认猫种具备祖先的所有特性而不必再说明猫种是脊椎动物胎生等这些它祖先所具备的特性。这种特性在Java种的类种也存在这就是继承。
### 1.2. Java中的继承
例如要在平面上描述两个类圆和矩形那么这两个类好像有一些基本的特性是一致的。例如填充颜色color是否填充 filled对象创建时间 dateCreated等。另外需要对上述属性设置或者读取的方法getColor()setColor()isFilled()setFilled()等。
如果每个类都需要描述所有的这些特性好像有点浪费如何做到降低代码冗余我们使用到继承如下图表述GeometrocObject是父类保护所有子类共有的特性数据和方法Circle和Rectangle继承于GeometrocObject。这样在编写Circle和Rectangle的时候就只需要对其特性进行描述就可以了这两个类的共性在其父类中以及体现出来了子类完全继承父类的所有特性数据和方法
![image-20230311122706219](img/image-20230311122706219.png)
代码实现如下:
#### 1.2.1. 父类
SimpleGeometricObject
```java
public class SimpleGeometricObject {
private String color = "white";
private boolean filled;
private java.util.Date dateCreated;
/** Construct a default geometric object */
public SimpleGeometricObject() {
dateCreated = new java.util.Date();
}
/**
* Construct a geometric object with the specified color and filled value
*/
public SimpleGeometricObject(String color, boolean filled) {
dateCreated = new java.util.Date();
this.color = color;
this.filled = filled;
}
/** Return color */
public String getColor() {
return color;
}
/** Set a new color */
public void setColor(String color) {
this.color = color;
}
/**
* Return filled. Since filled is boolean, its get method is named isFilled
*/
public boolean isFilled() {
return filled;
}
/** Set a new filled */
public void setFilled(boolean filled) {
this.filled = filled;
}
/** Get dateCreated */
public java.util.Date getDateCreated() {
return dateCreated;
}
/** Return a string representation of this object */
public String toString() {
return "created on " + dateCreated + "\ncolor: " + color + " and filled: " + filled;
}
}
```
可以看到父类对共有的变量和方法进行了定义,虽然父类可以实例化,但是好像实例化父类并没有什么实际的意义。
**注意:这个类最好不能被实例化,因为没有意义。下一章将借用这个离职,并使其不能被实例化。**
#### 1.2.2. 子类Circle
CircleFromSimpleGeometricObject
```java
public class CircleFromSimpleGeometricObject extends SimpleGeometricObject {
private double radius;
public CircleFromSimpleGeometricObject() {
}
public CircleFromSimpleGeometricObject(double radius) {
this.radius = radius;
}
public CircleFromSimpleGeometricObject(double radius, String color, boolean filled) {
this.radius = radius;
setColor(color);
setFilled(filled);
}
/** Return radius */
public double getRadius() {
return radius;
}
/** Set a new radius */
public void setRadius(double radius) {
this.radius = radius;
}
/** Return area */
public double getArea() {
return radius * radius * Math.PI;
}
/** Return diameter */
public double getDiameter() {
return 2 * radius;
}
/** Return perimeter */
public double getPerimeter() {
return 2 * radius * Math.PI;
}
/* Print the circle info */
public void printCircle() {
System.out.println("The circle is created " + getDateCreated() + " and the radius is " + radius);
}
}
```
1. 子类Circle使用`extend SimpleGeometricObject`表明继承于`SimpleGeometricObject`那么子类拥有父类的所有特性(数据和方法);
2. 但是请注意,虽然子类拥有父类的所有特性,不意味子类就可以无限制的访问这些特性,例如`color``filled``dateCreated` 这些是父类的私有变量在子类中并不能访问而要通过对应的getter和setter函数访问这些函数是公开的
3. `CircleFromSimpleGeometricObject`增加了自己的特性`radius`并设置成私有以及对应的getter和setter函数
4. 子类中增加了自己的行为:`getArea()`、`getPerimeter`和`printCircle()`
5. 当然,子类型的构造函数也不一样了,这是另外一个话题,后面会讲解。
#### 1.2.3. 子类
RectangleFromSimpleGeometricObject
```java
public class RectangleFromSimpleGeometricObject extends SimpleGeometricObject {
private double width;
private double height;
public RectangleFromSimpleGeometricObject() {
}
public RectangleFromSimpleGeometricObject(double width, double height) {
this.width = width;
this.height = height;
}
public RectangleFromSimpleGeometricObject(double width, double height, String color, boolean filled) {
this.width = width;
this.height = height;
setColor(color);
setFilled(filled);
}
/** Return width */
public double getWidth() {
return width;
}
/** Set a new width */
public void setWidth(double width) {
this.width = width;
}
/** Return height */
public double getHeight() {
return height;
}
/** Set a new height */
public void setHeight(double height) {
this.height = height;
}
/** Return area */
public double getArea() {
return width * height;
}
/** Return perimeter */
public double getPerimeter() {
return 2 * (width + height);
}
}
```
RectangleFromSimpleGeometricObject也使用同样的方式继承于SimpleGeometricObject只是新增的变量和方法不太一样。
#### 1.2.4. 测试主类
TestCircleRectangle
```java
public class TestCircleRectangle {
public static void main(String[] args) {
CircleFromSimpleGeometricObject circle = new CircleFromSimpleGeometricObject(1);
System.out.println("A circle " + circle.toString());
System.out.println("The color is " + circle.getColor());
System.out.println("The radius is " + circle.getRadius());
System.out.println("The area is " + circle.getArea());
System.out.println("The diameter is " + circle.getDiameter());
RectangleFromSimpleGeometricObject rectangle = new RectangleFromSimpleGeometricObject(2, 4);
System.out.println("\nA rectangle " + rectangle.toString());
System.out.println("The area is " + rectangle.getArea());
System.out.println("The perimeter is " + rectangle.getPerimeter());
}
}
```
通过测试程序,我们发现子类的对象引用变量上可以调用`toString()``getColor()` 等方法(这些方法是在父类中定义的),说明子类的确继承了父类的所有特性。
**思考一个问题:`getArea()`函数和`getPerimeter()`函数既然在两个子类中存在,就说明是这两个子类的共性;那为什么不把这两个函数写在父类当中?对了其实是因为他们的算法不同,虽然函数签名是一样的。下一章将要学习如何把这个方法放到父类中去。**
另外一个是`toString()`这个函数非常特别,后面会讲到。
## 2. 子类的构造函数
### 2.1. 构造函数也被继承了吗?
> No. They are not inherited.They are invoked explicitly or implicitly. Explicitly using the super keyword.
>
> A constructor is used to construct an instance of a class. Unlike properties and methods, a superclass's constructors are not inherited in the subclass. They can only be invoked from the subclasses' constructors, using the keyword super. If the keyword super is not explicitly used, the superclass's no-arg constructor is automatically invoked.
构造函数并不会被子类型所继承子类型通过显式或者隐式的方式调用父类的构造函数显式调用使用super关键字。如果没有显式调用那么子类型将隐式调用父类型不带参数的构造函数。
**为了便于子类型调用父类的构造函数,建议在定义类型的时候,要么不写构造函数,否则建议写一个不带参数的构造函数。**
A constructor may invoke an overloaded constructor or its superclasss constructor. If none of them is invoked explicitly, the compiler puts super() as the first statement in the . For example
![image-20230311222741376](img/image-20230311222741376.png)
![image-20230311222819514](img/image-20230311222819514.png)
父类如果只定义了带参数的构造函数那么Java不会分配不带参数的构造函数没有不带参数的构造函数这时如果子类没有显式调用父类带参数的构造函数将导致构造函数找不到的问题。
例如:
```java
class Person {
int age;
public Person(int a) {
age = a;
}
}
class Man extends Person {
}
```
上诉例子中定义的 Man 类将出现编译错误,因为 Person 缺少不带参数的构造函数Person 又没有显式的调用Person带参数的构造函数。修改 Man 类如下就可以了:
```java
class Man extends Person {
public Man() {
super(10);
}
}
```
### 2.2. super关键字
The keyword super refers to the superclass of the class in which super appears. This keyword can be used in three ways:
1. To call a superclass constructor
2. To call a superclass method
3. To access superclass fields
super关键字调用父类构造方法还可以理解为什么还需要super关键字去调用父类的函数和字段`CircleFromSimpleGeometricObject` 不是可以直接调用父类`SimpleGeometricObject`的方法吗?例如:`getColor()`等方法?
这是因为子类可以覆盖override父类的方法后面的覆盖父类方法的小节会讲解。
### 2.3. 构造函数链
当使用new关键字构建一个对象的时候如果这个对象所属的类有父类通常情况下都有那么是否和一步一步构建的我们看看下面的例子。
```java
public class Faculty extends Employee {
public static void main(String[] args) {
new Faculty();
}
public Faculty() {
super();
System.out.println("(4) Faculty's no-arg constructor is invoked");
}
}
class Employee extends PPP {
public Employee() {
this("(2) Invoke Employees overloaded constructor");
System.out.println("(3) Employee's no-arg constructor is invoked");
}
public Employee(String s) {
super();
System.out.println(s);
}
}
class PPP {
public PPP() {
System.out.println("(1) Person's no-arg constructor is invoked");
}
}
```
main函数开起来比较奇怪为什么在Faculty内中又使用new 方法构造自己所在的类其实大家不用太关心main函数在那里。首先因为java虚拟机要访问到这个静态函数作为程序的入口其次对于 main 函数只要被构建的类对于main函数来说是可以访问就行了。上述所有类型都定义在一个文件间当中这样如同是一个包因此main函数可以访问该文件中的所有类进而也可以访问 Faculty 类,构建 Faculty 类的对象当然也是可以的。其实把main函数放在这个文件的任何类中都是可以的只不过我们习惯放在 Faculty 这个类中(这个文档的文件名也是和这个类一致的)。
我们接下来仔细分析一下发生了什么,运行的结果是:
```
(1) Person's no-arg constructor is invoked
(2) Invoke Employees overloaded constructor
(3) Employee's no-arg constructor is invoked
(4) Faculty's no-arg constructor is invoked
```
也就是说PPP的构造函数被最先执行完成注意不是最先被调用然后是 Employee 的构造函数被执行完成,最后是 Faculty 的构造函数被执行完成。
```mermaid
sequenceDiagram
activate Faculty()
Faculty()->>Employee(): 显式调用super()
deactivate Faculty()
activate Employee()
Employee()->>PPP(): 隐式调用 PPP()
deactivate Employee()
activate PPP()
Note over PPP():1
PPP()->>Employee(): PPP()调用返回
activate Employee()
deactivate PPP()
Note over Employee():2、3
deactivate Employee()
Employee()->>Faculty():Employee()调用返回
activate Faculty()
Note over Faculty():4
deactivate Faculty()
```
可以看到这是典型的函数调用栈的方式最先进入的函数最后退出。其实PPP类还隐式调用了Object()会在Object小节讲解。
## 3. Overriding 覆盖父类的方法
在很多时候,子类型可能需要改变父类型的一些行为(函数),这时子类型可以定义一个与父类型一样的函数,但是实现不一样,这样父类型的实现就被隐藏了。
### 3.1. 继承父类型的函数
先来看看子类型继承父类型,拥有父类型的全部能力的情况:
```java
class Fruit {
public String toString() {
return "This is fruit.";
}
}
class Apple extends Fruit {
}
public class Test {
public static void main(String[] args) {
Apple apple = new Apple();
System.out.println(apple.toString()); // 输出This is fruit.
}
}
```
Apple中没有toString()函数但是其父类型后因此Apple也得到了这样的能力。其实是间接调用父类型的toString()函数。
### 3.2. 覆盖父类型的函数
如果在Apple中也定义一个toString()函数会有什么样的结果?
```java
class Fruit {
public String toString() {
return "This is fruit.";
}
}
class Apple extends Fruit {
public String toString() {
return "This is an apple.";
}
}
public class Test {
public static void main(String[] args) {
Apple apple = new Apple();
System.out.println(apple.toString()); // 输出This is an apple.
}
}
```
Apple类型中的 toString() 函数覆盖了父类型的同名函数,这样的方式叫做 override。在Apple类型的对象上面调用toString()会出现Apple类自己toString()函数的行为而不会调用Fruit类型的toString()。换句话说,子类型如果定义了和父类型一样的函数,父类型的这个同名函数将不可见。
### 3.3. 使用super关键字调用父类型被隐藏的函数
子类如果覆盖了父类的某个函数,但是在子类中(只能在直接子类中)任然有办法调用父类被隐藏的函数。
```java
class Fruit {
public String toString() {
return "This is fruit.";
}
}
class Apple extends Fruit {
public String toString() {
return super.toString() + "\nThis is an apple.";
}
public void printFruitToString() {
System.out.println(super.toString());
}
}
public class Test {
public static void main(String[] args) {
Apple apple = new Apple();
System.out.println(apple.toString());
apple.printFruitToString();
}
}
```
结果是:
```
This is fruit.
This is an apple.
This is fruit.
```
1、2行输出是`System.out.println(apple.toString());` 的结果。可以看到第1行是Apple类中`toString()`函数使用`super.toString()`调用了Fruit类中`toString`的结果。第3行使用Apple的函数`printFruitToString()`,本质也是使用了`super.toString()`。
**注意super关键字只能在直接子类中使用表示其直接父类。**
### 3.4. Overriding vs. Overloading
> An instance method can be overridden only if it is accessible. Thus a private method cannot be overridden, because it is not accessible outside its own class. If a method defined in a subclass is private in its superclass, the two methods are completely unrelated.
>
> (了解)实例函数在子类中可以访问的情况下才可以被覆盖。如果父类中有一个私有函数,子类中定义了一个函数(与父类函数名,参数都一样),那么这两个函数没有任何关系。
>
> Like an instance method, a static method can be inherited. However, a static method cannot be overridden. If a static method defined in the superclass is redefined in a subclass, the method defined in the superclass is hidden.
>
> (了解)和实例函数一样,静态函数可以被子类继承;但是静态函数不能被覆盖;如果子类型重新定义了父类型的静态函数,父类型的静态函数将被一直隐藏。
![image-20230312150607935](img/image-20230312150607935.png)
覆盖Overriding只在父类型函数和字类型函数一样的情况下出现
重载Overloading函数签名不一样
上图中左边是覆盖,右边是重载。
## 4. Object类
如果在定义的时候不使用关键字extends指定父类那么默认其父类是Object。也就是说所有的引用类型都是从Object直接或者是间接继承扩展而来的。
```java
class Fruit{
}
// 与下面是一样的
class Fruit extends Object{
}
```
### 4.1. toString方法
这样以来所有引用类型都具备Object所具备的所有能力最常用的就是toString()方法。
toString()方法是在Object中定义的方法会打印一个该对象的描述。表述的大约格式是类名@对象地址
```java
class Fruit{
}
public class Test {
public static void main(String[] args) {
Fruit fruit = new Fruit();
System.out.println(fruit.toString());
}
}
```
试试上面的例子。
## 5. Polymorphism多态
> Polymorphism means that a variable of a supertype can refer to a subtype object.
>
> 多态是指直接或者间接的父类型引用变量可以用来引用字类型的对象。很难理解,有什么用?
>
> A class defines a type. A type defined by a subclass is called a subtype, and a type defined by its superclass is called a supertype. Therefore, you can say that Circle is a subtype of GeometricObject and GeometricObject is a supertype for Circle.
>
> 上面是说父类型和字类型的关系。例如借用本章开头的分类图:猫种可以说成是属于猫属、猫科、食肉动物目。。。就是这意思,这有什么用?
我们先来看看一个例子:
```java
class Fruit {
public String toString() {
return "This is fruit.";
}
}
class Apple extends Fruit {
public String toString() {
return "This is an apple.";
}
}
class Orange extends Fruit {
public String toString() {
return "This is an orange.";
}
}
public class Test {
public static void main(String[] args) {
Apple apple = new Apple();
Orange orange = new Orange();
printInfo(apple);
printInfo(orange);
}
static void printInfo(Fruit fruit) {
System.out.println(fruit.toString());
}
}
```
### 5.1. 动态函数绑定
## 6. 引用类型的类型转换
### 6.1. 子类型转换成父类型
### 6.2. 父类型转换成子类型
## 7. equals 方法
## 8. ArrayList类
## 9. protected 访问修饰
## 10. final 修饰

@ -11,8 +11,8 @@
3. 第三章:类与对象
1. [类与对象](03%EF%BC%9A%E7%B1%BB%E4%B8%8E%E5%AF%B9%E8%B1%A1/01.md)
2. [面向对象思维](03%EF%BC%9A%E7%B1%BB%E4%B8%8E%E5%AF%B9%E8%B1%A1/02.md)
3. [继承与多态](03%EF%BC%9A%E7%B1%BB%E4%B8%8E%E5%AF%B9%E8%B1%A1/04.md)
4. [抽象类与接口](03%EF%BC%9A%E7%B1%BB%E4%B8%8E%E5%AF%B9%E8%B1%A1/05.md)
3. [继承与多态](03%EF%BC%9A%E7%B1%BB%E4%B8%8E%E5%AF%B9%E8%B1%A1/03.md)
4. [抽象类与接口](03%EF%BC%9A%E7%B1%BB%E4%B8%8E%E5%AF%B9%E8%B1%A1/04.md)
4. 第四章:异常处理
5. 第五章IO编程
6. 第六章:集合与框架

Loading…
Cancel
Save