多态性

多态性

多态性:同一基本类型下的不同的子类型所表现的不同行为,称之为多态性。多态通过分离做什么怎么做,从另一个角度将接口和实现
分离开来。“封装”通过合并特征和行为来创建新的数据类型。“实现隐藏”则通过将细节"私有化"把接口和实现分离开来。继承允许将对
象视为它自己本身的类型或其他基类型加以处理,而多态性则利用了封装和继承的特性实现了消除(我认为减弱更合适)类型间的耦合关系。
多态性所依赖的技术成为后期绑定(运行时绑定,动态绑定)即编译时期父类引用指向子类对象,在编译时期,编译器并不知道具体的对象
而在运行时期根据引用所指向的对象不同而进行不同的方法绑定,也就体现了动态的行为。

多态只关乎普通方法

私有方法,域,静态方法都不具备多态性.
一旦理解了多态性,很容易认为所有事物都可以发生多态,然而只有普通的方法调用可以是多态的。私有方法,域,静态方法都不具备多态
性.
private方法被自动认为是final方法,并且对导出类是屏蔽的。所以即使在子类中添加一个与父类方法签名完全一致的方法,这个方法也
不会被认为是覆盖或者覆写父类方法,而完全是一个新的方法。确切的说,在导出类中,对于基类的private方法,最好采用不同的名字。
示例代码如下:


public class Base { private void test(){ System.out.println("Base test"); } public static void main(String[] args) { //父类引用指向子类对象 Base ba = new Sub(); /** * 私有的方法并不会有多态性,因为它对子类是不可见的 * ba所指向的仍然是一个Sub对象,而Sub对象有一个新的test()方法,但是新的test方法与Base并没有任何关系 * 所以ba指向Sub对象时,根本不会与Sub的新的test()方法发生任何联系,这时,ba调用的test()方法仍然是 * Base的test()方法 */ ba.test();//输出 Base test } } public class Sub extends Base{ public void test(){ System.out.println("Sub test"); //super.test();ERROR: The method test() from the type Base is not visible } }

直接访问某个域,这个访问就将在编译期间进行解析,如下所示:


public class Super { public int field = 0; public int getField(){ return field; } } public class Sub extends Super{ public int field = 1; public int getField(){ return field; } public int getSuperField(){ return super.field; } } public class FieldAccess { public static void main(String[] args) { Super sup = new Sub();//upcast //sup.field = 0 sup.getField() = 1 System.out.println("sup.field = " + sup.field + " sup.getField() = " +sup.getField()); Sub sub = new Sub(); // sub.field = 1 sub.getField() = 1 sub.getSuperField() = 0 System.out.println("sub.field = " + sub.field + " sub.getField() = " +sub.getField() + " sub.getSuperField() = "+sub.getSuperField()); } }

当Sub对象向上转型为Super引用时,任何访问域的操作都将由编译器解析,因此不是多态的,在本例中,为Super.field和Sub.field分
配了不同的存储空间。这样,Sub实际上包含了两个称为field的域:它自己的和它从Super处得到的。所以在Sub中访问field域,并非
访问的Super版本的field域,如果想要访问Super版本的field域则必须显式的指明super.field

静态方法访问静态域

静态方法同样不具有多态性并且静态方法不存在重写override,只存在是否覆盖(隐藏,hiding)父类方法,如果父类中一个方法访问其
静态域,而子类直接继承这个方法,那么在子类中即使覆盖访问的这个静态域,其访问方法仍然会访问父类的静态域,除非在子类覆盖
(隐藏,hiding)父类的这个静态方法,如果在子类中覆盖(隐藏,hiding)父类的这个方法,那么就会访问其子类的静态域。
示例代码如下:

public class Super {
    public static int staticfield = 0;
    public static int staticfieldNotHide = 0;
    public static int  getStaticField(){
        return staticfield;
    }
    public static int  getStaticFieldNotHide(){
        return staticfieldNotHide;
    }
}

public class Sub extends Super{
    public static int staticfield = 1;
    public static int staticfieldNotHide = 1;
    public static int  getStaticField(){
        return staticfield;
    }
}


public class FieldAccess {
    public static void main(String[] args) {
        Super sup = new Sub();// upcasting
        //0   0  0 转型为super直接调用 输出的都是父类的值
        System.out.println(sup.staticfield + "   " + sup.getStaticField() + "  " + sup.getStaticFieldNotHide());

        Sub sub = new Sub();
        // 1 1 0 子类调用,域为子类的值,覆盖(hiding隐藏)过的方法返回子类的值,没有覆盖过的方法仍然返回父类的值
        System.out.println(sub.staticfield + "   " + sub.getStaticField() + "  " + sub.getStaticFieldNotHide());

    }
}

初始化顺序

应该还记得的是在没有继承的情况下(实际上这也不会,因为所有的类型都会继承Object类型),对象的初始化顺序应该是这样的:
首先根据顺序加载静态属性,加载顺序包括静态属性的初始化和静态代码块的初始化,无论是静态属性还是静态代码块都是按照顺序加载
再由实例属性的顺序初始化,根据实例属性的顺序和非静态代码块的顺序初始化实例属性。
最后构造函数执行初始化.示例代码如下:

public class Value {
    private String str;
    public Value(String str){
        this.str = str;
        System.out.println(str);
    }
}


public class Super {
    public static Value superV1 = new Value("super static v1");
    public static Value superV2;

    static {
        System.out.println("父类静态初始化块");
        superV2 = new Value("super static v2 ");
    }
    public static Value superV3 = new Value("super static v3");

    public Value superV4 = new Value("super v4");
    public Value superv5;
    {
        System.out.println("父类非静态初始化块");
        superv5 = new Value("super v5");
    }

    public Value superV7;

    public Super() {
        System.out.println("super 构造函数");
        this.superV7 = new Value("super v7");
    }

    public Value superV6 = new Value("super v6");

}

public class FieldAccess {
    public static void main(String[] args) {
        System.out.println("----------------");
        Super su = new Super();
    }
}
/**
输出:
super static v1
父类静态初始化块
super static v2
super static v3
super v4
父类非静态初始化块
super v5
super v6
super 构造函数
super v7

**/

那么在有继承的情况是什么样呢?应该是首先加载父类的静态属性,再加载子类的静态属性,再加载父类的实例属性,再调用父类
构造函数,再加载子类的实例属性,再调用子类构造函数。示例代码如下:



public class FieldAccess { public static void main(String[] args) { System.out.println("----------------"); Sub su = new Sub(); } } /** 输出: ---------------- super static v1 父类静态初始化块 super static v2 super static v3 sub static v1 子类静态初始化块 sub static v2 sub static v3 super v4 父类非静态初始化块 super v5 super v6 super 构造函数 super v7 sub v4 子类非静态初始化块 sub v5 sub v6 sub 构造函数 sub v7 **/

author by zhaob
time : 2014-09-13 10:40

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值