java 类继承_java中类继承,到底继承了什么?

继承的最大好处就是为了实现代码的复用。那么,子类到底从父类得到的什么呢?

实例成员

父类的private成员不会被子类继承,子类不能访问。但是子类对象的确包含父类的私有成员。

父类的 包访问成员 继承为子类的包访问成员。就好像他们直接定义在子类中一样。

父类的 protected 成员继承为子类的protected 成员。就好像他们直接定义在子类中一样。

父类的 public 成员继承为子类的public 成员,就好像他们直接定义在子类中一样。

实例方法

继承到的实例方法在子类中可以直接被使用,还需重点理解是方法的重写和重载。

重写override

一个继承链中,父类的方法对于子类来说具有相同的语义,但是不同的细节操作,因此子类需要override父类的这个方法以满足自己的需求。

注意的点:

1、方法名,参数表一定和父类中的相同,返回类型相同,或者是子类。

1、访问权限一定不低于父类的实例方法

2、抛出的异常一定是父类方法抛出的异常相同,或者子类。

如果拿C++和java对比,那么java中的实例方法默认都是virtual的(java中没有virtual这个key word),因此在java中,子类可以直接重写父类方法的任何非final实例方法,但是在C++中,除非父类使用virtual标记一个方法为虚方法,子类才可以override这个方法。

对于重写的方法,javac是不能确定的具体要调用那个类的方法,而是产生特殊的字节码让jvm去动态决定什么方法。这个就是所谓的前期绑定和后期绑定的差异。

public classTest

{public static voidmain(String [] args){

Object o= newSubClass();

o.toString();

}

}classSubClass extends Object

{publicString toString() //重写Object 的toString方法

{return "SubClass";

}

}

Compiled from "Test.java"

public classTest {publicTest();

Code:0: aload_01: invokespecial #1 // Method java/lang/Object."":()V

4: return

public static voidmain(java.lang.String[]);

Code:0: new #2 //class SubClass

3: dup4: invokespecial #3 // Method SubClass."":()V

7: astore_18: aload_19: invokevirtual #4 // Method java/lang/Object.toString:()Ljava/lang/String;

12: pop13: return}

其中3处红色标记 的代码,重要的区别就是invokespecial和 invokevirtual:invokespecial 代表前期绑定,在编译期就能决定调用什么方法,由javac确定调用什么方法。invokevirtual 则是方法的后期绑定,由JVM决定调用什么方法。

第一处是Test类的构造函数调用父类Object的构造函数,编译期确定,他是前期绑定。

第二处是因为new 了一个SubClass对象,调用SubClass的构造函数,编译期确定,他也是前期绑定的。

第三处是因为我们的 Object o 引用了重写了toString方法的SubClass对象,javac不能知道具体调用Object中的toString,还是SubClass中的toString,于是产生特殊代码让JVM去决定。

修饰为 static  、 final 、private 的方法一定是前期绑定,因为他们根本都不存在override。

JVM需要在运行时动态决定调用那个版本的方法,这个过程对JVM来说就是 virtual method lookup(虚方法查找)。JVM将会从实际对象所属的类 和 他的最近的父类中查找。如果自己定义了,则调用自己的版本,如果没有则调用父类的版本。

重载overload

重载的定义:函数重载是指在同一作用域内,可以有一组具有相同函数名,不同参数列表的函数,这组函数被称为重载函数。

注意:重载与否,不考虑函数的返回类型。C++也是如此。也就是说,2个函数的返回类型同不同都不影响他们能不能形成重载,只要他们函数名相同,参数表不同就满足重载。

Java是默认是支持跨类重载的。但是C++就默认不支持在子类中重载父类的实例方法。那也就是说:java中的实例方法会自动导入到子类的作用域中,而C++则不是。

public classTest

{public static voidmain(String[] args)

{

Derive d= newDerive();

d.print("hello", 2);

d.print("world");

/* output

hello

hello

world

world

world

world

world

*/

}

}classBase

{public voidprint(String msg)

{for(int i=0;i<5;++i)

{

System.out.println(msg);

}

}

}class Derive extendsBase

{public void print(String msg,inttimes) //在子类中重载父类方法

{for(int i=0;i

{

System.out.println(msg);

}

}

}

对于C++

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

#include#include

using namespacestd;classBase

{public:void print(const string& msg) const{for(int i=0;i<5;++i)

{

std::cout<

}

}

};class Derive:publicBase

{public:/**需要使用using Base::print将父类中的版本引如到子类的作用域中,这样才能形成跨类重载,否则子类在使用一个参数版本的print函数时,会出现以下编译错误:

*

* error: no matching function for call to ‘Derive::print(const char [6])’

*意思是编译器在Derive类中找不到print(const char [6])版本的函数

**/

usingBase::print;void print(const string& msg,const int times) const{for(int i=0;i

{

std::cout<

}

}

};intmain()

{

Derive d ;

d.print("hello",2);

d.print("world");

}

View Code

实例字段

实例字段没有什么要说的,要说的就是实例字段的隐藏了:在子类中定义一个和父类同名的字段,那么子类中的名称将会隐藏父类中的同名字段。

几乎没有人使用这个技术,如果用到了,那么说明代码设计有问题(bad code)。

classBase

{protected int i = 100;

}class Derive extendsBase

{private int i = 1000; //隐藏了父类字段 ipublic voidfoo()

{

System.out.println(i);//代表this.i

System.out.println(this.i);

System.out.println(super.i); //使用父类被隐藏的i

}

}

使用父类被隐藏的字段,也可以使用cast,这是最终极的手段。因为super只能引用最近父类的成员,而不能引用父类的父类的成员。但使用cast可以做到。

classA

{protected int i = 100;

}class B extendsA

{protected int i = 1000;

}class C extendsB

{private int i = 10000;public voidfoo()

{

System.out.println("this.i:"+this.i); //10000

System.out.println("B.this.i:"+ ((B)this).i ); //1000

System.out.println("A.this.i:"+ ((A)this).i ); //100}

}

static成员

static会被子类继承吗?答案是会。他们会被继承为子类的static成员,而不是子类实例的成员。

同样,private static成员不会被继承,只有 包访问 权限 ,protected public 成员才会被继承。

父类的private成员不会被子类继承,子类不能访问。

父类的 包访问成员 继承为子类的包访问成员。就好像他们直接定义在子类中一样。

父类的 protected 成员继承为子类的protected 成员。就好像他们直接定义在子类中一样。

父类的 public 成员继承为子类的public 成员,就好像他们直接定义在子类中一样。

static 成员同样也可以使用实例成员的访问修饰符 public ,包访问,protected , priavcte。

static方法

static方法不能被override,只能被隐藏。

static字段

和实例字段一样,static字段也可以被隐藏。如果要引用被隐藏的父类static字段,则需要显式的通过父类的类名来使用。隐藏static字段通常也最好不要使用。

构造函数

构造函数不能继承,但是子类一定可以(也必须)借用父类的构造函数。java保证:除了Object类对象,每一个类的实例在构造时,先去调用父类的构造函数。

我们自定义类的构造函数的第一句一定是super(xx,...),如果不是,那么第一句就一定是this(xx,...)去调用本类的另一个构造函数。

如果子类构造函数不显式的调用super(),那么,javac会自动插入super(),也就是父类无参数的构造函数。

对于构造函数,其实类中所有构造函数都是隐式static的。很明显的例证就是 构造函数無需通過实例就可以调用。

欢迎转载,请注明出处:www.cnblogs.com/lulipro

为了获得更好的阅读体验,请访问原博客地址。

限于本人水平,如果文章和代码有表述不当之处,还请不吝赐教。

代码钢琴家

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值