Java中的字节和字符
Posted zhengshuangxi
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java中的字节和字符相关的知识,希望对你有一定的参考价值。
最近在看Java中的IO相关知识,发现对字节和字符的理解还不够。写篇总结记录一下。
一、字节
所谓字节(Byte),是计算机数据存储的一种计量单位。一个二进制位称为比特(bit),8个比特组成一个字节,也就是说一个字节可以用于区分256个整数(0~255)。由此我们可以知道,字节本是面向计算机数据存储及传输的基本单位,后续的字符也就是以字节为单位存储的,不同编码的字符占用的字节数不同。
那么在Java中,除了存储的意义外,Java还将字节Byte作为一种基本数据类型,该数据类型在内存中占用一个字节,用于(-128~127)范围内的整数
byte a = -128; byte b = 127;
总的来说,字节在Java中有两种含义:
- 存储的单位
- Java的数据类型,用于表示-128~127范围的整数
二、字符
计算机底层存储的是字节,字符的设计则是用于展示符号。屏幕上显示的各种文字,数字,符号等就是解码的字符。所以我们说字符是用来显示的符号,它将存储的字节转换成人们看得懂的符号,因此字符的核心就是定义字节与展示符号之间的关系,这种映射关系通常也叫做编码。
2.1、编码的由来
为什么要编码呢?前面我们知道数据都是以字节为单位存储在计算机中,字节可以区分256个整数,最容易想到的就是将这256个整数定义为256种状态并分别对应256个字符。但是人类符号太多了,256种是不够的。所以人们想到将多个字节合并起来表示人类语言符号,编码的问题就转化成了字节的组合问题。
2.2、编码的常见格式
如今有很多编码格式,常见的如ASCII、ISO-8859-1、GB2312、GBK、UTF-8、UTF-16等等。
ASCII编码是最基础的编码格式,标准的ASCII码一共有128个,占用字节的低7位,将英语系语种的符号都能覆盖住,但是总的来说能表示的字符还是非常有限。
ISO-8859-1编码是ASCII编码的一种扩展,它用了字节的8位,能表示256种字符,且向下兼容ASCII,包含了绝大多数的西欧符号。
GB2312是双字节编码,意味着它使用两个字节来表示符号,包含有6763个汉字。
GBK是GB2312的一个扩展,也是双字节编码,能够表示21003个汉字,且向下兼容GB2312。
...
编码的规范越来越多,不同语言的国家都定义了自己的语言符号编码标准,一时间编码标准百花齐放,在互联网的时代里交流十分不便,不同编码体系之间的信息交流都需要采用不同的解码方案,不然就会出现乱码的现象。于是国际标准化组织ISO制定了一个能够容纳世界上所有文字和符号的字符编码方案Unicode。Unicode是一个字符集,它规定了人类所有字符对应的二进制数,至于这个二进制数怎么存储则是由开发者来进行实现。其中比较流行的实现是UTF-8和UTF-16,还有一种UTF-32。
UTF-32编码使用4个字节,也就是32位二进制存储Unicode字符,效率高但是空间浪费。
UTF-8编码是一种变长的编码方式,它使用1~6个字节来存储,对于英语系的字符使用一个字节,向下兼容ASCII,对于汉字则使用两个字节,依次类推,这样就能够节省一定的空间。
UTF-16编码是介于两者之间的一种编码方式。对于部分字符采用2个字节,另一部分字符采用4个字节。因此UTF-16无法兼容ASCII。
在平时的使用中,UTF-8的使用还是比较多,就是由于它既能向下兼容ASCII,还能够在一定程度上节省空间。
2.3、Java IO流中的编码和解码
Java中是如何进行编码和解码的呢?我们知道,编码/解码的过程主要是发生在字符与字节之间转换的过程。在展示字符的时候,我们将内存中的字节解码成符号,在存储或者传输文件时,我们将字符编码位字节数据。
解码
解码的过程是将字节转换为字符,也就是我们在读取文件或者网络数据的过程。
在java中,我们通过FileReader读取文件数据,FileReader继承自InputStreamReader。在InputStreamReader中使用了解码器StreamDecoder。
// InputStreamReader.java import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; import sun.nio.cs.StreamDecoder; public class InputStreamReader extends Reader // 解码器,按照指定编码方式将字节转换成字符 private final StreamDecoder sd; // 通过dec指定解码器使用的编码方案 public InputStreamReader(InputStream in, CharsetDecoder dec) super(in); if (dec == null) throw new NullPointerException("charset decoder"); sd = StreamDecoder.forInputStreamReader(in, this, dec); // 读字符,以int形式(4字节)返回字符 public int read() throws IOException return sd.read();
通过上述InputStreamReader源码我们可以知道:
- 读取输入流时,通过StreamDecoder完成字节到字符的转换
- 可以通过构造方法来设置编码方案
- 读取的字符以int型数据返回,即4个字节
另外,上述列举只是源码的一部分,我们设置编码方案有很多种形式,如在构造方法种传入编码方式的String类型名称、传入CharSet类型的字符集以及上述的CharsetDecoder类型的字符解码方式。如果不传入编码方案,则默认为当前环境的编码方案。
编码
与解码类似,在存储文件或者写入数据的时候,我们将字符转换为字节,写入文件或者网络。
在java种,我们通过FileWriter来写入文件,FileWriter继承自OutputStreamWriter。在OutputStreamWriter种使用了编码器StreamEncoder。
// OutputStreamWriter.java import java.nio.charset.Charset; import java.nio.charset.CharsetEncoder; import sun.nio.cs.StreamEncoder; public class OutputStreamWriter extends Writer // 编码器,按照指定编码方式将字符转换成字节 private final StreamEncoder se; // 通过enc指定编码方案 public OutputStreamWriter(OutputStream out, CharsetEncoder enc) super(out); if (enc == null) throw new NullPointerException("charset encoder"); se = StreamEncoder.forOutputStreamWriter(out, this, enc); // 写字符,写入的字符以int类型传入 public void write(int c) throws IOException se.write(c);
通过源码我们可以知道:
- 写入输出流时,通过StreamEncoder完成字符到字节的转换
- 通过构造方法指定编码方案
- 写入的字符都是int类型
以上是关于Java中的字节和字符的主要内容,如果未能解决你的问题,请参考以下文章