继承介绍
在java语言中,类可以从其他类继承而来,并继承父类的成员和方法。
继承是简单但是非常强大:当你需要创建一个新类,但是已经有其他类,它已经包含了一些你需要的代码,那么你可以从已有的这个类,继承新的类。这样,你不用写重复的代码和调试,就可以重用已有类的成员和方法。
子类从父类继承所有的成员(变量,方法和内部类),构造方法不是成员,所以不会被继承,但是子类的构造方法可以调用父类的构造方法。
在java平台中,java.lang.Object是所有类的父类,有些类直接继承Object,有些类间继承其他类,其他类继承自Object。
继承例子
Bicycle类
public class Bicycle {
// the Bicycle class has
// three fields
public int cadence;
public int gear;
public int speed;
// the Bicycle class has
// one constructor
public Bicycle(int startCadence, int startSpeed, int startGear) {
gear = startGear;
cadence = startCadence;
speed = startSpeed;
}
// the Bicycle class has
// four methods
public void setCadence(int newValue) {
cadence = newValue;
}
public void setGear(int newValue) {
gear = newValue;
}
public void applyBrake(int decrement) {
speed -= decrement;
}
public void speedUp(int increment) {
speed += increment;
}
}
一个类MountainBike
类继承自Bicycle:
public class MountainBike extends Bicycle {
// the MountainBike subclass adds
// one field
public int seatHeight;
// the MountainBike subclass has one
// constructor
public MountainBike(int startHeight,
int startCadence,
int startSpeed,
int startGear) {
super(startCadence, startSpeed, startGear);
seatHeight = startHeight;
}
// the MountainBike subclass adds
// one method
public void setHeight(int newValue) {
seatHeight = newValue;
}
}
MountainBike
继承Bicycle的所有变量和方法,并添加了seatHeight变量和对应的set方法。
MountainBike
新类,它有四个变量和五个方法,不过你未必要全部都要使用。
如果Bicycle的方法很复杂,并已经使用了大量的时间调试,那么这种复用代码的方式,是相当简单并有价值。
子类能做的事情
子类继承父类所有的public和protected成员,不管父类在哪个包。如果子类和父类在同一个包,它也会继承父类的 package-private成员(没有修饰public,privat,protected的成员),对于继承的成员,可以替换它,隐藏它,或者补充 新成员:
1.被继承的成员变量可以直接使用,就像使用其他成员
2.声明一个新成员变量,名字和父类的成员变量名字一样,就隐藏了该成员(不推荐)
3.声明不存在父类的新成员变量。
4.继承的方法可以直接使用
5.在子类实现一个与父类签名一样的方法,可以覆盖父类的方法。
6.在子类实现一个与父类签名一样的新的static方法,可以覆盖父类的方法。
7.可以声明一个不存在父类的新成员方法。
8.实现一个子类的构造器,通过隐式或者显示使用super调用父类的构造器。
父类的private成员
子类不会继承父类的private成员,但是,如果父类已经有public或protected方法访问的私有成员,那么通过可以继承的方法,依然可以间接访问父类的private成员.
内部类可以访问嵌套类的所有成员,包括嵌套类private成员。
对象转换
一个对象的实例化,我们可能这么写:
public MountainBike myBike = new MountainBike();
这表示myBike是MountainBike类型。
MountainBike
派生自Bicycle
和Object,所以,一个
MountainBike实例既是一个Bicycle,也是一个Object.
而逆转的未必是可以的:一个Bicycle未必是MountainBike。同样的,一个Object未必是
Bicycle
或者MountainBike。
类型转换显示在允许的继承和实现中,一个对象,从一种类型替换为另一种类型的用法.例如
Object obj = new MountainBike();
这样obj既是一个Object,也是一个Mountainbike。
另一边,我们这么写:
MountainBike myBike = obj;
我们会遇到一个编译时错误,因为对于obj,编译器不知道它是一个MountainBike。尽管如此,我们可以告诉编译器,通过显示转换,将obj转换为类型MountainBike
MountainBike myBike = (MountainBike)obj;
这种转换为插入一个运行时检测,编译器会安全假设obj是一个MountainBike类型,但如果obj不是一个MountainBike,运行时,会抛出异常。
当然,你可以使用instanceof操作符做逻辑测试,判断obj是否MountainBike类型再做转换
if (obj instanceof MountainBike) {
MountainBike myBike = (MountainBike)obj;
}
这样,我们做类型转换,就不会有运行时异常抛出了。
大家都知道子类继承父类是类型的继承,包括属性和方法!如果子类和父类中的方法签名相同就叫覆盖!如果子类和父类的属性相同,父类就会隐藏自己的属性!
但是如果我用父类和子类所创建的引用指向子类所创建的对象,父类引用所调用子类对象中的属性值或方法的结果是什么呢?
看代码:
public class FieldDemo {
public static void main(String[] args){
Student t = new Student("Jack");
Person p = t;//父类创建的引用指向子类所创建的对象
System.out.println(t.name+","+p.name);
System.out.println(t.getName()+","+p.getName());
}
}
class Person{
String name;
int age;
public String getName(){
return this.name;
}
}
class Student extends Person{
String name; // 属性和父类属性名相同,但在做开发时一般不会和父类属性名相同!!
public Student(String name){
this.name = name;
super.name = "Rose"; // 为父类中的属性赋值
}
public String getName(){
return this.name;
}
}
返回结果是:Jack,Rose
Jack,Jack
原因是:在Java中,属性绑定到类型,方法绑定到对象!
内存图如下:
关于JAVA继承类的静态变量、成员变量、父子类构造方法调用顺序的探讨 .
综合网上的相关帖子和我自己的调试,研究了一下关于JAVA继承类的静态变量、成员变量、父子类构造方法调用顺序问题。首先看一段程序:
class X {
Y b =new Y();//7、这里是父类成员变量初始化
static Y sb=new Y();//1、父类静态变量,输出static Y(静态代码块先初始化),2、Y
static{
System.out.println("static X父类静态代码块");//3、执行静态代码块
new Y();//4、这里只是输出Y,有static Y(静态代码块只执行一次)
}
X() {
System.out.println("X");//8、父类成员变量初始化之后,执行父类构造器输出X
}
}
class Y {
static{
System.out.println("static Y");
}
Y() {//执行构造函数
//这里有个super()==Object()
System.out.println("Y");
}
}
publicclass Z extends X {
finalstaticint mead=45;
finalbyte b=16;
static Y sb=new Y();//5、子类的静态变量,输出Y
static{
System.out.println("static Z");//6、子类的静态代码块
}
Y y =new Y();//9、这里是子类成员变量初始化
Z() {
//这里有super()==new X()
this.y =null;
System.out.println("Z");//10、子类成员变量初始化之后,执行子类构造器输出Z
}
publicstaticvoid main(String[] args) {
new Z();
}
}
执行结果:
static Y
Y
static X父类静态代码块
Y
Y
static Z
Y
X
Y
Z
解释:
static的东西在编译的时候就向内存要到了存取空间,他们的初始化要早于非static,顺序是先父类再子类。
初始化类,先执行super()父类的的构造函数(final和static之后),父类的构造函数先执行super()直到object super(),完了执行一般成员变量的初始化
一般成员变量初始化完毕,执行构造器里面的代码(super()之后的代码).
父类的初始化完成后(子类构造器里面super执行完毕),才轮到子类的成员变量初始化
子类成员变量初始化完毕,开始执行子类构造器里面的代码(super()之后的代码).
注意:
静态块和静态变量的调用顺序是按照书写顺序执行的,比如上边X类中静态块和静态变量的书写顺序颠倒如下:
class X {
Y b = new Y();
static{
System.out.println("static X父类静态代码块");
new Y();
}
static Y sb= new Y();
X() {
System.out.println("X");
}
}
则执行结果为:
static X父类静态代码块
static Y
Y
Y
Y
static Z
Y
X
Y
Z
最后:
确定变量空间和初始化赋值是分开进行的,先一次性确定静态成员变量空间 并赋给二进制0 ,然后按照书写顺序逐一赋值
如下代码,输出结果是0.0
publicclass Test {
staticint i=f();
staticdouble d=0.1234;
publicstaticvoid main(String[] args) {
new Test();
}
staticint f(){
System.out.println(d);
return3;
}
}
原址: