Java数据类型
Java数据类型分为两个大类:基本数据类型和引用数据类型
基本数据类型
Java语言提供了八种基本类型
基本类型中的,数字类型默认值为0,布尔类型默认值为false
byte
*byte数据类型是8位
*最大值为127(2^7-1)
*最小值为-128(-2^7)
short
*short数据类型是16位
*最小值为-2^15
*最大值为2^15-1
int
*int数据类型有32位
- 最小值为-2^31
- 最大值为2^31-1
long
*long的数据类型为64位
*最小值为-2^61
*最大值为2^61-1
*这与前面的有区别,定义的时候要有L
*long a=10L
float
*float数据类型是单精度32位,浮点类型
*float f=12.3f
double
*double是双精度类型64位
*double d=1234.5 - 这个要写d不写都可以,浮点类型默认是double类型的
boolean
*只有true和false两个值,默认值是false
char
char是16位的Unicode字符
引用类型
在Java中,应用类型指向一个对象,指向对象的变量是引用变量
除了基本数据类型,其他的都是引用数据类型,比如:数组
引用数据类型的默认值都是null
自动装箱和拆箱
基本数据类型都有与之对应的包装类型,Java 5 增加了自动装箱和自动拆箱的机制,方便基本数据类型和包装类型互相转换。
在Java 5 之前,我们要创建一个Integer的包装类型(int的对应的包装类型为Integer)只能
Integet a = new Integer(20);
或者
Integet a = Integer.valueOf(20);
但是在Java 5 以及之后,我们可以做这么写
Integet a = 20;
这里的20默认就是int类型的,这里默认就是采用自动装箱的机制,把int类型转换成Integer类型的数据,反之如果把Integer转换成int就叫作拆箱。
Java 5 之后可以把int型数据,直接赋给Integer对象。
自动装箱和自动拆箱为程序员提供了很大的方便,提高了开发的效率。
装箱和拆箱的实现
包装类型中都有两个方法
第一个是valueOf():把给定的基本数据类型转换成对应的包装类型
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
上面是jdk中Integer的Integer 中valueOf方法,给方法传一个int 类型的名字叫i的参数,方法最后返回的是Integer类型的参数。
第二个是xxxValue()方法:将包装类型转换成对应额基本数据类型
类看看jdk中是怎么定义的
public int intValue() {
return value;
}
private final int value;
/**
* Constructs a newly allocated {@code Integer} object that
* represents the specified {@code int} value.
*
* @param value the value to be represented by the
* {@code Integer} object.
*/
public Integer(int value) {
this.value = value;
}
我们可以看到返回的value是int类型的
具体来分析一下Integer的部分源代码
public final class Integer extends Number implements Comparable<Integer> {
//一进来就定义了int类型的最大值和最小值
//@Native表示java与其他语言的调用
@Native public static final int MIN_VALUE = 0x80000000;
@Native public static final int MAX_VALUE = 0x7fffffff;
//这里定义了一个静态的内部类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方法
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
//一个value常量
private final int value;
//一个构造器
public Integer(int value) {
this.value = value;
}
//拆箱调用的函数
public int intValue() {
return value;
}
}
为什么定义了静态内部类
内部类缓存了从[low,high]的Integer对象,也就是从[-128,127],还有一个Integer类型的数组cache[],这个数组就是用来放这些Integer对象的。
valueOf方法
valueOf方法,是用来把int转成Integer的
方法内部首先去判断这个传进来的int的大小,范围是不是落在了[-128,127],如果在这里面,就直接去调用内部类中的cache[]数组,把对应的Integer对象取出来。如果不在这个范围里面,就new Integer(i),调用Integer的有参构造创新新的Integer对象。所以落在-128到127之间,是不会再去创建新对象的,这里是做题的一个坑。
有参构造器
会把要装箱的int,放在常量value中,拆箱的时候直接把这个常量返回出来就可以了
包装类型中,Integer,Byte,Short,Long复用了[-128,127]这些对象,而Charater复用了[0,127]不能表示负数。
基本数据类型类型的存储方式
这里就得稍微分析一下虚拟机的内存区域
这里主要讲一讲运行时数据区
运行时数据区包含了,方法区,虚拟机栈,本地方法区,堆,程序计数器
程序计数器(或者叫指令计数器)
程序计数器的作用就是,当前线程的行号指示器。
每一个线程都有自己的程序计数器,程序计数器是线程私有的。为了就是在多线程环境下,记录线程暂停时运行到哪一行了,切换线程时才能正确的恢复正确的执行位置。
虚拟机栈(简称栈)
虚拟机栈是用来描述Java方法执行的内存模型。每一个方法在执行时,都会创建一个栈帧,用于存储局部变量表,操作数栈,动态链接,方法出口等信息。每一个方法的调用到执行结束,就对应着栈帧在虚拟机栈中的入栈到出栈的过程。
局部变量表:用于存放基本数据类型、对象引用、returnAddress类型(指向一条字节码指令的地址)
操作数栈:虚拟机栈中用于计算的临时数据存储区
public int test1(){
int a=6+10;
int b=a+5;
return b;
}
int a=6+10;
编译过程中已经计算了6+10=16,把16压入操作数栈的栈顶,然后把16从操作数栈中弹出来放入局部变量表索引为1的slot
int b=a+5;
把a也就是局部变量表中索引为1的slot,重新压入操作数栈中,然后再把3压入操作数栈中,将栈顶的这两个弹出并进行加法运算后再压入操作数栈顶,然后从栈顶弹出来放入操作数栈中索引为2的slot
这就是操作数栈的具体运算。
本地方法栈
这个本地方法区与虚拟机栈有些类似,虚拟机栈为执行Java方法服务。而本地方法栈为的是执行Native方法服务。
堆
堆是用于存放对象实例的,几乎所有的对象实例都在这里分配内存。所有线程共享
方法区
方法区是用于存放已经被虚拟机加载的类信息,常量,静态变量以及即时编译器编译后的代码等数据。
即时编译器:用于把Java字节码转换成可以直接发送给处理器的指令程序。
public void(int a){
int i=1;
int j=1;
}
这是一个方法,方法时放在虚拟机栈中的,变量则放在栈中的局部变量表,a,i,j都是放在局部变量表中
class A{
int i=1;
A a=new A();
}
这是一个类,类实例化的对象放在堆中,所以成员变量也在堆中,i和a也都在堆中
上面讲过的基本数据类型的包装类就是在常量池(对象池)中查找对应的对象,找不到再去常量池创建该对象