## 继承
### 语法格式
class A extends B {}
* A 类即子类、派生类、subclass
B 类即父类、基类、超类、superclass
* 一旦子类继承父类后,子类会获取父类中所有声明的属性和方法,但是父类中 private 属性或方法,因封装性,在子类中不可见
* 子类继承父类后,可以声明自己特有的属性和方法,以扩展类的功能
* 一个子类只能有一个父类(但是类可以实现多个接口),一个类可以被多个子类继承
多层继承是允许的,直接继承的即直接父类,间接继承的即间接父类,继承后会获取所有直接/间接父类的属性和方法
## 重写 Override
子类根据需要对从父类继承来的方法进行改造,即方法的重写、覆盖。重写以后,通过子类对象调用子父类中的同名同参数的方法时,实际执行的是子类重写父类的方法。
**重载与重写**
* 重载:重载发生在同一个类中,允许存在多个同名不同参(类型、个数)的方法,编译器会根据方法不同的参数列表,对同名方法的方法名进行修饰,对于编译器而言,这些方法就成了不同方法,它们的调用地址在编译时期就绑定了,即早绑定或者静态绑定
* 多态与重写:重写发生在子类,子类方法的方法名和参数列表与父类相同,运行时期才会确定具体调用重写的哪个方法,即晚绑定或者动态绑定
> 不要犯傻,如果它不是晚绑定,它就不是多态。
### 要求
1. 子类重写的方法**必须**和父类中被重写的方法具有相同的**方法名**、**参数列表**
2. 子类重写的方法的返回值类型**不大于**父类中被重写的方法
|父类被重写方法的返回值|子类重写方法的返回值|
|:---:|:---:|
|void|void|
|A类|A类或A类的子类|
|基本数据类型|相同的基本数据类型|
3. 子类重写的方法的访问权限**不小于**父类中被重写的方法,子类不能重写父类的 private 方法
4. 子类重写的方法抛出异常**不大于**父类中被重写的方法的异常(否则在多态的情况下,可能出现处理不了的异常类型)
5. 子父类中的同名同参的方法,必须同时被声明为非 static(使用重写),或同时声明为 static(不是重写),static 方法随类的加载而加载,static 方法不可被重写。
## super 关键字
super 可以理解为 父类的...,使用 super 关键字可以调用父类的属性、方法、构造器
**调用父类的属性和方法**
当子类中定义了与父类同名的属性或重写了父类的方法时,要想在子类中调用父类中声明的属性或方法,则必须显式的使用 **super.属性** 或 **super.方法** 的方式,表明调用的是父类中声明的属性或方法。
**调用父类的构造器**
* 可以在子类的构造器中显式的使用 **super(形参列表)** 的方式,调用父类中声明的指定的构造器,且必须声明在子类构造器的首行,所以,针对于 **this(形参列表)** 或 **super(形参列表)** 只能二选一,不能同时出现
* 在构造器的首行,没显式的声明 **this(形参列表)** 或 **super(形参列表)**,则默认调用的是父类中**空参**的构造器:super()
* 在类的多个构造器中,至少一个类的构造器中使用了 **super(形参列表)**,调用父类中的构造器
## 对象实例化的全过程
* 结果上来看:子类继承父类后,获取了父类中声明的属性和方法,创建子类的对象,在堆空间中,就会加载所有父类中声明的属性
* 过程上来看:n 个构造器 **最多** 有 n-1 个 构造器使用到了 this() 关键字调用本类中的其他构造器,而没有使用 this() 的构造器一定使用 super() 关键字显示或隐式的调用了父类的构造器,直至调用 java.lang.Object 的空参构造器,正因如此,子类才会获取父类中的所有结构,子类对象才可以考虑进行调用
* 注意:尽管调用了父类的构造器,但是内存中只创建了一个对象
## 多态
引用变量有两个类型:编译时类型、运行时类型,**编译时类型**由**声明**该变量所使用的类型决定,**运行时类型**由实际赋给该变量的**对象**决定,若编译时类型和运行时类型不一致,即形成多态。简言之,多态就是父类的引用指向子类的对象。
* 多态的使用前提:有类的继承关系,有方法的重写
* 多态情况下,对象调用子父类同名同参的方法时,实际运行时执行的是子类重写的方法,即虚拟方法调用,但是,如果该对象调用父类中没有的方法,则无法通过编译
* 多态可以应用在抽象类和接口上
```java
// 一个数据库的例子
public void doData(Connection conn) {
// 规范步骤操作数据库
// conn可以是任何数据库实现的对象,比如MySQL会重写如下的数据操作的标准方法
conn.method1();
conn.method2();
conn.method3();
}
```
**注意** 多态不适用于属性,编译时和运行时都看声明变量所使用的的类型,父类的引用不能调用子类所特有的属性和方法
## 向下转型
### 为什么使用向下转型
有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法的,但是由于变量声明为父类类型,导致编译时,只能调用父类中声明的属性和方法。若要调用子类特有的属性和方法,必须使用向下转型。而实现向下转型,就要使用强制类型转换。
```java
class Man extends Person {
//...
}
class Test {
void testMethod() {
// 此时p是 Person 类型,但对象是Man的对象,使用p无法调用Man特有的属性和方法
Person p = new Man();
// 强制类型转换,把 Person 类型的p转换成Man类型的m
// 这样就可以使用m去调用Man特有的属性和方法
Man m = (Man)p;
}
}
```
但是使用强制类型转换的时候,可能出现 **ClassCastException** 异常,所以有必要先使用 instanceof 来检查,返回true之后再进行强转
### instanceof
* a instanceof A:判断对象 a 是否是类 A 的一个实例。如果是,返回true;如果不是,返回false
* 要求 a 所属的类与类 A 必须是子类和父类的关系,否则编译错误
## Objiect类
如果一个类没有使用 extends 显式的声明其父类,则其默认继承于 java.lang.Object类,所有 Java 类(除了 java.lang.Object)都直接或间接继承于 java.lang.Object 类,Object类没有属性,只有一个空参构造器
### 常用方法
**最常用**
* equals()
* toString()
* getClass()
* hashCode()
* clone()
**垃圾回收相关**
* finalize()
**多线程**
* wait()
* notify()
* notifyAll()
### == 和 equals()
**==**
* 是一个运算符
* 可以用于基本数据类型变量和引用数据类型变量的比较
* 若比较**基本数据类型**变量,实际比较的是变量所保存的数据数值是否相等,而且所比较的两个变量的类型不一定相同(不可用于boolean和其他类型的比较)
* 若比较**引用数据类型**变量,实际比较的是引用所指向对象的地址值是否相同,即比较两个引用**是否指向同一对象**实体
**equals()**
* 是一个方法
* 只能通过对象调用,不可以使用在基本数据类型变量上
* 若比较的类没有重写 Object 中的 equals(),则其等价于 == ,即比较两个对象的地址值
* 若类重写了 equals() 方法,比如 String 类、Date 类、File 类、包装类等,则比较对象的**实体内容**是否相同,即比较对象的某个或某些属性的值是否相同
* 自定义类可以重写 equals() 方法,实际开发中,IDEA 可以自动生成重写的 equals() 方法
```java
class MyClass {
int data;
String str;
@Override
public boolean equals(Object obj) {
if(obj == null)
return false;
if(this == obj)
return true;
if(obj instanceof MyClass) {
MyClass anotherObj = (MyClass)obj;
return this.data==anotherObj.data && this.str.equals(anotherObj.str);
}else {
return false;
}
}
}
```
### toString
* Object 类中的 toString() 就是返回一个字符串,内容是 **对象类名@对象的16进制哈希值**(JVM地址值)
* 重写了Object类的toString的类,比如 String 类、Date 类,调用 toString() 方法,会返回对象的“实体内容”
* 当使用 System.out.println() 打印一个对象的引用时,实际上是在该方法的参数中调用该对象的 toString() 方法,参数就是对象 toString() 的返回值
## 包装类 Wrapper
针对八中几本数据类型定义对应的应用类型:包装类
|基本数据类型|对应包装类|
|:---:|:---:|
|byte|Byte|
|short|Short|
|int|Integer|
|long|Long|
|float|Float|
|double|Double|
|boolean|Boolean|
|char|Character|
其中 Byte、Short、Integer、Long、Float、Double 类有共同的父类 Number
### 基本数据类型--->包装类
```java
// 包装类的构造器,手动装箱
int num1 = 10;
Integer num2 = new Integer(num1);
// 自动装箱
int num3 = 10;
Integer num4 = num3;
boolean b1 = true;
Boolean b2 = b1;
```
### 包装类--->基本数据类型
```java
// 包装类的方法,手动拆箱
Integer num1 = new Integer(10);
int num2 = num1.intValue();
Double num3 = new Double(3.14);
double num4 = num3.doubleValue();
// 自动拆箱
int num5 = num1;
```
### 基本数据类型--->String
```java
// 连接一个空字符串
double num = 3.14;
String str1 = ""+ num;
// String 类重载的静态方法 valueOf( int/double/... )
float f = 3.14F;
String str2 = String.valueOf(f);
```
### String--->基本数据类型
```java
// 对应包装类的静态方法 parseXxx(String)
// z字符串内容必须是纯粹数值
String str1 = "123";
int num = Integer.parseInt(str);
// 不区分大小写
String str2 = "TrUe";
boolean b = Boolean.parseBoolean(str2);
```
### 包装类--->String
```java
// String 类重载的静态方法 valueOf( Integer/Double/... )
Double d = new Double(5.2);
String str1 = String.valueOf(d);
// 包装类的 toString 方法
String str2 = d.toString();
```
### String--->包装类
```java
// 包装类的构造器
Integer num = new Integer("123");
// 字符串必须是纯粹的数值
```

Java基础 | 面向对象(中)