zoukankan      html  css  js  c++  java
  • 第5章 继承

    类、超类和子类

    Object:所有类的超类

    泛型数组列表

    对象包装器和自动装箱

    参数数量可变的方法

    枚举类

    反射

    继承设计的技巧

    5.1 类、超类和子类

    关键字extends表明正在构造的新类派生于一个已存在的类。已存在的类称为超类(super class)、基类(base class)或父类(parent class);新类称为子类(subclass)、派生类(derived class)或孩子类(child class)。

    在父类中的方法,子类不一定适用,需要提供一个新的方法来覆盖(override)超类中的这个方法。

    调用父类的方法可以通过super解决这个问题,调用父类的构造,仅限子类构造中,super()。

    注释:有些人认为super与this引用是类似的概念,实际上,这样比较并不太恰当。这是因为super不是一个对象的引用,不能将super赋值给另一个对象变量,他只是一个指示编译器调用超类方法的特殊关键字。

    一个对象变量(例如,变量e)可以指示多种实际类型的现象被称为多态(polymorphism)。在运行时能够自动选择调用哪个方法的现象称为动态绑定(dynamic binding)。

    5.1.1 继承层次

    由一个公共超类派生出来的所有类的集合被称为继承层次(inheritance hierarchy);在继承层次中,从某个特定的类到其祖先的路径被称为该类的继承链(inheritance chain)。

    5.1.2 多态

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

    在Java程序设计语言中,对象变量是多态的。

    5.1.3 动态绑定

    1)编译器查看对象的声明类型和方法名。

    2)接下来,编译器将查看调用方法时提供的参数类型。

    3)如果是private方法、static方法,final方法或者构造器,那么编译器将可以准确的知道应该调用那个方法,我们将这种调用方式称为静态绑定(static binding)。

    4)当程序运行,并且采用动态绑定调用方法时,虚拟机一定调用与x所引用对象的实际类型最合适的那个类的方法。

    虚拟机预先为每个类创建了一个方法表(method table),其中列出了所有方法的签名和实际调用的方法。

    5.1.4 阻止继承:final类和方法

    不允许扩展的类被称为final类。

    类中的特定方法也可以被声明为final。如果这样做,子类就不能覆盖这个方法(final类中的所有方法自动地成为final方法)。

    将方法或类声明为final主要目的是:确保它们不会在子类中改变语义。

    5.1.5 强制类型转换

    只能在继承层次内进行类型转换。

    在将超类转换成子类之前,应该使用instanceof进行检查。

    5.1.6 抽象类

    从某种角度看,祖先类更加通用,人们只将它作为派生其他类的基类,而不作为想使用的特定的实例类。

    抽象类不能被实例化。也就是说,如果将一个类声明为abstract,就不能创建这个类的对象。

    5.1.7 受保护的访问

    java用户控制可见性的4个访问修饰符:

    1)仅对本类可见——private。

    2)对所有类可见——public。

    3)对本包和所有子类可见——protected。

    4)对本包可见——默认(很遗憾),不需要修饰符。

    5.2 Object:所有类的超类

    Object类是Java中所有类的始祖,在java中每个类都是由它扩展而来的。

    可以使用Object类型的变量引用任何类型的对象。

    在java中,只有基本类型(primitive types)不是对象。所有的数组类型,不管是对象数组还是基本类型的数组都扩展与Object类。

    5.2.1 equals方法

    Object类中的equals方法用于检测一个对象是否等于另外一个对象。

    5.2.2 相等测试与继承

    java语言规范要求equals方法具有下面的特性:

    1)自反性:对于任何非空引用x,x.equals(x)应该返回true。

    2)对称性:对于任何引用x和y,当且仅当y.equals(x)返回true,x.equals(y)也应该返回true。

    3)传递性:对于任何引用x、y和z,如果x.equals(y)返回true,y.equals(z)返回true,x.equals(z)也应该返回true。

    4)一致性:如果x和y引用的对象没有发生变化,反复调用x.equals(y)应该返回同样的结果。

    5)对于任何非空引用x,x.equals(null)应该返回false。

    不同的情况:

    如果子类能够拥有自己的相等概念,则对称性需求将强制采用getClass进行检测。

    如果由超类决定相等的概念,那么就可以使用instanceof进行检测,这样可以在不同子类的对象之间进行相等的比较。

    下面给出编写一个完美的eauals方法的建议:

    1)显示参数命名为(otherObject),稍后需要将它转换成另一个叫做other的变量。

    2)检测this与otherObject是否引用同意个对象:

    if(this==otherObject) return true;

    3)检测otherObject是否为null,如果为null,返回false。这项检测是很必要的。

    if(otherObject==null) return false;

    4)比较this与otherObject是否属于同一个类。如果equals的语义在每个子类中有所改变,就使用getClass检测:

    if(getClass()!=otherObject.getClass())return false;

    如果所有的子类都拥有统一的语义,就使用instanceof检测:

    if(!(otherObject intanceof ClassName)) return false;

    5)将otherObject转换为相应的类类型变量:

    ClassName other = (ClassName) otherObject;

    6)现在开始对所有需要比较的域进行比较。使用==比较基本数据类型,使用equals比较对象域。如果所有的域都匹配,就返回true;否则返回false。

    5.2.3 hashCode方法

    如果重新定义equals方法,就必须重新定义hashCode方法,以便用户可以将对象插入到散列表中。

    不过,在java7中还可以做两个改进。首先最好使用null安全的方法Objects.hashCode。如果其参数为null,这个方法会返回0,否则返回对参数调用hashCode的结果。

    eauals与hashCode的定义必须一致:如果x.eauals(y)返回true,那么x.hashCode()必须与y.hashCode()具有相同的值。

    5.2.4 toString方法

    用于返回表示对象值的字符串。

    5.3 泛型数组列表

    ArrayList

    ArrayList<Employee> staff = new ArrayList<Employee>();

    5.4 对象包装器与自动装箱

    所有的基本类型都有一个与之对应的类。这些对象包装器拥有很鲜明的名字:Integer、Long、Float、Double、Short、Byte、Character、Void和Boolean(前6个类派生于公共的超类Number)。对象包装器类是不可变的,类还是final。

    自动装箱(基本类型自动转换成包装类),自动拆箱(包装类自动转换为基本类型)。

    5.5 参数变量可变的方法

    这里的省略号...是java代码的一部分,它表明这个方法可以接收任意数量的的对象。

    允许将一个数组传递给可变参数方法的最后一个参数。

    5.6枚举类

    public enum Size{SMALL,MEDIUM,LARGE,EXTRA_LARGE}

    实际上,这个声明定义的类型是一个类,他刚好有4个实例。

    因此在比较两个枚举类型的值时,永远不需要调用equals方法,而直接使用"=="就可以了。

    如果需要的话,可以在枚举类型中添加一些构造器、方法和域。当然、构造器只是在构造枚举常量的时候被调用。

    5.7 反射

    能够分析类能力的程序称为反射(reflective)。反射机制的功能极其强大,在下面可以看到,反射机制可以用来:

    在运行中分析类的能力。

    在运行中查看对象。

    实现通用的数组操作代码。

    利用Method对象,这个对象很像C++中的函数指针。

    5.7.1 Class类

    Object类中的getClass()方法将会返回一个Class类的实例。

    类名.class获得一个Class类的实例。

    Class.forName加载一个类。

    虚拟机为每个类管理一个Class对象。因此,可以利用==运算符实现两个类对象比较的操作。

    5.7.2 捕获异常

    5.7.3 利用反射分析类的能力

    在java.lang.reflect包中有三个类Field、Method和Constructor分别用于描述类的域、方法和构造器。

    这三个类都有一个叫做getName的方法,用来返回项目的名称。Field类有一个getType方法,用来返回描述域所属类型的Class对象。Method和Constructor类有能够报告参数类型的方法,Method类还有一个报告返回类型的方法。这三个类还有一个叫做getModifiers的方法,它将返回一个整型数值,用不同的位开关描述public和static这样的修饰符使用状况。另外,还可以利用java.lang.reflect包中的Modifier类的静态方法分析getModifiers返回的整型数值。例如,可以使用Modifier类中的isPubli、isPrivate或isFinal判断方法或构造器是否是public、private或final。另外,还可以利用Modifier.toString方法将修饰符打印出来。

    Class类中的getFields、getMethods和getConstructors方法将分别返回类提供的public域、方法和构造器数组,其中包括超类的公有成员。Class类的getDeclareFields、getDeclareMethods和getDeclaredConstructors方法将分别返回类中声明的全部域、方法和构造器,其中包括私有和受保护成员,但不包括超类的成员。

    5.7.4 在运行是使用反射分析对象

    5.7.5 使用反射编写泛型数组代码

    5.7.6 调用任意方法

    利用Method类中的invoke方法。

    5.8 继承设计的技巧

    1)将公共操作和域放在超类。

    2)不要使用受保护的域。

    3)使用继承实现"is-a"。

    4)除非所有继承的方法都有意义,否则不要使用继承。

    5)在覆盖方法时,不要改变预期的行为。

    6)使用多态,而非类型信息。

    无论什么时候,对于下面这种形式的代码

    if(x is of type1)

        action1(x);

    else if(x is of type2)

        action2(x);

    都应该考虑使用多态性。

    7)不要过多地使用反射。

     

     

     

  • 相关阅读:
    表的相关操作
    存储引擎介绍
    库的相关操作
    初始数据库
    linux版本的mysql安装
    mysql在windows上的安装即配置
    线程实际操作篇
    用户模板和用户场景
    顶会热词统计
    移动端疫情展示
  • 原文地址:https://www.cnblogs.com/xuzhen97/p/7724366.html
Copyright © 2011-2022 走看看