news 2026/9/27 22:51:36

Java基础:字符集和IO流

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java基础:字符集和IO流

Java IO 学习笔记

一、字符集、编码和解码

1. 三种常见字符集(必考)

字符集汉字字节英文/数字字节特点
ASCII不支持汉字1 字节只有字母、数字、符号
GBK2 字节1 字节Windows 默认中文编码
UTF-83 字节1 字节互联网通用、项目最常用
两个核心结论
  • 乱码根本原因:编码、解码使用的字符集不一致
  • 英文数字几乎不会乱码:GBK、UTF-8 都兼容 ASCII

2. 编码 & 解码 核心概念

  • 编码:字符 → 字节(往外传输、存储)
  • 解码:字节 → 字符(读取展示)
编码方法(String → byte[])
  • byte[] getBytes():使用系统默认字符集编码(不推荐)
  • byte[] getBytes("字符集"):指定字符集编码(推荐)
解码方法(byte[] → String)
  • new String(byte[]):系统默认字符集解码
  • new String(byte[], "字符集"):指定字符集解码(推荐)

3. 演示代码

importjava.util.Arrays;publicclassCharSetDemo{publicstaticvoidmain(String[]args)throwsException{Stringstr="中国";// UTF-8 编码:汉字3字节byte[]bytesUtf8=str.getBytes("UTF-8");System.out.println("UTF-8 字节:"+Arrays.toString(bytesUtf8));// GBK 编码:汉字2字节byte[]bytesGbk=str.getBytes("GBK");System.out.println("GBK 字节:"+Arrays.toString(bytesGbk));// 编码GBK、解码UTF-8 → 乱码StringbadStr=newString(bytesGbk,"UTF-8");System.out.println("乱码结果:"+badStr);// 编码、解码一致 → 正常StringgoodStr=newString(bytesGbk,"GBK");System.out.println("正确结果:"+goodStr);}}

4. 相关问题

问:为什么英文、数字一般不会乱码?

UTF-8、GBK 都向下兼容 ASCII,英文字节完全一致。

问:UTF-8 和 GBK 汉字占用字节?

UTF-8 汉字 3 字节,GBK 汉字 2 字节。

问:乱码产生的根本原因?

编码时使用的字符集与解码时使用的字符集不统一。

问:Java 如何手动完成编码解码?

编码调用String.getBytes(指定字符集);解码调用new String(字节数组,指定字符集),二者字符集必须一致。

二、IO流作用读写文件中的数据。

本质:把数据从一个地方搬运到另一个地方,流就是数据传输的通道

  1. 输入 Input:读数据 → 从外部(文件、网络、内存)读到程序内存
  2. 输出 Output:写数据 → 把程序内存的数据写到外部(文件、网络、控制台)

三、四大抽象父类(按数据类型+流向划分)

口诀:输入读,输出写;Stream字节,Reader/Writer字符

类型父类作用适用场景
字节输入流InputStream读字节数据(文件→程序)所有文件:文本、图片、视频、音频
字节输出流OutputStream写字节数据(程序→文件)所有文件
字符输入流Reader读字符数据(文件→程序)仅文本文件,自动处理中文
字符输出流Writer写字符数据(程序→文件)仅文本文件

区分技巧

  • 类名以Stream结尾 → 字节流
  • 类名以Reader/Writer结尾 → 字符流
  • 输入:数据流向程序(读)
  • 输出:数据从程序向外(写)

四、文件节点流(最常用实现类)

1. 字节流

  • FileInputStream:文件字节输入流,读文件字节
  • FileOutputStream:文件字节输出流,写文件字节

字节流万能,任何类型文件都可以复制。

2. 字符流

  • FileReader:文件字符输入流,读取文本
  • FileWriter:文件字符输出流,写出文本

只适合文本文件,读写中文更方便。

五、核心方法

✅ 字节输入流 FileInputStream

  • int read():读取1个字节,返回字节;读到末尾返回-1
  • int read(byte[] buf):读取一批字节存入字节数组,返回读到的字节数量;末尾-1
  • byte[] readAllBytes()一次性读取全部字节,返回字节数组(JDK11+,适合小文件)
  • void close()关闭流,释放操作系统资源,必须调用

读取中文注意事项

  1. UTF-8 中文占 3 字节,GBK 中文占 2 字节;单个字节循环读取会拆分汉字,乱码。
  2. 解决方案:一次性读取全部字节,new String(bytes,"UTF-8")转字符串。
  3. 缺点:readAllBytes()/ 定义和文件等大数组读取,大文件会内存溢出 OOM。大文件要用字节数组分段循环读取。
// 根据文件路径创建输入流FileInputStreamfis=newFileInputStream("a.txt");// 根据File对象创建FileInputStreamfis=newFileInputStream(newFile("a.txt"));注意:文件必须存在,否则抛出 `FileNotFoundException`-----------------------------------------------FileInputStreamfis=newFileInputStream("a.txt");byte[]buffer=newbyte[1024];// 缓冲区 ,定义一个字节数组用于每次读取字节intlen;//定义一个变量记住每次读取了多少个字节while((len=fis.read(buffer))!=-1){Strings=newString(buffer,0,len);//把读取到的字节数组转换成字符串输出System.out.print(s);}fis.close();------------------------------------------------byte[]bytes=is.readAllBytes();//一次性读完所有的字节Strings=newString(bytes);

✅ 字节输出流 FileOutputStream

  • void write(int b):写入1个字节
  • void write(byte[] b):写入整个字节数组
  • void write(byte[] b, off, len):写入数组一部分(off起始索引,len长度)

⚠️ FileOutputStream 默认覆盖原有文件;构造器第二个参数传true代表追加写入,不覆盖
new FileOutputStream("a.txt", true)

// 覆盖模式:文件不存在自动创建;存在则清空原有内容FileOutputStreamfos=newFileOutputStream("a.txt");// 追加模式:第二个参数true,文件末尾追加,不清空旧内容FileOutputStreamfos=newFileOutputStream("a.txt",true);-------------------------------------------------//中文字符串转字节数组:`str.getBytes("UTF-8")`Stringstr="你好";//写中文fos.write(str.getBytes(StandardCharsets.UTF_8));

✅ 字符流 FileReader / FileWriter

方法和字节流几乎一样,只是数组是char[]。

⚠️字符输出流必须刷新/关闭:flush() 刷新缓冲区;close()关闭同时自动flush。不刷新数据留在缓冲区写不到文件。
文件字符输入流 FileReader(读文本文件):字符流只能操作文本文件,自动按编码解码,读取中文不会乱码。底层还是字节流 + 编码。

六、异常处理(重点)

JDK7推荐try-with-resources

实现AutoCloseable接口的流,可以写在try()里,代码执行完毕自动关闭流,不用手动写finally。

try(FileInputStreamfis=newFileInputStream("a.txt")){//读写代码}catch(Exceptione){e.printStackTrace();}

七、缓冲流(包装流,提速!)

黑马视频

缓冲流也叫高效流、包装流,本身不能直接操作文件,需要包裹节点流,对原始字节 / 字符流的增强
核心目的:减少磁盘 IO 次数,提升读写性能。

【方案1:普通字节流,数组1KB】
D盘文件 --(每次读1KB,频繁磁盘IO)–> FileInputStream --> byte[1KB]数组(内存) --> 应用程序
✅ 特点:IO次数多,速度最慢

【方案2:普通字节流,数组8KB】
D盘文件 --(每次读8KB,IO次数变少)–> FileInputStream --> byte[8KB]数组(内存) --> 应用程序
✅ 特点:比1KB快;没有预读,你要多少才读多少

【方案3:BufferedInputStream缓冲流(自带8KB缓冲区)】推荐
D盘文件 --(一次性预读满8KB到内置缓冲区)–> FileInputStream --> 缓冲流内置8KB缓存池(内存) --> 应用程序
✅ 特点:预加载,后续read直接读内存,磁盘IO最少,性能最优

字节缓冲流 :
BufferedInputStream字节缓冲输入流
BufferedOutputStream字节缓冲输出流

字符缓冲流:
BufferedReader字符缓冲输入流( 特有方法:String readLine()读取一行文本,读到末尾返回 null)
BufferedWriter字符缓冲输出流(特有方法:newLine()跨平台换行)

缓冲流缓冲大小包装原始流特有方法适用文件类型
BufferedInputStream8192 字节FileInputStream无任意文件
BufferedOutputStream8192 字节FileOutputStream无任意文件
BufferedReader8192 字符FileReaderreadLine() 读一行仅文本
BufferedWriter8192 字符FileWriternewLine() 换行仅文本

重点:缓冲流只是加速,底层还是原来的流。关闭包装流时,内部包裹的流会自动关闭。

✅ 缓冲流属于包装流,不能直接对接文件,需要包装原始流(FileInputStream/FileOutputStream)。
✅ 缓冲区默认大小:8192 字节(8KB),可以在构造方法自定义缓冲区大小。
✅ 关闭资源:只关闭外层缓冲流即可,内部被包装的流会自动关闭。
✅ 性能核心:减少磁盘 IO 操作次数,IO 操作最耗时的就是硬盘读写。

配套的缓冲字节输出流BufferedOutputStream:数据先写到内存缓冲区,缓冲区满了才一次性写入硬盘;也可以手动flush()强制刷新缓冲区。

publicclassTest{publicstaticvoidmain(String[]args){//需求:把《出师表》的文章顺序进行恢复到一个新文件中。try(//1. 定义一个(高级流)缓存字符输入流管道与源文件接通。BufferedReaderbr=newBufferedReader(newFileReader("src\\main\\resources\\a.txt"));//5. 定义一个缓存字符输出管道与目标文件接通。BufferedWriterbw=newBufferedWriter(newFileWriter("src\\main\\resources\\a01.txt"))){//2. 定义一个 List 集合存储读取的每行数据。ArrayList<String>lists=newArrayList<>();//3. 按照读取行数,存入到文件中去,定义一个循环按照行读取数据,存入到 List 集合中去。Stringline;while((line=br.readLine())!=null){lists.add(line);}// 4. 对List集合中的每行数据按照首字符编号升序排序。Collections.sort(lists);//只能处理个位数的序号Collections.sort(lists,newComparator<String>(){//重写排序规则@Overridepublicintcompare(Strings1,Strings2){// 按 . 分割,取出前面的数字部分StringnumStr1=s1.split("\\.")[0];StringnumStr2=s2.split("\\.")[0];intnum1=Integer.parseInt(numStr1);intnum2=Integer.parseInt(numStr2);returnInteger.compare(num1,num2);}});System.out.println(lists);//6. 遍历 List 集合中的每个元素,用缓冲输出管道写出并换行。for(Stringlist:lists){bw.write(list);bw.newLine();//换行}}catch(Exceptione){e.printStackTrace();}}}

拓展 : InputStreamReader字符输入转换流(读)

作用:解决文件编码和程序默认编码不一致时,字符流读取文本乱码问题。
public InputStreamReader(InputStream is)把原始的字节输入流,按照代码默认编码转成字符输入流(效果等价 FileReader)
public InputStreamReader(InputStream is , String charset)把原始的字节输入流,按照指定字符集编码转成字符输入流(⭐重点)

OutputStreamWriter 字符输出转换流(写)
作用:把程序中的字符,按照指定编码转成字节写入文件,控制写出文件的编码格式。
public OutputStreamWriter(OutputStream os)字符按 JVM 默认编码转为字节输出流等价 FileWriter,不推荐自定义编码场景
public OutputStreamWriter(OutputStream os, String charset)字符按指定字符集转为字节输出流 ⭐重点控制输出文件编码为 GBK/UTF-8

  1. ✅ 身份:InputStreamReader / OutputStreamWriter属于字符流,不是字节流,只是包装字节流。
  2. ✅ FileReader =new InputStreamReader(new FileInputStream(file), 默认编码)
  3. ✅ 编码必须和文件真实编码一致,否则依然乱码。
  4. ✅ 关闭流:只需要关闭外层转换流 / 缓冲流,内部包装的字节流会自动关闭。
  5. ✅ OutputStreamWriter 写出数据,数据先放缓冲区;flush()/close()才会刷入磁盘。
  6. ❌ 误区:转换流可以提高读写速度。

转换流不提升性能,它的唯一职责是编码转换;想要提速要搭配 BufferedReader/BufferedWriter。

八、打印流 PrintStream & PrintWriter

一、打印流概述

打印流包含两个类:PrintStream、PrintWriter

  • 作用:便捷、高效输出数据,打印啥,输出就是啥
  • System.out本质就是PrintStream对象,控制台输出就是打印流

✅ 共同点:
打印API使用方式完全一样,都拥有print()/println(),可以输出任意类型数据。

二、两个打印流区别

类名流类型继承特点
PrintStream字节打印流OutputStream底层字节流,支持写字节数据
PrintWriter字符打印流Writer底层字符流,支持写字符,文本输出优先推荐

三、PrintStream 构造器

构造器说明
public PrintStream(OutputStream/File/String)打印流直接通向字节输出流 / 文件 / 文件路径
public PrintStream(String fileName, Charset charset)指定写出的字符编码
public PrintStream(OutputStream out, boolean autoFlush)开启自动刷新
public PrintStream(OutputStream out, boolean autoFlush, String encoding)自动刷新 + 指定字符编码(重点)

四、PrintStream 核心方法

方法说明
println(任意类型)打印任意类型数据,自带换行
print(任意类型)打印任意类型数据,不带换行
write(int / byte[] / byte[]部分)继承父类,支持写字节数据

⭐重点区分:

  • println(97):原样输出字符串97
  • write(97):输出ASCII码对应的字符a

五、PrintWriter 构造器(字符打印流)

// 1. 直接传入文件路径,指定编码PrintWriterpw=newPrintWriter("test.txt","UTF-8");// 2. 包装字符输出流,开启自动刷新PrintWriterpw=newPrintWriter(newFileWriter("test.txt"),true);

打印流优势

  1. 使用简单,支持直接输出任意类型(int、double、boolean、String 等),原样打印
  2. 性能高效
  3. 支持自动刷新 autoFlush
  4. 可以自定义输出文件的字符编码
  5. 打印方法不强制抛出 IO 异常,内部自行捕获处理

九、IO 流选择原则(必背)

  1. 如果是图片、视频、音频:只能用字节流
  2. 如果是纯文本文件:字节流 / 字符流都可以,优先字符流(中文友好)
  3. 文件复制:一律推荐字节流(万能)

十、常见坑

  1. 判断文件读取结束标志:返回 -1(字节流) /null(BufferedReader 的 readLine),不要用判断!=0
  2. FileWriter 不调用 flush/close,数据丢失
  3. 输出流默认覆盖文件,追加需要开启追加开关 true
  4. 字符流不能复制图片视频!会损坏文件
  5. 流用完必须关闭,释放操作系统资源;try-with-resources 自动关闭

十一、相关问题

问:IO 流分为哪四大类?各自作用?

字节输入流InputStream(读字节)
字节输出流OutputStream(写字节)
字符输入流Reader(读字符)
字符输出流Writer(写字符)
字节流支持所有文件,字符流只支持文本文件。

问 :字节流和字符流区别?

字节流操作字节,可以处理任意文件;字符流操作字符,仅文本文件,自动处理中文。

问:缓冲流作用?

增加缓冲区,减少磁盘 IO 次数,提升读写性能;BufferedReader 有 readLine 读一行,BufferedWriter 有 newLine 换行。

问:try-with-resources 作用?

自动关闭实现 AutoCloseable 接口的流,简化代码,避免忘记关闭流。

问 : 缓冲流为什么快

减少磁盘 IO 次数。磁盘读写速度远低于内存,缓冲流批量加载数据到内存缓冲区,程序直接读写内存,不用频繁访问硬盘。

问:缓冲流关闭的时候,只关外层吗?

是的。缓冲流的 close () 内部会自动调用被包装流的 close (),只需要关闭缓冲流即可。

问:字符缓冲流缓冲区是 8192 字符,等于 8KB 吗?

不等于。char 占 2 字节,8192 字符占用内存为 16KB;字节缓冲流 8192 字节就是 8KB。

问:flush () 和 close () 的区别?

flush ():把缓冲区数据刷入磁盘,流还能继续读写;
close ():底层自动执行 flush (),然后关闭流资源,关闭之后流不能继续使用。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/27 22:51:35

密码学系列 - 后量子密码 - (3)同源ECC_vs_PQC

同源ECC: 基于椭圆曲线的同源密码 传统 ECC 和同源密码虽然都用椭圆曲线&#xff0c;但安全性依赖的问题完全不同。同源 ECC 理论上可以抗量子 PQC: 后量子密码 注意: 上一代的同源密码SIDH/SIKE 已经被攻破&#xff0c;不能作为抗量子方案使用。 1. 不同方案的抗量子性 方案数…

作者头像 李华
网站建设 2026/9/27 22:51:20

和一位假期值守数字人直播间的运营聊了两个小时

假期第二天下午&#xff0c;我和阿茴约在她店里聊了两个小时。她的汉服体验馆开在古城景区边上&#xff0c;假期是客流高峰&#xff0c;可她的重心反而全在直播间上——因为到店的客人再多&#xff0c;也多不过线上那间挂着数字人的直播间。 阿茴的情况在中小商家里有代表性&am…

作者头像 李华
网站建设 2026/9/27 22:50:47

第 22-3 篇:vllm_mgr.py——进程守护与看门狗

上一篇&#xff1a;22-2《/admin API——状态、配置、加载、转换》&#xff5c; 下一篇&#xff1a;23-1《国密 SM3——KAT 红线先于实现》 只读验证篇&#xff1a;本文仅板端 --help/status 只读实测&#xff1b;未对运行中的引擎演示 start/stop/kill&#xff08;诚实边界见…

作者头像 李华
网站建设 2026/9/27 22:50:33

一文搞懂网站图标的制作h1优化代码报价单

一文搞懂网站图标的制作h1优化代码报价单 网站做好了没人访问,这大概是每个站长最头疼的事。你花了钱做页面,盯着后台数据,发现流量像死水一样不动,甚至连搜索引擎收录都慢吞吞的。这时候,很多人会忽略一个细节:你的H1标签和图标(Favicon)配合得好不好?别笑,这真不是小事。今天这篇,咱们就一文搞懂网…

作者头像 李华
网站建设 2026/9/27 22:50:20

专注高密网站建设选哪家好

高密网站建设选哪家好?从零搭建到SEO霸屏实战指南 自己不会代码想做网站,却总被各种技术名词劝退?别慌。很多高密本地的老板和项目负责人都卡在这一步:想做个像样的企业官网或商城,怕被外包公司坑,又怕自己从零搭建太折腾。其实,只要搞懂逻辑,专注高密网站建设这事儿没那么复杂。…

作者头像 李华