Java基础:数据类型

目录

数据类型

[8 种基本数据类型](#8 种基本数据类型)

[int 和 long 互相转换](#int 和 long 互相转换)

[用 bigDecimal 而不是 double](#用 bigDecimal 而不是 double)

装箱与拆箱

[128 陷阱](#128 陷阱)

[Integer 缓存](#Integer 缓存)


数据类型

8 种基本数据类型

浮点数默认为 double(float 型数值在末尾加上 f 或 F),整数默认为 int(long 型在末尾加上 l 或 L)。

int 和 long 互相转换

long 的范围比 int 大,int 转为 long 是安全的,但 long 转为 int 有可能导致溢出。

  • int 转 long 安全;
java 复制代码
 int intValue = 10;
 long longValue = intValue; // 自动转换,安全
  • long 转 int 有可能溢出。
java 复制代码
 long longValue = 100L;
 int intValue = (int) longValue; // 强制类型转换,可能会有数据丢失或溢出

将 long 转为 int 时,如果 longValue 的值超过了 int 类型的范围,结果将会是截断后的低位部分。

用 bigDecimal 而不是 double

计算机中主要采用二进制的科学计数法来存储和表示浮点数,遵循 IEEE 754。将实数表示为

这种格式(符号位、尾数、阶码),不仅能用统一形式表示极大或极小的数,而且还能节省内存空间并提高计算效率。

下图用 float 和 double 举例,仅仅 double 的阶位就远超过 long 类型所能表示的范围。

double 会出现精度丢失的问题,double 执行的是二进制浮点运算,二进制有些情况下不能准确地表示一个小数,就像十进制不能准确的表示1/3 (1/3 = 0.3333...),也就是说二进制表示小数的时候只能表示"若干 1/(2^n) 的组合"。由于若干个 1/(2^n) 无法表示 0.1,所以 double 无法表示 0.1 等浮点数。

而 Decimal 是精确计算, 所以一般牵扯到金钱的计算 , 都使用 Decimal。

java 复制代码
 public class BigDecimalExample {
   public static void main(String[] args) {
   BigDecimal num1 = new BigDecimal("0.1");
   BigDecimal num2 = new BigDecimal("0.2");
 ​
   BigDecimal sum = num1.add(num2);
   BigDecimal product = num1.multiply(num2);
 ​
   System.out.println("Sum: " + sum); // 0.3
   System.out.println("Product: " + product); // 0.02
  }
 }

使用 BigDecimal 可以确保精确的十进制数值计算,避免了使用 double 可能出现的舍入误差。需要注意的是,在创建 BigDecimal 对象时,应该使用字符串作为参数,而不是直接使用浮点数值,以避免浮点数精度丢失。

装箱与拆箱

Java 是面向对象的,但基本数据类型不是对象,包装类使得基本数据类型具备对象的特性。

装箱 :基本数据类型转换为包装类。自动装箱有两种情况,包括赋值时 和方法调用 时。Integer.valueOf(数值)。

  • 赋值时自动装箱:
java 复制代码
 Integer i = 10; // 自动装箱
  • 方法调用时自动装箱:
java 复制代码
 public static Integer show(Integer iParam) {
   System.out.println("i : " + iParam);
   return iParam;
 }
 ​
 show(3); // 自动装箱

拆箱 :包装类转化为基本数据类型。对象.intValue()。

经典题

java 复制代码
 public static void main(String[] args) {
   int a = 10;
   int b = 10;
 
   Integer a1 = 10;
   Integer b1 = 10;
 
   Integer a2 = new Integer(10);
   Integer b2 = new Integer(10);
 
   System.out.println(a == b);
   System.out.println(a1 == b1);
   System.out.println(a2 == b2);
   System.out.println(a1 == a);
   System.out.println(a1.equals(a));
   System.out.println(a1 == a2);
   System.out.println(a == a2);
 }
代码表达式 运行结果 核心物理逻辑
a == b true 基本类型值比较 :int 存储在栈帧的局部变量表中,比较的是字面量数值。
a1 == b1 true IntegerCache 机制 :自动装箱调用 Integer.valueOf(10)。由于 10 在 [-128, 127] 缓存范围内,a1 与 b1 指向堆中同一个缓存对象。
a2 == b2 false 显式堆分配 :new 关键字绕过缓存,在堆内存中强制开辟两个独立的空间,其引用地址不同。
a1 == a true 自动拆箱 :当包装类与基本类型进行 == 运算时,包装类 a1 会自动调用 intValue(),转化为基本类型后进行值比较。
a1.equals(a) true 比较数值内容 :equals 被重写,比较的是对象内部包裹的 int 数值。
a1 == a2 false 引用比较 :a1 指向常量池缓存对象,a2 指向 new 出来的堆对象,地址不一致。
a == a2 true 自动拆箱 :同 a1 == a,a2 拆箱为基本类型 10,与 a 进行数值比较。

128 陷阱

由于 Integer 缓存范围为 -128 到 127,当使用 == 比较该范围外的对象(如 128)时,会因创建新对象 导致地址不一致返回 false,而范围内则为 true。

解决方案

比较 Integer 对象时,应使用重写后的 .equals() 方法而不是 == 操作符。

Integer 缓存

源码分析

java 复制代码
 private static class IntegerCache {
   static final int low = -128;
   static final int high;
   static final Integer[] cache;
   static Integer[] archivedCache;
 ​
   static {
   // high value may be configured by property
   int h = 127;
   String integerCacheHighPropValue =
   VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
   if (integerCacheHighPropValue != null) {
   try {
   h = Math.max(parseInt(integerCacheHighPropValue), 127);
   // Maximum array size is Integer.MAX_VALUE
   h = Math.min(h, Integer.MAX_VALUE - (-low) -1);
  } catch( NumberFormatException nfe) {
   // If the property cannot be parsed into an int, ignore it.
  }
  }
   high = h;
 ​
   // Load IntegerCache.archivedCache from archive, if possible
   CDS.initializeFromArchive(IntegerCache.class);
   int size = (high - low) + 1;
 ​
   // Use the archived cache if it exists and is large enough
   if (archivedCache == null || size > archivedCache.length) {
   Integer[] c = new Integer[size];
   int j = low;
   for(int i = 0; i < c.length; i++) {
   c[i] = new Integer(j++);
  }
   archivedCache = c;
  }
   cache = archivedCache;
   // range [-128, 127] must be interned (JLS7 5.1.7)
   assert IntegerCache.high >= 127;
  }
 ​
   private IntegerCache() {}
 }
 ​
 @IntrinsicCandidate
 public static Integer valueOf(int i) {
   if (i >= IntegerCache.low && i <= IntegerCache.high)
   return IntegerCache.cache[i + (-IntegerCache.low)];
   return new Integer(i);
 }

Java 的 Integer 类内部实现了一个静态缓存池 ,用于存储特定范围内的整数值对应的 Integer 对象,只在装箱时生效,new 时不生效。

默认情况下,这个范围是 -128 至 127。当通过 Integer.valueOf(int) 方法创建一个在这个范围内的整数对象时,并不会每次都生成新的对象实例,而是复用缓存中的现有对象。

可见 c 数组中存储的是指向数值的指针,而不是数值本身。

相关推荐
小羊没烦恼!3 天前
微服务化的基石——持续集成
java·大数据·word·powerpoint·.net
倒头就睡的小比特3 天前
算法竞赛C++常用的STL
c++·算法
俊昭喜喜里3 天前
java中的继承和多态的区别
java
小羊没烦恼!3 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
譕痕3 天前
JSONObject与JSONArray封装数据格式区别
java·json
胡写代码3 天前
别再前后端各写一套表单校验了
java·后端
小鱼能吃糖3 天前
缺陷修复总览 · mall电商项目:5类缺陷,1个病根,4个业务域
java·电商
此时不提桶,更待何时3 天前
01-06-A-JVM排查实战详解
java·jvm
伞伞悦读3 天前
【第38期】Python 模块与包详解:import、from、模块搜索路径、包结构和 __init__
开发语言·python
猎头南楼3 天前
知识社区推荐系统实践:新用户冷启动与长短期兴趣建模的挑战 资深推荐算法工程师
人工智能·深度学习·算法·机器学习