ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Java基础:字符集和IO流

Java基础:字符集和IO流 Java IO 学习笔记一、字符集、编码和解码1. 三种常见字符集必考字符集汉字字节英文/数字字节特点ASCII不支持汉字1 字节只有字母、数字、符号GBK2 字节1 字节Windows 默认中文编码UTF-83 字节1 字节互联网通用、项目最常用两个核心结论乱码根本原因编码、解码使用的字符集不一致英文数字几乎不会乱码GBK、UTF-8 都兼容 ASCII2. 编码 解码 核心概念编码字符 → 字节往外传输、存储解码字节 → 字符读取展示编码方法String → byte[]byte[] getBytes()使用系统默认字符集编码不推荐byte[] getBytes(字符集)指定字符集编码推荐解码方法byte[] → Stringnew String(byte[])系统默认字符集解码new String(byte[], 字符集)指定字符集解码推荐3. 演示代码importjava.util.Arrays;publicclassCharSetDemo{publicstaticvoidmain(String[]args)throwsException{Stringstr中国;// UTF-8 编码汉字3字节byte[]bytesUtf8str.getBytes(UTF-8);System.out.println(UTF-8 字节Arrays.toString(bytesUtf8));// GBK 编码汉字2字节byte[]bytesGbkstr.getBytes(GBK);System.out.println(GBK 字节Arrays.toString(bytesGbk));// 编码GBK、解码UTF-8 → 乱码StringbadStrnewString(bytesGbk,UTF-8);System.out.println(乱码结果badStr);// 编码、解码一致 → 正常StringgoodStrnewString(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流作用读写文件中的数据。本质把数据从一个地方搬运到另一个地方流就是数据传输的通道输入 Input读数据 → 从外部文件、网络、内存读到程序内存输出 Output写数据 → 把程序内存的数据写到外部文件、网络、控制台三、四大抽象父类按数据类型流向划分口诀输入读输出写Stream字节Reader/Writer字符类型父类作用适用场景字节输入流InputStream读字节数据文件→程序所有文件文本、图片、视频、音频字节输出流OutputStream写字节数据程序→文件所有文件字符输入流Reader读字符数据文件→程序仅文本文件自动处理中文字符输出流Writer写字符数据程序→文件仅文本文件区分技巧类名以Stream结尾 → 字节流类名以Reader/Writer结尾 → 字符流输入数据流向程序读输出数据从程序向外写四、文件节点流最常用实现类1. 字节流FileInputStream文件字节输入流读文件字节FileOutputStream文件字节输出流写文件字节字节流万能任何类型文件都可以复制。2. 字符流FileReader文件字符输入流读取文本FileWriter文件字符输出流写出文本只适合文本文件读写中文更方便。五、核心方法✅ 字节输入流 FileInputStreamint read()读取1个字节返回字节读到末尾返回-1int read(byte[] buf)读取一批字节存入字节数组返回读到的字节数量末尾-1byte[] readAllBytes()一次性读取全部字节返回字节数组JDK11适合小文件void close()关闭流释放操作系统资源必须调用读取中文注意事项UTF-8 中文占 3 字节GBK 中文占 2 字节单个字节循环读取会拆分汉字乱码。解决方案一次性读取全部字节new String(bytes,UTF-8)转字符串。缺点readAllBytes()/ 定义和文件等大数组读取大文件会内存溢出 OOM。大文件要用字节数组分段循环读取。// 根据文件路径创建输入流FileInputStreamfisnewFileInputStream(a.txt);// 根据File对象创建FileInputStreamfisnewFileInputStream(newFile(a.txt));注意文件必须存在否则抛出 FileNotFoundException-----------------------------------------------FileInputStreamfisnewFileInputStream(a.txt);byte[]buffernewbyte[1024];// 缓冲区 ,定义一个字节数组用于每次读取字节intlen;//定义一个变量记住每次读取了多少个字节while((lenfis.read(buffer))!-1){StringsnewString(buffer,0,len);//把读取到的字节数组转换成字符串输出System.out.print(s);}fis.close();------------------------------------------------byte[]bytesis.readAllBytes();//一次性读完所有的字节StringsnewString(bytes);✅ 字节输出流 FileOutputStreamvoid write(int b)写入1个字节void write(byte[] b)写入整个字节数组void write(byte[] b, off, len)写入数组一部分off起始索引len长度⚠️ FileOutputStream 默认覆盖原有文件构造器第二个参数传true代表追加写入不覆盖new FileOutputStream(a.txt, true)// 覆盖模式文件不存在自动创建存在则清空原有内容FileOutputStreamfosnewFileOutputStream(a.txt);// 追加模式第二个参数true文件末尾追加不清空旧内容FileOutputStreamfosnewFileOutputStream(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(FileInputStreamfisnewFileInputStream(a.txt)){//读写代码}catch(Exceptione){e.printStackTrace();}七、缓冲流包装流提速黑马视频缓冲流也叫高效流、包装流本身不能直接操作文件需要包裹节点流对原始字节 / 字符流的增强核心目的减少磁盘 IO 次数提升读写性能。【方案1普通字节流数组1KB】D盘文件 --(每次读1KB频繁磁盘IO)– FileInputStream -- byte[1KB]数组(内存) -- 应用程序✅ 特点IO次数多速度最慢【方案2普通字节流数组8KB】D盘文件 --(每次读8KBIO次数变少)– FileInputStream -- byte[8KB]数组(内存) -- 应用程序✅ 特点比1KB快没有预读你要多少才读多少【方案3BufferedInputStream缓冲流自带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. 定义一个(高级流)缓存字符输入流管道与源文件接通。BufferedReaderbrnewBufferedReader(newFileReader(src\\main\\resources\\a.txt));//5. 定义一个缓存字符输出管道与目标文件接通。BufferedWriterbwnewBufferedWriter(newFileWriter(src\\main\\resources\\a01.txt))){//2. 定义一个 List 集合存储读取的每行数据。ArrayListStringlistsnewArrayList();//3. 按照读取行数,存入到文件中去,定义一个循环按照行读取数据存入到 List 集合中去。Stringline;while((linebr.readLine())!null){lists.add(line);}// 4. 对List集合中的每行数据按照首字符编号升序排序。Collections.sort(lists);//只能处理个位数的序号Collections.sort(lists,newComparatorString(){//重写排序规则Overridepublicintcompare(Strings1,Strings2){// 按 . 分割取出前面的数字部分StringnumStr1s1.split(\\.)[0];StringnumStr2s2.split(\\.)[0];intnum1Integer.parseInt(numStr1);intnum2Integer.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)把原始的字节输入流按照代码默认编码转成字符输入流效果等价 FileReaderpublic InputStreamReader(InputStream is , String charset)把原始的字节输入流按照指定字符集编码转成字符输入流⭐重点OutputStreamWriter 字符输出转换流写作用:把程序中的字符按照指定编码转成字节写入文件控制写出文件的编码格式。public OutputStreamWriter(OutputStream os)字符按 JVM 默认编码转为字节输出流等价 FileWriter不推荐自定义编码场景public OutputStreamWriter(OutputStream os, String charset)字符按指定字符集转为字节输出流 ⭐重点控制输出文件编码为 GBK/UTF-8✅ 身份InputStreamReader / OutputStreamWriter属于字符流不是字节流只是包装字节流。✅ FileReader new InputStreamReader(new FileInputStream(file), 默认编码)✅ 编码必须和文件真实编码一致否则依然乱码。✅ 关闭流只需要关闭外层转换流 / 缓冲流内部包装的字节流会自动关闭。✅ OutputStreamWriter 写出数据数据先放缓冲区flush()/close()才会刷入磁盘。❌ 误区转换流可以提高读写速度。转换流不提升性能它的唯一职责是编码转换想要提速要搭配 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)原样输出字符串97write(97)输出ASCII码对应的字符a五、PrintWriter 构造器字符打印流// 1. 直接传入文件路径指定编码PrintWriterpwnewPrintWriter(test.txt,UTF-8);// 2. 包装字符输出流开启自动刷新PrintWriterpwnewPrintWriter(newFileWriter(test.txt),true);打印流优势使用简单支持直接输出任意类型int、double、boolean、String 等原样打印性能高效支持自动刷新 autoFlush可以自定义输出文件的字符编码打印方法不强制抛出 IO 异常内部自行捕获处理九、IO 流选择原则必背如果是图片、视频、音频只能用字节流如果是纯文本文件字节流 / 字符流都可以优先字符流中文友好文件复制一律推荐字节流万能十、常见坑判断文件读取结束标志返回 -1字节流 /nullBufferedReader 的 readLine不要用判断0FileWriter 不调用 flush/close数据丢失输出流默认覆盖文件追加需要开启追加开关 true字符流不能复制图片视频会损坏文件流用完必须关闭释放操作系统资源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 ()然后关闭流资源关闭之后流不能继续使用。
返回列表