基本数据类型
Java 基本数据可分为布尔型、整数型、浮点型、字符型四类 8 基本数据类型。
- 布尔型:boolean
- 整数型:byte、short、int、long
- 浮点型:float、double
- 字符型:char
8 基本类型取值如下:
数据类型
代表含义
默认值
取值
包装类
boolean
布尔型
false
0(false) 到 1(true)
Boolean
byte
字节型
(byte)0
﹣128 到 127
Byte
char
字符型
\u00000
'\u0000' 到 '\uFFFF'
Character
short
短整数型
(short)0
-215215 到 215215
﹣1
Short
int
整数型
0
﹣231231 到 231231﹣1
Integer
long
长整数型
0L
﹣263263 到 263263﹣1
Long
float
单浮点型
0.0f
1.4e-45 到 3.4e+38
Float
double
双浮点型
0.0d
4.9e-324 到 1.798e+308
Double
除 char 的包装类 Character 和 int 的包装类 Integer 此外,其他基本数据类型的包装只需要首字母大写即可。本文下半部分详细说明了包装的功能和特点。
在代码中,我们可以查看某种类型的值范围,代码如下:
public static void main(String[] args) { // Byte 取值:-128 ~ 127 System.out.println(String.format("Byte 取值:%d ~ %d", Byte.MIN_VALUE, Byte.MAX_VALUE)); // Int 取值:-2147483648 ~ 2147483647 System.out.println(String.format("Int 取值:%d ~ %d", Integer.MIN_VALUE, Integer.MAX_VALUE));}
包装类型我们知道 8 由于基本数据类型有相应的包装类型,所有基本数据类型都有相应的包装类型 Java 设计理念是一切都是对象,很多时候我们需要以对象的形式操作一个功能,比如获得哈希值(hashCode)或获取类(getClass)等。
包装的特点是什么?
1. 功能丰富
包装本质上是一个对象,对象包含属性和方法,如 hashCode、getClass 、max、min 等。
2. 泛型类型参数可以定义
泛型可以定义为包装类,但基本类型不好。
比如使用 Integer 定义泛型,代码:
List<Integer> list = new ArrayList<>();
如果使用 int 定义会报错,代码:
List list = new ArrayList<>(); // 编译器代码报错
3. 序列化
因为包装类别已经实现了 Serializable 因此,包装类自然支持序列化和反序列化。例如 Integer 类图如下:
4. 类型转换
包装类提供了类型转换的方法,可以很容易地实现类型之间的转换,如 Integer 类型转换代码:
String age = "18";int ageInt = Integer.parseInt(age) + 2;// 输出结果:20system.out.println(ageInt);
5. 数据缓存在高频区间
这一特性是高频区间数据缓存的重要用途之一 Integer 例如,在数值范围内 -128~127 现有对象将直接重复使用,在此范围之外的数字将在堆上产生。
我们使用 == 对 Integer 验证代码如下:
public static void main(String[] args) { // Integer 高频区缓存范围 -128~127 Integer num1 = 127; Integer num2 = 127; // Integer 取值 127 == 结果为 true(值127 num1==num2 => true) System.out.println("值127 num1==num2 => " + (num1 == num2)); Integer num3 = 128; Integer num4 = 128; // Integer 取值 128 == 结果为 false(值128 num3==num4 => false) System.out.println("值128 num3==num4 => " + (num3 == num4)); }
很明显,从上面的代码可以看出,Integer 为 127 当现有对象被重用时,当值为 128 新对象在堆上重新生成。
为什么会产生高频区域数据缓存?我们可以通过查看源代码来找到“线索” 源代码如下:JDK8:
public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i);}
由此可见,现有对象将直接用于高频区域的数值,非高频区域的数值将被重新使用 new 一个新的对象。
各包装高频区域的取值范围:
- Boolean:使用静态 final 定义将返回静态值
- Byte:缓存区 -128~127
- Short:缓存区 -128~127
- Character:缓存区 0~127
- Long:缓存区 -128~127
- Integer:缓存区 -128~127
- int 的默认值是 0,而 Integer 的默认值是 null。
- 建议比较所有包装对象之间的值
equals()
方法,由于包装中的非高频区数据会堆积,高频区会重用现有对象,导致相同的代码,由于值的不同,产生两个完全不同的结果。代码示例:
public static void main(String[] args) { // Integer 高频区缓存范围 -128~127 Integer num1 = 127; Integer num2 = 127; // Integer 取值 127 == 结果为 true(值127 num1==num2 => true) System.out.println("值127 num1==num2 => " + (num1 == num2)); Integer num3 = 128; Integer num4 = 128; // Integer 取值 128 == 结果为 false(值128 num3==num4 => false) System.out.println("值128 num3==num4 => " + (num3 == num4)); // Integer 取值 128 equals 结果为 true(值128 num3.equals(num4) => true) System.out.println("值128 num3.equals(num4) => " + num3.equals(num4);}
- Float 和 Double 没有缓存,其他包装类别也有缓存。
- Integer 它是唯一能修改缓存范围的包装类别 VM optons 加入参数:
-XX:AutoBoxCacheMax=666 即修改缓存的最大值为
666
。
示例代码如下:
public static void main(String[] args) { Integer num1 = -128; Integer num2 = -128; System.out.println("值为-128 => " + (num1 == num2)); Integer num3 = 666; Integer num4 = 666; System.out.println("值为666 => " + (num3 == num4)); Integer num5 = 667; Integer num6 = 667; System.out.println("值为667 => " + (num5 == num6));}
结果如下:
值为-128 => true值为666 => true值为667 => false
由此可见将 Integer 最大缓存修改为 666 之后,667 而不是缓存,而是 -128~666 两者之间的数量都被缓存了。
相关面试题1. 以下 Integer 代码输出的结果是?Integer age = 10;Integer age2 = 10;Integer age3 = 133;Integer age4 = 133;System.out.println((age == age2) + "," + (age3 == age4));
答:true,false
Double num = 10d;Double num2 = 10d;Double num3 = 133d;Double num4 = 133d;System.out.println((num == num2) + "," + (num3 == num4));
答:false,false
int i = 100;Integer j = new Integer(100);System.out.println(i == j);System.out.println(j.equals(i));
A:true,trueB:true,falseC:false,trueD:false,false
答:A
题目分析:有人认为这是和 Integer 高速缓存是相关的,但你会发现将值改为值 10000 结果也是 true,true
,这是因为 Integer 和 int 比较时会自动拆箱 int 相当于两个 int 相比之下,值必须是 true,true
。
final int iMax = Integer.MAX_VALUE;System.out.println(iMax + 1);
A:2147483648B:-2147483648C:程序报错D:以上都不是
答:B
主题分析:这是因为整数在内存中以补码的形式表示,最高位是符号位 0 表示正数,1 当执行时,表示负数 +1 时间,最高位变成了 1.结果成了 -2147483648。
5. 以下程序执行的结果是什么?Set<Short> set = new HashSet<>();for (short i = 0; i < 5; i++) { set.add(i); set.remove(i - 1);}System.out.println(set.size());
A:1B:0C:5D:以上都不是
答:5
题目解析:Short 类型 -1 然后转换成 Int 类型,remove() 在集合中找不到时,在集合中找不到 Int 数据类型,所以没有删除任何元素,执行的结果是 5。
6.short s=2;s=s+1;
会报错吗?short s=2;s+=1;
会报错吗?答:s=s+1 会报错,s+=1 因为 s=s+1 会导致 short 类型升级为 int 类型,所以会报错,但是 s+=1 还是原来的 short 类型,所以不会报错。
7.float f=3.4;
你会报错吗?为什么?答:会因为值而报错 3.4 是 double 类型,float 类型级别小于 double 类型,所以会报错。如下图所示:
8. 为什么需要包装?答:包装有两个原因。
① Java 设计理念是万物都是对象,包装类体现了面向对象的设计理念;② 包装类包含许多属性和方法,比基本数据类型功能更多,如获取哈希值(hashCode)或获取类(getClass)的方法等。
9. 基本类 int 和包装类 Integer,在 -128~127 现有的缓存对象会在两者之间重用吗?这是正确的吗?答:不正确,只有包装高频区域数据才有缓存。
10. 包装类 Double 和 Integer 都有高频区域数据缓存,这是正确的吗?答:不正确,基本数据类型的包装只有 Double 和 Float 高频区域没有缓存。
11. 用什么方法比较包装值?答:由于高频区域数据缓存,建议使用包装类 equals() 该方法比较了值。
12. 包装有哪些功能?答:包装类提供以下功能。
- 功能丰富:包装包含 hashCode、getClass 、max、min 等方法;
- 一般类型参数可以定义:例如
List<Integer> list = new ArrayList<>();
; - 序列化:包装类实现了 Serializable 因此,包装类自然支持序列化和反序列化;
- 类型转换:包装类型提供了方便的类型转换方法,如 Integer 的 parseInt() 方法;
- 现有的缓存对象可用于高频区域数据缓存。
详见正文“包装类型”部分。
13. 泛型可以是基本类型吗?为什么?答:基本数据类型不能用于泛型。泛型在 JVM(Java虚拟机)编译时会删除类型,如代码 List<Integer> list
在 JVM 编译时会转换成 List list
,因为泛型是在 JDK 5 时间提供,而 JVM 类型擦除是一种与之前代码兼容的折中方案,类型擦除后变成 Object,而 Object 基本数据类型不能存储,但基本数据类型对应的包装类型可以使用,因此类似 List<int> list
这样的代码是不允许的,编译阶段会检查报错,但是 List<Integer> list
被允许。
答:我们知道正确使用包装类可以提供程序执行效率和现有缓存。一般来说,有以下原则可以选择基本数据类型或包装类型。
① 所有 POJO 包装类必须使用类别属性;② RPC 方法返回值和参数必须包装;③ 推荐使用所有局部变量的基本数据类型。
15. 基本数据类型在 JVM 一定要存放在栈里吗?为什么?答:基本数据类型不一定存储在堆栈中,因为基本类型的存储位置取决于声明的作用域。
- 当基本数据类型为局部变量时,如方法中声明的变量存储在方法堆栈中。当方法结束时,系统会释放方法堆栈,方法中的变量也会随着堆栈的销毁而结束,这就是为什么局部变量只能在方法中使用;
- 当基本数据类型为全局变量时,如类中声明的变量存储在堆上,因为全局变量不会随着某种方法的实施而被破坏。
Integer i1 = new Integer(10);Integer i2 = new Integer(10);Integer i3 = Integer.valueOf(10);Integer i4 = Integer.valueOf(10);System.out.println(i1 == i2);System.out.println(i2 == i3);System.out.println(i3 == i4);
A:false,false,falseB:false,false,trueC:false,true,trueD:true,false,false
答:B
题目解析:new Integer(10) 每次都会创造新的对象,Integer.valueOf(10) 缓存池中的物体将被使用。
17. 3*0.1==0.3 返回值是多少?答:返回值如下:false。
主题分析:由于有些浮点数不能完全准确地表示,如下代码:
System.out.println(3 * 0.1);
返回的结果是:0.3万万万万万万万万万四。