Java中的数据类型分为基本数据类型和引用类型两大类

计算机中的二进制表示

在计算机中,所有的内容都是二进制形式表示。十进制是以10为进位,如9+1=10;二进制则是满2进位(因为我们的计算机是电子的,电平信号只有高位和低位,你也可以暂且理解为通电和不通电,高电平代表1,低电平代表0,由于只有0和1,因此只能使用2进制表示我们的数字!)比如1+1=10=2^1+0,一个位也叫一个bit,8个bit称为1字节,16个bit称为一个字,32个bit称为一个双字,64个bit称为一个四字,我们一般采用字节来描述数据大小。
十进制的7 -> 在二进制中为 111 = 2^2 + 2^1 + 2^0
现在有4个bit位,最大能够表示多大的数字呢?

  • 最小:0000 => 0
  • 最大:1111 => 23+22+21+20 => 8 + 4 + 2 + 1 = 15

在Java中,无论是小数还是整数,他们都要带有符号(和C语言不同,C语言有无符号数)所以,首位就作为我们的符号位,还是以4个bit为例,首位现在作为符号位(1代表负数,0代表正数):

  • 最小:1111 => -(22+21+2^0) => -7
  • 最大:0111 => +(22+21+2^0) => +7 => 7

现在,我们4bit能够表示的范围变为了-7~+7,这样的表示方式称为原码。

计算机中的加减法

原码

虽然原码表示简单,但是原码在做加减法的时候,很麻烦!以4bit位为例:

1+(-1) = 0001 + 1001 = 怎么让计算机去计算?(虽然我们知道该去怎么算,但是计算机不知道!)

我们得创造一种更好的表示方式!于是我们引入了反码:

反码

  • 正数的反码是其本身
  • 负数的反码是在其原码的基础上, 符号位不变,其余各个位取反

经过上面的定义,我们再来进行加减法:

1+(-1) = 0001 + 1110 = 1111 => -0 (直接相加,这样就简单多了!)

思考:1111代表-0,0000代表+0,在我们实数的范围内,0有正负之分吗?

  • 0既不是正数也不是负数,那么显然这样的表示依然不够合理!

补码

根据上面的问题,我们引入了最终的解决方案,那就是补码,定义如下:

  • 正数的补码就是其本身 (不变!)
  • 负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1. (即在反码的基础上+1)

其实现在就已经能够想通了,-0其实已经被消除了!我们再来看上面的运算:

1+(-1) = 0001 + 1111 = (1)0000 => +0 (现在无论你怎么算,也不会有-0了!)

所以现在,4bit位能够表示的范围是:-8~+7(Java使用的就是补码!)


整数类型

整数类型是最容易理解的类型!既然我们知道了计算机中的二进制数字是如何表示的,那么我们就可以很轻松的以二进制的形式来表达我们十进制的内容了。

在Java中,整数类型包括以下几个:

  • byte 字节型 (8个bit,也就是1个字节)范围:-128~+127
  • short 短整形(16个bit,也就是2个字节)范围:-32768~+32767
  • int 整形(32个bit,也就是4个字节)最常用的类型!
  • long 长整形(64个bit,也就是8个字节)最后需要添加l或L

long都装不下怎么办?BigInteger!

数字已经达到byte的最大值了,还能加吗?为了便于理解,以4bit为例:

0111 + 0001 = 1000 => -8(你没看错,就是这样!)

整数还能使用8进制、16进制表示:

  • 十进制为15 = 八进制表示为017 = 十六进制表示为 0xF = 二进制表示 1111 (代码里面不能使用二进制!)

字符类型和字符串

在Java中,存在字符类型,它能够代表一个字符:

  • char 字符型(16个bit,也就是2字节,它不带符号!)范围是0 ~ 65535
  • 使用Unicode表示就是:\u0000 ~ \uffff

字符要用单引号扩起来!比如 char c = ‘淦’;

字符其实本质也是数字,但是这些数字通过编码表进行映射,代表了不同的字符,比如字符'A'的ASCII码就是数字65,所以,char类型其实可以转换为上面的整数类型。

Java的char采用Unicode编码表(不是ASCII编码!),Unicode编码表包含ASCII的所有内容,同时还包括了全世界的语言,ASCII只有1字节,而Unicode编码是2字节,能够代表65536种文字,足以包含全世界的文字了!(我们编译出来的字节码文件也是使用Unicode编码的,所以利用这种特性,其实Java支持中文变量名称、方法名称甚至是类名)

既然char只能代表一个字符,那怎么才能包含一句话呢?(关于数组,我们这里先不了解,数组我们放在面向对象章节讲解)

String就是Java中的字符串类型(注意,它是一个类,创建出来的字符串本质是一个对象,不是我们的基本类型)字符串就像它的名字一样,代表一串字符,也就是一句完整的话。

字符串用双引号括起来!比如:String str = “一日三餐没烦恼”;

小数类型

小数类型比较难理解(比较难理解指的是原理,不是使用)首先来看看Java中的小数类型包含哪些:

  • float 单精度浮点型 (32bit,4字节)
  • double 双精度浮点型(64bit,8字节)

思考:小数的范围该怎么定义呢?我们首先要了解的是小数在计算机里面是如何存放的:

根据国际标准 IEEE 754,任意一个二进制浮点数 V 可以表示成下面的形式:
V = (-1)^S × M × 2^E
(1)(-1)^S 表示符号位,当 S=0,V 为正数;当 S=1,V 为负数。
(2)M 表示有效数字,大于等于 1,小于 2,但整数部分的 1 不变,因此可以省略。(例如尾数为1111010,那么M实际上就是1.111010,尾数首位必须是1,1后面紧跟小数点,如果出现0001111这样的情况,去掉前面的0,移动1到首位;题外话:随着时间的发展,IEEE 754标准默认第一位为1,故为了能够存放更多数据,就舍去了第一位,比如保存1.0101 的时候, 只保存 0101,这样能够多存储一位数据)
(3)2^E 表示指数位。(用于移动小数点)

比如: 对于十进制的 5.25 对应的二进制为:101.01,相当于:1.0101*2^2。所以,S 为 0,M 为 1.0101,E 为 2。所以,对于浮点类型,最大值和最小值不仅取决于符号和尾数,还有它的阶码。我们在这里就不去计算了,想了解的可以去搜索相关资料。

思考:就算double有64bit位数,但是依然存在精度限制,如果我要进行高精度的计算,怎么办?BigDecimal!

布尔类型

布尔类型(boolean)只有truefalse两种值,也就是要么为真,要么为假,布尔类型的变量通常用作流程控制判断语句。(C语言一般使用0表示false,除0以外的所有数都表示true)布尔类型占据的空间大小并未明确定义,而是根据不同的JVM会有不同的实现。

类型转换

隐式类型转换

隐式类型转换支持字节数小的类型自动转换为字节数大的类型,整数类型自动转换为小数类型,转换规则如下:

  • byte→short(char)→int→long→float→double

问题:为什么long比float大,还能转换为float呢?小数的存储规则让float的最大值比long还大,只是可能会丢失某些位上的精度!

所以,如下的代码就能够正常运行:

  1. byte b = 9;
  2. short s = b;
  3. int i = s;
  4. long l = i;
  5. float f = l;
  6. double d = f;
  7. System.out.println(d);
  8. //输出 9.0

显示类型转换

显示类型转换也叫做强制类型转换,也就是说,违反隐式转换的规则,牺牲精度强行进行类型转换。

int i = 128;
byte b = (byte)i;
System.out.println(b);

//输出 -128

为什么结果是-128?精度丢失了!

  • int 类型的128表示:00000000 00000000 00000000 10000000
  • byte类型转换后表示:xxxxxxxx xxxxxxxx xxxxxxxx 10000000 => -128

数据类型自动提升

在参与运算时(也可以位于表达式中时,自增自减除外),所有的byte型、short型和char的值将被提升到int型:

byte b = 105;
b = b + 1;   //报错!
System.out.println(b);

这个特性是由 Java虚拟机规范 定义的,也是为了提高运行的效率。其他的特性还有:

  • 如果一个操作数是long型,计算结果就是long型
  • 如果一个操作数是float型,计算结果就是float型
  • 如果一个操作数是double型,计算结果就是double型