Java 05 – Java的运算符

Java 05 – Java的运算符

1 运算符的基本概念

(1) 运算符: 对常量和变量进行操作的符号, 就叫作为运算符.

常见的运算符有: ① 算术运算符, ② 赋值运算符, ③ 关系运算符, ④ 逻辑运算符, ⑤ 三元(三目)运算符.

(2) 表达式: 用运算符将常量或变量连接起来的、符合Java语法的式子称为表达式. 不同运算符连接的式子体现的是不同类型的表达式.

比如: 定义两个int类型的变量a, b, 做加法: a + b —— 这就是一个表达式.

2 算数运算符

(1) 算数运算符的分类

运算符 作用 运算符 作用
+ % 求模, 也就是求余数
- ++ 自增, 也就是+1
* -- 自减, 也就是-1
/

(2) 求模(取余)和除法的区别

%: 取余运算符, 得到的是两个相除数据的余数 —— 用来判断第1个数是否能被第2个数整除.

/: 除法运算符, 得到是两个相除数据的商, 省去余数 —— 它的整数除和小数除是有区别的: 整数之间的除法, 只保留整数部分而舍弃小数部分, 比如:

int x = 3510; 
x = x / 1000 * 1000;   // 此时x的结果是3000

使用说明:

① 在控制结果的范围时, 可以使用求模(mod)运算, 结果的正负以被模数为参照. 比如:

运算 结果 运算 结果
2 % -3 2 4 % -3 1
-2 % 3 -2 -4 % 3 -1

%2 —— 结果是0、1交替的数, 可以作为开关设置;

③ 如果对负数取模, 模数的负号可以忽略不记, 比如: 5 % -2 = 1.

(3) 字符和字符串参与加法操作

字符参与运算, 其实是取这个字符在ASCII表中对应的数值来操作, 比如:

'a' —— 对应数字97, 'A' —— 对应数字65, '0' —— 对应数字48.

字符串参与+运算, 内部进行的不是加法运算, 而是字符串的拼接 —— 也就是说, 字符串和其他类型的数据做拼接, 结果是字符串类型.

+ 除字符串相加功能之外, 还能把非字符串转换成字符串, 比如:

System.out.println("5 + 5 = " + 5 + 5);    // 打印的结果是: 5 + 5 = 55

(4) 自增++与自减–的用法

++-- 运算符: 对变量作自加1或者自减1的操作, 它们既可以跟在变量后面, 也可以放在变量前面, 比如:

a++;
--b;

使用说明:

① 单独使用的时候, ++--放在变量前面或者后面, 运算效果都是一样的.

② 参与操作的时候:

· ++--在变量的后面, 先对变量进行相关操作, 再对变量作++--运算 —— 先运算, 再自增(自减);

· ++--在变量的前面, 先对变量作++--, 再对变量进行其他相关操作 —— 先自增(自减), 再运算.

int a = 1, b = 1;
int c, d;
c = ++a;      // 执行完后c = 2 ——— 先a = a + 1; 再c = a;
d = b++;      // 执行完后d = 1 ——— 先开辟临时空间temp记录b的值, 然后: d = temp; b = b + 1;
// 到这里, a=2, b=2, c=2, d=1

(5) 示例代码

/**
 * 算术运算符的使用
 *
 * @author Heal Chow
 * @date 2019/03/04 23:26
 */
public class OperatorDemo1 {
    public static void main(String[] args) {
        // 定义两个变量
        int a = 3;
        int b = 5;

        // 算数运算符的基本使用:
        System.out.println(a + b);    // 8
        System.out.println(a - b);    // -2
        System.out.println(a * b);    // 15
        System.out.println(a / b);    // 0

        // 整数相除只能得到整数, 要想得到小数, 就必须有浮点数参与运算
        System.out.println(3 / 4);    // 0
        System.out.println(3.0 / 4);  // 0.75

        // 除法与求模的区别:
        System.out.println(a / b);    // 0
        System.out.println(a % b);    // 3

        // 字符参与加法操作
        char c1 = '0';
        char c2 = 'a';
        System.out.println(a + c1);   // 3 + 48 = 51
        System.out.println(a + c2);   // 3 + 97 = 100

        // 字符串参与加法操作
        System.out.println("hello" + a);      // hello3
        System.out.println("hello" + a + b);  // hello35
        System.out.println(a + b + "hello");  // 8hello(先a + b)

        // 自增(++)与自减(--)的用法
        int d = 10;
        System.out.println("初始值d:" + d);    // 初始值d:10
        // 单独使用, 单独执行下述任一句:
        // d++;
        // ++d;
        // System.out.println("运算后d:" + d); // d = 11

        // 参与操作, 单独执行下述任一句:
        int e = 10;
        // int f = e++;   // e = 11, f = 10
        int f = ++e;      // e = 11, f = 11
        System.out.println("运算后e:" + e);    // 运算后e:11
        System.out.println("运算后f:" + f);    // 运算后f:11
    }
}

3 赋值运算符

(1) 赋值运算符的分类

基本的赋值运算符: =, 扩展的赋值运算符: +=, -=, *=, /=, %=.

举例说明:

a += 20; —— 相当于: a = (a的数据类型)(a + 20); —— 有强制类型转换.

a -= 10; —— 相当于: a = (a的数据类型)(a 1 10); —— 有强制类型转换.

short s = 4;
s = s + 5;    // 编译报错: 无法确定右侧的结果是否在short范围之内, 容易丢失精度
s += 5;       // 编译通过: += 是赋值运算符, 暗含强制类型提升

(2) 示例代码

/**
 * 赋值运算符的使用
 *
 * @author Heal Chow
 * @date 2019/03/04 23:35
 */
public class OperatorDemo2 {
    public static void main(String[] args) {
        // 把10赋值给int类型的变量a
        int a = 10;
        // += 把左边和右边的数据进行运算, 最后赋值给左边. 左边只能是变量
        a += 10;// 相当于a = a + 10
        System.out.println("a:" + a);   // a:20

        short s = 20;
        // s -= 5; // 相当于 s = s - 5;
        s = (short) (s - 5);
        System.out.println("s:" + s);   // s:15
    }
}

4 关系运算符

(1) 关系运算符的分类

关系运算符有以下6种: ==, !=, >, >=, <, <=.

关系运算符的结果都是boolean型: 要么是true, 要么是false.

注意: 关系运算符==不能误写为赋值运算符=, 这是很多新手常犯的错误之一.

(2) 示例代码

/**
 * 关系运算符的使用
 *
 * @author Heal Chow
 * @date 2019/03/04 23:42
 */
public class OperatorDemo3 {
    public static void main(String[] args) {
        int a = 10;
        int b = 20;
        int c = 10;
        System.out.println(a == b);    // false
        System.out.println(a == c);    // true
        System.out.println("-----------------");

        System.out.println(a != b);    // true
        System.out.println(a != c);    // false
        System.out.println("-----------------");

        System.out.println(a > b);     // false
        System.out.println(a > c);     // false
        System.out.println("-----------------");

        System.out.println(a >= b);    // false
        System.out.println(a >= c);    // true
        System.out.println("-----------------");

        int x = 3;
        int y = 4;
        // System.out.println(x == y);    // false
        // System.out.println(x = y);     // 4, 是将y赋值给x, 再将x输出

        boolean bb = (x == y);
        // boolean cc = (x = y);  // 报错
        int cc = (x = y);
        System.out.println("bb:" + bb);   // bb:false
        System.out.println("cc:" + cc);   // cc:4
    }
}

5 逻辑运算符

(1) 逻辑运算符的分类

逻辑运算符包括: &(逻辑与), |(逻辑或), ^(逻辑异或), !(逻辑非), &&(双与), ||(双或).

(2) 逻辑运算符的基本用法

逻辑运算符一般用于连接boolean类型的表达式或值, 也就是连接关系表达式.

表达式: 就是用运算符把常量或变量连接起来的符合Java语法的式子. 比如:

算术表达式: a + b, 比较表达式(条件表达式): a == b.

使用说明:

& – 逻辑与: 表达式中只要有0false, 结果就是false, 如果没有0false, 那结果就是true;

| – 逻辑或: 表达式中只要有1true, 结果就是true, 如果没有1true, 那结果就是false;

^ – 逻辑异或: 表达式前后相同, 结果就是false, 如果不同, 结果就是true;

! – 逻辑非: 取反的意思, 非true就是false, 非false就是true.

特点: 偶数个表达式或值, 不改变结果.

(3) 示例代码

/**
 * 逻辑运算符的用法
 *
 * @author Heal Chow
 * @date 2019/03/04 23:48
 */
public class OperatorDemo4 {
    public static void main(String[] args) {
        int a = 10;
        int b = 20;
        int c = 30;

        System.out.println((a > b) & (a > c));   // false
        System.out.println((a < b) & (a > c));   // false
        System.out.println((a > b) & (a < c));   // false
        System.out.println((a < b) & (a < c));   // true
        System.out.println("---------------");

        System.out.println((a > b) | (a > c));   // false
        System.out.println((a < b) | (a > c));   // true
        System.out.println((a > b) | (a < c));   // true
        System.out.println((a < b) | (a < c));   // true
        System.out.println("---------------");

        System.out.println((a > b) ^ (a > c));   // false
        System.out.println((a < b) ^ (a > c));   // true
        System.out.println((a > b) ^ (a < c));   // true
        System.out.println((a < b) ^ (a < c));   // false
        System.out.println("---------------");

        System.out.println((a > b));    // false
        System.out.println(!(a > b));   // !false -> true
        System.out.println(!!(a > b));  // !!false -> !true -> false
    }
}

(4) 逻辑运算符&&&的区别

① 最终结果相同;

&&具有短路效果 —— 如果左边是false, 就立即结束判断, 也就是说不执行右边的表达式;

&没有短路效果 —— 无论左边是falsetrue, 右边的表达式都会执行.

(5) 逻辑运算符|||的区别

① 最终结果一样;

||具有短路效果, 如果左边是true, 就立即结束判断, 也就是说不执行右边的表达式;

|没有短路效果, 无论左边是falsetrue, 右边的表达式都会执行.

(6) 示例代码

/**
 * &&和&、||和|的示例
 *
 * @author Heal Chow
 * @date 2019/03/04 23:55
 */
public class OperatorDemo5 {
    public static void main(String[] args) {
        int a = 10;
        int b = 20;
        int c = 30;
        System.out.println((a > b) && (a > c));   // false
        System.out.println((a < b) && (a > c));   // false
        System.out.println((a > b) && (a < c));   // false
        System.out.println((a < b) && (a < c));   // true
        System.out.println("---------------");

        System.out.println((a > b) || (a > c));   // false
        System.out.println((a < b) || (a > c));   // true
        System.out.println((a > b) || (a < c));   // true
        System.out.println((a < b) || (a < c));   // true
        System.out.println("---------------");

        int x = 3;
        int y = 4;
        // System.out.println((x++ > 4) & (y++ > 5)); // false & false
        // System.out.println("x:" + x);   // x:4 --> 前后部都执行, x自增为4
        // System.out.println("y:" + y);   // y:5 --> y自增为5

        System.out.println((x++ > 4) && (y++ > 5));
        System.out.println("x:" + x);   // x:4 --> 前部为执行, 结果为false, x自增为4
        System.out.println("y:" + y);   // y:4 --> 由于短路, 后部的y++没有执行
    }
}

6 三元运算符

(1) 三元运算符的使用格式

(关系表达式) ? 表达式1 : 表达式2;

如果关系表达式的结果是true, 那就执行表达式1, 最后的结果是表达式1的结果;

如果关系表达式的结果为false, 那就执行表达式2, 最后的结果是表达式2的结果.

(2) 示例代码

/**
 * 三元运算符的使用
 *
 * @author Heal Chow
 * @date 2019/03/05 00:03
 */
public class OperatorDemo6 {
    public static void main(String[] args) {
        // 获取2个整数中大的那个数
        int a1 = 10;
        int b1 = 20;

        // a1大于b1吗? 大于的话就是a1, 小于的话就是b1
        int c1 = (a1 > b1) ? a1 : b1;
        System.out.println("c1:" + c1);     // c1:20
        System.out.println("---------------");

        // 比较两个整数是否相同
        int a2 = 10;
        int b2 = 20;
        boolean flag = (a2 == b2) ? true : false;
        // boolean flag = (a2 == b2);
        System.out.println(flag);           // false
        System.out.println("---------------");

        // 获取三个整数中的最大值
        int a3 = 10;
        int b3 = 30;
        int c3 = 20;
        // 先比较某两个整数的大值, 再比较第三个
        int temp = ((a3 > b3) ? a3 : b3);
        int max = ((temp > c3) ? temp : c3);
        System.out.println("max:" + max);   // max:30

    }
}

7 扩展: 位运算

7.1 原码、反码和补码

(1) 关于原码:

+3的原码为00000011 —— 第一位是符号位;

-3的原码为10000011 —— 第一位是符号位.

(2) 关于反码:

+3的反码为00000011 —— 正数的反码是其本身;
-3的反码为11111100 —— 负数的反码是在其原码的基础上, 符号位不变, 其余各个位取反.

(3) 关于补码:

+3的补码为00000011 —— 正数的补码就是其本身;

-3的补码为11111101 —— 负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1, 也就是在反码的基础上+1.

(4) 扩展说明:

使用补码可以把减法变为加法, 而且0的问题也会解决(+0和-0的补码都是00000000).

Java中使用补码的格式保存byte, short, int, long类型的数据.

7.2 位运算符

运算符 功能 操作说明
& 对补码的每一位进行与运算 二进制位进行&运算, 只有1 & 1 = 1, 其他都是0;
可用于获取二进制数中的有效位1.
| 对补码的每一位进行或运算 二进制位进行|运算, 只有0 | 0 = 0, 其他都是1
^ 对补码的每一位进行异或运算 相同二进制位进行^运算, 结果是0, 即1 ^ 1 = 0, 0 ^ 0 = 0;
不同二进制位进行^运算, 结果是1, 即1 ^ 0 = 1, 0 ^ 1 = 1;
一个数亦或另一个数两次, 结果是它本身(6^3^3 = 6).
~ 按补码的每一位进行取反操作 一个数取反再 +1 等于他的相反数(~5+1 = -5), 再取反并+1, 等于它自己.
>> 带符号右移, 高位补符号位, 正数右移1位相当于除以2 被移位的二进制最高位是0, 右移后空缺位补0;
被移位的二进制最高位是1, 右移后空缺位补1.
>>> 无符号右移, 高位补0, 正数右移1位相当于除以2 被移位的二进制最高位无论是0或1, 空缺位都补0.
多用在操作二进制数据的某一段二进制位.
<< 左移, 正数左移1位相当于乘2

说明: 除了~是一元操作符外, 其他位操作符都是二元操作符号.

7.3 byte类型转int类型

Java在将byte类型的数值转为int的过程中, 会对符号位进行扩充: 10000000 -> 11111111 11111111 11111111 10000000, 这样数值就能保持不变. 比如下面的例子中, 字节类型的-128也是整型的-128.

byte b = -128;
int i1 = b & 0xff;
int i2 = b;
System.out.println(i1); // 128
System.out.println(i2); // -128
System.out.println(Integer.toBinaryString(i1)); // 10000000
System.out.println(Integer.toBinaryString(i2)); // 11111111111111111111111110000000

但是如果byte是无符号的整数, 就要考虑使用0xff进行与操作 —— 这样得到的结果数值不变, 还能避免Java自动进行符号位扩充.

0xff的补码为11111111, 意味着和它与操作将只保留后8位, 并且结果总是非负数.

7.4 [扩展] 一道位运算的面试题

一般位运算也意味着高效的运算: 现代计算机底层对所有的计算都是转换成了对二进制的计算.

由此产生了很多技巧性的题目, 比如这道常见的面试题: 对两个整数变量的值进行互换(不需要第三方变量).

方案① —— 通过加/减法实现:

// + - 法可能会造成精度的丢失
a = a + b; 
b = a – b; 
a = a – b; 

方案② —— 通过位运算实现 —— 效率更高:

// 一个数异或另一个数两次, 结果为该数本身, 不会造成精度丢失
a = a ^ b; 
b = a ^ b; 
a = a ^ b; 

说明: 示例代码可在 我的GitHub上 查看并下载.

参考资料

Java位操作指南

(全文完)

瘦风的南
微信关注《瘦风的南墙》 在移动端阅读文章

(感谢阅读, 转载请注明作者和出处 瘦风的南墙 , 请勿用于任何商业用途)

——=== 访问 本站404页面 寻找遗失儿童 ===——

发表评论

你的个人信息不会被公开, 注意:标记为 * 的项必填。