[Thinking in Java]第3章-操作符
3.1 更简单的打印语句
3.2 使用Java操作符
3.3 优先级
3.4 赋值
3.5 算术操作符
3.6 自动递增和递减
3.7 关系操作符
3.8 逻辑操作符
3.9 直接常量
3.10 按位操作符
3.11 移位操作符
3.12 条件操作符
3.13 字符串操作符+和+=
3.14 类型转换
3.1 更简单的打印语句
学习编程语言的同学遇到的第一个程序无非是打印Hello, world了,然而在Java中要写成
System.out.println("Hello, world");
我们都会感觉太冗长了,能不能简单一些呢?静态导入可以省略掉System,就像这样
import static java.lang.System.*;
然后这样写就可以打印出Hello, world
out.println("Hello, world");
能不能再简单些呢?像C语言那样?实际上,这里的System是类,out是System的一个静态成员,是PrintStream类型的,因为在System类中是这样写的,
public final class System { ...... public final static PrintStream out = null; ...... }
可以得到启发,自己也写一个类,然后静态导入此类的静态输出方法,就可以像C语言那样输出内容了
package p1; import static java.lang.System.*; public class MyPrint { public static void print(String x) { out.print(x); } // 重载print public static void println(String x) { out.println(x); } //重载println }
写第一个java程序
import static p1.MyPrint.*; public class Example { public static void main(String[] args) { println("Hello,world"); } }
NOTE:大家可以将自己写的打印输出类打包成jar文件,使用命令jar cvf p1.jar p1/MyPrint.class,其中p1是包名,MyPrint是类名,大家可以自定义,然后放在jdk目录下的lib目录中,比如D:\jdk\lib\p1.jar,然后修改环境变量,在CLASSPATH中添加%JAVA_HOME%\lib\p1.jar,这样就可以一劳永逸了,只需要import static p1.MyPrint.*;就可以直接使用println方法了
3.2 使用Java操作符
这里提到操作符的副作用,这篇博文讲得很详细 表达式的副作用
简单说,变量X在一个表达式的运算后,如果值未变,操作符没有副作用,如果值改变了,操作符产生副作用
3.3 优先级
优先级 | 运算符 | 结合性 |
1 | () [] . | 从左至右 |
2 | ! +(正) -(负) ~ ++(递增) --(递减) | 从右向左 |
3 | * / % | 从左至右 |
4 | +(加) -(减) | 从左至右 |
5 | << >> >>> | 从左至右 |
6 | < , <= , > , >= , instanceof | 从左至右 |
7 | == != | 从左至右 |
8 | &(按位与) | 从左至右 |
9 | ^(异或) | 从左至右 |
10 | |(按位或) | 从左至右 |
11 | &&(逻辑与,短路与) | 从左至右 |
12 | ||(逻辑或,短路或) | 从左至右 |
13 | ?: | 从右向左 |
14 |
= , += , -= , *= , /= , %= , &= , |= , ^= , ~=, <<= , >>= , >>>= |
从右向左 |
其实不需要刻意去记,只要有万能的小括号,不仅使程序更易于阅读,还减少错误
3.4 赋值
3.5 算术操作符
算术操作符有 +(加), -(减),*(乘),/(除),%(取模)
int sum = 9 - 8 + 6 / 3 * 5 % 3;// sum = 2
在优先级表上同一行的操作符的优先级是一样的。这个例子中,先是6 / 3 = 2,再2 * 5 = 10,再10 % 3 = 1,最后9 - 8 + 1 = 2
也可以使用来自C语言的简化操作符,也就是位于优先级表的第14行的操作符
int x = 1; int y = x += 2;// y = 3
注意,第14行的操作符的结合性是从右至左的,所以先x += 2,得到x = 3,最后y = x,得到y = 3
3.6 自动递增和递减
递增操作符分为前缀递增和后缀递增,递减操作符也分为前缀递增和后缀递减,比如
x++ ++x
x-- --x
就像递增操作符,x++的意思是先使用x的值,运算后x自动加1;而++x的意思是先让x自动加1,然后使用x的值
int x = 5; int y = x++ + x-- + ++x + --x;// x = 5, y = 22
上面的例子相当于
int x = 5; int y = x++; y = x--; y = ++x; y = --x;
注意,不能对同一个变量使用2个或2个以上的递增递减操作符,比如下面的例子错误
x++--;// 错误,不能对同一个变量同时使用2个递增递减操作符
3.7 关系操作符
关系操作符运算的结果是一个boolean值,有<(小于),<=(小于或等于),>(大于),>=(大于或等于),==(等于)和!=(不等于)
一个简单的例子
int x = 7; print(x < 8); // true print(x <= 8);// true print(x > 8); // false print(x >= 8);// false print(x == 8);// false print(x != 8);// true
Java中==和!=还可以用来比较对象,比如
1 public class Person { 2 int age; 3 4 public static void main(String[] args) { 5 String s1 = "ggg"; 6 String s2 = "ggg"; 7 Person zhangSan = new Person(18); 8 Person liSi = new Person(18); 9 println(s1 == s2);// true 10 println(zhangSan == liSi);// false 11 } 12 13 Person(int newAge) { 14 age = newAge; 15 } 16 }
结果出乎意料了,第9行是true,可第10行却是false!这是因为,==和!=比较的是对象的引用,而非对象的内容。String类型的s1和s2引用的是同样一个字符串常量,因此相等,而Person类型的zhangSan和liSi虽然内容一样,但是属于两个不同的对象,就像同一班里有两个小明,年龄也一样,但是他们的基因不一样。
既然这样,那我们应该如何比较两个对象的内容呢?可以使用从根类继承下来的equals()方法
1 public class Person { 2 int age; 3 4 public static void main(String[] args) { 5 String s1 = "ggg"; 6 String s2 = "ggg"; 7 Person zhangSan = new Person(18); 8 Person liSi = new Person(18); 9 println(s1.equals(s2));// true 10 println(zhangSan.equals(liSi));// false 11 } 12 13 Person(int newAge) { 14 age = newAge; 15 } 16 }
结果又让人费解,凭什么第9行的结果是true,而第10行的结果还是false?其实String类覆盖了Object类的equals()方法,而这个例子尚未覆盖它,实际上从Object类继承的equals()方法默认是这样的
public boolean equals(Object obj) { return (this == obj); }
所以第10行返回的结果当然是false了
3.8 逻辑操作符
逻辑操作符“与”(&&)、“或”(||)、“非”(!)能根据参数的逻辑关系,生成一个布尔值(true或false)。
值得注意的是,&&和||是短路的,比如p&&q,如果p为假,则不必再计算q,如果p为真,则继续计算q,比如p||q,如果p为真,则不必计算q,如果p为假,则继续计算q。
1 public class Demo { 2 boolean flag; 3 4 public static void main(String[] args) { 5 boolean x = new Demo(false).flag && new Demo(true).flag && new Demo(true).flag;// false 6 boolean y = new Demo(false).flag || new Demo(true).flag || new Demo(true).flag;// false true 7 } 8 9 Demo(boolean newFlag) { 10 flag = newFlag; 11 print(flag + " "); 12 } 13 }
因为短路,第5行只打印false (空格),第6行也只打印false true (空格)
3.9 直接常量
直接引用《Thinking in Java》的代码
1 public class Literals { 2 public static void main(String[] args) { 3 int i1 = 0x2f;//十六进制,小写 4 println("i1: " + Integer.toBinaryString(i1)); 5 int i2 = 0X2F;//十六进制,大写 6 println("i2: " + Integer.toBinaryString(i2)); 7 int i3 = 0177;//八进制,以0开始 8 println("i3: " + Integer.toBinaryString(i3)); 9 10 char c = 0xffff;//十六进制,char类型的最大值 11 println("c: " + Integer.toBinaryString(c)); 12 byte b = 0x7f;//十六进制,byte的最大值 13 println("b: " + Integer.toBinaryString(b)); 14 short s = 0x7fff;//十六进制,short的最大值 15 println("s: " + Integer.toBinaryString(s)); 16 17 long n1 = 200L;//long类型,后缀大L 18 long n2 = 200l;//long类型,后缀小l 19 long n3 = 200;//long类型,无后缀 20 float f1 = 1;//float类型,无后缀 21 float f2 = 1F;//float类型,后缀大F 22 float f3 = 1f;//float类型,后缀小f 23 double d1 = 1d;//double类型,后缀小d 24 double d2 = 1D;//double类型,后缀大D 25 } 26 }/*输出结果 27 i1: 101111 28 i2: 101111 29 i3: 1111111 30 c: 1111111111111111 31 b: 1111111 32 s: 111111111111111 33 */
在C、C++或者Java中,二进制都没有直接常量的表示方法,不过用十六进制和八进制来表示二进制会更加直观、简洁和更易于阅读
既然是直接常量,程序就休想修改它的值了,比如下面的代码不能编译
int x = 1++;
3.10 按位操作符
按位操作符用来操作整数基本数据类型中的单个“比特”(bit),即二进制位。按位操作符会对两个参数中对应的位执行布尔代数运算,并最终生成一个结果。
如果两个输入位都是1,那么 1 & 1 = 1;
否则 1 & 0 = 0;0 & 1 = 0;
如果两个输入位有一个是1,那么 1 | 0 = 1;0 | 1 = 1;
否则 0 | 0 = 0;
如果两个输入位不痛,那么 1 ^ 0 = 1;0 ^ 1 = 1;
否则 1 ^ 1 = 0;0 ^ 0 = 0;
按位“非”是一元操作符, ~1 = 0;~0 = 1
按位操作符和逻辑操作符都使用了同样的符号,因此我们能方便地记住它们的含义:由于位是非常“小”的,所以按位操作符仅使用了一个字符
按位操作符可与等号(=)联合使用,以便合并运算和赋值:&=、|=、和^=都是合法的(由于~是一元操作符,所以不存在~=)
我们将布尔类型作为一种单比特值对待,因而它会有些独特。对于布尔值,用按位操作符的话,将不再短路。我们可以对布尔值进行&、|、^,但不能~(为了避免与!混淆),其中按位异或(^)使用如下
boolean p = true; boolean q = false; boolean r; r = p ^ p;// false r = p ^ q;// true r = q ^ p;// true r = q ^ q;// false
3.11 移位操作符
移位操作符有左移<<、有符号右移>>、无符号右移>>>
x << n;将x向左移动n位,在低位补0
x >> n;将x向右移动n位,若x是正数,则高位补0,若x是负数,则高位补1
x >>> n;将x向右移动n位,无论如何,在高位补0
无论对0怎么移动,结果都是0
如果对char、byte或者short类型的数值进行移位处理,那么在移位之前,它们先转换成int类型,并且得到的结果也是一个int类型的值。
“移位”可与“等号”(<<=或>>=或>>>=)组合使用
3.12 条件操作符
条件操作符的语法如下形式:
expression ? value1 : value2;
如果expression为true,结果为value1,否则为value2。需要注意的是,条件操作符的结合性是从右至左的,比如
int x = 2 > 1 ? 10 : 100 > 99 ? 1000 : 10000;// x = 10
先执行100 > 99 ? 1000 : 10000;结果为1000,再执行2 > 1 ? 10 : 1000;所以结果为10
3.13 字符串操作符+和+=
String s1 = "Hello,"; String s2 = "world"; String s3 = s1 + s2;// s3 = "Hello,world" String s1 += s2;// s1 = "Hello,world"
3.14 类型转换
只要类型比int小(即byte、char或short),那么在运算之前,这些值会自动转换成int,这样一来,最终生成的结果就是int类型了。如果想把结果赋值给较小的类型,就必须使用强制类型转换(既然把结果赋给了较小的类型,就可能出现信息丢失)。通常,表达式中出现的最大的数据类型决定了表达式最终结果的数据类型。如果将一个float值与一个double值相乘,结果就是double;如果将一个int和一个long相加,结果为long
类型转换的应用:精确到小数点后n位
// 精确到小数点后3位 double pi = 3.141592653; double x = (int) Math.round(pi * 1000) / 1000.0;// x = 3.142
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。