Java的IO

本文详细介绍了 Java 中的 IO 流概念,包括 FileInputStream、FileReader 和 RandomAccessFile 的使用方法,探讨了字节流与字符流的区别,并展示了如何利用装饰器模式增强流的功能。

java.io包中最重要的是五个类和一个接口:File、OutputStream、InputStream、Writer、Reader和Serializable

《开实》P399


我们可以用FileInputStream(文件字符流)或FileReader(文件字节流)来读文件,这两个类可以让我们分别以字符和字节的方式来读取文件内容,但是它们都有一个不足之处,就是只能从文件头开始读,然后读到文件结束。但是有时候我们只希望读取文件的一部分,或者是说随机的读取文件,那么我们就可以利用RandomAccessFile。RandomAccessFile提供了seek()方法,用来定位将要读写文件的指针位置,我们也可以通过调用getFilePointer()方法来获取当前指针的位置




其中,字节流:OutputStream、InputStream。字符流:Writer、Reader。

字节流

抽象类,主要操作byte型数据
File file=new File("d:"+File.separator+"test.txt");
String str="Hello";
byte[] b1=str.getBytes();
byte[] b2=new byte[1024];
OutputStream out=new FileOutputStream(file);
InputStream in=new FileInputStream(file);
out.write(b1);//将数组b1中内容写到到输出流
int len=in.read(b2);//将输入流中内容读到到b2数组中



字符流

抽象类,Unicode字符,双字节
File file=new File("d:"+File.separator+"test.txt");
String str="Hello";
char[] c=new char[1024];
Writer w=new FileWriter(file);
Reader r=new FileReader(file);
w.write(str);//将字符串中内容写到到输出流
int len=r.read(c);//将输入流中内容读到到char数组中



字节流与字符流的区别

1.Reader和Writer要解决的,最主要的问题就是国际化。原先的I/O类库只支持8位的字节流,因此不可能很好地处理16位的Unicode字符流。Unicode是国际化的字符集(更何况Java内置的char就是16位的Unicode字符),这样加了Reader和Writer之后,所有的I/O就都支持Unicode了。此外新类库的性能也比旧的好。

但是,Read和Write并不是取代InputStream和OutputStream,有时,你还必须同时使用"基于byte的类"和"基于字符的类"。为此,它还提供了两个"适配器(adapter)"类。InputStreamReader负责将InputStream转化成Reader,而OutputStreamWriter则将OutputStream转化成Writer。

2.字节流在使用时直接对文件进行读写,而字符流会用到缓冲区(内存),通过缓冲区对文件进行操作。假如在分别使用字符流和字节流写文件之后都不关闭输出流,会发现,使用字节流时,文件中已存在要写入的内容;而使用字符流时,文件中是空的,这是因为在关闭输出流时会强制输出缓冲区的内容,所以关闭后文件中才会有字符(强制输出也可以用flush())。

计算机访问外部设备,要比直接访问内存慢得多,如果我们每一次 write 方法的调用都直接写到外部设备(如直接写入硬盘文件), CPU 就要花费更多的时间等待外部设备;如果我们开辟一个内存缓冲区,程序的每一次write 方法都是写到这个内存缓冲区中,只有这个缓冲区被装满后,系统才将这个缓冲区的内容一次集中写到外部设备。使用内存缓冲区有两个方面的好处,一是有效地提高了 CPU 的使用率,二是 write 并没有马上真正写入到外设,我们还有机会回滚部分写入的数据。使用缓冲区,能提高整个计算机系统的效率,但也会降低单个程序自身的效率,由于有这么一个中间缓冲区,数据并没有马上写入到目标中去,例如在网络流中,就会造成一些滞后。

3.文本文件、内存-->字符,图片声音音频文件、硬盘、传输-->字节。所以字节流使用更为广泛



ByteArrayInputStream:向内存中写入数据。ByteArrayOutputStream:将内存中的数据读出

内存操作流一般在生成一些临时信息时使用,而如果把这些临时信息保存在文件,则执行完成后还要删除文件。

使用实例:

String str="Hello";
ByteArrayInputStream bis=null;
ByteArrayOutputStream bos=null;
bis=new ByteArrayInputStream(str.getBytes());<span style="white-space:pre">	</span>//向内存中输出(写入)内容
bos=new ByteArrayOutputStream();

int temp=0;
while((temp=bis.read())!=-1){<span style="white-space:pre">	</span>//bis是InputStream的子类,所以可以使用read()
<span style="white-space:pre">	</span>char c=(char) temp;<span style="white-space:pre">	</span>//将读取的数字变成字符
<span style="white-space:pre">	</span>bos.write(Character.toLowerCase(c));<span style="white-space:pre">	</span>//变成小写
}
String s=bos.toString();<span style="white-space:pre">	</span>//取出内容,可以发现大写都变成了小写,而所有的操作都在内存中完成


DataInputStream:与平台无关的数据操作,继承自FilterInputStream类,同时实现了DataInput接口,在此接口中定义了一系列读入各种数据的方法readXXX(),如readInt()、readFloat()、readChar()。






在上面的关系图中可以看出:
1.InputStream是所有的输入字节流的父类,它是一个抽象类。
2. ByteArrayInputStream、StringBufferInputStream、FileInputStream是三种基本的介质流,它们分别将Byte数组、StringBuffer、和本地文件中读取数据。PipedInputStream是从与其它线程共用的管道中读取数据。
3. ObjectInputStream和所有FilterInputStream的子类都是装饰流(装饰器模式的主角)。

FileInputStream类是InputStream类的子类,用来处理以文件作为数据输入源的数据流。
BufferedInputStream顾名思义,就是在对流进行写入时提供一个buffer来提高IO效率。在进行磁盘或网络IO时,原始的InputStream对数据读取的过程都是一个字节一个字节操作的,而BufferedInputStream在其内部提供了一个buffer,在读数据时,会一次读取一大块数据到buffer中,这样比单字节的操作效率要高的多,特别是进程磁盘IO和对大量数据进行读写的时候。
1) ByteArrayInputStream:内存操作流。把内存中的一个缓冲区作为 InputStream 使用,将内容写入内存中(对 内存 的输入输出操作),操作byte数组;
2) StringBufferInputStream:把一个 String 对象作为 InputStream;
3) FileInputStream:文件流。把一个文件作为 InputStream,实现对文件的读取操作;
4) PipedInputStream:管道流。实现了 pipe 的概念,,主要在 线程 中使用;
5) SequenceInputStream:合并流。把多个 InputStream 合并为一个 InputStream用于将两个文件的内容合并成一个;
6) FilterInputStream:抽象类,作为”修饰器”与其他对象相连以提供有用接口;
7) ObjectInputStream: 对象输入流。把一个类对象作为InputStream.主要是为了实现对象的串行化.
8) DataInputStream:数据操作流。提供了与平台无关的数据操作
9)PushbackInputStream:回退流





转换流

OutputStreamWriter:是Writer的子类,将输出的字符流转化成字节流
InputStreamReader:是Reader的子类,将输入的字节流转化成字符流
例如,文件的操作:内存中的字符数据需要经过OutputStreamWriter变为字节流才能写进文件,读取时需要将读入的字节通过InputStreamReader变成字符流
Writer out;
out=new  OutputStreamWriter(FileOutputStream(file));//输出到文件,要用字节流
out.write("Hello");//内存中的数据是字符,所以用Writer
注意:FileOutputStream是OutputStream的子类,但 FileWriter不是Writer的子类而是OutputStreamWriter的子类。

BufferedReader:用于从缓冲区中读取内容(所有输入字节数据都是存放在缓冲区)
BufferedReader的构造方法只能接受字符流,而键盘输入System.in是字节流,所以要用转换流转换一下
BufferedReader br=null;
br=new BufferedReader(new InputStreamReader(System.in));

String s=null;
s=br.readLine();//将System.in变成字符流放入BufferedReader之后,通过readLine()等待用户输入
readLine():一次性将数据从缓冲区全部读取出来。上例中会一直等待用户输入,属于阻塞操作



Java IO 的一般使用原则 :  

一、按数据来源(去向)分类:

1 、是文件: FileInputStream, FileOutputStream, ( 字节流 )FileReader, FileWriter( 字符 )

2 、是 byte[] : ByteArrayInputStream, ByteArrayOutputStream( 字节流 )

3 、是 Char[]: CharArrayReader, CharArrayWriter( 字符流 )

4 、是 String: StringBufferInputStream, StringBufferOuputStream ( 字节流 )StringReader, StringWriter( 字符流 )

5 、网络数据流: InputStream, OutputStream,( 字节流 ) Reader, Writer( 字符流 )

二、按是否格式化输出分:

1 、要格式化输出: PrintStream, PrintWriter

三、按是否要缓冲分:

1 、要缓冲: BufferedInputStream, BufferedOutputStream,( 字节流 ) BufferedReader, BufferedWriter( 字符流 )

四、按数据格式分:

1 、二进制格式(只要不能确定是纯文本的) : InputStream, OutputStream 及其所有带 Stream 结束的子类

2 、纯文本格式(含纯英文与汉字或其他编码方式); Reader, Writer 及其所有带 Reader, Writer 的子类

五、按输入输出分:

1 、输入: Reader, InputStream 类型的子类

2 、输出: Writer, OutputStream 类型的子类

六、特殊需要:

1 、从 Stream 到 Reader,Writer 的转换类: InputStreamReader, OutputStreamWriter

2 、对象输入输出: ObjectInputStream, ObjectOutputStream

3 、进程间通信: PipeInputStream, PipeOutputStream, PipeReader, PipeWriter

4 、合并输入: SequenceInputStream

5 、更特殊的需要: PushbackInputStream, PushbackReader, LineNumberInputStream, LineNumberReader




Java多态

OutputStream的哪个子类为其实例化,就具备了向哪里输出的能力,比如用FileOutputStream是向文件输出,用PrintStream的子类System.out则是向显示器输出,这就是Java多态性的好处,根据子类的不同完成不同的任务。


http://blog.youkuaiyun.com/cruise_h/article/details/14046053





序列化(持久化)

http://www.cnblogs.com/vicenteforever/articles/1471775.html

序列化是把一个对象变成二进制的数据流的一种方法,该对象的类必须实现Serializable接口,然后就可以经过二进制数据流进行传输
Serializable接口是一个标识接口(空接口),表示一个类可以被序列化,里面没有定义任何方法。
实现了S接口后,该对象可以通过对象输出流ObjectOutputStream和对象输入流ObjectInputStream进行数据传输。

由于同一个类的不同对象的方法是一样的,只有属性不同,所以只有属性被序列化
对象的类声明了Serializable接口后,该对象所有的内容都会被序列化,如果希望自定义,则可以让该类实现Externalizable接口。或者使用transient关键字,被该关键字声明的属性不会被序列化,静态成员也不会被序列化。

<think>好的,我现在需要处理用户关于Java IO的问题。用户希望了解Java IO的概念或者解决相关问题,特别是询问教程和故障排除的方法。首先,我得回顾一下Java IO的基础知识,确保自己理解正确,然后组织回答的结构。 首先,Java IO主要涉及输入输出流,用于处理数据的读写。核心包是java.io,里面有很多类,比如File、InputStream、OutputStream、Reader、Writer等。用户可能需要知道这些类的层次结构和使用方法。 接下来,常见的IO操作包括文件读写、网络通信、序列化等。比如,FileInputStream和FileOutputStream用于字节流,而FileReader和FileWriter用于字符流。缓冲流(BufferedInputStream等)可以提高效率,这部分应该提到。 然后,异常处理是Java IO中很重要的一部分,比如IOException,需要正确处理try-with-resources来自动关闭流,避免资源泄漏。这部分用户可能在故障排除时会遇到问题,比如未正确关闭流导致文件锁定或资源耗尽。 关于教程,Oracle的官方文档是权威资源,应该引用。比如引用[3]提到了JSP的信息,但用户需要的是IO的教程,可能需要检查是否有其他引用相关。不过用户给出的引用中,引用[1]是关于JDK 7的故障排除工具,可能和调试IO问题相关,比如使用jdb进行调试。引用[5]提到字节码信息,可能和IO关系不大,但故障排除时可能需要查看异常堆栈,这可能涉及字节码或类加载的问题。 对于故障排除,常见问题包括文件路径错误、权限问题、字符编码不一致导致的乱码,或者流未正确关闭。比如,文件找不到可能是因为相对路径的当前工作目录与预期不符,或者文件被其他进程占用。这时候需要检查路径是否正确,是否有读取权限,使用绝对路径测试,或者使用工具如Process Explorer查看文件句柄。 此外,NIO(New IO)在Java 1.4引入,提供了非阻塞IO和通道等高效处理方式,用户可能也需要了解NIO和传统IO的区别,比如使用Channel和Buffer的方式,或者使用Files类简化操作。 在组织回答时,需要分步骤讲解,比如先介绍核心概念,然后常见操作,接着是故障排除技巧,最后提供学习资源和相关工具。同时,生成的问题要围绕Java IO的进一步学习,比如NIO、性能优化、常见异常处理等。 需要确保回答中的LaTeX格式正确,比如类名和方法名可能需要用代码块,而数学表达式则用$...$。例如,文件路径的转义可能需要讨论,比如在字符串中使用反斜杠需要转义,写成`"C:\\\\path\\\\to\\\\file"`,但这里可能不需要数学表达式,所以可能不需要用到LaTeX的公式部分。 检查用户提供的引用,是否有与IO相关的资源。引用[4]是关于安卓USB驱动的,可能不相关。引用[2]关于反射,可能也不直接相关,但故障排除时可能会用到反射来检查类路径或资源加载,这可能间接相关,但可能不需要特别提到。 总结回答结构: 1. Java IO核心概念:流、字节流与字符流、缓冲流、NIO。 2. 常见操作:文件读写示例代码。 3. 故障排除:常见问题及解决方法,如路径、权限、编码、资源泄漏。 4. 学习资源:官方教程、工具(如jdb)。 5. 相关问题:如何选择IO和NIO,处理大文件,字符编码问题,关闭资源方法,常见异常处理。 需要确保回答中的引用正确,比如引用[1]关于JDK工具,可能在故障排除时使用jdb或者jstack等工具分析问题,可以提到。引用[3]的链接虽然是JSP,但Oracle的Java EE教程可能包含IO相关内容,需要检查链接是否有效,但用户提供的链接可能已失效,需要注意。不过用户给出的引用可能有误,比如引用[3]的网址是javaee.github.io,可能是正确的,但需要确认是否有IO相关部分。 最后,代码示例需要正确,比如使用try-with-resources来读取文件,展示正确的异常处理方式。同时,确保所有技术术语准确,例如InputStreamReader处理字符编码,BufferedReader提高读取效率。 可能的问题点:用户提供的引用中是否有直接关于Java IO的内容?引用[1]提到JDK工具,可以用于调试IO问题,比如使用jdb调试应用程序,或者在出现IO异常时分析线程状态。引用[5]关于字节码信息,可能与IO问题关系不大,但类加载时找不到资源文件可能会涉及,需要看具体情况。 因此,在故障排除部分,可以建议使用JDK工具如jstack检查是否有线程阻塞在IO操作,或者使用jdb进行调试,引用[1]。同时,处理资源泄漏时,可以使用try-with-resources来自动关闭流,这是Java 7的特性,可能需要提到。 综上所述,回答的结构应该是先介绍核心概念,然后示例代码,接着故障排除步骤,引用相关资源,最后生成相关问题。</think>### Java IO 概念与问题排查指南 #### 一、核心概念 Java IO(输入输出)用于处理数据读写,核心包为 `java.io`,主要分为两类: 1. **字节流**:处理二进制数据,基类为 `InputStream` 和 `OutputStream`,例如 `FileInputStream` 和 `BufferedOutputStream`。 2. **字符流**:处理文本数据,基类为 `Reader` 和 `Writer`,例如 `FileReader` 和 `BufferedWriter`。 3. **NIO(New IO)**:Java 1.4 引入,通过 `Channel` 和 `Buffer` 实现高效非阻塞操作,例如 `Files.readAllLines()`[^1]。 #### 二、常见操作示例 1. **文件读取(字符流)**: ```java try (BufferedReader reader = new BufferedReader(new FileReader("test.txt"))) { String line; while ((line = reader.readLine()) != null) { System.out.println(line); } } catch (IOException e) { e.printStackTrace(); } ``` 2. **文件写入(字节流)**: ```java try (OutputStream out = new BufferedOutputStream(new FileOutputStream("output.dat"))) { out.write("Hello".getBytes()); } catch (IOException e) { e.printStackTrace(); } ``` #### 三、故障排查技巧 1. **文件路径问题** - 使用绝对路径或检查工作目录:`System.getProperty("user.dir")`。 - 路径分隔符兼容性:使用 `File.separator` 替代硬编码的 `/` 或 `\`。 2. **权限与资源占用** - 确认文件可读/写:`file.canRead()` 和 `file.canWrite()`。 - 确保流正确关闭:使用 **try-with-resources**(Java 7+)避免资源泄漏。 3. **字符编码问题** - 指定编码格式:例如 `new InputStreamReader(new FileInputStream("file.txt"), StandardCharsets.UTF_8)`。 4. **调试工具** - 使用 `jdb`(Java Debugger)追踪 IO 异常堆栈[^1]。 - 通过 `jstack` 分析线程阻塞(如网络 IO 超时)。 #### 四、学习资源 1. **官方教程** - [Oracle Java IO 教程](https://docs.oracle.com/javase/tutorial/essential/io/)(涵盖基础与 NIO)。 2. **进阶内容** - Java NIO.2(Java 7+)的 `Path` 和 `Files` 类[^3]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值