当前位置 > 主页 > 万和大讲堂 >


Java中按值传递和按引用传递问题

2015-06-15 14:08

    相信对很多初学Java的人来说这个问题还是很重要,因为不同于C,C++,Java明确说明取消了指针,因为指针往往是再带来方便的同时也是导致代码不安全的根源,而且还会使得程序变得非常复杂和难以理解。Java放弃指针只是在Java语言中没有明确的指针定义,实质上,每一个new语句返回的都是一个指针的引用。只不过在大多数时候Java不用关心如何操作这个“指针”,更不像在操作C++的指针那样胆战心惊,唯一要多注意的是在给函数传递对象的时候。所以,初学Java的人不必花费太多精力去了解,但是对于一个真正的Java程序员来说这个问题是基础。废话少说,直接进入正题:

 

  Java中是传值还是传引用呢 ?

 

  关于这个问题,就像光到底是波还是粒子的问题一样众说纷纭。但是我们首先要搞清楚一点:不管Java的参数类型是什么,一律传递参数的副本(引用thinking in Java 中的一句话:When you're passing primitives into a method, you get a distinct copy of the primitive.When you're passing a reference into a method, you get a copy of the reference)。南京Java培训机构

 

  Java中的变量分为以下两类:

 

  (1)基本类型变量(int,long,double,boolean,float, byte,char),对于这些变量,Java传递的是值的副本(与c++相同),即把自己赋值一份传递,即使自己的副本变了,自己也不会改变;

 

  (2)对象型变量。Java传递的是引用的副本(复制指向地址的指针),而不是自己实际值的副本。为啥这么做呢?原因很简单,因为对象类型是放在堆里边的(new一个对象的时候实际上是再堆中开辟出了一块内存空间存放对象,在栈中存放指向对象的指针),所以访问位于堆中的对象相对于基本类型数据来说比较慢;另一方面是因为对象类型本身就比较大,如果采用复制对象值的方法,不仅浪费内存而且速度比较慢。

 

  在thinking in Java 中解释是这样的:不管是基本类型还是对象类型,都是传值。这种说法是把引用的副本也看作了一种“值”,也能够说的过去,但是本人还是更赞成将按值传递和按引用传递分开比较好理解。南京Java培训机构

 

  注意:Java中String类型也是对象型变量,所以它传递的是引用的副本。String是一个非可变类,其传值还是传引用显得没什么区别。

 

  理论说完了,下面看几个例子:

 

  num1:传基本类型

 

  public class Test {

 

  public static void test(boolean test) {

 

  test = !test;

 

  System.out.println("In test(boolean):test = " + test);

 

  }

 

  public static void main(String args[]) {

 

  boolean test = true;

 

  System.out.println("Before test(boolean):test = " + test);

 

  test(test);

 

  System.out.println("After test(boolean):test = " + test);

 

  }

 

  }

 

  运行结果:

 

  Before test(boolean):test = true

 

  In test(boolean):test = false   //传进去的是boolean类型的副本,副本改变了但是原值不改变。南京Java培训机构

 

  After test(boolean):test = true

 

  num2:传对象类型

 

  public class Test {

 

  public static void test(StringBuffer str) {

 

  str.append(", World!");

 

  }

 

  public static void main(String args[]) {

 

  StringBuffer string = new StringBuffer("Hello");

 

  test(string);

 

  System.out.println(string);

 

  }

 

  }

 

  运行结果:Hello, World!   //通过引用副本找到地址,然后修改里边的值,即修改了对象。

 

  num3:关于传String

 

  public class Test {

 

  public static void test(String str) {

 

  str = "World";

 

  }

 

  public static void main(String args[]) {

 

  String string = "Hello";

 

  test(string);

 

  System.out.println(string);

 

  }

 

  }

 

  一眼看去输出结果应该是:World

 

  但是实际的运行结果却是:Hello,String不是按引用传递的吗?难道出问题了?

 

  查了一下,原因如下:

 

  执行str = “World”;这句的时候,系统会自动生成一个新的String对象,并把这个新对象的值设为“World”,然后将这个对像的引用赋值给str,str是原来String的副本,改变的是副本的值,那么就与原来的值没有关系了。当test函数执行完毕,str随之释放,原来的内存地址上内容没发生任何变化。因此输出hello,num2中str.append(", World!");就不一样了,StringBuffer是产生一块内存空间,相关的增、删、改操作都在其中进行,所以此句是在同一段内存上进行的,即修改了原值,str所指向的引用并没有变。 南京Java培训机构


最近开班 more>
  • Python基础班
  • Java全栈开发
  • 前端基础班
  • 软件测试
  • 云原生精英班
  • 云网预科班
  • CISP
  • HCIE-Datacom(HCIA,HCIP基础)
  • HCIP-Datacom(HCIA基础)
  • HCIA-Datacom(0基础)
  • HCIE-Datacom(HCIA,HCIP基础)
  • HCIP-Datacom(HCIA基础)
  • HCIA-Datacom(0基础)
  • OCM 12C
  • OCP 19C
  • RHCE 9.0
  • 随时开课
  • 12月26日
  • 随时开课
  • 12月4日
  • 12月16日
  • 12月9日
  • 12月21日
  • 1月6日
  • 12月30日
  • 12月16日
  • 12月21日
  • 12月7日
  • 12月7日
  • ——
  • 随时开课
  • 12月16日
    • 姓 名 :
    • 电 话 :
    • 课 程 :

技术交流群

  • Java大数据交流群560819979加入
  • Python技术交流群595083299加入
  • Oracle技术交流群595119011加入
  • Web前端技术交流群604697610加入
  • Huawei技术交流群482919361加入
  • Redhat技术交流群587875348加入
  • UI设计技术交流群511649801加入
  • Cisco技术交流群596886705加入
  • IT运维技术交流群605888381加入