Java继承

继承介绍

在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派生自BicycleObject,所以,一个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;
    }
}

原址:

http://blog.csdn.net/pisota/article/details/5332848


原文地址:https://www.cnblogs.com/biggestfish/p/2916817.html