3、运算

所有运算符都能根据自己的运算对象生成一个值。除此以外,一些运算符可改变运算对象的值,这叫作“副作用”(Side Effect)。

几乎所有运算符都只能操作基本类型(Primitives)。唯一的例外是 =、== 和 !=,它们能操作所有对象(这也是令人混淆的一个地方)。除此以外,String 类支持 + 和 +=。

1、赋值

右边可以是任何常量、变量或者可产生一个返回值的表达式。但左边必须是一个明确的、已命名的变量。也就是说,必须要有一个物理的空间来存放右边的值。

基本类型的赋值都是直接的,而不像对象,赋予的只是其内存的引用。

// operators/Assignment.java
// Assignment with objects is a bit tricky
class Tank {
    int level;
}

public class Assignment {

    public static void main(String[] args) {
        Tank t1 = new Tank();
        Tank t2 = new Tank();
        t1.level = 9;
        t2.level = 47;
        System.out.println("1: t1.level: " + t1.level +
            ", t2.level: " + t2.level);
        t1 = t2;
        System.out.println("2: t1.level: " + t1.level +
            ", t2.level: " + t2.level);
        t1.level = 27;
        System.out.println("3: t1.level: " + t1.level +
            ", t2.level: " + t2.level);
    }
}

在 Java 中,由于赋予的只是对象的引用,改变 t1 也就改变了 t2。 这是因为 t1 和 t2 此时指向的是堆中同一个对象。(t1 原始对象的引用在 t2 赋值给其时丢失,它引用的对象会在垃圾回收时被清理)。

2、方法调用中的别名

当我们把对象传递给方法时,会发生别名现象。

// operators/PassObject.java
// 正在传递的对象可能不是你之前使用的
class Letter {
    char c;
}

public class PassObject {
    static void f(Letter y) {
        y.c = 'z';
    }

    public static void main(String[] args) {
        Letter x = new Letter();
        x.c = 'a';
        System.out.println("1: x.c: " + x.c);
        f(x);
        System.out.println("2: x.c: " + x.c);
     }
}

结果:

1: x.c: a
2: x.c: z

一旦传递了一个引用。那么实际上 y.c =‘z’; 是在方法 f() 之外改变对象。

3、递增和递减

前递增和前递减(如 ++a 或 --a),会先执行递增/减运算,再返回值。而对于后递增和后递减(如 a++ 或 a–),会先返回值,再执行递增/减运算。

4、测试等价对象

// operators/Equivalence.java
public class Equivalence {
    public static void main(String[] args) {
        Integer n1 = 128;
        Integer n2 = 128;
        System.out.println(n1 == n2);
        System.out.println(n1 != n2);
    }
}

结果:

false
true

尽管对象的内容一样,对象的引用却不一样。== 和 != 比较的是对象引用,所以输出实际上应该是先输出 false,再输出 true.
Integer 内部维护着一个 IntegerCache 的缓存,默认缓存范围是 [-128, 127],所以 [-128, 127] 之间的值用== 和 != 比较也能能到正确的结果。
必须使用所有对象(不包括基本类型)中都存在的 equals() 方法.

// operators/EqualsMethod2.java
// 默认的 equals() 方法没有比较内容
class Value {
    int i;
}

public class EqualsMethod2 {
    public static void main(String[] args) {
        Value v1 = new Value();
        Value v2 = new Value();
        v1.i = v2.i = 100;
        System.out.println(v1.equals(v2));
    }
}

结果:

false

原因: equals() 的默认行为是比较对象的引用而非具体内容。因此,除非你在新类中覆写 equals() 方法,否则我们将获取不到想要的结果。
大多数 Java 库类通过覆写 equals() 方法比较对象的内容而不是其引用。

5、逻辑运算

在 Java 逻辑运算中,我们不能像 C/C++ 那样使用非布尔值, 而仅能使用 AND、 OR、 NOT。
逻辑运算符支持一种称为“短路”(short-circuiting)的现象。整个表达式会在运算到可以明确结果时就停止并返回结果,这意味着该逻辑表达式的后半部分不会被执行到。

6、字面值常量

通常,当我们向程序中插入一个字面值常量(Literal)时,编译器会确切地识别它的类型。

在文本值的后面添加字符可以让编译器识别该文本值的类型。

  • 前缀

Long :大写 L 或小写 l (不推荐使用 l,因为容易与阿拉伯数值 1 混淆)。 float:大写 F 或小写 f 。
double :大写 D 或小写 d。

  • 后缀

16进制: 0x 或 0X
8进制: 0
2进制: 0b 或 0B

我们可以在数字字面量中包含下划线 _,以使结果更清晰。这对于大数值的分组特别有用。

// operators/Underscores.java
public class Underscores {
    public static void main(String[] args) {
        double d = 341_435_936.445_667;
        System.out.println(d);
        int bin = 0b0010_1111_1010_1111_1010_1111_1010_1111;
        System.out.println(Integer.toBinaryString(bin));
        System.out.printf("%x%n", bin); // [1]
        long hex = 0x7f_e9_b7_aa;
        System.out.printf("%x%n", hex);
    }
}

结果

3.41435936445667E8
101111101011111010111110101111
2fafafaf
7fe9b7aa

下面是合理使用的规则:

  1. 仅限单 _,不能多条相连。
  2. 数值开头和结尾不允许出现 _。
  3. F、D 和 L的前后禁止出现 _
  4. 二进制前导 b 和 十六进制 x 前后禁止出现 _。

7、位运算

位运算符允许我们操作一个整型数字中的单个二进制位。位运算符会对两个整数对应的位执行布尔代数,从而产生结果。

位运算源自 C 语言的底层操作。我们经常要直接操纵硬件,频繁设置硬件寄存器内的二进制位。Java 的设计初衷是电视机顶盒嵌入式开发,所以这种底层的操作仍被保留了下来。但是,你可能不会使用太多位运算。

若两个输入位都是 1,则按位“与运算符” & 运算后结果是 1,否则结果是 0。若两个输入位里至少有一个是 1,则按位“或运算符” | 运算后结果是 1;只有在两个输入位都是 0 的情况下,运算结果才是 0。若两个输入位的某一个是 1,另一个不是 1,那么按位“异或运算符” ^ 运算后结果才是 1。按位“非运算符” ~ 属于一元运算符;它只对一个自变量进行操作(其他所有运算符都是二元运算符)。按位非运算后结果与输入位相反。例如输入 0,则输出 1;输入 1,则输出 0。

我们可以对 boolean 型变量执行与、或、异或运算,但不能执行非运算(大概是为了避免与逻辑“非”混淆)。对于布尔值,位运算符具有与逻辑运算符相同的效果,只是它们不会中途“短路”

右移位运算符有“正”、“负”值:若值为正,则在高位插入 0;若值为负,则在高位插入 1。Java 也添加了一种“不分正负”的右移位运算符(>>>),它使用了“零扩展”(zero extension):无论正负,都在高位插入 0。

如果移动 char、byte 或 short,则会在移动发生之前将其提升为 int,结果为 int。若对一个 long 值进行处理,最后得到的结果也是 long。

移位可以与等号 <<= 或 >>= 或 >>>= 组合使用。左值被替换为其移位运算后的值。左值被替换为其移位运算后的值。但是,问题来了,当无符号右移与赋值相结合时,若将其与 byte 或 short 一起使用的话,则结果错误。取而代之的是,它们被提升为 int 型并右移,但在重新赋值时被截断。在这种情况下,结果为 -1。

// operators/URShift.java
// 测试无符号右移
public class URShift {
    public static void main(String[] args) {
        int i = -1;
        System.out.println(Integer.toBinaryString(i));
        i >>>= 10;
        System.out.println(Integer.toBinaryString(i));
        long l = -1;
        System.out.println(Long.toBinaryString(l));
        l >>>= 10;
        System.out.println(Long.toBinaryString(l));
        short s = -1;
        System.out.println(Integer.toBinaryString(s));
        s >>>= 10;
        System.out.println(Integer.toBinaryString(s));
        byte b = -1;
        System.out.println(Integer.toBinaryString(b));
        b >>>= 10;
        System.out.println(Integer.toBinaryString(b));
        b = -1;
        System.out.println(Integer.toBinaryString(b));
        System.out.println(Integer.toBinaryString(b>>>10));
    }
}
11111111111111111111111111111111
1111111111111111111111
1111111111111111111111111111111111111111111111111111111111111111
111111111111111111111111111111111111111111111111111111
11111111111111111111111111111111
11111111111111111111111111111111
11111111111111111111111111111111
11111111111111111111111111111111
11111111111111111111111111111111
1111111111111111111111

8、类型转换

在 Java 里,类型转换则是一种比较安全的操作。但是,若将数据类型进行“向下转换”(Narrowing Conversion)的操作(将容量较大的数据类型转换成容量较小的类型),可能会发生信息丢失的危险。此时,编译器会强迫我们进行转型,好比在提醒我们:该操作可能危险,若你坚持让我这么做,那么对不起,请明确需要转换的类型。 对于“向上转换”(Widening conversion),则不必进行显式的类型转换,因为较大类型的数据肯定能容纳较小类型的数据,不会造成任何信息的丢失。

从 float 和 double 转换为整数值时,小数位将被截断。若你想对结果进行四舍五入,可以使用 java.lang.Math 的 round() 方法。

两个大的 int 型整数相乘时,结果有可能超出 int 型的范围。
【参考】:On java 8中文翻译

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值