java 方法 内存_Java中类,对象,方法的内存分配

以下针对引用数据类型:在内存中,类是静态的概念,它存在于内存中的CodeSegment中。当我们使用new关键字生成对象时,JVM根据类的代码,去堆内存中开辟一块控件,存放该对象,该对象拥有一些属性,拥有一些方法。但是同一个类的对象和对象之间并不是没有联系的,看下面的例子:

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 classStudent{2 staticString schoolName;3 String name;4 intage;5

6 voidspeak(String name){7 System.out.println(name);8 }9

10 voidread(){11 }12 }13 classTest{14 public statis voidmain(String[] args){15 Student a = newStudent();16 Student b = newStudent();17 }18 }

View Code

在上面的例子中,生成了两个Student对象,两个对象拥有各自的name和age属性,而schoolName属性由于是static的,被所有的对象所共有,而且每个对象都有权更改这个属性。而对于方法speak()和read()来讲,它们被所有的对象所共有,但并不是说哪个对象拥有这些方法。方法仅仅是一种逻辑片段而已,它不是实物,严格来讲不能被拥有。方法存在的意义就是对对象进行修改。(我的理解)上述speak()方法,虽然两个对象都拥有相同的方法,但是由于其操作的对象不同,所以执行起来的效果也不同。再说一次,方法是看不见摸不着的,它仅仅是一种逻辑操作!只有当作用于具体的对象时,方法才具体化了!方法在不执行的时候不占用内存空间,只有在执行的时候才会占用内存空间。就好比说一个人会翻跟斗,他翻跟斗的时候是需要空间的,但是他不翻跟斗的时候是不需要额外的空间的。但是不管他翻不翻跟斗,他始终是具有翻跟斗的技能的。

Java中的内存布局(其他面向对象的语言也是如此)

Java中的内存空间分为四种:1. code segment存储class文件的内容,即我们写的代码。2. data segment存储静态变量3. heap segment堆空间,存储new出来的对象4. stack segment栈空间,存储引用类型的引用(注意,这里存储的不一定是对象所处的物理地址,但是一定能够根据这个内容在堆中找到对应的对象),局部变量和方法执行时的方法的代码

以上面的speak(String name)方法的调用为例来分析下内存:调用该方法时,首先在栈控件开辟了一块区域存放name引用。然后将传入的那个对象的“地址”赋值给这个引用。于是出现了什么情况?两个引用指向同一个对象。而我们操作对象时是通过对象的引用来执行操作,所以当一个对象有一个以上的引用同时指向它时,就会出现一些比较混乱的事情了。

通过马士兵在课上讲的一个小例子来看看:

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 public classTest {2

3 public static voidmain(String[] args) {4 Test test = newTest();5 int data = 10;6 BirthDate b1 = new BirthDate(5,4,1993);7 BirthDate b2 = new BirthDate(25,5,1992);8

9 test.change(data);10 test.change1(b1);11 test.change2(b2);12 System.out.println(data);13 b1.display();14 b2.display();15 }16

17 void change(inti){18 i = 100;19 }20

21 voidchange1(BirthDate b){22 b = new BirthDate(1,1,1);23 }24

25 voidchange2(BirthDate b){26 b.setDay(100);27 }28 }29

30 classBirthDate{31 intday;32 intmonth;33 intyear;34

35 BirthDate(int day,int month,intyear){36 this.day =day;37 this.month =month;38 this.year =year;39 }40

41 public intgetDay() {42 returnday;43 }44 public void setDay(intday) {45 this.day =day;46 }47 public intgetMonth() {48 returnmonth;49 }50 public void setMonth(intmonth) {51 this.month =month;52 }53 public intgetYear() {54 returnyear;55 }56 public void setYear(intyear) {57 this.year =year;58 }59

60 public voiddisplay(){61 System.out.println("day = "+day+"\nmonth = "+month+"\nyear = "+year);62 }63 }

View Code

输出为:

10day= 5month= 4year= 1993day= 100month= 5year= 1992

从输出结果来看看发生了什么:1. 调用change(int i)此时在栈空间中新建了一个i,并把data的值复制给i。这个时候在栈空间中有两个int类型的数,二者的值虽然都为10,但是二者毫无关系,栈空间中有两个10.在方法体中对i进行赋值,该操作是对i进行的,并不影响data,所以当方法结束时data还是原来的data,连地址都没变一下。同时在方法结束时i自动从栈空间中消失。2. 调用change1(BirthDate b)此时在栈空间中新建一个引用b,在调用该方法的时候,将传进来的引用的值复制给b,即b和b1拥有相同的内容,指向同一个对象。在方法体中对b又进行赋值操作,首先在堆空间中new出一个新的对象,然后将b改为指向这个新的对象。该操作也未影响b1。方法结束后,引用b消失,刚才new出来的新对象成了垃圾,等待GC的回收。3. 调用change2(BirthDate b)同上,先在对中新建一个引用,名为b,然后将其指向b2所指向的对象。注意,此时调用了该对象的方法,修改了部分属性,所以此操作改变了这个对象,而b2也指向这个对象,所以最后b2的输出发生了变化。

从上面的三个方法可以看出,当方法中的参数列表不为空时:- 如果参数是引用数据类型,该方法执行的过程首先是创建若干个引用,然后将这些引用的值和传进来的引用的值一一对应复制。复制完之后,传进来的参数(引用)的工作也就完成了。- 如果参数是基本数据类型,那么首先在栈中创建对应数量个变量,将这些变量的值和传进来的参数一一对应复制。复制完之后也没有外面什么事了。

综上:传参时要注意,如果对传进去的参数(实际上是引用)进行了重新的赋值操作,那么该方法应该有一个返回值,否则该方法是没有意义的,如同上面的change1()。

方法一般有两个作用:1. 对某变量进行改变。 2. 根据传进来的参数返回另一变量。

如果一个方法不想有返回值,只是想对某变量进行改变,不要将该对象作为参数传进去,而直接在方法中获得其访问权限然后直接更改。方法的参数列表为空。

如果一个方法要有返回值,最好先在方法内部new一个临时变量,先将传进来的参数复制一下,逻辑执行完后,把临时变量return出去。

20170620更新:其实以上问题涉及到的东西是值传递与引用传递。在C++中二者都有,但是在Java中只有值传递。具体到实践中分两种情况:- 传递的是基本数据类型:其实传递的是值的拷贝。在方法中对值进行操作,并不影响传进去的那个值。如上面的change()方法,传值进去时只是按照data的样子重新创建了一个i,本质上data和i除了值相同以外,是两个独立的个体。

传递的是数组对象或者其他对象:实际上传递的是对象的引用,但是并不是把引用传过去,而是把引用复制过去。就像上面的change1()方法一样,其本质是将传参b1这个引用的值复制给引用b。b1和b除了值相同外,是两个独立的个体。但是由于二者值相同,所以指向了堆内存中的同一个对象,二者都可以用来操作对象。

总结一下:传值,传的都是栈中所储存的东西的拷贝。如果传进去的东西是基本数据类型,那么就直接复制一份,对其操作不影响原来的数据。如果传进去的是一个引用,那么其实也是复制一份,所以指向同一对象。当操作这个引用时,改变了这个引用所指向的对象,看起来会让人觉得当时传进去的是对象本身,不然怎么在方法中对其修改会改变原本的对象呢?其实这是个假象。时刻记住,传进函数的都是栈内存中的东西,堆内存的东西是不会被传进去的。而函数内部能不能改变原来对象的值,就要看你是不是保持了原来传进去的引用所指向的对象没变。

PS:才疏学浅,如有错误请指出,谢谢!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值