CoreJava 笔记总结-第五章 继承

第五章 继承

类、超类和子类

定义子类

  • extend: 该关键字表示继承
public class Manager extends Employee // java中所有的继承都是公共继承
{
    added methods and fields
}
  • extend: 表示正在构造的类派生于一个已存在的类.这个已存在的类称为超类(基类,父类). 新类称为子类(派生类, 孩子类)
  • 子类比超类拥有更多的功能
  • 设计类: 最一般的类放在超类中, 更特殊的方法放入子类

覆盖方法

  • super: 调用超类方法

    public class Manager extends Employee
    {
        public double getSalary(){
    		double baseSalary = super.getSalary();
            return baseSalary + bonus;
        }
    }
    

子类构造器

  • 由于Manager构造器不能访问Emplyee类的私有字段, 所以通过super来调用初始化这些私有字段

    public Manager(String name, double salary, int year, int month, int day)
    {
        super(name, salary, year, month, day);//必须放在第一句
        bonus = 0;
    }
    
  • this: 1. 隐式参数的引用 2. 调用该类的其他构造器

    super: 1. 调用其他超类的方法 2. 调用超类的构造器


多态

  • 多态: 一个变量能够指示多种实际类型的现象
Manager boss = new Manager(...)
Employee[] staff = new Employee[3];
staff[0] = boss;
  • 上面的例子中boss, staff[0]指示同一个对象, 但是编译器只将staff[0]看成一个Emplyee对象
  • 子类引用的数组可以转换成超类引用的数组
Manager[] managers = new Manager[10];
Employee[] staff = managers;
  • 重载解析: 选择所有名字相同的方法中存在一个与提供参数完全相匹配的方法
  • 签名: 方法的名字和参数列表. 返回类型不是签名的一部分
  • 在覆盖一个方法时,子类方法不能低于超类方法的可见性.e.g.: 超类为public, 子类也必须为public

阻止继承: final类和方法

  • final类: 不允许扩展的类, 其中的方法自动设置成为fianl方法, 不包括字段
  • final方法: 子类不能覆盖的方法
  • fianl字段: 构造对象后就不允许改变其值

强制类型转换

  • Manager boss = (Manager) staff[0];

  • Manager boss = (Manager) staff[1]; // ERROR 抛出异常: ClassCastException

  • if (staff[1] instanceof Manager){
        boss = (Manager) staff[1];
        ....
    }//查看能否强制类型转换成功
    
  • 继承层次: 由一个公共类派生出来所有类的集合

    1. 只能在继承层次类进行强制类型转换
    2. 在将超类强制类型转换为子类之前, 应该用instanceof进行检查

抽象类

  • 位于上层的类更具有一般性,更加抽象

  • 包含一个及以上抽象方法的类必须声明为抽象类

  • public abstract class Person
    {
        ...
        public abstract String getDescription();
    }
    
  • 抽象类也可以包含字段和具体方法

  • 抽象类不能实例化. new Person("dvsj"); //ERROR

  • 可以定义一个抽象类的对象变量, 引用非抽象子类的对象

Person p = new Student("Alice");
package chapter5_inheritance.abstruct_classes;

public abstract class Person {//抽象类
    public abstract String getDescription();//抽象方法

    private String name;

    public Person(String name) {
        this.name = name;
    }

    public String getName() {//抽象类中也可以由方法
        return name;
    }
}


package chapter5_inheritance.abstruct_classes;


public class Student extends Person {
    private String major;

    /**
     * @param name  the student's name
     * @param major the student's major
     */

    public Student(String name, String major) {
        super(name); // 继承Person
        this.major = major;//隐式参数的引用
    }

    public String getDescription() { // 对应抽象类中的抽象方法
        return "A student major in " + major;
    }
}

package chapter5_inheritance.abstruct_classes;

import java.time.LocalDate;

public class Employee extends Person
{

    private double salary;
    private LocalDate hireDay;

    public Employee(String name, double salary, int year, int month, int day) {
        super(name);
        this.salary = salary;
        hireDay = LocalDate.of(year, month, day);
    }

    public double getSalary() {
        return salary;
    }

    public LocalDate getHireDay() {
        return hireDay;
    }

    public void raiseSalary(double byPercent) {
        double delta = salary * byPercent / 100;
        salary += delta;
    }

    public String getDescription() {
        return String.format("An employee with a salary of %.2f", salary);
    }
}


package chapter5_inheritance.abstruct_classes;

public class Person_test {

    public static void main(String[] args) {
        /* 声明为Person类型 */
        var people = new Person[2];
        /* 每一个Person类型的people[i] 声明为对应子类类型 */
        people[0] = new Student("oukunnan", "Software Engineering");
        people[1] = new Employee("vfjh", 1500.0, 1259, 5, 12);
        /* 注意点 类型 参数: 实参数组 */
        for (Person p : people) {
            System.out.println(p.getName() + ", " + p.getDescription());
        }
    }
}


受保护访问

  • protected: 限制超类中某个方法只允许子类访问, 希望允许子类的方法访问超类的某个字段
  • 保护字段只能由同一个包中的类访问

访问控制修饰符小结

  1. private: 仅对本类可见
  2. public: 对外部完全可见
  3. protected: 对本包和所有子类可见
  4. 无: 对本包可见, 不需要修饰符

Object: 所有类的超类

Object类型的变量

  • 可以使用object类型的变量引用任何类型的对象
Object obj = new Employee("sbjd", 2000);
  • 只能作为各种值的一个泛型容器, 进行具体操作需要强制类型转换
Employee e = (Employee)obj;
  • 只有基本类型不是对象: 数值, 字符, 布尔类型的值

equals方法

  • 该方法确定两个对象的引用是否相等
public boolean equals(Object otherObject) {

    if (this == otherObject) {
        return true;
    }//判引用
    if (otherObject == null) {
        return false;
    }//判空
    if (getClass() != otherObject.getClass()) {
        return false;
    }//判对象所属类是否相等
    Employee other = (Employee) otherObject;
	//调用Object.equals()而不是name.equals(other.name)为了防备name, hireDay为null情况
    return Objects.equals(name, other.name) && salary == other.salary
        && Objects.equals(hireDay, other.hireDay);
}
  • 子类中: if(!super.equals(otherObject)) return false; ...

相等测试与继承

  • equals方法要求: 自反性, 对称性, 传递性, 一致性
  • 比较数组元素是否相等, 使用静态方法Arrays.equals
  • @Override标记要覆盖超类的那些子类方法

hashCode方法

  • 散列码: 值由对象的储存地址得出
  • 字符串的散列码时由内容导出的
  • Object.hasCode: null安全的方法
  • Object.hash():组合多个散列值, 这个方法对于各个参数调用Object.hasCode()
  • Arrays.hashCode:数组类型字段计算散列码
  • hasCode, equals方法必须兼容

toString方法

  • x.toString() 等价于 "" + x
  • System.out.println(x)会自动调用x.toString()
  • 对于数组: Arrays.toString, Arrays.deepToString

泛型数组列表

  • ArrayList类: 类似于数组, 在添加或者删除数组元素时可以自动调整数组容量
  • 它是一个有类型参数的泛型类

声明数组列表

  • 以下三种声明方式等价
ArrayList<Employee> staff = new ArrayList<Employee>();
ArrayList<Employee> staff = new ArrayList<>(); // 菱形语法
var staff = new ArrayList<Employee>();
  • add: 将元素添加到列表中
  • ensureCapacity: 确保数组列表在不重新分配内部储存数组的情况下有足够容量储存给定数量的元素
  • trimToSize: 将数组列表的存储容量消减到当前大小
  • size: staff.size() 等价于数组的a.length, 表示实际包含元素个数

访问数组列表元素

  • get: 得到指定索引位置的值
  • set: 重新设置索引位置已经存在的值
  • add(int index, E obj):后移元素从而插入obj到指定位置
  • remov(int index): 删除指定索引位置的元素, 后面的元素前移
  • toArray: 将数组元素拷贝到一个数组中
list.toArray(a); //将数组列表list中的元素拷贝到数组a中

类型化与原始数组列表的兼容性

  • public class EmployeeDB
    {
        public void update(ArrayList list){...}
        public ArrayList find(String query) {...}
    
  • 可以将类型化的数组列表传递给update, 不需要强制类型转换

  • 但是原始ArrayList赋给类型化ArrayList会有警告, 强制类型转换也不可以消除警告,会引发类型转换有误的另一个警告

  • @SuppressWarning("unchecked") ArrayList<Employee> result = (ArrayList<Employee>) emplyeeDB.find(x);
    
  • 用以上注解标记问题不太严重的强制类型转换的变量


对象包装器与自动装箱

  • 包装器: 所有的基本类型对应的类(Integer, Double, Long, Float, Short, Byte, Character, Boolean)

  • 包装器类是final, 不能派生子类

  • 尖括号中的类型参数不允许是基本类型, 可以这样var list = new ArrayList<Integer>();

  • 自动装箱:

    list.add(3) --> list.add(Integer.valueOf(3))
    
  • 自动拆箱:

int n = list.get(i); --> list.get(i).intValue();
  • Integer a = 1000;
    Integer b = 1000;
    if (a == b) ..//通常失败
    

    ==运算符比较的是对象是否占有相同的内存位置

  • -128~127之间的short, int被包装到固定的对象中, 上面的比较就会成功


参数数量可变的方法

  • ...: 是Java代码的一部分, 表示这个方法可以接受任意数量的对象

    public class PrintStream
    {
        public PrintStream printf(String fmt, Object... args){return format(fmt, args);}
    }
    
  • printf方法接受两个参数, 一个是格式化字符串, 另一个是Object[]数组, 其中保存着所有其他参数(整数或者其他基本类型的值会自动装箱为对象), Object...参数类型和Object[]完全一样


枚举类

  • public enum Size { SMALL, MEDIUM, LARGE, EXTRA_LARG};
  • 枚举构造器总是私有的
  • 所有枚举类型都是Enum的子类
  • values: 返回一个包含全部枚举值的数组

反射

  • 反射: 能够分析类能力的程序

Class

  • getClass: 返回类的名字
  • forName: 获得类名对应的Class对象
  • T.class: 代表匹配的类对象
  • Class实际上是泛型类, Employee.class-->Class<Employee>

声明异常入门

  • 处理器处理抛出的异常
  • 异常分为
    1. 非检查型异常
    2. 检查型异常

利用反射分析类的能力

  • java.lang.reflect包中有三个类Field, Method, Constructor分别用于描述类的字段,方法和构造器, 这三个类都有一个gteName方法, 返回其名称
  • Class类中的getFields, getMethods, getConstructors 方法返回这个类支持的公共字段,方法和构造器的数组, 其中包括超类的公共成员
  • Class类中的getDeclaredFields, getDeclareMethods, getDeclaredConstructors 方法返回这个类声明的全部字段,方法和构造器,包括私有成员,包成员,受保护成员,不包括超类成员
package chapter5_inheritance;

import java.util.*;
import java.lang.reflect.*;

public class reflection {

    public static void main(String[] args)
            throws ReflectiveOperationException
    {
        // read class name from command line args or user input
        String name;
        if (args.length > 0) name = args[0];
        else
        {
            var in = new Scanner(System.in);
            System.out.println("Enter class name (e.g. java.util.Date): ");
            name = in.next();
        }

        // print class name and superclass name (if != Object)
        Class cl = Class.forName(name);
        Class supercl = cl.getSuperclass();
        String modifiers = Modifier.toString(cl.getModifiers());
        if (modifiers.length() > 0) System.out.print(modifiers + " ");
        System.out.print("class " + name);
        if (supercl != null && supercl != Object.class) System.out.print(" extends "
                + supercl.getName());

        System.out.print("\n{\n");
        printConstructors(cl);
        System.out.println();
        printMethods(cl);
        System.out.println();
        printFields(cl);
        System.out.println("}");
    }

    /**
     * Prints all constructors of a class
     * @param cl a class
     */
    public static void printConstructors(Class cl)
    {
        Constructor[] constructors = cl.getDeclaredConstructors();

        for (Constructor c : constructors)
        {
            String name = c.getName();
            System.out.print("   ");
            String modifiers = Modifier.toString(c.getModifiers());
            if (modifiers.length() > 0) System.out.print(modifiers + " ");
            System.out.print(name + "(");

            // print parameter types
            Class[] paramTypes = c.getParameterTypes();
            for (int j = 0; j < paramTypes.length; j++)
            {
                if (j > 0) System.out.print(", ");
                System.out.print(paramTypes[j].getName());
            }
            System.out.println(");");
        }
    }

    /**
     * Prints all methods of a class
     * @param cl a class
     */
    public static void printMethods(Class cl)
    {
        Method[] methods = cl.getDeclaredMethods();

        for (Method m : methods)
        {
            Class retType = m.getReturnType();
            String name = m.getName();

            System.out.print("   ");
            // print modifiers, return type and method name
            String modifiers = Modifier.toString(m.getModifiers());
            if (modifiers.length() > 0) System.out.print(modifiers + " ");
            System.out.print(retType.getName() + " " + name + "(");

            // print parameter types
            Class[] paramTypes = m.getParameterTypes();
            for (int j = 0; j < paramTypes.length; j++)
            {
                if (j > 0) System.out.print(", ");
                System.out.print(paramTypes[j].getName());
            }
            System.out.println(");");
        }
    }

    /**
     * Prints all fields of a class
     * @param cl a class
     */
    public static void printFields(Class cl)
    {
        Field[] fields = cl.getDeclaredFields();

        for (Field f : fields)
        {
            Class type = f.getType();
            String name = f.getName();
            System.out.print("   ");
            String modifiers = Modifier.toString(f.getModifiers());
            if (modifiers.length() > 0) System.out.print(modifiers + " ");
            System.out.println(type.getName() + " " + name + ";");
        }
    }
}

/*
Enter class name (e.g. java.util.Date): 
java.lang.Double
public final class java.lang.Double extends java.lang.Number
{
   public java.lang.Double(double);
   public java.lang.Double(java.lang.String);

   public boolean equals(java.lang.Object);
   public static java.lang.String toString(double);
   public java.lang.String toString();
   public int hashCode();
   public static int hashCode(double);
   public static double min(double, double);
   public static double max(double, double);
   public static native long doubleToRawLongBits(double);
   public static long doubleToLongBits(double);
   public static native double longBitsToDouble(long);
   public volatile int compareTo(java.lang.Object);
   public int compareTo(java.lang.Double);
   public byte byteValue();
   public short shortValue();
   public int intValue();
   public long longValue();
   public float floatValue();
   public double doubleValue();
   public static java.lang.Double valueOf(java.lang.String);
   public static java.lang.Double valueOf(double);
   public static java.lang.String toHexString(double);
   public static int compare(double, double);
   public static boolean isNaN(double);
   public boolean isNaN();
   public static boolean isInfinite(double);
   public boolean isInfinite();
   public static boolean isFinite(double);
   public static double sum(double, double);
   public static double parseDouble(java.lang.String);

   public static final double POSITIVE_INFINITY;
   public static final double NEGATIVE_INFINITY;
   public static final double NaN;
   public static final double MAX_VALUE;
   public static final double MIN_NORMAL;
   public static final double MIN_VALUE;
   public static final int MAX_EXPONENT;
   public static final int MIN_EXPONENT;
   public static final int SIZE;
   public static final int BYTES;
   public static final java.lang.Class TYPE;
   private final double value;
   private static final long serialVersionUID;
}
*/

继承的设计技巧

  1. 将公共操作和字段放在超类中
  2. 不要使用受保护的字段
  3. 使用继承实现"is-a"关系
  4. 除非所有继承方法都有意义, 否则不要使用继承
  5. 在覆盖方法时,不要改变预期的行为
  6. 使用多态, 而不要使用类型信息
  7. 不要滥用反射

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

风起风里

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值