【java】第十四章、I/O输入输出流

本文详细介绍了Java中的I/O流,包括File类在文件和文件夹操作中的应用,字节流和字符流的概念,以及如何进行缓冲流、数据流的使用。还讲解了如何将字符流转为字节流,并讨论了流的关闭方式,强调了资源释放的重要性。
摘要由CSDN通过智能技术生成

一、 I/O流概述

在这里插入图片描述
输入流就是指某数据源到程序的流
在这里插入图片描述
输出流则是程序中的数据传输到了某个目的地
在这里插入图片描述
其中,字节流和字符流都是抽象类,我们要详细介绍的是它们的子类
在这里插入图片描述
这些类的命名有一个特点:字节流以Stream结尾,字符流都是以Reader或Writer结尾的

二、File类(文件的操作)

在这里插入图片描述
在创建File对象时,有三种构造方法可以选择
在这里插入图片描述
在这里插入图片描述
绝对路径:是一个文件的具体位置
第二种构造方法:文件夹和文件名是分开的

package file_file;

import java.io.File;

public class Demo {
	public static void main(String[] args) {
		/**
		 * 项目下的路径(默认路径):word.txt
		 * 包中的文件路径:src/file_file/word.txt
		 * 注意:   /表示文件夹
		 *        \\表示文件夹(转义字符)
		 * 绝对路径:D:\\test\\word.txt       
		 */
		File f1 = new File("D:\\test\\word.txt\\");        //第一种构造方法
		File f2 = new File("D:\\test\\", "word.txt");      //第二种构造方法
		
		File dir = new File("D:\\test\\");                 //文件夹
		File f3 = new File(dir, "word.txt");               //第三种构造方法
		
		System.out.println(f1.getAbsolutePath());          //输出文件的绝对路径
		System.out.println(f2.getAbsolutePath());          //输出文件的绝对路径
		System.out.println(f3.getAbsolutePath());          //输出文件的绝对路径
		
		System.out.println(f1 == f2);
		System.out.println(f1.equals(f2));
	}

}

注意:
f1 == f2(结果为false,因为f1和 f2是两个独立的File对象)
f1.equals(f2) (结果为true,因为f1和 f2指向的是同一个文件)
在这里插入图片描述

package file_file;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class Demo {
	public static void main(String[] args) {
		/**
		 * 项目下的路径(默认路径):word.txt
		 * 包中的文件路径:src/file_file/word.txt
		 * 注意:   /表示文件夹
		 *        \\表示文件夹(转义字符)
		 * 绝对路径:D:\\test\\word.txt       
		 */
		File f1 = new File("D:\\test\\word.txt\\");        //第一种构造方法
//		File f2 = new File("D:\\test\\", "word.txt");      //第二种构造方法
//		
//		File dir = new File("D:\\test\\");                 //文件夹
//		File f3 = new File(dir, "word.txt");               //第三种构造方法
//		
//		System.out.println(f1.getAbsolutePath());          //输出文件的绝对路径
//		System.out.println(f2.getAbsolutePath());          //输出文件的绝对路径
//		System.out.println(f3.getAbsolutePath());          //输出文件的绝对路径
//		
//		System.out.println(f1 == f2);
//		System.out.println(f1.equals(f2));
		
		System.out.println("文件是否存在:" + f1.exists());     //判断文件是否存在
		System.out.println("文件名:" + f1.getName());        //输出文件名
		System.out.println("文件的绝对路径:" + f1.getAbsolutePath());  //输出文件的绝对路径
		System.out.println("是否是隐藏文件:" + f1.isHidden());  //是否是隐藏文件
		System.out.println("文件的字节数:" + f1.length());     //输出文件大小,单位:字节
		Date date = new Date(f1.lastModified());            //通过毫秒值 创建日期类
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
		System.out.println("文件的最后修改时间:" + sdf.format(date));  //文件最后的修改时间 
		
//		boolean del = f1.delete();                              //删除文件
//		System.out.println("删除文件是否成功:" + del);
		
		try {
			//已存在的文件,不能重新创建(不能覆盖已有文件)
			boolean creat = f1.createNewFile();                 //创建新空的文件
			System.out.println("创建文件是否成功:" + creat);
		} catch (IOException e) {
			e.printStackTrace();
		}

	}
}

在这里插入图片描述
如果我们获取的文件是不存在的,我们发现程序仍然可以运行(但是输出的值都是默认值),这是不对的
所以我们应该在一开始就判断文件是否存在

三、File类(文件夹的操作)

在这里插入图片描述

package file_directory;

import java.io.File;

public class Demo {
	public static void main(String[] args) {
		File dir = new File("dir/dir2/dir3/dir4/");
		boolean flag = dir.mkdir();           //创建文件夹
		boolean flag2 = dir.mkdirs();         //创建文件夹及其父文件夹(创建很多文件夹)
		System.out.println("创建文件夹是否成功:" + flag);
		System.out.println("创建多层文件夹是否成功:" + flag2);
		
//		//删除文件路径最后一个文件夹
//		boolean del = dir.delete();           //删除文件夹
//		System.out.println("删除文件夹是否成功:" + del);
		
		File f = new File("C:\\Windows\\");   //C盘Windows文件夹
		File files[] = f.listFiles();         //返回文件夹下 所有的子文件及子文件夹
		for(File tmp: files) {
			if(tmp.isFile()) {
				System.out.println("文件:" + tmp.getName());
			}else if(tmp.isDirectory()) {
				System.out.println("文件夹:" + tmp.getName());
			}
		}
	}

}

在这里插入图片描述
mkdir()这个方法只能创建一层文件夹,不能创建多层文件夹
创建文件夹及其父文件夹(多层文件夹)用mkdirs()这个方法
listfiles()方法返回的是我文件夹下所有的子文件及子文件夹(返回的值是文件数组,所以可以通过遍历数组来获取所有的子文件)

四、文件字节流

电脑中的文件都是用二进制来储存的,读取文件时就是在读取这些二进制字节码java提供了读取这些字节码的文件字节流
在这里插入图片描述
输入流用来读文件,输出流用来写文件
注意:在使用完字节流之后,一定要将其关闭
在这里插入图片描述

package file_IO_stream;

import java.io.*;

public class Demo {
	public static void main(String[] args) {
		File f = new File("word.txt");		
		FileOutputStream out  = null;
		
		try {
			out = new FileOutputStream(f, true);    //文件输出流,替换文件内容//在文件末尾追加内容
			
			String str = "你见过洛杉矶,凌晨四点的样子吗?";
			byte b[] = str.getBytes();        //字符串转换为字节数组
			out.write(b);                     //将字节数组中数据写入到文件中
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if(out != null) {
				try {
					out.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		
		FileInputStream in  = null;
		
		try {
			in = new FileInputStream(f);      //输入流读文件
			byte b2[] = new byte[200];        //缓冲区
			int len = in.read(b2);            //读入缓冲区的总字节数
			System.out.println("文件中的数据是:" + new String(b2, 0, len));    //去掉空格
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}finally {
			if(in != null) {
				try {
					in.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}

	}

}

在这里插入图片描述
当我们反复向这个文件中写值时,会覆盖原来的内容(可以再添加一个参数true,这样就是在文件末尾追加内容,其默认值为false(替换文件内容))
在文件之后添加内容还是替换内容用一个布尔值来控制
如何将byte数组里面的东西变成我们能看到的内容:用String的构造方法,java会自动将这个字节数组转换成对应的字符串
**注意:**滚动条能拉到很往后(因为字节数组长度为1024,所以字符串后面全是空格)
不显示它后面的这些空格的方法:记录读入缓冲区的总字节数,在String的构造方法中添加两个参数

五、文件字符流

在这里插入图片描述
字符流能避免读取数据不全而造成的乱码问题
java中的文件字符流有两个:FileReader 文件字符输入流 FileWriter文件字符输出流
在这里插入图片描述
在这里插入图片描述

package file_char_stream;

import java.io.*;

public class Demo {
	public static void main(String[] args) {
		File f = new File("word.txt");
		FileWriter fw = null;
		try {
			fw = new FileWriter(f, true);     //在源文件后追加新内容
			
			String str = "天行健,自强不息;地势坤,厚德载物。";
			fw.write(str);              //将字符串写入到文本文档
			
		} catch (IOException e) {
			e.printStackTrace();
		}finally {
			if(fw != null) {
				try {
					fw.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		
		FileReader fr = null;
		try {
			fr = new FileReader(f);
			
			char ch[] = new char[1024]; //缓冲区
			int count;                  //已经读出的字符数
			while( (count = fr.read(ch)) != -1) {            //循环读取文件中的数据,直到所有字符都读完
				System.out.println("文件中的内容为:" + new String(ch, 0, count));
			}
			
		} catch (IOException e) {
			e.printStackTrace();
		}finally {
			if(fr != null) {
				try {
					fr.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}

	}

}

在这里插入图片描述

六、缓冲字节流

在这里插入图片描述
在流中创造一片缓冲区,让数据一车一车运输(而不是一点一点运输),大大降低数据源和目的地的交互次数,效益会明显提高
在这里插入图片描述
缓冲字节流的使用方法和其他字节流一样,但是它需要用在其他字节流之上
这是没有缓冲字节输入流的情况:

package buffered_IO_stream;

import java.io.*;

public class Demo {
	public static void main(String[] args) {
		File f = new File("C:\\Program Files\\dotnet\\ThirdPartyNotices.txt\\");
		
		FileInputStream in = null;
		long start = System.currentTimeMillis();    //获取流开始时毫秒值
		try {
			in = new FileInputStream(f);
			byte b[] = new byte[1024];              //缓冲区字节数组(这个缓冲区与Buffered不同)
			while(in.read(b) != -1) { 
				
			}
			
			long end = System.currentTimeMillis();    //获取流结束时毫秒值
			System.out.println("运行经历的毫秒数:" + (end - start));
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}finally {
			if(in != null) {
				try {
					in.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}

	}

}

在这里插入图片描述

这是有缓冲字节输入流的情况:

package buffered_IO_stream;

import java.io.*;

public class Demo {
	public static void main(String[] args) {
		File f = new File("C:\\Program Files\\dotnet\\ThirdPartyNotices.txt\\");
		
		BufferedInputStream bi = null;
		FileInputStream in = null;
		long start = System.currentTimeMillis();    //获取流开始时毫秒值
		try {
			in = new FileInputStream(f);
			bi = new BufferedInputStream(in);       //将文件字节流包装成缓冲字节流
			byte b[] = new byte[1024];              //缓冲区字节数组(这个缓冲区与Buffered不同)
			while(bi.read(b) != -1) {               //使用缓冲流读取数据
				
			}
			
			long end = System.currentTimeMillis();    //获取流结束时毫秒值
			System.out.println("运行经历的毫秒数:" + (end - start));
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}finally {
			if(in != null) {
				try {
					in.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if(bi != null) {
				try {
					bi.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			
		}

	}

}

在这里插入图片描述
文件字节流包装成缓冲字节流,所以在读数据时就不能用文件字节流读了,而是要用缓冲字节流来读数据
现在,这里运行经历的毫秒数大大减少了
这就是使用缓冲字节流的效果:大大提高运行效率

package buffered_IO_stream;

import java.io.*;

public class Demo2 {
	public static void main(String[] args) {
		File f = new File("word.txt");

		BufferedOutputStream bo = null; // 大大提高了运行效率
		FileOutputStream out = null;
		try {
			out = new FileOutputStream(f);
			bo = new BufferedOutputStream(out); // 将文件字节流包装成缓冲字节流
			String str = "天生我材必有用,千金散尽还复来。";
			byte b[] = str.getBytes();
			bo.write(b);
			bo.flush();   //刷新。强制将缓冲区数据写入文件,即使缓冲区没有被写满

		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (out != null) {
				try {
					out.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (bo != null) {
				try {
					bo.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}

		}

	}

}

在这里插入图片描述
其实这样看除了效率以外,没有什么变化
但是缓冲输出流提供了这样一个方法:flush()(这个方法的意思是刷新,可以强制将缓冲区数据写入文件中,即使缓冲区没有被写满,这其实也是提高效率的一个动作)
使用缓冲字节输出流时,要多进行刷新操作(否则缓冲区会等待 里面被写满之后,才写入文件)
总结:缓冲字节流可以提高其它流的运行效率(将其它流包装起来)

七、缓冲字符流

在这里插入图片描述
缓冲字符流有个最大的特点:可以以行为单位进行输入输出(之前介绍的都是以字节或字符为单位的)
要注意流的关闭规则:先创建的,后关闭

package buffered_char_stream;

import java.io.*;

public class Demo {
	public static void main(String[] args) {
		File f = new File("word.txt");
		FileWriter fw = null;
		BufferedWriter bw = null;
		
		try {
			fw = new FileWriter(f);
			bw = new BufferedWriter(fw);        //将文件字符输出流包装成缓冲字符流
			
			String str1 = "世界这么大";
			String str2 = "我想去看看";
			bw.write(str1);                     //第一行是数据
			bw.newLine();                       //创建一个新行
			bw.write(str2);                     //第二行的数据
		} catch (IOException e) {
			e.printStackTrace();
		}finally {                              //要注意流的关闭顺序,先创建的后关闭
			if(bw != null) {
				try {
					bw.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if(fw != null) {
				try {
					fw.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		
		FileReader fr = null;
		BufferedReader br = null;
		
		try {
			fr = new FileReader(f);
			br = new BufferedReader(fr);     //将文件字符输入流包装成缓冲字符输入流
			
			String tmp = null;
			int i = 1;                       //计数器
			while((tmp = br.readLine()) != null) {         //循环读取文件中的内容
				System.out.println("第" + i + "行:" + tmp);
				i++;
			}
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}finally {
			if(br != null) {
				try {
					br.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if(fr != null) {
				try {
					fr.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}

	}
}

在这里插入图片描述

八、数据流

在这里插入图片描述
数据流可以从流中读取或写入java的基本数据类型
DataInputStream数据输入流
DataOutputStream数据输出流

package data_IO_stream;

import java.io.*;

public class Demo {
	public static void main(String[] args) {
		File f = new File("word.txt");
		FileOutputStream out = null;
		DataOutputStream dos = null;
		
		try {
			out = new FileOutputStream(f);
			dos = new DataOutputStream(out);        //将文件流包装成数据流
			
			dos.writeUTF("这是写入字符串数据");           //写入字符串数据
			dos.writeDouble(3.14);                  //写入浮点型数据
			dos.writeBoolean(true);                 //写入布尔类型的数据
			dos.writeInt(123);                      //写入整型数据
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}finally {
			if(dos != null) {
				try {
					dos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if(out != null) {
				try {
					out.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		
		FileInputStream in = null;
		DataInputStream di = null;
		
		try {
			in = new FileInputStream(f);
			di = new DataInputStream(in);
			System.out.println("readUTF()读取数据:" + di.readUTF());       //读文件中的String类型数据
			System.out.println("readDouble()读取数据:" + di.readDouble()); //读文件中的Double类型数据
			System.out.println("readBoolean()读取数据:" + di.readBoolean());
			System.out.println("readInt()读取数据:" + di.readInt());
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}finally {
			if(di != null) {
				try {
					di.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if(in != null) {
				try {
					in.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}	
	}
}

在这里插入图片描述
将一些内容写入文本文档时,里面显示的都是乱码(因为写入的是字节码),那如何去读我们写的数据呢? 要解析字节码, 用到数据输入流,将这个文件的字节码读出来,通过相应的方法将这些字节码解析成我们对应的数据类型

注意:当写入数值类型的值时(比如int和double),尽量不要写在一起

九、字符流转为字节流

在这里插入图片描述
java提供了两种流来处理数据,分别是字节流和字符流。
写代码时可以发现,这两种流的底层机制不同,这就导致字节流和字符流不能互相调用(字节流更偏向底层,而字符流的功能更强大)
java提供了两个用来将字节流封装成字符流的类
InputStreamReader和OutputStreamWriter,这样就可以把字节流变成字符流了
在这里插入图片描述
这两个包装类还提供了一个很重要的功能:字符编码的转换
java能够跨平台,所以能兼容不同的字符集

package byte_transform_char;

import java.io.*;

public class Demo {
	public static void main(String[] args) {
		File f = new File("word.txt");
		FileOutputStream fos = null;
		BufferedWriter bw = null;
		OutputStreamWriter osw = null;
		
		try {
			fos = new FileOutputStream(f);
			osw = new OutputStreamWriter(fos, "GBK");   //字节流转为字符流的桥梁,将数据按照GBK字符集写入
			bw = new BufferedWriter(osw);
			bw.write("你见过洛杉矶凌晨四点的样子吗?");
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}finally {
			if(bw != null) {
				try {
					bw.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if(osw != null) {
				try {
					osw.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if(fos != null) {
				try {
					fos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			
		}
		
		FileInputStream fis = null;
		BufferedReader br = null;
		InputStreamReader isr = null;
		
		try {
			fis = new FileInputStream(f);
			isr = new InputStreamReader(fis, "GBK");
			br = new BufferedReader(isr);
			String str = br.readLine();
			System.out.println("读出的内容为:" + str);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}finally {
			if(br != null) {
				try {
					br.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if(isr != null) {
				try {
					isr.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if(fis != null) {
				try {
					fis.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

在这里插入图片描述
若写入的时候用GBK字符集,读出的时候用UTF-8的字符集,这两个字符集解码的方式是不一样的(虽然在文本文档中看到的是正常的汉字,因为这个文本文档是自动按照GBK来解码的,但是这里是通过代码底层解码,如果它俩不匹配,就会出现乱码

十、流的两种关闭方式

在这里插入图片描述
若不关闭数据流,会造成资源无法释放的问题若访问文件的数据流不关闭,其他数据流就无法访问这个文件了,这是一个非常大的问题(但是我现在把close()写在try…catch语句里也不好,一旦发生异常,关闭的这行代码就执行不到了,同样会造成资源无法释放的问题)
java为了简化这种繁琐的代码,又提供了另外一种关闭方式:使用try语句自动关闭流
try…catch结束后,流会自动关闭(即使执行过程发生了异常,流也会被关闭)

package way_of_close_of_stream;

import java.io.*;

public class Demo {
	public static void main(String[] args) {
		File f = new File("word.txt");
		FileInputStream fis = null;
		BufferedReader br = null;
		InputStreamReader isr = null;
		
		try {
			fis = new FileInputStream(f);
			isr = new InputStreamReader(fis, "GBK");
			br = new BufferedReader(isr);
			
			String str = br.readLine();
			System.out.println("读出的内容为:" + str);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}finally {
			if(br != null) {
				try {
					br.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if(isr != null) {
				try {
					isr.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if(fis != null) {
				try {
					fis.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

在这里插入图片描述
这种写法很繁琐,我们创建的所有流都要显式关闭(而且不能把关闭语句写在一起,这样的话出现异常后面的关闭操作都不会执行),这是数据流的一个弊端。

package way_of_close_of_stream;

import java.io.*;

public class Demo {
	public static void main(String[] args) {
		try(FileInputStream fis = new FileInputStream("word.txt");
				InputStreamReader isr = new InputStreamReader(fis, "GBK");
				BufferedReader br = new BufferedReader(isr);){
			String str = br.readLine();
			System.out.println("读出的内容为:" + str);
		} catch(Exception e) {
			e.printStackTrace();
		}
	}
}

在这里插入图片描述
在try语句这里添加一个圆括号在里面创建流的对象(注意创建顺序),这里finally就不需要写了,因为try…catch结束后这三个流会自动关闭

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值