java基础(十):IO技术流
目录:
- File类
- IO流基础知识
- IO流的原理及概念
- IO流的分类
- IO流类的体系
- 基本IO流
- 文件流
- 缓冲流
- 其他IO流
- 转换流
- 打印流
- 字节数组流/字符数组流/字符串流
- 数据流
- 对象序列化
- 对象流
File类
文件和目录路径名的抽象表示形式。一个File对象可以代表一个文件或目录
可以实现获取文件和目录属性等功能
可以实现对文件和目录的创建、删除等功能
File不访问文件内容
流的原理
- 在Java程序中,对于数据的输入/输出操作以“流” (stream) 方式进行;
- J2SDK提供了各种各样的“流”类,用以获取不同种类的数据;程序中通过标准的方法输入或输出数据。
- Java的流类型一般位于java.io包中
数据源
data source. 提供原始数据的原始媒介。常见的:数据库、文件、其他程序、内存、网络连接、IO设备。
数据源就像水箱,流就像水管中流着的水流,程序就是我们最终的用户。 流是一个抽象、动态的概念,是一连串连续动态的数据集合。
流的分类:
按照流的方向:
- 输入流:数据源到程序(InputStream、Reader读进来)
- 输出流:程序到目的地(OutPutStream、Writer写出去)
按照处理的数据单元:
- 字节流:按照字节读取数据(InputStream、OutputStream)
- 字符流:按照字符读取数据(Reader、Writer)
按照功能不同:
- 节点流:可以直接从数据源或目的地读写数据。
- 处理流(包装流):不直接连接到数据源或目的地,是其他流进行封装。目的主要是简化操作和提高性能.
节点流和处理流的关系
- 节点流处于io操作的第一线,所有操作必须通过他们进行;
- 处理流可以对其他流进行处理(提高效率或操作灵活性).
InputStream和OutputStream
- Java语言中最基本的两个字节输入输出类。
- 其他所有字节输入输出流类都继承自这两个基类。
- 这两个类都是抽象类,不能创建它们的实例,只能使用它们的子类.
Reader和Writer
- Java语言中最基本的两个字符输入输出类。
- 其他所有字符输入输出流类都继承自这两个基类。
- 这两个类都是抽象类,不能创建它们的实例,只能使用它们的子类.
FileInputStream/FileOutputStream
使用FileInputStream读取文件内容
abstract int read( );
int read( byte b[ ] );
int read( byte b[ ], int off, int len );
int available( );
close( );
使用FileOutputStream写内容到文件
abstract void write( int b );
void write( byte b[ ] );
void write( byte b[ ], int off, int len );
void flush( );
void close( );
缓冲字节流
BufferedInputStream和BufferedOutputStream
- FileInputStream和FileOutputStream是节点流
- BufferedInputStream和BufferedOutputStream是处理流(包装流)
- 读文件和写文件都使用了缓冲区,减少了读写次数,从而提高了效率
- 当创建这两个缓冲流的对象时,会创建了内部缓冲数组,缺省使用32字节大小的缓冲区.
- 当读取数据时,数据按块读入缓冲区,其后的读操作则直接访问缓冲区
- 当写入数据时,首先写入缓冲区,当缓冲区满时,其中的数据写入所连接的输出流。使用方法flush()可以强制将缓冲区的内容全部写入输出流
- 关闭流的顺序和打开流的顺序相反.只要关闭高层流即可,关闭高层流其实关闭的底层节点流
- Flush的使用:手动将buffer中内容写入文件
缓冲字符流
BufferedReader
- readLine() 读取一个文本行的数据
BufferedWriter
- newLine(); 写入一个行分隔符。
使用缓冲字符流是复制文本文件常用的方式
String str = br.readLine(); while(str != null){ bw.write(str); bw.newLine(); str = br.readLine(); }
转换流
InputStreamReader和OutputStreamWriter
- 为处理流 : 用于将字节流转化成字符流,字符流与字节流之间的桥梁
- InputStreamReader的作用是把InputStream转换成Reader
- OutputStreamWriter的作用是把OutputStream转换成Writer
只存在将字节流转换为字符流的转换流,不存在将字符流转换为字节流的转换流,因为没必要。
System.in代表标准输入,即键盘输入,是InputStream的实例
Reader reader = new InputStreamReader(System.in); BufferedReader br = new BufferedReader(reader); System.out.println("请输入用户名:"); String str = br.readLine(); System.out.println(str);
打印流
PrintStream
- PrintStream提供了一系列的print()和println(),可以实现将基本数据类型格式化成字符串输出。对象类型将先调用toString(),然后输出该方法返回的字符串
- System.out就是PrintStream的一个实例,代表显示器
- System.err 也是PrintStream的一个实例,代表显示器
- PrintStream的输出功能非常强大,通常需要输出文本内容,都可以将输出流包装成PrintStream后进行输出
- PrintStream的方法都不抛出IOException
PrintWriter
- PrintStream的对应字符流,功能相同,方法对应。
- PrintWriter的方法也不抛出IOException
- 复制文件时可以使用PrintWriter代替BufferedWriter完成,更简单
字节/字符数组/字符串流
ByteArrayInputStream和ByteArrayOutputStream
- 数据源或目的地为:字节数组
- 只有字节流,没有字符流
- 节点流
CharArrayReader和CharArrayWriter
- 数据源或目的地为:字符数组
- 只有字符流,没有字节流
- 节点流
StringReader和StringWriter
- 数据源或目的地为:字符串
- 只有字符流,没有字节流
- 节点流
DataInputStream和DataOutputStream
DataInputStream和DataOutputStream
- 提供了可以存取所有Java基础类型数据(如:int,double 等)和String的方法。
- 处理流,只针对字节流,二进制文件
输入流链和输出流链
注意:只要关闭上层流即可
对象序列化
对象序列化 (Serialization)
- 将Java对象转换成字节序列(IO字节流)
对象反序列化 (DeSerialization)
- 从字节序列中恢复Java对象
为什么序列化 ?
序列化以后的对象可以保存到磁盘上,也可以在网络上传输,使得不同的计算机可以共享对象.(序列化的字节序列是平台无关的)
对象序列化的条件
只有实现了Serializable接口的类的对象才可以被序列化。
Serializable接口中没有任何的方法,实现该接口的类不需要实现额外的方法。
如果对象的属性是对象,属性对应类也必须实现Serializable接口
如何实现序列化
创建ObjectOutputStream对象
调用writeObject()输出对象
OutputStream fos = new FileOutputStream(new File("d:/java6.txt")); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(stu); oos.close();
如何实现反序列化
创建ObjectInputStream对象
调用readObject()读取对象
InputStream fis = new FileInputStream(new File("d:/java6.txt")); ObjectInputStream ois = new ObjectInputStream(fis); Student stu = (Student)ois.readObject(); System.out.println(stu.getAge()+" "+stu.getScore());
序列化能保存的元素
- 只能保存对象的非静态成员变量
- 不能保存任何成员方法和静态的成员变量
- 不保存transient成员变量
- 如果一个对象的成员变量是一个对象,这个对象的成员变量也会保存
- 串行化保存的只是变量的值,对于变量的任何修饰符,都不能保存
使用对象流把一个对象写到文件时不仅保证该对象是序列化的,而且该对象的成员对象也必须是可序列化的。
如果一个可序列化的对象包含对某个不可序列化的对象的引用,那么整个序列化操作将会失败,
并且会抛出一个NotSerializableException。我们可以将这个引用标记为transient,那么对象仍然可以序列化。
同一个对象多次序列化的处理
- 所有保存到磁盘中的对象都有一个序列化编号
- 序列化一个对象中,首先检查该对象是否已经序列化过 如果没有,进行序列化 如果已经序列化,将不再重新序列化,而是输出编号即可
如果不希望某些属性(敏感)序列化,或不希望出现递归序列
- 为属性添加transient关键字(完成排除在序列化之外)
- 自定义序列化(不仅可以决定哪些属性不参与序列化,还可以定义属性具体如何序列化)
序列化版本不兼容
修改了实例属性后,会影响版本号,从而导致反序列化不成功
解决方案:为Java对象指定序列化版本号serialVersionUID
小结
IO基础
流的原理
对于数据的输入/输出操作以“流” (stream) 方式进行
数据源就像水箱,流就像水管中流着的水流,程序就是我们最终的用户
流的分类
输入流和输出流
字节流和字符流
节点流和处理流(包装流 装饰流)
流的体系
InputStream 字节输入流
OutputStream 字节输出流
Reader 字符输入流
Writer 字符输出流
具体IO介绍
文件流:节点流
FileInputStream和FileOutputStream
FileReader和FileWriter
缓冲流:包装流
BufferedInputStream和BufferedOutputStream
BufferedReader和BufferedWriter
转换流:包装流 字节流转换成字符流 System.in
InputStreamReader和OutputStreamWriter
打印流:包装流 只有输出流 System.out
PrintStream和PrintWriter
数据流:包装流 只有字节流 基本类型和String
DataInputStream和DataOutputStream
对象流:包装流 只有字节流 序列化 对象
ObjectInputStream和ObjectOutputStream
代码示例
1.使用流方式将C:\1\1.jpg复制到c:\2\girl.jpg
/** * 使用流方式将C:\1\1.jpg复制到c:\2\girl.jpg * @author Terry * */ public class CopyDemo { /** * @param args */ public static void main(String[] args) { String path1 = "c:\\1\\1.jpg"; String path2 = "c:\\2\\girl.jpg"; copyTo(path1, path2); System.out.println("文件复制结束"); } /** * 将原文件复制到目标位置 * @param sourcePath :原文件所在路径 * @param destPath :复制后文件的目标位置 */ static void copyTo(String sourcePath, String destPath){ //声明流对象 InputStream input = null; OutputStream output = null; try { //创建流对象 //用于读取文件 input = new FileInputStream(sourcePath); //用来写入文件 output = new FileOutputStream(destPath); //开始读取文件 //临时缓冲区(将读取到的内容存放到数组b中) byte[] b = new byte[1024]; //存储每次读取的长度 int len = 0; int i=0; while((len = input.read(b)) != -1){ //将刚刚读取到的内容写入到文件中 output.write(b); } } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally{ //建议分开使用try-catch语句 //这样可以有效避免上面input.close()方法出现异常造成下面的output.close()无法执行 try { input.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } try { output.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
2.
/** * 使用InputStream读取文件 * 作用 * InputStream用于以流方式读取文件中内容(源——>程序) * * byte[]——>字符串 * 创建 * InputStream 对象名 = new FileInputStream(文件路径); * InputStream 对象名 = new FileInputStream(File对象); * 常用方法 * read() :每次读取一个字节 * * close() :关闭当前流媒体对象 * available() :估计读取内容的剩余字节数 * @author Terry * */ public class InputStreamDemo1 { /** * @param args * @throws IOException */ public static void main(String[] args) throws IOException { String path = "c:\\1.txt"; //streamRead1(path); streamRead2(path); } /** * 使用Stream对象读取文件内容 * 使用byte数组作为缓存一次性读取多个字符内容(可以有效地提高读取的效率) * * 基本可以解决中文乱码问题 * @param path * @throws IOException */ static void streamRead2(String path) throws IOException{ //创建Stream对象 InputStream stream = new FileInputStream(path); //读取内容 //作为临时缓存存储流内容 byte[] b = new byte[24]; //一次性读取8个字节 //读取一次 //int len = stream.read(b); //System.out.println(new String(b)); //使用循环的方式进行读取 int index = 0; int len; while((len = stream.read(b)) != -1){ System.out.println(new String(b)); System.out.println("读取次数:"+ (++index)); } //关闭Strema对象 stream.close(); } /** * 每次读取一个字符 * 缺点: * 1、无法正确读取中文内容 * 2、每次读取一个字节效率较低 * @param path */ static void streamRead1(String path){ InputStream stream = null; try { /* //创建了FileInputStream对象并直接传递文件路径 InputStream stream = new FileInputStream(path); //创建FileInputStream并传递一个File对象 //创建File对象(目的是为了给FileInputstream最为参数) File file = new File(path); //创建对象并将file对象最为参数进行使用 InputStream stream = new FileInputStream(file); */ //创建FileInputStream对象并以File类型对象为参数(合并了上面的1~2两步) stream= new FileInputStream(new File(path)); /* //读取一个字符内容 int content = stream.read(); //读取过程中,如果未读取到任何内容则返回-1 while(content !=-1){ //打印字符内容 System.out.print((char)content); //继续读取下一个内容 content = stream.read(); } */ //上面代码的变形 int content = -1; while((content = stream.read()) != -1){ System.out.print((char)content); } } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally{ try { //关闭Stream对象 stream.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
3.
/** * OutputStream * 作用 * 以字节的形式将内容输出到物理文件中。输出时可以输出单个字节或数组 * * 字符串——>byte[] * 创建对象 * OutputStream 对象名 = new FileOutputStream(路径名 [,模式]); * OutputStream 对象名 = new FileOutputStream(File对象 [,模式]); * * 模式 :布尔类型值。该值指示了文件的输出方式,如果值true则表示追加方式进行输出 * 相反,如果未false则表示覆盖模式进行输出(默认模式) * * 常用方法 * write() :将字节内容进行输出 * close() :关闭流对象 * @param args */ public static void main(String[] args) { String path = "c:\\2.txt"; //streamWrite1(path); //streamWrite2(path); //streamWrite3(path); streamWrite4(path); } static void streamWrite4(String path) { //声明一个OutputStream对象 OutputStream stream = null; //实例化Stream对象 try { stream = new FileOutputStream(path,true); //定义待输出字符串内容 String str= "我要下课"; //字符串转为byte[] byte[] array =str.getBytes(); //输出字符串内容 stream.write(array); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally{ try { //关闭流对象 stream.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } /** * 将一个字符串内容输出到物理文件中 * 将一个byte[]中的内容进行输出(效率高于单字符输出) * * @param path */ static void streamWrite3(String path){ //声明stream对象 OutputStream stream=null; try { //创建stream对象 stream = new FileOutputStream(path); //定义待输出内容 String str = "Hello Wrold!这里是尚学堂"; //字符串——〉byte[] byte[] array = str.getBytes(); //直接将byte[]进行输出 stream.write(array); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally{ try { //关闭对象 stream.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } /** * 将一个字符串内容输出到物理文件中 * 每次输出一个字节,输出效率较低 * * @param path */ static void streamWrite2(String path){ //声明一个OutputStream对象 OutputStream stream = null; try { //创建OutputStream对象 stream = new FileOutputStream(path); //定义待输出字符串 String str = "Hello Wrold!这里是尚学堂"; //怎么把一个字符串转为byte数组呢? //getBytes()方法可以获取一个字符串的byte数组 byte[] array = str.getBytes(); //将数组中的内容进行输出 for(byte b : array){ stream.write(b); } } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally{ try { //关闭输出流对象 stream.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } /** * 通过OutputStream向物理文件中输出内容 * @param path */ static void streamWrite1(String path){ //声明了一个OutputStream对象 OutputStream stream = null; try { //创建对象 stream = new FileOutputStream(path); //通过输出流向物理文件中输出内容 //通过write()方法向文件输出一个字节的内容(100——字符d) stream.write(100); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally{ try { //关闭输出流对象 stream.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
4.
/** * @param args */ public static void main(String[] args) { Scanner input = new Scanner(System.in); //提示用户键盘输入内容 System.out.print("请输入日志内容:"); String content = input.nextLine(); //将用户键盘输入的内容写入到文件中 writeToLog(content); } static void writeToLog(String content) { OutputStream stream = null; //获取文件名 String fileName = "c:" +File.separatorChar+ getFileName(); try { //设置文件的创建方式 //true表示文件存在则进行追加操作,否则则进行创建新文件 stream = new FileOutputStream(fileName,true); //将文件内容输出到文件中 stream.write(content.getBytes()); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally{ try { //关闭流对象 stream.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } /** * 生成一个特定格式的文件名 * @return */ static String getFileName(){ //生成一个文件名 //log + "_" + 日期 //创建一个Date对象 Date date = new Date(); SimpleDateFormat fmt = new SimpleDateFormat("yyyyMMdd"); //返回日期格式的文件名 return "log" + fmt.format(date) + ".log"; }
5.
/** * FileReader类 * 一个工具类,内部提供了用于以字符方式读取文件内容的方法 * * 方法 * read() :以字符为单位进行读取,每次读取一个字符 * close() :关闭当前对象 * skip() :跳过指定个个数的字符 * * * @author Terry * */ public class FileReaderDemo { /** * @param args */ public static void main(String[] args) { String path = "c:\\1.log"; //reader1(path); //reader2(path); //reader3(path); reader4(path); } /** * 以字符方式读取文件内容 * 定义一个与字符个数相符的缓冲区 * @param path */ static void reader4(String path){ //声明reader对象 Reader reader = null; try { File file = new File(path); //创建reader对象 reader = new FileReader(file); //声明了一个与文件字符个数相同的char型数组 //cbuf——char型数组。作为临时的缓冲区 char[] cbuf = new char[(int)file.length()]; reader.read(cbuf); System.out.println(cbuf); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally{ try { reader.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } /** * 读取字符型文本的内容,每次读取一组内容 * @param path */ static void reader3(String path) { //声明reader对象 Reader reader = null; try { //创建reader对象 reader = new FileReader(path); char[] array = new char[10]; /* //读取了部分数据 int len = reader.read(array); System.out.println(array); */ //将字符内容缓存到数组中 while(reader.read(array) != -1){ System.out.print(array); } } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * 循环遍历,每次读取一个字符 * 缺点:效率低 * 优点:有效的解决了中文读取错误的问题(InputStream) * @param path */ static void reader2(String path){ //声明reader对象 Reader reader = null; try { //实例化一个reader对象 reader =new FileReader(path); int v; //如果read()的返回结果为-1则表示读取失败 while((v=reader.read())!= -1){ System.out.print((char)v); } } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * 利用Reader读取文件内容中的第一个字符 * @param path */ static void reader1(String path){ //声明Reader对象 Reader reader = null; try { /* //以File对象作为参数进行文件读取 File file = new File(path); reader = new FileReader(file); */ //以字符串型的路径作为参数进行对象创建 reader = new FileReader(path); //读取单个字符(读取一个完整的字符,即使是中文字符也不会出现半个字符的情况) int v = reader.read(); System.out.println((char)v); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally{ try { //关闭reader对象 reader.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
6.
/** * FileWriter 概述 将字符流写入到文件中 * 方法 * writ() * flush() :刷新缓冲区 * * flush * 以字符方式写文件内容时,writer对象临时将内容存储到了缓冲区中。为了提高效率,只有当 * 调用close()方法时才对内容进行输出。 * flush()方法可以刷新缓冲区,在刷新过程中会强制将内容输出到文件中 * * 缓冲区 * 缓冲区就是一块内存区域。 * * @author Terry * */ public class FileWriterDemo { /** * @param args */ public static void main(String[] args) { String path = "c:\\1.txt"; // write(path); //write2(path); write3(path); } /** * 不关闭对象执行代码 * 如果未关闭对象执行代码则内容不会被输出 * 如果使用了flush之后即使未关闭内容也会被输出 * @param path */ static void write3(String path) { // 声明一个Writer对象 Writer writer = null; try { // 创建Writer对象 writer = new FileWriter(path); String str = "哇哈哈哈哈,终于要放学了"; writer.write(str); //加上后即使没有使用close()方法内容依旧被输出了 writer.flush(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { } } /** * 直接向文件中写入一个字符串 * * @param path */ static void write2(String path) { // 声明一个Writer对象 Writer writer = null; try { // 创建Writer对象 writer = new FileWriter(path); String str = "哇哈哈哈哈,终于要放学了"; writer.write(str); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { try { // 关闭对象 writer.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } /** * 向文件中写入一个字符 * * @param path */ static void write(String path) { // 声明一个Writer对象 Writer writer = null; try { // 创建writer对象 writer = new FileWriter(path); // 写入一个字符 writer.write(65); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { try { // 关闭reader对象 writer.close(); } catch (IOException e) { e.printStackTrace(); } } } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 25岁的心里话
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现