论坛首页 入门技术论坛

区别类方法、实例方法和字段的调用

浏览 4852 次
精华帖 (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程序的动态连接性中起到核心作用。
   发表时间: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()是在被实际对象调用的时候才动态连接的。


同意,不过只是连接,应该并非编译了方法栈地址,而这个操作应该是在类定义的时候编译的。


上面是自己写的总结,我不知道我的理解是否到位
0 请登录后投票
   发表时间: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作为编译时常量存入方法区,编译之后他们的地址和值不再改变,故曰静态绑定。
而引用是以指向对象的指针存在的,在执行期间,父类的引用有可能指向不同子类的实例方法(也就是多态),这种在执行期间才确定具体方法的机制叫动态绑定。

不过,不知楼上所讲虚拟方法指的是什么呢?
0 请登录后投票
   发表时间:2009-04-19  
我说的虚拟方法是指

virtual(C++)   abstract(JAVA)

不过 ,你说到“引用”,我一直归纳为“指针”,但指针这个词总有点变扭,我暂称“句柄”


涉及到“多态”,我想势必就会涉及到虚拟方法,我不知道JAVA有没有像C++那种“虚表”的 机制。

另外static方法与一般方法存储的类栈区域肯定是有区别的,你不能笼统的归纳到
引用
“类的方法表”
中。
0 请登录后投票
   发表时间:2009-04-19  
1、Java中开发人员可操作的只有引用,不能直接操作指针,但是在底层实现引用的却是指针:JVM会将指向已加载类的引用由“符号引用(该类的全限名称)”替换为指针。类似的,底层对方法的调用也用到指针。
2、Java对多态的实现是依靠跟C++中“虚表”类似的方法表。同样的,方法表中数组元素是指向超类、接口的方法的指针,也通过偏移量来,但是由于Java编译期间的方法只是用符号代替的,故只有运行时方法被第一次调用之后,方法表中的元素才能够获得所指向的方法地址,进而指向它。
3、类方法与实例方法的存储是否有区别我也还不太明了,需要进一步研究。望与楼上共同进步~
0 请登录后投票
   发表时间:2009-04-20   最后修改:2009-04-20
引用
Java中开发人员可操作的只有引用


这句话有什么根据么? 我所知道C++说引用的特性是,他是对象地址(当然还包括基本数据类型)的副本,而这个副本,我的理解是竟然是副本,就没有去更改的理由,因为你改的不是“原件”。

   对JAVA中的可变类,我理解的是直接操作指针的,如参数传递进去的可变类对象,是可以直接被更改的。
   对于不可变类,上述更改就无效了,因为不可变类操作的是指针的副本,也就是“引用”了。

当然,大家共勉,共同进步!
0 请登录后投票
   发表时间:2009-04-20   最后修改:2009-04-20
Java中开发人员不可以操作指针这是大学课程中就讲过的。引用在JVM中直接就是指向对象的指针,而不是你所理解的C++中对象的副本。正因为此,通过引用是可以找到对象并其做更改。

关于参数传递:如果对象作为参数传递给方法,则传递的是引用的“拷贝”,但是利用该拷贝调用方法时,却调用了所指向的实际对象的方法,因此可以更改对象,详细信息请参考:http://www.iteye.com/topic/4189#

个人感觉C_J总是根据C++经验去理解Java,Java中很多事情是没那么复杂的。比如
对JAVA中的可变类,我理解的是直接操作指针的,如参数传递进去的可变类对象,是可以直接被更改的。
   对于不可变类,上述更改就无效了,因为不可变类操作的是指针的副本,也就是“引用”了。
java中可变类和不可变类通过访问控制修饰符final来实现的,参考这里
0 请登录后投票
   发表时间: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对象也不错嘛
哎,其实是经历了一次惨痛的面试!!
0 请登录后投票
   发表时间:2009-04-22  
首先希望共患难的兄弟能够尽早振作起来,找工作十有八九不如意,心态要好才能越来越坚强,祝早日找到满意的工作!

这个问题不在于Test是可变类还是不可变的,而是关于以类实例为参数的方法倒底传入了什么!在java中,传入的实际是引用的拷贝,类似于你以前说的“副本”,这就意味着call()方法里的t“复制”了obj,和obj指向Test的同一个实例(也就是实际对象)。但同时,我在贴子最开始有个引用:
引用
当JVM调用一个类方法时,它会基于对象引用的类型(通常在编译时可知)来选择所调用的方法。相反,当JVM调用一个实例方法是,它会基于实际对象的类(只能在运行时得知)来选择所调用的方法。

这样看,无论是obj.setName("abc");还是t.setName("abc");其实都调用了实际对象的那个实例方法。但是t=t2;这句便只是让t具有了指向t2的指针。
0 请登录后投票
   发表时间:2009-04-22  
哈哈  首先感谢你的祝福

引用
这个问题不在于Test是可变类还是不可变的

不同意
你可以试试传个
String 对象进去

引用
这就意味着call()方法里的t“复制”了obj,和obj指向Test的同一个实例(也就是实际对象)。

同意
这里的复制,不管在C++也好,JAVA也好都是“参数拷贝”。这句话咱达成了共识。

引用
无论是obj.setName("abc");还是t.setName("abc");其实都调用了实际对象的那个实例方法


同意
因为都指向了同一个地址


0 请登录后投票
论坛首页 入门技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics