超类与子类
1.概念
Java用关键字extends表明正在构造的新类派生于一个已经存在的类。已存在的类称为超类(superclass),基类(base class)或父类(parent class);新类称为子类(subclass),派生类(derived)或孩子类(child class)。Java程序员一般使用术语超类和子类。
超类相比于子类并没有优于子类或拥有比子类更多的功能。相反,子类可能封装了更多的数据或功能。
一个例子:
class Employee { private int id; private String name; private double salary; public Employee(String name,double salary){ ... } public String getName() public double getSalary() { return salary; } public int getId()
这样一个公司员工的类。属性有id,名字,薪水。
公司有经理,经理的待遇与普通员工有一些差异,不过差异不大。比如,经理在干完活后可以领取奖金,普通员工只能领取薪水,其他都一样。此时,面对这样一点小小的差异,我不愿意从头到尾编写一个新的类,那么就要用到继承。
为经理定义一个新类Manager,增加一点新功能,然后重用Emploee的部分代码即可,将其所有域保留下来。两者之间存在一种明显的关系,is-a关系。即每名经理也是一个员工。
每个子类也是一个超类。is-a关系是继承的一个明显特征。
增加奖金域:
public class Manager extends Emploee{ private double bonus; .... public double setBonus(double bonus){ this.bonus = bonus; } .... }
这样,子类就可以使用超类一些方法,因为Manager类自动继承了Emploee中的这些方法。但Emploee却不能使用setBonus()方法。
2.覆盖方法
事实上,子类继承自父类的方法,有些不太适用于子类,例如,父类的getSalary()方法,返回薪水。经理的薪水是怎样的呢?显然,是原来的薪水加奖金。
使用下面的方法来覆盖父类的getSalary()方法:
public double getsalary(){ double baseSalary = super.getSalary(); return baseSalary + bonus; }
这里用到了super关键字。直接return salary+bonus;这样的语句是不行的。
原因:Manager的getSalary()方法不能直接访问超类的私有域。只有Emploee类的方法才能访问。
因此,借助super调用超类的方法,可帮助我们访问超类中的域。
3.子类构造器
同样需要借助super构造器。因为需要访问超类私有域。
public Manager(String name,double salary,int id){ super(name,salary,id); bonus = 0; }
这样简写,当超类构造器有对应的参数的构造方法时,即会调用那个构造函数。若无,会产生错误。
如果子类没有显示的使用super调用超类的构造器,则自动调用超类的默认(无参数)构造器。
4.多态
现有一经理,两员工,输出薪水。
Employee[] staff = new Employee[3]; Manage boss = new Manager(); staff[0] = boss; staff[1] = new Employee(); staff[2] = new Employee(); for (Employee e:staff){ system.out.println(e.getSalary()); }
这段代码能够正确的输出薪水。jvm在运行时能够自动的选择调用正确的方法,引用超类时调用超类方法,引用子类时调用子类方法。虚拟机知道e实际引用的对象类型,正确调用方法。
一个对象变量能够指示不同多种实际类型的现象称为多态。
在运行时能够自动选择调用哪个方法的现象称为动态绑定。
如上例子,staff[0]赋值给e,即将一个子类的对象赋值给超类,e即引用了两种实际类型。这里,编译器将staff[0]看做Emploee对象,因而不能这样调用:staff[0].setBonus(5000);可以这样调用:boss.setBonus(5000);
不能将超类的引用赋值给子类变量,原因是显而易见的,相反的is-a关系。
5.阻止继承
有时候,希望阻止某个类定义子类。不允许拓展的类称为final类。如果在定义类时使用了而final修饰符,就表明这个这个类是final类。
public final class Executive extends Manager{ ... }
类中的方法也可以定义为final方法,这样做子类就不能覆盖该方法。
public final String getName()
6.抽象类和抽象方法
有时候,希望父类更加通用,派生出多种多样的子类,并且只将父类作为派生其他类的基类,而不作为想使用的特定的实例类。
这时,即可使用抽象类。
抽象类中可以有抽象方法,抽象方法只有声明而没有实现。如:
public abstract class Person{ ... public abstract String getDescription(); }
包含一个或多个抽象方法的类必须声明为抽象类。
抽象类的实现在子类中,有以下两种方式:
1)在抽象类中定义部分抽象方法或不定义抽象方法,那么就必须将子类声明为抽象类。
2)在抽象类中定义全部抽象方法,子类就不是抽象的了。
抽象类不能被实例化。
可以定义一个抽象类的对象,但它只能引用非抽象类子类的对象。
Person p = new Student();
p引用的是子类Student的对象。
7.可见性控制
1)仅对本类可见---private
2)对所有类可见---public
3)对本包和所有子类可见---protected
4)对本包可见---默认
Object:所有类的超类
Object类是java中所有对象的超类,每个类都继承自Object。
以下是Object类的一些方法
1.equals方法
作用:检测一个对象是否等于另一个对象。实际上是判断两个对象是否具有相同的引用。
有些情况下,需要重写该方法,即有时检测而两个对象状态的相等性而不是引用。
覆盖equal方法的一种实现
1)显示参数命名为otherObject
2)检测this是否与otherObject引用同一对象,是返回true,否继续
if(this==otherObject) return true;
3)检测otherObject是否为null,是返回false,否继续
if(otherObject==null) return false;
4)比较this与otherObject是否为同一个类,(如果equals语义在每个子类中有所改变,使用getClass()方法;一致则使用instanceof),不是返回false,是继续
if(getClass()!=otherObject.getClass()) return false;
if(!(otherObject instance ClassName)) return false;
这里是考虑由超类决定相等概念还是由子类决定相等概念。
5)将otherObject转换成相应的类类型变量,比较所有域。
ClassName other = (ClassName) otherObject
.....
2.hashcode方法
hashcode是由对象导出的一个整形值。散列码是没有规律的。如果x和y是两个不同的对象,那么它们的散列值基本不会相同。
相同的字符串拥有相同的散列码。例如:String s ="OK",String t =new String("OK"), s.hashcode()和t.hashcode()返回结果都是2524。这是因为字符串散列码由内容导出。
如果重写quals方法,那么就需要重写hashcode方法,以便将对象插入到散列表中。
3.toString方法
作用:返回表示对象值的字符串
一般用于获取对象状态