Java基础:文件管理与I/O流

Java语言使用File类对文件和目录进行操作,查找文件时需要实现FilenameFilter或FileFilter接口。另外,读写文件内容可以通过FileInputStreamFileOutputStreamFileReaderFileWriter类实现。

一、File类

File 类是 Java 处理文件 I/O 的基础。这个抽象既能表示文件,也能表示目录。也就是说文件和目录是可以通过File封装成对象的。

1.1 File类构造函数

  • File(String path):如果path是实际存在的路径,则该File对象表示的是目录;如果path是文件名,则该File对象表示的是文件
  • File(String path, String name):path是路径名,name是文件名。
  • File(File dir, String name):dir是路径对象,name是文件名
	public void test(){
		// File(String pathname):通过一个路径名创建File对象
		File file = new File("D:\\demo\\a.txt"); 
		System.out.println(file.toString());
		// File(String parent, String child): 通过父路径和子路径名创建File对象
		File file2 = new File("D:\\demo", "a.txt");
		System.out.println(file2.toString());
		// File(File parent, String child):通过父路径File对象和子路径名创建File对象
		File file3 = new File("D:\\demo");
		System.out.println(file3.toString());
		File file4 = new File(file3, "a.txt");
		System.out.println(file4.toString());
	}

         程序运行结果:

1.2 File类创建功能

方法名说明
boolean createNewFile()

当具有该名称的文件不存在时,创建一个由该抽象路径命名的新空文件

如果文件不存在,就创建文件并返回true

如果文件存在,就返回false

boolean mkdir()

创建由此抽象路径名命名的目录

如果目录不存在,就创建文件并返回true

如果目录存在,就返回false

boolean mkdirs()

创建由此抽象路径名命名的目录,包含任何必须但不存在的父目录

如果目录不存在,就创建文件并返回true

如果目录存在,就返回false

	/*
	 * 创建功能:
	 * A:创建文件,如果文件不存在,就创建。存在,就不创建。
	 * 		public boolean createNewFile() throws IOException
	 * 		
	 * B:创建文件夹,如果文件夹不存在,就创建。存在,就不创建。
	 * 	 public boolean mkdir() 单级文件夹
	 *   public boolean mkdirs() 多级文件夹
	 *   
	 * 到底创建的是文件还是文件夹,取决于你使用的方法。不取决于后缀名。
	 */
	 public void CreatFile() throws IOException{
		// 创建File文件
		File file = new File("d:\\a.txt");  //File:文件和目录名的抽象表现形式
		// public boolean createNewFile() throws IOException
		System.out.println("createNewFile:" + file.createNewFile());
		
		// 创建目录
	    File file2 = new File("d:\\demo");
		System.out.println("mkdir:" + file2.mkdir());
		
		// 创建多级目录
		File file3 = new File("d:\\ccc\\dddd\\eee\\fff");
		System.out.println("mkdirs:" + file3.mkdirs());
	 }

1.3 File类判断和获取功能

方法名说明
public boolean isDirectory()测试此抽象路径名表示的File是否为目录
public boolean isFile()测试此抽象路径名表示的File是否为文件
public boolean exists()测试此抽象路径名表示的File是否存在
public String getAbsolutePath()返回抽象路径名的绝对路径名字符串
public String getPath()将此抽象路径名转换为路径名字符串
public String getName()返回由此抽象路径名表示的文件或目录的名称
public String[] list()返回此抽象路径名表示的目录中的文件和目录的名称字符串数组
public File[] listFiles()返回此抽象路径名表示的目录中的文件和目录的File对象数组

1.4 File类的删除功能

方法名说明
public boolean delete()删除由此抽象路径名表示的文件或目录

二、流

        Java将数据的输入输出(I/O)操作当作“流”来处理,“流”是一组有序的数据序列。“流”分为两种形式:输入流和输出流,从数据源中读取数据是输入流,将数据写入到目的地是输出流。

2.1 Java流的设计理念

       如下图,数据输入的数据源有多种形式,如文件、网络和键盘等,键盘是默认的标准输入设备。而数据输出的目的地也有多种形式,如文件、网络和控制台,控制台是默认的标准输出设备。

 所有的输入形式都抽象为输入流,所有的输出形式都抽象为输出流,它们与设备无关。

2.2 流的分类

        以字节为单位的流称为字节流,以字符为单位的流称为字符流。Java SE提供4个顶级抽象类,两个字节流抽象类:InputStream和OutputStream;两个字符流抽象类:Reader和Writer。 

字节输入流:

        字节输入流根类是InputStream,其子类如下图所示:

yH5BAAAAAAALAAAAAAOAA4AAAIMhI+py+0Po5y02qsKADs=wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw==

InputStream主要方法如下:

  • int read():读取一个字节,返回0到255范围内的int字节值。如果已经到达流末尾,而且没有可用的字节,则返回值-1。 
  • int read(byte b[] ):读取多个字节,数据放到字节数组b中,返回值为实际读取的字节的数
    量,如果已经到达流末尾,而且没有可用的字节,则返回值-1。
  • int read(byte b[ ], int off, int len):最多读取len个字节,数据放到以下标off开始字节数组b中,将读取的第一个字节存储在元素b[off]中,下一个存储在b[off+1]中,依次类推。返回值为实际读取的字节的数量,如果已经到达流末尾,而且没有可用的字节,则返回值-1。
  • void close():流操作完毕后必须关闭。

字节输入流FileInputStream类使用示例:


/*
 * 字节输入流读数据步骤:
 * A:创建字节输入流对象
 * B:调用方法,读取数据
 * C:释放资源
 * 
 * 字节输入流读取数据有两种方式:
 * A:一次读取一个字节
 * B:一次读取一个字节数组
 * 
 * 一次读取一个字节:public int read()
 */
public class InputStreamTest {

	public static void main(String[] args) {
		InputStreamTest inStream = new InputStreamTest();
		try {
			inStream.CopyFileTest();
		} catch (IOException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}
	}
	
	/**
	 * 一次读取一个字节,循环读取。当读取到文件末尾时,返回值为-1
	 * @throws IOException
	 */
    public void InputTest() throws IOException{
    	File file = new File("F:\\a.txt");
    	// 创建字节输入流对象
    	// FileInputStream fis = new FileInputStream("fis.txt");
    	FileInputStream fis = new FileInputStream(file);
		
		int by = 0;
		//fis.read() 从输入流中读取一个字节返回int型变量,若到达文件末尾,则返回-1
		//读取的是字节,为何返回int型数据:
		/*   1、方法解释中的-1相当于是数据字典告诉调用者文件已到底,可以结束读取了,这里的-1是Int型
         *   2、那么当文件未到底时,我们读取的是字节,若返回byte类型,那么势必造成同一方法返回类型不同的情况,这是不允许的
         *   3、我们读取的字节实际是由8位二进制组成,二进制文件不利于直观查看,可以转成常用的十进制进行展示,因此需要把读取的字节从二进制转成十进制整数,故返回int型
         *   4、 因此结合以上3点,保证返回类型一致以及直观查看的情况,因此该方法虽然读取的是字节但返回int型
		 * 
		 * */
		while ((by = fis.read()) != -1) {  //从输入流中读取一个字节返回int型变量,若到达文件末尾,则返回-1
			System.out.print((char) by);
		}
		
		fis.close();
    }
    
    /*
     * 为了提高读取数据的效率,就有了第二种方案。
     * 一次读取一个字节数组:public int read(byte[] b):返回的是实际的读取长度,把数据读取到字节数组中
     */
    public void InputTest2() throws IOException{
    	// 创建字节输入流对象
    	FileInputStream fis = new FileInputStream("C:\\Users\\lenovo\\Pictures\\变形记.txt");
        
		byte[] bys = new byte[1024 * 1024]; // 这里的数据一般是1024或者其整数倍
		int len = 0;
		while ((len = fis.read(bys)) != -1) {
			System.out.println(new String(bys, 0, len));
			System.out.println();
		}
		fis.close();
    }
    
    public void CopyFileTest() throws IOException{
    	File file = new File("C:\\Users\\lenovo\\Pictures\\变形记.txt");
    	File file2 = new File("F:\\b.txt");
    	
    	FileInputStream fis = new FileInputStream(file);
    	FileOutputStream fos = new FileOutputStream(file2);
    	
    	//读取数据的存放地址
    	byte by[] = new byte[1024];
    	int len=0;
    	while((len = fis.read(by))!=-1){
    		System.out.println(new String(by, 0, len));
    		//写数据
    		fos.write(by,0,len);
    	}
    	//释放资源
    	fos.close();
    	fis.close();
    }
}

字节输出流

         字节输出流根类是OutputStream,其子类如下图所示:

OutputStream主要方法如下:

  •  void write(int b):将b写入到输出流,b是int类型占有32位,写入过程是写入b 的8个低位,b的24个高位将被忽略。
  • void write(byte b[ ]):将b.length个字节从指定字节数组b写入到输出流。
  • void write(byte b[ ], int off, int len):把字节数组b中从下标off开始,长度为len的字节写入到输出流。
  • void flush():刷空输出流,并输出所有被缓存的字节。由于某些流支持缓存功能,该方法将把缓存中所有内容强制输出到流中
  • void close( ):流操作完毕后必须关闭。

 字节输出流FileOutputStream类使用示例: 

/*
 * 
 * 需求:请把一句话:"helloworld"写入一个文本文件。
 * 通过简单的分析后,我们找到了OutputStream。
 * 由于OutputStream是一个抽象类,所以我们应该找其子类来学习。
 * 学习前,我们先回想一个问题,就是硬盘上的文件,java提供了哪个类表示呢?File
 * 再结合现在的字节输出流OutputStream,两个拼接一下就有了一个新的名字:FileOutputStream
 * 
 * FileOutputStream的构造方法:
 * 		FileOutputStream(File file)
 * 		FileOutputStream(String name)
 * 
 * 字节输出流操作步骤:
 * 		A:创建字节输出流对象
 * 		B:调用写数据功能写数据
 * 		C:释放资源
 * 
 * 练习:请把字节的名字写到一个文本文件
 */
public class OutStreamTest {
	
	public void WriteTxt() throws IOException{
		//创建字节输出流
		/*
		 * 创建字节输入流对象做了哪些事情呢? A:调用系统功能创建了一个文件a.txt B:创建了一个对象fos
		 * C:把fos对象指向了a.txt文件
		 */
		FileOutputStream fos = new FileOutputStream("F:\\a.txt");  
		// 写数据
		// public void write(int b):一次写一个字节的数据
		// fos.write(57);
		// fos.write(55);
		// fos.write(97);

		// public void write(byte[] b):一次写一个字节数组的数据
		// byte[] bys = {97,98,99,100,101};
		// fos.write(bys);
		// 曾经我们讲解过,可以通过String得到byte[]
		// byte[] bys = "abcde".getBytes();
		// fos.write(bys);
		fos.write("wyf12".getBytes());
		
		// public void write(byte[] b,int off,int len):一次写一个字节数组的一部分
		// fos.write("helloworld".getBytes(), 0, 5);
		// fos.write("helloworld".getBytes(), 0, "helloworld".length());
		
		//释放资源
		//关闭此文件输出流并释放与此流有关的所有系统资源。
		/*
		 * 两件事情:
		 * A:关闭流对象(流对象不可以继续在写数据了)
		 * B:释放与此流相关的资源(通知系统去释放与此流相关的资源)
		 */
		fos.close();
		System.out.println("完成!");
	}
	
	/*
	 * 两个小问题:
	 * A:实现数据的换行?
	 * 		不同的系统,针对换行符号的识别是不一样的。
	 * 		Mac:		\r
	 * 		linux:		\n
	 * 		windows:	\r\n
	 * 		系统自带的记事本软件,只能识别该系统能够识别的换行。
	 * 		而Eclipse自带的记事本,以及Editplus却可以识别任意的换行符。
	 * 
	 * B:实现数据的追加写入? 
	 * 		public FileOutputStream(String name,boolean append)
	 * 		如果第二个参数为 true,则将字节写入文件末尾处,而不是写入文件开始处。
	 */
	public void WriteEnter() throws IOException{
		File file = new File("F:\\a.txt");
		FileOutputStream fos = new FileOutputStream(file,true);  //写入的方式是向末尾追加数据
		
		for(int i=0;i<10;i++){
			fos.write(("hello!"+String.valueOf(i)+" \r\n").getBytes());
		}
		
		// 释放资源
		fos.close();
	}
	
	public static void main(String[] arg){
		OutStreamTest test = new OutStreamTest();
		try {
			test.WriteTxt();
			test.WriteEnter();
		} catch (IOException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}
	}
}


        由上面的FileInputStream示例和FileOutputStream示例可以看出,以数组的方式读写数据比一次一个字节的读写方式快的多,因此Java本身在设计的时候,就设计出内置数组的缓冲区流!

        字节缓冲输入流BufferedInputStream和字节缓冲输出流BufferedOutputStream示例代码如下:

/*
 * 由于数组的方式一次比一个字节的方式快很多,所以,java本身在设计的时候,也考虑到了。
 * 就设计出了内置数组的缓冲区流。
 * 字节缓冲输入流
 * 		BufferedInputStream
 * 字节缓冲输出流
 * 		BufferedOutputStream
 * 
 * 通过看构造方法,我们发现,缓冲流不能直接操作文件。
 * 是建立在基本的操作流之上的。
 * 所以,这种流也被称之为高级流。
 */
public class BufferInOutputTest {
	
	//输入缓冲流。
	public void BufferInTest() throws IOException{
		// 读数据
		BufferedInputStream bis = new BufferedInputStream(new FileInputStream("F:\\a.txt"));
		int by=0;
		while((by=bis.read())!=-1){  //一个字节一个字节读
			System.out.print((char)by);
		}
		System.out.println("---------------------------");
		
		bis = new BufferedInputStream(new FileInputStream("F:\\a.txt"));
		byte[] bys = new byte[1024];
		int len = 0;
		while ((len = bis.read(bys)) != -1) {  
			System.out.print(new String(bys, 0, len));
		}
		bis.close();
	}
	
	//输出缓冲流
	public void BufferOutTest() throws IOException{
		//写数据
		BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("F:\\a.txt",true));
		bos.write("WangYunfei\r\n".getBytes());
	    bos.close(); //释放资源
	}
	
	public static void main(String[] arg){
		BufferInOutputTest bf = new BufferInOutputTest();
		try {
			bf.BufferOutTest();
			bf.BufferInTest();
		} catch (IOException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}
	}
}

对比四种不同读写数据方式的效率:


/*
 * 数据源:
 * 		F:\\哥有老婆.mp4
 * 目的地:
 * 		项目路径下D:\\copy.mp4
 * 
 * 四种方式:
 * A:基本字节流一次读写一个字节	37100毫秒
 * B:基本字节流一次读写一个字节数组	共耗时:51毫秒
 * C:高效字节流一次读写一个字节	共耗时:368毫秒
 * D:高效字节流一次读写一个字节数组	共耗时:16毫秒
 */
public class IOReadWrite {

	public static void main(String[] arg){
		IOReadWrite ioRw = new IOReadWrite();
		try {
			ioRw.method4();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	//方法1: 基本字节流一次读写一个字节(FileInputStream\FileOutputStream)
	public void method1() throws IOException{
		File file = new File("F:\\哥有老婆.mp4");
		File file2 = new File("D:\\copy1.mp4");
		//基本文件输入字节流
		FileInputStream fis = new FileInputStream(file);
		//基本文件输出字节流
		FileOutputStream fos = new FileOutputStream(file2);
		
		int by;
		long startTime = System.currentTimeMillis(); //获取开始时间
		while((by = fis.read())!=-1){  //一个字节一个字节读取
			//将读出的一个字节利用输出流写入文件
			fos.write(by);
		}
		long endTime = System.currentTimeMillis(); //获取结束时间
		//释放资源
		fis.close();
		fos.close();
		System.out.println("程序运行时间:" + (endTime - startTime) + "ms");
	}
    
	//方法2: 基本字节流一次读写一个字节数组(FileInputStream\FileOutputStream)
	public void method2() throws IOException{
		File file = new File("F:\\哥有老婆.mp4");
		File file2 = new File("D:\\copy2.mp4");
		
		//定义基本字节输入输出流
		FileInputStream fis = new FileInputStream(file);
		FileOutputStream fos = new FileOutputStream(file2);
		
		byte[] by = new byte[1024];
		int len=0;
		long startTime = System.currentTimeMillis(); //获取开始时间
		while((len = fis.read(by))!=-1){
			fos.write(by, 0, len);
		}
		long endTime = System.currentTimeMillis(); //获取结束时间
		fos.close();
		fis.close();
		System.out.println("程序运行时间:" + (endTime - startTime) + "ms");
	}
	
	//方法3:  高效字节流一次读写一个字节 (BufferedInputStream\BufferedOutputStream)
	public void method3() throws IOException{
		File file = new File("F:\\哥有老婆.mp4");
		File file2 = new File("D:\\copy3.mp4");
		
		//定义输入输出高效字节流
		BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
		BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file2));
		
		int by;
		long startTime = System.currentTimeMillis(); //获取开始时间
		while((by = bis.read())!=-1){
			bos.write(by);
		}
		long endTime = System.currentTimeMillis(); //获取结束时间
		
		//释放资源
		bos.close();
		bis.close();
		System.out.println("程序运行时间:" + (endTime - startTime) + "ms");
	}
	
	//方法4: 高效字节流一次读写一个字节数组 (最快的方式)(BufferedInputStream\BufferedOutputStream)
	public void method4() throws IOException{
		File file = new File("F:\\哥有老婆.mp4");
		File file2 = new File("D:\\copy4.mp4");
		
		BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
		BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file2));
		
		byte[] by=new byte[1024];
		int len;
		long startTime = System.currentTimeMillis();
		while((len = bis.read(by))!=-1){
			bos.write(by, 0, len);
		}
		long endTime = System.currentTimeMillis();
		
		//释放资源
		bis.close();
		bos.close();
		System.out.println("程序运行时间:" + (endTime - startTime) + "ms");
	}
}

字符输入流

        字符输入流根类是Reader,这类流以16位的Unicode编码表示的字符为基本处理单位。其子类如下图所示:

Reader主要方法如下:

  • int read():读取一个字符,返回值范围在0~65535(0x00~0xffff)之间。如果因为已经到达流末尾,则返回值-1。
  • int read(char[] cbuf):将字符读入到数组cbuf中,返回值为实际读取的字符的数量,如果因为已经到达流末尾,则返回值-1。
  • int read(char[] cbuf, int off, int len):最多读取len个字符,数据放到以下标off开始字符数组 cbuf中,将读取的第一个字符存储在元素cbuf[off]中,下一个存储在cbuf[off+1]中,依次类推。返回值为实际读取的字符的数量,如果因为已经到达流末尾,则返回值-1。
  • void close():流操作完毕后必须关闭。

 字符输出流

        字符输出流根类是Writer,这类流以16位的Unicode编码表示的字符为基本处理单位,下图为其子类。

 Writer主要方法如下:

  • void write(int c):将整数值为c的字符写入到输出流,c是int类型占有32位,写入过程是写入c的16个低位,c的16个高位将被忽略。
  • void write(char[] cbuf):将字符数组cbuf写入到输出流。
  • void write(char[] cbuf, int off, int len):把字符数组cbuf中从下标off开始,长度为len的字符写入到输出流。
  • void write(String str):将字符串str中的字符写入输出流。
  • void write(String str,int off,int len):将字符串str中从索引off开始处的len个字符写入输出流。
  • void flush():刷空输出流,并输出所有被缓存的字符。由于某些流支持缓存功能,该方法将把缓存中所有内容强制输出到流中。
  • void close( ):流操作完毕后必须关闭。 


InputStreamReader、BufferedReader及OutputStreamWriter、BufferedWriter使用示例:

public class IOReadWriteTest {

	public static void main(String[] arg){
		IOReadWriteTest ioRw = new IOReadWriteTest();
		File file = new File("C:\\Users\\lenovo\\Pictures\\格列佛游记.txt");
		//File file = new File("F:\\a.txt");
		try {
			ioRw.GetTxt(file);
			//ioRw.GetDataToArray(file);
		} catch (IOException e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}
	}
	
	//获取Txt文件
	public void GetTxt(File file) throws IOException{
		//InputStreamReader(字节流与字符流之间的桥梁,可以指定编码格式)(因此InputStreamReader构造函数中有FileInputStream(字节流))
		InputStreamReader isr = new InputStreamReader(new FileInputStream(file),"UTF-8");
		//定义高效字符流
		BufferedReader br = new BufferedReader(isr);
		
		//方法1:高效字符流一行一行获取
//		String line = null;
//		while((line = br.readLine())!=null){
//			System.out.println(line);
//		}
		//方法2: 高效字符流一次获取一个数组
//		char[] ch = new char[1024];
//		int len=0;
//		while((len=br.read(ch))!=-1){
//			System.out.print(new String(ch,0,len));
//		}
		//方法3:高效字符流一次获取一个字符
		int len = 0;
		while((len = br.read())!=-1){
			System.out.print((char)len);
		}
		
		br.close();
	}
	/*
	 * 把ArrayList集合中的字符串数据存储到文本文件
	 * 
	 * 数据源:
	 * 		ArrayList
	 * 目的地:
	 * 		array.txt
	 */
	public void GetArrayString(File file) throws IOException{
		ArrayList<String> arr = new ArrayList<String>();
		arr.add("wang");
		arr.add("yun");
		arr.add("fei");
		
		//创建OutputStreamWriter类,将arr中的字符串按照指定的编码格式写入文本文件
		OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(file,true),"UTF-8");
		BufferedWriter bw =new BufferedWriter(osw);
		
		for(String s:arr){
			bw.write(s);
			bw.newLine();
			bw.flush();
		}
		bw.flush();
		//释放资源,在释放资源之前要先刷新
		bw.close();
	}
	
	/*
	 * 从文本文件中读取数据(每一行为一个字符串数据)到集合中,并遍历集合
	 * 
	 * 数据源:
	 * 		array.txt
	 * 目的地:
	 * 		ArrayList
	 */
	public void GetDataToArray(File file) throws IOException{
		//创建InputStreamReader类,读取txt文件中的内容
		InputStreamReader isr = new InputStreamReader(new FileInputStream(file),"UTF-8");
		BufferedReader br =new BufferedReader(isr);
		
		ArrayList<String> arr = new ArrayList<String>();
		String line = null;
		while((line = br.readLine())!= null){
			arr.add(line);
		}
		
		isr.close();
		br.close();
		
		//遍历集合
		for(String s:arr){
			System.out.println(s);
		}
	}
	
}

 结束:

 IO流分类:
            A:数据流向
               输入流    读数据
               输出流    写数据
            B:数据类型
               字节流
               字符流
            默认情况下,IO流分类说的是按照数据类型分。
   
 IO流的四个基类:
           字节流
               字节输入流    InputStream
               字节输出流    OutputStream
           字符流
                字符输入流    Reader

                字符输出流    Writer

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值