热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Java堆、栈、常量池和值传递、引用传递详解

先不要太关注参数到底是值传递还是引用传递,抛开这个想法,先搞清楚Java中值、对象、对象的引用是怎么存储的?栈:存放8种基本数据类型的变量和对象的引用(对象的引用保存的只是对象本身

先不要太关注参数到底是值传递还是引用传递,抛开这个想法,先搞清楚Java中值、对象、对象的引用是怎么存储的?


  • 栈:存放8种基本数据类型的变量和对象的引用(对象的引用保存的只是对象本身的地址),对象本身不存放在栈中,而是存放在堆和常量池中。
  • 堆:存放所有new出来的对象或数组。JVM不定时查看堆中的对象,如果没有引用指向这个对象就回收。
  • 常量池:存放字符串常量和基本类型常量(public static final)。

一、基本数据类型

基本数据类型的变量和常量:变量和引用存储在栈中,常量存储在常量池中。
以int为例:

int m1 = 7;  
int m2 = 7;
public static final int m3 = 7;
public static final int m4 = 7;

这里写图片描述
基本数据类型跟堆没有任何关系。


二、数组,字符串和其他引用类型

下面的例子对于数组,字符串和其他引用类型都是一样的,只是用字符串举例。
以String为例:
对于字符串:其对象的引用都是存储在栈中,对象本身如果是编译期已经创建好(直接用双引号赋值的:String s1 = “mistra1”; )的就存储在常量池中,如果是运行期(new出来的)就存储在堆中。对于equals相等的字符串,在常量池中永远只有一份,在堆中有多份。

String m1 = "mistra";  
String m2 = "mistra";
String m3 = new String("mistra");
String m4 = new String("mistra");

这里写图片描述
String m1 = “mistra”; ,这行代码被执行的时候,JVM首先在常量池中查找是否已经存在了值为”mistra”的这么一个对象,它的判断依据是String类equals(Object obj)方法的返回值。如果有,则不再创建新的对象,直接返回已存在对象的引用;如果没有,则先创建这个对象,然后把它加入到字符串池中,再将它的引用返回。

通过new产生一个字符串(假设为”mistra”)时,会先去常量池中查找是否已经有了”mistra”对象,如果没有则在常量池中创建一个此字符串对象,然后在堆中再创建一个常量池中此”mistra”对象的拷贝对象。
String s = new String(“mistra”);产生几个对象?一个或两个,如果常量池中原来没有”mistra”,就是两个。

  • 对于成员变量和局部变量:成员变量就是在方法外部,类的内部定义的变量;局部变量就是在方法或语句块内部定义的变量。局部变量必须初始化。 形式参数是局部变量,局部变量的数据存在于栈内存中。栈内存中的局部变量随着方法的消失而消失。 成员变量存储在堆中的对象里面,由垃圾回收器负责回收。

三、到底是值传递还是引用传递?

基本数据类型:值就直接保存在变量中。
引用类型:变量中保存的只是实际对象的地址。这里的变量就是对象的引用,引用指向实际对象,实际对象中保存着内容。

|>>>>>赋值运算符(=)的作用
赋值运算符对基本数据类型和引用类型的作用看下图就可以明白:
这里写图片描述
对于基本数据类型num ,赋值运算符会直接改变变量的值,原来的值被覆盖掉。
对于引用类型str,赋值运算符会改变引用中所保存的地址,原来的地址被覆盖掉。但是原来的对象(“mistra”)不会被改变。如上图所示,”mistra” 字符串对象没有被改变。(没有被任何引用所指向的对象是垃圾,会被垃圾回收器回收)

1、基本数据类型:
public class ParameterTransferTest1 {
public static void main(String[] args) {
int a = 3;
change(a);
System.out.println(a);
}
public static void change(int i) {
i =10;
}
}
输出结果:3

基本数据类型是值传递,传递的只是一个副本,与原值无关。 相当于把a的值3 copy给了 i,i 改变值并不会影响a的值。

2、没有提供改变自身方法的引用类型:

这里涉及到String与StringBuilder的知识点了。

public class ParameterTransferTest2 {
public static void main(String [] args){
String str = "a";
change(str);
System.out.println(str);
}
public static void change(String i) {
i = "b";
}
}
输出结果:a

每次改变String对象的值都是新创建了一个对象,原对象还在。而StringBuilder对象不同,每次改变都是在对象本身改变,不创建新对象。
假设str指向了地址x010,把这个地址引用传递给了 i,i 也指向x010,但是执行 i = “b”;之后,i 指向了新地址x011,并不影响str指向的地址,所以没有改变。

3、提供了改变自身方法的引用类型:
public class ParameterTransferTest3 {
public static void main(String [] args){
StringBuilder sb = new StringBuilder("a");
change(sb);
System.out.println(sb);
}
public static void change(StringBuilder i) {
i.append("b");
}
}
输出结果:ab

假设sb指向了地址x010,把这个地址引用传递给了 i,i 也指向x010,但是执行 i.append(“b”);之后,i 指向的地址不变,还是指向x010,所以影响了sb的值。


这里放几道例题加深理解:例题原博客地址

public class foo {
public static void main(String sgf[]) {
StringBuffer a=new StringBuffer(“A”);
StringBuffer b=new StringBuffer(“B”);
operate(a,b);
System.out.println(a+”.”+b);
}

static void operate(StringBuffer x,StringBuffer y) {
x.append(y);
y=x;
}
}

这里写图片描述

public class Example {    
String str = "abc";
char[] ch = { 'a', 'b', 'c' };
public static void main(String args[]) {
Example ex = new Example();
ex.change(ex.str, ex.ch);
System.out.println(ex.str);
System.out.println(ex.ch);
}
public void change(String str, char ch[]) {
*** // ch = new char[]{'a','b','c'};
str = "change";
ch[0] = 'c';
}
}
输出结果:
abc
cba

变量ch和引用传递是一样的,都是操作的为同一对象,如果有* * *的一行不注释的话,结果就为
abc
abc


推荐阅读
  • 本文介绍了如何在给定的有序字符序列中插入新字符,并保持序列的有序性。通过示例代码演示了插入过程,以及插入后的字符序列。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • Java中包装类的设计原因以及操作方法
    本文主要介绍了Java中设计包装类的原因以及操作方法。在Java中,除了对象类型,还有八大基本类型,为了将基本类型转换成对象,Java引入了包装类。文章通过介绍包装类的定义和实现,解答了为什么需要包装类的问题,并提供了简单易用的操作方法。通过本文的学习,读者可以更好地理解和应用Java中的包装类。 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 猜字母游戏
    猜字母游戏猜字母游戏——设计数据结构猜字母游戏——设计程序结构猜字母游戏——实现字母生成方法猜字母游戏——实现字母检测方法猜字母游戏——实现主方法1猜字母游戏——设计数据结构1.1 ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • Nginx使用(server参数配置)
    本文介绍了Nginx的使用,重点讲解了server参数配置,包括端口号、主机名、根目录等内容。同时,还介绍了Nginx的反向代理功能。 ... [详细]
  • JavaSE笔试题-接口、抽象类、多态等问题解答
    本文解答了JavaSE笔试题中关于接口、抽象类、多态等问题。包括Math类的取整数方法、接口是否可继承、抽象类是否可实现接口、抽象类是否可继承具体类、抽象类中是否可以有静态main方法等问题。同时介绍了面向对象的特征,以及Java中实现多态的机制。 ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • 本文介绍了一个Java猜拳小游戏的代码,通过使用Scanner类获取用户输入的拳的数字,并随机生成计算机的拳,然后判断胜负。该游戏可以选择剪刀、石头、布三种拳,通过比较两者的拳来决定胜负。 ... [详细]
author-avatar
sweeteenring
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有