4.1 操作符概述
多数操作符的结合性为从左到右,只有赋值操作符,以及复合赋值操作符的结合性是从右到左的,
常用操作符的优先级顺序:
优先级 | 操作符分类 | 操作符 |
---|---|---|
最高 | 一元操作符 | ! ++ -- - ~ |
数学运算操作符、移位操作符 | * / % + - >> << >>> | |
比较操作符 | > < >= <= != == | |
逻辑操作符 | && ` | |
三元条件运算符 | ? : | |
最低 | 赋值操作符 | = *= -= += /= %= |
4.2 整型操作符
一元整型操作符表:
操作符 | 实际操作 | 例子 |
---|---|---|
- | 改变整数符号,取反 | -x 相当于 -1*x |
~ | 逐位取反 | ~x |
++ -- | 自增,自减 | x++ --x |
“++” 和 “–” 操作符会改变所作用的变量本身的值,而 “-” 和 “~” 操作符并不改变变量本身的值。“++” 和 “–” 操作符既可作为前置操作符,也可作为后置操作符 ,
int i = 10, j = 10, k = 10, l = 10, m = 10, n = 10;
i--; j++; m = ~k; n = -l;
System.out.println("%d %d %d %d %d %d", i, j, k, l, m, n);
//结果:9 11 10 10 -11 -10
二元整型操作符表:
操作符 | 实际操作 | 例子 |
---|---|---|
+ - * / % | 数学运算 | a+b a-b … |
& | 与运算:对应两二进制位均为1时结果为1,否则为0 | 1&1=1 1&0=0 0&0=0 0&1=0 |
` | ` | 或运算:对应的两二进制位有一个为1时,结果位就为1。 |
^ | 异或运算:对应的两二进制位相同的时候,结果位返回0,否则返回1。异或相当于二进制无进位加法 | 1^1=0 1^0=1 0^1=1 0^0=0 |
<< | 算数左移:位按指定的值向左移动对应的位数,超过的位将丢失,空位补0,左移也可以看作是对此数乘以2。 | -4<<2=-16 4<<2=16 |
>> | 算数右移:把位按指定的值向右移动对应的位数,移动过程中,正数最高位补0,负数最高位补1,右移也可以看作是对此数除以2。 | 8>>2=2 -8>>2=-2 |
>>> | 逻辑右移 | -8>>>2=1073741822 |
注:没有<<<
符号。如果进行无符号左移的话,Java的符号位就被丢掉了,相当于没有符号位了。没有符号位不就变成无符号数了吗?!
位操作符用法举例:
public class BitMover{
public static byte swap(byte b) {
int lowBit = b & 0xF; //低四位
int highBit = b & 0xF0; //高四位
int res = lowBit<<4 | highbit>>>4; //交换高低位
return (byte)res;
}
public static void main(String[] args) {
System.out.println(swap((byte)1));
//结果: 16
}
}
4.3 浮点型操作符
多数整型操作符也可作为浮点型操作符,但浮点运算不支持位运。Java 浮点类型数据有两个特殊的值:负无穷大 (-Infinity)和正无穷大 (Infinity),用来表示无效的浮点运算的结果。
4.4 比较操作符和逻辑操作符
比较操作符和逻辑操作符表:
|操作符|实际操作|
|–|–|–|
|<
>
<=
>=
==
!=
|小于、大于、小于等于、大于等于、等于、不等于|
|&&
&
|短路与,非短路与|
|||
|
|短路或,非短路或|
|!
|非|
“&&” 和 “||” 是短路 (short circuit)操作符 ,“&” 和 “|” 是非短路操作符,它们的区别是:对于短路操作符,如果能根据操作符左边的布尔表达式就能推算出整个表达式的布尔值,将不执行操作符右边的布尔表达式。对于非短路操作符,始终会执行操作符两边的布尔表达式。在某些情况下,短路操作符有助于提高程序代码的安全性。
4.5 三目操作符
Java 语言中有一个特殊的三元操作符 " ? : ",它的语法形式为:boolean ? fuction1 : function2
。该操作符也是短路操作符,要么执行表达式1, 要么执行表达式。
4.6 字符串连接操作符
操作符 “+” 能够连接字符串,并生成新的字符串。
4.7 操作符 “==” 与对象的 equals() 方法
操作符 “==”
操作符 “==” 比较两个操作元是否相等,这两个操作元既可以是基本类型,也可以是引用类型。在 java.lang.Object 类中定义了 equals()
方法,用来比较两个对象是否相等。
当操作符 “==” 两边都是引用类型变量时 ,这两个引用变量必须都引用同 一个对象,结果才为 true。
Integer i1 = new Integer(1);
Integer i2 = new Integer(1); //i1 i2引用不同对象但是值相同
Integer i3 = i1; //i1 i3引用同一个对象
System.out.printf("%b %b", i1 == i2, i3 == i1);
//结果:false true
“" 用于比较引用类型变量时,"” 两边的变量被显式声明的类型必须是同种类型或有继承关系,即位于继承树的同一个继承分支上,否则编译报错。而在运行时, Java 虚拟机将根据两边的引用变量实际引用的对象进行比较。
假设有 4 个类 Creature 、 Animal 、 Dog 和 Cat 类,它们的继承关系如下
Creature
|
Animal
/ \
Dog Cat
以下代码中变量 dog 被声明为 Dog 类型,变量 animal 被声明为 Animal 类型,它们之间有继承关系,尽管这两个变量实际上引用的 Dog 对象和 Cat 对象之间没有继承关系 ,仍然可以用 “==” 表达式比较:
Dog dog = new Dog();
Creature creature = dog;
boolean res1 = dog == creature; //true 变量creature和dog引用同一Dog对象
Animal animal = new Cat();
boolean res2 = dog == animal; //false 合法比较,Animal类和Dog类具有继承关系
Cat cat = new Cat();
boolean res3 = cat == dog; //编译出错,Dog类和Cat类没有继承关系
操作符"==" 也可以用于数组类型。
boolean res1 = new int[4] == new long[4]; //编译出错,类型不一致
boolean res2 = new int[4] == new int[4]; //false 非同一对象
int[] arr1 = new int[4];
int[] arr2 = arr1;
boolean res3 = arr1 == arr2; //true
对象的 equals() 方法
equals()
方法是在 Object 类中定义的方法,它的比较规则为:当参数 obj 引用的对象与当前对象为同一个对象时,就返问 true, 否则返回 false。
public boolean equals(Object obj) {
if (this == obj) return true;
else return false;
}
在 JDK 中有一些类覆盖了 Object 类的 equals()
方法,它们的比较规则为:如果两个对象的类型一致,并且内容一致,即使不是同一个对象,也返回 true。这些类包括: java.io.File
、java.util.Date
、java.lang.String
、包装类。
Integer i1 = new Integer(1);
Integer i2 = new Integer(1);
boolean res1 = i1.equals(i2); //true 类型一致值相等
boolean res2 = i1 == i2; //false 非同一个对象
Float f1 = new Float("10F");
Double d1 = new Double("10D");
boolean res3 = f1.equals(d1); //false 类型不一致
boolean res4 = f1.equals(new Float("10")); //true
实际运用中 ,比较字符串是否相等时,通常都是按照内容来比较才会有意义,因此应该调用 String 类的 equals()
方法,而不是采用 “==” 操作符。
用户自定义的类中也可以覆盖 Object 类的 equals() 方法,重新定义比较规则,例如以下 Person 类的 equalsO方法的比较规则为:只要两个对象都是 Person 对象,并且它们的 name 属性相同,那么比较结果为 true, 否则返回 false。
public class Person{
private String name;
public Person(String name) {
this.name = name;
}
public boolean equals(Object o) {
if (this == o)
return true;
if (!(o instanceof Person))
return false;
final Person other = (Person) o;
if (this.name.equals(other.name))
return true;
else
return false;
}
public static void main(String[] args) {
Person person1 = new Person("Tom");
Person person2 = new Person("Tom");
boolean bool1 = person1 == person2; //false
boolean bool2 = person1.equals(person2); //true
}
}
4.8 instanceof操作符
instanceof 操作符用于判断一个引用类型所引用的对象是否是一个类的实例,一个类的实例包括类本身的实例,以及所有直接或间接的子类的实例。instanceof 操作符左边的操作元是一个引用类型,右边的操作元是一个类名或接口名。形式如下
obj instanceof ClassName
obj instanceof InterfaceName
对于引用类型变量,Java 编译器只根据变量被显式声明的类去编译。instanceof左边操作元被显式声明的类型和右边操作元必须是同种类或有继承关系,即位于继承树的同一个继承分支上 ,否则编译出错。在运行时,将根据左边操作元实际引用的对象来判断。
Dog dog = new Dog();
boolean b1 = dog instanceof Dog; //true
boolean b2 = dog instanceof Creature; //true
boolean b3 = dog instanceof Cat; //编译出错 Dog Cat类之间没有直接或间接继承关系
Animal animal = new Dog(); //变量animal被声明为Animal类型,但实际引用Dog对象
boolean b4 = animal instanceof Cat; //false
应用:假定 Animal 类是非抽象类,允许实例化,以下 islnstanceOfAnimal()
方法实现了判断obj是否引用 Animal 类本身的实例。
public boolean isInstanceOfAnimal(Object obj) {
return obj instanceof Animal && !(obj instanceof Dog) && !(obj instanceof Cat);
}
数组类型也可以用 instanceof 进行比较。
4.9 变量的赋值和类型转换
“=” 操作符是使用最频繁的两元操作符,它能够把右边操作元的值赋给左边操作元,并且以右边操作元的值作为运算结果。同种类型的变量之间可以直接赋值, 一个直接数可以直接赋值给和它同类型的变量。当不同类型的变量之间赋
值时,或者将一个直接数赋值给和它不同类型的变量时,需要类型转换。类型转换可分为自动类型转换和强制类型转换。自动转换是指运行时, Java 虚拟机自动把一种类型转换成另一种类型,强制类型转换是指在程序中显式地进行类型转换。
char c = 'a';
int i = c; //把char类型变量c赋值给int类型变量i,变量i的值为97 自动转换
int a = 129;
byte b = (byte)a; //把int型变量a强制转换为byte类型,变量b的值为-127 强制转换
值得注意的是,在进行自动或强制类型转换时,被转换的变量本身没有任何变化。
整型、浮点型、字符型数据可以进行混合运算。当类型不 一致时,需要进行类型转换,从低位类型到高位类型会进行自动转换,而从高位类型到低位类型需要进行强制类型的转换。
自动类型转换
表达式中不同类型的数据先自动转换为同一类型,然后进行运算,自动转换总是从低位类型到高位类型。这里的低位类型是指取值范围小的类型,高位类型是指取值范围大的类型。操作元自动转换的规则如下:
(byte, char, short, int, long, float) op double -> double
(byte, char, short, int, long) op float -> float
(byte, char, short, int) op long -> long
(byte, char, short) op int -> int
(byte, char, short) op (byte, char, short) -> int //specail
byte, short 和 char 类型的数据在如 x++ 这样的一元运算中不自动转换类型。
short a = 1, b = 1;
short c = a + b; //编译出错, a+b 表达式的值为 int 类型
short c = (short) (a + b);
在给方法传递参数时,也会出现类型自动转换的情况,转换规则与赋值运算的自动转换规则相同。
void method(long param) {/* */}
int a = 1;
method(a); //int类型参数a会被自动转换为long类型
强制类型转换
如果把高位类型赋值给低位类型,必须进行强制类型转换,否则,编译会出错。
在给方法传递参数时,如果把高位类型传给低位类型,也需要强制类型转换。
void method(byte param) {/* */}
int a = 1;
method(a); //编译出错
method((byte) a); //合法
强制类型转换有可能会导致数据溢出或精度的下降,应该尽量避免使用强制类型转换。
int a = 256;
byte b = (int)a; //数据溢出,强制类型转换高位舍弃,变量b的值为0
long c = (long) 3.1415 //精度丢失,前置类型转换小数舍弃,变量c的值为3
引用类型的类型转换
引用类型的变量之间赋值时 ,子类赋值给直接或间接父类,会自动进行类型转换。父类赋值给直接或间接子类,需要进行强制类型转换。对于引用类型变量, Java 编译器只根据变量被显式声明的类型去编译。引用类型变量之间赋值时,“=” 操作符两边的变量被显式声明的类型必须是同种类型或有继承关系,即位于继承树的同一个继承分支上,否则编译报错。
Animal animal = new Dog(); //合法,变量animal被声明为Animal类型,引用Dog对象
Dog dog = new Dog(); //合法
animal = dog; //合法,把Dog类型赋值给Animal父类型,自动转换
dog = animal; //编译出错,把Animal类型赋值给Dog子类型,需要强制转换
dog = (Dog)animal; //合法
Cat cat = new Cat();
Dog dog = (Dog) cat; //编译出错,Cat Dog之间没有直接或间接继承关系,不能强转
在运行时, Java 虚拟机将根据引用变量实际引用的对象进行类型转换。以下代码编译成功,因为变量 cat 声明为 Animal 类型,变量 dog 声明为 Dog 类型, Dog 类与Animal 类之间有继承关系。但在运行时将出错,抛出 ClassCastException
运行时异常,因为 cat 变最实际上引用的是 Cat 对象, Java 虚拟机无法将 Cat 对象转换为 Dog 类型:
Animal cat = new Cat();
Dog dog = (Dog)cat; //编译成功,但运行时抛出异常