当前位置: 首页 > article >正文

Java基础概览和常用知识(六)

基本数据类型

Java中的几种基本数据类型:

Java 中有 8 种基本数据类型,分别为:

  • 6 种数字类型:
    4 种整数型:byteshortintlong
    2 种浮点型:floatdouble
  • 1 种字符类型:char
  • 1 种布尔型:boolean

这 8 种基本数据类型的默认值以及所占空间的大小如下:

基本类型位数字节默认值取值范围
byte810-128 ~ 127
short1620-32768(-2^15) ~ 32767(2^15 - 1)
int3240-2147483648 ~ 2147483647
long6480L-263-1
char162'\u0000'0 ~ 65535(2^16 - 1)
float3240.0f1.4E-45 ~ 3.4028235E38
double6480.0d4.9E-324 ~ 1.7976931348623157E308
boolean1falsetrue、false

        可以看到,像 byteshortintlong能表示的最大正数都减 1 了。这是为什么呢?这是因为在二进制补码表示法中,最高位是用来表示符号的(0 表示正数,1 表示负数),其余位表示数值部分。所以,如果我们要表示最大的正数,我们需要把除了最高位之外的所有位都设为 1。如果我们再加 1,就会导致溢出,变成一个负数。
        对于 boolean,官方文档未明确定义,它依赖于 JVM 厂商的具体实现。逻辑上理解是占用 1 位,但是实际中会考虑计算机高效存储因素。
        另外,Java 的每种基本类型所占存储空间的大小不会像其他大多数语言那样随机器硬件架构的变化而变化。这种所占存储空间大小的不变性是 Java 程序比用其他大多数语言编写的程序更具可移植性的原因之一(《Java 编程思想》2.2 节有提到)。

注意:

  1. Java 里使用 long 类型的数据一定要在数值后面加上 L,否则将作为整型解析。
  2. Java 里使用 float 类型的数据一定要在数值后面加上 f 或 F,否则将无法通过编译。
  3. char a = 'h'char :单引号,String a = "hello" :双引号。

这八种基本类型都有对应的包装类分别为:ByteShortIntegerLongFloatDoubleCharacterBoolean

基本类型和包装类型的区别?

1. 本质特性

  • 基本类型:不是对象,不具备对象的特性,例如不能调用方法。它们直接映射到硬件,因此更高效。
  • 包装类型:是对象,拥有方法和字段。对象的调用是通过引用对象的地址来实现的。

2. 传递方式

  • 基本类型:在赋值或作为参数传递时,传递的是值的副本,即值的传递。
  • 包装类型:在赋值或作为参数传递时,传递的是对象的引用,即引用的传递。

3. 声明与内存分配

  • 基本类型:声明时不需要使用new关键字,直接在栈中分配内存空间。
  • 包装类型:声明时需要使用new关键字,在堆中分配内存空间。

4. 存储位置

  • 基本类型:直接将值保存在栈内存中。
  • 包装类型:将对象放在堆内存中,然后通过对象的引用来调用它们。

5. 初始值

  • 基本类型:有默认初始值,例如int的初始值为0,boolean的初始值为false
  • 包装类型:默认初始值为null,因为它们是对象,而对象在未初始化时默认为null

6. 使用方式

  • 基本类型:直接赋值使用,适用于大多数情况下的变量声明和计算。
  • 包装类型:主要用于需要将基本数据类型作为对象处理的情况,例如在集合框架(如CollectionMap)中存储基本数据类型时,需要使用对应的包装类型。

7. 额外功能

  • 基本类型:功能简单,仅支持基本的算术运算和类型转换。
  • 包装类型:提供了一些额外的方法和属性,可以执行一些基本数据类型无法实现的操作,例如字符串转换、数据比较等。此外,包装类型还支持自动装箱和拆箱功能,即自动将基本数据类型转换为对应的包装类对象,以及将包装类对象自动转换为对应的基本数据类型。

为什么说是几乎所有对象实例都存在于堆中呢?

这是因为 HotSpot 虚拟机引入了 JIT 优化之后,会对对象进行逃逸分析,如果发现某一个对象并没有逃逸到方法外部,那么就可能通过标量替换来实现栈上分配,而避免堆上分配内存

注意:基本数据类型存放在栈中是一个常见的误区! 基本数据类型的存储位置取决于它们的作用域和声明方式。如果它们是局部变量,那么它们会存放在栈中;如果它们是成员变量,那么它们会存放在堆/方法区/元空间中。

public class Test {
    // 成员变量,存放在堆中
    int a = 10;
    // 被 static 修饰的成员变量,JDK 1.7 及之前位于方法区,1.8 后存放于元空间,均不存放于堆中。
    // 变量属于类,不属于对象。
    static int b = 20;

    public void method() {
        // 局部变量,存放在栈中
        int c = 30;
        static int d = 40; // 编译错误,不能在方法中使用 static 修饰局部变量
    }
}

包装类型的缓存机制

Java 基本数据类型的包装类型的大部分都用到了缓存机制来提升性能。

缓存范围:

  • 整型包装类(Byte、Short、Integer、Long):通常缓存了从-128到127(包括-128和127)之间的数值。这个范围是根据实际应用中整型数据的常用范围来设定的,能够覆盖大多数常用情况。
  • 字符型包装类(Character):缓存了从0到127之间的字符。这是因为ASCII字符集只定义了128个字符,而Unicode字符集的前128个字符与ASCII字符集完全相同。
  • 布尔型包装类(Boolean):只缓存了true和false两个对象。
  • 需要注意的是,浮点数类型的包装类(Float和Double)并没有实现缓存机制,主要是因为浮点数的表示范围非常大,且使用场景多样,缓存效果并不明显。

Integer 缓存源码:

public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}
private static class IntegerCache {
    static final int low = -128;
    static final int high;
    static {
        // high value may be configured by property
        int h = 127;
    }
}

Character 缓存源码:

public static Character valueOf(char c) {
    if (c <= 127) { // must cache
      return CharacterCache.cache[(int)c];
    }
    return new Character(c);
}

private static class CharacterCache {
    private CharacterCache(){}
    static final Character cache[] = new Character[127 + 1];
    static {
        for (int i = 0; i < cache.length; i++)
            cache[i] = new Character((char)i);
    }

}

Boolean 缓存源码:

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

如果超出对应范围仍然会去创建新的对象,缓存的范围区间的大小只是在性能和资源之间的权衡。
两种浮点数类型的包装类 Float,Double 并没有实现缓存机制。

Integer i1 = 33;
Integer i2 = 33;
System.out.println(i1 == i2);// 输出 true

Float i11 = 333f;
Float i22 = 333f;
System.out.println(i11 == i22);// 输出 false

Double i3 = 1.2;
Double i4 = 1.2;
System.out.println(i3 == i4);// 输出 false

下面的代码的输出结果是 true 还是 false 呢?

Integer i1 = 40;
Integer i2 = new Integer(40);
System.out.println(i1 == i2);

Integer i1 = 40 这一行代码会发生装箱,也就是说这行代码等价于 Integer i1=Integer.valueOf(40) 。因此,i1 直接使用的是缓存中的对象。
Integer i2 = new Integer(40) 会直接创建新的对象。(答案是 false 。你答对了吗?)

记住:所有整型包装类对象之间值的比较,全部使用 equals 方法比较

【强制】所有整型包装类对象之间值的比较,全部使用 equals方法比较。
说明:对于Integer var=?在 -128 127 之间的赋值,Integer对象是在 IntegerCache.cache 产生会复用已有对象,这个区间内的Integer值可以直接使用==进行判断,但是这个区间之外的所有数据,都会在堆上产生,并不会复用已有对象,这是一个大坑,推荐使用 equals方法进行判断。


http://www.kler.cn/a/348922.html

相关文章:

  • 理解智能合约:区块链在Web3中的运作机制
  • 人工智能风险预警以及区块链解决方案探索
  • simple_transfer攻防世界
  • 搭建个人博客--1、前端页面
  • 【哈希】1. leetcode 1. 两数之和
  • 鸿蒙--播放器状态控制
  • springcloud之基于RabbitMQ消息总线方式刷新配置服务
  • Linux下的杀毒软件介绍
  • 使用OpenCV实现基于EigenFaces的人脸识别
  • 道路车辆功能安全 ISO 26262标准(4-3)—系统级产品开发
  • KinDEL数据集:包含8100万个小分子的库,为激酶抑制剂的发现提供了一个丰富且功能强大的资源。
  • JavaScript前端开发技术
  • IDEA断点调试查看底层源码---程序员必备核心素养
  • Android设置状态栏隐藏、固定颜色
  • SpringBoot +Vue3前后端分离项目入门基础实例二
  • 运用 JDK8 中的核心新特性
  • C++:vector(题目篇)
  • 022 elasticsearch文档管理(添加、修改、删除、批处理)
  • 【大数据应用开发】2023年全国职业院校技能大赛赛题第08套
  • 03 go语言(golang) - fmt包基本类型