锁定老帖子 主题:区别类方法、实例方法和字段的调用
精华帖 (0) :: 良好帖 (0) :: 新手帖 (2) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2009-04-16
父类定义: public class Parent{ public static String name = "父类的名字,静态常量"; public final int age = 40; public String birth = "父类生日,变量"; public static void sayHello(){ System.out.println("父类的类方法,Hello Parent~"); } public void sayBye(){ System.out.println("父类的实例方法,Bye Parent~"); } } 子类定义: public class Son extends Parent{ public static String name = "子类名字,静态变量"; public final int age = 20; public String birth = "子类生日,变量"; public static void sayHello(){ System.out.println("子类的类方法,Hello Child!"); } public void sayBye(){ System.out.println("子类的实例方法,Bye Child!"); } public static void main(String[] args){ //1 Parent obj1 = new Son(); System.out.println(obj1.name); System.out.println(obj1.age); System.out.println(obj1.birth); obj1.sayHello(); obj1.sayBye(); } } 输出结果: 引用 父类的名字,静态常量 40 父类生日,变量 父类的类方法,Hello Parent~ 子类的实例方法,Bye Child 主函数中声明了对Parent的引用obj1,这个引用指向Son的对象,即实际对象是利用new Son()所创建的实例。 上例中,obj1对调用了Parent类的静态方法sayHello(),而调用了Son类的实例方法sayHello()。对类方法和实例方法调用的区别,《深入Java虚拟机》中有这样一段话可以解释: 引用 当JVM调用一个类方法时,它会基于对象引用的类型(通常在编译时可知)来选择所调用的方法。相反,当JVM调用一个实例方法是,它会基于实际对象的类(只能在运行时得知)来选择所调用的方法。
对象引用obj1的类型Parent自加载进入JVM之后便在内存的方法区维护了静态方法,而Son类的实例方法sayBye()是在被实际对象调用的时候才动态连接的。 基于上面分析可以推测,因为类在方法区中还维护了字段信息、类(静态)变量、常量池,JVM调用一个字段时,它将基于对象引用的类型来选择所调用的字段,故obj1调用的字段皆来自于父类Parent。 注:常量池:常量池就是该类型所用常量的一个有序集合,包括直接常量(string、integer和floating point常量)和对其他类型、字段和方法的符号引用。池中的数据项就像数组一样是通过索引访问的,因为常量池存储了相应类型所用到的所有类型、字段和方法的符号引用,所以他在java程序的动态连接性中起到核心作用。 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2009-04-19
最后修改:2009-04-19
我的理解大致分为2大机制:
1,静态绑定:这种一般只跟类走 static方法,一般方法 2,动态绑定:这个得看指针指向了(C++可能复杂点) 虚拟方法 引用 关于“静态绑定”和“动态绑定” virtual就产生了“动态绑定”的机制。 普通方法是跟类走的(静态绑定,NEW 对象的时候并不临时生成指针,而在类定义的时候就已经编译好了函数地址),而虚拟方法是“动态绑定”,这种机制就需要动态指定函数地址,通过什么?就通过上面说的“虚表”和指向“虚表的指针”。 再看看你的代码 Parent obj1 = new Son(); //一个本指向一个子类对象的句柄强行转换为Parent obj1.sayHello(); //static被定义在类栈的静态区域(它是在加载JVM时候已经编译了),所以指向父类栈中 obj1.sayBye(); //而一般方法不会预编译,但他跟类定义跑,在new Son()的时候的动态连接Son类栈中的sayBye()方法。 引用 Son类的实例方法sayBye()是在被实际对象调用的时候才动态连接的。 同意,不过只是连接,应该并非编译了方法栈地址,而这个操作应该是在类定义的时候编译的。 上面是自己写的总结,我不知道我的理解是否到位 |
|
返回顶楼 | |
发表时间:2009-04-19
最后修改:2009-04-19
C_J 写道 Parent obj1 = new Son(); //一个本指向一个子类对象的句柄强行转换为Parent obj1.sayHello(); //static被定义在类栈的静态区域(它是在加载JVM时候已经编译了),所以指向父类栈中 obj1.sayBye(); //而一般方法不会预编译,但他跟类定义跑,在new Son()的时候的动态连接Son类栈中的sayBye()方法。 同意,不过只是连接,应该并非编译了方法栈地址,而这个操作应该是在类定义的时候编译的。 恩,根据引用所指对象的类型信息来动态调用方法,这应该就是所谓连接吧。 我的理解是,当Parent类被加载进入JVM的时候,静态方法的地址绑定到类的方法表中,静态字段name作为类变量、常量age和birth作为编译时常量存入方法区,编译之后他们的地址和值不再改变,故曰静态绑定。 而引用是以指向对象的指针存在的,在执行期间,父类的引用有可能指向不同子类的实例方法(也就是多态),这种在执行期间才确定具体方法的机制叫动态绑定。 不过,不知楼上所讲虚拟方法指的是什么呢? |
|
返回顶楼 | |
发表时间:2009-04-19
我说的虚拟方法是指
virtual(C++) abstract(JAVA) 不过 ,你说到“引用”,我一直归纳为“指针”,但指针这个词总有点变扭,我暂称“句柄” 涉及到“多态”,我想势必就会涉及到虚拟方法,我不知道JAVA有没有像C++那种“虚表”的 机制。 另外static方法与一般方法存储的类栈区域肯定是有区别的,你不能笼统的归纳到 引用 “类的方法表” 中。
|
|
返回顶楼 | |
发表时间:2009-04-19
1、Java中开发人员可操作的只有引用,不能直接操作指针,但是在底层实现引用的却是指针:JVM会将指向已加载类的引用由“符号引用(该类的全限名称)”替换为指针。类似的,底层对方法的调用也用到指针。
2、Java对多态的实现是依靠跟C++中“虚表”类似的方法表。同样的,方法表中数组元素是指向超类、接口的方法的指针,也通过偏移量来,但是由于Java编译期间的方法只是用符号代替的,故只有运行时方法被第一次调用之后,方法表中的元素才能够获得所指向的方法地址,进而指向它。 3、类方法与实例方法的存储是否有区别我也还不太明了,需要进一步研究。望与楼上共同进步~ |
|
返回顶楼 | |
发表时间:2009-04-20
最后修改:2009-04-20
引用 Java中开发人员可操作的只有引用
这句话有什么根据么? 我所知道C++说引用的特性是,他是对象地址(当然还包括基本数据类型)的副本,而这个副本,我的理解是竟然是副本,就没有去更改的理由,因为你改的不是“原件”。 对JAVA中的可变类,我理解的是直接操作指针的,如参数传递进去的可变类对象,是可以直接被更改的。 对于不可变类,上述更改就无效了,因为不可变类操作的是指针的副本,也就是“引用”了。 当然,大家共勉,共同进步! |
|
返回顶楼 | |
发表时间:2009-04-20
最后修改:2009-04-20
Java中开发人员不可以操作指针这是大学课程中就讲过的。引用在JVM中直接就是指向对象的指针,而不是你所理解的C++中对象的副本。正因为此,通过引用是可以找到对象并其做更改。
关于参数传递:如果对象作为参数传递给方法,则传递的是引用的“拷贝”,但是利用该拷贝调用方法时,却调用了所指向的实际对象的方法,因此可以更改对象,详细信息请参考:http://www.iteye.com/topic/4189# 个人感觉C_J总是根据C++经验去理解Java,Java中很多事情是没那么复杂的。比如 对JAVA中的可变类,我理解的是直接操作指针的,如参数传递进去的可变类对象,是可以直接被更改的。 对于不可变类,上述更改就无效了,因为不可变类操作的是指针的副本,也就是“引用”了。 java中可变类和不可变类通过访问控制修饰符final来实现的,参考这里 |
|
返回顶楼 | |
发表时间:2009-04-22
最后修改:2009-04-22
我觉得我们沟通还是没那么充分
引用 public void call(Test t) {
Test t2 = new Test(); t2.setName("cba'); t.setName("abc"); t = t2 ; } public static void main(String[] arg) { Test obj = new Test(); call (obj) ; System.out.println("obj"+obj.getName()); } 这位大哥的Test没有告诉我是不可变类和可变类(暂认为是可变类) 所以操作的是指针,传递的也可以认为是指针 竟然是可变类,所以 t.setName("abc"); 是行得通的,因为他们指向的是同一个地址。 但还是用C++的方式来理解的话 t和obj都是“指针”(目前产生了2个指针),目前指向了同一个地址 然后 t=t2,这里只改变了 t 这个“指针”,并没有改变“指针内容”,所以作者说 并没有影响“obj”,所以打出的是abc 其实final就有点“引用”的味道了。用C++的指针概念来理解JAVA对象也不错嘛 哎,其实是经历了一次惨痛的面试!! |
|
返回顶楼 | |
发表时间:2009-04-22
首先希望共患难的兄弟能够尽早振作起来,找工作十有八九不如意,心态要好才能越来越坚强,祝早日找到满意的工作!
这个问题不在于Test是可变类还是不可变的,而是关于以类实例为参数的方法倒底传入了什么!在java中,传入的实际是引用的拷贝,类似于你以前说的“副本”,这就意味着call()方法里的t“复制”了obj,和obj指向Test的同一个实例(也就是实际对象)。但同时,我在贴子最开始有个引用: 引用 当JVM调用一个类方法时,它会基于对象引用的类型(通常在编译时可知)来选择所调用的方法。相反,当JVM调用一个实例方法是,它会基于实际对象的类(只能在运行时得知)来选择所调用的方法。
这样看,无论是obj.setName("abc");还是t.setName("abc");其实都调用了实际对象的那个实例方法。但是t=t2;这句便只是让t具有了指向t2的指针。 |
|
返回顶楼 | |
发表时间:2009-04-22
哈哈 首先感谢你的祝福
引用 这个问题不在于Test是可变类还是不可变的
不同意 你可以试试传个 String 对象进去 引用 这就意味着call()方法里的t“复制”了obj,和obj指向Test的同一个实例(也就是实际对象)。
同意 这里的复制,不管在C++也好,JAVA也好都是“参数拷贝”。这句话咱达成了共识。 引用 无论是obj.setName("abc");还是t.setName("abc");其实都调用了实际对象的那个实例方法
同意 因为都指向了同一个地址 |
|
返回顶楼 | |