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)、流关系图

æµå³ç³»å¾

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

十甫寸木南

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值