public class Main {
public static void main(String[] args) {
String s1 = "123";
String s2 = s1;
s2 = "321";
System.out.println(s1);
int num1 = 1;
int num2 = num1;
num2 = 2;
System.out.print(num1);
Integer integer1 = new Integer(100);
Integer integer2 = integer1;
integer2 = 200;
System.out.print(integer1);
ObjectTest p1 = new ObjectTest(10);
ObjectTest p2 = p1;
p2.value = 20;
System.out.println(p1.value);
}
}
class ObjectTest{
int value;
ObjectTest(int value) {
this.value = value;
}
}
大家可以先把自己试着把自己心中的答案写一下,然后对照下面的运行结果。
分割线,不要偷看答案
运行结果
123
1
100
20
可以看出来,如果对象是引用类型,用 ObjectTest q2 = p1;这种对象引用赋值方式,实质上是将p2对象地址指针指向p1的内存地址,这就导致了修改p2实际上是修改了p1和p2的内存,所以导致了p1也发生了改变。
而基本类型(八大基本类型,byte、short、int、long、boolean、float、double、char)不同,是用拷贝内容的方式,即创建新的内存单元来存储对象的赋值,所以num1和num2,integer1和integer2是不同的内存地址,所以改变其中一方不会影响另一方。
String类型属于引用数据类型,不属于基本数据类型,但是String类型的数据是存放在常量池中的,也就是无法修改的!也就是说,当s2的值改变时,并不是修改了这个数据的值,而是把这个数据的引用从指向”123“这个常量改为了指向”321“这个常量。在这种情况下,s1仍然指向”123“不会受到影响。
那么,对于p1和p2,能不能也用内容拷贝的方式来赋值而达到修改其中一方而不互相干扰的效果呢?
当然可以!通过浅拷贝或者深拷贝达。
浅拷贝通过类实现Cloneable 接口,然后public重写clone并且通过super.clone()调用Object类中的原clone方法,然后用
p2 = p1.clone();当然,这句要加一个trycatch处理。就可以了
class ObjectTest implements Cloneable {
int val;
ObjectTest objectTest;
Integer integer;
ObjectTest(int val){
this.val = val;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
ObjectTest p1 = new ObjectTest(333);
p1.integer = 333;
p1.objectTest = new ObjectTest(333);
ObjectTest p2 = null;
try {
p2 = (ObjectTest) p1.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
p2.val = 222;
p2.objectTest = new ObjectTest(222);
p2.integer = 222;
System.out.println(p1.val);
System.out.println(p1.integer);
System.out.println(p1.objectTest.val);
深拷贝浅拷贝就不深入了。这两者的区别大家可以自己查一下。
java的方法都是传值的对吗,即改变了方法传参后方法体外的变量不改变对吗?
看看这个例子:
public class Main {
public static void main(String[] args) {
int num = 111;
Integer integer = 1;
int[] array = {1, 1, 1, 1, 1, 1};
ObjectTest objectTest = new ObjectTest(666);
System.out.println("方法体外array参数地址" + array);
changeNum(num);
changeInteger(integer);
changeArray(array);
changeObject(objectTest);
System.out.println("num = " + num);
System.out.println("integer = " + integer);
for (int a : array) {
System.out.print(a);
}
System.out.println();
System.out.println(objectTest.value);
}
public static void changeNum(int param) {
param = 222;
}
public static void changeInteger(Integer param) {
param = 2;
}
public static void changeArray(int[] param) {
System.out.println("方法体内param参数地址" + param);
for (int i = 0; i < param.length; i++) {
param[i] = i;
}
}
public static void changeObject(ObjectTest param) {
param.value = 555;
}
}
class ObjectTest {
int value;
ObjectTest(int value) {
this.value = value;
}
}
你们猜一下再看答案嘛,看看上面赋值例子。
方法体外array参数地址[I@4554617c
方法体内param参数地址[I@4554617c
num = 111
integer = 1
012345
555
基本类型及String,基本类型封装类是这样子的,没错。
但如果是除此之外的引用类型,就是传的地址了,这无可厚非,因为像引用类型比如数组,或者List这种,再或者一个类里面几百个其他类型的变量,想传值可怎么传啊!挨个遍历变量传值???所以肯定是传地址啦。
到这里基本可以总结一下了:
如基本类型及String,基本类型封装类这几种,不管是赋值,还是方法传参,都是用的传值的方式,得到值的变量再怎么整都不会影响原有的变量,因为只是拷贝的一份数值。
而其他引用类型,由于其内容的不可确定性(猜测),所以被设计为赋值传参时直接将地址赋予新的变量,这才导致改变新变量会对原变量造成影响。
//ps.上面实验都为本人设计,实验或者结论有错误遗漏的请斧正