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类型

原文地址:https://www.cnblogs.com/zhengshuangxi/p/11057972.html

时间: 2024-10-28 07:16:21

Java中的字节和字符的相关文章

c#与java中byte字节的区别及转换方法

原文:c#与java中byte字节的区别及转换方法 在java中  byte的范围在 [-128,127] 在C#中  byte的范围在 [0,255] 所以 java程序与C#程序 进行数据传输的时候 要先把java的byte数组转换成在[0,255]范围内的int型数组a[];再把a[]进行加密得到字符串str, 把字符串传到web服务上. 转换方法: int data[] = new int[bytes.length]; for(int i=0;i data[i] = bytes[i] &

Java中根据字节截取字符串

一.简介 为了统一世界各国的字符集,流行开了Unicode字符集,java也支持Unicode编码,即java中char存的是代码点值,即无论是'A'还是'中'都占两个字节. 代码点值:与Unicode编码表中字符相对应的代码值: 代码单元:就是java中的一个char,可一个以认为是字符编码的基本单元 二.根据字节截取字符串的代码实现 public String getSubString(String str, int length) throws Exception { int i; int

Java IO 接口--- 字节操作 字符操作 磁盘操作 网络操作

1.IO类库的基本结构 基于字节操作的IO接口分别是InputStream和OutputStream,InputStream的类结构图如下所示: 同InputStream类似,OutputStream类也有着相同的类结构图. 关于各个子类的使用可以参考JDK 的 API 说明文档,这里我们需要注意的是:操作数据的方式是可以组合的,如下所示: InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStrea

整理 JAVA中的IO流 (字符流和字节流两个大类)

java中的io流分为两类,字符和字节: OutputStream和InputStream字节流的父类,抽象.OutputStream有两个提供了实现的接口closable和flushable. Writer和Reader字符流的父类,抽象.实际上在流的操作中,底层与文件进行读写的都是字节流,因为即使是字符流的对象,其最终实现读写的也是用的字节流. 操作文件的字节子类FileOutputStream和FileInputStream.记住,这里面写出和读入的都是字节. class useByteS

java中的字节缓冲区ByteBuffer

一.概述:字节缓冲区 类结构: java.lang.Object java.nio.Buffer java.nio.ByteBuffer 类声明: public abstract class ByteBuffer extends Buffer implements Comparable<ByteBuffer> 此类针对字节缓冲区定义了以下六类操作: 读写单个字节的绝对和相对 get 和 put 方法: 将此缓冲区的连续字节序列传输到数组中的相对批量 get 方法: 将 byte 数组或其他字节

Java中如何输入一个字符

今天在QQ群上看见有人问如何在Java中输入一个字符的问题.查了下有以下三种方法吧 char c = new java.util.Scanner(System.in).next().charAt(0); 这算是最常用的了吧,实际上就是输入字符串后再利用charAt(0)得到 char c = new java.util.Scanner(System.in).next().toCharArray[0]; 勉强算得上第二种吧!我以前很常用的.可以用,但是毕竟不好,浪费资源,又没有第一种简单. cha

Java中如何判断一个字符是否是字母或数字

使用Java中Character类的静态方法: Character.isDigit(char c) //判断字符c是否是数字字符,如‘1’,‘2’,是则返回true,否则返回false   Character.isLowerCase(char c) || Character.isUpperCase(char c) //判断c是否是字母字符,前面LowerCase是小写,后面UpperCase是大写,是返回True,否则返回False Character.isLetterOrDigit(char

Java中字节流如何转字符流,OutputStreamWriter用法

OutputStreamWriter 将字节流转换为字符流.是字节流通向字符流的桥梁.如果不指定字符集编码,该解码过程将使用平台默认的字符编码,如:UTF-8: 步骤: 1.创建流 子类对象  绑定数据目的. 1 FileOutputStream fos = new FileOutputStream("c:\\utf.txt"); 2.将字节转为字符,并声明utf-8格式(万国码). 1 OutputStreamWriter sow = new OutputStreamWriter(f

JAVA笔记12__字节、字符缓冲流/打印流/对象流/

/** * !!:以后写流的时候一定要加入缓冲!! * 对文件或其它目标频繁的读写操作,效率低,性能差. * 缓冲流:好处是能更高效地读写信息,原理是将数据先缓冲起来,然后一起写入或读取出来. * * BufferedInputStream:字节缓冲流(有一个内部缓冲区数组,用于缓冲数据) */ public class Main { public static void main(String[] args) { input(); output(); } /** * 使用字节缓冲流进行读取操作