JAVA学习9——IO字节流字符流、属性集、缓冲流

1.IO——字节流

把数据的传输,可以看作是一种数据的流动,以内存为基准,分为输入流和输出流。输入流,即从其他设备(如硬盘)上读取到内存中的流。输出流,即从数据从内存写出到其他设备(如硬盘)上的流。根据数据类型,又可以分为字节流和字符流。
tips:1个非ASCII码字符(>127)=2个字节;1个字节=8个二进制位

IO流的四个顶级父类
在这里插入图片描述

1.1 字节输出流

/*
	java.io.OutputStream 字节输出流
		此抽象类是表示输出字节流的所有类的超类。
	定义了一些子类共性的成员方法:
		-	public void close():关闭输出流并释放与此流相关的任何系统资源。
		-	pubic void flush():刷新此输出流并强制任何缓冲的输出字节被写出。
		-	public void write(byte[] b):将b.length字节从指定的字节数组写入此输出流。
		-	public void write(byte[] b, int off, int len):从指定的字节数组写入len字节,从偏移量off开始输出到此输出流。
		-	public abstaract void write(int b):将指定的字节流输出流。
	
	java.io.FileoutputStream extends OutputStream
	FileOutputStream:文件字节输出流
	作用:把内存中的数据写入到硬盘的文件中

	构造方法:
		FileOutputStream(String name)创建一个向具有指定名称的文件中写入数据的输出文件流。
		FileOutputStream(File file) 创建一个向指定File对象表示的文件中,写入数据的文件输出流。
		参数:写入数据的目的地
			String name:目的地是一个文件的路径;
			File file:目的地是一个文件
		构造方法的作用:
			1.创建一个FileOutputStream对象
			2.会根据构造方法中传递的文件/文件路径,创建一个空的文件;
			3.会把FileOutputStream对象指向创建好的文件
*/

写如数据的原理(内存–>硬盘):java程序–>JVM(java虚拟机)–>OS–>OS调用写数据的方法–>把数据写入到文件中

字节输出流的使用步骤(重点):
1)创建一个FileOutputStream对象,构造方法中传递写入数据的目的地;
2)调用FileOutputStream对象中的方法write,把数据写入到文件中;
3)释放资源(流使用会占用一定的内存,使用完毕把内存清空,提升程序效率)

public class Demo {
	public static void main(String[] args) {
		FileOutputStream fos = new FileOutputStream("C:\\a.txt");
		fos.write(97);
		//写入时,会把10进制转化为二进制,再写入硬盘
		fos.close();
	}
}
/*
注意,任意的文本编辑器,打开文件时,都会查询编码表,把字节转换为字符表示,0-127,会查询ASCII表;其他值,则查询系统默认码表(中文系统中式GBK)
所以上面写入后,打开文件看到的是a字符。
*/

下面是两个一次写多个字节的方法:
- public void write(byte[] b): 将b.length字节从指定的字节数组写入此输出流
注意,如果写的第一个字节是正数(0-127),那么显示时会查询ASCII表;若是负数,则第一个字节和第二个会组成一个中文显示,查询系统默认码表(GBK或UTF-8);
GBK中一个中文是两个字节;UTF-8中一个中文是三个字节。

- public void write(byte[] b, int off, int len):从指定的字节数组写入len字节,从偏移量off开始,输出到此输出流。即把字节数组的一部分写入到文件中,off是数组开始的索引,len是写几个字节。

public class Demo {
	public static void main(String[] args) {
		FileOutputStream fos = new FileOutputStream(new File("C:\\a.txt"));
		fos.write(49);
		fos.write(48);
		fos.write(48);//在文件中显示100
		byte[] bytes = {-65,-66,-67,68,69}; //烤央E
		fos.write(bytes);
		/*
			写入字符的方法:可以用String类中的方法把字符串,转化为字节数组;byte[] getBytes(),把字符串转换为字节数组。
		*/
		byte[] bytes2 = "你好".getBytes();
		fos.write(bytes);
		fos.close();
	}
}

如果我们想要往一个文件追加写一些数据,需要用以下两个函数。

/*
追加写/续写:使用两个参数的构造方法
	FileOutputStream(String name, boolean append) 创建一个向具有指定name的文件中写入数据的输出文件流。
	FileOutputStream(File file, boolean append) 创建一个指定File对象表示的文件中写入数据的文件输出流。
	参数:
		String name, File file:写入数据的目的地
		boolean append:追加写开关
		true:创建对象不会覆盖源文件,继续在文件的末尾追加写数据
		false:创建一个新文件覆盖源文件
*/
public class Demo {
	public static void main(String[] args) throws IOException{
		FileOutputStream fos = new FileOutputStream("C:\\a.txt",true);
		for(int i=0;i<10;i++){
			fos.write("你好".getBytes());
			fos.write("\r\n".getBytes());
			/*
				换行符号:
					windows: \r\n
					linux: /n
					mac: /r
			*/
		}
	}
}

1.2 字节输入流

java.io.InputStream,是字节输入流。此抽象类是表示字节输入流的所有类的超类。它定义的子类共性方法主要有下面三个:

int read()	从输入流中读取数据的下一个字节
int read(byte[] b) 从输入流中读取一定数量的字节,并将其存储在缓冲区数组b中
void close() 关闭此输入流并释放资源

InputStream的其中一个实现类是FileInputStream

java.io.FileInputStream extends InputStream
FileInputStream:文件字节输入流
作用:把硬盘文件中的数据,读取到内存中使用

构造方法:
    FileInputStream(String name)
    FileInputStream(File file)
    参数:读取文件的数据源
    	String name:文件的路径
    	File file:文件
    构造方法的作用:
    	1.会创建一个FileInputStream对象;
    	2.会把FileInputStream对象指定的构造方法中尧都区的文件

读取数据原理:java程序–>JVM–>OS–>OS读取数据的方法–>读取文件

字节输入流的使用步骤(重点):
1)创建FileInputStream对象,构造方法中绑定要读取的数据源;
2)使用FileInputStream对象中的read方法,读取文件
3)释放资源;

FileInputStream fis = new FileInputStream("C:\\a.txt");
int len = fis.read();
System.out.println(len);//读取的是内容的二进制数据转成十进制的数据
//若读取完毕,即读到结束标记,则返回的是-1。


int len = 0 ;//记录读取到的字节
while(len = fis.read())!=-1) {// 布尔表达式
	System.out.print((char)len);//将读到的数据(二进制转为十进制)再转成char
}

字节流中一次读取多个字节的方法
int read(byte[] b) 从输入流中读取一定数量的字节,并将其存储再缓冲区b中。

FileInputStream fis = new FileInputStream("C:\\a.txt");
byte[] bytes = new byte[2];
int len = fis.read(bytes); //起到缓冲作用,存储读取到的多个字节。
System.out.println(len);//每次读取的有效字节个数,若已经读到最后,没有读到新的数据,就会返回-1
System.out.printlnt(new String(bytes));//若读到最后,那么读到的字节将不会变化了。每次新读取的字节,会依次从下标0开始覆盖bytes中的对应值。

若要读取文件中的所有数据,分批次读取,代码如下:

byte[] bytes = new byte[1024];
int len = 0;
while((len = fis.read(bytes))!=-1) {
	//String(byte[] bytes, int offset, int length)  把字节数组的一部分转换为字符串,offset为数组的开始索引。
	System.out.println(new String(bytes, 0 ,len));
}
fis.close();

1.3字节流输入输出综合案例——文件的复制

文件复制的步骤:
1)创建一个字节输入流,构造方法中绑定要读取的数据流;
2)创建一个字节输出流,构造方法中绑定要写入的目的地
3)使用字节输入流对象中的read读取文件;
4)使用字节输出流的方法write,把读取到的字节写入到目的地的文件中;
5)释放资源;

FileInputStream fis = new FileInputStream("C:\\a.jpg);
FileOutputStream fos = new FileOutputStream("D:\\a.jpg);
byte[] bytes = new byte[1024];
int len = 0;
while((len = fis.read(bytes))!=-1) {
	fos.write(bytes,0,len);//这里也可以用fis.read()和fos.write(len),但是由于每个字节都要循环一次,循环次数过多导致程序效率低下。
}
fos.close();//肯定是先读完,再写完,所以先关闭写的fos
fis.close();

2.IO——字符流

当使用字节流读取文本文件时,遇到中文字符时,可能不会显示完整的字符,因为一个中文字符可能占用多个字节存储。字符流就是以字符为单位读写的数据,专门用于处理文本文件。一个中文文字,GBK编码占用两个字节,UTF-8编码占用3个字节。

字符流一次读取一个字符,这个字符包括英文中文和普通符号。

2.1字符输入流Reader

java.io.Reader字符输入流,时字符输入流的顶层父类,定义了一些共性的成员方法,是一个抽象类。

共性的成员方法有三个:
int read() 读取单个字符并返回;
int read(char[] cbuf) 一次读取多个字符,并将字符读入数组;
void close() 关闭该流并释放与之关联的所有资源;

java.io.FileReader extends InputStreamReader extends Reader
FileReader:文件字符输入流
作用:把硬盘文件中的数据以字符的方式读取到内存中

构造方法:
FileReader(String fileName)
FileReader(File file)
这两个构造方法的作用:1)创建一个FileReader对象;2)会把FileReader对象指向要读取的文件

字符输入流使用的步骤:
1)创建FileReader对象,构造方法中绑定要读取的数据源
2)使用FileReader对象中的方法read读取文件
3)释放资源;

 FileReader fr = new FileReader("C:\\a.txt");
 int len = 0;
 while(len = fr.read()) != -1) {
 	System.out.println((char)len);
 }
/*多个字符读取*/
char[] cs = new char[1024];
int len = 0;
while(len = fr.read(cs))!=-1) {
	System.out.println(new String(cs,0,len));
}

 fr.close();

2.2 字符输出流Write

java.io.Writer:字符输出流,是所有字符输出流的最顶层的父类,是一个抽象类。

共性成员方法:
- void write(int c) 写入单个字符
- void write(char[] cbuf) 写入字符数组
- abstact void write(char[] cbuf, int off, int len) 写入字符数组的某一部分,off数组的开始索引,
- void write(String str)写入字符串
- void write(String str,int off, int len)写入字符串的某一部分,off字符串的开始索引
- void flush()刷新该流的缓冲
- void close() 关闭此流,但要先刷新它

java.io.FileWriter extends OutputStreamWriter extends Writer
FileWriter 文件字符输出流
作用:把内存中字符数据写入到文件中

构造方法:
FileWriter(File file)
FileWriter(String fileName)
作用:
1.创建一个FileWriter对象;
2.会根据构造方法中传递的文件/文件的路径,创建文件
3.会把FileWriter对象指向创建好的文件。

字符输出流的使用步骤:
1)创建FileWriter对象,构造方法中绑定要写入数据的目的地;
2)使用FileWriter中的方法write,把数据写入到内存缓冲区中(字符转换为字节的过程)
3)使用FileWriter中的方法flush,把内存缓冲区的数据,刷新到文件中;
4)释放资源(会把内存缓冲区的数据刷新到文件中)

FileWriter fw = new FileWriter("C:\\a.txt");
fw.write(97);
fw.flush();//这时才会把数据刷新到文件中去。

flush和close方法的区别:
flush:刷新缓冲区,流对象可以继续使用;
close:先刷新缓冲区,然后通知系统释放资源,流对象不可继续使用

下面是字符输出流写数据的其他方法

FileWriter fw = new FileWriter("C:\\a.txt");
char[] cs = {'a','b','c'};
fw.write(cs)
fw.write(cs,1,3);
fw.write("string字符串");
fw.write("ssffaa",2,3);

下面是字符输出流的续写和换行

FileWriter(String fileName, boolean append)
FileWriter(File file, boolean append)
参数:
append 续写开关,false的话则会创建新的文件覆盖源文件
换行的话 window下是”\r\n"

2.3 IO中的异常处理

在jdk7之前使用try catch finally处理流中异常,比较繁琐和麻烦。

//为了提高fw的作用域,让finally可以使用,因此在trycatch外创建FileWriter,并赋值为空
FileWriter fw = null;
try{
	//可能出现异常的代码
	fw = new FileWriter("C:\\a.txt",true);
	for(int i=0;i<2;i++) {
		fw.write("HellowWorld+"\r\n");
	}
} catch(IOException e) {
	//异常的处理逻辑
	System.out.println(e);
}finally {
	//一定会执行的代码
	//若创建对象失败了,fw的默认值似乎null,因此调用clos方法就会抛出空指针异常,因此需要判断
	if(fw!=null) {
		try {
			fw.close()
		} catch(IOException e) {
			e.printStackTrace();
		}
	}
}

在JDK7后,在try的后边可以增加一个(),在括号中可以创建流对象,那么这个流对象的作用域就在try中有效了;同时,在try中代码执行完毕后,会自动把流对象释放,就不用写finally了。格式如下:

try(定义流对象;定义流对象){
	可能会产生异常的代码
} catch(异常类变量 变量名) {
	异常的处理逻辑
}

因此上述代码可以简化为

而在JDK9新特性,try的前边可以定义流对象,在try后的()中可以直接引入流对象的名称,在try代码执行完毕后,流对象也可以直接释放掉,不用写finally。

public statci void main(String[] args) throws FileNotFoundException {
	FileInputStream fis = new FileInputStream("c:\\a.txt");
	FileOutputStream fos = new FileOutputStream("d:\\a.txt");
	try(fis;fos) {
		int len = 0;
		while((len = fis.read())!=-1) {
			fos.write(len);
		}
	} catch (IOException e) {
		System.out.println(e);
	}
}

3.属性集

Properties类表示了一个持久的数据集,Properties可以保存在流中或从流中加载,属性列表中每个键以及其对饮都是一个字符串。

/*
	java.util.Properties 集合 extends Hashtable<k,v> implements Map<k,v>
	Properties类表示了一个持久的属性集,Properties可保存在流中,或从流中加载。
	Properties集合是一个唯一和IO流相结合的集合
		可以使用Properties集合中的方法store,把集合中的临时数据,持久化写入硬盘中存储
		可以使用Properties集合中的方法load,把硬盘中保存的文件(键值对),读取到集合中使用。
	属性列表中每个键以及对应值都是一个字符串。
		Properties集合是一个双列集合,key和value默认都是字符串。
*/
/*
	Properties集合存储数据,遍历取出Properties集合中的数据
	Properties常用方法:
		Object setProperty(string key, String value)调用Hashtable的方法put
		String getProperty(String key)通过key找到value值,此方法相当于Map集合中的get(key)方法
		Set<String> stringPropertyNames()返回此属性列表中的键集,其中该键以及对应值是字符串,相当于Map集合中keySet方法。
*/
private static void show01() {
	Properties prop = new Properties();
	Prop.setProperty("lucy","174");
	Prop.setProperty("Nancy","165");
	Set<String> set = Prop.StringPropertyNames();
	for(String key:set) {
		String value = Prop.getProperty(key);
	}
}

/*
可以使用Properties集合中的方法store,把集合中的临时数据,持久化写入硬盘中存储
	void store(OutputStream out, String comments)
	void store(Writer writer, String comments)
	参数:
		OutputStream out:字节流输出,不能写入中文;
		Writer writer:字符输出流,可以写中文
		String comments:注释,用来解释说明保存的文件是做什么的,不能用中文,因为默认Unicode编码,一般使用空字符串
	
	使用步骤:
		1.创建Properties集合对象添加数据;
		2.创建字节输出流/字符输出流对象,构造方法中绑定要输出的目的地
		3.使用Properties集合中的方法store,把集合中临时数据,持久化写入硬盘存储
		4.释放资源
*/
 
 Properties prop = new Properties();
 Prop.setProperty("lucy","174");
Prop.setProperty("Nancy","165");

FileWriter fw = new FileWriter("C:\\a.txt");
prop.store(fw,"save data");//如果这里fw用匿名类来写,就不需要close,因为匿名类用完自动结束和释放
fw.close();

/*
	可以使用Properties集合中的方法load,把硬盘中保存的文件(键值对),读取到集合中使用。
	void load(InputStream inStream)//字节输入流,不能读取中文
	void load(Reader reader)//字节输入流,可以读取中文

	使用步骤:
		1.创建Properties集合对象;
		2.使用Properties集合对象中的方法Load读取保存键值对文件
		3.遍历Properties集合
	注意:
		1.存储键值对的文件值,键域值默认的连接符号可以使用=,空格(其他符号)
		2.存储键值对的文件中,可以使用#进行注释,被注释的不会被读取;
		3.存储键值对的文件中,值和键都是字符串,不用再加引号 
*/

Properties prop = new Properties();
prop.load(new FileReader("C:\\a.txt"); #为了中英文都能读,一般都用字符流
Set<String> set = prop.stringPropertyName();
for (String key : set) {
	String value = prop.getProperty(key);
	System.out.println(key+"="+value); 
}

3. 缓冲流

缓冲流能够更加高效的读写,缓冲流的基本原理,就是在创建流对象时,会创建一个内置的默认大小的缓冲区数组,通过缓冲区读写,减少IO次数,从而提高读写效率。

3.1字节缓冲流

java.io.BufferedOutputStream extends OutputStream
//字节缓冲输出流

使用方法:
1.创建FileOutputStream对象,构造方法中绑定要输出的目的地;
2.创建BufferedOutputStream对象,构造方法中传递FileOutputStream对象,提高FileOutputStream对象效率;
3.使用BufferedOutputStream对象中的方法write,把数据写入到缓冲区
4.使用BufferedOutputStream对象中的方法flush,把内部缓冲区中的数据,刷新到文件中;
5.释放资源(会调用flush方法先刷新数据)

FileOutputStream fos = new FileOutputStream("D:\\a.txt");
BufferedOutputStream bos = new BufferedOutputStream(fos);
bos.write("把数据写入到内部缓冲区中".getBytes());
bos.flush()
bos.close()
java.io.BufferedInputStream extends InputStream
字节缓冲输入流

构造方法
BufferedInputStream(InputStream in)

使用步骤:
1.创建FileInputStream对象,构造方法中绑定要读取的数据源;
2.创建BufferedInputStream对象,构造方法中传递FileInputStream对象,提高FileInputStream对象的读取效率
3.使用BufferedInputStream对象中的方法read,读取文件
4.释放资源

FileInputStream fis = new FileInputStream("D:\\a.txt");
BufferedInputStream bis = new BufferedInputStream(fis);
/*
int len = 0;
while(len = bis.read())!=-1) {
	System.out.println(len);
}
*/

byte[] bytes = new byte[1024];
int len = 0;
while(len = bis.read(bytes))!=-1) {
	System.out.println(new String(bytes,0,len));
}
bis.close();//同时也会把fis关闭

经过测试,缓冲流的效率要比普通流高很多,不论普通流是否有用缓冲区bytes进行一次读写多个。

3.2 字符缓冲流

java.io.BufferedWriter extends Writer
字符缓冲输出流
BufferedWriter(Writer out) 创建一个默认大小输出缓冲区的缓冲字符输出流。

特有成员方法 
	void newLine() 根据不同操作系统写一个行分隔符

使用步骤:
1.创建字符缓冲输出流对象,构造方法中传递字符输出流;
2.调用字符缓冲输出流中的方法write,把数据写入到内存缓冲区中
3.调用字符缓冲输出流中的方法flush,把内存缓冲区中数据刷新到文件中;
4.释放资源

java.io.BufferedReader extends Reader
字符缓冲输入流
构造方法:
	BufferedReader(Reader in)
特有成员方法:
	String readLine() 读取一行文本
	行的终止符号:通过下列字符之一,即可认为某行终止:换行('\n')、回车('\r')或者回车后直接跟换行(\r\n)
	返回值:包含该行内容的字符串,不包含任何终止符,如果已到达流末尾,返回null。

使用步骤:
1.创建字符缓冲输入流对象,构造方法中传递字符输出流。
2.使用字符缓冲输入流对象的方法read/readline读取文本;
3.释放资源

3.3 综合实例——对文本的内容进行排序

步骤:
1.创建一个HashMap集合对象,key:存储每行文本序号;value:存储每行的文本
2.创建字符缓冲输入流对象,构造方法中绑定字符输如流;
3.创建字符缓冲输出流对象,构造方法中绑定字符输出流;
4.使用字符缓冲输入流中的方法readline,逐行读取文本;
5.对读取到的文本进行切割,获取行中的序号和文本内容;
6.把切割好的序号和文本的内容存储到HashMap集合中(key序号是有序的,会自动排序1.2.3.4);
7.遍历hashMap集合,获取每一个键值对
8.把每一个键值对,拼接为一个文本行;
9.把拼接好的文本,使用字符流缓冲输出流中的方法write,写入到文件中
10.释放资源;

//读到一行文本后,文本切割
String[] arr = line.split("\\.");
map.put(arr[0],arr[1]);  

//遍历HashMap集合,获取键值对
for(String key : map.keySet()) {
	String value = map.get(key);
	line = key + "." +value;
	bw.write(line);
	bw.newLine();//换行
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值