90%人踩过的坑!Java运算符优先级与类型转换的终极避坑指南!

createh511小时前技术教程1

本文将全面介绍Java中的各种运算符,从基础算术运算到位操作符,再到Java特有的运算符,每个概念都配有实际代码示例和对比分析。

一、运算符基础概念

1.1 什么是运算符

运算符是用来对操作数进行特定操作的符号。在表达式 3 + 4 中,34 是操作数,+ 是运算符。

1.2 运算符分类

分类

运算符示例

说明

算术运算符

+, -, *, /, %

基本数学运算

关系运算符

>, <, ==, !=

比较操作数关系

逻辑运算符

&&, ||, !

布尔逻辑运算

位运算符

&, |, ^, ~

二进制位操作

赋值运算符

=, +=, -=

赋值操作

其他运算符

?:, instanceof

条件、类型检查

我把运算符分成6大类,用一句话帮你记住:

“算关逻位赋条”(算术、关系、逻辑、位运算、赋值、条件)

二、算术运算符

算术运算符用于执行基本数学运算。
想象你小时候学数学:

  • + 是加法(1+1=2)
  • - 是减法(5-3=2)
  • × 是乘法(2×3=6)
  • ÷ 是除法(6÷2=3)

Java里的运算符也是干这些事的,只是写法有点小变化:

  • * 代替了 ×(2*3=6)
  • / 代替了 ÷(6/2=3)

2.1 基本算术运算符

public class ArithmeticOperators {
    public static void main(String[] args) {
        int a = 10;
        int b = 3;
        
        System.out.println("a + b = " + (a + b)); // 13 加
        System.out.println("a - b = " + (a - b)); // 7 减
        System.out.println("a * b = " + (a * b)); // 30 乘
        System.out.println("a / b = " + (a / b)); // 3 (整数除法)
        System.out.println("a % b = " + (a % b)); // 1 取余(10除以3余1)
        
        double c = 10.0;
        System.out.println("c / b = " + (c / b)); // 3.333... (浮点除法)
    }
}

2.2 一元运算符

public class UnaryOperators {
    public static void main(String[] args) {
        int x = 5;
        
        // 正负号
        System.out.println(+x); // 5
        System.out.println(-x); // -5
        
        // 自增自减
        System.out.println(x++); // 5 (后置,先使用后增加)
        System.out.println(x);   // 6
        System.out.println(++x); // 7 (前置,先增加后使用)
        
        System.out.println(x--); // 7 (后置,先使用后减少)
        System.out.println(x);   // 6
        System.out.println(--x); // 5 (前置,先减少后使用)
    }
}

2.3 算术运算符优先级

优先级

运算符

结合性

++, – (后置)

左→右


+, - (一元), ++, – (前置)

右→左


*, /, %

左→右

+, - (二元)

左→右

2.4 算术运算符的坑与技巧

1)、整数除法的坑(新手必踩)

// 你以为的除法
System.out.println(5 / 2); // 输出啥?2.5?错!输出2!

// 正确做法(至少有一个浮点数)
System.out.println(5 / 2.0); // 输出2.5
System.out.println((double)5 / 2); // 输出2.5

分析:Java中两个整数相除结果还是整数,小数部分直接被砍掉(不是四舍五入)。就像你买5个苹果分给2个人,每人得2个,剩下的1个不分了。

2)、自增自减的玄机(笔试常考)

int a = 5;
int b = a++; // 先把a的值赋给b,然后a自增
System.out.println("a=" + a + ", b=" + b); // a=6, b=5

int c = 5;
int d = ++c; // 先c自增,再把值赋给d
System.out.println("c=" + c + ", d=" + d); // c=6, d=6

记忆口诀

  • i++:先用后加(先做事再吃饭)
  • ++i:先加后用(先吃饭再做事)

3)、取余运算的妙用

// 判断奇偶
int num = 7;
if (num % 2 == 0) {
    System.out.println("偶数");
} else {
    System.out.println("奇数"); // 输出这个
}

// 限制范围(比如游戏中的循环地图)
int position = 10;
int mapSize = 8;
int actualPosition = position % mapSize; // 2

三、关系运算符

关系运算符用于比较两个值,返回布尔结果。

public class RelationalOperators {
    public static void main(String[] args) {
        int a = 10;
        int b = 20;
        
        System.out.println("a == b: " + (a == b)); // false
        System.out.println("a != b: " + (a != b)); // true
        System.out.println("a > b: " + (a > b));   // false
        System.out.println("a < b: " + (a < b));   // true
        System.out.println("a >= b: " + (a >= b)); // false
        System.out.println("a <= b: " + (a <= b)); // true
        
        // 对象比较
        String s1 = new String("hello");
        String s2 = new String("hello");
        System.out.println("s1 == s2: " + (s1 == s2)); // false (比较引用)
        System.out.println("s1.equals(s2): " + s1.equals(s2)); // true (比较内容)
    }
}

浮点数比较的坑

// 错误示范
double d1 = 0.1 + 0.2;
double d2 = 0.3;
System.out.println(d1 == d2); // false!惊不惊喜?

// 正确做法
final double EPSILON = 1e-10; // 定义一个很小的误差范围
System.out.println(Math.abs(d1 - d2) < EPSILON); // true

重要提醒
1、字符串比较要用
equals()==比较的是内存地址!
2、计算机用二进制存小数会有精度误差,就像你用尺子量东西总有毫米级的误差一样。

四、逻辑运算符:真假判官

逻辑运算符用于组合布尔表达式。

4.1 基本逻辑运算符

public class LogicalOperators {
    public static void main(String[] args) {
        boolean a = true;
        boolean b = false;
        
        System.out.println("a && b: " + (a && b)); // false
        System.out.println("a || b: " + (a || b)); // true
        System.out.println("!a: " + (!a));        // false
        
        // 短路演示
        int x = 5;
        boolean result = (x < 10) || (x++ > 0);
        System.out.println("result: " + result); // true
        System.out.println("x: " + x);          // 5 (x++未执行)
    }
}

4.2 逻辑运算符对比

运算符

名称

特点

示例

&&

逻辑与

短路,第一个false则不再计算第二个

false && expr 不计算expr

||

逻辑或

短路,第一个true则不再计算第二个

true || expr 不计算expr

!

逻辑非

一元运算符,反转布尔值

!true → false

&

非短路与

总是计算两个操作数

false & expr 计算expr

|

非短路或

总是计算两个操作数

true | expr 计算expr

^

逻辑异或

相同为false,不同为true

true ^ false → true

4.3 短路特性(超实用!)

// 用户输入校验
String input = null;
// 如果用单个&会报空指针,因为两边都会计算
if (input != null && input.length() > 0) {
    System.out.println("有效输入");
} else {
    System.out.println("无效输入"); // 输出这个
}

短路原理

  • &&:左边为false时,右边不计算
  • ||:左边为true时,右边不计算

就像你找对象:

  • 要求"高富帅"(条件1 && 条件2 && 条件3),第一条不满足后面就不用问了
  • 要求"会做饭或会唱歌"(条件1 || 条件2),第一条满足就直接通过

五、位运算符

位运算符直接操作整数的二进制位。

5.1 基本位运算符

public class BitwiseOperators {
    public static void main(String[] args) {
        int a = 5;  // 二进制: 0101
        int b = 3;  // 二进制: 0011
        
        System.out.println("a & b: " + (a & b));  // 0001 → 1 (按位与)
        System.out.println("a | b: " + (a | b));  // 0111 → 7 (按位或)
        System.out.println("a ^ b: " + (a ^ b));  // 0110 → 6 (按位异或)
        System.out.println("~a: " + (~a));        // 111...1010 → -6 (按位取反)
        
        // 移位运算符
        int c = 8;  // 二进制: 1000
        System.out.println("c << 1: " + (c << 1)); // 10000 → 16 (左移)
        System.out.println("c >> 1: " + (c >> 1)); // 0100 → 4 (带符号右移)
        System.out.println("c >>> 1: " + (c >>> 1)); // 0100 → 4 (无符号右移)
        
        int d = -8;
        System.out.println("d >> 1: " + (d >> 1)); // 保持符号位
        System.out.println("d >>> 1: " + (d >>> 1)); // 不保持符号位
    }
}

5.2 高效计算技巧

// 乘以2的n次方
int num = 3;
int result = num << 2; // 3*4=12

// 除以2的n次方
result = num >> 1; // 3/2=1

// 判断奇偶(比%更快)
boolean isOdd = (num & 1) == 1; // true

// 交换两个数(不用临时变量)
int x = 5, y = 10;
x ^= y;
y ^= x;
x ^= y; // x=10, y=5


5.3 位运算符应用场景

  1. 权限控制:用位掩码表示不同权限
  2. 高效计算:某些数学运算用位操作更高效
  3. 加密算法:许多加密算法依赖位操作
  4. 优化存储:用位存储布尔值节省空间
/**
 * 该类实现了基于位运算的权限管理系统
 * 使用位掩码技术来表示和操作不同的权限
 */
public class Permission {
    // 权限常量定义,使用2的幂次方值(1,2,4,8...)确保每个权限对应二进制的一位
    
    /**
     * 读权限 - 二进制表示为0001
     */
    public static final int READ = 1;    // 0001
    
    /**
     * 写权限 - 二进制表示为0010
     */
    public static final int WRITE = 2;   // 0010
    
    /**
     * 执行权限 - 二进制表示为0100
     */
    public static final int EXECUTE = 4; // 0100
    
    /**
     * 存储当前权限的位掩码
     * 每一位代表一个权限,通过位运算来操作
     */
    private int permissions;
    
    /**
     * 设置权限位掩码(会覆盖原有权限)
     * @param permissions 新的权限位掩码
     */
    public void setPermissions(int permissions) {
        this.permissions = permissions;
    }
    
    /**
     * 添加一个权限(不会影响其他权限)
     * 使用位或运算(|)来添加权限
     * @param permission 要添加的权限(READ, WRITE 或 EXECUTE)
     */
    public void addPermission(int permission) {
        // 位或运算:只要有一个为1,结果位就为1
        permissions |= permission;
    }
    
    /**
     * 移除一个权限(不会影响其他权限)
     * 使用位与运算(&)和位非运算(~)来移除权限
     * @param permission 要移除的权限(READ, WRITE 或 EXECUTE)
     */
    public void removePermission(int permission) {
        // 1. 先对权限取反,例如0010变成1101
        // 2. 然后与原权限做与运算,将指定位置0
        permissions &= ~permission;
    }
    
    /**
     * 检查是否拥有某个权限
     * @param permission 要检查的权限(READ, WRITE 或 EXECUTE)
     * @return 如果拥有该权限返回true,否则返回false
     */
    public boolean hasPermission(int permission) {
        // 位与运算:如果结果等于要检查的权限,说明对应位都是1
        return (permissions & permission) == permission;
    }
    
    /**
     * 主方法,演示权限类的使用
     */
    public static void main(String[] args) {
        Permission file = new Permission();
        
        // 添加读和写权限
        file.addPermission(READ);
        file.addPermission(WRITE);
        
        // 检查权限
        System.out.println("Can read? " + file.hasPermission(READ));    // true
        System.out.println("Can execute? " + file.hasPermission(EXECUTE)); // false
        
        // 移除写权限
        file.removePermission(WRITE);
        System.out.println("Can write? " + file.hasPermission(WRITE));  // false
    }
}

六、赋值运算符

赋值运算符用于给变量赋值。

6.1 基本赋值运算符

public class AssignmentOperators {
    public static void main(String[] args) {
        // 简单赋值
        int a = 10;
        
        // 复合赋值
        a += 5;  // 等价于 a = a + 5
        System.out.println("a += 5: " + a); // 15
        
        a -= 3;  // a = a - 3
        System.out.println("a -= 3: " + a); // 12
        
        a *= 2;  // a = a * 2
        System.out.println("a *= 2: " + a); // 24
        
        a /= 6;  // a = a / 6
        System.out.println("a /= 6: " + a); // 4
        
        a %= 3;  // a = a % 3
        System.out.println("a %= 3: " + a); // 1
        
        // 位运算复合赋值
        int b = 5;
        b &= 3;  // b = b & 3 (0101 & 0011 = 0001)
        System.out.println("b &= 3: " + b); // 1
        
        b |= 6;  // b = b | 6 (0001 | 0110 = 0111)
        System.out.println("b |= 6: " + b); // 7
        
        b ^= 2;  // b = b ^ 2 (0111 ^ 0010 = 0101)
        System.out.println("b ^= 2: " + b); // 5
        
        b <<= 1; // b = b << 1 (0101 << 1 = 1010)
        System.out.println("b <<= 1: " + b); // 10
        
        b >>= 2; // b = b >> 2 (1010 >> 2 = 0010)
        System.out.println("b >>= 2: " + b); // 2
    }
}

6.2 赋值运算符特点

  1. 复合赋值运算符会自动进行类型转换
  2. 比简单赋值更简洁,有时性能略好
  3. 可以避免重复计算同一表达式
int[] arr = new int[10];
int index = 0;

// 复合赋值可以避免重复计算arr.length
for (int i = 0; i < arr.length; i++) {
    arr[i] = index += 2; // 比 index = index + 2 更简洁
}

七、条件运算符(三元运算符)

条件运算符是Java中唯一的三元运算符。

public class ConditionalOperator {
    public static void main(String[] args) {
        int a = 10;
        int b = 20;
        
        // 基本用法
        int max = (a > b) ? a : b;
        System.out.println("Max: " + max); // 20
        
        // 嵌套使用
        int x = 10, y = 20, z = 30;
        int largest = (x > y) ? ((x > z) ? x : z) : ((y > z) ? y : z);
        System.out.println("Largest: " + largest); // 30
        
        // 返回不同类型
        Object obj = (a < b) ? "a is smaller" : (b - a);
        System.out.println("obj: " + obj); // "a is smaller"
        
        // 实用示例:检查空值
        String input = null;
        String display = (input != null) ? input : "Default Value";
        System.out.println("display: " + display); // "Default Value"
    }
}

八、instanceof运算符

instanceof用于检查对象是否是特定类的实例。

public class InstanceofOperator {
    static class Animal {}
    static class Dog extends Animal {}
    static class Cat extends Animal {}
    
    public static void main(String[] args) {
        Animal animal = new Dog();
        
        System.out.println("animal instanceof Animal: " + (animal instanceof Animal)); // true
        System.out.println("animal instanceof Dog: " + (animal instanceof Dog));     // true
        System.out.println("animal instanceof Cat: " + (animal instanceof Cat));     // false
        System.out.println("animal instanceof Object: " + (animal instanceof Object)); // true
        
        // 与null检查
        Animal nullAnimal = null;
        System.out.println("nullAnimal instanceof Animal: " + (nullAnimal instanceof Animal)); // false
        
        // 接口检查
        String str = "Hello";
        System.out.println("str instanceof CharSequence: " + (str instanceof CharSequence)); // true
        
        // 数组检查
        int[] arr = new int[5];
        System.out.println("arr instanceof int[]: " + (arr instanceof int[])); // true
        System.out.println("arr instanceof Object: " + (arr instanceof Object)); // true
    }
}

九、运算符优先级与结合性

9.1 完整运算符优先级表

优先级

运算符

描述

结合性

1

()

方法调用

左→右


[]

数组下标



.

成员访问



->

Lambda表达式


2

++ –

后置自增/自减

左→右

3

++ –

前置自增/自减

右→左


+ -

一元加减



! ~

逻辑非、位非



(type)

强制类型转换



new

对象创建


4

* / %

乘、除、取模

左→右

5

+ -

加、减

左→右

6

<< >> >>>

移位

左→右

7

< <= > >=

关系比较

左→右


instanceof

类型比较


8

== !=

相等比较

左→右

9

&

按位与

左→右

10

^

按位异或

左→右

11

|

按位或

左→右

12

&&

逻辑与

左→右

13

||

逻辑或

左→右

14

?:

条件运算符

右→左

15

= += -= etc.

赋值运算符

右→左

9.2 优先级示例

public class OperatorPrecedence {
    public static void main(String[] args) {
        int a = 10, b = 5, c = 1;
        int result = a + b * c; // 等价于 a + (b * c)
        System.out.println("result: " + result); // 15
        
        boolean logicalResult = a > b && b < c; // 等价于 (a > b) && (b < c)
        System.out.println("logicalResult: " + logicalResult); // false
        
        int bitwiseResult = a & b | c; // 等价于 (a & b) | c
        System.out.println("bitwiseResult: " + bitwiseResult); // 1
        
        // 使用括号明确优先级
        int explicitResult = (a + b) * c;
        System.out.println("explicitResult: " + explicitResult); // 15
    }
}

十、Java运算符最佳实践

10.1 避免常见陷阱

  1. 整数除法5 / 2 结果是2,不是2.5
  2. 浮点数比较:避免直接==比较浮点数,使用差值比较
  3. 自增自减副作用:避免在复杂表达式中使用
  4. 短路逻辑:利用&&||的短路特性优化代码
  5. 运算符优先级混淆:不确定时使用括号明确优先级

10.2 性能考虑

  1. 位运算通常比算术运算快
  2. 复合赋值可能比简单赋值效率略高
  3. 避免在循环条件中重复计算

10.3 可读性建议

  1. 复杂表达式拆分成多行
  2. 使用括号明确意图
  3. 避免过度使用三元运算符嵌套
  4. 为特殊位操作添加注释
// 好例子:清晰易读
int total = (basePrice * quantity) + (taxRate * basePrice);
boolean isValid = (input != null) && (input.length() > 0);

// 坏例子:过于复杂
int result=a*b+c/d-e%f+g<<h&i|j^k;

十一、Java运算符高级应用

11.1 使用位运算实现标志位

public class BitFlags {
    public static final int FLAG_A = 1 << 0; // 0001
    public static final int FLAG_B = 1 << 1; // 0010
    public static final int FLAG_C = 1 << 2; // 0100
    public static final int FLAG_D = 1 << 3; // 1000
    
    private int flags;
    
    public void setFlag(int flag) {
        flags |= flag;
    }
    
    public void clearFlag(int flag) {
        flags &= ~flag;
    }
    
    public boolean hasFlag(int flag) {
        return (flags & flag) == flag;
    }
    
    public void toggleFlag(int flag) {
        flags ^= flag;
    }
    
    public static void main(String[] args) {
        BitFlags bf = new BitFlags();
        bf.setFlag(FLAG_A);
        bf.setFlag(FLAG_C);
        
        System.out.println("Has FLAG_A: " + bf.hasFlag(FLAG_A)); // true
        System.out.println("Has FLAG_B: " + bf.hasFlag(FLAG_B)); // false
        
        bf.toggleFlag(FLAG_A);
        System.out.println("Has FLAG_A after toggle: " + bf.hasFlag(FLAG_A)); // false
    }
}

11.2 高效数学运算技巧

public class MathTricks {
    public static void main(String[] args) {
        // 判断奇偶
        int num = 15;
        boolean isEven = (num & 1) == 0;
        System.out.println(num + " is even? " + isEven); // false
        
        // 交换两个数不使用临时变量
        int a = 5, b = 10;
        a ^= b;
        b ^= a;
        a ^= b;
        System.out.println("a: " + a + ", b: " + b); // a:10, b:5
        
        // 计算绝对值
        int x = -25;
        int abs = (x ^ (x >> 31)) - (x >> 31);
        System.out.println("abs(" + x + ") = " + abs); // 25
        
        // 判断是否为2的幂
        int n = 16;
        boolean isPowerOfTwo = (n & (n - 1)) == 0 && n != 0;
        System.out.println(n + " is power of two? " + isPowerOfTwo); // true
    }
}

11.3 运算符重载的替代方案

Java不支持运算符重载,但可以通过方法实现类似功能:

public class ComplexNumber {
    private final double real;
    private final double imaginary;
    
    public ComplexNumber(double real, double imaginary) {
        this.real = real;
        this.imaginary = imaginary;
    }
    
    public ComplexNumber add(ComplexNumber other) {
        return new ComplexNumber(this.real + other.real, 
                               this.imaginary + other.imaginary);
    }
    
    public ComplexNumber subtract(ComplexNumber other) {
        return new ComplexNumber(this.real - other.real,
                               this.imaginary - other.imaginary);
    }
    
    @Override
    public String toString() {
        return real + " + " + imaginary + "i";
    }
    
    public static void main(String[] args) {
        ComplexNumber c1 = new ComplexNumber(2, 3);
        ComplexNumber c2 = new ComplexNumber(4, 5);
        
        ComplexNumber sum = c1.add(c2);
        ComplexNumber diff = c1.subtract(c2);
        
        System.out.println("Sum: " + sum);     // 6.0 + 8.0i
        System.out.println("Difference: " + diff); // -2.0 + -2.0i
    }
}

十二、常见问题解答

Q1:i++和++i到底哪个更快?

实测:在现代JVM上几乎没有区别!编译器会优化。但为了代码可读性:

  • 单独使用:用i++
  • 需要返回值:根据业务需求选择

Q2:为什么128 == 128返回false?

Integer a = 128;
Integer b = 128;
System.out.println(a == b); // false

Integer c = 127;
Integer d = 127;
System.out.println(c == d); // true

原因:Integer缓存了-128到127的值,超出范围会new新对象。应该用equals()比较!

Q3:如何避免运算符优先级问题?

提醒

  1. 记不住优先级?加括号!
  2. 复杂表达式拆分成多行
  3. 团队统一代码风格
// 迷惑写法
int result = a << 1 + b * c >> 2;

// 清晰写法
int temp = b * c;
int shifted = a << (1 + temp);
result = shifted >> 2;

十三、进阶知识:JVM如何处理运算符?

13.1 字节码层面看运算

javap -c查看编译后的字节码:

int a = 5;
int b = a++ + ++a;

对应的字节码:

0: iconst_5     // 把5压入栈
1: istore_1     // 存储到变量a
2: iload_1      // 加载a的值(5)
3: iinc 1, 1    // a自增(a=6)
6: iinc 1, 1    // a再自增(a=7)
9: iload_1      // 加载a的值(7)
10: iadd        // 栈顶两个值相加(5+7)
11: istore_2    // 存储到b

13.2 性能优化建议

  1. 循环中的运算:把不变的计算提到循环外
// 差
for (int i = 0; i < list.size() * 2; i++) {...}

// 好
int limit = list.size() * 2;
for (int i = 0; i < limit; i++) {...}
  1. 避免重复计算
// 差
double area = Math.PI * r * r;
double circumference = 2 * Math.PI * r;

// 好
final double piR = Math.PI * r;
double area = piR * r;
double circumference = 2 * piR;

总结

  1. 基础要牢:算术、关系、逻辑三大件必须烂熟于心
  2. 避坑指南:整数除法要小心浮点比较用误差范围字符串比较用equals
  3. 高级技巧:位运算处理标志位利用短路特性优化代码三目运算符简化if-else
  4. 最佳实践:不确定优先级就加括号复杂表达式拆解写适当使用复合赋值

这些 Java 运算符小精灵们,在代码的世界里各司其职,有时会让你的代码顺利运行,有时也会调皮地给你制造一些小麻烦,但正是它们的存在,让编程变得充满乐趣!

相关文章

在 JavaScript 中替换所有指定字符 3 种方法

在 JS 没有提供一种简便的方法来替换所有指定字符。 在 Java 中有一个 replaceAll() ,replaceAll(String regex, String replacement))方法...

在Java中实现字符串的动态替换

比如消息通知,短信发送之类的我们肯定是要用到字符串模版的替换的要在Java中实现字符串的动态替换,可以使用String.format方法或者MessageFormat类或者三方包。以下是使用这三种方法...

正则表达式学习之替换分组练习

切割案例小练习:字符串77 23 91 99 31 排序输出23 31 77 91 99分析:1、 字符切割数组2、 字符数组转换成数字数组3、 排序4、 遍历拼接字符串代码结果替换案例小练习字符串:...

Shell语言搜索路径、字符串替换、易用性

若文章对您有帮助,欢迎关注 程序员小迷 。助您在编程路上越走越好!Shell为了方便操作内核,一般为动态、弱类型语言。变量不管是什么类型,本质都是字符串,根据实际情况做转换。字符串替换新产品升级有时就...

Java面试“字符串三兄弟”String、StringBuilder、StringBuffer

Java面试中的“字符串三兄弟”:String、StringBuilder与StringBuffer在Java的世界里,字符串是一个非常重要的数据类型。而在众多的字符串操作类中,String、Stri...

漫画:腾讯面试题,请实现把字符串中的空格替换为“%20”

面试现场题目描述请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。import java.u...