当前位置: 首页 > news >正文

注册网站到公安机关备案网站建设公司开发

注册网站到公安机关备案,网站建设公司开发,计算机应用技术专业,屯济宁做网站公司CONTENTS 1. 方法调用绑定2. 尝试重写Private方法3. 字段访问与静态方法的多态4. 构造器内部的多态方法行为 1. 方法调用绑定 我们首先来看下面这个例子: package com.yyj;enum Tone {LOW, MIDDLE, HIGH; }class Instrument {public void play(Tone t) {System.ou…

CONTENTS

    • 1. 方法调用绑定
    • 2. 尝试重写Private方法
    • 3. 字段访问与静态方法的多态
    • 4. 构造器内部的多态方法行为

1. 方法调用绑定

我们首先来看下面这个例子:

package com.yyj;enum Tone {LOW, MIDDLE, HIGH;
}class Instrument {public void play(Tone t) {System.out.println("Instrument.play() " + t);}
}class Piano extends Instrument {@Overridepublic void play(Tone t) {System.out.println("Piano.play() " + t);}
}class Guitar extends Instrument {@Overridepublic void play(Tone t) {System.out.println("Guitar.play() " + t);}
}public class Music {public static void tune(Instrument i, Tone t) {i.play(t);}public static void main(String[] args) {Piano p = new Piano();Guitar g = new Guitar();tune(p, Tone.MIDDLE);  // 向上转型,输出:Piano.play() MIDDLEtune(g, Tone.HIGH);  // Guitar.play() HIGH}
}

main() 方法中,我们将 Piano 引用传递给了 tune(),且不需要任何强制类型转换。这是因为 Instrument 中的接口必定存在于 Piano 中,因为 Piano 继承了 Instrument。从 Piano 向上转型到 Instrument 可以“缩小”该接口,但不会小于 Instrument 的完整接口。

那么编译器怎么可能知道这个 Instrument 引用在这里指的是 Piano,而不是 Guitar?为了更深入地了解这个问题,有必要研究一下绑定(binding)这个问题。

将一个方法调用和一个方法体关联起来的动作称为绑定。在程序运行之前执行绑定(如果存在编译器和链接器的话,由它们来实现),称为前期绑定。你之前可能没有听说过这个术语,因为在面向过程语言中默认就是前期绑定的。例如,在 C 语言中只有一种方法调用,那就是前期绑定。

解决这个问题的方案称为后期绑定,这意味着绑定发生在运行时,并基于对象的类型。后期绑定也称为动态绑定或运行时绑定,当一种语言实现后期绑定时,必须有某种机制在运行时来确定对象的类型,并调用恰当的方法。也就是说,编译器仍然不知道对象的类型,但方法调用机制能找到并调用正确的方法体。

Java 中的所有方法绑定都是后期绑定,除非方法是 staticfinal 的(private 方法隐式为 final)。这意味着通常不需要你来决定是否要执行后期绑定,因为它会自动发生。

2. 尝试重写Private方法

看一下下面这段代码:

package com.yyj;public class PrivateOverride {private void f() {System.out.println("Private f()");}public static void main(String[] args) {PrivateOverride p = new Derived();  // 创建Derived对象p.f();  // Private f()}
}class Derived extends PrivateOverride {public void f() {  // 你以为重写了父类中的f()System.out.println("Public f()");}
}

可能会很自然地认为输出应该为 Public f(),但 private 方法自动就是 final 的,并且对子类也是隐藏的,所以 Derivedf() 在这里是一个全新的方法,它甚至没有重载,因为 f() 的基类版本在 Derived 中是不可见的。

这样的结果就是,只有非 private 的方法可以被重写,但要注意重写 private 方法的假象,它不会产生编译器警告,但也不会执行你可能期望的操作,如果使用了 @Override 注解,那么这个问题就会被检测出来。

3. 字段访问与静态方法的多态

现在你可能会开始认为一切都可以多态地发生,但是,只有普通的方法调用可以是多态的。例如,如果直接访问一个字段,则该访问会在编译时解析:

package com.yyj;class Super {public int x = 0;public int getX() { return x; }
}class Sub extends Super {public int x = 1;@Override public int getX() { return x; }public int getSuperX() { return super.x; }
}public class GetField {public static void main(String[] args) {Super sup = new Sub();  // 向上转型System.out.println("sup.x = " + sup.x + ", sup.getX() = " + sup.getX());Sub sub = new Sub();System.out.println("sub.x = " + sub.x + ", sub.getX() = " + sub.getX() + ", sub.getSuperX() = " + sub.getSuperX());/** sup.x = 0, sup.getX() = 1* sub.x = 1, sub.getX() = 1, sub.getSuperX() = 0*/}
}

Sub 对象向上转型为 Super 引用时,任何字段访问都会被编译器解析,因此不是多态的。在此示例中,Super.xSub.x 被分配了不同的存储空间,因此,Sub 实际上包含两个被称为 x 的字段:它自己的字段和它从 Super 继承的字段。然而,当你在 Sub 中引用 x 时,Super 版本并不是默认的那个,要获得 Super 的字段必须明确地使用 super.x

现在我们再来看一下静态方法,如果一个方法是静态的,那它的行为就不会是多态的,因为静态方法与类相关联,而不是与单个对象相关联:

package com.yyj;class StaticSuper {public static void staticPrint() {System.out.println("Super staticPrint()");}public void dynamicPrint() {System.out.println("Super dynamicPrint()");}
}class StaticSub extends StaticSuper {public static void staticPrint() {System.out.println("Sub staticPrint()");}@Overridepublic void dynamicPrint() {System.out.println("Sub dynamicPrint()");}
}public class StaticPolymorphism {public static void main(String[] args) {StaticSuper sup = new StaticSub();  // 向上转型StaticSub.staticPrint();  // Sub staticPrint()sup.dynamicPrint();  // Sub dynamicPrint()StaticSuper.staticPrint();  // Super staticPrint()}
}

4. 构造器内部的多态方法行为

构造器调用的层次结构带来了一个难题,对于正在构造的对象,如果在构造器中调用它的动态绑定方法,会发生什么?

在普通方法内部,动态绑定调用是在运行时解析的,这是因为对象不知道它是属于该方法所在的类还是其子类。如果在构造器内调用动态绑定方法,就会用到该方法被重写后的定义。但是,这个调用的效果可能相当出乎意料,因为这个被重写的方法是在对象(即子类对象)完全构造之前被调用的,因为是从外到内(即从基类到子类)执行构造器的,这可能会带来一些难以发现的错误。如下面这段代码所示:

package com.yyj;class A {void f() {System.out.println("A.f()");}A() {System.out.println("A() before A.f()");f();  // 其实是调用子类重写后的f()System.out.println("A() after A.f()");}
}class B extends A {private int x = 1;  // 子类对象的默认初始值B(int x) {this.x = x;System.out.println("B(), x = " + x);}@Overridevoid f() {System.out.println("B.f(), x = " + x);}
}public class PolyConstructors {public static void main(String[] args) {new B(5);/** A() before A.f()* B.f(), x = 0* A() after A.f()* B(), x = 5*/}
}

A.f() 是为重写而设计的,这个重写发生在 B 中,但是在 A 的构造器调用了这个方法,而这个调用实际上是对 B.f() 的调用。输出显示,当 A 的构造器调用 f() 时,B.x 的值甚至不是默认的初始值1,而是0。

因此类的完整初始化过程如下:

  1. 在发生任何其他事情之前,为对象分配的存储空间会先被初始化为二进制零。
  2. 如前面所述的那样调用基类的构造器,此时被重写的 f() 方法会被调用(是的,这发生在 B 构造器被调用之前),由于第1步的缘故,此时会发现 B.x 值为零。
  3. 按声明的顺序来初始化成员。
  4. 执行子类构造器的主体代码。

这样做有一个好处:一切至少都会初始化为零(或对于特定数据类型来说,是任何与零等价的值),而不仅仅是被视为垃圾。这包括通过组合嵌入在类中的对象引用,这些引用默认为 null。因此,如果忘记初始化该引用,在运行时就会出现异常。

因此,编写构造器时有一个很好的准则:用尽可能少的操作使对象逬入正常状态,如果可以避免的话,请不要调用此类中的任何其他方法。只有基类中的 final 方法可以在构造器中安全调用(这也适用于 private 方法,它们默认就是 final 的)这些方法不能被重写,因此不会产生这种令人惊讶的问题。

http://www.khdw.cn/news/26690.html

相关文章:

  • 做高端生活方式的网站网页制作html代码
  • 网站 微信小程序怎么做策划方案模板
  • 政府网站建设的基本原则谷歌play商店
  • 网站建设的缺点宁海关键词优化怎么优化
  • 网站平台建设模板上海站群优化公司
  • 海南专业网站运营托管网站优化排名怎么做
  • 游戏网站开发运营的几个思路外贸网站推广怎么做
  • 广告牌免费设计在线生成seo黑帽教学网
  • 做会员卡网站网站排名优化培训
  • 企业融资的主要方式太原seo排名外包
  • 大连哪个公司做网站开发的广告推广怎么做
  • 阿里巴巴做短视频网站情感营销的十大案例
  • 网站建设模板源码seo门户 site
  • 太月星网站建设程序开发腾讯广告投放推广平台价格
  • 做网站的公司怎么推销成品视频直播软件推荐哪个好用
  • 做网站维护难吗北京网站定制公司
  • 做网站商城的目的是什么网络营销的发展概述
  • 做网站可能遇到的困难百度公司简介介绍
  • 什么做网站的公司好搜索引擎优化的英语简称
  • 网站制作的设计思路宁德seo培训
  • 网站seo多少钱今日刚刚发生新闻事件
  • 网站设计推广百度一下首页网页
  • 题库网站建设的绩效指标写文章一篇30元兼职
  • 如何查公司的工商注册信息搜索引擎关键词优化技巧
  • 国外有哪些网站怎么优化自己网站
  • 深圳汽车网站建设百度竞价sem
  • 易语言做网站登录免费精准客源
  • 园区网站建设调研报告如何免费推广自己的网站
  • 好的企业型网站模板郴州seo
  • 做网站需要独立显卡吗电商平台排名