java对象赋值引起的问题
在java赋值的时候,有种情况是把一个对象赋值给另一个对象,这时会引起别名的问题。所以在写代码的时候一定要尽量避免这个问题。要知道为什么会引起这个问题首先要知道什么是引用(reference)。
//创建了一个引用s,引用是用来操纵对象的。引用不能被直接使用,必须让它指向一个实际的对象
String s;
//new String ("abc") 是实际的对象,引用s指向这个对象。java中创建对象通常用new关键字
s = new String ("abc");
我们来拿基本数据类型赋值和java对象赋值来做个对比:
基本数据类型存储的时候实际上存储的是一个实际的值,而并非对象的引用。所以把一个变量赋值给另一个变量的时候,是直接将一个地方的内容复制到另一个地方。
对象的存储分两部分,分别是引用和new的实际对象。引用存在堆栈中。java在调用new实例一个对象的时,在堆中分配一块内存给这个对象。通过 “=” 使引用指向实际分配内存的对象。java中把一个对象赋值给另一个对象实际上赋值的是引用,而不是对象。
/**
*
*/
public class Test {
public static void main(String[] args) {
//基本数据类型赋值
int a = 12;
int b = 40;
System.out.println("1: a: " + a + ", b: " + b);//1: a: 12, b: 40
//把b等于40复制给a,所以a也等于40
a = b;
System.out.println("2: a: " + a + ", b: " + b);//2: a: 40, b: 40
//修改了a的值,而b根本就不会受这种修改的影响
a = 15;
System.out.println("3: a: " + a + ", b: " + b);//3: a: 15, b: 40
//对象赋值
Bean b1 = new Bean();
Bean b2 = new Bean();
b1.num = 12;
b2.num = 40;
System.out.println("1: b1.num: " + b1.num + ", b2.num: " + b2.num);//1: b1.num: 12, b2.num: 40
//b2的引用赋值给了b1,b1原来的引用被覆盖了,b1和b2现在是相同的引用,所以b1和b2都指向了原本只有b2指向的那个对象,所以值都是40
b1 = b2;
System.out.println("2: b1.num: " + b1.num + ", b2.num: " + b2.num);//2: b1.num: 40, b2.num: 40
//对b1的修改其实也是在修改b2,因为它俩指向的是同一个对象,所以都是15
b1.num = 15;
System.out.println("3: b1.num: " + b1.num + ", b2.num: " + b2.num);//3: b1.num: 15, b2.num: 15
//这样直接把一个对象赋值给了另外一个对象,引起来别名问题,如果想使b1数据的值修改成和b2一样,不会引起问题的方式应该是b1.num = b2.num
}
}
class Bean {
int num;
}
自动递增和递减的两种方式的区别
/**
* java中自动递增和自动递减分前缀式和后缀式。以变量 i 为例,前缀式:++i、--i ,后缀式:i++、i--
* 对于前缀式,我们是在执行完运算后才得到值,对于后缀式,则是在运算执行之前就得到值
*/
public class Test{
public static void main(String[] args) {
int i = 1;
System.out.println(i);//1
//++i 是在 i 执行完 ++ 操作,才得到值,输出为2
System.out.println(++i);//2
//i=2,i++ 是先得到值是2,然后再++,所以输出是2
System.out.println(i++);//2
//在上面i执行了++操作,所以输出是3
System.out.println(i);//3
System.out.println(--i);//2
System.out.println(i--);//2
System.out.println(i);//1
}
}
"==" 和 equals()
public class Test {
public static void main(String[] args) {
// 基本数据类型
int a = 12;
int b = 12;
//基本数据类型直接比较数值
System.out.println(a == b);//true
//对象
Integer x = new Integer(1);
Integer y = new Integer(1);
//x==y比较的实际是引用,虽然x和y的内容相同,但是引用不同所以是false
System.out.println(x == y);//false
//equals()方法默认比较的也是引用,但是java类库大多数都重写了该方法直接比较内容,所以x.equals(y)是相等的
System.out.println(x.equals(y));//true
Bean b1 = new Bean();
Bean b2 = new Bean();
b1.num = 12;
b2.num = 12;
//equals()对象比较的是引用,所以是false。如果想b1.equals(b2)等于true,需要重写Bean类中的equals()方法,让其比较对象中的内容
System.out.println(b1.equals(b2));//false
}
}
class Bean {
int num;
}
短路现象
/**
* 其实&& 和 ||是逻辑操作符,& 和 | 是按位操作符,& 和 | 在操作的是布尔值时与逻辑操作符 && 和 ||有相同的效果,所以才放在一起比较,
* 两者的区别是:逻辑操作符有短路现象,按位操作符没有短路现象。
* 短路现象:逻辑表达式中一旦能够明确的无误的确定整个表达试的值,就不需要计算余下的部分了。
* 如果逻辑表达式都有一部分不必计算,那将获得潜在的性能提升,所以写代码尽量要用有短路现象的表达式&& 和 ||
*/
public class Test {
public static void main(String[] args) {
System.out.println("&& ----------");
boolean a = test1(1) && test2(2) && test3(7);
//因为&&具有短路现象,test1(1)值为ture,所以继续执行test2(2),单test2(2)位false,可以断定 a 的值为false,所以不执行test3(7)
System.out.println("result: " + a);
System.out.println("& ----------");
boolean b = test1(1) & test2(2) & test3(7);
//因为&不具有短路现象,所以test1(1)、 test2(2)、 test3(7)都执行
System.out.println("result: " + b);
}
static boolean test1(int val){
System.out.println("test1");
return val > 0;
}
static boolean test2(int val){
System.out.println("test2");
return val > 2;
}
static boolean test3(int val){
System.out.println("test3");
return val > 5;
}
}
输出结果:
&& ----------
test1
test2
result: false
& ----------
test1
test2
test3
result: false