Java基础—— *** 作符的细节及其使用技巧

Java基础—— *** 作符的细节及其使用技巧,第1张

Java基础—— *** 作符的细节及其使用技巧

本文参考自Java编程思想第四版,并结合自己现有知识做的一些总结。

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

Java中大多数 *** 作符的使用都与C一致,但同时Java也做了一些改进和优化。在本文中,我将结合书中所读给大家分享一下我认为的Java中使用 *** 作符的注意点和技巧。

1. 优先级

当一个表达式中存在多个 *** 作符时, *** 作符的优先级就决定了各部分的计算顺序。Java中完整的运算符优先级规则很多,我们只需要记住最简单常用的——先乘除后加减,先算数运算后按位运算,逻辑与(&&)>逻辑或(||),运算符优先级相同时先左后右。当遇到不确定谁该先计算的时候,不要节省括号的使用,把你想要先计算的表达式用括号括起来,记住()是第一优先级。

2. 赋值符号

赋值符号“=“,将右边的值赋给左边的值。

如下:

int a = 4;
int b = a;
float c = 3.0f;
float d = c;

这是最常见也是正确的写法,他不会带来任何相关的影响(如在后面的语句中改变a的值,不会影响b,改变c的值不会影响d),因为基本数据类型的值也存在于堆栈中,在赋值的时候,只是将右边的值内容复制一份,然后存储到左边的值中,二者在之后的语句中不会有相关影响。

但是在为对象“赋值”的时候,结果又与基本数据类型大不相同。

public class A{
   int id;
    public static void main(String[] args){
        A a1 = new A();
        A a2 = new A();
        a1.id = 1;
        a2.id = 2;
        System.out.println(a1.id + " " + a2.id);
        a1 = a2;
        System.out.println(a1.id);
        a1.id = 1;
        System.out.println(a2.id);
    }
}

输出:

1 2
2
1

在Java中,我们 *** 作一个对象时,实际上 *** 作对这个对象的引用。这个时候若将一个对象的引用赋值给另一个对象的引用,如上面代码中的a1 = a2;,两个不同的对象引用都指向了同一个实际的对象。这时,无论是利用哪个对象引用 *** 作了实际对象,在另外一个对象引用 *** 作实际对象时,该实际对象已经发生了改变。

因此在实际编码的时候,我们需要尽量减少这种显示的对象引用间的赋值。因为很可能在某一个地方你不小心改变了实际对象的值,当你用其它指向该实际对象的对象引用访问它时,你会觉得很奇怪(你没有意识到还有其它的对象引用指向到了它,并且 *** 作会同步到这个实际对象上)。

3. 关系 *** 作符

关系 *** 作符包括:> < >= <= == !=,生成的值一个boolean数据。如果操作数之间的关系是真的,则返回true,否则返回false。

所有的关系 *** 作符都可以作用于基本数据类型(boolean只能被==和!=作用),这些与我们在C语言中所学到的基本一致。需要注意的是,==和!=可以作用于两个对象,并且会产生让你觉得不正确的答案。

public class test2 {
    public static void main(String[] args) {
        Integer i1 = new Integer(1);
        Integer i2 = new Integer(1);
        System.out.println(i1 == i2);
    }
}

大家可以猜一下上面这段代码会输出什么,按照我们前面所学的知识以及常识来说,应该会输出true(因为它们实际的值相等),但事实上它输出了false。这是为什么呢?

关系 *** 作符和!=作用两个对象时,实际上是比较这两个对象的引用是否相同。那如何知道两个对象的引用的是不相同的呢。还记得我们在上一篇文章中所说的,对象的引用是存放在堆栈中的,声明了两个不同的对象引用,并且不是某一个对象引用指向了另一个对象引用,它们自然是不相同的。如果声明Integer i2 = i1;这时i1i2就会返回true。

如果想要比较两个对象的真实内容是否相等时,我们可以重写并调用equals方法(来自Object类)。Integer中已经重写了equals方法。

    public boolean equals(Object obj) {
        if (obj instanceof Integer) {
            return value == ((Integer)obj).intValue();
        }
        return false;
    }

可以看出Integer类重写的equals方法是比较两个Integer对象的真实值是否相等。

public class test2 {
    public static void main(String[] args) {
        Integer i1 = new Integer(1);
        Integer i2 = new Integer(1);
        System.out.println(i1.equals(i2));
    }
}

输出:

true

**注意:**我们自己定义的类的equals方法是继承自Object类的,它默认还是比较对象的引用,如果想要通过其它方式比较两个对象是否相等,我们需要重写equals方法。

4. 逻辑 *** 作符

逻辑 *** 作符:&&(与),||(或),!(非)

注意:逻辑 *** 作符只能作用于boolean值!!!

在C语言中

while(2){
    
}

是可以编译通过的,同时c语言中0可以表示false,1可以表示true,同时!0=1,这在Java中是不允许的,编译器会在编译期间直接报错。

下面是Java中逻辑 *** 作符允许的一些 *** 作:

if(true || false)
if(true && false)
if((2 > 1) || (1 > 2))   
if((2 > 1) || (1 > 2)) 
if(!true)
if(!false)
.......

"短路"现象:

表达式1 || 表达式2 || 表达式3 || 表达式4 …

在表达式x为true时,表达式x+1到最后一个表达式就不会去计算了,因为最后结果一定为true

表达式1 && 表达式2 && 表达式3 && 表达式4 …

在表达式x为false时,表达式x+1到最后一个表达式就不会去计算了,因为最后结果一定为false

public class shorttest {
    static boolean test1(int val){
        System.out.println("test1");
        return val > 1;
    }
    static boolean test2(int val){
        System.out.println("test2");
        return val > 2;
    }
    static boolean test3(int val){
        System.out.println("test3");
        return val > 3;
    }
    public static void main(String[] args) {
        boolean b = test1(2) && test2(1) && test3(4);
    }
}

如果没有短路现象的话,输出的值应该为

test1
test2
test3

但由于短路现象的存在,并且test2返回false,全是&&逻辑运算符,所以最后不会执行方法test3。

5. 直接常量

程序中有的直接常量是模棱两可的,这时我们需要对编译器加以适当的“指导”,用于给直接量添加一些额外信息。

long:2324l,2324L

float:2.0f,2.0F

double:2.0d,2.0D

八进制:0开头,后面跟0-7的数

十六进制:0x223abc 0x223ABC

// short占两个字节
short a = 0x1;

上述代码是可以通过编译的,你试想一下0x1这个常量会被识别为什么数据类型是short,还是识别为int然后截取为short(丢掉高24位)?这里我们无法得到答案。

char a = 0xffff;
short b = 0xffff;

这个时候,a是可以通过编译的,但b不行,这是为什么呢?二者明明都是2个字节。这就牵扯到了0xffff是被识别为char或short,还是先识别为int,再进行截取。

    如果是识别为char或short,那么它刚好两个字节,在计算机中,常量用补码存在,0xffff对应的值为-1,那么按道理二者都是通过编译的。但又不对,Java中char支持的编码是Unicode编码,为此char是个无符号数,是不存在负号的。那么结果应该是a不能通过编译,b可以,这与实际结果相反,所有0xffff没有被识别为两个字节。事实是会被识别为int,这时0xffff先被识别为0x0000ffff(对应的值为255),然后自动转化为两个字节(值还是255,由于char是无符号数,255刚好是它的最大值,而short就不行(255大于short的最大值),编译器要求你0xffff,进行强制转化(short b = (short) 0xffff;)。这刚好符合实际结果。

Java中浮点数的默认值是double

float = 1.0;是无法通过编译的,因为1.0的数据类型默认是double,double->float需要显示转化,或者我们可以写成float = 1.0f;

当算数 *** 作符作用于byte,short,char时,会自动将它们的数据类型升到int,这个是否如果最终的值还希望并且确实是byte,short,char时,需要进行显示的数据类型转化,如下:

byte c = 1;
byte d = 1;
byte e = (byte) (c + d);
byte e = (byte) (c - d);
byte e = (byte) (c * d);
byte e = (byte) (c / d);
byte e = (byte) (c << d);
byte e = (byte) (c >> d);
byte e = (byte) (c & 1);
byte e = (byte) (c | 1);
byte e = (byte) (~c);
6. 位 *** 作符

按位与:

同为1时为1,否则为0
00000011    3
	&
00000010    2
	=
00000010	2

按位或:

同为0时为0,否则为1
00000011    3
	&
00000010    2
	=
00000011	3

按位非:

0变1,1变0
~1
~00000001
 11111110(负数,补码)
 为得到源码,减1取反
 -1=
 11111101
 取反=
 10000010
 结果=-2

左移:

低位补0
1 << 2;
00000001 << 2;
00000100
等于4

带符号右移:

高位补符号位的值
4 >> 1;
00000100 >> 1;
00000010
等于2
-4 >> 1;
11111100 >> 1;
11111110
等于
减一取反 ->11111101->10000010
-2

无符号右移:

无论符号位为什么,高位均补0
使用byte类型的数据进行无符号右移时,会先转化为int类型,然后移位,
如果最后要再赋值给byte类型的数据需要截断。(移动时符号位也会移动)。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5715484.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存