5.1 类,父类与子类
5.1.1 子类定义与方法覆盖
1.子类可以继承父类成员变量与方法,通过extends 关键字。
class subClass extends parentClass{...}
2.子类可以通过定义相同签名的方法来覆盖父类方法(无需关键字)。
3.子类不可访问父类私有方法及成员变量,可通过super.XXX来访问父类非私有成员变量与方法。
4.父类被覆盖的方法也可用super.XXX在子类中调用。
5.子类中可以增加成员变量,方法和覆盖方法,但不可以删除父类的任何变量与方法。
6.子类覆盖父类方法时,可见性不能低于父类方法。如父类是public,子类必须为public。
7.每个类只可有一个父类,但一个类可以用无限多个子类。
5.1.2 子类构造器
子类构造器的第一条语句必须使用super(….)调用父类构造器。
例:
public Manager(String Name,double Salary,int year,int month,int day)
{
super(name,salary,year,month,day);
bounus = 0;
}
如果子类构造器没有调用父类的构造器,则将自动调用父类的无参构造器。若父类无无参构造器,则会报错。
5.1.3 多态
多态:一个对象,两种形态(或多种形态)。
对象的多态:每一个子类对象首先也是一个父类对象(is-a规则)。
因为子类是从父类派生而来,它保留了父类的所有特征,并加以丰富和修改。如每一个dog也是一个animal,它首先是一个动物,然后才是狗。
可以将一个子类对象赋给父类变量,但该变量不可以使用子类中特有的方法,只能调用父类原有的方法,如果子类覆盖了父类的方法,那么该变量会调用子类覆盖后的方法。
例:
class Employee{
private String name;
private double salary;
protected int age;
private LocalDate hireDay;
public Employee(String n,double s,int year,int month,int day){//父类构造器
name = n;
salary = s;
hireDay = LocalDate.of(year,month,day);
}
public String getName(){
return this.name;
}
public double getSalary(){
return this.salary;
}
public LocalDate getHireDay(){
return this.hireDay;
}
public void raiseSalary(double byPercent){//按比例加薪
double raise = this.salary*byPercent/100;
this.salary += raise;
}
}
class Manager extends Employee
{
private double bonus;
public Manager(String n, double s, int year, int month, int day,int bonus) {
super(n, s, year, month, day);//调用父类构造器
this.bonus = bonus;
}
public double getSalary()//覆盖父类方法
{
return this.bonus+super.getSalary();//调用父类方法
}
public double getBonus(){//子类特有方法
return this.bonus;
}
public void setBonus(double b)
{
this.bonus = b;
}
}
Employee e = new Manager(“Bob”,1000,2002,9,1,500);//将子类对象赋给父类变量
e.getBounus();//error,父类变量无法调用子类特有方法。
double s = e.getSalary();//e = 1500.调用子类覆盖了的getSalary方法
5.1.4 方法调用
java编译器确定调用的是哪个方法的步骤:
(1)先将类中同名的,声明类型符合的方法罗列出来。
(2)查看这些方法中有没有参数类型匹配上的。
注意:子类覆盖父类方法时,可改变其返回值。因为返回值不属于方法签名。但是不可以改变父类方法的参数,因为参数属于方法签名,如果改变了参数,就相当于定义了一个子类特有的新方法,不再是覆盖了。
动态绑定:只有运行时才知道是哪类对象调用了方法。
例:
Employee e = new Manager(“Bob”,1000,2002,9,1,500);//将子类对象赋给父类变量
e.getSalary();//动态绑定,只有运行时才知道e调用的是子类的覆盖方法
静态绑定:调用private、static、final方法或者类构造器,这类方法能在编译时就确定是哪类对象调用的。
5.1.5 阻止继承:final类和方法
不可以被继承的类为final类。
例:
public final class XXX{...}
非final类中的特定方法也可被声明为 final,这样子类就不能覆盖该方法(final类中所有方法自动成为final方法)。
成员变量也可以为final,被赋值后不可修改(成员变量的final和方法的final意义截然不同)。
将类声明为final,只有其中的方法自动变为final,而不包括成员变量。
5.1.6 强制类型转换
将某个类的对象转换成另一个类的引用。
进行类型转换的唯一原因:在暂时忽略对象的实际类型之后,使用对象的全部功能。
比如将子类对象赋值给父类变量时,该变量无法调用子类的特有方法,所以要将其强制转换为子类变量。
只有父类变量转换为子类变量时,才需要强制类型转换,将转换类型用括号括起来。
在进行类型转换之前,需要用instanof判断能否成功转换。
如:
Employee e = new Employee(......);
if(e instanceof Manager)
{
Manaer boss = (Manager e);//这种情况是不能成功转换的,如果不用instanceof检测会抛出异常
}
综上:
1.只能在继承层次进行类型转换(从父转子需强制转换)
2.将父类转换为子类之前,应该用instanceof进行检查。
3.尽量少使用强制转换和instanceof。
5.1.7 抽象类
当一个方法不需要在当前类中实现,只需要在子类中实现时,可用abstract关键字。
如定义一个Person类,该类用来描述一个人的基本信息。现在要在该类中加入一个getDescription方法用来输出对一个人的一段描述。
那么在Person类中,该方法不容易实现,因为只是对一个人的描述不可能做到非常具体,但是在其子类Student和Emoloyee中,该方法很好具体实现。
抽象类:包含一个或多个抽象方法的类必须被声明为抽象的abstract。
public abstract class Person
{
...
public abstact String getDescription();
...
}
除抽象方法外,抽象类还可包含具体数据和具体方法。
如果子类不定义父类抽象方法或只定义了部分抽象方法,那必须将子类也标记为抽象类。
如果子类定义了全部抽象方法,那子类也就不是抽象类了。除非又定义了新的抽象方法。
类即使不含抽象方法,也可以将类声明为抽象类。
抽象类不可被实例化。
可定义一个抽象类对象变量,它只能引用非抽象的子类的对象。但是和之前在多态中说过的一样,不能调用子类的特有方法。
5.1.8 控制可见性的4个访问修饰符
(1)仅对本类可见—–private.
(2)对所有类可见—–public.
(3)对本包和所有子类可见—–protected.
(4)对本包可见——–默认,无需修饰符.