ZetCode

Java运算符

最后修改于 2024 年 1 月 27 日

本文介绍如何在Java中使用运算符。

运算符是一种特殊符号,表示执行特定的过程。编程语言中的运算符取自数学。程序员处理数据。运算符用于处理数据。操作数是运算符的输入(参数)之一。

表达式由操作数和运算符构成。表达式的运算符指示要应用于操作数的操作。表达式中运算符的求值顺序由运算符的优先级结合性决定。

运算符通常有一个或两个操作数。仅使用一个操作数的运算符称为一元运算符。使用两个操作数的运算符称为二元运算符。还有一个三元运算符?:,它使用三个操作数。

某些运算符可以在不同的上下文中使用。例如,+运算符。它可以用于不同的情况。它可以加数字、连接字符串或表示数字的符号。我们说运算符是重载的。

Java 符号运算符

有两个符号运算符:+-。它们用于表示或改变值的符号。

com/zetcode/SignOperators.java
package com.zetcode;

public class SignOperators {

    public static void main(String[] args) {

        System.out.println(2);
        System.out.println(+2);
        System.out.println(-2);
    }
}

+- 符号表示值的符号。加号可用于表示我们有一个正数。它可以省略,而且通常也是这样做的。

com/zetcode/MinusSign.java
package com.zetcode;

public class MinusSign {

    public static void main(String[] args) {

        int a = 1;

        System.out.println(-a);
        System.out.println(-(-a));
    }
}

减号会改变值的符号。

Java 赋值运算符

赋值运算符 = 将一个值赋给一个变量。变量是值的占位符。在数学中,= 运算符具有不同的含义。在一个等式中,= 运算符是一个相等运算符。等式的左侧等于右侧。

int x = 1;

在这里,我们将一个数字赋给 x 变量。

x = x + 1;

这个表达式在数学上没有意义,但在编程中是合法的。该表达式将 x 变量加 1。右侧等于 2,并将 2 赋值给 x。

3 = x;

此代码行会导致语法错误。我们不能将值分配给字面量。

Java 连接字符串

在 Java 中,+ 运算符也用于连接字符串。

com/zetcode/ConcatenateStrings.java
package com.zetcode;

public class ConcatenateStrings {

    public static void main(String[] args) {

        System.out.println("Return " + "of " + "the king.");
        System.out.println("Return".concat(" of").concat(" the king."));
    }
}

我们将三个字符串连接在一起。

System.out.println("Return " + "of " + "the king.");

字符串使用 + 运算符连接。

System.out.println("Return".concat(" of").concat(" the king."));

连接字符串的另一种方法是 concat 方法。

$ java ConcatenateStrings.java
Return of the king.
Return of the king.

Java 递增和递减运算符

在编程中,将一个值递增或递减 1 是一个常见的任务。Java 有两个方便的运算符:++--

x++;
x = x + 1;
...
y--;
y = y - 1;

以上两对表达式执行相同的操作。

com/zetcode/IncDec.java
package com.zetcode;

public class IncDec {

    public static void main(String[] args) {

        int x = 6;

        x++;
        x++;

        System.out.println(x);

        x--;
        System.out.println(x);
    }
}

在上面的示例中,我们演示了这两个运算符的用法。

int x = 6;

x++;
x++;

我们将 x 变量初始化为 6。然后我们对 x 加一两次。现在变量的值是 8。

x--;

我们使用了递减运算符。现在变量的值是 7。

$ java IncDec.java
8
7

这是示例的输出。

Java 算术运算符

下表是 Java 中的算术运算符。

符号名称
+加法
-减法
*乘法
/除法
%余数

下面的示例展示了算术运算。

com/zetcode/Arithmetic.java
package com.zetcode;

public class Arithmetic {

    public static void main(String[] args) {

        int a = 10;
        int b = 11;
        int c = 12;

        int add = a + b + c;
        int sb = c - a;
        int mult = a * b;
        int div = c / 3;
        int rem = c % a;

        System.out.println(add);
        System.out.println(sb);
        System.out.println(mult);
        System.out.println(div);
        System.out.println(rem);
    }
}

在前面的示例中,我们使用了加法、减法、乘法、除法和取余运算。这些都与数学中的一样熟悉。

int rem = c % a;

% 运算符称为余数或模运算符。它用于找出两个数相除的余数。例如,9 % 4,9 模 4 等于 1,因为 4 能整除 9 两次,余数为 1。

$ java Arithmetic.java
33
2
110
4
2

接下来,我们展示整数和浮点数除法之间的区别。

com/zetcode/Division.java
package com.zetcode;

public class Division {

    public static void main(String[] args) {

        int c = 5 / 2;
        System.out.println(c);

        double d = 5 / 2.0;
        System.out.println(d);
    }
}

在前面的示例中,我们对两个数进行了除法运算。

int c = 5 / 2;

在这段代码中,我们进行了整数除法。除法运算的返回值是整数。当我们对两个整数进行除法时,结果是一个整数。

double d = 5 / 2.0;

如果其中一个值是双精度浮点数或单精度浮点数,我们就执行浮点数除法。在本例中,第二个操作数是双精度浮点数,所以结果也是双精度浮点数。

$ java Division.java
2
2.5

我们看到了程序的运行结果。

Java 布尔运算符

在 Java 中,我们有三个逻辑运算符。 boolean 关键字用于声明布尔值。

符号名称
&&逻辑与
||逻辑或
!逻辑非

布尔运算符也称为逻辑运算符。

com/zetcode/BooleanOperators.java
package com.zetcode;

public class BooleanOperators {

    public static void main(String[] args) {

        int x = 3;
        int y = 8;

        System.out.println(x == y);
        System.out.println(y > x);

        if (y > x) {

            System.out.println("y is greater than x");
        }
    }
}

许多表达式会产生布尔值。例如,布尔值用于条件语句。

System.out.println(x == y);
System.out.println(y > x);

关系运算符始终产生布尔值。这两行打印 false 和 true。

if (y > x) {

    System.out.println("y is greater than x");
}

只有当括号内的条件满足时,才会执行 if 语句的主体。y > x 返回 true,因此消息 "y is greater than x" 会打印到终端。

truefalse 关键字表示 Java 中的布尔字面量。

com/zetcode/AndOperator.java
package com.zetcode;

public class AndOperator {

    public static void main(String[] args) {

        boolean a = true && true;
        boolean b = true && false;
        boolean c = false && true;
        boolean d = false && false;

        System.out.println(a);
        System.out.println(b);
        System.out.println(c);
        System.out.println(d);
    }
}

代码示例展示了逻辑与 (&&) 运算符。仅当两个操作数都为 true 时,它才为 true。

$ java AndOperator.java
true
false
false
false

只有一个表达式的结果为 true

逻辑或 (||) 运算符,如果任何一个操作数为真,则求值为 true

com/zetcode/OrOperator.java
package com.zetcode;

public class OrOperator {

    public static void main(String[] args) {

        boolean a = true || true;
        boolean b = true || false;
        boolean c = false || true;
        boolean d = false || false;

        System.out.println(a);
        System.out.println(b);
        System.out.println(c);
        System.out.println(d);
    }
}

如果运算符的任一边为 true,则操作的结果为 true。

$ java OrOperator.java
true
true
true
false

四个表达式中有三个的结果为 true

否定运算符 ! 将 true 变为 false,将 false 变为 true。

com/zetcode/Negation.java
package com.zetcode;

public class Negation {

    public static void main(String[] args) {

        System.out.println(! true);
        System.out.println(! false);
        System.out.println(! (4 < 3));
    }
}

该示例展示了否定运算符的实际应用。

$ java Negation.java
false
true
true

||&& 运算符是短路求值的。短路求值意味着仅当第一个参数不足以确定表达式的值时,才对第二个参数求值:当逻辑与的第一个参数求值为 false 时,总值必须为 false;当逻辑或的第一个参数求值为 true 时,总值必须为 true。短路求值主要用于提高性能。

一个例子可以更清楚地说明这一点。

com/zetcode/ShortCircuit.java
package com.zetcode;

public class ShortCircuit {

    public static boolean One() {

        System.out.println("Inside one");
        return false;
    }

    public static boolean Two() {

        System.out.println("Inside two");
        return true;
    }

    public static void main(String[] args) {

        System.out.println("Short circuit");

        if (One() && Two()) {

            System.out.println("Pass");
        }

        System.out.println("#############");

        if (Two() || One()) {

            System.out.println("Pass");
        }
    }
}

我们在示例中有两个方法。 它们用作布尔表达式中的操作数。 我们将看到它们是否被调用。

if (One() && Two()) {

    System.out.println("Pass");
}

One 方法返回 false。 短路 && 不会评估第二个方法。 没有必要。 一旦一个操作数为假,逻辑结论的结果始终为假。 只有“Inside one”会打印到控制台。

if (Two() || One()) {

    System.out.println("Pass");
}

在第二种情况下,我们使用 || 运算符并将 Two 方法用作第一个操作数。 在这种情况下,“Inside two”和“Pass”字符串会打印到终端。 同样,没有必要评估第二个操作数,因为一旦第一个操作数求值为 true,逻辑或始终为 true。

$ java ShortCircuit.java
Short circuit
Inside one
#############
Inside two
Pass

我们看到了程序的运行结果。

Java 关系运算符

关系运算符用于比较值。这些运算符始终产生布尔值。

符号含义
<小于
<=小于或等于
>大于
>=大于或等于
==等于
!=不等于

关系运算符也称为比较运算符。

com/zetcode/Relational.java
package com.zetcode;

public class Relational {

    public static void main(String[] args) {

        System.out.println(3 < 4);
        System.out.println(3 == 4);
        System.out.println(4 >= 3);
        System.out.println(4 != 3);
    }
}

在代码示例中,我们有四个表达式。 这些表达式比较整数值。 每个表达式的结果为 true 或 false。 在 Java 中,我们使用 == 来比较数字。(一些语言,如 Ada、Visual Basic 或 Pascal 使用 = 来比较数字。)

Java 位运算符

十进制数对人类来说很自然。 二进制数是计算机原生的。 二进制、八进制、十进制或十六进制符号只是数字的符号表示。 位运算符使用二进制数的位。 位运算符在 Java 等高级语言中很少使用。

符号含义
~按位取反
^按位异或
&按位与
|按位或

按位取反运算符 将每个 1 变为 0,将 0 变为 1。

System.out.println(~7); // prints -8
System.out.println(~ -8); // prints 7

运算符反转数字 7 的所有位。 其中一位还确定数字是否为负数。 如果我们再次否定所有位,我们将再次得到数字 7。

按位与运算符对两个数字执行逐位比较。只有当操作数中对应的位都为 1 时,结果的该位才为 1。

      00110
   &  00011
   =  00010

第一个数字是 6 的二进制表示法,第二个是 3,结果是 2。

System.out.println(6 & 3); // prints 2
System.out.println(3 & 6); // prints 2

按位或运算符对两个数字执行逐位比较。当操作数中对应的位任一为 1 时,结果的该位为 1。

     00110
   | 00011
   = 00111

结果是 00110 或十进制的 7。

System.out.println(6 | 3); // prints 7
System.out.println(3 | 6); // prints 7

按位异或运算符对两个数字执行逐位比较。当操作数中对应的位任一为 1 但不是两者都为 1 时,结果的该位为 1。

      00110
   ^  00011
   =  00101

结果是 00101 或十进制的 5。

System.out.println(6 ^ 3); // prints 5
System.out.println(3 ^ 6); // prints 5

Java 复合赋值运算符

复合赋值运算符是由两个运算符组成的简写运算符。

a = a + 3;
a += 3;

+= 复合运算符是这些简写运算符之一。上面的两个表达式是相等的。将值 3 加到 a 变量上。

其他复合运算符是

-=   *=   /=   %=   &=   |=   <<=   >>=

以下示例使用两个复合运算符。

com/zetcode/CompoundOperators.java
package com.zetcode;

public class CompoundOperators {

    public static void main(String[] args) {

        int a = 1;
        a = a + 1;

        System.out.println(a);

        a += 5;
        System.out.println(a);

        a *= 3;
        System.out.println(a);
    }
}

我们使用 +=*= 复合运算符。

int a = 1;
a = a + 1;

a 变量初始化为 1。 使用非简写符号将值 1 添加到变量。

a += 5;

使用 += 复合运算符,我们将 5 添加到 a 变量。 该语句等于 a = a + 5;

a *= 3;

使用 *= 运算符,a 乘以 3。 该语句等于 a = a * 3;

$ java CompoundOperators.java
2
7
21

Java instanceof 运算符

instanceof 运算符将对象与指定的类型进行比较。

com/zetcode/InstanceofOperator.java
package com.zetcode;

class Base {}
class Derived extends Base {}

public class InstanceofOperator {

    public static void main(String[] args) {

        Base b = new Base();
        Derived d = new Derived();

        System.out.println(d instanceof Base);
        System.out.println(b instanceof Derived);
        System.out.println(d instanceof Object);
    }
}

在该示例中,我们有两个类:一个基类和一个从基类派生的类。

System.out.println(d instanceof Base);

此行检查变量 d 是否指向作为 Base 类实例的类。 由于 Derived 类继承自 Base 类,因此它也是 Base 类的实例。 该行打印 true。

System.out.println(b instanceof Derived);

b 对象不是 Derived 类的实例。 此行打印 false。

System.out.println(d instanceof Object);

每个类都有 Object 作为超类。 因此,d 对象也是 Object 类的实例。

$ java InstanceofOperator.java
true
false
true

Java Lambda 运算符

Java 8 引入了 lambda 运算符 (->)。

(parameters) -> expression
(parameters) -> { statements; }

这是 Java 中 lambda 表达式的基本语法。 Lambda 表达式允许在 Java 中创建更简洁的代码。

参数的类型声明是可选的;编译器可以从参数的值推断类型。 对于单个参数,括号是可选的;对于多个参数,它们是必需的。

如果表达式主体中只有一个语句,则大括号是可选的。 最后,如果主体有一个表达式要返回值,则 return 关键字是可选的; 需要大括号来指示表达式返回值。

com/zetcode/LambdaExpression.java
package com.zetcode;

import java.util.Arrays;

public class LambdaExpression {

    public static void main(String[] args) {

        String[] words = { "kind", "massive", "atom", "car", "blue" };

        Arrays.sort(words, (String s1, String s2) -> (s1.compareTo(s2)));

        System.out.println(Arrays.toString(words));
    }
}

在该示例中,我们定义了一个字符串数组。 该数组使用 Arrays.sort 方法和 lambda 表达式进行排序。

$ java LambdaExpression.java
[atom, blue, car, kind, massive]

Lambda 表达式主要用于定义函数式接口的内联实现,即仅具有单个方法的接口。 接口是用于强制执行契约的抽象类型。

com/zetcode/LambdaExpression2.java
package com.zetcode;

interface GreetingService {

    void greet(String message);
}

public class LambdaExpression2 {

    public static void main(String[] args) {

        GreetingService gs = (String msg) -> {
            System.out.println(msg);
        };

        gs.greet("Good night");
        gs.greet("Hello there");
    }
}

在该示例中,我们借助 lambda 表达式创建了一个问候服务。

interface GreetingService {

    void greet(String message);
}

创建了 GreetingService 接口。 实现此接口的所有对象都必须实现 greet 方法。

GreetingService gs = (String msg) -> {
    System.out.println(msg);
};

我们创建一个使用 lambda 表达式实现 GreetingService 的对象。 该对象具有一个将消息打印到控制台的方法。

gs.greet("Good night");

我们调用该对象的 greet 方法,该方法将给定的消息打印到控制台。

$ java LambdaExpression2.java
Good night
Hello there

有一些常见的函数式接口,例如 FunctionConsumerSupplier

com/zetcode/LambdaExpression3.java
package com.zetcode;

import java.util.function.Function;

public class LambdaExpression3 {

    public static void main(String[] args) {

        Function<Integer, Integer> square = (Integer x) -> x * x;
        System.out.println(square.apply(5));
    }
}

该示例使用 lambda 表达式来计算整数的平方。

Function<Integer, Integer> square = (Integer x) -> x * x;
System.out.println(square.apply(5));

Function 是一个接受一个参数并产生结果的函数。 lambda 表达式的操作产生给定整数的平方。

Java 双冒号运算符

双冒号运算符 (::) 用于创建对方法的引用。

com/zetcode/DoubleColonOperator.java
package com.zetcode;

import java.util.function.Consumer;

public class DoubleColonOperator {

    private static void greet(String msg) {

        System.out.println(msg);
    }

    public static void main(String[] args) {

        Consumer<String> f = DoubleColonOperator::greet;
        f.accept("Hello there");
    }
}

在代码示例中,我们使用双冒号运算符创建对静态方法的引用。

private static void greet(String msg) {

    System.out.println(msg);
}

我们有一个静态方法,可以将问候语打印到控制台。

Consumer<String> f = DoubleColonOperator::greet;

Consumer 是一个函数式接口,表示接受单个输入参数且不返回结果的操作。 使用双冒号运算符,我们创建对 greet 方法的引用。

f.accept("Hello there");

我们使用 accept 方法执行函数式操作。

Java 运算符优先级

运算符优先级告诉我们哪些运算符首先被求值。优先级级别对于避免表达式中的歧义是必需的。

以下表达式的结果是 28 还是 40?

3 + 5 * 5

就像在数学中一样,乘法运算符的优先级高于加法运算符。所以结果是 28。

(3 + 5) * 5

要更改求值顺序,我们可以使用括号。括号内的表达式总是首先被求值。上述表达式的结果是 40。

Java 运算符优先级列表

下表显示了按优先级排序的常见 Java 运算符(优先级最高的排在第一位)

运算符 含义 结合性
[] () . 数组访问、方法调用、对象成员访问 从左到右
++ -- + - 递增、递减、一元加号和减号 从右到左
! ~ (type) new 取反、按位非、类型转换、对象创建 从右到左
* / % 乘法、除法、取模 从左到右
+ - 加法、减法 从左到右
+ 字符串连接 从左到右
<< >> >>> 移位 从左到右
< <= > >= 关系 从左到右
instanceof 类型比较 从左到右
== != 相等 从左到右
& 按位与 从左到右
^ 按位异或 从左到右
| 按位或 从左到右
&& 逻辑与 从左到右
|| 逻辑或 从左到右
? : 三元 从右到左
= 简单赋值 从右到左
+= -= *= /= %= &= 复合赋值 从右到左
^= |= <<= >>= >>>= 复合赋值 从右到左
表:运算符优先级和结合性

表中同一行的运算符具有相同的优先级。 如果我们使用具有相同优先级的运算符,则应用结合性规则。

com/zetcode/Precedence.java
package com.zetcode;

public class Precedence {

    public static void main(String[] args) {

        System.out.println(3 + 5 * 5);
        System.out.println((3 + 5) * 5);

        System.out.println(! true | true);
        System.out.println(! (true | true));
    }
}

在这段代码示例中,我们展示了几个表达式。每个表达式的结果取决于优先级级别。

System.out.println(3 + 5 * 5);

此行打印 28。 乘法运算符的优先级高于加法。 首先,计算 5*5 的乘积,然后加 3。

System.out.println(! true | true);

在这种情况下,取反运算符的优先级高于按位或。 首先,初始的真值被取反为假,然后 | 运算符将假和真组合在一起,最终得到真。

$ java Precedence.java
28
40
true
false

Java 结合性规则

有时优先级不足以确定表达式的结果。还有另一个规则称为结合性。运算符的结合性决定了具有相同优先级级别的运算符的求值顺序。

9 / 3 * 3

这个表达式的结果是 9 还是 1?乘法、除法和模运算符是从左到右结合的。所以表达式的求值方式是:(9 / 3) * 3 结果是 9。

算术、布尔、关系和位运算符都是从左到右结合的。 赋值运算符、三元运算符、递增、递减、一元加号和减号、取反、按位非、类型转换、对象创建运算符都是从右到左结合的。

com/zetcode/Associativity.java
package com.zetcode;

public class Associativity {

    public static void main(String[] args) {

        int a, b, c, d;
        a = b = c = d = 0;

        String str = String.format("%d %d %d %d", a, b, c, d);
        System.out.println(str);

        int j = 0;
        j *= 3 + 1;
        System.out.println(j);
    }
}

在该示例中,我们有两种情况,其中结合性规则确定了表达式。

int a, b, c, d;
a = b = c = d = 0;

赋值运算符是从右到左结合的。 如果结合性是从左到右,则先前的表达式是不可能的。

int j = 0;
j *= 3 + 1;

复合赋值运算符是从右到左结合的。 我们可能期望结果为 1。但实际结果为 0。这是因为结合性。 首先评估右侧的表达式,然后应用复合赋值运算符。

$ java Associativity.java
0 0 0 0
0

Java 三元运算符

三元运算符 ?: 是一个条件运算符。 对于我们想要根据条件表达式选择两个值之一的情况,它是一个方便的运算符。

cond-exp ? exp1 : exp2

如果 cond-exp 为 true,则评估 exp1 并返回结果。 如果 cond-exp 为 false,则评估 exp2 并返回其结果。

com/zetcode/TernaryOperator.java
package com.zetcode;

public class TernaryOperator {

    public static void main(String[] args) {

        int age = 31;

        boolean adult = age >= 18 ? true : false;

        System.out.println(String.format("Adult: %s", adult));
    }
}

在大多数国家/地区,成年是基于年龄的。 如果您超过某个年龄,您就是成年人。 这是三元运算符的一种情况。

boolean adult = age >= 18 ? true : false;

首先,计算赋值运算符右侧的表达式。三元运算符的第一阶段是条件表达式求值。因此,如果年龄大于或等于 18,则返回 ? 字符后面的值。否则,返回 : 字符后面的值。然后将返回的值赋给 adult 变量。

$ java TernaryOperator.java
Adult: true

一个 31 岁的人是成年人。

计算质数

在以下示例中,我们将计算质数。

com/zetcode/PrimeNumbers.java
package com.zetcode;

public class PrimeNumbers {

    public static void main(String[] args) {

        int[] nums = { 0, 1, 2, 3, 4, 5, 6, 7, 8,
            9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
            19, 20, 21, 22, 23, 24, 25, 26, 27, 28 };

        System.out.print("Prime numbers: ");

        for (int num : nums) {

            if (num == 0 || num == 1) {
                continue;
            }

            if (num == 2 || num == 3) {

                System.out.print(num + " ");
                continue;
            }

            int i = (int) Math.sqrt(num);

            boolean isPrime = true;

            while (i > 1) {

                if (num % i == 0) {

                    isPrime = false;
                }

                i--;
            }

            if (isPrime) {

                System.out.print(num + " ");
            }
        }

        System.out.print('\n');
    }
}

在上面的示例中,我们处理了几个运算符。质数(或素数)是一个自然数,它恰好有两个不同的自然数除数:1 和它本身。我们选择一个数字,然后用从 1 到所选数字的数字来除它。实际上,我们不必尝试所有较小的数字;我们可以用小于或等于所选数字的平方根的数字来除它。该公式将有效。我们使用求余运算符。

int[] nums = { 0, 1, 2, 3, 4, 5, 6, 7, 8,
    9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
    19, 20, 21, 22, 23, 24, 25, 26, 27, 28 };

我们将从这些数字中计算质数。

if (num == 0 || num == 1) {
    continue;
}

值 0 和 1 不被认为是质数。

if (num == 2 || num == 3) {

    System.out.print(num + " ");
    continue;
}

我们跳过 2 和 3 的计算。它们是质数。请注意等号和条件或运算符的用法。== 的优先级高于 || 运算符。所以我们不需要使用括号。

int i = (int) Math.sqrt(num);

如果我们只尝试小于目标数字的平方根的数字,那就可以了。

while (i > 1) {
    ...
    i--;
}

这是一个 while 循环。i 是计算出的数字的平方根。我们使用递减运算符使 i 在每个循环周期中减一。当 i 小于 1 时,我们终止循环。例如,我们有数字 9。9 的平方根是 3。我们将用 3 和 2 除数字 9。这足以满足我们的计算。

if (num % i == 0) {

    isPrime = false;
}

如果求余运算符对于任何 i 值返回 0,则目标数字不是质数。

在本文中,我们介绍了 Java 表达式。我们提到了各种类型的运算符,并描述了表达式中的优先级和结合性规则。

来源

Java 运算符 - 教程

作者

我叫 Jan Bodnar,我是一位充满激情的程序员,拥有丰富的编程经验。我从 2007 年开始撰写编程文章。迄今为止,我撰写了超过 1,400 篇文章和 8 本电子书。我拥有超过十年的编程教学经验。

列出所有Java教程