关于多态性的动态绑定问题, 对象调用方法的执行过程.
关于多态性的动态绑定问题。对象调用方法的执行过程! 虽有钻牛角尖的嫌疑,但你一定会发现值得你发现的东西!! 虚拟机预先为每个类创建一个表,列出所有的方法签名和实际调用的方法: 对象调用方法的执行过程 1.首先,编译器察看调用方法的对象的声明类型和方法名,如果声明类型为Private、statie、final 构造器,则静态绑定,找出并调用方法。
关于多态性的动态绑定问题。对象调用方法的执行过程!
虽有钻牛角尖的嫌疑,但你一定会发现值得你发现的东西!!
虚拟机预先为每个类创建一个表,列出所有的方法签名和实际调用的方法:
对象调用方法的执行过程
1.首先,编译器察看调用方法的对象的声明类型和方法名,如果声明类型为Private、statie、final 构造器,则静态绑定,找出并调用方法。
2.否则编译器列出所有方法和继承为public的方法。
3.如果重载的话,编译器察看调用方法时的方法参数类型,发生重载解析。最后将获得被调用方法的方法签名(方法名+方法参数列表)。
4.如果被调用方法没重写,就直接搜索定义该方法签名的类的方法表,调用该方法。
5.如果被重写意味着这个方法可能是超类的方法也可能是子类的方法。虚拟机会从子类到超类搜索定义该方法签名的类,调用与调用该方法的对象的实际类型相匹配的类的方法表,找出并调用方法。
6.如果是使用super将会只是编译器调用超类的方法,搜索超类的方法表,找出并调用。
下面是社区jingweixml()以前对此发的意见。
编译和运行不管在哪种语言和哪类操作系统上都一样。问题的实质是:编译过程是编译器的事,与运行无关;而运行是操作系统的事,与编译无关。比如你问的“想知道编译器在进行对象调用方法的执行过程?”,这个问题本来就不对,编译器只是编译,调用是操作系统的事(对java是虚拟机的事),编译器编译时它顶多只关注下要静态绑定代码模块(函数或数据)还是动态绑定,静态绑定的意思是编译时它就把需要访问的变量和函数地址安排好了,当然开始是相对逻辑地址,待系统平台把程序代码搬到内存上运行时才把这些地址转换成实际的物理地址;而动态绑定就是编译时没有事先安排好要访问的变量或函数的实际地址位置,而是插入一些信息,这些信息一般是一些表,在程序运行时先调用这些表,再根据表里的指示信息去进一步调用相关对象(数据或函数),所谓面向对象的多态性或是所谓的虚函数实际就是这样解决的。
这里再想说下你提到的类的问题,类的定义实际只是语言层面的概念,即在语言环境里才有效,一旦编译成代码,类的定义就消失了,剩下的只是赤裸的变量和函数了,对于同一类里的变量和函数,编译时编译器就把它们做成相互调用的关系,比如某变量就变成了某函数的参数等,因此在源代码里我们常可写成直接去访问一个对象的变量,实际编译后就变成了调用这个变量的函数了,或者通过这个变量携带的一些信息表进一步指示去调用相关的函数,总之最后还是得通过调用函数才会起实际的作用。
最后你问的什么时候进行静态绑定,以上说很清楚了,就是在编译时就搞定。什么时候发生重载解析?那一定是运行的时候了。我想大致就这么回事吧,这也是本人的小见,请高手进一步指正。
另外以上说的编译和运行无关只是相对而言,实际编译软件的开发肯定是要根据其所基于的操作系统来开发的,实际是肯定要根据操作系统所能提供的API功能来做,而不可能超越它;如果操作系统是基于组件的模型的(如Windows的com及java系统的组件体系等),那你要想基于其上运行,开发语言就得依照它的组件规则来开发。另外需要说明的是,如果系统平台天生具备面向对象特性的,那我们进行语言编译器开发时就可直接利用它的特性,编译器需要包含的功能就少些,否则编译器就相对复杂,因为这意味着要在开发层面上添加许多支持面向对象的特性,这是没办法的事,因为操作系统不支持啊。
另外想说下关于有提到的构造函数问题。类也是数据类型,本质上声明一个类跟声明一个基本数据类型是一样的,如"class 类名"与"int 变量名"是一样的,都是为变量申请空间或是赋值,但类型发展到类时我们就喜欢在类里放入一些构造和析构函数,这就不得不涉及动态和静态创建(实际是申请)对象(实际是内存空间)问题,为什么要动态和静态的问题,这都是出于节省内存空间,也许"int a"需要内存空间小,所以很少人去想怎么动态创建和销毁它的问题,但类的体积可大可小,大的可以很大,如果不考虑动态创建和销毁问题,一个程序运行下来可能占用的内存空间就很大,所以在后来的编译器上就加入了动态创建和销毁对象的机制,这一机制就是“构造/析构函数”机制。实际上如果你不想用这些机制也行,那你就给类做静态连编吧,这样在编译时就直接创建对象空间插入二进制代码中,作为程序不可分离的一部分,如此而已。
更多推荐
所有评论(0)