J2EE企业级开发基础教程(4) - 变量及运算符的基本操作

1 变量的基本操作

介绍完八种基本类型,接下来我们看看对变量的一些基本操作。

1.1 强制类型转换

我们在 上一节中介绍,表示范围大的类型,不能给表示范围小的类型赋值。例如,下面的代码就会产生一个编译错误:
int a = 10;
byte b;
b = a; //!编译错误,
int 类型不能 直接给byte 类型赋值 编译出错的原因,在于 int 类型中能够保存的数值,有可能远大于byte类型的表示范围。就好像如果要把一个大汤盆中的东西倒到一个小碗中,很有可能会出问题。这时候,编译器 就会阻拦我们的这个操作。
但是,有些情况下,大汤盆中的东西只剩一点儿了,这个时候完全可以把大汤盆中的 东西倒到小碗中。对于上面的代码来说,虽然int的表示范围远大于byte类型,但是我们可 以确认,int变量a 中的数据,完全可以让一个byte变量来保存。这种情况,我们不希望编译器阻拦我们。因此,可以使用强制类型转换。
强制类型转换的语法如下:
(类型)变量
这个语法表示,把变量的值强制转换为某个特定的类型。例如上面的代码,我们要把a的值强制转换为byte类型,再给b赋值,则可以把代码写成:
b = (byte) a;
需要注意的是,上面的语法转换的是a变量的值。在执行过程中,会先分配一个byte类型的临时空间,然后把a的值转换成byte类型,然后放到这个临时空间去,最后把临时空间的值赋值给b。在转换过程中,并没有改变a变量的类型和值。
那a变量有4个字节,怎么转换成为1个字节的byte类型呢?Java会把a变量4个字节中,最后的一个字节取出来,然后把这个字节的值,放到临时空间去。而另外的3个字节的内容,则在强制类型转换的时候被舍弃。
因此,如果a中数值的范围超过了b所表示的范围,使用强制类型转换也能正常编译,但是转换的结果会有问题。例如
  int a = 150;
  byte b = (byte) a; // 编译通过,但是 b 的值为一个错误值
  System.out.println(b); // 输出为 - 106
为什么一个超过表示范围的正数,转换之后会变成一个负数呢?这涉及到在计算机中如何用二进制表示正数。例如,下面的代码:
short s = 450;
byte b = (byte) s;
首先我们来看short类型的变量s。一个short类型占2个字节,一个字节是8个二进制位,因此一个short变量占16个二进制位。这16个二进制位中,有15个字节是表示数值,最高的一个二进制位表示的是数值的符号。因此,整数的最高位也被称之为符号位。如果一个整数是正数,则它的符号位为0;相反,负数的符号位为1。
450是正数,因此其符号位为0。然后,把十进制数450,转换成二进制之后,得到结果111000010。然后,除了符号和数值位之外,在其余位上补0。最后,450在内存中表示如下:
0000000111000010
然后,把s强制转换成byte类型。此时,会开辟1个字节的空间,并把short类型最低8位取出来,得到结果如下:
11000010
可以看到,在转换过程中,舍弃了short变量的部分内容。
然后,11000010这个数字,被当做byte类型时,首先要判断它的符号。由于这个数字的最高位为1,因此java会把这个数当做是负数来处理。负数的表示方式与正数不同,相对比较复杂,在此不多介绍。需要明确的是,在强制类型转换时,如果对超过表示范围的数做强制类型转换,有可能产生一些意想不到的情况,例如正数变成负数,或者负数变成正数。因此,使用强制类型转换应当要注意,避免发生类似的错误。

1.2 自动类型提升

请看下面的一段代码:
byte a = 10, b = 20;
byte c = a+b;
对于大部分语言而言,这都应该是一段合法的代码,而对于Java语言来说,这段代码会出一个编译时错误:

要理 解产生这个错误的原因,就要了解 Java 的自动类型提升特性。我们首先来看下面 这个表达式:
byte a = 10, b = 20, c = 40;
byte d = a + b + c;
在运行上面的代码时,首先把 a b c 三个变量的值分别设为 10 20 40 。然后,在 计算 d 的值时,首先需要计算“ = ”右边表达式的值。由于这个表达式有两个“ + ”,因此在 计算的时候,需要进行两次加法操作:首先计算 a+b 的值,在计算出结果之后,再把结果和 c 的值相加。为此, Java 会首先获得 a 变量的值 10 ,然后获得 b 变量的值 20 ,之后 进行第 一次加法,计算出结果 30 。因为这个结果在后面的运算中还要使用,所以, Java 会把这个 数值保存在一个临时变量中。然后, Java 进行第二个加法运算时,首先取出临时变量的值 30 ,然后取出 c 变量的值 40 ,再计算出结果是 70 70 作为整个“ a+b+c ”表达式的值,也 会存在一个临时变量里。最后,把这个临时变量的值赋值给 d 变量。
可以这么来理解上述的过程:就好比我们在做 a+b+c 这道数学题时,首先,会把 a+b 的值计算出来,计算出来之后,会把这个值暂时写在草稿纸上。再然后,计算这个写在草稿 纸上的值和 c 相加的结果,并把 所得到的最终结果也写在草稿纸上。最后,再把草稿纸上的 值,重新写回到考卷上。
因此, Java 遇到例如 c = a + b 这样的式子时,会首先计算等号右边的 a+b 的值,然 后赋值给等号左边的变量 c 在计算过程中, Java 会创建一个 临时 变量 来保存 a+b 这个表达 式的计算结果,然后把这个临时变量 中的值赋值给 c 。基本过程如下:
计算 a+b 的值 -- > 保存到临时 变量 -- > 把临时 变量 的值赋给 c
问题在于, 尽管 a b 两个变量都是 byte 类型,但是 Java 为临时变量选择类型时,会 将这个类型“自动的提升”为
int 类型 ,大小 4 个字节。于是,上述过程中的第三步,就 成了把一个 int 类型赋值给 byte 类型的操作,从而产生一个编译时错误。这就是 Java 语言 中的自动类型提升特性。
要避免这个问题,只需要对其结果进行强制类型转换即可。即把原代码改为:
byte c = (byte)(a+b);
需要注意的是,由于是对 a+b 的结果进行强制转换,因此要对 a+b 这个表达式加上括号。

1.2.1 Java自动类型提升的规则

1. 如果运算数中存在 double ,则自动类型提升为 double
2. 如果运算数中没有 double 但存在 float ,则自动类型提升为 flo at
3. 如果运算数中没有浮点类型,但存在 long ,则自动类型提升为 long
4. 其他所有情况,自动类型提升为 int
换而言之, byte + byte byte + short 之类的运算,都会被自动提升为 int 类型。需要说 明的是, char 类型也能进行运算,并且 char 类型与其他类型运算时,也会进行相应的自动 类型提升。

2 运算符

在介绍 运算符 之前,首先介绍一个非常基本的概念:表达式。
所谓的表达式,指的是用 运算符 连接变量或字面值所形成的式子,例如: a+b 2+c 等。 需要强 调的一点是,任何一个 表达式 会有 一个 值。也可以理解为,所有表达式都会返回一 个结果,例如 1+2 会返回 3 作为表达式的结果。 表达式的值也有不同类型。例如,布尔表达式,就说明这个表达式的值的类型为 boolean 类型。因此,在写程序的过程中,一定要明确表达式的值,以及这个值的类型。

理解了表达式的概念,我们来关注形成表达式的关键元素: 运算符。

2.1 赋值号(=

赋值操作是编程中最常用的操作之一。 Java 中的赋值号为一个等号( = )。赋值号具有 右结合性,也就是说,会先计算赋值号右边的内容,然后把计算 结果赋值给左边的变量。 此外, a = b 构成一个赋值表达式, 其作用就是将变量 b
中的值赋值给变量 a 这个表达 式也有值,表达式的值为赋值号右边的计算结果。
由这 个特性,在 Java 中可以进行连等操作,即: a = b = c = 10 这样的赋值操作。

2.2 基本数学运算

基本数学运算包括加( + )减( - )乘( * )除( / )以及取余( % )操作。这些操作和数 学上的定义没有区别。
需要注意的是,类似于 3/2 这样的表达式。由于 3 2 都是整数类型,因此根据自动类 型提升的规则,结果也一定是整数类型。 因此, 3/2 这个表达式的值是 1 ,如果希 望得到数 学上精确的结果 1.5 则需要使用 3.0/2 这样的表达式 ,来保证结果数据是 double 类型的 此外, Java 中的 数学运算符 符合先乘除,后加减的规则。
例如: 2+3*4 这个表达式的 值为 14

2.3 +=*=/=

在实际编程中,经常会写出类似于 a = a + 2 之类的表达式,表示把 a 在原有值的基础上 增加 2 。这种写法有一种更方便的简写形式: a += 2 - = *= /= 运算符 += 类似。

2.4 ++--

对于a+=1这样的表达式而言,还有一种更加简单的运算符:++,与之类似的,a-=1可以用a--来代替。要注意的是,使用++(或--)运算符有两种方式:前缀式或后缀式。例如:
a++; //后缀式
++a; //前缀式
要注意的是这两种方式的区别和联系。首先,对于a++和++a这两种方式而言,对a的操作是完全一样的,都会在表达式运算结束之后把a的值加1。这两种方式所不同的地方在于表达式的值不同。
例如,假设a = 5,则不管执行a++还是++a,执行之后a的值均为6。所不同的是,a++这个表达式的值为5(即a加1之前的值),而++a这个表达式的值为6(即a加1之后的值)。a--和--a有类似的关系。

2.5运算符

Java 语言中包含了四种位 运算符 :按位与 (&) ,按位或( | ),按位异或( ^ ),取反 ~ 运算符 主要用于对数据的每个二进制位进行运算。 首先,从逻辑上说,与、或、异或的逻辑运算如下表所示:
运算值1运算值2异或
11110
10011
01011
00000
可以来这么记忆:与运算只有在两者都为 1 时结果才为 1 ,或运算只要有一个为 1 结果 就为 1 ,异或运算两者不同结果为 1。

2.6 移位操作

Java中的移位运算符有三种:算术右移(>>),逻辑右移(>>>)和左移(<<)。移位操作是指,把一个整数的二进制表示形式,向某个方向(左或者右)移动,并按照一定的规律,丢弃和补充相应位上的值。
逻辑右移与算术右移的区别很微妙。对于n>>>m(逻辑右移)与n>>m(算术右移)来说,当n为正数时,两种运算的结果是一样的。所不同的是,当n为负数时,算术右移的结果为一个负数,并且是基本符合算术规律的(所以它叫算术右),而逻辑右移的结果为一个正数。例如下面的代码:
int n = 12;
System.out.println(n>>2); //结果为3
System.out.println(n>>>2); //结果也为3
n =-12;
System.out.println(n>>2); //结果为负
System.out.println(n>>>2); //结果为正!!!
产生这种不同的原因,与计算机中表示整数的方式有关。之前介绍过,在计算机中,保存一个整数时,整数的最高位用来表示符号。其中,符号位为0表示正数,1表示负数。在使用算术右移时,移动数值时不会改变符号位的值,因此正数移动之后,符号位为0,负数移动之后,符号位依然是1。这样,对一个整数进行算术右移之后,正数依然是正数,负数
依然是负数。
然而使用逻辑右移时,最高位总会补上0。对于正数来说,符号位没有改变;而对于负数来说,符号位由1
变成了0。因此,使用逻辑右移时,所得到的结果总是正数。

2.7 布尔运算

我们首先把所有布尔运算符分为两大类。
第一类运算符为:>(大于), >=(大于等于), <(小于), <=(小于等于), ==(相等), !=(不相等),这些运算符都接受两个参数,返回一个布尔值,表示判断结果。需要注意的是判断相等(==)是两个等号,一定要把这个布尔运算和赋值号(=)区分开来。
第二类运算符为:与(&&)、或(||)、非(!),这些运算符只能接受两个布尔类型的运算数。非运算符表示取反,即!true结果为false,而!false结果为true。对于与运算和或运算,运算规则见下表:
运算数1运算数2
truetruetruetrue
truefalsefalsetrue
falsetruefalsetrue
falsefalsefalsefalse

布尔运算与(&&)和位运算与(&)具有类似的运算结果,而布尔运算或(||)和位运算或(|)也具有类似的运算结果。所不同的有两点:
1、布尔运算(&&和||)只能接受布尔值作为运算数,而位运算除了能进行布尔值的运算之外,还能进行整数运算;
2、布尔运算具有短路特性。
什么是短路特性呢?对于与运算而言,如果运算数1的值为false,则无论运算数2的值是true还是false,结果一定是false。因此,如果计算机遇到第一个运算数为false的与(&&)操作,则不会去查看第二个运算数而直接返回。这就是所谓的短路特性。
“与运算”能被false短路,相对应的,“或运算”能被true短路(如果第一个操作数为true,则不管第二个操作数如何,结果一定为true)。在Java中,布尔运算(&&和||)具有短路特性,而位运算(&和|)不具有短路特性。
短路特性和++(--)运算符结合在一起会产生很多有趣的式子。例如下面的式子:
int a = 4, b = 5;
boolean flag = (a++>4) && (b++>3);
请读者思考一下,这段代码运行结束之后,flag、变量a、变量b的值分别是什么?
答案:flag为false,a的值为5,b的值也为5。
说明:在上述代码中,有两个括号,这两个括号的值会由左向右依次执行。
首先,计算a++>4。在计算这个式子时,首先会执行a++,执行完这个表达式之后,a的值变为5。但是,a++这个表达式的值是4,而4>4为false,因此第一个括号中,表达式的值为false。之后,由于后面遇到的是“&&”运算符,这个运算符能够被false短路,因此,第一个括号中的表达式计算出是false之后,第二个括号中的代码不会执行,因此b++这个代码没有被执行,因此b的值没有被改变。可是,如果表达式变为:
int a = 4, b = 5;
boolean flag = (a++>4) &(b++>3);
由于位运算符 & 不具有短路特性,因此,尽管第一个表达式已经被计算 出是 false ,第二 个括号中的代码依然会执行。最终的结果, b
的值会变化为 6

2.8 三元操作符  ?:

Java中只有唯一的一个三元运算符,其基本语法如下:  
布尔表达式 ? 表达式1 :表达式2
说这个运算符是三元运算符,指的是这个运算符在使用时,能够接受三个部分参与运算。
从语法上说,第一部分是一个布尔表达式,第二、第三个部分分别是一个表达式。第一部分和第二部分用
“?”隔开,第二部分和第三部分用“:”隔开。这三个部分构成一个完整的三元表达式。
第一部分布尔表达式有一个值,这个值要么是true,要么是false。而表达式1有一个值,表达式2也有一
个值。这三个值参与运算,最后能够得出整个三元表达式的值。
整个表达式的值由下面的原则确定:
如果布尔表达式的值为true,则整个三元表达式的值为表达式1的值;如果布尔表达式的值为false,则整
个三元表达式的值为表达式2的值。例如下面的代码:
c = a>b ? a-b : b-a;
上面的代码,a>b是布尔表达式,a-b是表达式1,b-a是表达式2。而整个表达式的值要么是a-b的值,要么
是b-a的值。 得到结果之后, 再把计算所得的值赋值给变量c。
那么上面的代码完成什么功能呢?我们分情况讨论。如果a大于b,则(a>b)这个表达式的值为true,这样,
整个表达式的值就是a-b。 如果a小于b,则(a<b)这个表达式的值为false。因此,整个三元表达式的值就
是b-a。
综上所述,无论a和b的值是多少,上述代码都能让a和b这两个变量中,较大的变量减去较小的变量,
并把获得的差赋值给c变量。

3 运算符的优先级

在我们小学学习四则混合运算的时候,老师曾经反复的跟我们说,一定要注意,先乘除,
后加减。对于下面的式子:
2 + 3 * 2
这个式子要先计算3*2,再把所得到的结果与2相加。在这个运算的过程中,“先乘除,后加减”,
体现的就是运算符优先级的思想:乘除法的优先级比较高,如果有乘除运算的话,应当先计算。
在Java中,同样有运算符优先级的概念。我们把本章提到的运算符的优先级罗列如下:
优先级运算符说明
1()最高优先级
2!,~,++,--除了括号外,一元操作符优先级最高
3*,/,% 
4+,-Java中同样满足先乘除,后加减
5<<,>>,
>>>
移位操作
6<,<=,>,>=比大小
7==,!=判断是否相等
8&按位与
9^异或
10|按位或
11&&与(逻辑操作)
12||或(逻辑操作)
13?:三元操作
14=,+=,-=,*=,...所有的赋值操作
在Java中进行计算时,会先进行优先级高的运算,再进行优先级低的运算。但是,圆括号
“()”的优先级是最高的,如果有圆括号的话,就先计算圆括号中的值。
在写代码的时候,一般不用刻意去记忆操作符的优先级。如果不能确定运算符的优先级,可以
使用圆括号()来指定运算的顺序。

本章的重点是Java中一些关于变量的基本知识。希望读者通过本章的学习,能掌握8种基本类型一
些基本操作,掌握自动类型提升的概念和原则,掌握运算符的用法.

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值