zl程序教程

您现在的位置是:首页 >  Java

当前栏目

java-继承和多态

2023-03-07 09:15:51 时间

继承(inheritance) 继承满足“is-a”规则,即Manager is a Employee

/images/all/image-20221113161049769.png 如果子类的构造方法没有显式地调用超类的构造方法,则将自动地调用超类的无参构造,如果没有超类没有定义无参构造方法,编译报错。

this关键字的用途:

引用隐式参数 调用该类其他的构造方法 super关键字的用途:

调用超类的方法 调用超类的构造方法 调用其他构造方法的语句只能出现在构造方法中的第一行

import java.util.Date; import java.util.GregorianCalendar;

public class ManagerTest {

public static void main(String[] args) {
    Manager boss = new Manager("Carl Cracker", 80000, 1987, 12, 15);
    boss.setBonus(5000);

    Employee[] staff = new Employee[3];

    // 这里实际上使用了多态
    staff[0] = boss;
    staff[1] = new Employee("Harry Hacker", 50000, 1989, 10, 1);
    staff[2] = new Employee("Tommy Tester", 40000, 1990, 3, 15);


    for (Employee e : staff) {
        System.out.println("name=" + e.getName() + ",salary=" + e.getSalary());
    }
}

}

class Employee {

private String name;
private double salary;
private Date hireDay;

/**
 * @param n     the employee's name
 * @param s     the salary
 * @param year  the hire year
 * @param month the hire month
 * @param day   the hire day
 */
public Employee(String n, double s, int year, int month, int day) {
    name = n;
    salary = s;
    GregorianCalendar calendar = new GregorianCalendar(year, month - 1, day);
    hireDay = calendar.getTime();
}

public String getName() {
    return name;
}

public double getSalary() {
    return salary;
}

public Date getHireDay() {
    return hireDay;
}

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

}

class Manager extends Employee {

private double bonus;

/**
 * @param n     the manager's name
 * @param s     the salary
 * @param year  the hire year
 * @param month the hire month
 * @param day   the hire day
 */
public Manager(String n, double s, int year, int month, int day) {
    // 调用父类的构造方法必须出现在子类子类构造方法的第一行
    super(n, s, year, month, day);
    bonus = 0;
}

@Override
public double getSalary() {
    double baseSalary = super.getSalary();
    return baseSalary + bonus;
}

public void setBonus(double b) {
    bonus = b;
}

} 多态 “is-a”规则的另一种表述法是置换法则:程序中出现超类对象的任何地方都可以用子类对象置换

Java中的对象变量都是多态的,Employee变量既可以引用一个Employee对象,也可以引用Employee的任何一个子类(比如Manager)的对象

当把子类的对象赋给父类的变量时,就发生了向上造型

// Employee是声明类型 Employee[] staff = new Employee[3]; // 等号右边是动态类型(可能是Employee对象,也可能是它的子类对象) staff[0] = boss; staff[1] = new Employee("Harry Hacker", 50000, 1989, 10, 1); staff[2] = new Employee("Tommy Tester", 40000, 1990, 3, 15); 注意: 不能将一个超类的引用赋给子类变量(不是所有雇员都是经理)

函数调用的绑定 当通过对象变量调用函数的时候,调用哪个函数这件事情叫做绑定

静态绑定:根据变量的声明类型来决定

动态绑定:根据变量的动态类型来决定

java中默认绑定都是动态绑定

final类和final方法 如果想让一个类无法被继承,可以在class关键字前加上final关键字,这个类的所有方法也将自动加上final关键字

如果想让某个类的方法不能被重写,可以在方法名前加上final关键字

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

类型转换 有时候希望将超类转换为子类,这样就能调用子类的方法。但这一般是超类的设计问题。应该避免这种转换。

将超类转换为子类之前,应该使用instanceof进行检查, 避免出现类型转换异常(ClassCastException)

Employee[] staff = new Employee[3]; // staff[0]引用的是Manager对象, staff[1]和staff[2]引用的是Employee对象 staff[0] = boss; staff[1] = new Employee("Harry Hacker", 50000, 1989, 10, 1); staff[2] = new Employee("Tommy Tester", 40000, 1990, 3, 15);

// 假设要将Employee对象转换为Manager对象 Manager ma = (Manager) staff[0]; // Error, staff[1]引用的是Employee对象,会报ClassCastException异常 Manager mb = (Manager) staff[1]; // 正确做法: 转换前需要检查该对象是否属于转化后的对象类型 if (staff[1] instanceof Manager) {

Manager mb = (Manager) staff[1];

}

// 注意null不会抛出异常 System.out.println(null instanceof Manager); //输出false 抽象类 抽象类天然支持多态性,因为抽象类不能实例化,只能引用非抽象子类的对象

抽象类不一定包含抽象方法,有抽象方法一定要定义为抽象类 抽象类可以包含具体数据(比如name)和具体方法(比如getName) 抽象类也有构造方法 继承抽象类的子类必须实现所有的抽象方法 抽象类永远不能实例化,所有只能通过子类对象调用对应实现的抽象方法。 import java.util.Date; import java.util.GregorianCalendar;

public class PersonTest { public static void main(String[] args) {

  Person[] people = new Person[2];

  // fill the people array with Student and Employee objects
  people[0] = new Employee("Harry Hacker", 50000, 1989, 10, 1);
  people[1] = new Student("Maria Morris", "computer science");

  // print out names and descriptions of all Person objects
  for (Person p : people)
     System.out.println(p.getName() + ", " + p.getDescription());

} }

abstract class Person { private String name;

public Person(String n) {

  name = n;

}

public abstract String getDescription();

public String getName() {

  return name;

}

}

class Employee extends Person { public Employee(String n, double s, int year, int month, int day) {

  // 调用抽象类的构造方法
  super(n);
  salary = s;
  GregorianCalendar calendar = new GregorianCalendar(year, month - 1, day);
  hireDay = calendar.getTime();

}

public double getSalary() {

  return salary;

}

public Date getHireDay() {

  return hireDay;

}

public String getDescription() {

  return String.format("an employee with a salary of $%.2f", salary);

}

public void raiseSalary(double byPercent) {

  double raise = salary * byPercent / 100;
  salary += raise;

}

private double salary; private Date hireDay; }

class Student extends Person { /**

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

public Student(String n, String m) {

  // pass n to superclass constructor
  super(n);
  major = m;

}

public String getDescription() {

  return "a student majoring in " + major;

}

private String major; } Object类 所有class默认继承自Object

除基本类型之外,所有的对象数组和基本类型数组都继承了Object类

System.out.println(new Object() instanceof Object); // true System.out.println(new int[2] instanceof Object); // true System.out.println(new Person[2] instanceof Object); // true 重写object.equals

package EqualsTest;

import java.util.Date; import java.util.GregorianCalendar;

/**

  • This program demonstrates the equals method.
  • @version 1.11 2004-02-21
  • @author Cay Horstmann */

public class EqualsTest { public static void main(String[] args) {

  Employee alice1 = new Employee("Alice Adams", 75000, 1987, 12, 15);
  Employee alice2 = alice1;
  Employee alice3 = new Employee("Alice Adams", 75000, 1987, 12, 15);
  Employee bob = new Employee("Bob Brandson", 50000, 1989, 10, 1);

  System.out.println("alice1 == alice2: " + (alice1 == alice2));

  System.out.println("alice1 == alice3: " + (alice1 == alice3));

  System.out.println("alice1.equals(alice3): " + alice1.equals(alice3));

  System.out.println("alice1.equals(bob): " + alice1.equals(bob));

  System.out.println("bob.toString(): " + bob);

  Manager carl = new Manager("Carl Cracker", 80000, 1987, 12, 15);
  Manager boss = new Manager("Carl Cracker", 80000, 1987, 12, 15);
  boss.setBonus(5000);
  // System.out.println(对象),会自动调用该对象所属类的toString()方法
  System.out.println("boss.toString(): " + boss);
  System.out.println("carl.equals(boss): " + carl.equals(boss));
  System.out.println("alice1.hashCode(): " + alice1.hashCode());
  System.out.println("alice3.hashCode(): " + alice3.hashCode());
  System.out.println("bob.hashCode(): " + bob.hashCode());
  System.out.println("carl.hashCode(): " + carl.hashCode());

} }

class Employee { public Employee(String n, double s, int year, int month, int day) {

  name = n;
  salary = s;
  GregorianCalendar calendar = new GregorianCalendar(year, month - 1, day);
  hireDay = calendar.getTime();

}

public String getName() {

  return name;

}

public double getSalary() {

  return salary;

}

public Date getHireDay() {

  return hireDay;

}

public void raiseSalary(double byPercent) {

  double raise = salary * byPercent / 100;
  salary += raise;

}

@Override public boolean equals(Object otherObject) {

  // 1.检查是否引用自同一个对象(实际上就是检查两个对象的地址是否相同)
  // 注意我们的目的是判断两个对象的对应数据域是否相同,而不是只对比对象地址
  // 每个对象对应不同的hashCode,hashCode值即该对象的地址
  // 只比较相同对象的话意义不大
  if (this == otherObject) return true;

  // 2.比较的对象为null,返回false
  if (otherObject == null) return false;

  // 3.判断是否是同一个类的对象
  if (getClass() != otherObject.getClass()) return false;

  // 4.这里我们已经知道比较的同一个class的非空对象
  Employee other = (Employee) otherObject;

  // 5.检查它们的数据域是否相等
  // 注: 如果要检查两个数组是否相等,可以使用Arrays.equals()
  return name.equals(other.name) && salary == other.salary && hireDay.equals(other.hireDay);

}

// 如果重写了equals方法,必须重写hashCode方法 // 这样就能保证equals返回true时,两个对象的hashCode也一样 public int hashCode() {

  return 7 * name.hashCode() + 11 * new Double(salary).hashCode() + 13 * hireDay.hashCode();
  // 组合多个散列值时,可以使用下面这种方式
  //       return Objects.hash(name,salary,hireDay);

}

public String toString() {

  return getClass().getName() + "[name=" + name + ",salary=" + salary + ",hireDay=" + hireDay
        + "]"; 

}

private String name; private double salary; private Date hireDay; }

class Manager extends Employee { public Manager(String n, double s, int year, int month, int day) {

  super(n, s, year, month, day);
  bonus = 0;

}

public double getSalary() {

  double baseSalary = super.getSalary();
  return baseSalary + bonus;

}

public void setBonus(double b) {

  bonus = b;

}

public boolean equals(Object otherObject) {

  if (!super.equals(otherObject)) return false;
  Manager other = (Manager) otherObject;
  // super.equals checked that this and other belong to the same class
  return bonus == other.bonus;

}

public int hashCode() {

  return super.hashCode() + 17 * new Double(bonus).hashCode();

}

public String toString() {

  return super.toString() + "[bonus=" + bonus + "]";

}

private double bonus; } 自动装箱和自动拆箱 包装器类是不可变的。

如果在一个条件表达式中混合使用Integer和Double类型,Integer值就会拆箱,提升为double, 再装箱为Double

由于包装类引用可以为null,所以可能会抛出NullPointerException异常

Integer i1 = 1; Double d1 = 2.0; System.out.println(true ? i1 : d1); // 输出1.0

Integer i2 = null; // error, 会抛出NullPointerException异常 System.out.println(i2 * 3); 可变参数 找出数组中的最大值

public class Main {

public static void main(String[] args) {
    System.out.println(max(1,2,8.8,9.9)); // 9.9
}

public static double max(double... values) {
    // 负无穷
    double largest = Double.NEGATIVE_INFINITY;
    for (int i = 0; i < values.length; i++) {
        if (values[i] > largest) largest = values[i];
    }
    return largest;
}

} 枚举类 import java.util.Scanner;

public class EnumTest {

public static void main(String[] args) {
  Scanner in = new Scanner(System.in);
  System.out.print("Enter a size: (SMALL, MEDIUM, LARGE, EXTRA_LARGE) ");
  String input = in.next().toUpperCase();
  // 创建指定名字和类的枚举常量
  Size size = Enum.valueOf(Size.class, input);
  // toString()方法返回枚举常量名
  System.out.println("size=" + size);
  System.out.println("abbreviation=" + size.getAbbreviation());
  // 判断枚举常量时直接使用"=="
  if (size == Size.EXTRA_LARGE)
     System.out.println("Good job--you paid attention to the _.");
}

}

enum Size {

SMALL("S"), MEDIUM("M"), LARGE("L"), EXTRA_LARGE("XL");

private Size(String abbreviation) {
    this.abbreviation = abbreviation;
}

public String getAbbreviation() {
    return abbreviation;
}

private String abbreviation;

} 反射 获取class对象的三种方式:

使用Object类的getClass() 使用Class类的静态方法forName 直接使用.class jvm为每一种类型管理一个Class对象

// 第一种方式 Employee e = new Manager("Carl Cracker", 80000, 1987, 12, 1); System.out.println(e.getClass());

// 第二种方式(注意要捕获异常) // 动态修改要加载的类名 String className = "ManagerTest.Employee"; try {

System.out.println(Class.forName(className));

} catch (ClassNotFoundException ex) {

throw new RuntimeException(ex);

}

// 第三种方式 // Class类实际上是一种泛型类 Class employeeClass = Employee.class; // 一个Class 对象实际上表示的是一个类型, 而这个类型未必一定是一种类 System.out.println(int.class); // 输出int 利用反射创建一个有参构造的对象

注:new newInstance()使用的是无参构造方法

try {

Class<?> aClass = Class.forName("ManagerTest.Employee");
// 如果要使用有参构造方法,需要先获取一个构造方法
Constructor<?> c = aClass.getConstructor(String.class, double.class, int.class, int.class, int.class);
// 利用上面的构造方法创建对象
Employee e = (Employee)c.newInstance("Carl Cracker", 80000, 1987, 12, 15);
System.out.println(e.getName());

} catch (ClassNotFoundException | InvocationTargetException | NoSuchMethodException ex) {

throw new RuntimeException(ex);

} 通过反射机制获取类的成员变量,方法,构造方法等

package cc.bnblogs;

import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Arrays;

public class Main {

public static void main(String[] args) throws
        ClassNotFoundException, InstantiationException,
        IllegalAccessException, NoSuchFieldException {
    Class<?> aClass = Class.forName("cc.bnblogs.Point");
    Point p = (Point)aClass.newInstance();

    // 获取类的成员变量
    // 只能获取和修改public型的值
    Field x = aClass.getDeclaredField("x");
    Field y = aClass.getDeclaredField("y");
    Field str = aClass.getDeclaredField("color");
            
    // 要获取private变量的值,需要使用setAccessible(true)
    x.setAccessible(true);
    Object o = x.get(p);
      System.out.println(o);
    

    // 获取Point类的所有成员变量
    Field[] fields = aClass.getDeclaredFields();
    System.out.println(Arrays.toString(fields));

    // error,获取对象的私有成员变量
    // System.out.println(x.get(p));
    System.out.println(str.get(p));


    try {
        // 获取Point类的setColor方法
        Method mSetColor = aClass.getMethod("setColor", String.class);
        // 修改point的颜色
        mSetColor.invoke(p,"black");

        System.out.println(p.getColor());

        // 获取所有声明的方法
        Method[] methods = aClass.getDeclaredMethods();
        System.out.println(Arrays.toString(methods));
        
        // 使用Modifier类获取权限修饰符的名称
        System.out.println(Modifier.toString(aClass.getModifiers()));

    } catch (NoSuchMethodException | InvocationTargetException e) {
        throw new RuntimeException(e);
    }
}

}

class Point {

private int x;
private int y;

public String color;

Point() {

}

Point(int x,int y) {
    this.x = x;
    this.y = y;
}


public String getColor() {
    return color;
}

public void setColor(String color) {
    this.color = color;
}

public int getX() {
    return x;
}

public void setX(int x) {
    this.x = x;
}

public int getY() {
    return y;
}

public void setY(int y) {
    this.y = y;
}

} 反射机制实现访问父类的成员变量

Person e = new Employee("Harry Hacker", 50000, 1989, 10, 1); // 获取Employee的父类 Class<?> aClass = Class.forName("PersonTest.Employee").getSuperclass(); // 不能这样获取 // e.getClass(); 父类引用子类对象,e实际上还是Employee类 System.out.println(aClass); // 获取父类的成员变量name Field name = aClass.getDeclaredField("name"); name.setAccessible(true); Object o = name.get(e); System.out.println(o); 利用反射创建泛型数组

java.lang.reflect包中的Array类允许动态地创建数组。例如, 将这个特性应用到Array 类中的copyOf方法实现中,实现数组的动态拓展

import java.lang.reflect.Array; import java.util.Arrays;

public class CopyOfTest {

public static void main(String[] args) {
    int[] a = {1, 2, 3};
    a = (int[]) goodCopyOf(a,10);
    System.out.println(Arrays.toString(a));
    
    String[] b = {"Tom", "Dick", "Harry"};
    b = (String[]) goodCopyOf(b,10);
    System.out.println(Arrays.toString(b));

    System.out.println("The following call will generate an exception.");
    // Object不能强制转换为String
    // b = (String[]) badCopyOf(b,10);
}

public static Object[] badCopyOf(Object[] a, int newLength) {
    Object[] newArray = new Object[newLength];
    System.arraycopy(a,0,newArray,0,Math.min(a.length,newLength));
    return newArray;
}

public static Object goodCopyOf(Object a, int newLength) {
    Class cl = a.getClass();
    if (!cl.isArray()) return null;
    Class componentType = cl.getComponentType();
    int length = Array.getLength(a);
    Object newArray = Array.newInstance(componentType,newLength);
    System.arraycopy(a,0,newArray,0,Math.min(length,newLength));
    return newArray;
}

} 利用反射调用任意方法

调用静态方法,不需要获取类对象 调用非静态方法,需要获取类对象 反射调用私有静态方法(getDeclaredMethod,setAccessible) 反射调用私有非静态方法(getDeclaredMethod,setAccessible) package CopyofTest;

import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method;

public class CopyOfTest {

public static void main(String[] args) throws NoSuchMethodException, ClassNotFoundException, InvocationTargetException, InstantiationException, IllegalAccessException, NoSuchFieldException {
    Class<?> aClass = Class.forName("CopyofTest.Point");
    // 有参构造器
    Constructor<Point> constructor = Point.class.getConstructor(int.class, int.class);
    Point p = constructor.newInstance(1, 2);

    // 调用get和set方法
    Method getX = aClass.getMethod("getX");
    System.out.println(getX.invoke(p));

    Method setX = aClass.getMethod("setX",int.class);
    setX.invoke(p,10);
    System.out.println(getX.invoke(p));

    // 调用private非静态方法
    Method privateMethod = aClass.getDeclaredMethod("printStr");
    privateMethod.setAccessible(true);
    String str = (String) privateMethod.invoke(p);
    System.out.println(str);

    // 调用private静态方法
    Method privateStaticMethod = aClass.getDeclaredMethod("print");
    privateStaticMethod.setAccessible(true);

    // 调用静态函数, 第一个对象参数设置为null
    privateStaticMethod.invoke(null);
}

}

class Point {

private int x;
private int y;

public Point(int x,int y) {
    this.x = x;
    this.y = y;
}

public void setX(int x) {
    this.x = x;
}

public int getX() {
    return x;
}

public void setY(int y) {
    this.y = y;
}

public int getY() {
    return y;
}

private String printStr() {
    return "point";
}

private static void print() {
    System.out.println("print");
}

}