Java中基本类型和包装类

参考:(http://hinylover.space/2016/06/16/relearn-java-base-type-and-wrapper/ ,https://alexyyek.github.io/2014/12/29/wrapperClass/ )

基本类型运算

boolean类型数据可以进行逻辑运算(&&,||,!),其他的基本类型都可以进行数值计算(+,-,*,/).逻辑运算比较简单易懂,完全与逻辑数学的规则一致,而数值运算涉及到运算后的结果的类型问题,稍微复杂一点.一般来说,运算最终结果的类型与表达式中的最大的(占用空间最大)的类型相同.

        long l = 1 + 2L; // 与1L的类型一致
        int i = 1 + 2L; // 编译不通过
        float f = 1 + 2 + 1.2f; // 与1.2f的类型一致
        double d = 1 + 2 + 1.2; // 与1.2的类型一致

如果两种相同类型的数据进行运算,按理说,运算结果还是那个类型.但事实上,byte,char,short等类型是不满足这个结论的.

// 编译不通过,编辑器报:Type mismatch: cannot convert from int to byte 。
        byte b1 = 1;
        byte b2 = 1;
        byte b = b1 + b2;

// 编译不通过,编辑器报:Type mismatch: cannot convert from int to char 。
        char c1 = 1;
        char c2 = 1;
        char c = c1 + c2;

// 编译不通过,编辑器报:Type mismatch: cannot convert from int to short 。
        short s1 = 1;
        short s2 = 1;
        short s = s1 + s2;

从字面上来看,1+1 = 2 绝对没有超过这个类型的范围.下面的例子都可以编译通过

        byte s1 = 1 + 1;
        char s2 = 1 + 1;
        short s3 = 1 + 1;

这是因为Java中数值运算最低要求是int类型,如果参与运算的变量类型都没有超过int类型,则他们都会被自动升级为int类型再进行运算,所以他们运算后的结果类型也是int类型.这种方式所得到的结果是否超过了对应类型所表示的范围只能在运行时确定.

类型转换

java中除了boolean类型之外,其他7种类型相互之间可以进行转换.

七种类型按照转换排序:

byte<(short=char)<int<long<float<double

类型转换的总结 小可以直接转大,大转小会失去精度

包装类

Java是一门面向对象的语言,但是java中的基本数据却不是面向对象的,这在实际使用中存在很多不便,为了解决这个不足,在设计类时为每个基本数据类型都设计了一个对应的类进行代表.这样的类称为包装类.值得注意的是,所有的包装类都是final修饰的,也就是无法被继承和重写.

基本数据类型 大小 包装器类型
boolean / Boolean
char 16bit Character
byte 8bit Byte
short 16bit Short
int 32bit Integer
long 64bit Long
float 32bit Float
double 64bit Double
void / Void

装箱和拆箱

Integer a = 1; //装箱
int b = new Integer(1);  //拆箱

Java作为一种强类型的语言,对象直接赋值给引用类型变量,而基础数据只能赋值给基本类型变量,这个是毫无异议的。那么基本类型和包装类型为什么可以直接相互赋值呢?这其实是Java中的一种“语法糖” 自动拆箱和装箱

Integer a = 1; 首先生成一个常量1,然后调用Integer.valueOf(int) 方法返回Integer对象,最后将对象的地址赋值给变量a.Integer a = 1;其实相当于Integer a = Integer.valueOf(1);

int b = new Integer(1); 首先生成了一个int对象,值为1,然后调用Integer.intValue() 获取到对象的值,赋值给b

常见问题

Integer a = null;
...
int b = a; // 抛出NullPointException

上面的代码可以编译通过,但是会抛出空指针异常,前面已经说过了 int b = a;就是一个拆箱的过程,实际上是int b = a.intValue(),由于a的引用值为null,对空对象调用方法就会抛出NullPointException

面试问题

下面代码的输出

public class Main {
    public static void main(String[] args) {
        Integer i1 = 100;
        Integer i2 = 100;
        Integer i3 = 200;
        Integer i4 = 200;
        
        System.out.println(i1==i2);
        System.out.println(i3==i4);
    }
}

这里先说明"==" 和"equal"的区别

基本类型 == equals
字符串变量 对象内存中的首地址 字符串内容
非字符串变量 对象内存中的首地址 对象内存中的首地址
基本类型 不可用
包装类 地址 内容

结果

true

false
为什么会出现这样的结果呢?输出结果表名 i1和i2指向同一个地址,而i3和i4指向的是不同的地址.此时就需要看一个装箱valueOf的源代码

public static Integer valueOf(int i) {
        if(i >= -128 && i <= IntegerCache.high)
            return IntegerCache.cache[i + 128];
        else
            return new Integer(i);
    }

从上面代码看出,通过valueOf方法创建Integer对象的时候,如果数值在[-128,127]之间,便返回指向IntegerCache.cache中已经存在的对象的引用;否则创建新的Integer对象.

上面的代码中 i1 和 i2 的数值为100,因此会直接从cache中取已经存在的对象,所以 i1 和 i2 指向的是同一个对象,而 i3 和 i4 则是分别指向不同的对象。

那么下面的代码呢?

public static void main(String[] args) {
        Double i1 = 100.0;
        Double i2 = 100.0;
        Double i3 = 200.0;
        Double i4 = 200.0;
        
        System.out.println(i1==i2);
        System.out.println(i3==i4);
    }

实际输出

false

false

这里看一下Double装箱的valueOf方法就是到了

    public static Double valueOf(double d) {
        return new Double(d);
    }

Double的装箱都是创建了新的对象 .在这里只解释一下为什么Double类的valueOf方法会采用与Integer类的valueOf方法不同的实现。很简单:在某个范围内的整型数值的个数是有限的,而浮点数却不是。

下面的输出结果呢?

 public static void main(String[] args) {
        Boolean i1 = false;
        Boolean i2 = false;
        Boolean i3 = true;
        Boolean i4 = true;
         
        System.out.println(i1==i2);
        System.out.println(i3==i4);
    }

输出为:

true

true

至于为什么是这个结果,同样地,看了Boolean类的源码也会一目了然。下面是Boolean的valueOf方法的具体实现:

public static Boolean valueOf(boolean b) {
        return (b ? TRUE : FALSE);
    }

下面代码的输出

public static void main(String[] args) {
        Integer a = 1;
        Integer b = 2;
        Integer c = 3;
        Integer d = 3;
        Integer e = 321;
        Integer f = 321;
        Long g = 3L;
        Long h = 2L;
         
        System.out.println(c==d);
        System.out.println(e==f);
        System.out.println(c==(a+b));
        System.out.println(c.equals(a+b));
        System.out.println(g==(a+b));
        System.out.println(g.equals(a+b));   //equals方法并不会进行类型转换 故类型不同
        System.out.println(g.equals(a+h));
    }

这里面需要注意的是:当 “==” 运算符的两个操作数都是 包装器类型的引用,则是比较指向的是否是同一个对象,而如果其中有一个操作数是表达式(即包含算术运算)则比较的是数值(即会触发自动拆箱的过程)。另外,对于包装器类型,equals方法并不会进行类型转换。明白了这2点之后,上面的输出结果便一目了然:

true
false
true
true
true
false
true
posted @ 2020-09-22 21:10  刘指导  阅读(252)  评论(0编辑  收藏  举报