高级类特性1

面向对象的特征之二:继承

为什么要有继承?

  • 多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可。
  • 把共性的东西抽取出来形成父类,实际需求的子类在继承父类的基础上写自己特有的代码即可。
  • 此处的多个类称为子类,单独的这个类称为父类(基类或超类)。可以理解为:子类 is a 父类
  • 类继承语法规则:

    class Subclass extends Superclass{}
  • 作用:

    • 继承的出现提高了代码的复用性。
    • 继承的出现让类与类之间产生了关系,提供了多态的前提。
    • 不要仅为了获取其他类中某个功能而去继承。继承是要有逻辑关系在里面,不要随意继承。

类的继承

  • 子类继承了父类,就继承了父类的方法和属性。
  • 在子类中,可以使用父类中定义的方法和属性,也可以创建新的数据和方法。
  • 在Java中,继承的关键字用的是 ”extends“,即子类不是父类的子集,而是对父类的 ”扩展“。

关于继承的规则

子类不能直接访问父类中私有的(private)的成员变量和方法。

单继承和多层继承

  • Java只支持单继承,不支持多重继承

    • 一个子类只能有一个父类
    • 一个父类可以派生出多个子类
class SubDemo extends Demo{}    //OK
class SubDemo extends Demo1,Demo2...{}    //error

方法的重写(override)

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

    • 重写方法必须和被重写方法具有相同的方法名称、参数列表和返回值类型。(子类重写父类的方法,只是重新编写方法体的代码)
    • 重写方法不能使用比被重写方法更严格的访问权限。
    • 重写和被重写的方法须同时为static的,或者同时为非static的。
    • 子类方法抛出的异常不能大于父类被重写方法的异常。
  • 重载和重写的区别

    • 方法的重载:一个类可以有多个同名方法。
    • 方法的重写:子类可以重新写父类的方法,覆盖父类方法。

父子类访问修饰符的关系

  • 如果子类和父类在同一个包下,父类的成员修饰符只要不是私有的(private),那子类就都可以使用。
  • 如果子类的父类不在同一个包下,那么子类只能使用父类中protected和public修饰的成员。

关键字——super

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

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

    • 尤其当子父类出现同名成员时,可以用super进行区分。
    • super的追溯不仅限于直接父类。(多层继承,使用super,子类可以调用子类之上所有父类层级)
    • super和this的用法相像,this代表本类对象的引用,super代表父类的内存空间的标识。

调用父类的构造器

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

this和super的区别

No.区别点thissuper
1访问属性访问本类中的属性,如果本类没有此属性,则从父类中继续查找访问父类中的属性
2调用方法访问本类中的方法直接访问父级中的方法
3调用构造器调用本类的构造器,必须放在构造器的首行调用父类的构造器,必须放在子类构造器的首行
4特殊表示当前对象

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

  • 多态性,是面向对象中最重要的概念,在Java中有两种体现:

    • 方法的重载(overload,本类中的同名方法)和重写(override,子类对父类方法的覆盖)。
    • 对象的多态性——可以直接应用在抽象类和接口上。
  • Java引用变量有两个类型:编译时类型运行时类型

    • 编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。
    • 若编译时类型和运行时类型不一致,就出现多态(Polymorphism)。

对象的多态

  • 在Java中,子类的对象可以替代父类的对象使用

    • 一个变量只能有一种确定的数据类型。
    • 一个引用类型变量可能指向(引用)多种不同类型的对象。
    Person p = new Student();
    Person e = new Student();//Person类型的变量e,指向Student类型的对象
  • 子类可以看做是特殊的父类,所以父类类型的引用可以指向子类的对象:向上转型(upcasting)
  • 一个引用类型变量如果声明为父类的类型,但实际引用的是子类的对象,那么该变量就不能再访问子类中添加的属性和方法

    Student s = new Student();
    m.school = "pku";//合法,Student类有school成员变量
    Person e = new Student();
    e.school = "pku";//非法,Person类没有school成员变量
    
    属性是在编译时确定的,编译时e为Person类型,没有school成员变量,编译错误。

虚拟方法调用

  • 正常的方法调用

    Student s = new Student();
    s.showInfo();//调用的是Student类重写的的showInfo()方法    
    Person p = new Person();
    p.showInfo();//调用的是Person类的showInfo()方法
  • 虚拟方法调用(多态情况下)

    Person e = new Student();
    e.showInfo();//调用的是Student类重写的showInfo()方法    
  • 编译时类型和运行时类型

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

多态的小结

  • 前提:

    • 需要存在继承(父子类)或者实现关系
    • 要有覆盖操作(子类重写父类的方法)
  • 成员方法:

    • 成员方法的多态性,也就是动态绑定,必须得存在于方法的重写之上。
    • 编译时:要查看引用变量所属的类中是否有所调用的方法。
    • 运行时:调用实际对象所属的类中的重写方法。
  • 成员变量:

    • 不具备多态性,只看引用变量所属的类。
  • 子类继承父类

    • 若子类重写了父类方法,就意味着子类里定义的方法彻底覆盖了父类里的同名方法,系统将不可能把父类里的方法转移到子类中。
    • 对于实例变量则不存在这样的现象,即使子类里定义了与父类完全相同的实例变量,这个实例变量依然不可能覆盖父类中定义的实例变量。

多态性应用举例

方法声明的形参类型为父类类型,可以使用子类的对象作为实参调用该方法。

public class Test(){
    public void method(Person e){
        e.getInfo();
    }
    public static void main(String args[]){
        Test t = new Test();
        Student s = new Student();
        t.method(s);//子类的对象s传送给父类类型的参数e
    }
}

instanceof 操作符

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

Object 类

  • Object类是所有Java类的根父类(基类)
  • 如果在类的声明中未使用extends关键字指明其父类,则默认父类为Object类

Object 类中的主要方法

No.方法名称类型描述
1public Object()构造构造方法
2public boolean equals(Object obj)普通对象比较(引用对象)
3public int hashCode()普通取得Hash码
4public String toString()普通对象打印时调用(内存地址)

对象类型转换(casting)

  • 基本数据类型的casting:

    • 自动类型转换:小的数据类型可以自动转换成大的数据类型。

      long g = 20;
      double d = 12.0F;
    • 强制类型转换:可以把大的数据类型强制转换(casting)成小的数据类型。

      float f = (float)12.0;
      int a = (int)1200L;
  • 对Java对象的强制类型转换称为造型:

    • 从子类到父类的类型转换可以自动进行。
    • 从父类到子类的类型转换必须通过造型(强制类型转换)实现。
    • 无继承关系的引用类型间的转换是非法的。

==操作符与equals方法

  • ==操作符

    • 基本类型比较值:只要两个变量的值相等,即为true。
    • 引用类型比较引用(是否指向同一个对象):只有指向同一个对象时,==才返回true。
    • 用==操作符进行比较时,符号两边的数据类型必须兼容(可自动转换的基本数据类型除外),否则编译出错。
  • equals()

    • 所有类都继承了Object类,也就获得了equals()方法。还可以重写。
    • 只能比较引用类型,其作用与==相同,比较是否指向同一个对象。
  • 特例:当用equals()方法进行比较时,对类File、String、Date及包装类(Wrapper Class)来说,是比较类型及内容,而不考虑引用的是否是同一个对象。

    • 原因:在这些类中重写了Object类的equals()方法。
  • 对于对象来说:

    • 特殊的类,比如File、String、Date及包装类(Wrapper Class),使用==比较的是对象(对象的内存地址),equals 比较的是内容。
    • 除了特殊的类之外的其他普通的类的对象,==和equals比较的都是对象(对象的内存地址)。
    • 如果你想改变某一个类的equals方法,不想用equals来比较对象的内存地址,就需要重写equals()方法

包装类(Wrapper)

  • 针对八种基本数据定义相应的引用类型——包装类(封装类)。
  • 有了类的特点,就可以调用类中的方法。
基本数据类型包装类
booleanBoolean
byteByte
shortShort
intInteger
longLong
charCharacter
floatFloat
doubleDouble
  • 基本数据类型包装成包装类的实例——装箱

    • 通过包装类的构造器实现。

      int i = 500;
      Integer t = new Integer(i);//Java 9 以后已经弃用
    • 还可以通过字符串参数构造包装类对象。
  • 获得包装类对象中包装的基本类型变量——拆箱

    • 调用包装类的方法.xxxValue();
    • JDK1.5之后,支持自动装箱,自动拆箱。但类型必须匹配。
Last modification:January 16th, 2020 at 04:56 pm
If you think my article is useful to you, please feel free to appreciate