《Java基础知识点梳理》基础数据类型与包装类

知识点脑图

Java为什么要设计基础数据类型

Java语言是面向对象的,new一个对象是存在堆里的

像int这类使用非常频繁的类型如果每次使用都还要去new一个对象到堆里

这样会十分的笨重

所以有基础类型,它们使用是直接在栈空间存储,更加高效

有哪些基础数据类型

基础数据类型所占位数
boolen1
byte8
short16
int32
float32
double64
long64
char16
void(注意void也是java的基础数据类型)-

有了基础数据类型为什么还要有包装类

看到了基础数据类型可以提高效率,那为什么又有包装类的存在呢?

有几点:

  1. java语言是面向对象的,基础数据类型虽有益处但不符合面向对象的设计思想
  2. 基本数据类型用法单一,包装类灵活,且功能丰富
  3. java中集合和泛型等不支持基本数据类型的直接使用

java的包装类

java中所有的基础数据类型都对应一个包装类

基本数据类型包装类
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
charCharacter
booleanBoolean
voidVoid

值得注意的是,所有的包装类都是由final关键字进行修饰的,意味着这些包装类将不能够被继承

自动拆装箱

java语言允许包装类和基础数据类型之间进行转换,这个过程被称为自动拆箱与装箱

Integer a = 1;
int a = new Integer(1);

自动装箱就是Java自动将原始类型值转换成对应的对象,比如将int的变量转换成Integer对象,这个过程叫做装箱

反之将Integer对象转换成int类型值,这个过程叫做拆箱。

因为这里的装箱和拆箱是自动进行的非人为转换,所以就称作为自动装箱和拆箱。

实质

其实所谓的自动拆装箱是java的语法糖,这是一个刻意的设计,为了让程序开发与阅读是更加便利

public class Main {

    public static void main(String[] args) {
        Integer a = 1;
    }

}

这样一段代码通过工具查看编译后的字节码实质是这样的

image-20210809170122544

本质就是调用了Integer的valueOf方法进行实现的

再看

public class Main {

    public static void main(String[] args) {
        int a = new Integer(1);
    }

}

它的实质则是:

image-20210809170051391

本质就是调用了intValue

其他的类型同理

发生拆装箱的时机

最常见的是赋值操作时,如下面这段代码,就分别做了装箱与拆箱

public class Main {

    public static void main(String[] args) {
        Integer a = 1;
        int b = a;
    }

}

还有就是方法调用时,如下面这个代码

public class Main {

    public static void main(String[] args) {
        int a = 1;
        test(a);
    }
    
    private static void test(Integer a) {
        System.out.println("this is a test");
    }

}

看下它的字节码

image-20210809171714336

很明显这里进行了一次自动装箱

需要注意的地方

浪费资源

自动拆装箱方便了我们的开发,但如果不注意会很多对象创建上的问题

在诸如此类代码中:

public class Main {

    public static void main(String[] args) {
        Integer a = 1;
        for (int i = 0; i < 1000; i++) {
            a+=i;
        }
    }

}

这里就存在比较严重的无用的对象创建的问题,因为每一次的a+=i都要进行一次拆装箱,这样就会创建一个没有用的Integer对象在堆中

重载方法

通过上面的介绍,可以知道,java在方法参数传递上会自动的进行自动拆装箱的操作

但是这里有个特别的例子,就是当出现方法重载时

举个例子:

private void test(int i) {
    System.out.println("这是int");
}

private void test(Integer i) {
    System.out.println("这是integer");
}

这种情况调用的时候就不会出现自动拆箱或者装箱的情况

比较过程的拆装

有了自动拆装箱后,我们在使用比较时就需要小心

看下面的例子

public class Main {

    public static void main(String[] args) {
        Integer a = 1;
        int b = 1;
        System.out.println(a==b);
    }

}

此过程输出结果为true

查看字节码分析一下流程

image-20210810103937116

可以看到的是Integer a = 1先进行了一次装箱,然后在==进行运算时进行了一次拆箱,所以本质还是两个int的比较

常量池

这个算是的一个常见的面试题吧

先看代码

public class Main {

    public static void main(String[] args) {
        Integer a = 1;
        Integer b = 1;
        System.out.println(a==b); //  输出 true
        Integer c = 128;
        Integer d = 128;
        System.out.println(c==d); // 输出 false
    }

}

出现这种情况的原因就是Integer这个类型在自动装箱时做的一个缓存的处理

通过上面的介绍可以知道Integer a = 1本质调用的是Integer.valueOf

看下它的源码,很明显可以看到Integer的valueOf对-128至127的数据做了缓存处理,这一点在官方的注释中也可以看到

当超出这个范围,将会调用new Integer()产生新的示例

这是Java语言为了更好地性能做出的处理

image-20210810105055235

这里要提的一点就是,大家可能都记得Integer有缓存池(面试题养成的应急反应吗?哈哈哈)

但是常会忽略其他的包装类型也有常量池这个点!

各种各样的常量池

除了Integer,Java的其他数据类型也有常量池(但不全有)

有常量池的类型有:Byte、Short、Integer、Long、Character、Boolean

这里有意思的是Byte、Short、Integer、Long缓存的值都是[-128,127]

而Character是在[0,127],Boolean则只有true和false

注意:Float和Double这两个浮点类型包装类就是没有常量池

随意查看两个有常量池包装类的源码看看:

Long.valueOf

image-20210830120326814

Character.valueOf

image-20210830120418250

Boolean的实现就是定义两个常量,比较简单,反正就true和false

image-20210830120459916

需要留意

以上各种包装类在进行==比较的时候都要记得结合自动拆箱和装箱以及常量池的概念进行判断

否则可能会在你的程序里面留下意想不到的问题

参考

  • https://blog.csdn.net/xialei199023/article/details/63251295

  • https://droidyue.com/blog/2015/04/07/autoboxing-and-autounboxing-in-java/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值