004 Java运算符

JavaSE 第三讲:运算符 Operator

 

1. 当有若干个变量参与运算时,结果类型取决于这些变量中表示范围最大的那个变量类型。比如,参与运算的变量中,有整型int,有双精度浮点型double,有短整型short,那么最后的结果类型就是double。

2. int a = 1;

int b = 2;

double c = (double)a / b;

(double)a 并不是表示把a转换成double类型,而是生成一个匿名变量

上面的代码中,a与b都是整型,但是通过(double)a这种转换将a转换为一个匿名的变量,该变量的类型是double,但是要注意:a本身依旧是int类型,而不是double类型,这样,(double)a / b就是double类型除以int类型,结果自然是double类型。

3. 取模运算符:使用%表示。

int a = 5;

int b = 3;

int c = a % b;

上面代码的运行结果是2,因为5除以3结果是1余2。

取模的规律:取模的结果符号永远与被除数的符号相同

int a = 5;

int b = -3;

int c = a % b;

被除数是5,那么取模的结果是2

int a = -5;

int b = 3;

int c = a % b;

被除数是-5,那么取模的结果是-2

int a = 5;

int b = 3;

int c = a % b;

被除数是5,那么取模的结果是2

int a = -5;

int b = -3;

int c = a % b;

被除数是-5,那么取模的结果是-2。

 

 

4. 关系运算符:大于(>)、小于(<)、等于(==)、不等于(!=)、大于等于(>=)、小于等于(<=),关系运算的结果是个boolean值。

5. 逻辑运算符:重点讲解两个,逻辑运算符本身也返回一个boolean值。

1)逻辑与:使用&&表示,逻辑与是个双目运算符(即有两个操作数的运算符),只有当两个操作数都为真的时候,结果才为真;其余情况结果均为假。逻辑与表示的并且的意思

2)逻辑或:使用||表示,逻辑或也是个双目运算符,只有当两个操作数都为假的时候,结果才为假;其余情况结果均为真。逻辑或表示或者的意思。

6. 关于逻辑运算符的短路特性

1)逻辑与:如果第一个操作数为false,那么结果肯定就是false,所以在这种情况下,将不会执行逻辑与后面的运算了,即发生了短路。

2)逻辑或:如果第一个操作数为true,那么结果肯定就是true,所在在这种情况下,将不会执行逻辑或后面的运算了,即发生了短路。

7. 关于变量的自增与自减运算。

1)关于int b = a++,作用是将a的值先赋给b,然后再让a自增1.

2)关于int b = ++a,作用是将a的值先自增1,然后将自增后的结果赋给b。

 

算数运算符

 

编程过程中,基本上都是对数据对象的运算操作。

 

在Java编程语言里面,对象和数据的处理是通过运算符来完成的。运算符接受一个或多个操作数,并通过运算产生新值。

 

这里先说说最常见的Java算术运算符,完成基本的数学运算。

 

Java使用常见的算术操作符+、-、*、/来进行加、减、乘、除运算。

 

整数的求余操作(mod)用百分号%来表示。

 

这些算术运算符可以用于java基本数据类型中的数值型(byte,short,char,int,long,float,double)数据。

 

对于+、-和*运算符,都是非常容易理解的,它们分别接受两个操作数,通过运算后返回得到的新值。

 

我们知道,在数学计算中,一个数除以0是无意义的。

 

在Java中,对于以0作为除数的情况,根据操作数的数据类型,做了不同的处理:

 

对于整形的运算,它将会出现异常;而对于浮点型数据的运算,它将得到一个无穷大值或者NaN

 

至于什么时候是无穷大值,什么时候是NaN,可以自己编程运算试一试。提示一下,主要注意被除数的区别。另外,异常的概念在之后的内容会详细说明,现在可以把异常看做是一个例外,非正常情况即可。

 

下面来说说取模运算。

 

所谓“取模”操作,简单而言就是获得一个除法运算的余数。与其它语言不同,对于取模运算符来说,其操作数可以是浮点数,例如:10.6%3的结果为1.6,10%3.5的结果为3.0。

 

另外,因为取模运算也会执行除法操作,所以,对于整形数据来说,也不能使用0作为取模运算中的“除数”,否则也会出现和除法运算一样的异常。

 

+、-、*、/、%运算如果用在赋值语句中,还可以使用二元运算符的简捷方式来实现,比如:
x = x+5;


可以使用如下的运算式来表示:
x +=5;


它们在运算结果上是相等的。其他四个运算符也可以像上面这个例子中的运算符一样使用,也就是说,将运算符放在等号“=”的左边,如:*=、/=等。


递增递减运算符

 

在编写Java程序的时候,经常需要对一个变量加一或者减一,这个时候通常使用递增或递减运算符来完成。

 

递增和递减操作符有两种形式:前缀和后缀。前缀就是将运算符放在操作数前面,而后缀将运算符放在操作数后面。

 

int k =20;
k++;
此时,k的值为21。


前缀方式和后缀方式的作用都是对操作数加上或减去1,区别在于用在表达式中的时候。如:
int m = 10;
int n = 10;
int p = 2*++m;
int q = 2*n++;


此时,p的值是22,而q的值是20。m和n的值都是11。

 

这是因为,在进行p = 2*++m运算时,程序会先将m加上1然后再进行乘法运算。

 

而对于q=2*n++的后缀递增运算,则会首先取出n的数值进行乘法运算然后再将n递增1。所以,此时p的值是22(p=2*(10+1))而q的值是20(q=2*10),m和n的值却都为11。

注意,递增/递减操作符只能用于变量而不能用在数字本身,如,下面的用法是错误的:
 10--;
 5++;

因为本质上递增运算符是对变量做增一或者减一处理。比如 x++ 可以等价为 x=x+1;但是5++等价为5=5+1;很显然,这是错误的。


关系与布尔运算符

 

1.关系运算符

 

在Java中,提供了完整了关系运算符。Java中,关系运算符包括:>,<,>=,<=,==,!=,用来对两个简单类型操作数进行比较运算,所组成的表达式结果为boolean类型的值true或false。

注意:除了“==”和“!=”外,其他的关系运算符都不能用在boolean类型的操作数中。

 

在这边需要提醒注意的是,在Java中,“不等于”是用“!=”表示的,而不是一些编程语言的“<>”,而等于也是和一些编程语言不一样,它使用“==”而非“=”,在Java中,“=”用于赋值操作,而非关系运算符。


“==”和“!=”除了用于简单类型的操作数外,还可以用于比较引用类型数据。

 

2.布尔运算符

 

布尔运算符也称逻辑运算符,包括:!,&,|,^,&&,||,这些运算符分别实现“非”、“与”、“或”、“异或”、“短路与”、“短路或”等逻辑运算。

 

和关系运算一样,逻辑运算结果也是布尔类型值true或false,Java语言中逻辑运算规则与其它语言中的相近。


在布尔运算符中,需要特别说明的是,短路与“&&”和短路或“||”,这两个运算符是按照“短路”的方式进行求值的,也就是说,如果第一个表达是已经可以判断出整个表达式的值时,就不进行后面的运算了。

 

例如,当对表达式a&&b进行运算时,如果表达式a的值为false,将不对b的值进行计算。而当对表达式a||b进行运算时,如果a 的值为true,将不对b的值进行计算。

 

请看下面的例子。

 

public class ShortCircuit {
 public static void main(String[] agrs) {
  ShortCircuit sc = new ShortCircuit();
  System.out.println("短路或运算");
  System.out.println(sc.Ma() || sc.Mb() || sc.Mc());
  System.out.println("短路与运算");
  System.out.println(sc.Ma() && sc.Mb() && sc.Mc());
 }

 boolean Ma() {
  System.out.println("Ma()被调用!");
  return 1 < 2;// true
 }

 boolean Mb() {
  System.out.println("Mb()被调用!");
  return 1 == 2;// false
 }

 boolean Mc() {
  System.out.println("Mc()被调用!");
  return 1 > 2;// false
 }
}

上面的程序中,因为方法Ma()的值为true,而“或”运算中如果有一个表达式为真(true),则整个表达式均为真(true),因此,无需计算后面方法Mb()和方法Mc()这两个表达式就可以得到整个表达式的值了。

 

而第二条“短路与”语句,因为在逻辑“与”运算中,只需要一个表达式的值为假(false),则整个表达式的值都为假(false)。Ma()为真(true),所以将进行第二个表达式的运算,它将调用方法Mb(),而此时,Mb()方法的返回值为假(false),所以将不用进行后面的运算了。

编译运行上面的程序,将得到如下的输出:
短路或运算
Ma()被调用!
true
短路与运算
Ma()被调用!
Mb()被调用!
false


注意:运算符“&&”和“&”、“||”和“|”所求得的结果是一样的,它们的区别在于,“&”和“|”不会进行“短路”运算,而是会计算运算符两边的各个参数的值。

 

在实际编程中,如无特殊要求,建议均使用短路运算符。

 

因为在实际编程中,如果需要对两个逻辑布尔表达式进行与或运算,第一点,使用短路运算,在可以确认布尔运算结果的情况下,不需对第二个布尔运算进行计算,节省了运算资源时间。更重要的一点,很多时候两个布尔逻辑表达式是相关联的。

 

比如在一个与运算中,第一个布尔运算为true的情况下,再进行第二个布尔运算,从而得到结果是合理的。如果第一个已经是false了,若不是短路运算符的话,仍会执行第二个布尔运算,但实际情况是,第一个运算不通过的话,第二个布尔运算是会发生异常的。

 

看下面的例子。以Student类为例:

 

Student s = null;

 

if(s !=null && s.getAge()>18){

  System.out.println("成年");

}

 

如果不使用短路与,在s为null,最终结果肯定是false的情况下,Java运行仍会执行第二个布尔运算,但是此时s为null,调用null引用对象,会发生空指针引用异常的。

 

但如果使用短路运算,则不会进行第二个布尔运算,就不会发生这种情况了。避免发生难以发觉处理的异常信息。


三元运算符

 

大部分的运算符都是需要两个操作数来完成运算。比如前面提到的算术运算符,布尔运算符等,递增递减运算符则只需要一个操作数即可。根据运算符完成运算需要的操作数个数,将该运算符归为*元运算符,*记为运算操作数个数。也有称之为”目“的。

 

这里要说的是三元运算符,即该运算符完成运算需要3个操作数。这在Java运算符中仅此一例,所以它没有自己独有的名字,而是直接叫做三元运算符。

 

三元运算符“?:”,注意在这个运算符中,有2个符号组成。这个运算符的用法如下:
 condition?a:b


它的意思是,如果条件condition(布尔表达式)为true,则表达式的值为a,否则,表达式的值为b。我们来看一个简单的例子:
x>y?x:y


上面的表达式将返回x、y两个操作数中比较大的一个。比如,如果x等于5,y等于9,则x>y为false,那么,表达式的值为y;而如果x等于8,y等于4,则x>y为true,那么表达式的值为x。

 

在之后学习过Java分支控制语句之后,就会明白,它是直接等价于简单的逻辑判断赋值分支语句的。可以看做是等价的简化版本。


位运算符

 

Java中的位运算符,基本上除了涉及到比较底层,强调效率性能的部分之外,其他地方很少用到的,特别是做应用开发。这里只是简单的介绍一下,了解一下。

 

在操作整型数据时,可以使用运算符直接处理组成这些整数的各个二进制位。适用的数据类型有:byte、short、char、int、long。


位运算符“&”(与)会在两个操作数都为1时,返回一个1的输出值,而在其他情况下为0;

 

位运算符“|”(或)会在两个操作数中有一个为1时,返回一个1的值;

 

位运算符“^”(异或)会在两个操作数中有且只有一个为1时,返回1;

 

而位运算符“~”(取反)是一个单目运算符,它只有一个操作数,将返回位操作数的“相反值”,如操作数为1,则取反操作后返回值为0。

 

可以对比布尔运算符来看。把1看做true,把0看做false。这样就可以根据布尔运算的规则来理解了。

注意:
^、&和|也是布尔运算符。在布尔运算中,^、&和|会产生布尔值。这种运算符的处理就是运算符的重载了。不过Java中不允许自行进行运算符的重载扩展,和C++是不一样的。


在位运算符中,~(取反)是单目运算符,也就是它只有一个操作数。


移位运算符

 

移位运算符和位运算符使用频度差不多,基本上很少用到。这里也是简单提一下。

 

在n久之前,很多Java面试题会有这样类型的题目:以最有效率的算法完成乘法或者除法,这里就需要用到移位运算符,不过这里除数都是2的倍数。

 

一个字节由八个位(bit)组成,每个位(bit)可以为1或者0,整个数的值通过使用以2为基数的算法来决定。也就是说,最右边的为代表值1或者0;下一个位表示2或者0;再下一个表示4或者0… …,第n位表示2(n-1)或者0。


在Java中,除了Char类型外的其他整型数据,所有的整型数据类型的最左边都作为符号位。如果符号位是1,这个数就是负数,并使用补码来表示,即通过将各个位的值取反然后再加1来表示一个负数。例如:
 1=0… …001
 -1=1… …1111
 2=0… … 0010
 -2=1… …1110


移位运算将左操作数向左或向右移动右操作数给定的位数。


我们来看下面的例子:
 18<<2


因为整数的值被解释int类型,所以18被当作32位的数。因为18的高24位全部为0,所以,为简化起见,只考虑低8位:
 00010010


在<<操作过程中,它在低位插入右操作数指定的个数的0,同时扔掉相同位数的高位,因此,经过左移操作后的二进制表示为:
 01001000


它的十进制的值为2(7-1)+2(4-1) =72。


>>运算符将左操作数向右移动右操作数给定的位数,而扔掉相同位数的低位。向右移动后高位“腾出”的空间用全1或全0来填充。用0或1取决于原来这个左操作数最高位的值,如果最高位是1,则用全1来填充,否则,则用0来填充。这样,原来数据中的符号就不会丢失了。

 

例如,原来的数为负数,其最高位为1,通过>>操作后,最高位还是1,它还是负数。因此,>>被称为“有符号右移运算符”。

 

>>>运算符允许我们将有符号数当作无符号数来进行(向右)移位操作。当一个数被>>>向右移位时,低位数被丢弃,而在“腾出来”的高位填充上0。这样,无论这个数原来是否有符号,经过>>>移位后,都变成了正数。

注意:
没有与>>>对应的<<<操作。因为左移补位一定是0,没有疑义。


移位运算符<<、>>和>>>用于对整型数据进行按位移位操作,适用的数据类型有:byte、short、char、int、long,其中对于低于int型的操作数,将自动转换为int型,然后进行移位操作,最终得到的结果为int型。
“a<<b;”将二进制形式的a逐位左移b位,最低空出的b位补0;
“a>>b;”将二进制形式的a逐位右移b位,最高位空出的b位补原来的符号位;
“a>>>b;”将二进制形式的a逐位右移b位,最高位空出的b位补0。

 

对于int型以及低于int型的整数a进行移位(b位)时,系统先将b对32取模,得到的结果才是真正移位的位数,例如“127>>32”的结果是127(实际右移32%32=0位)。对于long型整数移位时,则是先将移位位数b对64取模,得到的结果才是实际移位的位数。


赋值运算符

 

我们在前面的学习中,其实已经在很多的地方都用到了赋值运算符。赋值运算符“=”将“=”右边的值赋给(更准确说是“复制到”)左边的变量。

 

“=”右边的值可以是任何的变量、常量或者一个可以产生值的表达式,而“=”的左边必须是一个明确的、命名的变量,但不可以为常量,如i = 100是合法的,而100 = i 却是不允许的。


对于基本数据类型的赋值,它非常的简单,它直接将“=”右边的值复制到左边的变量中;对于引用数据类型的赋值,操作的并非是对象本身,而是它的“对象引用”,它实际上是将“=”右边的引用(而非对象本身)复制到左边的变量中。

 

下面以一个示例来进一步说明。

 

public class Assignment {
 public static void main(String[] args) {
  // 简单数据类型
  int a, b = 100;
  a = b;
  b = 20;
  System.out.println("a = " + a);
  System.out.println("b= " + b);

  Person p1 = new Person(100);
  Person p2;
  p2 = p1;
  p1.setPersonId(111);
  System.out.println("p1的PersonId=" + p1.getPersonId());
  System.out.println("p2的PersonId=" + p2.getPersonId());
 }

}

class Person {
 private int personId;

 // 构造器
 public Person(int id) {
  personId = id;
 }

 public int getPersonId() {
  return personId;
 }

 public void setPersonId(int id) {
  personId = id;
 }
}

在这个示例中,我们定义了一个类“Person”,它有一个“personId”的属性。在类“Assignment”的main()方法中,我们定义了两个int简单数据类型的变量a、b,并给b赋值100,然后将b的值赋给变量a,此时实际上是将b的值的一个“副本”拷贝给了a,因此,a和b中任何一方的变化,都不会影响到另一方;

 

另外,我们还定义了两个Person引用类型的变量p1、p2,并给p1初始化了一个对象引用,然后,将p1的值赋给p2,此时,这个操作实际上是将p1的对象引用复制给了p2,此时,p1和p2所指向的是同一个对象!因此,无论通过变量p1还是p2去改变对象,都是改变的同一个对象。

编译并运行上面的类“Assignment”,将得到如下的输出:
a = 100
b= 20
p1的PersonId=111
p2的PersonId=111

 

将赋值运算符和其他的运算符结合起来,就可以作为一种特别的“扩展”赋值运算符。扩展赋值运算符有如下这些:+=,-=,*=,/=,%=,&=,|=,^=,>>=,<<=,>>>=等。

 

注意,并非所有的运算符都可以和赋值运算符结合成扩展赋值运算符。


扩展赋值运算符的引入只是为了简化赋值运算的表达形式,将“a=a operator b;”简化为“ a operator=b;”,其作用是相同的。


字符串连接运算符

 

这个运算符可以说是在之后的编程中,运用最多的运算符了,根据印象来看的话,仅次于赋值运算符。

 

这个运算符也是重载运算符,重载了+运算符。在前面也提到了,Java中再布尔运算符和位运算中,实现了不少运算符的重载。但是由于这些位运算符使用频率极低,人们几乎都忘了Java还有这么一类运算符,以至于很多时候,会把字符串连接运算符认为是Java中唯一重载的运算符。

 

那么当Java源代码中出现了+运算符后,它到底是加法运算符还是字符串连接运算符?这里就是Java编译器运行判断的基本原则:

 

当“+”用在表达式中的时候,如果其中有一个操作数是字符串类型(String),则Java会自动将另一个操作数也转换成字符串,然后将这两个字符串相连起来生成一个新的字符串。

 

所以这里有一个小技巧,如果你想把一个非字符串类型的数据对象变成字符串类型,那么可以通过将数字和一个空字符串相连的方式,来方便的将数字转换成字符串类型。

 

比如: int i=5;

          String s = i+"";

 

这样就很容易的实现了字符串类型的转换了。这在Java编程中是一个很实用的技巧。

 

下面来看一个例子,来分析一下:

 

public class StringPlus {
 public static void main(String[] agrs) {
  double x = 9.987;
  double y = 1;// 自动将int型的数值1提升到double类型1.0
  double t = x + y;
  String s = "Price is: " + x; // 得到一个字符串:“Price is:9.987”
  String st = "Total Price is: " + t;
// 得到一个字符串:“Total Price is:10.987”
  System.out.println(s);
  System.out.println(st);
  System.out.println("" + x + y);// 打印出一个字符串:“9.9871.0”
  System.out.println(x + y + "");// 打印出一个字符串:“10.987”
 }
}

 

从上面的例子中,我们可以看到,String和一个数字类型的数据进行“+”运算,将会得到一个新的字符串,这个字符串由旧的字符串和这个数字组成。


再来看这行程序:
 System.out.println(""+x+y);


根据运算符从左到右的结合原则,空字符串“""”首先和x进行运算,得到一个字符串,这个字符串的内容就是“9.987”,然后,这个字符串再和数字y进行运算,此时得到一个由x和y组合成的新的字符串:9.9871.0。


比较一下下面这条语句:
  System.out.println(x+y+"");


这条语句首先进行数值的相加运算,得到一个新的数值:10.987,然后再和空字符串进行连接运算,此时得到一个新的字符串,内容为“10.987”。


运算符的结合性

 

除了上面的这些运算符外,Java还提供其他非常丰富的运算符来进行其他的运算。


Java软件运算符在风格和功能上都与C和C++极为相似。按优先顺序列出了各种运算符(“L to R”表示左到右结合,“R to L”表示右到左结合)


分隔符:  []   ()   ;   , 
 R to L  ++  -- + - ~ ! (data type) 
 L to R  *  /  % 
 L to R  +  - 
 L to R  <<  >>  >>> 
 L to R  <  >  <=  >= instanceof 
 L to R  ==  != 
 L to R  & 
 L to R  ^
 L to R  |
 L to R  && 
 L to R  ||
 R to L  ?: 
 R to L   =   *=   /=   %=   +=   -=     <<=   >>=   >>>=   &=   ^=   |=


注意:


虽然instanceof这个运算符看起来确实很奇怪,但它确实是Java编程语言特有的运算符。


---------------------------------------------------

Java SE 第三讲:运算符 Operator

1. 当有若干个变量参与运算时,结果类型取决于这些变量中表示范围最大的那个变量类型。比如,参与运算的变量中,有整型int,有双精度浮点型double,有短整型short,那么最后的结果类型就是double。
2. int a = 1;
int b = 2;
double c = (double)a / b;
(double)a 并不是表示把a转换成double类型,而是生成一个匿名变量。
上面的代码中,a与b都是整型,但是通过(double)a这种转换将a转换为一个匿名的变量,该变量的类型是double,但是要注意:a本身依旧是int类型,而不是double类型,这样,(double)a / b就是double类型除以int类型,结果自然是double类型。
3. 取模运算符:使用%表示。
int a = 5;
int b = 3;
int c = a % b;
上面代码的运行结果是2,因为5除以3结果是1余2。
取模的规律:取模的结果符号永远与被除数的符号相同
int a = 5;
int b = -3;
int c = a % b;
被除数是5,那么取模的结果是2
int a = -5;
int b = 3;
int c = a % b;
被除数是-5,那么取模的结果是-2
int a = 5;
int b = 3;
int c = a % b;
被除数是5,那么取模的结果是2
int a = -5;
int b = -3;
int c = a % b;
被除数是-5,那么取模的结果是-2。


4. 关系运算符:大于(>)、小于(<)、等于(==)、不等于(!=)、大于等于(>=)、小于等于(<=),关系运算的结果是个boolean值。
5. 逻辑运算符:重点讲解两个,逻辑运算符本身也返回一个boolean值。
1) 逻辑与:使用&&表示,逻辑与是个双目运算符(即有两个操作数的运算符),只有当两个操作数都为真的时候,结果才为真;其余情况结果均为假。逻辑与表示的并且的意思

2) 逻辑或:使用||表示,逻辑或也是个双目运算符,只有当两个操作数都为假的时候,结果才为假;其余情况结果均为真。逻辑或表示或者的意思。
6. 关于逻辑运算符的短路特性。
1) 逻辑与:如果第一个操作数为false,那么结果肯定就是false,所以在这种情况下,将不会执行逻辑与后面的运算了,即发生了短路。
2) 逻辑或:如果第一个操作数为true,那么结果肯定就是true,所在在这种情况下,将不会执行逻辑或后面的运算了,即发生了短路。
7. 关于变量的自增与自减运算。
1) 关于int b = a++,作用是将a的值先赋给b,然后再让a自增1.
2) 关于int b = ++a,作用是将a的值先自增1,然后将自增后的结果赋给b。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值