继承所在的类时反复这些类的方法和域,可以添加一些新的方法和域,以满足新的需求
反射
式指在程序运行期间发现更多的类及其属性的能能力
类,超类和子类
- 继承关键字
extends
public class Manager extends Employee {
方法域
}
- 关键字extends表明正在构造的新类派生于一个已存在的类,已存在的类的方法称为
超类、基类、父类
;新类称为子类、孩子类
;子类比超类拥有的功能更加丰富 - 通过扩展超类定义子类的时候,仅需要指子类于超类不同之处。因此在设计类的时候,应该通过用的方法在超类中,而将具有特殊性的方法放在子类中
覆盖方法
super
关键字:我们可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类。
this
关键字:指向自己的引用。
class Animal {
void eat() {
System.out.println("animal : eat");
}
}
class Dog extends Animal {
void eat() {
System.out.println("dog : eat");
}
void eatTest() {
this.eat(); // this 调用自己的方法
super.eat(); // super 调用父类方法
}
}
public class Test {
public static void main(String[] args) {
Animal a = new Animal();
a.eat();
Dog d = new Dog();
d.eatTest();
}
}
animal : eat
dog : eat
animal : eat
子类构造器
super(n,s,year,month,day);
可以通过super实现对超类构造器的调用。使用super调用构造器的语句必须是子类构造器的第一天语句
- 一个对象变量,可以指示多种实际类型的现行被称为
多态
,在运行时能够自动地调用哪个方法的现象称之为动态绑定
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());
//输出每个人的工资
}
}
import java.time.*;
public class Employee
{
private String name;
private double salary;
private LocalDate hireDay;
public Employee(String name, double salary, int year, int month, int day)
{
this.name = name;
this.salary = salary;
hireDay = LocalDate.of(year, month, day);
}
public String getName()
{
return name;
}
public double getSalary()
{
return salary;
}
public LocalDate getHireDay()
{
return hireDay;
}
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
}
}
public class Manager extends Employee
{
private double bonus;
public Manager(String name, double salary, int year, int month, int day)
{
super(name, salary, year, month, day);
bonus = 0;
}
public double getSalary()
{
double baseSalary = super.getSalary();
return baseSalary + bonus;
}
public void setBonus(double b)
{
bonus = b;
}
}
Carl Cracker 85000.0
Harry Hacker 50000.0
Tommy Tester 40000.0
继承层次
继承并不仅于一个层次,有一个公共超类派生出来的所有类被称为继承层次,在继承层次中,从某个特定的类到其祖先的路径被称为该类的继承连
一个祖先可以拥有多个子孙继承链
多态
一个对象变量,可以指示多种实际类型的现行被称为多态
,在运行时能够自动地调用哪个方法的现象称之为动态绑定
- 有一个用来判断是否应该设计继承关系的简单规则,这就是“is-a”,它表明子类的每个对象也是超类
- 在java程序设计语言中,对象变量时多态的
- 不能将一个超类的引用赋给子类变量
例如:不是所有的雇员都是经理,但每一个经理都是雇员 - 在java中,子类数组的引用可以转换成超类数组的引用,而不是需要采用强制类型转换
Manager[] manages = new Manage[10];
Employee[] staff = managers;
将manager转换为employee
理解方法的调用
下面假设要调用x.f(args),隐式函数x声明为c类的一个对象
- 编译器查看对象的声明类型和方法名,编译器将会一一列举所有c类中名为f的方法和超类中的访问属性public且名为f的方法(超类的私有方法不可访问)。
- 接下来,编译器将其看调用方法时提供的参数类型,如果在所有的为f的方法中存在一个与提供的参数类型完全匹配这个选择这个方法。这个过程被称为重载解析
如果编译器没有找到与参数类匹配的 方法,或者发现经过类型转换后有多个方法与之匹配,就会报告一个错误
如果在子类中定义了一个与超类签名相同的方法,那么子类中这个方法就覆盖了超类中的相同签名的方法 - 如果private方法,static方法,final方法或者构造器,那么编译器将可以准确的知道应该调用哪个方法,我们将这种调用方式称为静态绑定
- 采用动态绑定调用方法时,虚拟机一定要调用与x所引用对象的实际类型最合适的那个类的方法,假设x的实际类型时d,他是c类的子类,如果d类定义了方法f(string),就直接调用它;否则d类的超类中寻找f(string),以此类推
虚拟机预先为每个类创建了一个方法表,其中列出了所有方法的签名和实际调用方法。如果调用super.f(param)编译器将被隐式参数超类的方法表进行搜索
动态绑定有一个非常重要的特性:无需对现存的代码进行修改,就可以对程序进行扩展
在覆盖一个方法的时候,子类方法不能低于超类方法的可见性,如果超类的方法是public,子类方法一定要声明为public
阻止继承:final类和方法
希望组织人们利用某个类定义子类,不允许扩展的类被称为final
类,如果在定义类的时候使用了final修饰符就表明这个类是final类
public final class Executive extends Manager{
}
类中的特定方法也可以被声明为final,如果这样,子类就不能覆盖这个方法(final类中的所有方法自动的改成final方法)
- 对于final域来说,构造对象之后就不允许改变它们的值了,如果将一个类声明为final,只有其中的方法自动生成为final而不包括域
- 将方法或类声明为final主要的目的是:确保他们不会子子类中改变语义
- 如果一个方法没有被覆盖并且很短,编译器就能对它进行优化处理,这个过程称为
内联
强制类型转换
将一个类型强制转换成另一个类型的过程被称为类型转换
double x = 3.045
int nx = (int) x;
有时候需要将某个类的对象引用转换成另一个类的对象引用,用一对圆括号括将目标类名括起来,并放置在需要转换的对象引用之前就可以了
Manger boss = (Manager) staff[0];
进行类型转换的唯一目的:在在世忽略对象的实际类型后,使用对象的全部功能
- 但将一个超类的引用赋给一个子类变量,必须进行类型转换,这样才能通过运行时的检查
在类型转换之前,先查看否能够成功的转换,这个过程可以用instanceof操作符完成
if (staff[1] instanceof Manager)
{
boss=(Manager)staff[1];
}
- 综上所述
只能在继承层次内进行类型转换。
在将超类转换成子类之前,应使用instanceof进行检查
抽象类
如果自下而上在类的继承层次结构中上移,位于层的类更具有通用性,甚至更加的抽象
为了提高程序的清晰度,包含一个或多个抽象方法的类本身须被声明为抽象类的
除了抽象方法之外,抽象类还可以包含具体的数据和具体的方法
例如:
public abstract class Person
{
private String name;
public Person(String name)
{
this.name = name;
}
public abstract String getDescription();
public String getName(){
return name;
}
}
- 抽象方法充当占位的角色,他们的具体实现在子类中,扩展抽象类可以有两种选择。
一种是:在抽象类中定义部分抽象类方法或不定义抽象类方法,这样就将子类也标记成抽象类;另一种时定义全部的抽象方法,这样一来,子类也就不是抽象的了 - 抽象类不能被实例化,可以定义一个抽象类的对象变量,但是它至只能引用非抽象子类的对象
受保护访问
任何声明为private的内容对其它类都时不可见的,则会对于子类也完全适用,即子类也不能访问超类的私有域
希望超类中的某些方法被子类访问,或允许子类的方法访问超类的某个域,可以使用protected
。
- 仅对本类可见——private
- 对所有类可见——public
- 对本包和所有子类可见——protected
- 对本包可见——默认,不需要修饰符