Java的继承和多态

Java是一种OP语言,即面向对象的语言,主要的特点有:封装、继承、多态、组合。

一.继承

1.super()

class Base{
	private int ma;//数据成员
	//静态代码块
	static{
		System.out.println("Base static init!");
	}
	//实例代码块
	{
		System.out.println("Base instanc init");
	}
	//构造方法
	public Base(int a){
		System.out.println("Base init");
		this.ma = a;
	}
}
上述代码定义了一个类,我们将它称之为基类(父类、超类),有自己的数据成员,构造函数
class Derive extends Base{
	private int mb;//子类自己的数据成员
	
	static{
		System.out.println("Derive static init!");
	}
	
	{
		System.out.println("Derive instanc init");
	}
	
	public Derive(int a,int b){
		super(a);//调用基类的构造函数,把基类的东西继承过来,必须第一行!!!
		this.mb = b;
		System.out.println("Derive init");		
	}
}

上述代码通过extends关键字将Derive类继承于Base基类,我们把Derive类叫作派生类(子类)。它也有自己的构造函数,并在构造函数的第一行有一个super(a)。这是因为派生类会继承除了构造函数之外的数据成员和成员方法,因此需要在派生类中显示的调用基类的构造函数。

 super用法: super( )            调用基类的构造函数

                    super.data        调用基类的数据成员

                    super.func()      调用基类的成员方法

如果不调用super,发现会报错。

2.派生类继承基类之后的打印顺序。

类—>对象的初始化顺序:①静态数据成员
                         ②静态代码块
                        ③实例数据成员
                                ④实例代码块

                         ⑤调用合适的构造函数

                Derive d = new Derive(10, 20);
		System.out.println("==========");
		Derive d2 = new Derive(10, 20);

我们在主函数里写这样一段代码,打印顺序如图。


静态代码块只打印了一次,这是因为静态代码块只初始化一次。

3.关于访问权限。


 同包子类同包非子类不同包子类不同包非子类
public可以可以可以可以
protected(包访问权限)可以可以可以不可以
private不可以不可以不可以不可以
默认(包访问权限)可以不可以不可以不可以

4.派生类和基类的相互复制

                Base b = new Base(50);
		Derive d = new Derive(10, 20);
		b = d;//将子类给父类
		Base b2 = new Derive(100, 1000);//基类引用了派生类对象,这个是多态的基础

我们可以定义一个基类对象和一个派生类对象,然后将派生类给基类,但不能将基类给派生类,因为这样子类自己的东西就没有了,会报错。

二. 多态

1.重载和覆盖(重写)

 函数名参数列表返回值
重载相同不同不要求
覆盖(重写)相同相同相同

接下来看一段代码

class Base2{//基类
	int ma;
	public Base2(int a){
		this.ma = a;
	}
	//如何区分实例方法和类方法(有无static)
	public void fun1(){//实例方法
		System.out.println("Base fun1!");
	}
	
	public static void fun2(){//类方法,有static
		System.out.println("Base fun2!");
	}
}
class Derive2 extends Base2{
	int mb;
	
	public Derive2(int a,int b){
		super(a);
		this.mb = b;
	}
	//重载
	public void fun1(int i){
		System.out.println("Derive fun1(参数)");
	}
}

class Derive3 extends Base2{
	int mc;
	
	public Derive3(int a,int c){
		super(a);
		this.mc = c;
	}
	
	public void fun1(){
		System.out.println("Derive3 fun1()");
	}
	
	public static void fun2(){
		System.out.println("Derive3 fun2()");
	}
}

打印结果如图。

Derive2类实现了类的重载,Derive3类实现了方法的覆盖。

2.invokespecial invokevirtual invokestatic

public static void main(String[] args) {
		Base2 b = new Base2(10);//invokespecial
		System.out.println(b);
		b.fun1();//invokevirtual
		Base2.fun2();//invokestatic
	}

Base2@1d7fbfb:当前引用对象的类信息 @1d7fbfb对象的地址


反汇编中出现了invokespecial invokevirtual invokestatic

Java中构造函数被处理为invokespecial,静态函数被处理为invokestatic,其他的都被处理为invokevirtual。

3.动态绑定和静态绑定

public static void main(String[] args) {
		Base2 b1 = new Derive3(10,20);
		System.out.println(b1);
		b1.fun1();  
	}

打印结果如图,看一下反汇编

打印结果时派生类的fun1( )函数,为什么反汇编显示调用基类的fun1( )呢?


这是因为Derive3对基类的fun1进行了覆盖,此时派生类的地址就会将基类的地址覆盖,所以打印的时派生类的fun1函数

public static void main(String[] args) {
		Base2 b1 = new Derive3(10,20);
		System.out.println(b1);
		b1.fun2();//打印基类的fun2   
	}

打印结果如图

为什么又会打印基类的fun2呢?因为fun2是静态方法,在编译时产生,而动态绑定是发生在运行时的,所以fun2方法不会有动态绑定。fun2属于静态绑定,发生在编译时。

3.构造函数也会发生动态绑定

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值