写在前边

  • 上次聊到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. windows 中cmd一些特殊命令

    chcp 65001  就是换成UTF-8代码页 chcp 936 可以换回默认的GBK chcp 437 是美国英语 shutdown -s -t 60   60秒后关机 shutdown /a  ...

  2. 剑指offer计划25(模拟中等)---java

    1.1.题目1 剑指 Offer 29. 顺时针打印矩阵 1.2.解法 常规开头,先判断特殊情况,然后创建四个变量存放矩阵四边的长度限制. 创建res数组存放结果. 循坏开始,遍历完一行或者一列,就将 ...

  3. python序列的修改、散列和切片

    新Vector类 接原vector类定义的新Vector类,原向量类是二维,现定义多维向量类: from array import array import reprlib import math c ...

  4. 定要过python二级 第三套

    第一模块   基础操作(共三道题) 1. 安装python 包  我在c  盘打开  但是它给我安装到了d盘得  anaconda3 下面  关键是  我在c盘  打开python .exe   创建 ...

  5. 鸿蒙内核源码分析(索引节点篇) | 谁是文件系统最重要的概念 | 百篇博客分析OpenHarmony源码 | v64.01

    百篇博客系列篇.本篇为: v64.xx 鸿蒙内核源码分析(索引节点篇) | 谁是文件系统最重要的概念 | 51.c.h.o 文件系统相关篇为: v62.xx 鸿蒙内核源码分析(文件概念篇) | 为什么 ...

  6. Anaconda和canda简介及区别

    Anaconda简介: 1.是一个开源的Python发行版本,其包含了conda.Python等软件包,numpy,pandas(数据分析),scipy等科学计算包,而无需再单独下载配置. 可以在同一 ...

  7. 基于注解实现jackson动态JsonProperty

    基于注解实现jackson动态JsonProperty @JsonProperty 此注解用于属性上,作用是把该属性的名称序列化为另外一个名称,如把trueName属性序列化为name,但是值是固定的 ...

  8. 『Mivik的萌新赛 & Chino的比赛 2020』T2 题解 Galgame

    如果这是我最后一篇题解,请每年为我上坟. Galgame 题目传送门 Decription as_lky 搞到了很多 Galgame(真的很多!).一款 Galgame 可以被描述为很多场景(Scen ...

  9. bzoj4094 && luogu3097 最优挤奶

    题目大意: 给定n个点排成一排,每个点有一个点权,有m次修改,每次改变某个点的点权并将最大点独立集计入答案,输出最终的答案 其中\(n\le 40000\ , \ m\le 50000\) QWQ说实 ...

  10. 2020.10.10--pta阶梯赛练习2补题

    7-3.N个数求和 本题的要求很简单,就是求N个数字的和.麻烦的是,这些数字是以有理数分子/分母的形式给出的,你输出的和也必须是有理数的形式. 输入格式: 输入第一行给出一个正整数N(≤100).随后 ...