Java面向对象(中)

1. OOP特征二:继承

1.1 继承的好处

  1. 减少代码冗余性,提高代码复用性
  2. 便于功能扩展
  3. 为之后多态性的使用,提供了前提

1.2 继承的格式

class A extends B() {}
  • A: 字类、派生类、subclass、superclass
  • B: 父类、超类、基类

  • 体现: 一旦子类A继承父类B以后,子类A中就获取了父类B中所有的声明的结构: 属性、方法

    特别的,父类中声明为private的属性或方法,子类继承父类以后,仍然认为获取了父类中私有的结构。只有因为封装性的影响,使得子类不能直接调用父类的结构而已。

  • 子类在继承基础上可以声明自己的属性和方法,实现功能扩展。

1.4 关于继承性的规定

  1. 一个类可以被多个子类继承
  2. 一个字类只能有一个父类:单继承
  3. 子父类是相对的概念,可以多重继承 C->B->A
  4. 子类直接继承的称为直接父类,间接继承的为间接父类
  5. 子类继承父类,就获取了父类以及所有间接父类的属性和方法

2. Object 类

  1. 如果我们没有显式的声明-一个类的父类的话,则此类继承于 Java.lang.Object
  2. 所有类(除了Java.lang.Object)都直接或间接继承于 Object 类

3. 方法的重写

定义: Oerride/Overwrite 在子类中可以根据需要对从父类中继承来的方法进行改造,也称为方法的 重置、覆盖。在程序执行时,子类的方法将覆盖父类的方法。

重写的规定:

  1. 子类重写的方法的方法名和形参列表与父类被重写的方法的方法名和形参列表相同
  2. 子类重写的方法的权限修饰符 不小于 父类被重写的方法的权限修饰符
  3. 子类不能重写父类中声明为 private 的方法。
  4. 返回值类型
    • 父类 void:子类 void
    • 父类 A 类型:子类 A 类或A的子类
    • 父类:基本数据类型:子类是相同类型
  5. 子类和父类中的同名同参数的方法要么都声明为非static的(考虑重写),要么都声明为static的(不是重写)。

4. super

4.1 super 关键字的作用:

  1. super 理解为:父类的
  2. super 可以用来调用:属性、方法、构造器

4.2 super 的使用

  1. 我们可以在子类的方法或构造器中。通过使用 super.属性super.方法 的方式,显式的调用父类中声明的属性或方法。
  2. 特殊的,当子类和父类中出现了同名的属性,要调用父类中声明的属性,必须显示调用 super.属性
  3. 父类中的方法被重写,要调用父类的此方法,则需要显示调用:super.方法()

4.3 super 调用构造器

  1. 我们可以在子类的构造器中显式的使用 super(新参列表) 的方式,调用父类中声明的构造器。
  2. super(新参列表) 必须放在首行!
  3. this(形参列表)this(形参列表) 只能二选一。
  4. 构造器没有显式使用 this()super(),则默认使用的是无参构造器。

4.4 子类实例化的全过程

  1. 从结果上来看: (继承性)

    子类继承父类以后,就获取了父类中声明的属性或方法。

    创建子类的对象,在堆空间中,就会加载所有父类中声明的属性。
  2. 从过程上来看: 当我们通过子类的构造器创建子类对象时,我们一-定会直接或间接的调用其父类的构造器,进而调用父类的父类的构造器,直到调用了java. lang. Object类中空参的构造器为止。正因为加载过所有的父类的结构,所以才可以看到内存中有父类中的结构,子类对象才可以考虑进行调用。

5. OOP特征三:多态

5.1 多态性的理解

  1. 理解多态性:可以理解为:一个事物的多种形态。
  2. 何为多态性

    对象的多态性是指 父类的引用指向子类的对象(或子类的对象赋给父类的引用)
  3. 多态的使用:虚拟方法调用 有了对象的多态性以后,我们在编译期,只能调用父类中声明的方法,但在运行期,我们实际执行的是子类重写父类的方法。(虚方法调用)

    总结:编译,看左边;运行,看右边。
  4. 多态性的使用前提:①类的继承关系 ②方法的重写
  5. 对象的多态性,只适用于方法,不适用于属性(编译和运行都看左边)

5.2 虚方法与动态绑定

  1. 虚拟方法调用

    子类中定义了 与父类同名同参数的方法,在多态情况下,将此时 父类的方法称为虚拟方法,父类根据赋给它的不同子类对象,动态调用属于子类的该方法。这样的方法调用在编译期是无法确定的。
  2. 动态绑定 编译时 e 为 Person 类型,而方法的调用是在运行时确定的,所以调用的是 Student 类的 getInfo() 方法。

5.3 向下转型

问题1

有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法的,但是由于 变量声明为父类类型,编译时,只能调用父类中声明的属性和方法。子类特有的属性和方法不能调用

解决方法
  • 如何才能调用子类特有的属性和方法?
  • 向下转型:使用 强制类型转换符
Person p1 = new Man();
Syatem.out.println(p1.value); //value为子类Man的特有属性,编译错误
// 解决:强制类型转换
System.out.println(((Circle)p1).value);
问题2

使用强转时,可能出现ClassCastException的异常。

(编译时提供,运行时不通过)

Person p2 = new Man();
Woman s1 = (Woman) p2;
解决方法

instanceof 关键字的使用:判断a是否为类A的实例,若是则返回 true,否则返回 false

a instanceof A
拓展

a instanceof A 返回 true,类B是类A的父类,则 a instanceof B 也返回 true

x instanceof Object 恒为 true

6. equals

6.1 回顾==的使用

  • 可以使用在基本数据类型变量和引用数据类型变量中
  • 如果比较的是基本数据类型变量:比较两个变量保存的 数据 是否相等。(不一定类型要相同) 如果比较的是引用数据类型变量:比较两个对象的 地址值 是否相同。即两个引用是否指向同一个对象实体

6.2 equals

  • Object类中 equals() 的定义:
public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
}

说明:在 Object 类中定义的 equals() 方法与 == 的作用是相同的:比较两个对象的地址值是否相等,即两个引用是否是否指向相同。

  1. 像 String、Date、File、包装类等都 重写 了 Object 类中的 equals() 方法。重写后,比较的不是两个引用的地址值是否相等,而是比较两个对象的 “实体值” 是否相同。

7. 包装类 Wrapper

7.1 包装类定义

  • 针对八种 基本数据类型定义相应的引用类型一包装类(封装类)
  • 有了类的特点,就可以调用类中的方法,使得基本数据类型的变量具有类的特征
  • 数值型的包装类的父类都是 Number
基本数据类型 包装类
byte Byte
short Short
int Integer
long Long
float Float
double Double
boolean Boolean
char Character

7.2 包装类使用

使用:关键是 基本数据类型、包装类、String 三者之间的 相互转换

  • 基本数据类型 --> 包装类

    使用构造器:new Xxx
Integer int2 = new Integer("123");
Float f1 = new Float("1234.567");
Boolean b1 = new Boolean("TrUe");
  • 包装类 --> 基本数据类型

    使用 xxxValue()
Integer in1 = new Integer("12345");
int i1 = in1.intValue()+12345; // 24690
  • 基本数据类型、包装类 --> String类型

    使用 String 重载的 ValueOf() 方法
float f1 = 12345.67f;
String s1 = String.valueOf(f1);
System.out.println(s1 instanceof String); //true
  • String类型 --> 基本数据类型、包装类

    调用包装类的 parseXxx() 方法
    String s1 = "1234567";
    int i1 = Integer.parseInt(s1);
    System.out.println(i1 +1); //1234568
    String s2 = "truE";
    System.out.println(Boolean.parseBoolean(s2));// true
    

7.3 自动装箱和自动拆箱

  • 自动装箱:基本数据类型 --> 包装类
int num1 = 12345;
Integer int1 = num1;
  • 自动拆箱:包装类 --> 基本数据类型
Integer int1 = new Integer("12345");
int num1 = int1;

P297-P342