前言:
前面记录了学习完Java核心卷,类与对象这一章后的一些体会和理解,接下来我会接着记录学习继承这一章节的心得。不足之处请多多指教。
1.类、超类和子类
前一个笔记中的Employee类,可以得出一个职工的薪水,但在现实生活中我们会发现一个公司中,并不只有普通员工还有经理等等一些特殊雇员。他们之间虽然存在差异,但他们都领薪水,不同的是,普通员工完成任务后可领取薪水,但经理完成任务后不仅可以领取薪水还可以领取奖金。
这时候就可以利用继承解决这一问题,通过extends关键字为经理定义一个继承于Employee类的新类Manager,Manager可以重用Employee类中编写的部分代码,并将其所有域保留下来。
格式:
class Manager extends Employee
{
添加方法和域
}
extends表明构造的新类派生于一个已存在的类,通常将已存在的类称为父类,派生的类称为子类。一般子类的功能多于父类的功能,就像刚刚的 Manager类它所封装的信息就多余Employee类封装的数据。
package Exemple;
public class Manager extends Employee{
private double bonus; //奖金
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;
}
}
通过代码,我们可以看到Manager类中并没有显式定义getName和getHireDay方法,也并没有定义name、salary等域,但是Manager的对象却可以使用它们,这是因为Manager自动继承了父类中的方法和域。也就是每个Manager的对象都包含了name、salary、hireDay和bonus这四个域。
在代码中我们使用了super,原因是我们想要使用是父类中的getSalary方法,super与this不同它不是一个对象的引用,不能将super赋给另一个对象变量,它只是一个指示编译器调用父类方法的特有关键字。
在构造器中:
public Manager(String n, double s, int year, int month, int day)
{
super(n,s,year,month,day)
bonus = b;
}
这里也使用了super,但这里的含义确实,调用Employee类中含有:n,s,year,month,day参数的构造器。
super关键字的用途:
1.调用父类的方法。2.调用父类的构造器。在调用构造器时super和this类似。调用构造器的语句只能作为另一个构造器的第一条语句出现。构造器参数既能传递给本类(this)的其它构造器,也可以传递父类(super)的构造器。完整代码:
package Exemple;
public class EmployeeTest {
public static void main(String[] args) {
Manager boss = new Manager("Tom",8000,1977,12,15);
boss.setBonus(5000);
Employee[] staff = new Employee[3];
staff[0] = boss;
staff[1] = new Employee("Carl Cracker1",65000,1989,10,11);
staff[2] = new Employee("Carl Cracker2",78000,1977,2,15);
for(Employee e:staff)
e.raiseSalary(5);
for(Employee e:staff)
System.out.println("name=" + e.getName() + ",salary=" + e.getSalary() + ",hirDay=" + e.getHireDay());
}
}
package Exemple;
public class Manager extends Employee{
private double bonus;
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;
}
}
package Exemple;
import java.util.*;
public class Employee {
private String name;
private double salary;
private Date hireDay;
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;
// }
public void raiseSalary(double byPercent)
{
double raise = this.salary * byPercent /100;
this.salary += raise;
}
}
1.1 继承层次
有一个公共超类派生出的所有类的集合被称为继承层次,在继承层次中,从某个特定的类到其祖先的路径被称为该类的继承链。通常一个祖先类可以有多个子孙继承链。
1.2 多态
在Java中,对像变量是多态的。一个Employee变量既可以引用一个Emlopyee类对象,也可以引用一个Employee类的任何一个子类的对象。以上例为例;
boss.setBonus(5000);
Employee[] staff = new Employee[3];
staff[0] = boss;
这里staff[0]和boss引用同一个对象,但是编译器将staff[0]视为Employee对象。所以可以这样调用
:boss.setBouns(5000);但是不可以这样:staff[0].setBouns(5000);因为staff[0]声明的类型是Employee,而setBouns()不是Employee的方法。
1.3 阻止继承
在定义类的时候使用final关键字时就表明这个类不允许被扩展。类中方法也可以声明为final,这样子类就不能覆盖这个方法(final类中的所有方法自动的成为final方法)。
1.4 抽象类
若自下而上仰视类的继承层次结构,会发现位于上层的类更加具有通用性,甚至更抽象。从某种角度看,祖先类更加通用,人们只将它视为派生其他类的基类。例如:我们对Empolee类层次进行拓展。一位雇员是一个人,一个学生也是一个人,我们将Person和Student类添加到类的层次结构。
每个人都有一些诸如姓名这样的属性。学生与雇员都有姓名属性,因此可以将getName()方法放置在位于继承层次关系较高的通用类中。例如现在要在上例中增加一个getDescriltion方法,它返回对一个人的简短描述。在Empolee和Student类中这个方法很容易实现,但在Person中应该提供什么内容,除了姓名Person一无所知。我们也可以让Person.getDescription()返回一个空字符串,但是更好的方法是使用abstract关键字,这样就可以不实现这个方法。
抽象方法充当着占位的作用,它们的具体实现在子类中。扩展抽象类有两种选择:
1.在子类中定义部分抽象方法或抽象方法也不定义,这样必须将子类也标记为抽象类
2.定义全部抽象方法,这样子类就不是抽象的了
package Exemple;
abstract class Person {
private String name;
public Person(String n)
{
name = n;
}
public abstract String getDescription();
public String getName()
{
return name;
}
}
package Exemple;
class Student extends Person{
private String major;
public Student(String n, String m)
{
super(n);
major = m;
}
public String getDescription()
{
return "a student majoring in " + major;
}
}
package Exemple;
import java.util.*;
class Employee extends Person{
private double salary;
private Date hireDay;
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;
}
@Override
public String getDescription() {
return String.format("an employee with a salary of $%.2f ",salary);
}
public void raiseSalary(double byPercent)
{
double raise = this.salary * byPercent /100;
this.salary += raise;
}
}
package Exemple;
import java.util.*;
public class PersonTest {
public static void main(String[] args) {
Person[] people = new Person[2];
people[0] = new Employee("Tom",5000,1988,10,1);
people[1] = new Student("Maria","computer science");
for(Person p: people)
{
System.out.println(p.getName() + ", " + p.getDescription());
}
}
}
2. 反射
能分析类能力的程序被称为反射,反射机制功能很强大,包括;1.在运行中分析类的能力,2.在运行中查看对象,例如:编写一个toString方法共所有类使用,3.实现数组的操作代码,4.利用Method对象。
2.1 Class类
程序运行期间,系统会为所有对象维护一个被称为运行时的类型标识。这个信息保存着每个对象所属的类足迹。虚拟机利用运行时信息选择相应的方法执行。
这里介绍个常用的Class类的方法,getName。这个方法返回类的名字,例如;System.out.println(e.getClass().getName()+" " +e.getName());若e是雇员就会输出:Exemple.Employee marry。其中Exemple是包名。
package Exemple;
import java.util.*;
public class PersonTest {
public static void main(String[] args) {
Person[] people = new Person[2];
people[0] = new Employee("Tom",5000,1988,10,1);
people[1] = new Student("Maria","computer science");
Employee e = new Employee("marry",4000,1988,12,1);//新建e对象
for(Person p: people)
{
System.out.println(p.getName() + ", " + p.getDescription());
}
System.out.println(e.getClass().getName() + " " +e.getName());//输出对应类名字
}
}