《Java编程思想》第三章 操作符

一、操作符
Java中几乎所有的操作符只能操作基本类型。例外的操作符有“=”,“==”和“!=”,这些操作符能够操作所有的对象。除此以外,String类支持“+”和“+=”。
当一个表达式中存在多个操作符时,操作符的优先级就决定了各部分的计算顺序。最简单的规则是先乘除后加减,最好用括号明确规定计算顺序。
1.1、赋值操作符

=

1.2、算术操作符
+,-,*,/,%
1.3、自动递增和递减
++,--
以上各有“前缀式”和“后缀式”两种使用方式
1.4、关系操作符
<,>,<=,>=,==,!=
1.5、逻辑操作符
&&,||,!
当使用逻辑操作符时,存在短路现象。通过短路机制,可以获得性能提升。
1.6、按位操作符
& | ^ ~
布尔类型被作为单比特值对待,因而可对布尔类型使用“&,|,^”按位操作符,而不能使用“~”按位操作符(避免与逻辑操作符中的“!”混淆)。
对布尔类型使用按位操作符号,不会产生短路现象。
比如以下例子中:
boolean flag1=true && false && true;
boolean flag2=true & false & true;
计算flag1会短路,计算flag2不会短路
1.7、移位操作符
<<,>>,>>>
<<:左移操作符,低位补0
>>:有符号右移操作符,高位补符号位
>>>:无符号右移操作符,高位补0
1.8、包含“赋值操作符”的复合操作符
赋值操作符可以跟有些操作符结合,得到复合操作符,比如“+=,-=,*=,/=,&=,...,<<=,>>=”
1.9、三元操作符
boolean-exp?value0:value1

二、基本类型
根据“ 《Java编程思想》第二章  一切都是对象”可知,基本类型有:boolean,char,byte,short,int,long,float,double。又可细分为布尔型(boolean),整型(char,byte,short,int,long)和浮点型(float,double)这3类。

三、基本类型之间的类型转换
基本类型之间的类型转换根据是否需要明确指定类型转换过程而分为显式类型转换和隐式类型转换,根据转换前后数值表达范围的变化可分为窄化转换和扩展转换,窄化转换可能造成数据丢失,扩展转换不会造成数据丢失。窄化转换必须是显式转换,扩展转换允许是隐式转换。
Java允许把任何基本类型转换成别的基本类型,但布尔型除外,布尔型根本不允许进行任何的类型转换。因此,最终允许的转换情况是:整型集合内部的类型转换,浮点型集合内部的类型转换以及整型和浮点型之间的类型转换。
3.1、类型一
整型——》浮点型:扩展转换
3.2、类型二
浮点型——》整型:窄化转换,处理方式是截尾,有可能丢失信息。
//未丢失信息:
float f=1f;
int a=(int)f;

//丢失信息:
float f=1.1f;
int a=(int)f;
3.3、类型三
整型——》整型:如果是扩展转换,不丢失信息;如果是窄化转换,处理方式是截取低端位相应位数的二进制位,有可能丢失信息。
//丢失信息:
int a=0x1ff;
byte b=(byte)a;
//那么截取低端位8位二进制位,得到b=0xff。

//未丢失信息:
int a=0x1f;
byte b=(byte)a;
//那么截取低端位8位二进制位,得到b=0x1f。
浮点型——》浮点型:如果是扩展转换,不丢失信息;如果是窄化转换,处理方式按照IEEE754标准规范[1],有可能丢失信息。

四、触发隐式扩展类型转换的几种情形
4.1、操作符触发
当“使用的操作符为‘算术操作符,按位操作符,移位操作符’,操作数为‘整型基本类型’”时,要求操作数的类型至少是“int型,即可以是int型或者long型,如果是char型,byte型,short型自动会转换成int型”。
以下以byte型为例,char型和short型同理。
byte ba = 1;
byte bb = 2;


//算术操作符的例子,编译错误。因为:右边表达式中ba和bb会被隐式扩展成int型,而左边变量为byte型
byte bc = ba + bb;
byte bd = ba - bb;
byte be = ba * bb;
byte bf = ba / bb;
byte bg = ba % bb;


//按位操作符的例子,编译错误。因为:右边表达式中ba和bb会被隐式扩展成int型,而左边变量为byte型
byte bh = ba & bb;


//移位操作符的例子,编译错误。因为:右边表达式中ba和bb会被隐式扩展成int型,而左边变量为byte型
byte bi = ba >> bb;
4.2、表达式中最大的数据类型决定了最终结果的数据类型
比如操作符两边都为整型,其中一边是整型中的A类型,另一边是整型中的B类型,A类型大于B类型,那么B类型变量会被隐式扩展成A类型。
比如如下例子:
//byte型的a被隐式扩展成int型
byte a=10;
int b=a;

//int型的b被隐式扩展成long型
long c=10;
long d=b+c;
又比如操作符两边都为浮点型,其中一边是float,另一边是double,那么float类型变量会被隐式扩展成double类型。
又比如操作符两边中一边是整型,一边是浮点型,那么整型类型变量会被隐式扩展成浮点类型。
当然,以上触发中,“4.1、操作符触发”也是会进行的。

五、数值直接常量
在Java中有3种数值直接常量,分别是:十六进制数值常量,八进制数值常量和十进制数值常量。
十六进制数值常量:以前缀0x(0X),后面跟随0-9或小写(或大写)的a-f来表示,比如0xffff。只可表示整型。
八进制数值常量:由前缀0以及后续的0-7来表示,比如0177。只可表示整型。
十进制数值常量:由0-9组成,如果是整型首位非0(整型情况下,首位为0表示八进制)。比如199,1000.321213。既可表示整型,也可表示浮点型。

数值直接常量隐含对应一个基本类型的变量。关于数值直接常量隐含对应一个基本类型的变量有以下3条规则:

1、在整型情形下(十六进制数值常量,八进制数值常量,十进制数值常量),数值直接常量对应的基本类型是int,通过在数值常量后加"l(或者L)"后缀,可以使得对应的基本类型是long。

在浮点型情形下(十进制数值常量),数值直接常量对应的基本类型是double,通过在数值常量后加"f(或者F)"后缀,可以使得对应的基本类型是float。通过在数值常量后加"d(或者D)"后缀,可以使得对应的基本类型是double。

比如有以下代码:

public class Main {
    void f1(char x) {
        System.out.println("f1(char)");
    }

    void f1(byte x) {
        System.out.println("f1(byte)");
    }

    void f1(short x) {
        System.out.println("f1(short)");
    }

    void f1(int x) {
        System.out.println("f1(int)");
    }

    void f1(long x) {
        System.out.println("f1(long)");
    }

    void f1(float x) {
        System.out.println("f1(float)");
    }

    void f1(double x) {
        System.out.println("f1(double)");
    }

    public static void main(String[] args) {
        Main main = new Main();
        main.f1(5);
        //main.f1(0x1fffffffff);
    }
}
运行后得到的结果如下:

f1(int)
从中可知,数值直接常量5对应的基本类型是int。

将注释语句的注释去掉,可以发现,编译器报如下错误:

Integer number too large

这是因为数值直接常量“0x1fffffffff”对应的基本类型是int,而它超出了int的表示范围,因而报出如上错误,如果在“0x1fffffffff”后加上L,那么显式指定对应的基本类型是long,编译就能通过。


又比如有以下代码:

public class Main {
    void f1(char x) {
        System.out.println("f1(char)");
    }

    void f1(byte x) {
        System.out.println("f1(byte)");
    }

    void f1(short x) {
        System.out.println("f1(short)");
    }

    void f1(int x) {
        System.out.println("f1(int)");
    }

    void f1(long x) {
        System.out.println("f1(long)");
    }

    void f1(float x) {
        System.out.println("f1(float)");
    }

    void f1(double x) {
        System.out.println("f1(double)");
    }

    public static void main(String[] args) {
        Main main = new Main();
        main.f1(5.0);
        main.f1(3.5E39);
        //main.f1(3.5E39F);
    }
}

运行后得到结果如下:

f1(double)
f1(double)
从中可知,数值直接常量5.0和3.5E39对应的基本类型是double。

将注释语句的注释去掉,可以发现,编译器报如下错误:

Floating point number too large
这是因为显式指定数值直接常量“3.5E39F“对应的基本类型是float,而它超出了float的表示范围,因而报出如上错误。

2、在Java程序中碰到数值直接常量,以其隐含对应的基本类型变量的身份去考虑,可以帮助我们理解,减少错误的发生。

3、关于“在Java程序中碰到数值直接常量,以其隐含对应的基本类型变量的身份去考虑”有一个例外:当数值直接常量为整型,且隐含对应的基本类型是int型,去给byte类型,short类型,char类型变量赋值,且数值直接常量的数值在相应变量的表示范围内,虽说此时仍旧可以以数值直接常量隐含对应的int类型变量身份去考虑问题,但是却不像真正的int类型变量那样需要显式窄化转换,可以认为这个特殊情况由编译器负责处理。
除此情况外,当碰到数值直接常量时,就完全可以以其隐含对应的基本类型变量的身份去考虑问题,该显式转换就显式转换,无需显式转换就不需显式转换。

可见如下代码所示例子:

public class Main {
    public static void main(String[] args) {
        byte b;
        short s;
        char c;
        int i;
        long l;
        float f;
        double d;

        int itmp;
        long ltmp;
        float ftmp;
        double dtmp;

        // 分隔符------------------------
        // 无需显式转换,验证的确无需显式转换
        f = 10;
        f = 10L;
        d = 10;
        d = 10L;
        f = 10.0F;
        d = 10.0;
        d = 10.0D;
        d = 10.0F;

        // 验证
        itmp = 10;
        ltmp = 10L;
        ftmp = 10.0F;
        dtmp = 10.0D;

        f = itmp;
        f = ltmp;
        d = itmp;
        d = ltmp;
        f = ftmp;
        d = dtmp;
        d = ftmp;

        // 分隔符------------------------
        // 需要显式转换,验证的确需要显式转换
        f = (float) 10.0;

        // 验证
        dtmp = 10.0;
        f = (float) dtmp;

        // 分隔符------------------------
        // 无需显式转换,验证的确无需显式转换
        i = 10;
        l = 10L;

        // 验证
        itmp = 10;
        ltmp = 10L;

        i = itmp;
        l = ltmp;

        // 分隔符------------------------
        // 需要显式转换,验证的确需要显式转换
        b = (byte) 10.0;
        b = (byte) 10.0D;

        s = (short) 10.0;
        s = (short) 10.0D;

        c = (char) 10.0;
        c = (char) 10.0D;

        i = (int) 10.0;
        i = (int) 10.0D;

        l = (long) 10.0;
        l = (long) 10.0D;

        // 验证
        ftmp = 10.0F;
        dtmp = 10.0D;

        b = (byte) ftmp;
        b = (byte) dtmp;

        s = (short) ftmp;
        s = (short) dtmp;

        c = (char) ftmp;
        c = (char) dtmp;

        i = (int) ftmp;
        i = (int) dtmp;

        l = (long) ftmp;
        l = (long) dtmp;

        // 分隔符------------------------
        // 需要显式转换,验证的确需要显式转换
        b = (byte) 2100000000;
        b = (byte) 2100000000L;

        s = (short) 2100000000;
        s = (short) 2100000000L;

        c = (char) 2100000000;
        c = (char) 2100000000L;

        // 验证
        itmp = 2100000000;
        ltmp = 2100000000L;

        b = (byte) itmp;
        b = (byte) ltmp;

        s = (short) itmp;
        s = (short) ltmp;

        c = (char) itmp;
        c = (char) ltmp;

        // 分隔符------------------------
        // 需要显式转换,验证“的确需要显式转换”
        b = (byte) 10L;
        s = (short) 10L;
        c = (char) 10L;

        ltmp = 10L;

        b = (byte) ltmp;
        s = (short) ltmp;
        c = (char) ltmp;

        // 分隔符------------------------
        // 本应显式转换,却由编译器进行处理,验证“的确是由编译器处理本应的显式转换”
        b = 10;
        s = 10;
        c = 10;

        // 验证
        itmp = 10;

        b = (byte) itmp;
        s = (short) itmp;
        c = (char) itmp;
    }
}


参考文献:

[1]http://stackoverflow.com/questions/10075058/converting-from-double-to-float-in-java

[2]http://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html

你可能感兴趣的:(《Java编程思想》第三章 操作符)