自述
Java中的基本数据类型可以说是相当基础的一部分了,在日常开发中经常用到,但我们平时却不会过于深究。
第一部分,重新回顾一遍基本数据类型相关的内容,对与比较模糊的地方着重的总结了一下。
第二部分,通过JDK文档,结合源码,深入了解各个包装类。
基本数据类型
基本特性
4种整数型:
数据类型 | 二进制位数 | 包装类 | 默认值 | 可表示范围 |
---|---|---|---|---|
byte | 8 | Byte | 0 | -128~127 |
short | 16 | Short | 0 | -32768~32767 |
int | 32 | Integer | 0 | -2147483648~2147483647 |
long | 64 | Long | 0L |
|
2种浮点型
数据类型 | 二进制位数 | 包装类 | 默认值 | 可表示范围 |
---|---|---|---|---|
float | 32 | Float | 0.0f | -2147483648~2147483647 |
double | 64 | Double | 0.0d |
|
1种字符型
数据类型 | 二进制位数 | 包装类 | 默认值 | 可表示范围 |
---|---|---|---|---|
char | 16 | Character | ‘/uoooo’(null) | 0~65535 |
1种布尔型
数据类型 | 二进制位数 | 包装类 | 默认值 | 可表示范围 |
---|---|---|---|---|
boolean | 1 | Boolean | false | true/false |
初始化的一些细节
对于像int i = 1
这样的基本操作我就不解释了,这里主要记录一些使用比较少,容易忽略的内容:
尾缀
尾缀用三种,分别是:long->l(L)、float->f(F)、double->d(D),大小写不敏感,但通常我们会将long的尾缀用大小L,其余两个用小写,因为小写的字母”l”跟数字”1”容易混淆。
字面量和初始化
- 如果一个字面量是一个整型,JVM会默认为int类型
- 如果一个字面量是一个浮点型,JVM会默认double类型。
所以如果声明float f = 1.0
会报错cannot convert from double to float
强制类型转换
这部分相信大家都有了解,进行转换时,表示范围如果扩大了,则可自动转换;如果缩小了,则需要强制类型转换:
- boolean型和其它基本数据类型之间不能相互转换;
- byte型可以转换为short、int、、long、float和double;
- short可转换为int、long、float和double;
- char可转换为int、long、float和double;
- int可转换为long、float和double;
- long可转换为float和double;
- float可转换为double;
Console控制台打印默认值
我们可以通过声明静态变量(或全局变量)的方式查看默认值。
public class Simple {
static byte b;
static short s;
static int i;
static long l;
static float f;
static double d;
static char c;
static boolean bln;
public static void main(String[] args) {
System.out.println("byte:\""+b+"\"");
System.out.println("short:\""+s+"\"");
System.out.println("int:\""+i+"\"");
System.out.println("long:\""+l+"\"");
System.out.println("float:\""+f+"\"");
System.out.println("double:\""+d+"\"");
System.out.println("char:\""+c+"\"");
System.out.println("boolean:\""+bln+"\"");
}
}
结果:
byte:"0"
short:"0"
int:"0"
long:"0"
float:"0.0"
double:"0.0"
char:" "
boolean:"false"
这里需要注意的是:
- 后缀是不会打印出来的。
- char的默认值,打印出来的是一个空白符号,而不是null或者其他(空格的Unicode为’\u0020’)。
char类型
char类型时我们日常开发中比较少接触的一种数据类型,所以这里单独拿出来讲一讲。
- char类型储存的是一个整型字面量,范围从0~65535;我们可以从
char c = 'a'+'a';//cannot convert from int to char
得到验证。 - char的赋值方式有2种,一种是通过整型字面量赋值
char c = 97
,另一种是通过字符型字面量赋值char c = 'a'
。(Java中,单引号”用来引用单个字符,双引号”“用来引用字符串) - 当打印char到控制台的时候,因为字符串拼接等原因,会出现比较复杂的情况:
char c1 = 'a';
char c2 = 97;
//因为该方法的内部实现是将根据
System.out.println(c1); //a
System.out.println(c2); //a
System.out.println(c1+c2); //194
System.out.println(c1+97); //194
System.out.println('a'+'a'); //194
我们先看看println的源码:
public void println(char x) {
synchronized (this) {
print(x);
newLine();
}
}
//再看看print(x)
public void print(char c) {
write(String.valueOf(c));
}
我们可以得出结论:
1. 对于传入的单个char类型,最终会调用String.valueOf方法,将其转换成一个String类型的变量然后输出到控制台。
2. 当两个char类型的变量相加时,由于char类型储存的是一个整型字面量,所以相当于两个int相加,所以时以int类型作为参数传入println方法的。
但当情况更加复杂时,结果又会如何?
System.out.println(c1+c2+""); //194
System.out.println(""+c1+c2); //aa
System.out.println(c1+""+c2); //aa
System.out.println(c1+c2+""+c1+c2); //194aa
这次出现的结果很有意思,前半部分相当于两个char类型的变量相加,是个int类型,后半部分则是单个的char转换为String类型后拼接而来。
这里涉及了字符串拼接,与JVM有关(本人JVM方面刚开始看,还不够了解,如果有大神出现的话,请纠正),这里试着解释一下,姑且一看吧:
- 字符串拼接是有先后顺序的,按照+号的出现顺序依次拼接。
- 第一句的
c1+c2
并不是字符串拼接,而是int类型相加;之后的+号才是字符串拼接,此时先将int转化为了String,然后空字符串进行拼接。(ps:+和+=是Java中唯二两个重载过的操作符) - 第二句中,
c1+""
是将char类型转化为String类型,然后与空字符串拼接;然后再转换后一个char为String,并再次拼接。 - 后面三、四句可以按照同样的方式进行理解。
- 不管是int还是char(其他基本类型也一样),转为String类型时,都相当于通过String.valueOf得到一个新的字符串。
包装类
概述
Java中一切都是对象,但是为了编程的方便还是引入了基本数据类型。同时为了能够将这些基本数据类型当成对象操作,Java为每一个基本数据类型都引入了对应的包装类(wrapper class)。
基本类型 | 二进制位数 | 包装类 | 基本类型 | 二进制位数 | 包装类 | |
---|---|---|---|---|---|---|
byte | 8 | Byte | boolean | 1 | Boolean | |
short | 16 | Short | char | 16 | Character | |
int | 32 | Integer | float | 32 | Float | |
long | 64 | Long | double | 64 | Double |
注意:Java中的基本数据类型只有以上8个,除了基本类型(primitive type),剩下的都是引用类型(reference type),所以包装类也属于引用类型。
自动装箱/拆箱
从Java5开始,引入泛型的概念,并同时引入了自动装箱/拆箱机制。引入该机制的目的是为了简化开发者的操作,使二者可以相互转换。
举个栗子:
首先我们要知道,对于一个整型字面量,java是会将其视为int类型;
// 声明并初始化一个基本数据类型
int i = 1;
// 声明并初始化一个对象
Integer integer = new Integer(2);
// 自动装箱机制
Integer integer2 = 2;
当然这种例子,在我们日常开发中,通常是不会出现的。我们一般遇到的开发场景是什么呢?
集合(比如List、Map、Set)
下面是我们日常开发中最为常见的,自动装箱的应用场景,以List为例:
List<Integer> list = new ArrayList<>();
// 如果没有自动装箱
list.add(new Integer(1));
// 有自动装箱
list.add(2);
自动拆箱跟自动装箱相反,接上面:
// 如果没有自动拆箱
Integer integer = list.get(0);
for(Integer integer2 : list){
...
}
// 有自动拆箱
int i = list.get(0);
for(int t : list){
...
}