一 Java中的值传递和引用传递(非常重要)
首先要明确的是:“对象传递(数组、类、接口)是引用传递,原始类型数据(整型、浮点型、字符型、布尔型)传递是值传递。”
那么什么是值传递和应用传递呢?
值传递是指对象被值传递,意味着传递了对象的一个副本,即使副本被改变,也不会影响源对象。(因为值传递的时候,实际上是将实参的值复制一份给形参。)
引用传递是指对象被引用传递,意味着传递的并不是实际的对象,而是对象的引用。因此,外部对引用对象的改变会反映到实际的对象上。(因为引用传递的时候,实际上是将实参的地址值复制一份给形参。)
有时候面试官不是单纯问你“Java中是值传递还是引用传递”是什么啊,而是给出一个例子,然后让你写出答案,这种也常见在笔试题目中!所以,非常重要了,请看下面的例子:
值传递和应用传递实例
1. 值传递
public static void main(String[] args) {
int num1 = 10;
int num2 = 20;
swap(num1, num2);
System.out.println("num1 = " + num1);
System.out.println("num2 = " + num2);
}
public static void swap(int a, int b) {
int temp = a;
a = b;
b = temp;
System.out.println("a = " + a);
System.out.println("b = " + b);
}
结果:
a = 20
b = 10
num1 = 10
num2 = 20
解析:
在swap方法中,a、b的值进行交换,并不会影响到num1、num2。因为,a、b中的值,只是从num1、num2的复制过来的。 也就是说,a、b相当于num1、num2的副本,副本的内容无论怎么修改,都不会影响到原件本身。
2. 引用传递
public static void main(String[] args) {
int[] arr = {1,2,3,4,5};
change(arr);
System.out.println(arr[0]);
}
public static void change(int[] array) {
//将数组的第一个元素变为0
array[0] = 0;
}
结果:
1
0
解析:
无论是主函数,还是change方法,操作的都是同一个地址值对应的数组。 因此,外部对引用对象的改变会反映到实际的对象上。
一些特殊的例子
1. StringBuffer类型传递
// 测试引用传递:StringBuffer
@org.junit.Test
public void method1() {
StringBuffer str = new StringBuffer("公众号:Java面试通关手册");
System.out.println(str);
change1(str);
System.out.println(str);
}
public static void change1(StringBuffer str) {
str = new StringBuffer("abc");//输出:“公众号:Java面试通关手册”
//str.append("欢迎大家关注");//输出:公众号:Java面试通关手册欢迎大家关注
//str.insert(3, "(编程)");//输出:公众号(编程):Java面试通关手册
}
结果:
公众号:Java面试通关手册
公众号:Java面试通关手册
解析:
很多要这个时候要问了:StringBuffer创建的明明也是对象,那为什么输出结果依然是原来的值呢?
因为在 change1
方法内部我们是新建了一个StringBuffer对象,所以 str
指向了另外一个地址,相应的操作也同样是指向另外的地址的。
那么,如果将 change1
方法改成如下图所示,想必大家应该知道输出什么了,如果你还不知道,那可能就是我讲的有问题了,我反思(开个玩笑,实际上,上面程序中已经给出答案):
public static void change1(StringBuffer str) {
str.append("欢迎大家关注");
str.insert(3, "(编程)");
}
2. String类型传递
// 测试引用传递:Sring
@org.junit.Test
public void method2() {
String str = new String("公众号:Java面试通关手册");
System.out.println(str);
change2(str);
System.out.println(str);
}
public static void change2(String str) {
// str="abc"; //输出:公众号:Java面试通关手册
str = new String("abc"); //输出:公众号:Java面试通关手册
}
结果:
公众号:Java面试通关手册
公众号:Java面试通关手册
可以看到不论是执行 str="abc;"
还是 str=newString("abc");
str的输出的值都不变。 按照我们上面讲“StringBuffer类型传递”的时候说的, str="abc;"
应该会让str的输出的值都不变。为什么呢?因为String在创建之后是不可变的
3. 一道类似的题目
下面的程序输出是什么?
public class Demo {
public static void main(String[] args) {
Person p = new Person("张三");
change(p);
System.out.println(p.name);
}
public static void change(Person p) {
Person person = new Person("李四");
p = person;
}
}
class Person {
String name;
public Person(String name) {
this.name = name;
}
}
很明显仍然会输出 张三
。因为 change
方法中重新创建了一个 Person
对象。
那么,如果把 change
方法改为下图所示,输出结果又是什么呢?
public static void change(Person p) {
p.name="李四";
}
答案我就不说了,我觉得大家如果认真看完上面的内容之后应该很很清楚了。
二 ==与equals(重要)
== : 它的作用是判断两个对象的地址是不是相等。即,判断两个对象是不是同一个对象。(基本数据类型==比较的是值,引用数据类型==比较的是内存地址)
equals() : 它的作用也是判断两个对象是否相等。但它一般有两种使用情况:
- 情况1:类没有覆盖equals()方法。则通过equals()比较该类的两个对象时,等价于通过“==”比较这两个对象。
- 情况2:类覆盖了equals()方法。一般,我们都覆盖equals()方法来两个对象的内容相等;若它们的内容相等,则返回true(即,认为这两个对象相等)。
举个例子:
public class test1 {
public static void main(String[] args) {
String a = new String("ab"); // a 为一个引用
String b = new String("ab"); // b为另一个引用,对象的内容一样
String aa = "ab"; // 放在常量池中
String bb = "ab"; // 从常量池中查找
if (aa == bb) // true
System.out.println("aa==bb");
if (a == b) // false,非同一对象
System.out.println("a==b");
if (a.equals(b)) // true
System.out.println("aEQb");
if (42 == 42.0) { // true
System.out.println("true");
}
}
}
上述运行结果说明:
- String中的equals方法是被重写过的,因为object的equals方法是比较的对象的内存地址,而String的equals方法比较的是对象的值。
- 当创建String类型的对象时,虚拟机会在常量池中查找有没有已经存在的值和要创建的值相同的对象,如果有就把它赋给当前引用。如果没有就在常量池中重新创建一个String对象
-
hashCode()与equals()的相关规定
- 如果两个对象相等,则hashcode一定也是相同的
- 两个对象相等,对两个对象分别调用equals方法都返回true
- 两个对象有相同的hashcode值,它们也不一定是相等的
- 因此,equals方法被覆盖过,则hashCode方法也必须被覆盖
- hashCode()的默认行为是对堆上的对象产生独特值。如果没有重写hashCode(),则该class的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)
原文地址:https://www.cnblogs.com/liuyang-93/p/10104223.html