您的位置:首页 > 其它

第三章:操作符(这是我第三遍编辑了,因为bug)

2017-06-20 09:41 281 查看

操作符

我之前也看过JavaScript中的操作符,大体上来说,区别不是很大。因为比较基础,我就不仔细分类了,就把一些关键点记录下来。

几乎所有操作符都只能操作基本类型,例外的操作符是=、==、!=。这三个操作符能操作所有的对象。另外String类型支持+和+=操作(字符串拼接)。

关于赋值操作

= 赋值操作符的右值可以是任何常数、变量或表达式。左值必须是一个明确的、已命名的变量。

在为基本类型变量赋值时,如
int a = b;
是将b的值赋给a,再对b进行任何操作都不会对a产生影响。但是如果是为一个引用类型变量赋值
StringBuffer a = b;
,根据前一章的知识,这种操作只会改变a的引用地址值,而不会真的创建一个与b相同的对象(这种行为需要new)。所以我们要是对b进行任何操作也会影响a。这个性质如果处理不好会引发别名问题。

我们知道,当给一个方法传递一个对象时,其实是在该方法的作用域内放入一个该对象引用的副本。通过这个副本,我们才可以操作相应的对象。也因为基本变量是直接传值的副本,故Java中是没有swap函数的:

public class Test {
public static void main(String[] miaoch) {
int a = 1, b = 2;
swap(a, b);
System.out.println("main scope:" + a + " " + b);
}
public static void swap(int _a, int _b) {
int temp = _a;
_a = _b;
_b = temp;
System.out.println("swap scope:" + _a + " " + _b);
}
}
-----------------
swap scope:2 1
main scope:1 2


原因很简单,传递给swap函数,其实就是在swap函数作用域中做了一种赋值操作
int _a = a, _b = b;
在swap函数的作用域内,_a_b的值的确能调换,但函数执行完毕后,_a_b的生命周期也就完结了。自始至终,你始终无法在swap中去修改main中的a和b变量。就好比前面的例子,你把b的值赋给a,再对a做任何操作都和b无关(基本类型)。

有了上面的经验,我们再看一个例子:

public class Test {
public static void main(String[] miaoch) {
StringBuffer test = new StringBuffer("hello");
changeSB(test);
System.out.println("main scope:" + test);
}
public static void changeSB(StringBuffer _test) {
_test.append(" world!");
_test = new StringBuffer("不存在的");
System.out.println("changeSB scope:" + _test);
}
}
----------------
changeSB scope:不存在的
main scope:hello world!


之前我认为传递对象,那就是传递引用。所以我认为此处的main scope下的test也应该为不存在的。但是结果却是
hello world!
。但是我们要是稍微改变一下代码:

StringBuffer test = new StringBuffer("hello");
StringBuffer _test = test;
_test.append(" world!");
_test = new StringBuffer("不存在的");
System.out.println("changeSB scope:" + _test);
System.out.println("main scope:" + test);


这样是不是所有问题都清楚了呢?传递给函数的不过是引用的一个副本,这种传递依旧称为值传递。真正的引用传递是这样的:

StringBuffer test = new StringBuffer("hello");
//引用传递-> changeSB scope (C++中是可以做到的)
test.append(" world!");
test = new StringBuffer("不存在的");
System.out.println("swap scope:" + test);//"不存在的"


关于一元加减操作

一元减相当于取相反数,一元加不过是为了与之对应,但是还有一个小作用就是将较小类型提升为int。

char c = 'a';
System.out.println(+c);//97


关于自增自减

i++;++i;
前者叫前缀式,后者是后缀式。对于前缀式,会先执行计算再生成值。对于后缀式,会先生成值再进行计算。

int i = 0;
System.out.println(++i);//i=1 返回计算后的i:1
System.out.println(i++);//i=2 返回计算前的i:1


关于逻辑操作符的短路

所谓短路,意思是指拼接的逻辑表达式不必全都进行计算,由此得到性能的提升。在电路中,比如几个灯泡串联。我们假设电流是从左往右移动,如果第一个灯泡断了,那么电流更不可能经过后续灯泡(其实用水流和管道的比喻更合适)。如果是并联的话,一条管道通了,就能到达终点,其余管道也就没有计算的必要。

int i=1,j=2,k=3;
boolean a = (i++ > 2) && (j++ > 2);//由于i++ > 2 false 因此短路了,j++不会执行
System.out.println(i);//2
System.out.println(j);<
4000
span class="hljs-comment">//2
boolean b = (++i >= 3) || (j++ > 10);//由于++i >= 3 true 因此短路了,j++不会执行
System.out.println(i);//3
System.out.println(j);//2


直接常量

常量除了用final去声明外,还有一种直接常量。最简单的
int a = 1;
此处的1就是一个直接常量。常量在程序编译阶段就为其分配了内存空间,而不是像普通对象一样,用到了才会new。既然如此,那么编译器就必须准确地知道常量所占的空间大小。这里只聊直接常量。对于这个常量1,它到底是short,int,还是long呢?

直接常量后面的后缀字符标志了它的类型。如果是大写(或小写)的 L ,那就是long型,如果是D或者d那就是double型,如果是F或者f那就是float型。此外以0x开头,后面跟着0~f(大写也可以)表示一个十六进制数。以0开头,后面跟着0~7则表示一个八进制数。当然还有利用e的写法。例如3e3。则代表
3*10∧3 = 3000;


移位操作

<< 左移 >> 右移 >>> 无符号右移 。其中左移和右移都是保持符号位不动,对低位补0或高位补0或1(根据符号)。而无符号右移就是连符号位一起移动,高位补0。如果对char、byte和short类型进行移位处理,则他们会被先转换为int型。并且得到的结果也是一个int型。

<<= 左移后赋给自己 >>= 右移后赋给自己 >>>= 无符号右移后赋给自己。当对char、byte和short类型(会先转换为int)进行上述操作会有一些奇怪的现象。

short i = -1;//16个1
System.out.println(Integer.toBinaryString(i));//转换为32位 32个1
i >>>= 17;//无符号右移17位,高位补0.得到17个0 15个1;
System.out.println(Integer.toBinaryString(i));//15个1
System.out.println(i);//15个1为short最大32767
short j = 1;//15个0 1个1
System.out.println(Integer.toBinaryString(j));//1个1
j <<= 15;//注意是转换成int后左移15位,而不是short上面左移15位(有符号左移),如果是后者会得到0
System.out.println(Integer.toBinaryString(j));//前十六位是1 后十六位是一个1,15个0; 然后截断
System.out.println(j);//截断后十六位 得到short的负最大值 -32768


关于三元操作符

这一部分已经很熟悉,不过还是有一个很有趣的东西。

System.out.println(true? 97:'a');//输出了a哦


不要以为这里代码出错了,其实之所以会输出a,是因为它将97认为是char型的。这里就涉及到三元表达式的返回类型问题了。像这个例子。’a’自然是char型的,97的话我们一般认为它是int型的。但是此处因为97没有超出char的范围。所以编译器会认为此处三元表达式会返回一个char型。至于其余几种情况,我也就不展开了。因为我也不是特别了解本质!

关于类型转换

我们都知道,窄型基本类型可以自动转换成宽型基本类型。而反过来则需要强转。前者给我们提供了便利,但是也埋下了隐患。比如我们很容易犯类似的错误:

long time = 50000 * 24 * 3600 * 1000;
System.out.println(time);//-737099776
//1111101101 11010100000100001100000000000000
//截取后32位11010100000100001100000000000000 为-737099776


哈?几个正数相乘得到了负数?其实原因很简单,我们误以为int能够成功地转成long,然而后面的都是int,int相乘还是得到int,如果越界了就执行越界该有的处理(比如截断)。要想避免这样的错误也很容易,只要指定50000L就行了。此处也涉及到了提升。int和long进行相加会得到long。而char、byte、short这些在运算时会自动转换为int,其结果也为int。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: