Java 多态实现的详细介绍
2017-11-08 16:43
239 查看
普通(非多态)方法的地址是在编译时确定的,调用它的字节码(invokespecial,invokestatic)指令可以直接调用该方法。 这有时被称为早期绑定(或者叫做静态绑定),因为方法名称在编译时绑定到某一具体的内存地址。 这是有效的,但并不总是方便!
有时,我们不清楚某些变量的类型应该是什么,直到我们运行该程序,因为它可能取决于用户输入,随机数或其他外部数据,如文件中的数据。
以上所说的普通方法即为:private、static标识的方法。
对于如下的代码:
创建的Number对象(在第10行)的实际类型取决于输入参数是一个整数(在这种情况下,它实际是一个Long对象)还是浮点数(在这种情况下,它实际是一个Double对象)。
在编译期间没有办法知道应该调用哪一个
在这个程序中,直到运行时间才知道:
这就是为什么多态性也被称为晚期绑定,延迟绑定或动态绑定的原因。 (在某些语言中,如C++多态方法被称为虚方法或虚函数。)
没有多态性,这个程序会更复杂; 你必须使用一些自定义的方法来将
然后您需要一个switch语句或一个if表达式来测试类型名称并自己调用正确的方法。(即,
这种代码非常难看,很容易被破坏,难以维护或增强。 这是Java支持多态方法的主要原因。
当在运行期间调用一些多态方法时,在这个表中查找方法名称来获取相应的地址。
方法表包含对象的动态绑定(多态)方法的名称和地址。 对于属于同一个类的所有对象,方法表是相同的,所以它们作为该类的元数据存储在方法区中。
方法表不是语言的一部分,只要最终结果相同,不同的JVM供应商可以随意实现多态。
Sun JVM在对象的常量池中混合方法表项,可以使用命令
这是说明性的,看看系统如何为某些类(如Integer)构造一个方法表。 最初方法表是空的。 然后方法表用最远的祖先类(通常是Object类)中的多态方法填充:
Integer Method Table part 1
下一个最远的祖先类(这里是Number类)中的多态方法(和已修改的现有条目)被添加到这个列表。
如果您查看API(JavaDocs),您会发现Number类不会覆盖任何方法,但会在表中添加六个新的方法。 所以方法表中的toString条目保持不变:
Integer Method Table part 2
这一直持续到所有父类的方法表已经合并为止。 最后再用Integer类的多态方法更新方法表。 现在toString被覆盖了:
Integer Method Table part 3
覆盖某些方法只会更改方法表中的地址,但不会更改名称。
这就是为什么javap输出将多态方法名称显示为Object.toString,而不仅仅是toString或Integer.toString。
看看这个方法调用的字节码(使用
“#11”是指方法表中的方法名称(如上所述,这是常量池的索引),在这种情况下是名为Object.toString的方法返回一个字符串)。
invokevirtual字节码指令会导致JVM将#11中的值不视为地址(不作为早期绑定的地址),而是在方法表中查找当前对象的方法的名称来获得相应的索引。
真正的执行逻辑为:在操作数栈获得对象的索引,然后调用该对象方法表中相同索引值的方法。
这就是它的工作原理!
此处
其中含有(CONSTANT_Class_info,CONSTANT_NameAndType_info ,CONSTANT_Utf8_info)
http://wpollock.com/Java/PolyMorphism.htm
有时,我们不清楚某些变量的类型应该是什么,直到我们运行该程序,因为它可能取决于用户输入,随机数或其他外部数据,如文件中的数据。
以上所说的普通方法即为:private、static标识的方法。
对于如下的代码:
import java.text.NumberFormat; public class PolymorphismDemo { public static void main(String[] args) throws Exception { if (args.length == 0) { System.out.println( "Usage: java PolymorphismDemo <some-number>"); return; } Number num = NumberFormat.getInstance().parse(args[0]); System.out.println("The number " + num.toString() + " has class " + num.getClass()); } }
创建的Number对象(在第10行)的实际类型取决于输入参数是一个整数(在这种情况下,它实际是一个Long对象)还是浮点数(在这种情况下,它实际是一个Double对象)。
在编译期间没有办法知道应该调用哪一个
toString方法! 它取决于变量num引用哪种类型的对象,可以是
Long.toString或
Double.toString。
在这个程序中,直到运行时间才知道:
1: C:\Temp>javac PolymorphismDemo.java 2: 3: C:\Temp>java PolymorphismDemo 123 4: The number 123 has class class java.lang.Long 5: 6: C:\Temp>java PolymorphismDemo 123.456 7: The number 123.456 has class class java.lang.Double
那么编译器可以做什么?
编译main方法时,不能将方法名toString(在第12行使用)绑定到一个具体地址。 相反,编译器会将绑定推迟到运行期间,通过使用字节码(invokevirtual)来查找正确的toString方法的地址。这就是为什么多态性也被称为晚期绑定,延迟绑定或动态绑定的原因。 (在某些语言中,如C++多态方法被称为虚方法或虚函数。)
没有多态性,这个程序会更复杂; 你必须使用一些自定义的方法来将
args[0]字符串转换为一个数字,这也会以某种方式返回一个类型名称。
然后您需要一个switch语句或一个if表达式来测试类型名称并自己调用正确的方法。(即,
Integer.toString(x)或
Double.toString(x))。
这种代码非常难看,很容易被破坏,难以维护或增强。 这是Java支持多态方法的主要原因。
多态是如何工作的?
对象的多态方法的地址存储在对象的方法表中。当在运行期间调用一些多态方法时,在这个表中查找方法名称来获取相应的地址。
方法表包含对象的动态绑定(多态)方法的名称和地址。 对于属于同一个类的所有对象,方法表是相同的,所以它们作为该类的元数据存储在方法区中。
方法表不是语言的一部分,只要最终结果相同,不同的JVM供应商可以随意实现多态。
Sun JVM在对象的常量池中混合方法表项,可以使用命令
javap -verbose foo查看。 (因为同一个类的所有对象都有相同的方法表,所以JVM可以把它保留在别的地方—-方法区。)
这是说明性的,看看系统如何为某些类(如Integer)构造一个方法表。 最初方法表是空的。 然后方法表用最远的祖先类(通常是Object类)中的多态方法填充:
Integer Method Table part 1
Method Name | Address | Comment |
---|---|---|
Object.toString | 111 | Object.toString method address |
… | … | 9 Additional methods |
如果您查看API(JavaDocs),您会发现Number类不会覆盖任何方法,但会在表中添加六个新的方法。 所以方法表中的toString条目保持不变:
Integer Method Table part 2
Method Name | Address | Comment |
---|---|---|
Object.toString | 111 | Object.toString method address |
Number.intValue | 222 | Number.intValue method address |
… | … | 15 Additional methods |
Integer Method Table part 3
Method Name | Address | Comment |
---|---|---|
Object.toString | 333 | Integer.toString method address |
Number.intValue | 444 | Integer.intValue method address |
Integer.parseInt | 555 | Integer.parseInt method address |
… | … | Additional methods |
这就是为什么javap输出将多态方法名称显示为Object.toString,而不仅仅是toString或Integer.toString。
看看这个方法调用的字节码(使用
javap -verbose PolymorphismDemo),其内容如下:
40: invokevirtual #11; //Method java/lang/Object.toString:()Ljava/lang/String;
“#11”是指方法表中的方法名称(如上所述,这是常量池的索引),在这种情况下是名为Object.toString的方法返回一个字符串)。
invokevirtual字节码指令会导致JVM将#11中的值不视为地址(不作为早期绑定的地址),而是在方法表中查找当前对象的方法的名称来获得相应的索引。
真正的执行逻辑为:在操作数栈获得对象的索引,然后调用该对象方法表中相同索引值的方法。
这就是它的工作原理!
此处
#11指的是常量池中的CONSTANT_Methodref_info
其中含有(CONSTANT_Class_info,CONSTANT_NameAndType_info ,CONSTANT_Utf8_info)
http://wpollock.com/Java/PolyMorphism.htm
相关文章推荐
- java后台实现生成二维码并且上传的详细介绍
- JAVA知识回顾-5(Interface与abstract类的介绍、Static class与not static class的区别、java多态的实现原理、Thread与Runable)
- java枚举使用详细介绍及实现
- 本文详细介绍用Java实现基于XML的购物车 stringcart
- java 中继承和多态详细介绍
- Java语言入门级的十二大特色详细介绍
- Java语言入门级的十二大特色详细介绍
- Java语言入门级的十二大特色详细介绍 转自天极
- Java语言入门级的十二大特色详细介绍
- Java IO详细介绍
- Java语言入门级的十二大特色详细介绍
- Java中针对多态的实现
- Java基础-Java语言的Socket类详细介绍-Java基础-Java-编程开发
- 多态的运用 实现java 数据类型判断
- Java编程高手对CLASSPATH应用的详细介绍
- 大家来讨论下Java多态的实现吧
- 详细介绍什么是实时JAVA
- 【导读】本文介绍如何利用带进度条的ASP无组件实现断点续传下载,给出详细代码
- Java的Override实现多态
- Java语言入门级的十二大特色详细介绍