写在前边

  • 上次聊到Java8新特性 lambda时,有小伙伴在评论区提及到了lambda对于局部变量的引用,补充着博客的时候,知识点一发散就有了这篇对于值传递还是引用传递的思考。关于这个问题为何会有如此多的误区,这篇就来破解ta!

果然知识网的发散是无止境的!

知识储备--堆和栈

  • 堆是指动态分配内存的一块区域,一般由程序员手动分配,比如 Java 中的 new、c里边的malloc。
  • 栈是编译器帮我们分配好的区域,一般用于存放函数的参数值,局部变量等

有关堆栈的相关知识在 迷途指针 中有所提及。

数据类型

Java中除了基本数据类型,其他的均是引用类型,包括数组等等。

基本数据类型和引用类型的区别

先看一下这两个变量的区别

void test1(){
int cnt = 0;
String str = new String("melo");
}

  1. cnt是基本类型,值就直接保存在变量中(存放在栈上)
  2. 而str是引用类型,变量中保存的只是实际对象的地址。一般称这种变量为"引用",引用指向实际对象,实际对象中保存着内容

比如我们创建了一个 Student student = new Student("Melo");

  • 在堆中开辟一块内存(真正的对象存放在堆上),其中保存了name等数据 , 而student只是保存了该对象的地址(存放在栈上)

当我们修改变量时

void test1(){
int cnt = 0;
cnt=1;
String str = new String("melo");
str="Melo";
}



对于基本类型 cnt,赋值运算符会直接改变变量的值,原来的值直接被覆盖掉了。

ta无依无靠,不像下边一样有房子可以住。

对于引用类型 str,赋值运算符只会改变引用中所保存的地址,虽然原来的地址被覆盖掉了,str指向了一个新的对象,但是原来的那个老对象没有发生变化,他还是老老实实待在原来的地方!!!

有学过c语言的同学应该很清楚,这里借助c语言中的“指针”打个比喻。

  • 引用类型str就相当于一个指针(旗子),插在了一个房子门口。现在给这个旗子挪个位置,只是让这个旗子放置在了另一个新的房子,原本的老房子还在那里,不会说因为你改变了旗子的位置,房子就塌了。

当然,原来那个房子没有旗子插着了,没有人住了。也不能总是放任ta在那占着空间,过段时间也许就会有人来把他给拆了回收了(JVM)。

这种没有地方引用到的对象就称为垃圾对象。

值传递

我们上次聊到lambda的时候,提及到了值传递,那里的拷贝副本,就是我们这里要说的值传递

  • 如果我们这里的方法块访问了外部的变量,而这个变量只是一个普通数据类型的话,相当于只是访问到了一份副本。当外部对这个变量进行修改时,lambda内部(只有副本)是无法感知到这个变量的修改的。

我们只是将实参传递给了方法的形参,将cnt值复制一份,赋值给形参val所以,函数内对形参的操作完全不会影响到实参真正存活的区域!而伴随着函数调用的结束,形参区域和其内的局部变量也会被释放。(方法栈的回收)

//基本类型的值传递
void unChange(int val) {
val = 100;
}
unChange(cnt); // cnt 并没有被改变

引用传递

实参传递给形参时,形参其实用的就是实参本身(而不再单纯只是拷贝一份副本出来了),当该形参变量被修改时,实参变量也会同步修改

Java中到底是引用传递还是值传递呢

内卷实例

//内卷
void involution(Student temp){
temp.setScore(100);
} public static void main(String[] args) {
Student student = new Student();
student.setName("Melo");
student.setScore(0);
System.out.println("躺平时的成绩->"+student.getScore());
new TestQuote().involution(student);
System.out.println("卷了几天后的成绩->"+student.getScore());
}

  • 这里看起来,好像符合我们引用传递的定义诶?

    • 对形参temp的修改,会反馈到外部实参student那里去?看起来操作的是同一个变量的样子?

反内卷实例

看下边这段"反内卷"的代码实例

//反内卷
void againInvolution(Student temp){
temp = new Student();
temp.setScore(100);
} public static void main(String[] args) {
Student student = new Student();
student.setName("Melo");
student.setScore(0);
System.out.println("企图内卷前的成绩->"+student.getScore());
new TestQuote().againInvolution(student);
System.out.println("遭受反内卷后的成绩->"+student.getScore());
}

  • 细心的同学可能发现了,我们这里多了一步操作 --> temp = new Student();

先给出答案吧,Java里边其实只有值传递!!!

  • 为什么这么说?

    其实我们这里的形参temp,只是拷贝了一份student的地址。可以理解为temp拷贝了这条指针,他也指向了student所指向的对象。

  • 也就是说,temp只是跟student同样指向了一个同一个对象而已,在第一个例子中,我们没有去重新修改temp的指向,所以会造成一种假象:我们对temp的修改似乎等价于对student的修改? 其实只是刚好两个指向了同一个对象而已!!

  • 而如果我们对temp重新赋值了呢, temp = new Student();

  • 对temp重新赋值后,此时temp就指向了另一个区域了,后续再对temp修改,根本不会影响原来的student指向的区域

    所以才会"反内卷"失败,跳出函数的时候,student所指向的对象成绩根本没有增长!!!

为什么会有误区呢?

  • 其实还是因为Java中数据类型的问题,基本数据类型看起来就像是值传递,而引用传递因为存放了地址,让我们能够访问到实参所指向的对象,容易让我们误以为我们的形参其实就等价于实参.

其他语言的引用

JS只有值传递,类似Java

指针传递(C语言)

注意指针传递跟引用传递是不一样的

  • 拿最老套的C语言手写swap来讲
#include <stdio.h>

void swap(int *a, int *b) {
int temp;
temp = *a;
*a = *b;
*b = temp;
} int main() {
int a = 5;
int b = 8;
//需要传递地址
swap(&a, &b);
printf("a = %d\n", a);
printf("b = %d", b);
}

引用传递(C++)

#include <iostream>

using namespace std;

int main()
{
//&标识符
void swap(int& x,int& y); int a = 5;
int b = 8; swap(a,b);
return 0;
} void swap(int& a,int& b){
int temp;
temp = a;
a = b;
b = temp;
}

总结

如果该语言没有&,@这种取地址的操作符,一般来说就只有值传递的。如js和java,

  • 而c,Pascal,go这些是可以传引用和传值的。

最后

  • 其实关于Java到底是引用传递还是值传递这个问题。我们只需要理解好本质就好了,通过上边的那两幅图,理解好本质才是关键,万变不离其宗。

你们不要再吵了! Java只有值传递..的更多相关文章

  1. java的值传递笔记

    1. 背景:开发小伙伴突然问我java是值传递还是引用传递,我说当然是值传递,只不过有时候传递一个对象时实际传递的是对象的地址值,所以让人容易产生一种引用传递的假象,貌似在李刚的疯狂java讲义有提到 ...

  2. java是值传递还是引用传递

    首先写一个简便的Employee,以便测试使用. class Employee { private String name; public Employee(String name) { this.n ...

  3. java面试3-对于java中值传递的理解(Hollis)

    这是根据Hollis的直面java内容习得(有兴趣的可以加他微信公众号) 对于初学者来说,要理解java中的值传递很难理解,为什么说java只有值传递?那引用传递呢? java中的错误理解: 错误理解 ...

  4. 堆栈详解 + 彻底理解Java的值传递和引用传递

    本文旨在用最通俗的语言讲述最枯燥的基本知识 学过Java基础的人都知道:值传递和引用传递是初次接触Java时的一个难点,有时候记得了语法却记不得怎么实际运用,有时候会的了运用却解释不出原理,而且坊间讨 ...

  5. Java只有值传递(Java值传递还是引用传递?)

    转载请注明原文地址:https://www.cnblogs.com/ygj0930/p/10830521.html 一:区分Java数据类型.变量类型 Java数据类型分两种:基本数据类型.引用类型. ...

  6. Java的值传递和引用传递的说法

    学过Java基础的人都知道:值传递和引用传递是初次接触Java时的一个难点,有时候记得了语法却记不得怎么实际运用,有时候会的了运用却解释不出原理,而且坊间讨论的话题又是充满争议:有的论坛帖子说Java ...

  7. Java 为值传递而不是引用传递

    ——reference Java is Pass by Value and Not Pass by Reference 其实这个问题是一个非常初级的问题,相关的概念初学者早已掌握,但是时间长了还是容易 ...

  8. Does Java pass by reference or pass by value?(Java是值传递还是引用传递) - 总结

    这个话题一直是Java程序员的一个热议话题,争论不断,但是不论是你百度搜也好还是去看官方的文档中所标明的也好,得到的都只有一个结论:Java只有值传递. 在这里就不贴代码细致解释了,让我们来看看一些论 ...

  9. Java是值传递还是引用传递?

    Java的值传递和引用传递在面试中一般都会都被涉及到,今天我们就来聊聊这个问题.这个问题一般是相对函数而言的,也就是Java中所说的方法参数,那么我们先来回顾一下在程序设计语言中有关参数传递给方法的两 ...

随机推荐

  1. java中避免集合死链调用

    目录 1. 前言 2. 场景 3. 环境 3.1 开发环境准备 3.2 数据准备 3.2.1 Mysql数据库表及数据 3.2.2 redis库数据 4. 解决方式 5.完整代码 5.1Model 5 ...

  2. Docker系列(12)- 部署Tomcat

    #官方的使用:我们之前的启动都是后台,停止容器后,容器还是可以看到#docker run -it --rm,一般用来测试,用完就会删除容器,镜像还在[root@localhost ~]# docker ...

  3. 项目部署(ubuntu+uwsgi+nginx+supervisor+django)

    一.在开发机上的准备工作 1. 确认项目没有bug. 2.设置`ALLOW_HOST`为你的域名,以及ip地址. 4.设置`DEBUG=False`,避免如果你的网站产生错误,而将错误信息暴漏给用户. ...

  4. CF280D-k-Maximum Subsequence Sum【模拟费用流,线段树】

    正题 题目链接:https://www.luogu.com.cn/problem/CF280D 题目大意 一个长度为\(n\)的序列,\(m\)次操作 修改一个数 询问一个区间中选出\(k\)段不交子 ...

  5. 深入浅出WPF-12.绘图与动画

    绘图 1)Brush(画刷) SolidColorBrush实心画刷,直接使用颜色赋值 LinearGradientBrush线性渐变画刷,色彩沿设定的直线方向.按设定的变化点进行渐变 RadialG ...

  6. 一、Ansible基础之入门篇

    目录 1. Ansible基础 1.1 介绍 1.2 工作原理 1.3 如何安装 1.3.1 先决条件 1.3.2 安装Ansible 1.4 管理节点与被管理节点建立SSH信任关系 1.5 快速入门 ...

  7. springcloud组件之注册中心eureka学习

    eureka的高可用 微服务架构中最核心的部分是服务治理,服务治理最基础的组件是注册中心.随着微服务架构的发展,出现了很多微服务架构的解决方案,其中包括我们熟知的Dubbo和Spring Cloud. ...

  8. 测试rac数据文件建本地及处理

    模拟用户zytuser的表空间ZYTUSER_TBS表空间添加数据文件到本地.--环境准备1.创建一个表空间--创建表空间create tablespace ZYTUSER_TBS datafile ...

  9. BurpSuite 功能概览

    简介 写作思想:相比较具体介绍某个功能的用法.会更加侧重于介绍 Burp 提供哪些功能.这样好处是在比较复杂的测试场景,如果Burp 刚好提供对应的功能,就不用花费精力造轮子了. 而需要掌握具体操作方 ...

  10. Idea热部署功能

    什么是Idea自动热部署? 热部署是指代码改动之后,调试过程中会服务自动重启,减少手动重启的麻烦,尤其是在微服务开发中,涉及到很多模块的调试更为重要. 如何开启热部署功能? 1.添加如下依赖到项目模块 ...