《Java编程的逻辑》精读二(数据类型,字符编码)

最近在阅读《Java编程的逻辑》一书,受益良多,在此对作者(马俊昌)万分的感谢。

计算机是一个机器,只能处理二进制数据,比如0100 1101。
但直接使用二进制进行编程开发,非常的不友好,所以有了各种高级开发语言,如 CC++C#JavaPHP等。
为了更好的操作二进制数据,高级开发语言大多引入了数据类型的概念,使用不同的类型来表示数据。

基本数据类型

对于 Java 语言来讲,有如下基本数据类型:

1、整数类型

byte

占1个字节,8位二进制,第1位是符号位,取值范围为:-27(即1000 0000) ~ 27-1(即0111 1111)。

byte b = 23;
short

占2个字节,16位二进制,第1位是符号位,取值范围为:-215(即1000 0000 0000 0000) ~ 215-1(即0111 1111 1111 1111)。

short s = 3333;
int

占4个字节,32位二进制,第1位是符号位,取值范围为:-231(即1000 0000 0000 0000 0000 0000 0000 0000) ~ 231-1(即0111 1111 1111 1111 1111 1111 1111 1111)。

int i = 9999;
long

占8个字节,64位二进制,第1位是符号位,取值范围为:-263 ~ 263-1。
整数值默认为int类型,给long类型变量赋值时需要在后面加小写字母 l 或大写字母 L,防止值超出了int的取值范围。

long l = 32323L;

2、浮点数类型

浮点数,即数学中的“小数”。
因为在计算机中表示小数的点不是固定的,而是浮动的,所以被称为“浮点数”。

浮点数在计算机中采用类似十进制的科学计数法来表示的,即m×2n,其中m称为“尾数”,n称为“指数”。
计算机中保存的小数,即保存的m和n。

由此可见,计算机只能精确的表示可以表述为2的几次方的小数(比如1×2-1:0.5、3×2-2:0.75、6×2-3:0.75),其他小数则无法精确的表示(类比一下十进制中的小数只能精确表示10的几次方的小数)。

float

占4个字节,32位二进制,第1位是符号位,中间23位保存尾数,最后8位保存指数。
浮点数的值默认为double类型,给float类型变量赋值时需要在后面加小写字母 f 或大写字母 F

float f = 2.11f;
double

占8个字节,64位二进制,第1位是符号位,中间52位保存尾数,最后11位保存指数。

double d = 2.11;

3、boolean类型

用boolean表示,值只有true或false两种。

boolean b = true;

boolean类型的大小没有明确定义。
下面参考Java中boolean类型到底占用多少个字节?:
单独的boolean类型编译后会用int代替,boolean值占用4个字节(32位二进制)。
boolean类型数组会被编译为byte数组,每个boolean值占用1个字节(8位二进制)。
使用int代替boolean,而不用byte或short的原因是,对于当下32位的处理器(CPU)来说,一次处理数据是32位(这里不是指的是32/64位系统,而是指CPU硬件层面),32 位 CPU 使用 4 个字节是最为节省的,哪怕你是 1 个 bit 他也是占用 4 个字节。因为 CPU 寻址系统只能 32 位 32 位地寻址,具有高效存取的特点。

4、字符(char)类型

用char表示,值是一个字符,中文或英文字符都行。
使用单引号把字符括起来。

char c1 = 'a';
char c2 = '我';

Java内部处理字符时,使用的是Unicode编码,具体编码格式为 UTF-16BE(UTF-16大端表示)。
所以字符类型本质上也是一个整数,占2个字节或4个字节,下面有具体说明。

Unicode编码

Unicode编码为世界上所有字符都分配了一个唯一的数字编号,范围从0x000000~0x10FFFF,前缀使用U+。
Unicode编码并没有规定这个编号怎么和二进制对应,下面是几个主要方案:

  • UTF-32
    这个简单,直接使用数字编号的二进制表示,占4个字节,32位二进制。
    缺点是浪费空间,实际采用比较少。
    这里需要注意字节的排列顺序(字节序):
    大端(Big Endian,BE)
    对应编码:UTF-32BE
    第一个字节是二进制表示中的最高位,最后一个字节是二进制表示中的最低位。
    小端(Little Endian,LE)
    对应编码:UTF-32LE
    第一个字节是二进制表示中的最低位,最后一个字节是二进制表示中的最高位。

  • UTF-16
    使用变长字节表示:
    常用字符集
    在U+0000~U+FFFF之间,即65536个数字之内,用2个字节可表示(16位二进制)。
    U+D800~U+DBFF之间的编号其实没有定义。
    增补字符集
    在U+10000~U+10FFFF之间,用4个字节(32位二进制)表示。
    前两个字节叫高代理项,范围是U+D800~U+DBFF,后两个字节叫低代理项,范围是U+DC00~U+DFFF。
    数字编号与这个二进制之间有一个转换算法。

    区分是2个字节还是4个字节,就看前两个字节的编号范围。
    如果在U+D800~U+DBFF范围内,就是4个字节。
    这里也有字节序的问题,有 UTF-16BEUTF-16LE 两种编码。
    UTF-16常用于系统内部编码,比UTF-32节省空间,但也至少需要2个字节表示,对于美国和西欧国家来说还是有点浪费。

  • UTF-8
    也使用变长字节表示,占用1~4个字节,每个字符的字节个数与字符在Unicode中的编号大小有关。
    对于编号小于128的字符,即ASCII码表中的字符,占用1个字节(8位二进制),最高位固定为0,剩余7位表示字符。
    对于编号大于128的字符,最高位字节(即BE表示中的第一个字节)中有几个连续的1,就表示该字符一共有几个字节(包括最高位字节,最高字节后面的字节都是10开头)。
    UTF-8是兼容ASCII码的,对于大部分中文,一个中文字符需要用3个字节表示(即1110 xxxx 10xx xxxx 10xx xxxx,其中x填补字符的Unicode编号的二进制形式(去掉最高位的0))。

非Unicode编码
  • ASCII(American Standard Code for Information Interchange)
    美国用,占用1个字节(8位二进制),最高位固定为0,剩余7位表示字符。
    数字32~126对应可打印字符,如大小写字母,各种特殊符号(!@#%^&*-+./)等。
    数字0~31和127表示控制字符,如空格,换行符,制表符等。
  • ISO 8859-1 / Latin-1
    西欧用,兼容ASCII,占用1个字节(8位二进制),最高位固定为1,剩余7位表示字符。0~127与ASCII一样,128~255有其他含义。
  • Windows-1252(现在主流使用)
    西欧用,兼容ASCII,与ISO 8859-1基本一样,区别在于数字128~159,增加了欧元等特殊字符。
  • GB2312
    中国用,主要针对简体中文(约7000个)和个别罕见字和繁体字。
    兼容ASCII。
    占用2个字节(16位二进制),最高位固定为1。
    如果最高位是0,按ASCII解析。
    高位字节范围0xA1~0xF7,低位字节范围0xA1~0xFE。
  • GBK
    中国用,向下兼容GB2312,增加了更多的简体字和繁体字,共约约21000个字符。
    兼容ASCII。
    占用2个字节(16位二进制),最高位固定为1。
    高位字节范围0x81~0xFE,低位字节范围0x40~0x7E和0x80~0xFE。
    低位字节从0x40开始,即低位字节最高位可能是0。
    使用高位字节(第一个字节)的最高位是否是1,来区分低位字节是否是汉字的一部分。
    如果第一个字节最高位是0,第一个字节按ASCII解析,然后再解析后面的字节是ASCII还是汉字。
    如果第一个字节最高位是1,则把第一个字节和第二个字节一起解析为一个汉字,解析完,跳到第三个字节解析。
  • GB18030
    中国用,向下兼容GBK、GB2312,增加了少数民族字符,中日韩统一字符,共约76000个字符。
    使用变长字节。
    兼容ASCII。
    对于2字节编码,GBK一摸一样,占用2个字节(16位二进制),高位字节范围0x81~0xFE,低位字节范围0x40~0x7E和0x80~0xFE。
    对于4字节编码,第一个字节范围为0x81~0xFE,第二个字节范围为0x30~0x39,第三个字节范围为0x81~0xFE,第一个字节范围为0x30~0x39。
    解析二进制,通过第二个字节的范围来确定是4字节编码还是2字节编码,因为2字节编码中的第二个字节比4字节编码中的第二个字节大。
  • Big5
    香港、台湾用,针对繁体中文的。
    兼容ASCII。
    占用2个字节(16位二进制),最高位固定为1。
    高位字节范围0x81~0xFE,低位字节范围0x40~0x7E和0xA1~0xFE。

⚠️ 字符乱码的问题,都是读取字符时用的编码与写字符时用的编码不一致造成的。

数组类型

数组中的元素是连续存放的。
数组的长度一旦确定,就不可更改。
基本数据类型的变量,在内存中只有一个对应的内存空间,直接存储变量的值。
而数组类型的变量,在内存中有两个对应的内存空间:一个存放该数组变量的值的起始位置,一个存放该数组变量的值的内容。
类似数组类型存储方式的类型,一般也称为引用类型。

在Java中,许多复杂的类型,都是使用基本数据类型和数组类型组合而成,比如:
String/StringBuilder/StringBuffer:内部使用char数组,所有字符串的操作都是基于该数组的。
ArrayList/ArrayDeque:内部使用泛型数组,所有Array操作都是基于该数组的。
Date:内部使用long值表示距离纪元时的毫秒数,所有时间操作都是基于该毫秒数的。

你可能感兴趣的:(《Java编程的逻辑》精读二(数据类型,字符编码))