深刻理解Java引用类型

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

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

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 变量没有影响。那说明,参数类型是简单类型的时候,是按值传递的。以参数形式传递简单类型的变量时,其实是将参数的值做了一个拷贝传进方法函数的,那么在方法函数里再怎么改变其值,其结果都是只改变了拷贝的值,而不是源值。算法

  1. 什么是引用

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

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

若是咱们定义了不止一个引用指向同一个对象,那么这些引用是不相同的,由于引用也是一种数据类型,须要必定的内存空间(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)。
在这里插入图片描述app

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

这里我描述了两个要点:

(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);

   }

}./*欢迎加入java交流Q君样:909038429一块儿吹水聊天

运行结果:

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]);

   }

}

  经过数组能够方便的实现值类型的数据源的交换,不过还有一种方法是将全部变量封装到一个类里面去,经过引用类型来实现。
image
最新2020整理收集的一些高频面试题(都整理成文档),有不少干货,包含mysql,netty,spring,线程,spring cloud、jvm、源码、算法等详细讲解,也有详细的学习规划图,面试题整理等,须要获取这些内容的朋友请加Q君样:909038429/./*欢迎加入java交流Q君样:909038429一块儿吹水聊天

相关文章
相关标签/搜索