Day4—面向对象(继承、多态、类转换、包装类)

继承性

class A extends B {}
  • 一旦子类A继承父类B之后,子类A中就获取了父类B中声明的所有属性和方法
  • 子类A继承了父类B之后,子类A内还可以定义自己的属性或方法,实现功能扩展

优点:

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

继承规则:

  1. Java只支持单继承和多层继承,不允许多重继承。
  2. 一个子类只能有一个父类。
  3. 一个父类可以派生出多个子类。
  4. 子类不能直接访问父类中私有的(private)的成员变量和方法。
受限于父类private封装性的影响,子类无法直接调用父类的私有结构

注:Object类是所有Java类的根父类。如果在类的声明中未使用extends关键字指明其父类,则默认父类为java.lang.Object类

方法重写(Override/Overwrite)
//父类
class Person {   
    // 属性
    ...
    
    // 构造器
    ...
    
    // 方法
    get(),set()...
    
    public void eating(){
        System.out.println("eating...");
    }

    public void sleeping(){
        System.out.println("sleeping...");
    }
}

//子类
class Student extends Person{  
    // 属性
    ...
    
    // 构造器
    ...
    
    // 方法
    get(),set()...
    
    // 重写的方法
    public void eating(){
        System.out.println("eating breakfast");
        System.out.println("eating lunch");
        System.out.println("eating dinner");
    }
    
    public void sleeping(){
        System.out.println("sleeping well");
    }

}

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

要求:

  1. 子类重写的方法必须和父类被重写的方法具有相同的方法名称、参数列表
  2. 子类重写的方法的返回值类型不能大于父类被重写的方法的返回值类型
  3. 子类重写的方法使用的访问权限不能小于父类被重写的方法的访问权限
    注:子类不能重写父类中声明为private权限的方法
  4. 子类方法抛出的异常不能大于父类被重写方法的异常
权限修饰符

Java权限修饰符public、protected、 (缺省)、 private置于类的成员定义前,用来限定对象对该类成员的访问权限。

权限修饰符

注:对于class的权限修饰只可以用public和default(缺省)。

  • public类可以在任意地方被访问。
  • default类只可以被同一个包内部的类访问。
super

定义父类:

// 父类
class Person2 {   
    // 属性
    String name;
    int age;
    int id = 1001;   // 身份证号
    
    // 构造器
    public Person2(){
    }
    
    public Person2(String name,int age){
        this.name = name;
        this.age = age;
    }
    
    // 方法
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    
    public void eating(){
        System.out.println("eating...");
    }
}

定义子类:

//子类
class Student2 extends Person2{  
    // 属性
    String major;
    int id = 1002;  // 学号
    
    // 构造器
    public Student2(){
//      super();
    }
    
    public Student2(String name,int age,String major){
        super(name,age);   // 利用super关键字调用父类的构造器
        this.major = major;
    }
    
    // 方法
    public void show(){
        System.out.println("id:"+ id);
        System.out.println("id:"+ super.id);
        System.out.println();
        eating();
        super.eating();
    }
}

在Java类中使用super来调用父类中的指定操作:

  • super可用于访问父类中定义的属性
  • super可用于调用父类中定义的成员方法
  • super可用于在子类构造器中调用父类的构造器

注:

  1. 尤其当子父类出现同名成员时,可以用super表明调用的是父类中的成员
  2. super的追溯不仅限于直接父类
  3. super和this的用法相像,this代表本类对象的引用,super代表父类的内存空间的标识
this与super的区别

利用super调用父类构造器

  • 子类中所有的构造器默认都会访问父类中空参数的构造器
  • 当父类中没有空参数的构造器时,子类的构造器必须通过this(参数列表)或者super(参数列表)语句指定调用本类或者父类中相应的构造器。同时,只能“二选一”,且必须放在构造器的首行
  • 如果子类构造器中既未显式调用父类或本类的构造器,且父类中又没有无参的构造器,则编译出错
子类对象实例化过程
子类对象的实例化过程

子类对象进行实例化时,都会直接调用this()指向当前类内部的其它构造器,或者通过super()指向父类的一个构造器,最终会调用Object类的空参构造器。因此:
1、this和super关键字不会出现在同一个构造器中;
2、构造器A中调用另一个构造器B,则构造器B需要再构造器A中首行进行声明。

多态性

SuperClass class = new SubClass();   // 左边为父类编译,右边为子类执行

若编译时类型和运行时类型不一致,就出现了对象的多态性(Polymorphism)
多态的使用也叫【虚拟方法调用】:
在编译阶段,只能调用父类声明的方法
在执行阶段,实际调用子类中重写父类的方法

public class Person {
    String name;
    int age;
    
    public void eat(){
        System.out.println("eating...");
    }
}

public class Man extends Person {
    boolean isSmoking;
    
    public void earnMoney(){
        System.out.println("earning money...");
    }
    
    public void eat(){      // override
        System.out.println("eating more...");
    }
}

        // 多态性体现
        // 父类的引用指向子类的对象,即对象的多态性
        Person p2 = new Man();
        
        // 多态的使用
        // 当调用子父类同名同参的方法时,实际执行的是子类中重写父类的方法
        // (虚拟方法调用)
        p2.eat();         //eating more...



        // 如何调用子类特有的属性和方法?
        // 强制类型转换(向下转型)
        Man m1 = (Man)p2;
        m1.earnMoney();
        m1.isSmoking = true;

不能调用子类所持有的方法、属性,因为在编译阶段时,p2是Person(父类)类型:
虽然有了对象的多态性以后,内存中实际上加载了子类特有的属性和方法,但由于变量声明为父类类型,导致编译时只能使用父类中声明的属性和方法
注:对象的多态性只适用于方法,不适用于属性

因此总结出多态性的使用前提:

  1. 要有类的继承关系
  2. 要有方法的重写
虚拟方法调用

正常的方法调用:

Person e = new Person();
e.getInfo();
Student e = new Student();
e.getInfo();

虚拟方法调用(多态情况下):
子类中定义了与父类同名同参数的方法,在多态情况下,将此时父类的方法称为虚拟方法,父类根据赋给它的不同子类对象,动态调用属于子类的该方法。这样的方法调用在编译期是无法确定的。

Person e = new Student();
e.getInfo(); //调用Student类的getInfo()方法

编译时类型和运行时类型:
编译时e为Person类型,而方法的调用是在运行时确定的,所以调用的是Student类的getInfo()方法。(动态绑定)

不要犯傻,如果它不是动态绑定,它就不是多态。——Bruce Eckel

instanceof
x instanceof A;

检验x是否为类A的对象,返回值为boolean型。

有时在使用强转时,可能会出现ClassCastException异常;因此为避免在转型时出现异常,可以使用进行instanceof判断:

if(p2 instanceof Man){
            Man m1 = (Man)p2;
            m1.earnMoney();
            m1.isSmoking = true;
        }
对象类型转换

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

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

Object类

Object类是所有Java类的根父类

  • 如果在类的声明中未使用extends关键字指明其父类,则默认父类为java.lang.Object类
  • Object类仅声明了一个空参构造器
  • Object类没有属性
equals()

1、这是一个方法,不是运算符
2、只能比较引用数据类型

       //Object类中equals()方法的定义:
       public boolean equals(Object obj){
           return (this == obj);
       }

Object类中定义的equals() 与 == 的作用相同,如果需要自定义比较方法,则需要对equals()方法进行重写

注:String、Data等包装类中重写了equals()的方法,比较的是对象中的具体内容是否相同

== 与 equals() 的区别
toString()

toString()方法在Object类中定义,其返回值是String类型,返回类名和它的引用地址。
如果需要自定义比较方法,则需要对toString()方法进行重写

注:String、Data等包装类中重写了toString()的方法,依据不同的类输出的信息也各不相同

单元测试

用于单独对方法进行测试的一种方式

import java.util.*;

import org.junit.Test;

// 单元测试
/*
 *  专门用于代码测试的方法
 *  对java类的要求:1、必须是public的   2、必须提供公共的无参构造器
 *  对于方法的要求:1、必须是public的   2、没有返回值,没有形参 
 * 
 *  单元测试上需要注明@Test,并且需要import org.junit.Test;
 */
public class UnitTest {
    
    @Test
    public void testEquals(){
        String s1 = "Kana" + 233;
        String s2 = "Kana233";
        System.out.println(s1.equals(s2));  // true
    }
    
    
    @Test
    public void testToString(){  // 可双击方法名,单独执行此测试方法
        Date d = new Date(23146786239L);
        System.out.println(d);
        System.out.println(d.toString());
    }
}

包装类

针对基本数据类型进行包装,使其可以像类一样进行使用:


image.png

基本数据——>包装类

包装类 类名 = new 包装类(变量名)

int num1 = 10;
Integer in1 = new Integer(num1);

Integer in2 = new Integer("123");

包装类——>基本属性

调用包装类的xxxValue()

Integer in1 = new Integer(12);
int i1 = in1.intValue();

自动装箱/自动开箱

JDK1.5之后,支持自动装箱,自动拆箱。但类型必须匹配

int num1 = 10;
Integer in1 = num1;   // 自动装箱
        
int num2 = in1; // 自动拆箱
System.out.println(++num2);   // 11

基本数据/包装类——>String

int num = 10;
Integer in = 20;
// 使用链接符
String s1 = "" + num;
        
// 使用类方法valueOf()
String s2 = String.valueOf(num);
String s3 = String.valueOf(in);

String——>基本数据/包装类

调用包装类中的parseXxx()

String str = "123";
String str1 = "false";
        
int num = Integer.parseInt(str);
System.out.println(++num);  // 124
        
boolean flag = Boolean.parseBoolean(str1);
System.out.println(flag);  // false
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容