java 基础——面向对象编程之继承、多态

继承

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

扫描二维码关注公众号,回复: 2334925 查看本文章

               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是静态方法,在编译时产生,而动态绑定是发生在运行时的。

最后强调一点,构造函数也会发生动态绑定。


猜你喜欢

转载自blog.csdn.net/alyson_jm/article/details/80404252