Java学习记录(中级)——【2】、Java I/O

(1)、文件对象

文件和文件夹都是用 File 代表。

[1]、创建文件对象(直接new)可以用绝对路径和相对路径两种

绝对路径不用说,从盘符开始,如:D:\MyFile\a.txt

相对路径,则是从项目目录开始,如:项目 MyProject 下的相对路径就在这个目录下

[2]、文件对象的常用方法:

方法名 功能
exists() 判断文件是否存在
isDirectory() 判断是否是文件夹
isFile() 判断是否是文件(非文件夹)
length() 文件长度
lastModified() 获取文件最后修改时间
setLastModified() 设置文件修改时间
renameTo() 文件重命名
list() 以字符串数组的形式,返回当前文件夹下的所有文件(不包含子文件及子文件夹)
listFiles() 以文件数组的形式,返回当前文件夹下的所有文件(不包含子文件及子文件夹)
getParent() 以字符串形式返回获取所在文件夹
getParentFile() 以文件形式返回获取所在文件夹
mkdir() 创建文件夹,如果父文件夹不存在,创建就无效
mkdirs() 创建文件夹,如果父文件夹不存在,就会创建父文件夹
createNewFile() 创建一个空文件,如果父文件夹不存在,就会抛出异常
listRoots() 列出所有的盘符c: d: e: 等等
delete() 刪除文件
deleteOnExit() JVM结束的时候,刪除文件,常用于临时文件的删除

(2)、流

流(Stream),就是一系列的数据。

当不同的介质之间有数据交互的时候,JAVA就使用流来实现。
数据源可以是文件,还可以是数据库,网络甚至是其他的程序;

比如读取文件的数据到程序中,站在程序的角度来看,就叫做输入流。
输入流: InputStream
输出流:OutputStream

ä»ä¹æ¯æµ

(3)、字节流,用于以字节的形式读取和写入数据

InputStream     字节输入流的所有类的超类; 
OutputStream  字节输出流的所有类的超类;

[1]、以字节流的形式读取文件内容

InputStream是字节输入流,同时也是抽象类,只提供方法声明,不提供方法的具体实现。
FileInputStream 是InputStream子类,以FileInputStream 为例进行文件读取

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class InputStreamTest {

	public static void main(String[] args) {

		try {
			// 准备文件a.txt其中的内容是AB,对应的ASCII分别是65 66
			File f = new File("a.txt");
			// 创建基于文件的输入流
			FileInputStream fis = new FileInputStream(f);
			// 创建字节数组,其长度就是文件的长度
			byte[] all = new byte[(int) f.length()];
			// 以字节流的形式读取文件所有内容
			fis.read(all);
			for (byte b : all) {
				// 打印出来是65 66
				System.out.println(b);
			}

			// 每次使用完流,都应该进行关闭
			fis.close();

		} catch (IOException e) {
			e.printStackTrace();
		}

	}

}

[2]、以字节流的形式向文件写入数据

OutputStream是字节输出流,同时也是抽象类,只提供方法声明,不提供方法的具体实现。
FileOutputStream 是OutputStream子类,以FileOutputStream 为例向文件写出数据;

【注意: 如果文件 b.txt 不存在,写出操作会自动创建该文件;但是如果是文件 dir/b.txt,而目录 dir 又不存在,则会抛出异常】

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

public class OutputStreamTest {

	public static void main(String[] args) {
		try {
			// 创建文件对象b.txt
			File f = new File("b.txt");
			// 准备长度是2的字节数组,用88,89初始化,其对应的字符分别是X,Y
			byte data[] = { 88, 89 };

			// 创建基于文件的输出流
			FileOutputStream fos = new FileOutputStream(f);
			// 把数据写入到输出流
			fos.write(data);
			// 关闭输出流
			fos.close();

		} catch (IOException e) {
			e.printStackTrace();
		}

	}

}

执行结果:

(4)、流的关闭

所有的流,无论是输入流还是输出流,使用完毕之后,都应该关闭。 如果不关闭,会产生对资源占用的浪费。 当量比较大的时候,会影响到业务的正常开展。

如果没有关闭流的话,eclipse会进行提醒(黄色下划线):↓ ↓ ↓ 

[1]、在 try 块中关闭流

在try的作用域里关闭文件输入流,在前面的示例中都是使用这种方式,这样做有一个弊端;
如果文件不存在,或者读取的时候出现问题而抛出异常,那么就不会执行这一行关闭流的代码,存在巨大的资源占用隐患。 不推荐使用

[2]、在 finally 块中关闭流

这是标准的关闭流的方式
1. 首先把流的引用声明在try的外面,如果声明在try里面,其作用域无法抵达finally.
2. 在finally关闭之前,要先判断该引用是否为空
3. 关闭的时候,需要再一次进行try catch处理

这是标准的严谨的关闭流的方式,但是看上去很繁琐,所以写不重要的或者测试代码的时候,都会采用上面的有隐患try的方式,因为不麻烦~

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

public class CloseStreamTest {

	public static void main(String[] args) {
		FileOutputStream fos = null;
		try {
			// 创建文件对象b.txt
			File f = new File("b.txt");
			// 准备长度是2的字节数组,用88,89初始化,其对应的字符分别是X,Y
			byte data[] = { 88, 89 };

			// 创建基于文件的输出流
			fos = new FileOutputStream(f);
			// 把数据写入到输出流
			fos.write(data);

		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			// 如果流引用不为空,则关闭
			if (fos != null) {
				try {
					// 关闭输出流
					fos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}

	}

}

[3]、使用 try() 的方式关闭流

【这种方式叫做 try-with-resources, 这是从JDK7开始支持的技术;

把流定义在try()里,try,catch或者finally结束的时候,会自动关闭。】

所有的流,都实现了一个接口叫做 AutoCloseable,任何类实现了这个接口,都可以在try()中进行实例化。 并且在try, catch, finally结束的时候自动关闭,回收相关资源。

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

public class CloseStreamWithTryTest {

	public static void main(String[] args) {
		// 创建文件对象b.txt
		File f = new File("b.txt");
		// 把流定义在try()里,try,catch或者finally结束的时候,会自动关闭
		try(FileOutputStream fos = new FileOutputStream(f)) {
			// 准备长度是2的字节数组,用88,89初始化,其对应的字符分别是X,Y
			byte data[] = { 88, 89 };
			// 把数据写入到输出流
			fos.write(data);

		} catch (IOException e) {
			e.printStackTrace();
		}

	}

}

(5)、字符流,专门用于以字符的形式读取和写入数据

Reader    字符输入流的所有类的超类;
Writer      字符输出流的所有类的超类;

[1]、使用字符流读取文件

FileReader 是Reader子类,以FileReader 为例进行文件读取

import java.io.File;
import java.io.FileReader;
import java.io.IOException;

public class ReaderTest {
	
	public static void main(String[] args) {
        // 准备文件a.txt其中的内容是【每一个字都是一个字符,Java采用的是Unicode编码,中文汉字也是一个字符】
        File f = new File("a.txt");
        // 创建基于文件的Reader
        try (FileReader fr = new FileReader(f)) {
            // 创建字符数组,其长度就是文件的长度
            char[] all = new char[(int) f.length()];
            // 以字符流的形式读取文件所有内容
            fr.read(all);
            for (char b : all) {
                // 打印出来是:【每 一 个 字 都 是 一 个 字 符 , J a v a 采 用 的 是 U n i c o d e 编 码 , 中 文 汉 字 也 是 一 个 字 符 】
                System.out.print(b + " ");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
 
    }

}

输出结果:

[2]、使用字符流把字符串写入到文件

FileWriter 是Writer的子类,以FileWriter 为例把字符串写入到文件

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

public class WriterTest {
	
	public static void main(String[] args) {
        // 准备文件c.txt
        File f = new File("c.txt");
        // 创建基于文件的Writer
        try (FileWriter fr = new FileWriter(f)) {
            // 创建内容字符串,转换为字符数组
            char[] all = new String("每一个汉字都是一个字符!").toCharArray();
            // 以字符流的形式将所有内容写入文件
            fr.write(all);
        } catch (IOException e) {
            e.printStackTrace();
        }
 
    }

}

执行结果:

(6)、编码问题

[1]、常见编码

工作后经常接触的编码方式有如下几种:
ISO-8859-1 ASCII 数字和西欧字母
GBK GB2312 BIG5 中文
UNICODE (统一码,万国码)

其中
ISO-8859-1 包含 ASCII
GB2312 是简体中文,BIG5是繁体中文,GBK同时包含简体和繁体以及日文。
UNICODE 包括了所有的文字,无论中文,英文,藏文,法文,世界所有的文字都包含其中

[2]、UNICODE和UTF

UNICODE每个数字都是很长的(4个字节),因为不仅要表示字母,还要表示汉字等。

如果完全按照UNICODE的方式来存储数据,就会有很大的浪费。
比如在ISO-8859-1中,a 字符对应的数字是0x61
而UNICODE中对应的数字是 0x00000061,倘若一篇文章大部分都是英文字母,那么按照UNICODE的方式进行数据保存就会消耗很多空间

在这种情况下,就出现了UNICODE的各种减肥子编码, 比如UTF-8对数字和字母就使用一个字节,而对汉字就使用3个字节,从而达到了减肥还能保证健康的效果

UTF-8,UTF-16和UTF-32 针对不同类型的数据有不同的减肥效果,一般说来UTF-8是比较常用的方式

[3]、Java使用的就是UNICODE编码

写在.java源代码中的汉字,在执行之后,都会变成JVM中的字符。
而这些中文字符采用的编码方式,都是使用UNICODE. "中"字对应的UNICODE是4E2D,所以在内存中,实际保存的数据就是十六进制的0x4E2D, 也就是十进制的20013。

[4]、用FileInputStream 字节流正确读取中文

为了能够正确的读取中文内容
1. 必须了解文本是以哪种编码方式保存字符的
2. 使用字节流读取了文本后,再使用对应的编码方式去识别这些数字,得到正确的字符
如本例,一个文件中的内容是字符 '中' ,编码方式是UTF-8,那么读出来的数据一定是E4B8AD。
再使用UTF-8编码方式识别E4B8AD,就能正确的得到字符 '中' 。

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class EncodingTest {
	
	public static void main(String[] args) {
        File f = new File("d.txt");
        try (FileInputStream fis = new FileInputStream(f);) {
            byte[] all = new byte[(int) f.length()];
            fis.read(all);
   
            //文件中读出来的数据是
            System.out.println("文件中读出来的数据是:");
            for (byte b : all)
            {
                int i = b&0x000000ff;  //只取16进制的后两位
                System.out.println(Integer.toHexString(i));
            }
            System.out.println("把这个数字,以UTF-8的编码方式读取:");
            String str = new String(all,"UTF-8");
            System.out.println(str);
        } catch (IOException e) {
            e.printStackTrace();
        }
   
    }

}

输出结果:

(7)、缓存流

以介质是硬盘为例,字节流和字符流的弊端: 
在每一次读写的时候,都会访问硬盘。 如果读写的频率比较高的时候,其性能表现不佳。

为了解决以上弊端,采用缓存流!!!


缓存流在读取的时候,会一次性读较多的数据到缓存中,以后每一次的读取,都是在缓存中访问,直到缓存中的数据读取完毕,再到硬盘中读取。 
【就好比吃饭,不用缓存就是每吃一口都到锅里去铲。用缓存就是先把饭盛到碗里,碗里的吃完了,再到锅里去铲 】

缓存流在写入数据的时候,会先把数据写入到缓存区,直到缓存区达到一定的量,才把这些数据,一起写入到硬盘中去。

【就好比做饭,不用每从米袋中抓一把米都倒到锅里;用缓存就是先把米抓到碗里,碗里装满了,再倒到锅里。】

按照这种操作模式,就不会像字节流,字符流那样每读/写一个字节都访问硬盘,从而减少了IO操作。

[1]、使用缓存流读取数据

缓存字符输入流 BufferedReader 可以一次读取一行数据;

缓存流必须建立在一个存在的流的基础上!!!

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;

public class BufferedStreamTest {

	public static void main(String[] args) {
		
		// 准备文件e.txt其中的内容是
		// 测试缓存流的文本,第一行
		// 测试缓存流的文本,第二行
		// 测试缓存流的文本,第三行
		File f = new File("e.txt");

		// 创建文件字符流
		// 缓存流必须建立在一个存在的流的基础上
		try (    FileReader fr = new FileReader(f);
                         BufferedReader br = new BufferedReader(fr);
                    ) {
			while (true) {
				// 一次读一行
				String line = br.readLine();
				if (line == null) {
					break;
				} else {
					System.out.println(line);
				}
			}

		} catch (Exception e) {
			e.printStackTrace();
		}

	}

}

输出结果:

[2]、使用缓存流写出数据

PrintWriter 缓存字符输出流, 可以一次写出一行数据;

缓存流必须建立在一个存在的流的基础上!!!

import java.io.File;
import java.io.FileWriter;
import java.io.PrintWriter;

public class PrintWriterStreamTest {
	
	public static void main(String[] args) {

		// 准备文件f.txt
		File f = new File("f.txt");

		// 创建文件字符流
		// 缓存流必须建立在一个存在的流的基础上
		try (FileWriter fw = new FileWriter(f); PrintWriter pw = new PrintWriter(fw);) {
			// 向文件f.txt中写入5行数据
			for (int i = 0; i < 5; i++) {
				String line = "这是写入的第" + i + "行数据";
				pw.println(line);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

}

执行结果:

[3]、flush

有的时候,需要立即把数据写入到硬盘,而不是等缓存满了才写出去。 这时候就需要用到 flush .

import java.io.File;
import java.io.FileWriter;
import java.io.PrintWriter;

public class StreamTest {

	public static void main(String[] args) {

		// 准备文件f.txt
		File f = new File("f.txt");

		// 创建文件字符流
		// 缓存流必须建立在一个存在的流的基础上
		try (FileWriter fw = new FileWriter(f); PrintWriter pw = new PrintWriter(fw);) {
			// 向文件f.txt中写入5行数据
			for (int i = 0; i < 5; i++) {
				String line = "这是写入的第" + i + "行数据";
				pw.println(line);
				// 强制把缓存中的数据写入硬盘,无论缓存是否已满
				pw.flush();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

}

执行结果:

(8)、数据流(属于字节流的一种)

DataInputStream       数据输入流 
DataOutputStream    数据输出流

[1]、利用数据流进行字符串的读写

使用数据流的writeUTF()和readUTF() 可以进行数据的格式化顺序读写;

【注: 要用DataInputStream 读取一个文件,这个文件必须是由DataOutputStream 写出的,否则会出现EOFException,因为DataOutputStream 在写出的时候会做一些特殊标记,只有DataInputStream 才能成功的读取。】

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class DataStreamTest {
	      
	    public static void main(String[] args) {
	        write();
	        read();
	    }
	 
	    private static void read() {
	        File f =new File("g.txt");
	        try (
	                FileInputStream fis  = new FileInputStream(f);
	                DataInputStream dis =new DataInputStream(fis);
	        ){
	            boolean b= dis.readBoolean();
	            int i = dis.readInt();
	            String str = dis.readUTF();
	             
	            System.out.println("读取到布尔值:"+b);
	            System.out.println("读取到整数:"+i);
	            System.out.println("读取到字符串:"+str);
	 
	        } catch (IOException e) {
	            e.printStackTrace();
	        }
	         
	    }
	 
	    private static void write() {
	        File f =new File("g.txt");
	        try (
	                FileOutputStream fos  = new FileOutputStream(f);
	                DataOutputStream dos =new DataOutputStream(fos);
	        ){
	            dos.writeBoolean(true);
	            dos.writeInt(300);
	            dos.writeUTF("中文");
	        } catch (IOException e) {
	            e.printStackTrace();
	        }
	         
	    }

}

执行结果:

(9)、对象流

对象流指的是可以直接把一个对象以流的形式传输给其他的介质,比如硬盘 

一个对象以流的形式进行传输,叫做序列化。 该对象所对应的类,必须是实现Serializable接口。

序列化一个对象:

【注:把一个对象序列化有一个前提是:这个对象的类,必须实现了Serializable接口】

创建一个User对象 user , 把该对象序列化到一个文件user.user , 然后再通过序列化把该文件转换为一个User对象

User.java :

import java.io.Serializable;
public class User implements Serializable{
	//表示这个类当前的版本,如果有了变化,比如新设计了属性,就应该修改这个版本号
	private static final long serialVersionUID = 1L;
	public String username;
	public int age;
}

ObjectStreamTest.java :

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class ObjectStreamTest {
	
	public static void main(String[] args) {
		
		//创建一个User对象
        //要把User对象直接保存在文件上,务必让User类实现Serializable接口
		User user = new User();
		user.username = "张三";
		user.age = 18;
		
		//准备一个文件用于保存该对象
		File f = new File("h.user");
		
		try(
				//创建对象输出流
				FileOutputStream fos = new FileOutputStream(f);
				ObjectOutputStream oos = new ObjectOutputStream(fos);
				//创建对象输入流 
				FileInputStream fis = new FileInputStream(f);
				ObjectInputStream ois = new ObjectInputStream(fis);
		    )
		{
			oos.writeObject(user);	// 写出对象
			User user2 = (User) ois.readObject();	// 读入对象
			System.out.println(user2.username + "," + user2.age);
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		
	}

}

运行结果:

(10)、流关系图

æµå³ç³»å¾

猜你喜欢

转载自blog.csdn.net/qq_37164975/article/details/82620873