zoukankan      html  css  js  c++  java
  • Core Java笔记 2.继承

    本章重点:

    • 继承
    • 多态与动态绑定
    • Object类
    • 对象包装器&自动打包

    继承

    涉及到的概念:
    超类(superclass)、子类(subclass)
    extends关键字
    super关键字
    多态(polymorphism)、动态绑定(dynamic binding)

    重点:

    1. 如果子类的构造器没有显式调用超类的构造器,则自动调用超类默认构造器; 如果超类没有不带参数的构造器,并且子类的构造器中又没有显式地调用超类的其他构造器,则将报告错误.
    2. super关键字: 一是调用超类的方法,二是调用超类的构造器.
    3. polymorphism: 一个对象变量可以引用多种实际类型的现象.
    4. dynamic binding: 在运行时能够自动地选择调用哪个方法的现象. Java中dynamic binding是默认的处理方式, 除非用final标记.

    继承层次

    Java 不支持多继承. 采用 单继承+接口.


    polymorphism & dynamic binding

    polymorphism

    多态示例:

    Employee e;
    e = new Employee(...); // Employee object expected
    e = new Manager(...);  // OK, Manager can be used as well
    
    Employee[] staff = new Employee[3];
    Manager boss = new Manager(...);
    staff[0] = boss;  // OK
    
    Manager m = staff[i];  // ERROR
    

    WARN

    package corejava.inheritance;
    
    /**
     * Created by guolong.fan on 15/4/21.
     */
    public class ArrayStoreExceptionTest {
    
        public static void main(String[] args) {
            Integer[] ints = new Integer[10];
            ints[0] = 10;
            Object[] objs = ints;        // OK!
            objs[0] = new Object();
    
            System.out.println(ints[0]);
        }
    
    } /**
    
    Exception in thread "main" java.lang.ArrayStoreException: java.lang.Object at corejava.inheritance.ArrayStoreExceptionTest.main(ArrayStoreExceptionTest.java:12)
     
     */
    

    dynamic binding

    调用对象方法的执行过程:

    1. 编译器查看对象的声明类型和方法名。比如方法f,编译器会一一列举类中及其超类中的所有访问权限为public且名为f的方法。
    2. 编译器查看调用方法时提供的参数类型,这个过程是overloading resolution。首先寻找参数完全匹配的方法,找到直接选择;次之,选择可以转化与之匹配的方法,没有的话,编译
      如果找到参数类型完全匹配,直接选择;次之,没有则选择可以转换与之匹配的方法;
    3. private、static、final方法,编译器可以准确地知道调用哪个方法,即静态绑定(static binding)。
    4. dynamic binding的原理是JVM预先给每个类创建了一个方法表(method table).

    小细节:

    • Java SE5.0开始支持协变。

    final类和 final方法

    final类可以阻止继承,final方法不可override.

    强制转换与 instanceof

    强制转换:

    double x = 3.405;
    int nx = (int)x; // 1. OK
    
    Employee staff = new Manager(...);
    Manager boss = (Manager) staff;  // 2. OK
    

    继承链进行向下转换,Java运行时系统运行下面的程序,会产生ClassCastException异常.

    Employee staff = new Employee(...);
    Manager boss = (Manager) staff;  // ERROR
    

    总结:

    1. 只能在继承层次内进行类型转换,尽量避免强制转换.
    2. 如有必要,在将超类转换为子类前,应该使用instanceof进行检查.
    if (staff instanceof Manager) {
        boss = (Manager) staff;
        ...
    }
    

    Object类

    Object类是 Java 中所有类的最终祖先. 在 Java 中只有基本类型不是对象.

    equals 方法

    典型的equals方法的写法:

    // super class
    class Employee {
        ...
        @Override
        public boolean equals(Object otherObject) {
            // 1. a quick test to see if the objects are identical
            if (this == otherObject) return true;
            
            // 2. must return false if the explicit parameter is null
            if (otherObject == null) return false;
            
            // 3.1 if the classes don't match, they can't be equal
            if (getClass() != otherObject.getClass()) {
                return false;
            }
            
            // or 3.2 如果所有的子类都拥有统一的语义,则使用instanceof检测.
            if (!(otherObject instanceof ClassName)) return false;
            
            // 4. now we know otherObject is a non-null Employee
            Employee other = (Employee) otherObject;
            
            // 5. test whether the fields have identical values
            return name.equals(other.name)
                && salary == other.salary
                && hireDay.equals(other.hireDay);
        }
    }
    
    // sub class
    Class Manager extends Employee {
        ...
        @Override
        public boolean equals(Object otherObject) {
            if (!super.equals(otherObject)) return false;
            
            // super.equals checked that this and otherObject belong to the same class
            Manager other = (Manager) otherObject;
            // test whether the fields have identical values in subclass
            return bonus = other.bonus;
        }
    }
    

    hashCode 方法

    Object 类中的 hashCode 默认实现是对象的存储地址. 当一个类重新定义了 equals 方法时,就必须重新定义 hashCode 方法, 且 equals 与 hashCode 的定义必须保持一致.

    class Employee {
        public int hashCode() {
            return 7 * name.hashCode +
                11 * new Double(salary).hashCode +
                13 * hireDay.hashCode();
        }
        ...
    }
    

    如果存在数组类型的域,可以使用静态的 Arrays.hashCode 计算 hash,这个散列码由数组元素的散列码组成.

    toString 方法

    典型写法:

    public class Employee {
        public String toString() {
            return getClass().getName()
                + "[name=" + name
                + ",salary=" + salary 
                + ",hireDay=" + hireDay
                + "]";
        }
        
        ...
    }
    
    public Manager extends Employee {
        public String toString() {
            return super.toString()
                + "[bonus=" + bonus
                + "]";
        }
    }
    

    x.toString()"" + x 等价. Object 类的 toString()className@hashCode.

    int[] luckyNumbers = {2, 3, 5, 7, 11, 13};
    String s = "" + luckyNumbers;  // [I@hashCode
    
    // 打印单维数组
    String s = Arrays.toString(luckyNumbers); // "[2,3,5,7,11,13]"
    // 打印多维数组
    String s = Arrays.deepToString(luckyNumbers); 
    

    对象包装器&自动打包

    1. Java 的基本类型都有与之对应的包装器(wrapper).
    2. Wrapper 是 final 类,并且是不可变的(即一单生成对象,对象本身不可变).
    3. Java 泛型不支持 基本类型,可以使用 wrapper.
    4. autoboxing & autounboxing 是编译器行为,而不是 JVM. 编译器通过添加相关代码得以实现(new Integer(), Integer.intValue()).
    5. String->Integer(int x = Integer.parseInt(s));

    下列情况不会发生 unboxing.

    Integer a = 1000;
    Integer b = 1000;
    if (a == b) ... // may equal, 自动打包要求 boolean、byte、char<=127, 介于-128~127的short和int被包装到固定的对象中.
    

    因为 Java 中基本类型是不可变的,所以要求 wrapper 也是不可变的.

    public static void triple(int x) { // won't work
        x = 3 * x;
    }
    
    public static void triple(Integer x) { // won't work
        x = 3 * x; // 因为 Integer 对象是不可变的.
    }
    

    Integer.parseInt 与 Integer.valueOf 的区别:

    public static int parseInt(java.lang.String s, int i) throws java.lang.NumberFormatException { /* compiled code */ }
    
        public static int parseInt(java.lang.String s) throws java.lang.NumberFormatException { /* compiled code */ }
    
        public static java.lang.Integer valueOf(java.lang.String s, int i) throws java.lang.NumberFormatException { /* compiled code */ }
    
        public static java.lang.Integer valueOf(java.lang.String s) throws java.lang.NumberFormatException { /* compiled code */ }
    

    继承设计的技巧

    1. 将公共操作和域放在超类.
    2. 不要使用受保护的域.
    3. 使用继承实现 "is-a" relation.
    4. 除非所有继承的方法都有意义,否则不要使用继承.
    5. 在override时不要改变预期的行为.
    6. 使用多态, 而非类型信息(避免类型判断),是代码更加易于扩展.
  • 相关阅读:
    【HDU 5750】Dertouzos(数学)
    【OpenJ_POJ C16D】Extracurricular Sports(构造,找规律)
    【CodeForces 266C】Below the Diagonal(模拟)
    【CodeForces 261B】Maxim and Restaurant(DP,期望)
    【ACdream 1187】Rational Number Tree(树,递归)
    A1231. Crash的数字表格(贾志鹏)
    [精准]圆周率
    poj1743 Musical Theme
    3343: 教主的魔法[分块]
    [HNOI2008]玩具装箱toy
  • 原文地址:https://www.cnblogs.com/nil2inf/p/4469213.html
Copyright © 2011-2022 走看看