【0 基础学 Java】(四)运算符

码字不易,对你有帮助 点赞/转发/关注 支持一下作者

微信搜公众号:不会编程的程序圆

看更多干货,获取第一时间更新

如果想看更好的排版,可以阅读原文
点击阅读原文

思维导图


image

目录


@[toc]

正文


前言

在正式开始讲 运算符 之前,我们先来讨论一下上节课我们所学的 变量 的一个注意点。

  • byte 与 char
  • short 与 char

上面两组类型之间的转换不管是从小到大还是从大到小,都需要进行强制类型转换

public class helloWorld{
    
    public static void main(String[] args) {
        
        byte bt = 10;// byte 类型大小为 1 个字节
        char ch = ' ';// char 类型大小为 2 个字节
        short sh = 10;// short 类型大小为 2 个字节
        
        ch = bt;// 将 1 个字节的 bt 赋值给 2 个字节的 ch
        //编译会报错:不兼容的类型: 从byte转换到char可能会有损失
        
        ch = sh;
        sh = ch;
        //编译器还是会报错:不兼容的类型:从short/char转换到char/short可能会有损失
    }
}

原因:

1、char 是字符型,而 byte 和 short 是数值型,他们之间不建议转换。

2、char 类型字符对应的 十进制 是没有负数的。


一 运算符

1. 算术运算符

+

-

*

/

%

注意:

  • int / int 结果还是 int, 需要使用 double 来计算

    int a = 1;
    int b = 2;
    System.out.println(a / b);
    
    // 结果为 0
    

    如何正确的输出我们想要的 0.5 呢?

    //以下三种方式得到的结果都是 0.5
    System.out.println(1.0 / 2);
    System.out.println(1 / 2.0);
    System.out.println(1.0 / 2.0);
    

    需要注意的是,在 1.0 / 2 时,1.0 无疑是 double 类型,在运算时 2 也会被提升为 double 类型,也就是 1.0 / 2.0

  • 0 不能作为除数

    int a = 1;
    int b = 0;
    System.out.println(a / b)
        
    // 运行结果
    Exception in thread "main" java.lang.ArithmeticException: / by zero
    at Test.main(Test.java:5)
    
  • % 表示取余,不仅仅可以对 int 求模,也能对 double 来求模

    System.out.println(11.5 % 2.0);
    
    //运行结果
    1.5
    
  • % 的操作数出现负数

    System.out.println(5 % 2);
    System.out.println(5 % -2);
    System.out.println(-5 % -2);
    System.out.println(-5 % -2);
    
    //运行结果
    1
    1
    -1
    -1
    

    我们在 【C语言必知必会】的【C语言入门到精通】的运算符一节中讨论过这个问题。

    简单的来说,结果正负取决于 左操作数 的正负


+=

-=

*=

/=

%=

int a = 10;
a += 1; // 等价于 a = a + 1
System.out.println(a);

//运行结果
11


++

--

int a = 10;
int b = ++a;
System.out.println(b);
int c = a++;
System.out.println(c);

//运行结果
11
11


结论:

  1. 如果不取自增运算的表达式的返回值, 则前置自增和后置自增没有区别。
  2. 如果取表达式的返回值, 则前置自增的返回值是自增之后的值, 后置自增的返回值是自增之前的值。


思考题:下面程序会输出什么?

int i = 10;
        
i = i++;
System.out.println(i);

点击查看答案

2. 关系运算符

==

!=

<

>

>=

<=


int a = 10;
int b = 20;
System.out.println(a == b);
System.out.println(a != b);
System.out.println(a < b);
System.out.println(a > b);
System.out.println(a <= b);
System.out.println(a >= b);

//运行结果
false
true
true
false
true
false

注意: 关系运算符的表达式返回值都是 boolean 类型。


3. 逻辑运算符

注意: 逻辑运算符的操作数(操作数往往是关系运算符的结果)和返回值都是 boolean 。

这一点是和 C 语言不同的地方:

int a = 1, b = 2;
        
System.out.println(a && b);

//编译错误
helloWorld.java:7: 错误: 二元运算符 '&&' 的操作数类型错误
System.out.println(a && b);
                     ^

应当做出如下修改:

boolean flag1 = false, flag2 = false;
        
System.out.println(flag1 && flag2);

//运行结果:
false


逻辑运算符主要有三个:

&&

||

!


逻辑与 &&

规则: 两个操作数都为 true, 结果为 true, 否则结果为 false 。

int a = 10;
int b = 20;
int c = 30;
System.out.println(a < b && b < c);

//运行结果
true
逻辑或 ||

规则: 两个操作数都为 true, 结果为 true, 否则结果为 false 。

int a = 10;
int b = 20;
int c = 30;
System.out.println(a < b || b > c)
    
//运行结果
true


逻辑非 !

规则: 操作数为 true, 结果为 false; 操作数为 false, 结果为 true(这是个单目运算符, 只有一个操作数)

int a = 10;
int b = 20;
System.out.println(!a < b);

//编译错误
! 只能作用于 boolean 类型,而 a 是 int 类型


短路求值

&& 和 || 遵守短路求值的规则。

System.out.println(10 > 20 && 10 / 0 == 0);
System.out.println(10 < 20 || 10 / 0 == 0);

//打印结果:
false
true

我们都知道, 计算 10 / 0 会导致程序抛出异常。但是上面的代码却能正常运行, 说明 10 / 0 并没有真正被求值。


结论:

  • 对于 && ,如果左侧表达式值为 false, 则表达式的整体的值一定是 false, 无需计算右侧表达式
  • 对于 || ,如果左侧表达式值为 true, 则表达式的整体的值一定是 true, 无需计算右侧表达式


**& 和 | (不推荐使用) **

& 和 | 如果操作数为 boolean 的时候, 也表示逻辑运算. 但是和 && 以及 || 相比, 它们不支持短路求值

System.out.println(10 > 20 & 10 / 0 == 0); // 程序抛出异常
System.out.println(10 < 20 | 10 / 0 == 0); // 程序抛出异常

4、位运算符

位运算符主要有 4 个:

&

|

~

^

位操作表示 按二进制位运算。计算机中都是使用二进制来表示数据的(01构成的序列), 按位运算就是在按照二进制位的
每一位依次进行计算。(这与 C 语言基本相同,我们在 C 的教程中也详细讲过,这里不做展开。)

按位与:&

如果两个二进制位都是 1, 则结果为 1, 否则结果为 0 。

int a = 10;//01010
int b = 20;//10100
System.out.println(a & b);

//运行结果:
0

操作数有负数的情况

byte a = -1;//1111 1111
byte b = 13;//0000 1101
 
 System.out.println(a & b);

//运行结果:
13


byte a = -1;//1111 1111
byte b = -2;//1111 1110

System.out.println(a & b);

//运行结果:
-2

这说明,负数补码的符号位是参数 & 运算的。

按位或:|

如果两个二进制位都是 0, 则结果为 0, 否则结果为 1 。

int a = 10;//01010
int b = 20;//10100
System.out.println(a | b);

//运行结果
30(11110)

注意: 当 & 和 | 的操作数为整数(int, short, long, byte) 的时候, 表示按位运算, 当操作数为 boolean 的时候, 表示逻辑运算 。

按位取反:~

如果该位为 0 则转为 1, 如果该位为 1 则转为 0 。

int a = 0xf;
System.out.printf("%x\n", ~a);

//运行结果:
fffffff0

解析:

  • 0x 前缀的数字为 十六进制 数字. 十六进制可以看成是二进制的简化表示方式. 一个十六进制数字对应 4 个二进
    制位.
  • 0xf 表示 10 进制的 15, 也就是二进制的 1111
  • printf 能够格式化输出内容, %x 表示按照十六进制输出(在 C 语言中,转换说明 x 表示以十六进制打印 unsigned int 类型的值,unsigned int 是 4 个 字节,Java 中虽然没有 unsigned int 但是 %x 应该同 C 一样打印 4 个字节。所以,a 虽然是 0xf, 但是被编译器看作是:0x0000000f )。
  • \n 表示换行符
按位异或:^

如果两个数字的二进制位相同, 则结果为 0, 相异则结果为 1 。

byte a = 10;//1010
byte b = 14;//1110
System.out.println(a ^ b);

//运行结果:
4(0010)


5、移位运算(了解)

如果你想搞明白,可以去看看我的 C 语言的操作符的文章 和 【C进阶】第一节数据存储。

<<

>>

>>>


左移:<<

最左侧位不要了, 最右侧补 0 。

int a = 0x10;

System.out.printf("%x\n", a << 1);

//运行结果:(注意打印的格式是 16 进制)
20(相当于乘以 2)
右移:>>

最右侧位不要了, 最左侧补符号位(正数补0, 负数补1) 。

int a = 0x10;
//0x10 -> 16 -> 0001 0000 >> 1 -> 0000 1000 -> 8

System.out.printf("%x\n", a >> 1);

// 运行结果
8(相当于除以 2)


int b = 0xffff0000;//这是一个负数

System.out.printf("%x\n", b >> 1);

// 运行结果
ffff8000
无符号右移:>>>

最右侧位不要了, 最左侧补 0 。

int a = -1;
System.out.println(a >> 1);
System.out.println(a >>> 1);

//运行结果:
-1 
2147483647


思考一下:

byte a = -1;

System.out.println(a >>> 1);//这样会得到 127吗?

//运行结果:
2147483647

为什么会输出这样的值?

前面我们说过,运算过程中 byte,short 这类大小小于 4 个字节的类型会进行数值提升,转化为 int


那么这样可以吗?

byte a = -1;// 1111 1111
//a >>> 1 -> 0111 1111 1111 1111 1111 1111 1111 1111
byte b = (byte)(a >>> 1);
//知道了数值提升,我们这样将运算后的 int 转化为 byte ,这样可以得到正确结果吗? 

System.out.println(b);

//运行结果:
-1

这是因为:第二步强制类型转换时,int 会发生截断,byte 只得到 int 的后 8 位:1111 1111。在输出 b 时,1111 1111 还是会被当作 -1 。这样看似 >>> 操作符没有什么卵用,但是我们应该知道,在计算机内部真实的情况。

总结:
  1. 左移 1 位, 相当于原数字 * 2. 左移 N 位, 相当于原数字 * 2 的N次方。
  2. 右移 1 位, 相当于原数字 / 2. 右移 N 位, 相当于原数字 / 2 的N次方。
  3. 由于计算机计算移位效率高于计算乘除, 当某个代码正好乘除 2 的N次方的时候可以用移位运算代替。
  4. 移动负数位或者移位位数过大都没有意义 。

6、条件运算符

表达式1 ? 表达式2 : 表达式3

含义:当 表达式1 的值为 true 时, 整个表达式的值为 表达式2 的值; 当 表达式1 的值为 false 时, 整个表达式的值为 表达式3 的值

// 求两个整数的最大值
int a = 10;
int b = 20;
int max = a > b ? a : b;

7、运算符优先级

运算符之间是有优先级的. 具体的规则我们不必记忆. 在可能存在歧义的代码中加上括号即可 。

想看详细的还是去看我 C 语言运算符相关的教学。

总结

  1. % 操作再 Java 中也能针对 double 来计算 。
  2. 需要区分清楚 前置自增 和 后置自增之间的区别 。
  3. 由于 Java 是强类型语言, 因此对于类型检查较严格, 因此像 && 之类的运算操作数必须是 boolean 。
  4. 要区分清楚 & 和 | 什么时候是表示按位运算, 什么时候表示逻辑运算 。

二 注释

Java 的注释方式和 C 一样,主要有以下几种:

//行注释
/*这是
一个
块注释*/
/*
 *这是
 *文档
 *注释
 */

三 关键字

与 C 语言相同,定义的 变量名 不能与 关键字 冲突。

用于定义访问权限修饰符 private protected public
定义类,函数,变量修饰符 abstract final static synchronized
定义类与类之间的关系 extends implements
定义建立实例以及引用实例,判断实例 new this super instanceof
用于异常处理 try catch finally throw throws
用于包 package import
其他修饰符 native strictfp transient volatile assert

[0 基础学 java ] 系列的代码可以在我的 Github 仓库查看,地址如下:

https://github.com/hairrrrr/Java_SE_EnjoyLearning

欢迎 star !(点一个 star 方便你下回查看)

本系列的教学也可以在 GitHub 观看(GitHub 上看教学的好处是所有文章的目录比较清晰)。


以上就是本次的内容。

如果文章有错误欢迎指正和补充,感谢!

最后,如果你还有什么问题或者想知道到的,可以在评论区告诉我呦,我可以在后面的文章加上你们的真知灼见。

关注我,看更多干货!

我是程序圆,我们下次再见。

你可能感兴趣的:(【0 基础学 Java】(四)运算符)