面向对象编程主线五多态

面向对象编程

五、第五阶段–多态性

5.1、理解多态性

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

  2. 何为多态性:

    1. 对象的多态性:父类的引用指向子类的对象(或子类的对象赋给父类的引用)
  3. 多态的使用,虚拟方法调用:当掉用子父类同名同参数的方法时。实际执行的是子类重写父类的方法

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

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

  4. 多态性的使用前提:

    ​ ① 类的继承关系

    ​ ② 要有方法的重写

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

package cs20230316.java1;

public class PersonTest {
    public static void main(String[] args) {
        Person p1 =new Person();
        p1.eat();

        Man man = new Man();
        man.earnMoney();
        man.eat();
        //对象的多态性:父类的引用指向子类的对象
        Person p2 = new Man();
        Person p3 = new Woman();
        //多态的使用:当掉用子父类同名同参数的方法时。实际执行的是子类重写父类的方法----虚拟方法调用
        p2.eat();
        p3.eat();
        //p2.earnMoney();//Person中没有该方法,所以不能编译通过
    }
}

5.2、方法的重载与重写

1.二者的定义细节:略

2.从编译和运行的角度看:
重载,是指允许存在多个同名方法,而这些方法的参数不同。编译器根据方法不同的参数表,对同名方法的名称做修饰。对于编译器而言,这些同名方法就成了不同的方法。它们的调用地址在编译期就绑定了Java的重载是可以包括父类和子类的,即子类可以重载父类的同名不同参数的方法。
所以:对于重载而言,在方法调用之前,编译器就已经确定了所要调用的方法,这称为**“早绑定”或“静态绑定”
而对于
多态**,只有等到方法调用的那一刻,解释运行器才会确定所要调用的具体方法,这称为**“晚绑定”或“动态绑定”**。
引用一句Bruce Eckel的话:“不要犯傻,如果它不是晚绑定,它就不是多态。”

5.3、instanceof

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

如何才能调用子类特有的属性和方法?

答:向下转型:使用强制类型转换符。

//对象的多态性:父类的引用指向子类的对象
Person p2 = new Man();
Man m1 = (Man) p2;

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

5.3.1、使用方法

a instanceof A判断对象a是否是类A的实例,如果是,返回true;如果不是。返回false。

使用情境:为了避免在向下转型时出现ClassCastException的异常,我们在向下转型之前,先进行 instanceof的判断,一旦返回true,就进行向下转型;如果返回false,不进行向下转型。

//练习:
//问题一:编译时通过,运行时不通过
//举例一
Person p4 = new Woman();//Woman和Man都是Person的子类,不能相互转换
Man m3 = (Man) p3;
//举例二
Person p5 = new Person();
Man m4 = (Man) p5;//new 的就是 Person,不能转为子类

//问题二:编译通过,运行时也通过
Object obj = new Woman();//Object是间接父类
Person p = (Person) obj;//Persons 直接父类,让间接父类转为直接父类是可行的

//问题二:编译不通过
Man m5 = new Woman();

面向对象编程主线五多态_第1张图片

5.4、多态性的总结和理解

  1. 子类重写了父类方法,就意味着子类里定义的方法彻底覆盖了父类里的同名方法,系统将不可能把父类里的方法转移到子类中:编译看左边,运行看右边
  2. 对于实例变量则不存在这样的现象,即使子类里定义了与父类完全相同的实例变量,这个实例变量依然不可能覆盖父类中定义的实例变量编译运行都看左边
  • 谈谈你对多态性的理解?(还需重点理解)

    ① 实现代码的通用性。

    ② Object类中定义的public boolean equals(object obj){}

    ​ JDBC:使用java程序操作(获取数据库连接,CRUD)数据库(MySQL、Oracle、DB2、SQL Server )

    ③ 抽象类、接口的使用肯定体现了多态性。(抽象类、接口不能实例化)

5.5、Object类的使用

java.lang.Object类

  1. Object类是所有Java类的根父类

  2. 如果在类的声明中未使用extends关键字指明其父类,则默认父类为java.lang.Object类

  3. Object类的方法具有通用性

    1. 属性:无

    2. 方法:equals() / toString() / getClass() / hashCode() / clone() / finalize()

      wait() 、 notify() 、notifyAll()

面试题:final、 finally 、finalize的区别?

5.5.1、equals()方法和==运算符

面试题: == 和 equals() 区别

5.5.1.1、== 运算符

​ ① 可以使用在基本数据类型变量和引用数据类型变量中

​ ② 如果比较的是基本数据类型变量:比较两个变量保存的数据是否相等。(不一定类型要相同,因为有自动类型提升)

​ ③ 如果比较的是引用数据类型变量:比较两个变量的地址值是否相等。即两个引用是否指向同一个对象实体

补充: == 符号使用时,必须保证符号左右两边的变量类型一致。

//基本数据类型
int i = 20;
char c = 20;
System.out.println(c == i);//true

char c1 = 'A';
char c2 = 65;
System.out.println(c1 == c2);//true

//引用数据类型
//因为都是 new 了不同的对象
Customer cust1 = new Customer("Tom","Andy");
Customer cust2 = new Customer("Tom","Andy");
System.out.println(cust2 == cust1);//false

String str1 = new String("Cute");
String str2 = new String("Cute");
System.out.println(str2 == str1);//false

5.5.1.2、equals()方法

equals()方法的使用:

① 是一个方法,而非运算符

② 只能适用于引用数据类型

③ Object类中equals()方法的定义:

public boolean equals(Object obj) {
    return (this == obj);
}
说明:Object类中定义的equals()方法和 == 的作用是相同的,比较两个对象的比较两个变量的地址值是否相等。即两个引用是否指向同一个对象实体

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

⑤ 通常情况下,我们自定义的类如果使用equals()方法的话,也通常是比较两个对象的“实体内容”是否相同。那么,我们就需要对Object类中的equals()方法重写

​ 重写的原则:比较两个对象的实体内内容是否相同。

5.5.1.3、toString()方法

Object类中toString()的使用

  1. 当我们输出一个对象的引用时,实际上就是调用当前对象的toString()
  2. Object类中toString()的定义:
  3. 像String 、Date、File、包装类等都重写了Object类中的toString()方法。使得在调用对象的toString()时,返回“实体内容”信息。
  4. 自定义类也可以重写toString()方法,当调用此方法时,返回对象的“实体内容”。
Object类中toString()的定义:
public String toString() {
    return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

5.5.2、单元测试方法的使用

想测试哪一块就单独测试这一块,其他不测试。

JUnit

Eclipse中单元测试的使用

Java中的JUnit单元测试
步骤:
1.选中当前工程–右键选择: build path - add libraries - JUnit 4 -下一步
2.创建Java类,进行单元测试。
此时的Java类要求:此类是public的2此类提供公共的无参的构造器
3.此类中声明单元测试方法。
此时的单元测试方法:方法的权限是public,没有返回值,没有形参
4.此单元测试方法上需要声明注解:@Test,并在单元测试类中导入:import org.junit.Test;
5.声明好单元测试方法以后,就可以在方法体内测试相关的代码。
6.写完代码以后,左键双击单元测试方法名,右键:run as - JUnit Test
    说明:
    1.如果执行结果没有任何异常:绿条
    2.如果执行结果出现异常:红条

5.6、包装类(Wrapper)

面向对象编程主线五多态_第2张图片

虚线框中的都是数值类,父类都是Number

5.6.1、包装类的使用

  1. java提供了8种基本数据类型对应的包装类,使得基本数据类型的变量具有类的特征

  2. 掌握的:基本数据类型、包装类、String三者之间的相互转换

    ① 基本数据类型–>包装类:调用包装类的构造器,见5.6.2的自动封箱、自动拆箱

    ② 包装类–>基本数据类型:调用包装类的Xxx的xxxValue(),如:intValue()、floatValue():见5.6.2的自动封箱、自动拆箱

    基本数据类型、包装类–>String类型:调用String重载的valueOf(Xxx xxx)

    String类型–>基本数据类型、包装类:调用包装类中的parseXxx(String s)

    //基本数据类型、包装类-->String类型
    int num4 = 10;
    //方式一:连接运算
    String str1 = num4 + "";
    //方式二:调用String重载的valueOf(Xxx xxx)
    float f1 = 12.3f;
    String str2 = String.valueOf(f1);//12.3
    
    Double d1 = 12.4;
    String str3 = String.valueOf(d1);
    
 //String类型-->基本数据类型、包装类:调用包装类的 parseXxx(String s)
        String str5 = "123a";
        //错误写法
//        int num1 = (int) str5;
        //可能会报 NumberFormatException的错,所以必须保证String的数据可以转换
        int n1 = Integer.parseInt(str5);
        System.out.println(n1+5);

        //只要不是 true,就全部都是false
        String s2 = "true ";
        boolean b1 = Boolean.parseBoolean(s2);

5.6.2、JDK5.0新特性

JDK5.0新特性:自动装箱自动拆箱

//自动装箱:基本数据类型-->包装类
int num2 = 10;
Integer int1 = num2;//自动装箱
System.out.println(int1);

boolean b2 = true;
Boolean b3 = b2;//自动装箱
System.out.println(b3);
//自动拆箱:包装类-->基本数据类型
System.out.println(int1.toString());//自动拆箱

int num3 = int1;//自动拆箱
System.out.println(num3);
//自动装箱:基本数据类型-->包装类
int num2 = 10;
Integer int1 = num2;//自动装箱
System.out.println(int1);

boolean b2 = true;
Boolean b3 = b2;//自动装箱
System.out.println(b3);
//自动拆箱:包装类-->基本数据类型
System.out.println(int1.toString());//自动拆箱

int num3 = int1;//自动拆箱
System.out.println(num3);

你可能感兴趣的:(Java,java,开发语言)