- 一、数据类型的分类
- 二、Java数据类型在内存中的存储
- 三、基本数据类型对应的包装类型
- 1、为什么需要使用包装类型
- 2、装箱与拆箱
- 3、基本数据类型和包装类型的区别
- 4、面试中相关问题
一、数据类型的分类
数据类型 | 所占字节 | 取值范围 | 默认值 | 包装类 |
---|---|---|---|---|
byte(字节型) | 1个字节 | -128 ~ 127 | 0 | java.lang.Byte |
short(短整型) | 2个字节 | -215 ~ 215-1 | 0 | java.lang.Short |
int(整型) | 4个字节 | -231 ~ 231-1 | 0 | java.lang.Integer |
long(长整型) | 8个字节 | -263 ~ 263-1 | 0 | java.lang.Long |
char(字符型) | 2个字节 | 0 ~ 216-1 | ‘\u0000’ | java.lang.Character |
float(单精度浮点型) | 4个字节 | -3.4E38 ~ 3.4E38 | 0.0F | java.lang.Float |
double(双精度浮点型) | 8个字节 | -1.7E308 ~ 1.7E308 | 0.0D | java.lang.Double |
boolean(布尔型) | 1个字节 | true和false | false | java.lang.Boolean |
注意:
char型变量是用来存储Unicode编码的字符的,unicode编码字符集中包含了汉字,所以char型变量中可以存储汉字。不过,如果某个特殊的汉字没有被包含在unicode编码字符集中,那么,这个char型变量中就不能存储这个特殊汉字。补充说明:unicode编码占用两个字节,所以char类型的变量也是占用两个字节。
boolean类型理论上占1bit(1/8字节),而实际中按1byte(1字节)处理。
Tipes:
int类型占用32位由标准委员会规定,int类型占用4个字节,每个字节包含8个比特位。因为之前的cpu架构是32位,所以int被定义为了32位,正好是一个指令大小,同时也便于取址,方便运算。即使现在cpu达到了64位,为了保证int的向前兼容性,它的取值范围也并没有随之改变。java代码在虚拟机上运行,而虚拟机的实现依赖于cpu,如果把int类型定义为32位,就会为虚拟机的设计带来很多简化工作。
二、Java数据类型在内存中的存储
1)、基本数据类型:基本数据类型不存在“引用”的概念,基本数据类型都是直接存在虚拟机栈中,数据本身的值就是存储在栈空间里面。
2)、引用数据类型:引用数据类型继承于Object类(其中Object也是引用类型),都是按照Java里面存储对象的内存模型来进行数据存储的,使用Java内存堆和内存栈来进行这种类型的数据存储,简单地讲,“引用”是存储在有序的内存栈上的,而对象本身的值存储在内存堆上的。
区别:基本数据类型和引用数据类型的区别主要在于基本数据类型是分配在栈上的,而引用数据类型指针是存储在栈上,对象是存储在堆上的。
三、基本数据类型对应的包装类型
1、为什么需要使用包装类
Java 是个面向对象语言,所有的操作都是基于对象。Object类是Java中所有对象的基类,Object 类可以表示任意类型数据。但Java中的一些基本数据类型并不是对象,没有对象的操作。如何让对象类型与基本数据类型联系起来,这时就需要一个过渡类型数据,称为包装类。
通过引入一个中间类,就可以将Object 类型数据和基本数据类型建立了联系。Java中的基本数据类型没有类的操作属性,而Java中的操作都是基于对象。很多操作如:ArrayList、HashMap、泛型等都是针对对象的操作,为了让基本数据类型也具有类的特性于是就引入了包装类。包装类是对基本数据类型的包装,并丰富了基本类型数据的操作。
2、装箱(Autoboxing)与拆箱(unboxing)
自JDK1.5版本后,Java引入了自动装箱、拆箱操作。使基础类型数据和包装类类型数据可以直接互通使用,JVM自动判断并进行装箱、拆箱,省去人为操作。
public class Test {
public static void main(String[] args) {
int i = 10;
//显示装箱
Integer integer =(Integer)10;
//显示拆箱
int i = (int)integer;
//int 转为 Integer 类型,自动装箱过程
Integer a = 10;
//这种方式不会触发自动装箱过程,因为没有调用包装器的valueOf方法
Integer integer = new Integer(10);
//Integer 转为 int 类型,自动拆箱过程
int a = integer.intValue();
//自动装箱
Integer x = i;
Integer z = 10;
//自动拆箱
int y = x;
}
}
运行代码:
public class Test {
public static void main(String[] args) {
Integer i = 10;
int n = i;
}
}
反编译class文件之后得到如下内容:
public class com.zhxg.zhsq.base.Test {
public com.zhxg.zhsq.base.Test();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: bipush 10
2: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
5: astore_1
6: aload_1
7: invokevirtual #3 // Method java/lang/Integer.intValue:()I
10: istore_2
11: return
}
总结: 从反编译得到的字节码内容可以看出,在装箱的时候自动调用的是Integer的valueOf(int)方法,而在拆箱的时候自动调用的是Integer的intValue方法。装箱过程是通过调用包装器的valueOf方法实现的,而拆箱过程是通过调用包装器的xxxValue方法实现的,其中xxx代表对应的基本数据类型。
3、基本数据类型和包装数据类型的区别
1、声明方式不同: 基本类型不使用new关键字,而包装类型需要使用new关键字来在堆中分配存储空间;
2、存储方式及位置不同: 基本类型是直接将变量值存储在栈中,而包装类型是将对象放在堆中,然后通过引用来使用;
3、初始值不同: 基本类型的初始值如int为0,boolean为false,而包装类型的初始值为null;
4、使用方式不同: 基本类型直接赋值直接使用就好,而包装类型在集合如Collection、Map时会使用到。
4、面试中相关问题
1)、Integer:下面这段代码的输出结果是什么
public class Test {
public static void main(String[] args) {
Integer i1 = 100;
Integer i2 = 100;
Integer i3 = 200;
Integer i4 = 200;
System.out.println(i1==i2);//true
System.out.println(i3==i4);//false
}
}
输出结果表明i1和i2指向的是同一个对象,而i3和i4指向的是不同的对象。此时只需一看源码便知究竟,下面这段代码是Integer的valueOf方法的具体实现:
/**
* Returns an {@code Integer} instance representing the specified
* {@code int} value. If a new {@code Integer} instance is not
* required, this method should generally be used in preference to
* the constructor {@link #Integer(int)}, as this method is likely
* to yield significantly better space and time performance by
* caching frequently requested values.
*
* This method will always cache values in the range -128 to 127,
* inclusive, and may cache other values outside of this range.
*
* @param i an {@code int} value.
* @return an {@code Integer} instance representing {@code i}.
* @since 1.5
*/
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
其中IntegerCache类的实现为:
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
这个原码可以看出来,在通过valueOf方法创建Integer对象的时候,如果数值在[-128,127]之间,程序便返回指向IntegerCache.cache中已经存在的对象的引用,否则创建一个新的Integer对象。
2)、Double及Float:下面这段代码的输出结果是什么
public class Test {
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);//false
System.out.println(i3==i4);//false
Float i5 = 100.0f;
Float i6 = 100.0f;
Float i7 = 200.0f;
Float i8 = 200.0f;
System.out.println(i5==i6);//false
System.out.println(i7==i8);//false
}
}
Double及Float对应的valueOf方法如下:
public static Double valueOf(double d) {
return new Double(d);
}
public static Float valueOf(float f) {
return new Float(f);
}
至于具体为什么,可以去查看Double类的valueOf的实现。在这里只解释一下为什么Double类的valueOf方法会采用与Integer类的valueOf方法不同的实现。因为在某个范围内的整型数值的个数是有限的,而浮点数却不是。
3)、Boolean:下面这段代码的输出结果是什么
public static void main(String[] args) {
Boolean i1 = false;
Boolean i2 = false;
Boolean i3 = true;
Boolean i4 = true;
System.out.println(i1==i2);//true
System.out.println(i3==i4);//true
}
Boolean对应的valueOf方法如下:TRUE和FALSE都是被final修饰,无法被修改,返回的都是同一个对象,所以对应的指针相同。
public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false);
public static Boolean valueOf(String s) {
return parseBoolean(s) ? TRUE : FALSE;
}
4)、Character:下面这段代码的输出结果是什么
public static void main(String[] args) {
Character i1 = 100;
Character i2 = 100;
Character i3 = 128;
Character i4 = 128;
System.out.println(i1 == i2);//true
System.out.println(i3 == i4);//false
//Character.valueOf()
}
Character对应的valueOf方法如下:
public static Character valueOf(char c) {
if (c <= 127) { // must cache
return CharacterCache.cache[(int)c];
}
return new Character(c);
}
所以对于Character,当创建的对象在[0,127]时返回的是同一个对象。
总结: Integer、Short、Byte、Character、Long这几个类的valueOf方法的实现是类似的。Double、Float的valueOf方法的实现是类似的。