浅谈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中字节与字符的区别的文章就介绍到这了,更多相关java 字节与字符 内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!