深入理解Java引用类型

深入理解Java引用类型

  在Java中类型可分为两大类:值类型与引用类型。值类型就是基本数据类型(如int ,double 等),而引用类型,是指除了基本的变量类型之外的所有类型(如通过 class 定义的类型)。所有的类型在内存中都会分配一定的存储空间(形参在使用的时候也会分配存储空间,方法调用完成之后,这块存储空间自动消失), 基本的变量类型只有一块存储空间(分配在stack中), 而引用类型有两块存储空间(一块在stack中,一块在heap中),在函数调用时Java是传值还是传引用,这个估计很多人至今都很糊涂,下面用图形与代码来解释:

 

  在上图中引用类型在传参时不是在heap中再分配一块内存来存变量c 所指向的A(),而是让a 指向同一个A 的实例,这就与C++ 中的指针一样,先声明指针变量a,b,c,d 在传参的时候让a 指向c所指向的内存,让 d 指向 b 所指向的内存。很明显Java中的引用与C++中的指针在原理上是相类似的,但记住Java没有指针,只有引用。下面再通过一些具体的代码来讨论引用:

 

1. 简单类型是按值传递的

Java 方法的参数是简单类型的时候,是按值传递的 (pass by value)。这一点我们可以通过一个简单的例子来说明:

package test;

 

public class Test {

   //交换两个变量的值

   public static void Swap(int a,int b){

      int c=a;

      a=b;

      b=c;

      System.out.println("a: "+a);

      System.out.println("b: "+b);

   }

  

   public static void main(String[] args){

      int c=10;

      int d=20;

      Swap(c,d);

      System.out.println("After Swap:");

      System.out.println("c: "+d);

      System.out.println("d: "+c);

   }

}

运行结果:

a: 20

b: 10

After Swap:

c: 20

d: 10

  不难看出,虽然在 Swap (a,b) 方法中改变了传进来的参数的值,但对这个参数源变量本身并没有影响,即对 main(String[]) 方法里的 a,b 变量没有影响。那说明,参数类型是简单类型的时候,是按值传递的。以参数形式传递简单类型的变量时,实际上是将参数的值作了一个拷贝传进方法函数的,那么在方法函数里再怎么改变其值,其结果都是只改变了拷贝的值,而不是源值。

 

2. 什么是引用

  Java 是传值还是传引用,问题主要出在对象的传递上,因为 Java 中简单类型没有引用。既然争论中提到了引用这个东西,为了搞清楚这个问题,我们必须要知道引用是什么。

  简单的说,引用其实就像是一个对象的名字或者别名 (alias),一个对象在内存中会请求一块空间来保存数据,根据对象的大小,它可能需要占用的空间大小也不等。访问对象的时候,我们不会直接是访问对象在内存中的数据,而是通过引用去访问。引用也是一种数据类型,我们可以把它想象为类似 C++ 语言中指针的东西,它指示了对象在内存中的地址——只不过我们不能够观察到这个地址究竟是什么。

如果我们定义了不止一个引用指向同一个对象,那么这些引用是不相同的,因为引用也是一种数据类型,需要一定的内存空间(stack,栈空间)来保存。但是它们的值是相同的,都指示同一个对象在内存(heap,堆空间)的中位置。比如:

      String a="This is a Text!";

      String b=a;

 

  通过上面的代码和图形示例不难看出,a 和 b 是不同的两个引用,我们使用了两个定义语句来定义它们。但它们的值是一样的,都指向同一个对象 "This is a Text!"。但要注意String 对象的值本身是不可更改的 (像 b = "World"; b = a; 这种情况不是改变了 "World" 这一对象的值,而是改变了它的引用 b 的值使之指向了另一个 String 对象 a)

 

  如图,开始b 的值为绿线所指向的“Word Two”,然后 b=a; 使 b 指向了红线所指向的”Word“.

 

这里我描述了两个要点:

(1) 引用是一种数据类型(保存在stack中),保存了对象在内存(heap,堆空间)中的地址,这种类型即不是我们平时所说的简单数据类型也不是类实例(对象);

(2) 不同的引用可能指向同一个对象,换句话说,一个对象可以有多个引用,即该类类型的变量。

 

3. 对象是如何传递的呢

  随着学习的深入,你也许会对对象的传递方式产生疑问,即对象究竟是“按值传递”还是“按引用传递”?

(1)认为是“按值传递”的:

package test;

 

public class Test {

      public static void Sample(int a){

         a+=20;

         System.out.println("a: "+a);

      }

     

      public static void main(String[] args){

         int b=10;

         Sample(b);

         System.out.println("b: "+b);

   }

}

运行结果:

a: 30

b: 10

    在这段代码里,修改变量 a 的值,不改变变量 b 的值,所以它是“值传递”。

(2)认为是“按引用传递”的:

package test;

 

public class Test {

      public static void Sample(StringBuffer a){

         a.append(" Changed ");

         System.out.println("a: "+a);

      }

     

      public static void main(String[] args){

         StringBuffer b=new StringBuffer("This is a test!");

         Sample(b);

         System.out.println("b: "+b);

   }

}

运行结果:

a: This is a test! Changed

b: This is a test! Changed

  在Sample(StringBuffer)这个函数中,修改了引用 a 的值,同时 b 的值也变化了,所以它是“按引用传递”的!

  那么对象(记住在Java中一切皆对象,无论是int a;还是String a;,这两个变量a都是对象)在传递的时候究竟是按什么方式传递的呢?其答案就只能是:即是按值传递也是按引用传递,但通常基本数据类型(如int,double等)我们认为其是“值传递”,而自定义数据类型(class)我们认为其是“引用传递”。

 

4. 正确看待传值还是传引用的问题

  要正确的看待这个问题必须要搞清楚为什么会有这样一个问题。

  实际上,问题来源于 C,而不是 Java。

  C 语言中有一种数据类型叫做指针,于是将一个数据作为参数传递给某个函数的时候,就有两种方式:传值,或是传指针。 在值传递时,修改函数中的变量值不会改变原有变量的值,但是通过指针却会改变。

    void Swap(int a,int b){ int c=a;a=b;b=c;}

    void Swap(int *a,int *b){ int c=*a;*a=*b;*b=c; }

    int c=10;

    int d=20;

    Swap(c,d);    //不改变 c , d 的值

    Swap(&c,&d);  //改变 c , d 的值

  许多的 C 程序员开始转向学习 Java,他们发现,使用类似 SwapValue(T,T)(当T 为值类型时) 的方法仍然不能改变通过参数传递进来的简单数据类型的值,但是如果T时一个引用类型时,则可能将其成员随意更改。于是他们觉得这很像是 C 语言中传值/传指针的问题。但是 Java 中没有指针,那么这个问题就演变成了传值/传引用的问题。可惜将这个问题放在 Java 中进行讨论并不恰当。

  讨论这样一个问题的最终目的只是为了搞清楚何种情况才能在方法函数中方便的更改参数的值并使之长期有效。

 

5. 如何实现类似 swap 的方法

  传值还是传引用的问题,到此已经算是解决了,但是我们仍然不能解决这样一个问题:如果我有两个 int型的变量 a 和 b,我想写一个方法来交换它们的值,应该怎么办?有很多方法,这里介绍一种简单的方法:

package test;

 

public class Test {

      public static void Swap(int[] a){

         int c=a[0];

         a[0]=a[1];

         a[1]=c;

      }

     

      public static void main(String[] args){

         int[] a=new int[2];

         a[0]=10;

         a[1]=20;

         Swap(a);

         System.out.println(a[0]);

         System.out.println(a[1]);

   }

}

  通过数组可以方便的实现值类型的数据源的交换,不过还有一种方法是将所有变量封装到一个类里面去,通过引用类型来实现。

转载于:https://www.cnblogs.com/SilentCode/p/4858790.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/358204.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

值得一谈的鸿蒙2.0,程序员们拿起你们手中的编译器撸一下hello world

一款“面向未来”、面向全场景(移动办公、运动健康、社交通信、媒体娱乐等)的分布式操作系统 。现已开源,名为OpenHarmony。 2019年8月9日,华为在HDC开发者大会上正式发布鸿蒙系统。 2020年9月10日,华为在HDC开发者大会上如约发布鸿蒙 2.0,并面向应用开发者发布Beta版本…

GitHack使用方法

ctf小白刚下载这个,不知道怎么用。现在会用啦就记录一下~ 下载地址:https://github.com/lijiejie/GitHack 下载下来后,通过cmd使用,如下图所示: 总结(命令): GitHack.py http://XXXX…

值得一谈的鸿蒙2.0,赶紧撸一下hello world

一款“面向未来”、面向全场景(移动办公、运动健康、社交通信、媒体娱乐等)的分布式操作系统 。现已开源,名为OpenHarmony。2019年8月9日,华为在HDC开发者大会上正式发布鸿蒙系统。2020年9月10日,华为在HDC开发者大会上…

【Jenkins】未授权访问漏洞

一、漏洞介绍 1 ) Jenkins介绍 Jenkins是一个基于Java开发的开源项目,可在Tomcat等流行的servlet容器中运行,也可以独立运行,其功能如下: 用于持续性、自动的构建/测试软件项目监控或跑一些定时任务监控外部调用执行的工作 2 …

解决谷歌浏览器F12打不开调试页面的问题。

最近应用着急上线,需要批量测试,发现F12不好使啦。 解决办法: 1.找到谷歌浏览器:设置-扩展程序-开发者模式。关闭。 2.刷新浏览器或者重启浏览器后,打开需要调试的页面,鼠标点一下地址栏,按F1…

还有另一个报告生成器?

如果您具有业务应用程序开发的经验,那么很可能会遇到要求该应用程序具有灵活的报告机制的需求。 我工作的公司主要专注于开发业务解决方案,而报告是必不可少的,的确,它必须包含我们开发的所有企业系统的方面。 为了在我们的系统中…

ARL资产侦察灯塔系统搭建及使用

ARL资产侦察灯塔系统搭建及使用 ARL(Asset Reconnaissance Lighthouse)资产侦查灯塔旨在快速发现并整理企业外网资产并为资产构建基础数据库,无需登录凭证或特殊访问即可主动发现并识别资产,让甲方安全团队或者渗透测试人员快速寻…

window电脑关闭自动更新的方法,妈妈再也不用担心我开机等电脑啦

公司的新电脑,每次关机开机都会更新,今天更新了3次,每次半小时,我忍不了,赶紧关闭自动更新。 1.winR打开面板输入services.msc,然后回车。 2.找到windows Update,右键找到 属性。 3.把启动类型改为“禁用”…

我是如何解决电脑连接WiFi提示无线适配器或访问点有问题

今天国庆后第一天上班,打开电脑一直连接不上我手机的热点,提示无线适配器或访问点有问题。可能是由于重启电脑自动更新系统的缘故,关闭电脑自动更新的办法请参考我的上一篇文章:window电脑关闭自动更新的方法 不说废话&#xff0…

如何使用git下载别人在github的代码?

今天东哥让我帮他们公司检查一下几十款产品的前端代码,我用新电脑作业了一下,简单分享git如何下载别人的代码。 完整教程请参考我上篇文章:——> Git教程学习总结 首先本地有git环境。打开git bash here。 查看一下版本 初始化git使…

AppScan的安装与使用

AppScan的安装与使用 实验环境:Win7 Win2k8 将软件包复制到这个目录下: 双击 复制到C盘: 复制到C盘的这个目录下,并替换: 然后到菜单栏中,双击查看: 这样就可以使用了: 使用App…

cmd黑窗口命令行模式进入指定盘符下的任意文件夹

通过使用命令,并加入参数 /d ,可以通过一个步骤,直接进入指定盘符下的任意文件夹 比如要进入C盘下的C:\Users\Pactera\Desktop\rtt_web目录,则输入cd /d C:\Users\Pactera\Desktop\rtt_web

接口测试工具 restlet client

参考: https://blog.csdn.net/sunrainamazing/article/details/81281382https://blog.csdn.net/zlp1992/article/details/76706017 文章目录 下载若 你能翻墙下载 可以去如下网址下载方式二 采用离线CRX的方式进行安装(我用的第二种方式) 安装其次 , 一…

attr()与setAttribute()的区别

先看红色标注的: 这里传过来的this是个元素节点,因此currentTr也得用获取节点的方式parentNode去获取,而不能写parent(),这是第一个需要注意的地方。 第二个问题,就是怎么给currentTr设置属性和值得问题。我一开始想当…

我是如何成功准备VUE项目之前的开发环境?

提前安装好node, 参考我上一篇文章--------> 不同node.js版本 1.从git上把代码拷贝下来。 git clone +项目地址。 2.cmd模式下安装npm。 npm install 3.启动服务,npm run serve 。这里需要注意的是serve千万不要打成server啦,不然容易报错,这里还有两个小技…

Tomcat样列目录session 操控漏洞

一、基本情况 漏洞级别:中危 漏洞类别:session操纵漏洞 二、问题描述 ApacheTomcat默认安装页面中存在examples样例目录。里面存放着Servlets、JSP、WebSocket的一些服务脚本和接口等样例。Servletsexamples服务样例下存在一个session的样例。该样例可…

Failed to compile. ./src/utils/request.js Module not found: Error: Can‘t resolve ‘util-merge‘ in ‘C

在webstorm运行vue的一个项目,访问主页报错: Failed to compile. ./src/utils/request.js Module not found: Error: Cant resolve util-merge in C:\Users\Pactera\Desktop\rtt_web\src\utils 检查了一下代码是缺少util-merge插件。可以在编译器上进…

发布:NetBeans IDE 8.1 Beta

NetBeans IDE 8.1 Beta已发布。 NetBeans IDE在为Java开发人员提供完整而全面的工具方面拥有超过15年的丰富经验。 在过去的几年中,NetBeans IDE已为JavaScript开发人员实现了同样的目标。 特别是,NetBeans IDE 8.1引入了用于Node.js应用程序开发的完整而…

域名DNS解析工具ping/nslookup/dig/host

常见 DNS 记录的类型 类型目的A地址记录,用来指定域名的 IPv4 地址,如果需要将域名指向一个 IP 地址,就需要添加 A 记录。AAAA用来指定主机名(或域名)对应的 IPv6 地址记录。CNAME如果需要将域名指向另一个域名,再由另一个域名提供…

RegularExpressionValidator 常用

RegularExpressionValidator 控件用于验证输入值是否匹配正则表达式指定的模式 属性: ControlToValidate"要验证的控件名称" ValidationExpression"验证规则" ErrorMessage"所要显示的错误信息" text“当验证失败时显示的消息。” Fo…