判断类型是否继承_【Java学习 | Javase】继承与多态

整理自:老师课件、《Java核心技术》

继承与多态

判断类型是否继承_【Java学习 | Javase】继承与多态_第1张图片

继承

概念

  • 父类/超类

    • 所有子集的公共域和公共方法的集合

  • 子类

    • 父类的特殊化,是对公共域和方法在功能、内涵方面的拓展和延伸

  • object类

    • 是所有类的祖先

  • 有一个用来判断是否应该设计为继承关系的简单规则,即“is-a"规则,它表明子类的每个对象也是父类的对象

构造方法的作用

  • 子类可以在自己构造方法中使用关键字super来调用父类的构造方法,但super调用语句必须是子类构造方法的第一个可执行语句

  • 子类在自己定义的构造方法中如果没有用super明确调用父类的构造方法,则在创建对象时,首先自动执行父类的无参构造方法,然后再执行自己定义的构造方法

  • 如果一个类未指定构造方法,则系统自动提供无参构造方法。但如果自定义了构造方法,则系统不再提供无参构造方法

  • 在有继承关系时,创建子类对象,无论调用无参还是有参构造,都会调用父类的构造器,对父类属性初始化

  • 但若在初始化父类属性之前,若子类重载构造器进行this()调用,则执行this(),此时隐式super()不再出现

  • 调用子类构造器时,若没有this()表达式,则第一行是隐式super(),但如果子类构造器中第一行已经明确选择父类带参构造器,则进入父类带参构造器

  • 构造器中不能同时写this()和super(),因二者都要求放在首行


多态

  • “is-a"规则的另一种表达法是置换法则,它表明程序中出现超类对象的任何地方都可以用子类对象置换

方法的重载(参数多态)

  • 同一个类中某个方法有多种形态,通过参数区分

  • 方法调用的匹配处理原则

    • 首先按“精确匹配”原则去查找匹配方法,如果找不到,则按“自动类型转换匹配”原则去查找能匹配的方法

    • “精确匹配”即实参与形参类型完全一致

    • “自动转换匹配”指虽然实参和形参类型不同,但能把实参的数据按自动类型转换原则赋值给形参,遵循就近原则

方法的覆盖

  • 对于父类的某个方法,在子类中重新定义一个相同形态的方法

  • 方法名、参数列表完全相同才会产生方法覆盖;返回类型通常也要一致,但若返回类型为引用类型时,允许子类方法的返回类型是父类方法返回类型的子类型

  • 覆盖不能改变方法的静态与非静态属性。子类中不能将父类非静态方法定义为静态方法,反之也一样

  • 不允许子类方法的访问修饰符比父类有更多的限制

  • final方法不能被覆盖


对象引用转换

对象引用自动转换

  • 允许将子类对象赋值给父类的引用变量,但反之不行。这种向上转型的赋值也经常发生在方法调用的参数传递时,如果一个方法的形参定义的是父类引用类型,那么调用这个方法时,可以使用子类对象作为实参。当然,任何方法调用将首先考虑参数精确匹配,然后才考虑转换匹配

对象引用强制转换

  • 将父类引用赋值给子类变量时要进行强制转换,强制转换在编译时总是认可的,但运行时的情况取决于对象的值

  • 为了避免出现此情况,一般先用instanceof操作符判断,该二元操作符左边是对象,右边是类,当对象是右边类或子类所创建对象时,返回true;否则,返回false(包括左边对象为null)

    • 类的实例包含本身的实例,以及所有直接或间接子类的实例

    • instanceof左边显示声明的类型与右边操作元必须是同种类或存在继承关系,也就是说需要位于同一个继承树,否则会编译错误

    • 实际用法应该是左边为父类引用,右边为子类,若为true,则可以强制转换


访问继承成员

  • 如果子类中定义了与父类同名的属性,在子类中将隐藏来自父类的同名属性变量

  • 通过子类的引用变量访问自己的对象,无论属性和方法优先考虑子类新定义的。自己类中没有的再到父类找

  • 父类引用变量指向子类对象时的成员访问

    • 实例方法根据变量所引用对象的实际类型进行访问

    • 属性和静态方法根据引用变量的类型进行访问


几个特殊类

Object类

  • 是所有Java类的祖先,有几个常用方法

  • public boolean equals(Object obj):该方法的本意用于两个对象的“深度”比较,即比较两对象封装的数据是否相等;运算符“==”当且仅当两个对象引用指向同一对象才为真。但在Object类中,equals方法是采用“==”运算进行比较

    • 如果重新定义equals方法,必须重新定义hashCode方法

  • public String toString():返回对象的字符串描述;默认是返回“类名@地址散列码”

    • 绝大多数的toString方法都遵循这样的格式:类名,随后是一对方括号括起来的域值。最好通过getClass().getName()获得类名的字符串,而不是将类名硬加到toString方法中,而且这样子类只需要调用super.toString()然后再加上子类特有的域,返回的类名是子类的类名

    • 只要对象与一个字符串通过操作符"+"连接起来,Java编译就会自动调用toString方法

  • public final Class getClass():返回对象的所属类

  • protected void finalize():Java垃圾回收程序在删除对象前自动执行

Class类

  • Java运行环境提供了反射机制,这种机制允许程序中动态获取类的信息,以及动态调用对象的方法。其相关的类主要有Class类、Field类、Method类、Constructor类和Array类,除Class类在java.lang包下,其他都在java.lang.reflect

  • 获取Class类型的对象:Class类封装一个对象或接口运行时的状态,当装载类时,Class类型的对象自动创建

    • 方法1:调用Object类的getClass()方法

    • 方法2:使用Class类的forName(…)方法

    • 方法3:如果T是一个Java类型,那么T.class就代表了该类型匹配的Class对象。如String.class代表字符串类型,int.class代表整数类型


访问控制修饰符

访问修饰符是一组限定类、域或方法是否可以被程序其他部分访问和调用的修饰符

Java用来修饰类的访问控制符只有public,表示类对外开放;类定义时也可以无访问修饰,表示类只限于同一包中访问使用

修饰类的成员的访问控制符有3种:public、 protected 、private,还有一种是无修饰符的默认情况

  • 公共访问控制符public

    • 作为类的修饰符,将类声明为公共类,表明它可以被所有的其他类所访问和引用

    • 作为类的成员的访问修饰符,表明在其他类中可以无限制地访问该成员

    • 要想做到类成员在任何地方(包内+包外)可以被访问,在进行类设计时必须同时满足两点:首先类被定义为public,其次,类的成员被定义为public

  • 缺省访问控制符

    • 没有给出访问控制符情形

    • 该类只能被同一个包中的类访问和引用,该类的方法与属性亦然

  • 私有访问控制符private

    • 用private修饰的域或方法只能在该类自身中被访问,是最高的保护级别,一般提供getter and setter间接访问或修改域

    • 通常,出于系统设计的安全性考虑,将类的成员属性定义为private形式保护起来,而将类的成员方法定义为public形式对外公开,这是类封装特性的一种体现

  • 保护访问控制符protected

    • 用protected修饰的成员可以在三种类中所引用:

    • 该类本身

    • 与它在同一个包的其他类

    • 在其他包中的该类的子类

外界能访问某个类的成员的条件是:首先能够访问类,其次还要能访问类的成员


final修饰符的作用

  • final作为修饰符表明是最终类(不能有子类)

    • final类的所有方法自动地成为final方法,但不包括域

  • final修饰方法表明不能被子类覆盖

  • final定义属性表明只能赋值一次即常量

    • 若将引用类型的变量标记为final,那么该变量固定指向一个对象,但是可以改变对象内的属性值

    • 与普通属性变量不同,系统不会给常量赋默认初值不允许在实例方法中赋值或者直接赋值。如果是实例常量(未赋值),允许在初始化代码块或构造方法中赋值一次(顺序为:定义时赋值-->初始化代码块-->构造方法,一旦前面赋值了后面不可赋值);同理,对于类常量,只允许在定义时赋值或静态代码块赋值,顺序前者比后者先,同样一旦前面赋值了后面不可赋值

易错点

1、父类自定义构造方法,但没有手动添加无参构造方法

public 

2、虽然创建子类对象时会自动调用父类的构造方法,但是并没有创建父类对象。

分析:可以看到以下代码的创建的对象始终为一个,因为地址相同

/*
    输出
    Base()Sub@15db9742
    Sub()Sub@15db9742
    hello
*/

3、this与super相互调用

/*
    输出:
    Base()Sub@15db9742
    Base带参构造
    Base()Sub@15db9742
    Base空构造
    Sub()Sub@15db9742
    Sub带参构造
    Sub()Sub@15db9742
    Sub空构造
    Sub:10
    39
*/

4、方法重载

4.1、精确匹配

/*
    test(int)5
    test(double)5.0
*/

4.2、自动类型转换匹配+就近原则

/*
    test(long)5
    test(double)5.0
*/

4.3、重载不匹配

/*
    错误: 对于test(double), 找不到合适的方法
    t.test(5.0);
    ^
    方法 Test.test(int)不适用
    (参数不匹配; 从double转换到int可能会有损失)
    方法 Test.test(long)不适用
    (参数不匹配; 从double转换到long可能会有损失)
*/

5、方法覆盖时函数名、参数列表要完全相同;返回类型若是引用类型允许是父类方法返回类型的子类;子类覆盖的方法不能比父类方法的访问权限更低

分析:父类returnThis的返回类型是父类

public 

6、对象引用强制在编译时总是可以的,但在运行时可能出错

public 

7、访问继承成员时,若是子类引用变量指向子类对象,访问方法或属性时优先考虑子类自身的;若是父类引用变量指向子类对象,那么实例方法根据引用对象的实际类型进行访问,属性和静态方法根据引用变量的类型访问

public 

8、访问修饰符测试

  • 只能被public或缺省修饰符修饰

//Modifier 'private' not allowed here
  • 缺省修饰符:类只能在本包内被访问;若在其他包内访问非本包的类的属性,则出现以下错误,与访问非本包的缺省修饰符修饰的类相似

'age' is not 
package com.gdut.ldh;
  • private修饰符:若在非本类内访问private修饰的属性

'age' has 
  • protected修饰符:只能被本包或其他包的子类访问

分析:Test2与Test3在同一个包,而Test在另一个包

package com.gdut.bjpowernode;

9、常量赋值

public 

10、final类的所有方法自动转换为final方法,但不包括域

public 

11、instanceof操作符

  • 假设父类为:

public 
  • 当s为null时,s instanceof Parent不会产生异常,而是返回false。因为null没有引用任何对象,当然也不会引用Parent的对象

public 
  • 左边是右边类的实例,返回true,实际用法是第二个if判断是否可以强制转换

public 
  • 左右两边需位于同一棵继承树,否则编译错误

public 

12、子类数组的引用可以转换成超类数组的引用,而不需要采用强制类型转换

  • Sub[0]是一个Sub,也一定是一个Parent[0].然而,请注意,此时subs与parents指向同一个数组,当执行parents[0] = new Parent();语句时,编译器会通过。但在这里,我们把一个父类加到子类里了,当访问subs[0].x将导致访问一个不存在的值。为了防止出现此情况,运行时会触发一个运行时异常:java.lang.ArrayStoreException

public 

你可能感兴趣的:(判断类型是否继承)