继承
Java中的继承是一种机制,表示为一个对象获取父对象的所有属性和行为,派生类通过 extends 关键字来继承基类。
//基类
class Base{
private int ma;
public Base(int a){
this.ma = a;
System.out.println("Base");
}
}
// 子类 派生类
class Derive extends Base{
private int mb;
public Derive(int a,int b) {
super(a);//基类的构造函数。必须显式的调用。必须放在第一行。
this.mb = b;
System.out.println("Deiver");
}
}
派生类继承了基类的除了基类的构造函数的其他数据成员。所以在派生类的构造函数中必须显式的调用基类的构造函数,且必须放在第一行.
super 关键字表示基类的对象,通过 super 可以显式的调用基类的方法和数据成员。
类和对象的初始化顺序
我们看下面这个例子,通过这个例子我们可以看出基类在派生类对象的初始化顺序。
class Base{//基类
private int ma;
public Base(int a){
this.ma = a;
System.out.println("Base");
}
static{//静态代码块
System.out.println("Base.static init!!!");
}
//实例代码块
{
System.out.println("Base.sinstance init!!!");
}
}
//Derive 子类 派生类
class Derive extends Base{
private int mb;
public Derive(int a,int b) {
super(a);//基类的构造函数。必须显式的调用。必须放在第一行。
this.mb = b;
System.out.println("Deiver");
}
static{
System.out.println("Derive.static init!!!");
}
{
System.out.println("Derive.sinstance init!!!");
}
}
public static void main(String[] args) {
Derive d = new Derive(1,2);
System.out.println("================");
Derive d2 = new Derive(1,2);
}
运行结果:
Base.static init!!!
Derive.static init!!!
Base.sinstance init!!!
Base 构造函数
Derive.sinstance init!!!
Deiver 构造函数
================
Base.sinstance init!!!
Base 构造函数
Derive.sinstance init!!!
Deiver 构造函数
我们可以看出在继承关系中类的初始化顺序
注意:静态代码块只初始化一次,所以 d2 没有静态初始化的部分。
基类数据成员在派生类中的访问权限。
派生类和基类之间的相互复制
- 派生类可以赋给基类
- 基类不能赋给派生类
public static void main(String[] args) {
//基类引用 引用了派生类对象。
Base b2 = new Derive(10,20);
Base b = new Base(2);
Derive d = new Derive(10,20);
b = d;
d = b;//报错
}
多态
实现多态必须具备三个条件:继承、重写、向上转型。
首先我们先来说明几个概念
重载 overLoad
- 函数名相同
- 参数列表不相同
- 返回值无要求
class Base2{ private int ma; public Base2(int a){ this.ma = a; } //实例方法 public void fun1(){ System.out.println("Base.fun11111"); } } class Derive2 extends Base2{ private int mb; public Derive2(int a,int b) { super(a); this.mb = b; } /* * 重载 * 函数名相同、参数列表不相同、返回值无要求 */ public void fun1(int a){ System.out.println("derive.fun11111"); } }
public static void main(String[] args){ Derive2 d = new Derive2(10,20); d.fun1(); d.fun1(10); }
运行结果:Base.fun11111
derive.fun11111
上例中 fun1( ) 和 fun1( 10 ) 是不同的函数,是因为在 Derive2 中对 fun1( ) 进行了重载。
重写 override
- 函数名相同
- 参数列表相同
- 返回值相同
class Base2{ private int ma; public Base2(int a){ this.ma = a; } //实例方法 public void fun1(){ System.out.println("Base.fun11111"); } } class Deriver3 extends Base2{ private int mc; public Deriver3(int a,int c){ super(a); this.mc = c; fun1(); } /* * 重写(覆盖) * 函数名相同、参数列表相同、返回值相同。 */ public void fun1(){ super.fun1(); System.out.println("deriver3.fun11111"); } }
public static void main(String[] args){ Deriver3 d = new Deriver3(10,20); d.fun1(); }
运行结果:Base.fun11111
deriver3.fun11111
上例中 d.fun1() 调用的是 Deriver3 中的 fun1( ) 函数,这就是重写。
动态绑定和静态绑定
class Base2{
private int ma;
public Base2(int a){
this.ma = a;
}
public void fun1(){
System.out.println("Base.fun11111");
}
public static void fun2(){
System.out.println("Base.fun22222");
}
}
class Deriver3 extends Base2{
private int mc;
public Deriver3(int a,int c){
super(a);
this.mc = c;
}
public void fun1(){
System.out.println("deriver3.fun11111");
}
public static void fun2(){
System.out.println("deriver3.fun22222");
}
}
public static void main(String[] args){
Base2 b = new Deriver3(1,10);
b.fun1();
}
运行结果:deriver3.fun11111
为什么基类的引用调用的方法是派生类的呢,这是因为发生了动态绑定。如下图所示。
在方法区中每一个类都有一个方法表,方法表中存储着各种方法名称和方法所在的地址,在调用方法的时候先通过类找到其方法表,然后在方法表中找到方法所在的地址,然后再找到方法执行。
public static void main(String[] args){
Base2 b = new Deriver3(1,10);
b.fun2();
}
运行结果:Base.fun22222
那么为什么这个方法又执行的是基类的呢?这是因为发生了静态绑定。
因为fun2是静态方法,在编译时产生,而动态绑定是发生在运行时的。
最后强调一点,构造函数也会发生动态绑定。