目录
在最底层,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 直接常量
直接常量的后缀字符表示类型,
类型 | 后缀字符 | 前缀字符 |
---|---|---|
long | L或者l | - |
float | F或者f | - |
double | D或者d | - |
十六进制数 | - | 0x或者0X |
八进制数 | - | 0 |
C/C++和java中没有二进制数的直接常量表示法。其他进制数可以通过Integer
和Long
的toBinaryString()
方法,自动转化为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 移位操作符
<<, >>, <<<, >>>
<<=, >>=, <<<=, >>>=
为了防止移位超出最大位数:对char
、byte
、short
进行移位,进行移位之前,先会转换为int
类型,计算结果也是int
值,只有数值低5位有用(因为2的5次方是32,int
类型数值只有32位)。long
类型的值,结果long
,只有低6位有用。
如果对byte
和short
进行无符号左移和无符号右移位运算时,可能会出现结果会不正确。因为在移位之前,会转换为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.3333333333333335public 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