Java数据类型最全讲解

这篇文章我们学习java的基本数据类型,这也是面试喜欢考的基本内容,所以还是仔细过一遍吧。

首先我们要知道java有两大数据类型:

原始数据类型(又名内置数据类型)

引用数据类型

基本数据类型比较简单,比如int、long。 引用数据类型类似于 C++ 指针,它指向一个对象。 我们使用的所有对象或数组都属于引用数据类型,它的默认值为null。

本文重点介绍基本数据类型,但也会讲解这些基本类型对应的包装类,也就是引用类型。

1.八种基本数据类型

Java有8种基本数据类型,需要记住

4 种整数类型:byte、short、int、long

2 种浮点类型:float、double

1 字符类型:char

1 布尔类型:布尔

看到这些熟悉的名字,你应该完全明白什么是基本数据类型了。 简单的说,它们其实就是常用的小写数据类型。

下图显示了它们占用的位数和字节数。 Boolean没有明确的定义,一般只占一位。

基本型

位数

字节

最小值

最大值

默认值

字节

8个

1个

-128

vba format 科学计数_java科学计数法转换成数字_js科学计数法转换数字

127

短的

16

2个

-32768

32767

整数

32

4个

-2147483648

2147483647

长的

64

8个

-9223372036854775808

9223372036854775807

0L(L不区分大小写,但是小写容易和1混淆,所以一般都是大写)

字符

16

2个

'u0000'

'uFFFF'

'u0000'(即一个空格)

漂浮

32

vba format 科学计数_java科学计数法转换成数字_js科学计数法转换数字

4个

1.4 E-45

3.4028235 E38

0f

双倍的

64

8个

4.9 E-324

1.7976931348623157 E308

0d

布尔值

1个

没有最小值和最大值

没有最小值和最大值

错误的

E是科学记数法。 E后面的数字表示E前面的数字乘以10。例如3.14E-3乘以10的负三次方:

3.14 x 0.001 =0.00314

那么这些最大值和最小值可以通过它们的包装类中的两个常量MAX_VALUE和MIN_VALUE来查看,例如:

        System.out.println("Integer最小值:" + Integer.MIN_VALUE);  
        System.out.println("Integer最大值:" + Integer.MAX_VALUE)

关于什么是wrapper类,第三节会详细解释java科学计数法转换成数字,这里先剧透一下,就是Integer、Double等基本数据类型对应的大写类。

2.char数据的赋值

char 类型比较特殊。 它是用java编码的Unicode,占用2个16位字节,无符号,所以它的范围是0-65535。 可以理解为可以存储65536个不同的符号,但是计算机不能直接存储符号。 所以它实际上是用整数存储的,只是用Unicode编码来表示字符。 每个字符都有一个专属的固定Unicode编码,所以每个数字对应一个符号。

另一种赋值方式是通过16位Unicode编码,比较特殊,可能用得少,格式为'u1234'。 'u'表示这是一个Unicode字符,里面的四位其实就是16进制的Unicode编码。 char的包装类中的MAX_VALUE和MIN_VALUE这两个常量也是这样存储的字符。

例如:将'u7801'或整数30721赋值给char,结果等于character:code。 如果以10进制计算7801,你会发现结果是30721。

所以可以通过三种形式给它赋值

① 用单引号括起来的单个字符

② 0-65535之间的整数(Unicode值的十进制)

③用单引号括起来的字符编码(Unicode值的十六进制)

当然结果和范围一样,最后只能表示一个字符,所以我们一般使用第一种方式赋值。 只是当你看到有人赋一个整数,结果是一个未来的字符时,不要大惊小怪。

例如:

    public static void main(String[] args) {
        char a = 'A';
        char b = 65;
        char c = 'u0041';
        System.out.println(a);
        System.out.println(b);
        System.out.println(c);
    }

输出结果是一样的,都是:A

所以以后不要下意识的认为char字符'6'转换成整数也是6,最后其实要看Unicode值,结果应该是整数:54

3. 包装

这8种基本类型都有对应的包装类,也就是java中的另一种数据类型:引用数据类型,它有自己的属性和方法,默认值为null。

我们平时在使用基础数据的时候,也更多的是用到它们的包装类。 因为这些基本数据类型只能声明和存储一个值,甚至不能为null,也没有我们常用的类型转换或equals()方法。 基本数据类型只能用于计算,而封装类比较完备,适合业务使用。

基本数据类型对应的封装类有:Byte、Short、Integer、Long、Float、Double、Character、Boolean。 也就是说,除了char对应Character,int对应Integer外,其他的都只是大写。

注意:这里要注意String。 它既不是基本类型也不是包装类。 不要把它误认为是字符类型的基本类型,虽然它的重要性不低于基本类型。

字符类的基本类型只有字符。

4. 自动装箱和拆箱

你应该听说过自动拆箱。 它是针对基本类型和它们的封装类,让它们自动转换。

装箱:用相应的应用程序类型包装原始类型

拆箱:将包装器类型转换为原始数据类型

这个说起来可能不是很清楚,但是这个功能我们一直都在用。 比如在java se5之前,如果要初始化一个Integer对象,需要这样:

Integer i = new Integer(10);

看起来很正常很别扭,因为每一个new对象都是通过new来初始化的,但是好像我们在使用的时候从来没有遇到过这样的麻烦。 一般来说,我们是:

Integer i=10;

这是自动装箱。 理论上10是数据的基本类型,只能直接赋值给int类型。 现在我们把它赋值给int的包装类Integer。 其实java自动将基础数据10装箱,打包到包装类中。 简单地说,为你创建一个 Integer 对象,并将值存储为 10。

自动拆箱则相反。 Java自动将Integer变量拆箱为具体的整型值,这样包装类变量就可以直接赋值给int基本数据类型。

自动拆箱:

Integer i=10;   //自动装箱
int j=i;        //自动拆箱

大多数包装类和基本类型混合的地方,都会自动进行拆箱。 下面第 6 节将详细介绍自动拆箱的场景。

5、自动拆箱的实现原理

通过编译和反编译类字节码文件,我们就可以知道答案了

先写一个这样的测试代码:

public class Test {
    public static void main(String[] args) {
        Integer i=10;
        int j=i;
    }
}

打开cmd输入javac Test.java编译得到Test.class

然后用jd-gui工具反编译Test.class得到如下代码:

public class Test
{
  public static void main(String[] paramArrayOfString)
  {
    Integer localInteger = Integer.valueOf(10);
    int i = localInteger.intValue();
  }
}

可以看出,自动拆箱的核心是在包装类变量上使用两种方法。 打包的时候就是Integer.valueOf()。 通过查看源码我们可以知道本质上是调用了new Integer初始化。 拆箱的时候是intValue(),也是直接返回int的值。

貌似高端的所谓自动拆箱,其实只是偷偷调用了包装类的两个方法,只是我们没有发现罢了。 另外,其他基本类型的开箱同理。 比如char的两个方法分别是valueOf()和charValue()。 你可以自己测试一下。

总结:装箱过程是通过调用wrapper的valueOf方法实现的,拆箱过程是通过调用wrapper的xxxValue方法实现的。

6.自动拆包场景

除了我们最常见的assignment,还有一些场景会进行自动拆箱。 下面可以反编译查看原理

① 将基本数据类型放入集合类

例如

List li = new ArrayList();
for (int i = 1; i < 50; i ++){
    li.add(i);
}

这里使用自动装箱:li.add(Integer.valueOf(i));

②比较大小

Integer a=1;
System.out.println(a==1);

结果是真的,其实这里用到了自动拆箱:System.out.println(a.intValue()==1);

③ 手术

Integer i = 1;
System.out.println(i+2);

这里使用了自动拆箱,只有拆成基本类型才能进行操作:System.out.println(i.intValue()+2);

④ 三元运算符

这种情况比较少见,我也是在网上搜集资料才知道的。

boolean flag = true;
Integer i = 0;
int j = 1;
int k = flag ? i : j;

这里使用自动拆箱:int k = flag ? i.intValue() : j,当第二个和第三个操作数分别是原始类型和对象时,对象将自动拆箱。 所以一不小心,可能会触发空指针异常,所以要注意这一点。

这些场景中最需要注意的是计算部分。 以后遇到封装类的计算时,要记住它们是拆箱成基本数据类型再计算的。

7.不同基本类型之间的运算①不同基本类型之间

在java中,除了boolean,其他基本类型都可以自动转换。 转换的形式是从低到高java科学计数法转换成数字,即当两个不同的基本类型一起操作时,低类型将按以下顺序转换为高类型。

byte, short, char——“int”——“long”——“float——”double

byte、short 和 char 是特殊的。 即使不涉及高等类型,在比较运算时也会直接自动转为int进行处理。 比如short、int、float类型相加,在运算前都会全部转换为三种float类型。

②基本类型和String

如果基本类型用String操作,会自动转换为String类型,但这里有以下特殊情况:

System.out.println(1+2+"3"+4);

这里从左到右,由于1+2是两个整数,所以会先计算3,再把3+"3"转成String。 同样,“33”+4 将变成字符串 334。

8.包装类和常量池

Java的基本类型的包装类大多实现了常量池技术:Byte、Short、Integer、Long、Character、Boolean,即传递一定范围的缓存,避免重复创建对象。

①Byte、Short、Integer、Long默认创建[-128,127]范围内的数据

②字符默认创建[0,127]范围内的数据

③Boolean默认返回True或false

这些可以通过源码的valueOf()方法查看,也就是你在任何地方通过valueOf()创建基础数据(自动装箱)的时候,都是判断同一个常量池中是否有这个值。 如果有就直接返回常量池中的对象,如果没有就给你一个新的对象。

因此,如果通过autoboxing或者valueOf获取到一个wrapper对象,当数据的值在缓存范围内时,无论获取多少次相同的数据,本质上都指向同一个对象,所以内存地址和喜欢自然也是平等的。

至于浮点数,是没有缓存的,因为一定范围内的整数是有限的,而浮点数是无限的。 即使范围是0到1,浮点数也是数不胜数,很难定位哪些浮点数是常用的,所以没有浮点数的常量池缓存。

参考:

一、什么是Java自动拆箱:

2、深入剖析Java装箱拆箱:

3、java不同基本类型之间的操作: