java学习笔记 java编程思想 第3章 操作符


在最底层,Java中的数据是通过使用操作符来操作的。

3.1 更简单的打印语句

import java.util.Date;
// javase5新增加的概念 静态导入(static import)
import static net.mindview.util.Print.*;

public class Test01 {

}

class HelloDate {
    public static void main(String[] args) {
        print("Hello, it's: ");
        print(new Date());
    }
}
// 运行结果
Hello, it's: 
Sat Mar 27 20:19:13 CST 2021

练习1

import static  net.mindview.util.Print.*;

public class Exec01 {
    public static void main(String[] args) {
        System.out.println("Hello");
        print("Hello");
    }
}
// 运行结果
Hello
Hello

3.2 使用Java操作符

大股份操作符都操作基本数据类型,而“=”、“==”、“!=”还可以操作对象。此外,String类支持“+”和“+=”。

3.3 优先级

在字符串中,“+”的意思是“字符串连接”。

public class Test02 {

    public static void main(String[] args) {
        int x = 1, y = 2, z = 3;
        int a = x + y - 2/2 + z;  // 5
        int b = x + (y - 2) / (2 + z);  // 1
        System.out.println("a="  + a + ", b=" + b);
    }
}
// 运型结果
a=5, b=1

3.4 赋值

赋值使用操作符“=”。意思是:取右边的值(即右值),把它估值给左边(即左值)。

  • 右值:可以是任何常数、变量、表达式(只要能生成一个值就能当作右值)。
  • 左值:必须是一个明确的、已命名的变量。常数不能作为左值。也就是说,必须有一个物理空间可以存储等号右边的值。
a = 4;

对基本数据类型的赋值,使用a=b,修改a的内容,b的内容不会受到这中修改的影响。

但是在为对象赋值时,就发生了变化。对一个对象进行操作时,其实真正操作的是对象的引用。

代码实例

import static net.mindview.util.Print.*;

public class Test03 {

    public static void main(String[] args) {
        Tank t1 = new Tank();
        Tank t2 = new Tank();
        t1.level = 9;
        t2.level = 10;
        print("1: t1:level=" + t1.level + ", t2:level=" + t2.level);
        // t1也指向了t2指向的那个level=10的对象,
        // 之前t1指向的那个level=9的对象没有任何引用指向它,会被垃圾回收器自动清理。
        t1 = t2;
        print("1: t1:level=" + t1.level + ", t2:level=" + t2.level);
        t1.level = 2222;
        print("1: t1:level=" + t1.level + ", t2:level=" + t2.level);
    }
}

class Tank {
    int level;
}
// 运行结果
1: t1:level=9, t2:level=10
2: t1:level=10, t2:level=10
3: t1:level=2222, t2:level=2222

这中现象称为“别名现象”,它是java操作对象的一种的方式。

// 避免别名问题的办法就是不进行引用赋值,而是将对象里面的基本类型进行赋值。
t1.level = t2.level;

这样做可以保持连个对象彼此相互对立。但是直接操作对象内的域容易导致混乱,违背了面向对象程序设计的原则。这可不是小问题,所有从现在开始就应该注意,为对象赋值可能会产生意想不到的结果。

练习2

public class Exec02 {

    public static void main(String[] args) {
        Student stu1 = new Student();
        Student stu2 = new Student();
        stu1.score = 99;
        stu2.score = 80;
        System.out.println("1: stu1: score=" + stu1.score + ", stu2: score=" + stu2.score);
        stu1 = stu2;
        System.out.println("2: stu1: score=" + stu1.score + ", stu2: score=" + stu2.score);
        stu2.score = 89.0f;
        System.out.println("3: stu1: score=" + stu1.score + ", stu2: score=" + stu2.score);
    }
}

class Student {
    int sno;
    float score;
}

// 运行结果
1: stu1: score=99.0, stu2: score=80.0
2: stu1: score=80.0, stu2: score=80.0
3: stu1: score=89.0, stu2: score=89.0

3.4.1 方法调用中的别名问题

将一个对象传递给方法时,也会出现别名问题。

代码示例

import static net.mindview.util.Print.print;

public class PassObject {
    public static void f(Letter y) {
        y.c = 'z';
    }
    public static void main(String[] args) {
        Letter l = new Letter();
        l.c = 'a';
        print("1: c=" + l.c);
        // 传递引用,f(Letter y)会将传入的引用l复制一份,赋值给局部变量y
        // 实际改变的是f()之外的对象
        f(l);
        print("2: c=" + l.c);
    }
}
class Letter {
    char c;
}
// 运行结果
1: c=a
2: c=z

练习3

import static net.mindview.util.Print.print;

public class Exec03 {
    float score;
    public static void main(String[] args) {
        Exec03 e = new Exec03();
        e.score = 10.0f;
        print("1: score=" + e.score);
        e.method1(e);
        print("2: score=" + e.score);
    }
    
    public void method1(Exec03 obj) {
        obj.score = 20.0f;
    }
}
// 运行结果
1: score=10.0
2: score=20.0

3.5 算术操作符

+, -, *, /, %
+=, -=. *=, /=, %=

练习4

import static net.mindview.util.Print.print;

public class Exec04 {
    public static void main(String[] args) {
        Vehicle vehicle1 = new Vehicle();
        vehicle1.distance = 50;
        vehicle1.time = 0.5;
        // 车辆行驶的平均速度 单位:km/h
        double velocity = vehicle1.computeVelocityByDistanceAndTime(vehicle1.distance, vehicle1.time);
        print("d=" + vehicle1.distance + "km, t=" + vehicle1.time + "h, v=" + velocity + "km/h");
    }

}
class Vehicle {
    double distance;  // 车辆行驶路程 单位:km
    double time;  // 车辆行驶时间 单位:hour

    public double computeVelocityByDistanceAndTime(double d, double t) {
        return d / t;
    }
}
// 运行结果
d=50.0km, t=0.5h, v=100.0km/h

3.6 自动递增和递减

代码

import static net.mindview.util.Print.print;

public class Test05 {
    public static void main(String[] args) {
        int i = 1;
        print("i : " + i);  // 1
        print("++i : " + ++i);  // 2
        print("i++ : " + i++);  // 2
        print("i : " + i);  // 3
        print("--i : " + --i);  // 2
        print("i-- : "  + i--);  // 2
        print("i : " + i);  // 1
    }
}
// 运行结果
i : 1
++i : 2
i++ : 2
i : 3
--i : 2
i-- : 2
i : 1

3.7 关系操作符

3.7.1 测试对象的等价性

class EquivalenceInteger {
    public static void main(String[] args) {
        // 因为不通过new创建对象,直接使用字面值赋值,s1和s2都指向同一个地址,即整数常量池中的10的地址
        Integer n1 = 10;
        Integer n2 = 10;
        System.out.println(n1 == n2);  // true
        System.out.println(n1 != n2);  // false
		
        // 通过new创建了两个对象,s1和s2分别指向了两个对象。
        // ==和!=都比较的是堆中对象的的地址值。
        Integer n3 = new Integer(10);
        Integer n4 = new Integer(10);
        System.out.println(n3 == n4);  // false
        System.out.println(n3 != n4);  // true
    }
}
// 运行结果
true
false
false
true


class EqualMethod {
    public static void main(String[] args) {
        Integer n1 = new Integer(10);
        Integer n2 = new Integer(10);
        System.out.println(n1.equals(n2));  // true
    }
}
// 运行结果
true


class Value {
    int i;
}

class EqualMethod2 {
    public static void main(String[] args) {
        Value v1 = new Value();
        Value v2 = new Value();
        // 因为equals()方法默认是比较引用的,两个引用不一样所以说false。
        System.out.println(v1.equals(v2));  // false
    }
}
// 运行结果
false

练习5

public class Exec05 {
    public static void main(String[] args) {
        Dog d1 = new Dog();
        d1.name = "spot";
        d1.says = "Ruff!";
        Dog d2 = new Dog();
        d2.name = "scruffy";
        d2.says = "Wurf!";
        System.out.println(d1.name + ", " + d1.says);
        System.out.println(d2.name + ", " + d2.says);
    }
}

class Dog {
    String name;
    String says;
}
// 运行结果
spot, Ruff!
scruffy, Wurf!

练习6

public class Exec06 {
    public static void main(String[] args) {
        Cat c1 = new Cat();
        c1.name = "spot";
        c1.says = "Ruff!";
        Cat c2 = new Cat();
        c2.name = "scruffy";
        c2.says = "Wurf!";
        Cat c = c1;
        System.out.println(c == c1);  // true
        System.out.println(c.equals(c1));  // true
    }
}

class Cat {
    String name;
    String says;
}
// 运行结果
true
true

3.8 逻辑运算符

&&, ||, !

逻辑运算符只能用在布尔值上,这与C/C++不一样。

整数类型不能转换为布尔类型,其他类型都可以转换。

练习7

import java.util.Random;

public class Exec07 {

    public static void main(String[] args) {
        Coin coin = new Coin();
        Exec07 t = new Exec07();
        int count = 0;
        System.out.println("=========开始扔硬币============");
        while ( count++ < 10) {
            System.out.print("第" + count +"次: ");
            t.throwCoin(coin);
            coin.show();
        }
    }

    public void throwCoin(Coin coin) {
        Random random = new Random();
        int n = random.nextInt(1000);
        coin.isPositive = n % 2 == 0;
    }
}

class Coin {
    boolean isPositive;

    public void show() {
        if (isPositive) System.out.println("正面");
        else System.out.println("反面");
    }
}
// 运行结果
=========开始扔硬币============1: 反面
第2: 反面
第3: 反面
第4: 正面
第5: 反面
第6: 正面
第7: 反面
第8: 正面
第9: 反面
第10: 反面

3.8.1 短路

import static net.mindview.util.Print.*;

public class Test07 {

    public static void main(String[] args) {
        Test07 t = new Test07();
        // test3(1)没有执行
        boolean ret = t.test1(0) && t.test2(4) && t.test3(1);
        System.out.println(ret);
    }

    public boolean test1(int val) {
        print("test(" + val + ")");
        print("result: " + (val < 1));
        return val < 1;
    }

    public boolean test2(int val) {
        print("test(" + val + ")");
        print("result: " + (val < 2));
        return val < 2;
    }

    public boolean test3(int val) {
        print("test(" + val + ")");
        print("result: " + (val < 2));
        return val < 3;
    }
}
// 运行结果
test(0)
result: true
test(4)
result: false
false

3.9 直接常量

直接常量的后缀字符表示类型,

类型后缀字符前缀字符
longL或者l-
floatF或者f-
doubleD或者d-
十六进制数-0x或者0X
八进制数-0

C/C++和java中没有二进制数的直接常量表示法。其他进制数可以通过IntegerLongtoBinaryString()方法,自动转化为int,显示二进制数。

练习8

public class Exec08 {

    public static void main(String[] args) {
        long x = 0x1L;
        long y = 0xAL;
        long w = 01L;
        long v = 010L;
        System.out.println("x = " + Long.toBinaryString(x));
        System.out.println("y = " + Long.toBinaryString(y));
        System.out.println("x + y = " + Long.toBinaryString(x + y));
        System.out.println("w = " + Long.toBinaryString(w));
        System.out.println("v = " + Long.toBinaryString(v));
        System.out.println("w + v = " + Long.toBinaryString(w + v));
    }
}
// 运行结果
x = 1
y = 1010
x + y = 1011
w = 1
v = 1000
w + v = 1001

3.9.1 指数记数法

练习 9

public class Exec09 {

    public static void main(String[] args) {
        float minFloat = 1.4E-45f;
        float maxFloat = 3.4028235E38f;
        System.out.println("float min=" + minFloat + ", max=" + maxFloat);

        double minDouble = 4.9E-324d;
        double maxDouble = 1.7976931348623157E308d;
        System.out.println("double min=" + minDouble + ", max=" + maxDouble);
    }
}
// 运行结果
float min=1.4E-45, max=3.4028235E38
double min=4.9E-324, max=1.7976931348623157E308

3.10 按位操作符

&, |, ~, ^
&=, |=, ^=

按位操作符,不会“短路”。在移位表达式中,不能使用别运算,原因将在后面解释。

练习10

public class Exec10 {

    public static void main(String[] args) {
        int x = 0x55;
        int y = 0xAA;
        System.out.printf("%8s\n", Integer.toBinaryString(x));
        System.out.printf("%8s\n", Integer.toBinaryString(y));
    }
}
// 运行结果
 1010101
10101010

3.11 移位操作符

<<, >>, <<<, >>>
<<=, >>=, <<<=, >>>=

为了防止移位超出最大位数:对charbyteshort进行移位,进行移位之前,先会转换为int类型,计算结果也是int值,只有数值低5位有用(因为2的5次方是32,int类型数值只有32位)。long类型的值,结果long,只有低6位有用。

如果对byteshort进行无符号左移和无符号右移位运算时,可能会出现结果会不正确。因为在移位之前,会转换为int类型,再右移,然后将结果截断,赋值给原来的类型,这个过程可能会出现结果是-1的情况。

import static net.mindview.util.Print.*;

public class Test08 {

    public static void main(String[] args) {
        int x = -1;
        // 负数使用的是补码
        // -1的原码是10000000 00000000 00000000 00000001
        // -1的反码是11111111 11111111 11111111 11111110
        // -1的补码是11111111 11111111 11111111 11111111
        print(Integer.toBinaryString(x));  // 11111111 11111111 11111111 11111111
        // x无符号右移10位,位
        x >>>= 10;
        // 将x的补码无符号右移10位的00000000 11111111 11111111 11111111
        print(Integer.toBinaryString(x)); // 11111111 11111111 11111111

        long y = -1;
        // -1 的原码是 10000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
        // -1 的反码是 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111110
        // -1 的补码是 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
        print(Long.toBinaryString(y));  // 11111111 11111111 11111111 11111111 11111111 11111111 11111111 11111111
        y >>>= 10;
        print(Long.toBinaryString(y));  // 00000000 00111111 11111111 11111111 11111111 11111111 11111111 11111111

        short s = -1;
        // -1 的原码是 10000000 00000001
        // 先将short类型,转换为int类型 10000000 00000000 00000000 00000001
        // 然后后面的计算和int一样了
        print(Integer.toBinaryString(s));
        s >>>= 10;
        // 11111111 11111111 11111111 11111111
        // 右移10位是  00111111 11111111 11111111
        // 然后截取 11111111 11111111
        print(Integer.toBinaryString(s));

        byte b = -1;
        print(Integer.toBinaryString(b));
        // 这个移位运算的结果,没有赋值给b,而是直接打印出来的int类型,没有截断,所以结果数正确的。
        print(Integer.toBinaryString(b>>>10));
    }
}

练习 11

public class Exec11 {
    public static void main(String[] args) {
        int x = 0x80000000;
        for (int i = 0; i < 32; i++) {
            x >>>= 1;
            System.out.println(Integer.toBinaryString(x));
        }
    }
}
// 运行结果
1000000000000000000000000000000
100000000000000000000000000000
......
100
10
1
0

练习 12

public class Exec12 {

    public static void main(String[] args) {
        int x = 0xffffffff;
        x <<= 5;
        System.out.println(Integer.toBinaryString(x));
        for (int i = 0; i < 32; i++) {
            x >>>= 1;
            System.out.println(Integer.toBinaryString(x));
        }
    }
}
// 运行结果
11111111111111111111111111100000
1111111111111111111111111110000
111111111111111111111111111000
11111111111111111111111111100
1111111111111111111111111110
111111111111111111111111111
.......
11
1
0

练习13

public class Exec13 {

    public static void main(String[] args) {
        char ch = '~';
        // 126=0111 1110
        show(ch);
    }

    public static void show(char ch) {
        System.out.println(Integer.toBinaryString(ch));
    }
}
// 运行结果
1111110

3.12 三元操作符 if-else

boolean-exp ? value0 : value1

如果boolean-exp(布尔表达式)为true,就计算value0,value0就是最终整个三元操作符的值。如果boolean-exp(布尔表达式)为false,就计算value1。value1就是最终整个三元操作符的值。

3.13 字符串操作符 +和 +=

在字符串中的用途:连接不同的字符串、

这是是一种操作符重载(operator overloading)。

public class Test09 {

    public static void main(String[] args) {
        int x = 0, y = 1, z = 2;
        String s = "x, y, z";
        print(s + x + y + z);  // x, y, z012
        print(x + " " + s);  // 0 x, y, z
        s += "(summed) =";
        print(s + (x + y + z));  // x, y, z(summed) =3
        // 基本类型转换为字符串常用的方法
        print("" + x);  // 0
    }
}
// 运行结果
x, y, z012
0 x, y, z
x, y, z(summed) =3
0

3.14 使用操作符时常犯的错误

while(x = y) {
    // ...
}

在java中,x = y不会像C/C++那样,会将int类型转换为boolean类型,导致死循环。

但是,在java中,有和C/C++一样的问题,就是使用按位与(&)、按位或(|)代替逻辑与(&&)、逻辑或(||),但是操作数必须是boolean类型。

  • &:不是短路运算。当操作数全部是整型数值或者可与转换为整型数值时,是位运算符,进行位运算。当操作数全部为布尔类型时,是逻辑运算符。

3.15 类型转换操作符

java中有两种类型转换:

  • 隐式类型转换:将一种数据类型自动转换为另一种类型,不需要显示的声明。如:10 / 3.0 的结果是3.3333333333333335

    public class A {
        public static void main(String[] args) {
            int a = 10;
            System.out.println(a / 3);      // 3
            System.out.println(a / 3.0);    // 3.3333333333333335
        } 	
    }
    
  • 显示类型转换:将想要转换的数据类型放到圆括号里面,可以显式的进行数据类型的转换,或者在不能自动进行准换的时候强制进行类型转换。

    public class A {
        public static void msin(String[] args) {
            int i = 100;
            // 对变量进行类型转换
            long a = (long) i;
            // 对数值进行类型转换
            long b = (long) 200.1;
            String s = "10";
            // int b = (int) s;    // 编译时的错误: 不兼容的类型: String无法转换为int
      }
    }
    

从信息是否丢失的角度,分为:

  • 窄化转换(narrowing conversion):将能容纳信息多的类型转换为容纳信息少的数据类型,这可能会造成信息地丢失。编译器会提示需要显示转换。
  • 扩展转换(widening conversion):不会造成信息丢失,编译器不会提示需要显示转换。

java允许把任何基本数据类型转换为别的基本类数据型,但布尔类型除外。“类”数据类型不允许随意进行类型转换,必须在其所属类型的类族之间进行类型转换。

3.15.1 截尾和舍入

在进行narrowing conversion时,必须注意截尾和舍入问题。不管是float还是是double类型,转换为int时,总是将数字截尾,不是四舍五入。

public class A {
    public static void main(String[] args) {
        float fa = 21.7f;
        float fb = 21.4f;
        double da = 21.7;
        double db = 21.4;
        System.out.println("(int)fa : " + (int)fa);  // 21
        System.out.println("(int)fb : " + (int)fb);  // 21
        System.out.println("(int)da : " + (int)da);  // 21
        System.out.println("(int)db : " + (int)db);  // 21
    }
}

如果想四舍五入,需要使用java.lang.Math类中的round()方法。

public class A {
    public static void main(String[] args) {
        float fa = 21.7f;
        float fb = 21.4f;
        double da = 21.7;
        double db = 21.4;
        System.out.println("Math.round(fa) : " + Math.round(fa));  // 22
        System.out.println("Math.round(fb) : " + Math.round(fb));  // 21
        System.out.println("Math.round(da) : " + Math.round(da));  // 22
        System.out.println("Math.round(db) : " + Math.round(db));  // 21
    }
}

3.15.2 提升

除布尔类型的其他基本数据类型,执行算术运算或按位运算,会将类型小的,自动转换为类型大的。比如:

  • byte类型进行位运算:会先自动转换为int类型,然后再进行位运算,最后的结果是int类型的数值。
  • int类型和double类型相加:会先将int类型转换为double类型,然后再在相加,最后的结果是double类型的数值。

3.16 Java没有sizeof()

C/C++中,sizeof()可以得到系统为某个数据分配的字节数。这最大的原因是为了“移植”。在不同机器上,数据类型的字节数可能不同。

java不需要sizeof()操作符,因为所有数据类型在所有的机器上大小都是相同的。

3.17 操作符小姐

必须注意narrowing conversion的结果,否则会造成信息地丢失。

布尔类型没有运算,智能赋值为true或false,测试它是否为true或false。布尔值不能相加相减。

chart、byte和short在运算过程中,会自动进行数据类型提升为int类型,得到的结果是int值,必须显式类型转换为指定的类型。

对两个较大的int类型值座乘法运算,有很大可能会溢出,但是编译器部署提示任何警告或异常。

// 因为int的最大值是2147483647,100000 * 100000的结果为10000000000大于2147483647,产生溢出。
public class A {
    public static void main(String[] args) {
        int a = 100000;
        int b = 100000;
        
        System.out.println( a * b);  // 1410065408
    }
}

练习14

public class Exec14 {

    public static void main(String[] args) {
        compareTwoStr("asdf", "asdf");
        compareTwoStr("asd", "asdf");
    }

    public static void compareTwoStr(String s1, String s2) {
        System.out.println("" + s1 + " == " + s2 + ": " + (s1 == s2));
        System.out.println("" + s1 + " != " + s2 + ": " + (s1 != s2));
        System.out.println("" + s1 + ".equals(" + s2 + "): " + (s1.equals(s2)));
    }
}
// 运行结果
asdf == asdf: true
asdf != asdf: false
asdf.equals(asdf): true
asd == asdf: false
asd != asdf: true
asd.equals(asdf): false
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值