Java之基本类型和引用类型、引用传递和值传递、“==”和equals()

作者:金良(golden1314521@gmail.com) csdn博客:http://blog.youkuaiyun.com/u012176591


1.引用类型和基本类型

Java中数据类型分为两大类,是基本类型和对象类型。相应的,变量也就有两种类型:基本类型和引用类型。

基本类型的值就是一个数字,一个字符或一个布尔值。引用类型,是一个对象类型的啊,它的值是指向内存空间的引用,就是地址,所指向的内存中保存着变量所表示的一个值或一组值。

Java的基本数据类型如下表所示,其余的都是对象类型,如String类型、Integer类型、数组类型等。


变量的基本类型和引用类型的区别:

基本数据类型在声明时系统就给它分配空间。

举个例子:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. int a;  
  2. a=10;//正确,因为声明a时就分配了空间  

而引用与之不同,它声明时只给变量分配了引用空间,而不分配数据空间。

举个例子:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. MyDate today;  
  2. today.day = 4; //发生错误,因为today对象的数据空间未分配。  
  那我们怎么给它赋值啊?引用类型变量在声明后必须通过实例化开辟数据空间,才能对变量所指向的对象进行访问。举个例子:
[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. MyDate today;            //将变量分配一个保存引用的空间  
  2. today = new MyDate();     //执行new MyDate(),给today变量开辟数据空间,然后把空间的首地址传给today变量  
  3. today.day = 4;//正确,执行赋值操作,  
引用类型变量的赋值过程图解如下:

这是赋值过程的代码:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. MyDate a,b;        //在内存开辟两个引用空间  
  2. a  =  new MyDate();//开辟MyDate对象的数据空间,并把该空间的首地址赋给a  
  3. b  =  a;           //将a存储空间中的地址写到b的存储空间中  
这是赋值过程的图示:



2.引用传递 和 值传递

引用类型:除了在函数传值的时候是"引用传递",在任何用"="向对象变量赋值的时候都是"引用传递"。
值传递:基本类型的传递都属于值传递,和C语言一样,当把Java的基本数据类型(如int,char,double等)作为入口参数传给函数体的时候,传入的参数在函数体内部变成了局部变量,这个局部变量是输入参数的一个拷贝,所有的函数体内部的操作都是针对这个拷贝的操作,函数执行结束后,这个局部变量也就完成了它的使命,它影响不到作为输入参数的变量。这种方式的参数传递被称为"值传递"。

引用传递举例(注释有分析):

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. package bupt.xujinliang.mydatatest;  
  2. /** 
  3.  *  
  4.  * @author jin 
  5.  * 
  6.  */  
  7. class MyDate {  
  8.     String  Year;  
  9.     String  Month;  
  10.     String  Day;  
  11.     String  Date;  
  12.     public MyDate(){  
  13.         Year = "2014";  
  14.         Month ="06";  
  15.         Day = "14";  
  16.         Date = Year + "." + Month + "."+ Day;  
  17.     }  
  18.      public String toString(){  
  19.         return Date;  
  20.     }      
  21. }  
  22. public class MyDateTest {  
  23.        MyDate mydate1 = new MyDate();//new一个对象,开辟数据空间  
  24.        MyDate mydate2 = mydate1;// 赋值语句,使 mydate1和 mydate2将指向同一内存空间   
  25.        MyDate mydate3 = new MyDate();//重新new一个对象,则mydate3和mydate1是指向不同的内存空间的:  
  26.          
  27.        public void changeDate(MyDate mydate){   
  28.             mydate.Date = "2014.09.26";  
  29.         }  
  30.              
  31.         public static void main(String[] args) {  
  32.             MyDateTest mydatetest = new MyDateTest();  
  33.             System.out.println("Before call changeDate() method: ");  
  34.             System.out.println("\tmydate1: " + mydatetest.mydate1);  
  35.             System.out.println("\tmydate2: " + mydatetest.mydate2);  
  36.             System.out.println("\tmydate3: " + mydatetest.mydate3);  
  37.               
  38.             mydatetest.changeDate(mydatetest.mydate1);  
  39.               
  40.             System.out.println("After call changeDate() method: ");  
  41.             System.out.println("\tmydate1: " + mydatetest.mydate1);  
  42.             System.out.println("\tmydate2: " + mydatetest.mydate2);//赋值语句,二者将指向同一内存空间  
  43.             System.out.println("\tmydate2: " + mydatetest.mydate3);//与前者不指向同一内存空间  
  44.         }  
  45. }  
上述程序的输出:



3.“==”和equals()

了解了前边所讲的基本数据类型和引用类型的概念,下面讲下Java中“==”和equals的区别。

二者对比如下:


上面的列表中的引用数据类型需要注意一下,引用数据类型可以指8种基本数据类型以外的所有类,所以它是无限的,在JavaAPI中出现的类中的绝大部分都重写了equals()方法,都满足表中引用数据类型的要求;不过当你写了一个自己的类,默认情况下是没有重写equals()方法的,这种情况稍后会讲到。


代码:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. package bupt.xujinliang.equalsexample;  
  2. /** 
  3.  *  
  4.  * @author jin 
  5.  * 
  6.  */  
  7. public class EqualsExample {  
  8.     public static void main(String[] args) {  
  9.         Integer obj1 = new Integer(5);  
  10.         Integer obj2 = new Integer(15);  
  11.         Integer obj3 = new Integer(5);  
  12.         Integer obj4 = obj2;  
  13.         System.out.println("obj1.equals(obj1):\t"+obj1.equals(obj1));//true  
  14.         System.out.println("obj1.equals(obj2):\t"+obj1.equals(obj2));//false  
  15.         System.out.println("obj1.equals(obj3):\t"+obj1.equals(obj3));//true  
  16.         System.out.println("obj2.equals(obj4):\t"+obj2.equals(obj4));//true  
  17.         System.out.println("----------------------------------");  
  18.         System.out.println("obj1==obj1:\t"+(obj1==obj1));//true  
  19.         System.out.println("obj1==obj2:\t"+(obj1==obj2));//false  
  20.         System.out.println("obj1==obj3:\t"+(obj1==obj3));//false  
  21.         System.out.println("obj2==obj4:\t"+(obj2==obj4));//true  
  22.     }  
  23. }  

运行效果:


但是,对于一般的Object类,或者用户自己写的Java类,默认其equals()方法就是采用==进行比较的,所以二者等效为同一个对象为真,否则为假。那么为什么上述的引用变量类型Integer的equals()方法和==不等效呢,因为它重写了equals方法,所以才有上述代码的运行结果。

这面的类没有重写equals()方法:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. package bupt.xujinliang.equalsobject;  
  2. /** 
  3.  *  
  4.  * @author jin 
  5.  * 
  6.  */  
  7. class MyObject {  
  8.     int num;  
  9.     MyObject(int num) {  
  10.         this.num = num;  
  11.     }  
  12. }  
  13.   
  14. public class EqualsObject {   
  15.     public static void main(String[] args) {  
  16.         MyObject myobj1 = new MyObject(5);  
  17.         MyObject myobj2 = new MyObject(15);  
  18.         MyObject myobj3 = new MyObject(5);  
  19.         MyObject myobj4 = myobj1;  
  20.         MyObject myobj5 = myobj2;  
  21.         MyObject myobj6 = myobj3;  
  22.           
  23.         System.out.println("myobj1.equals(myobj1):\t"+myobj1.equals(myobj1));//true  
  24.         System.out.println("myobj1.equals(myobj2):\t"+myobj1.equals(myobj2));//false  
  25.         System.out.println("myobj1.equals(myobj3):\t"+myobj1.equals(myobj3));//false  
  26.         System.out.println("myobj2.equals(myobj4):\t"+myobj2.equals(myobj4));//false  
  27.         System.out.println("----------------------------------");  
  28.         System.out.println("myobj1==myobj1:\t"+(myobj1==myobj1));//true  
  29.         System.out.println("myobj1==myobj2:\t"+(myobj1==myobj2));//false  
  30.         System.out.println("myobj1==myobj3:\t"+(myobj1==myobj3));//false  
  31.         System.out.println("myobj2==myobj4:\t"+(myobj2==myobj4));//false  
  32.     }  
  33. }  
上述代码的运行结果如下图所示,可以看到用equals()方法和==是等效的。


下面我们对MyObject类的equals()方法做如下重写:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public boolean equals(Object obj) {  
  2.     if(this == obj)//如果是同一个实例,则相等  
  3.         return true;  
  4.     if(null == obj) //如果obj为空,则不相等  
  5.         return false;  
  6.     if(getClass() != obj.getClass()) //如果类型不同,则不相等  
  7.         return false;  
  8.     MyObject other = (MyObject)obj;  
  9.     if(num == other.num) //当类仅有的整型成员变量相等时,相等  
  10.         return true;  
  11.     return false;  
  12.       
  13. }  

重新运行程序,则结果如下所示:

0
0
 
 
我的同类文章
<think>我们正在回答用户的问题:Java基本数据类型引用数据类型的对比 根据用户要求,我们需要参考站内引用[1][2][3][4]的内容,并结合我们之前已经回答过的内容(用户上一次的问题回答)来组织答案。 之前我们已经详细列出了基本数据类型的8种类型及其细节,以及引用数据类型的分类。现在用户要求对比两者的差异。 根据引用内容,我们可以总结出以下差异点: 1. 存储位置:基本数据类型在栈上(但注意:全局基本数据类型在堆上),引用数据类型的实际对象在堆上,栈上存储引用(地址)。 2. 存储内容:基本数据类型存储实际值,引用数据类型存储的是对象的引用(地址)。 3. 默认值:基本数据类型有各自的默认值(如0, 0.0, false等),引用数据类型默认值为null。 4. 参数传递基本数据类型值传递(复制值),引用数据类型引用传递(复制引用,即地址)。 5. 比较方式:基本数据类型用==比较值,引用数据类型用==比较地址,比较内容需要重写equals方法。 6. 内存管理:基本数据类型由系统自动分配释放,引用数据类型的对象由垃圾回收器管理。 另外,引用[4]提到:定义在类中的全局变量(成员变量)无论是基本类型还是引用类型,都存放在堆区。 我们需要将这些对比点整理成表格,并辅以必要的解释代码示例。 同时,按照要求,我们需要在回答末尾生成相关问题。 注意:数学表达式要用$...$,独立公式用$$...$$,代码块用```language ... ```,引用标识在相关段落末尾自然添加。</think>### Java基本数据类型引用数据类型对比详解 #### 一、核心差异对比 | **特性** | **基本数据类型** | **引用数据类型** | |------------------|-------------------------------------|-------------------------------------| | **存储内容** | 实际数据值(直接存储)[^1] | 对象内存地址(间接引用)[^2] | | **内存分配** | 栈内存(局部变量)<br>堆内存(成员变量)[^4] | 对象在堆内存,变量存储堆地址[^3] | | **默认值** | 数值型:0<br>布尔型:`false`<br>字符型:`\u0000` | `null`(未指向任何对象)[^3] | | **参数传递** | 值传递(复制数据值)[^4] | 引用传递(复制内存地址)[^4] | | **比较操作** | `==`比较实际值 | `==`比较内存地址<br>`equals()`比较内容(需重写)[^3] | | **内存管理** | 自动回收(栈帧弹出时) | 垃圾回收器(GC)管理堆内存[^2] | | **典型示例** | `int a = 10;` | `String s = new String("Hi");` | #### 二、存储机制深度解析 1. **基本数据类型存储模型** ```mermaid graph LR A[变量声明 int x=5] --> B[栈内存分配4字节] B --> C[直接存储二进制值 00000101] ``` - 局部变量:栈内存直接存储值(高效访问)[^4] - 成员变量:随对象存储在堆中 2. **引用数据类型存储模型** ```mermaid graph LR A[变量声明 String s] --> B[栈内存分配4/8字节地址] B --> C[指向堆内存对象地址] C --> D[堆内存存储实际数据] ``` - 栈内存:仅存储**堆地址引用**(通常4/8字节)[^3] - 堆内存:存储实际对象数据(含对象头字段) #### 三、关键差异场景演示 1. **参数传递本质区别** ```java // 基本数据类型 - 值传递 void modify(int num) { num = 100; } // 不影响原始值 // 引用数据类型 - 引用传递 void modify(StringBuilder sb) { sb.append("!"); } // 修改原始对象 ``` 2. **比较操作陷阱** ```java Integer a = 127, b = 127; System.out.println(a == b); // true (缓存范围) Integer c = 128, d = 128; System.out.println(c == d); // false (地址不同) System.out.println(c.equals(d)); // true (值相同) ``` 3. **空指针风险** ```java int num = null; // 编译错误(基本类型不能为null) String str = null; System.out.println(str.length()); // 运行时NullPointerException ``` #### 四、设计哲学与性能影响 1. **基本类型优化原则** - CPU直接支持运算(如`int`加法对应单条CPU指令) - 空间高效:`byte[1000]`仅占1KB,`Byte[1000]`占约4KB(含对象头) - 适用场景:数值计算、标志位、临时变量 2. **引用类型核心价值** - 对象抽象:封装状态行为(OOP核心) - 动态扩展:运行时多态、继承体系 - 资源共享:多个引用指向同一对象(如共享配置) > 💡 **现代JVM优化**:通过**逃逸分析**将部分引用类型转为栈分配(如`-XX:+DoEscapeAnalysis`)[^4] --- ### 相关问题 1. 为什么Java基本数据类型的`==`比较行为与引用类型不同?这种设计如何影响程序正确性?[^3] 2. 在内存敏感场景(如Android开发)中,如何权衡基本类型数组(`int[]`)与包装类集合(`ArrayList<Integer>`)的使用?[^4] 3. Java自动装箱/拆箱机制如何弥合基本类型引用类型的鸿沟?可能引发哪些性能问题?[^2] 4. 为什么`String`虽然是引用类型,但设计为不可变对象?这与内存管理有何关联?[^1] 5. JVM如何通过逃逸分析优化引用类型的栈上分配?这种优化有哪些限制条件?[^4]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值