OutputStreamWriter分析

本文详细介绍了InputStreamReader和OutputStreamWriter两个类的功能与用法。InputStreamReader用于将字节流转换为字符流,支持指定字符集;而OutputStreamWriter则将字符流转换回字节流,同样支持指定字符集。通过具体实例展示了如何高效地使用这两个类。

写这个类的原因,网上有很多介绍这两个类的,InputStreamReader类的使用,没有任何异议,而OutputStreamWriter,看了网的帖子和博客说的都不是很清楚,所以小皮虾好好研究了一下OutputStreamWriter类,所以下面的总结有不对之处,欢迎指正!
api类中解释:
InputStreamReader 是字节流通向字符流的桥梁, 将字节流转换为字符流
OutputStreamWriter 是字符流通向字节流的桥梁,将字符流转换为字节流
一 、InputStreamReader类
1.引入InputStreamReader类
作用:它可以使用指定的charset 读取字节并将其解码为字符
其构造函数如下:
InputStreamReader (InputStream in)
创建一个使用默认字符集的 InputStreamReader。
InputStreamReader (InputStream in, Charset cs)
创建使用给定字符集的 InputStreamReader。
InputStreamReader (InputStream in, CharsetDecoder dec)
创建使用给定字符集解码器的 InputStreamReader。
InputStreamReader (InputStream in, String charsetName)
创建使用指定字符集的 InputStreamReader。
2.举例说明如下:
使用起来无异议,很容易明白,使用实例如下:
为了达到最高效率,可要考虑在 BufferedReader 内包装 InputStreamReader。
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));

eg:class TransStreamDemo{
   public static void main(String[] args){
      //因为system中的in方法是system的静态方法public static final InputStream                   in
     // 所以可以向上转换为InputStream父类
     InputStream in = System.in;//读取键盘的输入
     //传入对象为in,将字节流转换为字符流Reader的对象isr
     InputStreamReader isr = new InputStreamReader(in);
     //System.in就读取一个字符。为了能一次读取一行使用者的输入,使用了BufferedReader来对使用者输入的字符进行缓冲。readLine()  
     //方法会在读取到使用者的换行字符时,再一次将整行字符串传入,所以使用BufferedReader类,可以提高读取字符的效率
       BufferdReader bufr =new BufferedReader(isr);
       String line = null;
       while((line = bufr.readline())!=null){

           System.out.println(line.toUpperCase);
       }
       bufr.close;
   }
}                   

二、 OutputStreamWriter类
1.引入OutputStreamWriter类
作用:可使用指定的charset 将要写入流中的字符编码成字节
其构造函数如下:
OutputStreamWriter(OutputStream out)
创建使用默认字符编码的 OutputStreamWriter。 OutputStreamWriter(OutputStream out,Charset cs)
创建使用给定字符集的 OutputStreamWriter。 OutputStreamWriter(OutputStream out,CharsetEncoder enc)
创建使用给定字符集编码器的 OutputStreamWriter。 OutputStreamWriter(OutputStream out,String charsetName)
创建使用指定字符集的 OutputStreamWriter。
2.举例说明如下:

eg:public static void transWriteByBuf() throws IOException {  
   //因为system中的in方法是system的静态方法public static final InputStream in
   // 所以可以向上转换为InputStream父类
   InputStream in = System.in;//读取键盘的输入
   //传入对象为in,将字节流转换为字符流Reader的对象isr
   InputStreamReader isr = new InputStreamReader(in);
   //System.in就读取一个字符。为了能一次读取一行使用者的输入,使用了    BufferedReader来对使用者输入的字符进行缓冲。readLine()                       
   //方法会在读取到使用者的换行字符时,再一次将整行字符串传入,所以使用BufferedReader类,可以提高读取字符的效率
   BufferdReader bufr =new BufferedReader(isr);
   //将字符流转换为字节流
   // 因为system中的in方法是system的静态方法public static final OutputStream in,所以可以向上转换为OutputStream父类
   OutputStream out = System.out;//打印到控制台。   
   OutputStreamWriter osr = new OutputStreamWriter(out);//输出      
   BufferedWriter bufw = new BufferedWriter(osr);//缓冲  
   String line = null;
   while((line = bufr.readline())!=null)
   {
    //这个语句很重要,是把读到字符流写到osr字符流转换对象中,转换成字节流打印到控制台因为这是一个写的过程,与InputStreamReader 类相反,所以这个地方如果理解透了,也就真正的理解了OutputStreamWriter 类,而不是网上很多说的OutputStreamWriter 是 将字节流转换为字符流,所以api的解释一般是不会错,有时候是我们自己没有真的的理解。
       bufw.write(line);
       bufw.newline();
       bufw.flush();
       }
       bufr.close;

 }

“`

<think> 我们再次逐行分析给定的代码。代码的功能是重定向标准输入、输出和错误流,然后执行业务逻辑,最后恢复原始流。 注意:我们已经进行了两次分析,这次需要更深入或从不同角度进行解析。 但为了避免重复,我们将从代码的健壮性、可扩展性和潜在问题角度进行深入分析。 </think>### 逐行深度分析(增强版) ```java import java.io.*; import java.nio.charset.StandardCharsets; import java.util.Scanner; ``` - **深度分析**: - `java.io.*`:导入Java I/O核心包,包含: - `InputStream`/`OutputStream`:字节流基类 - `FileInputStream`/`FileOutputStream`:文件字节流实现 - `PrintStream`:格式化输出流 - `StandardCharsets`:Java 7+引入的标准字符集常量类,避免硬编码字符串错误 - `Scanner`:文本扫描器,内部使用正则表达式解析原始数据 - **潜在问题**:通配符导入(`.*`)可能引起命名冲突,生产环境推荐显式导入 --- ```java public class StreamRedirector { ``` - **类设计分析**: - 未声明包名:默认包不推荐,应使用有意义的包结构 - 未实现Closeable接口:应考虑资源自动关闭支持 - **改进建议**: ```java package com.example.stream; public class StreamRedirector implements AutoCloseable { ... } ``` --- ```java public static void main(String[] args) throws Exception { ``` - **异常处理深度分析**: - `throws Exception`是通用声明,但实际可能抛出: - `FileNotFoundException`:文件不存在 - `SecurityException`:安全管理器禁止文件访问 - `UnsupportedEncodingException`:非法编码名称 - **生产级改进**: ```java public static void main(String[] args) { try { // 业务代码 } catch (FileNotFoundException e) { System.err.println("配置文件缺失: " + e.getMessage()); } catch (SecurityException e) { System.err.println("安全限制: " + e.getMessage()); } catch (Exception e) { e.printStackTrace(); } } ``` --- ```java // 1. 备份原始标准流 InputStream originalIn = System.in; PrintStream originalOut = System.out; PrintStream originalErr = System.err; ``` - **系统流特性分析**: - `System.in`:默认是`BufferedInputStream`包装的`FileInputStream`,对应文件描述符0 - `System.out`:默认是`PrintStream`包装的`BufferedOutputStream`,对应文件描述符1 - `System.err`:默认是`PrintStream`包装的`BufferedOutputStream`,对应文件描述符2 - **关键事实**:原始流是JVM启动时初始化的,关闭它们不会关闭底层文件描述符 --- ```java try { // 2. 重定向标准输入 FileInputStream fileInput = new FileInputStream("input.txt"); System.setIn(fileInput); ``` - **文件流深入分析**: - `FileInputStream`构造过程: 1. 通过`FileSystem`获取文件路径 2. 调用原生方法`open0()`打开文件描述符 - **资源泄漏风险**: - 未实现`AutoCloseable`接口管理 - 文件描述符泄漏上限:Linux默认1024/进程 - **改进方案**: ```java try (FileInputStream fileInput = new FileInputStream("input.txt")) { System.setIn(fileInput); // 业务逻辑 } ``` --- ```java // 3. 重定向标准输出 FileOutputStream fileOutput = new FileOutputStream("output.txt"); PrintStream newOut = new PrintStream(fileOutput, true, StandardCharsets.UTF_8.name()); System.setOut(newOut); ``` - **PrintStream机制深度解析**: - 构造函数调用链: ```mermaid graph LR A[PrintStream] --> B[new OutputStreamWriter] B --> C[new StreamEncoder] C --> D[Charset.encode] ``` - `autoFlush=true`时: - 调用`println()`后自动执行`flush()` - 但`print()`方法不会触发自动刷新 - **性能考量**:UTF-8编码比ISO-8859-1慢约30%(需处理多字节字符) --- ```java // 4. 重定向标准错误 FileOutputStream errorOutput = new FileOutputStream("error.log"); PrintStream newErr = new PrintStream(errorOutput, true, StandardCharsets.UTF_8.name()); System.setErr(newErr); ``` - **错误流特殊性质**: - 与标准输出区别: - 默认不缓冲(`PrintStream`内部有缓冲) - 在IDE中常显示为红色文本 - 操作系统层面独立管道 - **跨平台问题**:Windows错误流优先级高于标准输出,可能导致日志顺序混乱 --- ```java // 5. 执行业务方法 executeBusinessLogic(); ``` - **线程安全分析**: - `System.setOut()`修改的是静态变量,影响所有线程 - 多线程环境下需考虑: - 线程A重定向后,线程B的输出也被重定向 - 并发写入同一文件需要同步控制 - **改进方案**: ```java synchronized (System.out) { System.setOut(newOut); // 业务逻辑 } ``` --- ```java } finally { // 6. 还原原始标准流 System.setIn(originalIn); System.setOut(originalOut); System.setErr(originalErr); } ``` - **还原过程深度分析**: - 还原顺序无关紧要,因三者独立 - **潜在缺陷**: - 如果`originalOut`已被关闭,后续输出抛出`IOException` - finally块中发生异常会导致原始流无法恢复 - **防御性编程**: ```java } finally { safeRestore(System.in, originalIn); safeRestore(System.out, originalOut); safeRestore(System.err, originalErr); } private static void safeRestore(PrintStream current, PrintStream original) { try { if (!current.equals(original)) { System.setOut(original); } } catch (Exception e) { // 记录错误但继续执行 } } ``` --- ```java private static void executeBusinessLogic() { try { Scanner scanner = new Scanner(System.in); System.out.println("读取内容: " + scanner.nextLine()); ``` - **Scanner工作机制详解**: 1. 创建`Scanner`时初始化: - 默认缓冲区大小:1024字节 - 使用`java.util.regex.Matcher`进行模式匹配 2. `nextLine()`执行流程: ```mermaid sequenceDiagram Scanner->>+System.in: read() System.in->>+FileInputStream: read() FileInputStream->>+Native: read0() Native-->>-FileInputStream: bytes FileInputStream-->>-System.in: bytes System.in-->>-Scanner: bytes Scanner->>Pattern: match line separator Pattern-->>Scanner: match result Scanner->>String: construct substring ``` - **资源消耗**:每次创建Scanner都新建缓冲区和正则引擎 --- ```java throw new RuntimeException("业务逻辑异常示例"); ``` - **异常构造原理**: - `RuntimeException`创建过程: 1. 填充堆栈跟踪:`native fillInStackTrace()` 2. 初始化详细消息 - **性能影响**:获取堆栈跟踪是昂贵操作(约10μs/调用) --- ```java } catch (Exception e) { e.printStackTrace(); } } } ``` - **printStackTrace()深度解析**: - 实现源码关键片段: ```java public void printStackTrace(PrintStream s) { synchronized (s) { s.println(this); for (StackTraceElement traceElement : getStackTrace()) { s.println("\tat " + traceElement); } } } ``` - **输出格式**: - 首行:异常类名 + 消息 - 后续行:"at" + 类名.方法名(源文件:行号) - **性能陷阱**:在高频异常场景可能导致性能瓶颈 ### 高级应用场景 1. **动态重定向** ```java // 实现按条件切换输出目标 class ToggleOutputStream extends OutputStream { private OutputStream current; void switchTo(OutputStream os) { this.current = os; } @Override public void write(int b) throws IOException { current.write(b); } } // 使用示例 ToggleOutputStream toggle = new ToggleOutputStream(); System.setOut(new PrintStream(toggle)); ``` 2. **日志上下文保持** ```java // 保存重定向状态到线程局部变量 class RedirectContext implements AutoCloseable { private final InputStream origIn; private final PrintStream origOut; public RedirectContext(File in, File out) { origIn = System.in; origOut = System.out; System.setIn(new FileInputStream(in)); System.setOut(new PrintStream(out)); } @Override public void close() { System.setIn(origIn); System.setOut(origOut); } } // 使用示例 try (RedirectContext ctx = new RedirectContext(input, output)) { // 业务代码 } ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值