构建子类

子类和超类

我们现在先来搞清两概念:子类超类。先来看以下Java语句。

public class Manager extends Employee
{
}

在该语句中,extends起到了继承的作用,它表明正在构造的新类派生于一个已经存在的类,Manager继承了Employee。Manager称为子类(派生类、孩子类),而Employee称为超类(基类、父类)。“超”和“子”这两个字来源于计算机科学和数学理论中集合语言的术语,Employee集合包含了Manager集合,是Manager的超集,而Manager集合属于Employee集合,是Employee集合的子集。

覆盖方法

有时我们会遇到超类中的某些方法不适用于子类,需要编写新的方法的情况,如下例所示。

public class Employee  //Employee类的相关定义
{
  private double salary;
  ...
  public double getSalary()
  {
    return salary
  }
}

我们的子类多了一个存储奖金的域,以及设置这个域的方法。

public class Manager extends Employee
{
  private double bonus;
  public void setBonus(double bonus)
  {
    this.bonus = bonus;
  }
}

当我们想获取Manager的工资时,与Employee不同,我们需要把工资和奖金的加和一起返回,这时,Employee类中的getSalary()方法就无法实现此功能。需要在子类中编写如下方法。

public double getSalary()
{
  double salary = super.getSalary();//super.getSalary()是调用超类中的方法,
  return salary+bonus;//如果不调用超类中的方法,是无法直接访问超类中的实例域的
}

在子类中可以增加域、增加方法以及覆盖超类中的方法,但是绝不能删除继承的域和方法。

子类构造器

Manager类的构造器如下所示

public Manager(String Name,double salary,int year,int month,int day)
{
  super(name,salary,year,month,day);//调用超类Employee中含有name等参数的构造器
  bonus=0;
}

这里用super的原因与覆盖方法中的原因相同,子类的构造器无法访问超类的私有域,因此需要用Employee超类中的构造器对超类中的私有域进行初始化。
如果子类的构造器没有显式的调用超类中的构造器,则子类的构造器会调用超类中没有参数的构造器,如果超类中没有无参数的构造器,则子类的初始化会报错。
super关键字的两个作用是:调用超类的方法以及调用超类的构造器
一个对象可以指示多个实际类型的现象称为多态,在运行时能够自动地选择哪种方法的现象称为动态绑定。

继承层次

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

多态

在JAVA程序设计语言中,对象变量是多态的。一个Employee变量既可以引用一个Employee类对象,也可以引用Employee任意子类的对象。如下例所示。

Manager boss = new Manager(...);
Employee[] staff = new Employee[3];
staff[0] = boss;
boss.setBonus(5000);//OK
staff[0].setBonus(5000);//error! staff[]属于Employee类

在这个例子中,变量staff[0]和boss引用同一个对象,但是编译器将staff[0]看作是Employee对象。

方法调用过程

  • 编译器查看对象的声明类型和方法名(编译器将会一一列举该类中
    所有名为f的方法和其超类中访问属性为public且名为f的方法)
  • 编译器查看调用方法时提供的参数类型(找到参数类型匹配的方法)
  • 如果是private方法、static方法、final方法或者构造器,那么编译器将可以准确地知道应该调用哪个方法,该种调用方式称为静态绑定
  • 当采用动态绑定的方法时,会寻找最合适的方法,现在子类中查找,再去超类中查找。

final关键字

final关键字可以用在变量、方法和类上。如下图所示

java 子类 父类new java父类和子类的例子_构造器


final变量与普通变量的区别是,普通变量在被初始化之后还能被更改,而final变量一旦被初始化后就无法再更改变量值,也就是成为了一个常量。但需要注意的是,如果final变量是对对象的引用,那么我们虽然不能改变final变量引用对象,但是我们可以更改被引用变量的内部状态,如下例所示

class Example 
{ 
    public static void main(String[] args)  
    { 
        // a final reference variable string 
        final StringBuilder astring = new StringBuilder("Hello world"); 
          
        System.out.println(astring); //输出Hello world
          
        // changing internal state of object 
        // reference by final reference variable astring 
        astring.append("for everyone"); 
          
        System.out.println(astring); //输出Hello worldfor everyone
    }     
}

当使用final关键字声明方法时,该方法无法被覆盖。

class A 
{
    final void m1() 
    {
        System.out.println("This is a final method.");
    }
}

class B extends A 
{
    void m1()
    { 
        // COMPILE-ERROR! Can't override.
        System.out.println("Illegal!");
    }
}

当使用final关键字声明类时,该类无法被继承。

final class A
{
     // methods and fields
}
// The following class is illegal.
class B extends A 
{ 
    // COMPILE-ERROR! Can't subclass A
}

受保护访问

使用protected方法可以允许类访问超类中的某些域或者方法,例如,将Employee类中的Salary变量声明为protected,则子类Manager中的方法可以直接访问该变量,不过它只能访问Manager类中的Salary变量,而不能访问Employee类中的变量。

泛型数组列表

ArrayList是一个采用类型参数的泛型类,为了指定数组列表保存的元素对象类型,需要用一对尖括号将类名括起来加在后面,该类使用起来像数组,但在添加或者删除元素时,具有自动调节容量大小的功能。

构造一个保存Employee对象的数组列表:

ArrayList<Employee> staff = new ArrayList<Employee>();//不指定初始容量
ArrayList<Employee> staff = new ArrayList<Employee>(100);//显式指定初始容量为100

将元素插入数组列表:

staff.add(new Employee(...));

返回数组的实际大小:

staff.size();

一旦能够确认数组列表的大小不再发生变化,就可以调用trimToSize方法。这个方法将存储区域的大小调整为当前元素数量所需要的存储空间数目。垃圾回收器将回收多余的存储空间

staff.trimToSize();

更改数组中的第i个元素:

staff.set(i,harry);

获取数组中第i个位置的元素:

staff.get(i);

在数组第n个位置上插入元素

staff.add(n,harry);

删除数组上的第n个位置上的元素:

staff.remove(n);

对象包装器与自动装箱

在Java中,所有的基本类型都有与之对应的类,如integer类对应的基本类型是int,通常,这些类称为包装器。

由int类型转为integer的过程称之为自动装箱(autoboxing),而由integer类转为int类型的过程称之为自动拆箱。

枚举类

以一个例子说明

public class EnumTest
{
  public class void main(String[] args)
  {
    Scanner in = new Scanner(System.in);
    System.out.println("Enter a size:(SMALL,MEDIUM,LARGE,EXTRA_LARGE)")
    String input = in.next().toUpperCase();  //获取输入
    Size size = Enum.valueOf(Size.class,input);     //获取输入的枚举类型
    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;
}