5. 面向对象编程(中)

二、面向对象特征之二: 继承性

格式:

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

特点:

  1. 一旦子类A继承父类B以后,子类A中就获取了父类B中声明的所有的属性和方法。
    特别的,父类中声明为private的属性或方法,子类继承父类以后,仍然认为获取了父类中私有的结构。只有因为封装性的影响,使得子类不能直接调用父类的结构而已。
  2. 子类继承父类以后,还可以声明自己特有的属性或方法:实现功能的拓展。

Java中关于继承性的规定:

  1. 一个类可以被多个子类继承。(多层继承)
  2. Java中类的单继承性:一个类只能有一个父类;
  3. 子父类是相对的概念。
  4. 子类直接继承的父类,称为:直接父类。间接继承的父类称为:间接父类
  5. 子类继承父类以后,就获取了直接父类以及所有间接父类中声明的属性和方法

补充:

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

作用:

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

方法的重写

重写:子类继承父类以后,可以对父类中同名同参数的方法,进行覆盖操作

重写的规定:

方法的声明:权限修饰符 返回值类型 方法名(形参列表) throws 异常的类型{
                        //方法体
                    }

约定俗称:子类中的叫重写的方法,父类中的叫被重写的方法

  1. 子类重写的方法的方法名和形参列表与父类被重写的方法的方法名和形参列表相同
  2. 子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符
    • 特殊情况:子类不能重写父类中声明为private权限的方法
  3. 返回值类型:
    • 父类被重写的方法的返回值类型是void,则子类重写的方法的返回值类型只能是void
    • 父类被重写的方法的返回值类型是A类型(应用数据类型),则子类重写的方法的返回值类型可以是A类或A类的子类
    • 父类被重写的方法的返回值类型是基本数据类型(比如:double),则子类重写的方法的返回值类型必须是相同的基本数据类型(必须也是double)
  4. 子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型

注意:子类和父类中的同名同参数的方法要么都声明为非static的(考虑重写),要么都声明为static的(不是重写)。

关键字:super

super可以简单理解为“父类的”;

super的使用:

  • 调用属性和方法
    • 我们可以在子类的方法或构造器中。通过使用"super.属性"或"super.方法"的方式,显式的调用父类中声明的属性或方法。通常情况下,我们习惯省略"super."
    • 特殊情况:当子类和父类中定义了同名的属性时,我们要想在子类中调用父类中声明的属性,则必须显式的使用"super.属性"的方式,表明调用的是父类中声明的属性。
    • 特殊情况:当子类重写了父类中的方法以后,我们想在子类的方法中调用父类中被重写的方法时,则必须显式的使用"super.方法"的方式,表明调用的是父类中被重写的方法。
  • 调用构造器
    • 我们可以在子类的构造器中显式的使用"super(形参列表)"的方式,调用父类中声明的指定的构造器
    • "super(形参列表)"的使用,必须声明在子类构造器的首行!
    • 在类的构造器中,针对于"this(形参列表)"或"super(形参列表)"只能二选一,不能同时出现
    • 在构造器的首行,没有显式的声明"this(形参列表)"或"super(形参列表)",则默认调用的是父类中空参的构造器:super()【父类中没有空参构造器会报错,解决办法:①在父类中创建空参构造器 ②调用父类中其它的构造器】
    • 在类的多个构造器中,至少有一个类的构造器中使用了"super(形参列表)",调用父类中的构造器
a89aa5fd2db2cfcf369ad077627190ed.png

子类对象实例化的全过程

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

明确:虽然创建子类对象时,调用了父类的构造器,但是自始至终就创建过一个对象,即为new的子类对象。

三、面向对象特征之三: 多态性

对象的多态性:父类的引用指向子类的对象(或子类的对象赋给父类的引用):向上转型(upcasting)。

例:

People p1=new Students();

多态的使用:虚拟方法调用

有了对象的多态性以后,我们在编译期,只能调用父类中声明的方法,但在运行期,我们实际执行的是子类重写父类的方法。

总结:编译看左边;运行看右边。

多态性的使用前提: ① 需要继承(或实现)关系 ② 有方法的重写

对象的多态性只适用于方法,不适用于属性(编译和运行都看左边)

Java引用变量有两个类型:编译时类型和运行时类型。
编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定

对于重载,在方法调用之前,编译器就已经确定了所要调用的方法,这称为“早绑定”或“静态绑定”;

对于多态,只有等到方法调用的那一刻,解释运行器才会确定所要调用的具体方法,这称为“晚绑定”或“动态绑定”。

对象类型转换 (Casting )

基本数据类型转换:
1.自动数据类型转换
2.强制类型转换

Java对象的强制类型转换(称为造型)

  • 从子类到父类的类型转换可以自动进行
  • 从父类到子类的类型转换必须通过造型(强制类型转换)实现
  • 无继承关系的引用类型间的转换是非法的
  • 在造型前可以使用instanceof操作符测试一个对象的类型

0812d19ca70cdcc7b342c106e975b50f.png739b72ab7551899cccf7304a3042031b.png

Person p1 = new Man();
//有了对象的多态性后,内存中实际上是加载了子类特有的属性和方法的(new后就会在内存中存放man类的结构),但是由于变量声明为父类类型,
//导致编译时,只能调用父类中声明的属性和方法。子类特有的属性和方法不能调用。

//向下转型:使用强制类型转换符
Man m1 = (Man)p1;
//使用强转时,可能出现ClassCastException的异常
Woman w1 = (Woman)p1;
//为了避免在向下转型时出现ClassCastException的异常,我们在向下转型之前,先进行instanceof的判断

instanceof 操作符:

x instanceof A:检验x是否为类A的对象,返回值为boolean型。
要求x所属的类与类A必须是子类和父类的关系,否则编译错误。
如果类B是类A的父类,x instanceof B值也为true。

Object类的使用

Object类是所有Java类的根父类
Object类中的功能(属性、方法)就具有通用性。

  • 属性:无
  • Object类只声明了一个空参的构造器
  • 方法:equals() / toString() / getClass() /hashCode() / clone() / finalize()、wait() 、 notify()、notifyAll()

回顾==的使用

== :运算符

    1. 可以使用在基本数据类型变量和引用数据类型变量中
    1. 如果比较的是 基本数据类型变量:比较两个变量保存的数据是否相等。(不一定类型要相同)
    • 如果比较的是引用数据类型变量:比较两个对象的地址值是否相同.即两个引用是否指向同一个对象实体(== 符号使用时,必须保证符号左右两边的变量类型一致。)

equals()方法的使用

  1. 是一个方法,而非运算符
  2. 只能适用于引用数据类型
  3. Object类中equals()的定义:
public boolean equals(Object obj) {
            return (this == obj);
      }
  • 说明:Object类中定义的equals()和==的作用是相同的:比较两个对象的地址值是否相同.即两个引用是否指向同一个对象实体
  1. 像String、Date、File、包装类等都重写了Object类中的equals()方法。重写以后,比较的不是两个引用的地址是否相同,而是比较两个对象的"实体内容"是否相同。
  2. 通常情况下,我们自定义的类如果使用equals()的话,也通常是比较两个对象的"实体内容"是否相同。那么,我们就需要对Object类中的equals()进行重写.
    • 重写的目的:比较两个对象的实体内容是否相同.

toString()方法的使用

  1. 当我们输出一个对象的引用时,实际上就是调用当前对象的toString()
特别的:
String s=null;
System.out.println(s);//null(有保护机制)
System.out.println(s.toString());//出现NullPointerException
  1. Object类中toString()的定义: 返回类名和它的引用地址。
public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
     }
  1. 像String、Date、File、包装类等都重写了Object类中的toString()方法。使得在调用对象的toString()时,返回"实体内容"信息
  2. 自定义类也可以重写toString()方法,当调用此方法时,返回对象的"实体内容"

包装类的使用

为了使基本数据类型的变量具有类的特征,针对八种基本数据类型定义相应的引用类型—包装类(封装类)
cb623f9d0e6eb5af03b9141f8ff6d334.png
73996aabc1e037b927ff4293b89c2f6f.png

一般基本数据类型与包装类的转换都使用自动装箱和自动拆箱,主要关注与String类转换的parseXxx(String)方法和valueOf(变量)方法

//基本数据类型、包装类--->String类型
float f1 = 12.3f;
String str2 =  String.valueOf(f1);//"12.3"
//String类型 --->基本数据类型、包装类:调用包装类的parseXxx(String s)
String str1 = "123";
int num2 = Integer.parseInt(str1);

小知识点:

Integer i = new Integer(1);
Integer j = new Integer(1);
System.out.println(i == j);//false
//Integer内部定义了IntegerCache结构,IntegerCache中定义了Integer[],
//保存了从-128~127范围的整数。如果我们使用自动装箱的方式,给Integer赋值的范围在
//-128~127范围内时,可以直接使用数组中的元素,不用再去new了。目的:提高效率
Integer m = 1;
Integer n = 1;
System.out.println(m == n);//true

Integer x = 128;//相当于new了一个Integer对象
Integer y = 128;//相当于new了一个Integer对象
System.out.println(x == y);//false
posted @ 2023-09-30 16:13  LemonPuer  阅读(23)  评论(0)    收藏  举报