IO流的详细概述和应用案例(值得推荐)

IO流的详细概述和应用案例(值得推荐)

一、阐述IO流的概述和分类

IO流概述:
处理设备间数据传输问题
IO流分类:
1、按流向(一定要站在Java程序这边看问题)
–>输入流
–>输出流
2、按类型
–>字节流(图片,视频,音频,等多媒体文件)
–>字符流(.txt结尾的文本文件)

图解:
IO流的详细概述和应用案例(值得推荐)_第1张图片

API总汇:

1、IO【字节流】抽象类和具体类的关系图:适合任意类型的文件读写
IO流的详细概述和应用案例(值得推荐)_第2张图片
2、IO【字符流】抽象类和具体类的关系图:只适合文本类型的文件读写
IO流的详细概述和应用案例(值得推荐)_第3张图片

二、阐述FileOutputStream

1、字节流写数据:

  • OutputStream:此抽象类是表示输出字节流的所有类的超类

  • FileOutputStream:文件输出流是用于将数据写入 File

  • 构造方法:
    FileOutputStream(String name):创建一个向具有指定名称的文件中写入数据的输出文件流。

2、字节流写数据的步骤:
—A:创建字节输出流对象
—B:调用写数据的方法
—C:释放资源

首先我写一个Demo让大家看看:
编写使用FileOutputStream写数据的案例

public class FileOutputStreamDemo {
	public static void main(String[] args) throws IOException {
		//创建字节输出流对象
		FileOutputStream fos = new FileOutputStream("a.txt");
		/*
		 * 创建字节输出流对象做了这样的三件事情:
		 * A:调用系统功能创建了文件
		 * B:创建字节输出流对象
		 * C:让fos这个对象指向a.txt这个文件
		 */
		
		//write(int b) 
		fos.write(65);
		fos.write(66);
		
		//最后我们还要做一个事情
		//close() 关闭此文件输出流并释放与此流有关的所有系统资源。
		fos.close();
	}
}

三、FileOutputStream写数据的三种方式【应用】

1、构造方法:

  • FileOutputStream(String name)
  • FileOutputStream(File file)

  • public void write(int b):一次写一个字节

  • public void write(byte[] b):一次写一个字节数组
    String类中的getByes(),返回值为byte[]数组

  • public void write(byte[] b,int off,int len):一次写一个字节数组的一部分


2、字节流写数据的步骤:
—A:创建字节输出流对象
—B:调用写数据的方法
—C:释放资源

public class FileOutputStreamDemo2 {
	public static void main(String[] args) throws IOException {
		//创建字节输出流对象
		//FileOutputStream(String name) 
		FileOutputStream fos = new FileOutputStream("b.txt");
		//new File(name)
//		FileOutputStream fos = new FileOutputStream(new File("b.txt"));
		
		//FileOutputStream(File file) 
//		File file = new File("b.txt");
//		FileOutputStream fos = new FileOutputStream(file);
//		FileOutputStream fos = new FileOutputStream(new File("b.txt"));
		
		//public void write(int b):一次写一个字节
//		fos.write(65);
		
		//public void write(byte[] b):一次写一个字节数组
//		byte[] bys = {65,66,67,68,69};
//		fos.write(bys);
		//需求:我如果是一个字符串的数据,能写吗?
		//String -- byte[]
		//String类中有一个方法:public byte[] getBytes()
//		byte[] bys = "ABCDE".getBytes();
//		fos.write(bys);
//		fos.write("ABCDE".getBytes());
		
		//public void write(byte[] b,int off,int len):一次写一个字节数组的一部分
		fos.write("ABCDE".getBytes(),0,3);
		
		//释放资源
		fos.close();
	}
}

四、FileOutputStream如何实现换行和追加写数据【应用】

1、如何实现数据的换行?

  • 不同的操作系统,针对换行的符号识别是不一样的。
  • windows:\r\n
  • linux:\n
  • mac:\r

2、如何实现数据的追加写入?

  • 用构造方法带第二个参数是true的情况即可
例如:FileOutputStream fos = new FileOutputStream("c.txt",true);
public class FileOutputStreamDemo3 {
	public static void main(String[] args) throws IOException {
		//创建字节输出流对象
		//FileOutputStream fos = new FileOutputStream("c.txt");
		//FileOutputStream(String name, boolean append) 
		//如果第二个参数为 true,则将字节写入文件末尾处,而不是写入文件开始处
		FileOutputStream fos = new FileOutputStream("c.txt",true);
		
		//调用写数据的方法
		for(int x=0; x<10; x++) {
			fos.write("hello".getBytes());
			//加入换行符号
			fos.write("\r\n".getBytes());
		}
		
		//释放资源
		fos.close();
	}
}

五、FileOutputStream写数据加入异常处理【应用】

public class FileOutputStreamDemo4 {
	public static void main(String[] args) {
//		FileOutputStream fos = new FileOutputStream("d.txt");
//		fos.write("hello".getBytes());
//		fos.close();
		
		//分开做异常处理
//		FileOutputStream fos = null;
//		try {
//			fos = new FileOutputStream("d.txt");
//		} catch (FileNotFoundException e) {
//			e.printStackTrace();
//		}
//		try {
//			fos.write("hello".getBytes());
//		} catch (IOException e) {
//			e.printStackTrace();
//		}
//		try {
//			fos.close();
//		} catch (IOException e) {
//			e.printStackTrace();
//		}
		
		//放在一起做异常处理
//		try{
//			FileOutputStream fos = new FileOutputStream("d.txt");
//			fos.write("hello".getBytes());
//			fos.close();
//		}catch(IOException e) {
//			e.printStackTrace();
//		}
		//这种方式代码虽然简洁了,但是释放资源的动作可能未执行到
		//try...catch...finally
		FileOutputStream fos = null;
		try{
			//FileOutputStream fos = new FileOutputStream("d.txt");
//			fos = new FileOutputStream("z:\\d.txt");
			fos = new FileOutputStream("d.txt");
			fos.write("hello".getBytes());
		}catch(IOException e) {
			e.printStackTrace();
		}finally {
			if(fos!=null) {
				//释放资源
				try {
					fos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
}

六、FileInputStream读数据方式

1、一次读一个字节

public class FileInputStreamDemo {
	public static void main(String[] args) throws IOException {
		//创建字节输入流对象
		FileInputStream fis = new FileInputStream("a.txt");
		
		/*
		//调用读数据的方法
		//public int read():读取一个字节的数据,如果已到达文件末尾,则返回 -1。 
		int by = fis.read();
		System.out.println(by);
		System.out.println((char)by);
		
		//再来读取一次
		by = fis.read();
		System.out.println(by);
		System.out.println((char)by);
		
		//发现读数据的代码的重复度很高,想用循环改进
		//但是我们不知道循环的结束条件
		//再来读取两次
		by = fis.read();
		System.out.println(by);
		by = fis.read();
		System.out.println(by);
		*/
		
//		int by = fis.read();
//		while(by != -1) {
//			System.out.print((char)by);
//			by = fis.read();
//		}
		
		//改进版本
		int by;
		//fis.read()
		//by=fis.read()
		//by != -1
		while((by=fis.read())!=-1) {
			System.out.print((char)by);
		}
		
		//释放资源
		fis.close();
	}
}

2、一次读取一个字节数组

public class FileInputStreamDemo2 {
	public static void main(String[] args) throws IOException {
		//创建字节输入流对象
		FileInputStream fis = new FileInputStream("b.txt");
		
		/*
		//调用读数据的方法
		//定义一个数组
		byte[] bys = new byte[5];
		
		//第一次读取
		int len = fis.read(bys);
		System.out.println(len);
		//byte[] -- String
		//String(byte[] bytes) 
		//String(byte[] bytes, int offset, int length) 
		System.out.println(new String(bys));
		
		//第二次读取
		len = fis.read(bys);
		System.out.println(len);
		System.out.println(new String(bys));
		
		//第三次读取
		len = fis.read(bys);
		System.out.println(len);
//		System.out.println(new String(bys));
		System.out.println(new String(bys,0,len));
		
		//第四次读取
		len = fis.read(bys);
		System.out.println(len);
		//第五次读取
		len = fis.read(bys);
		System.out.println(len);
		*/
		
		/*
		 * hello\r\n
		 * world\r\n
		 * 
		 * hello
		 * \r\nwor
  		 * ld\r\nr
		 */
		
		/*
		byte[] bys = new byte[5];
		int len = fis.read(bys);
		while(len != -1) {
			System.out.print(new String(bys,0,len));
			len = fis.read(bys);
		}
		*/
		
		//最终版代码
		byte[] bys = new byte[1024]; //1024或者1024的整数倍
		//1G = 1024MB
		//1MB = 1024KB
		//...
		int len;
		while((len=fis.read(bys))!=-1) {
			System.out.print(new String(bys,0,len));
		}
		
		//释放资源
		fis.close();
	}
}

七、字节流练习之复制图片【应用】

/*
 * 把d:\\mn.jpg内容复制到当前项目目录下的mn.jpg中
 * 
 * 数据源:
 * 		d:\\mn.jpg---读数据---FileInputStream
 * 目的地:
 * 		mn.jpg---写数据---FileOutputStream
 */
public class CopyJpgTest {
	public static void main(String[] args) throws IOException {
		//封装数据源
		FileInputStream fis = new FileInputStream("d:\\mn.jpg");
		//封装目的地
		FileOutputStream fos = new FileOutputStream("mn.jpg");
		
		//读写数据
		//方式1:一次读取一个字节,一次写一个字节(自己练习)
		//方式2:一次读取一个字节数组,一次写一个字节数组的一部分
		byte[] bys = new byte[1024];
		int len;
		while((len=fis.read(bys))!=-1) {
			fos.write(bys,0,len);
		}
		
		//释放资源
		fos.close();
		fis.close();
	}
}

八、字节缓冲区流的概述和使用【应用】

字节缓冲区流:

  • BufferedOutputStream:字节缓冲输出流
  • BufferedInputStream:字节缓冲输入流
  • BufferedOutputStream(OutputStream out)
    使用这种构造方法,它提供了一个默认的缓冲区大小,所以一般我们使用默认的缓冲区就可以了。

  • 为什么构造方法传递的是一个:OutputStream,而不是具体的文件或者路径呢?
    —》字节缓冲区流仅仅提供缓冲区,而真正的底层的读写数据还得需要基本的流对象进行操作。
public class BufferedStreamDemo {
	public static void main(String[] args) throws IOException {
		// BufferedOutputStream(OutputStream out)
		// FileOutputStream fos = new FileOutputStream("a.txt");
		// BufferedOutputStream bos = new BufferedOutputStream(fos);
		// 上面的两句等价于下面的这一句
		// BufferedOutputStream bos = new BufferedOutputStream(new
		// FileOutputStream("a.txt"));
		// bos.write("hello".getBytes());
		// bos.close();

		// BufferedInputStream(InputStream in)
		BufferedInputStream bis = new BufferedInputStream(new FileInputStream("a.txt"));
		//方式1:一次读取一个字节
//		int by;
//		while((by=bis.read())!=-1) {
//			System.out.print((char)by);
//		}
		
		//方式2:一次读取一个字节数组
		byte[] bys = new byte[1024];
		int len;
		while((len=bis.read(bys))!=-1) {
			System.out.print(new String(bys,0,len));
		}
		
		bis.close();
	}
}

九、字节流四种方式复制AVI并测试效率【应用】

1、把d:\复制图片.avi复制到当前项目目录下的copy.avi中

  • 数据源:d:\复制图片.avi
  • 目的地:copy.avi

2、四种方式比较复制效率

  • 基本字节流一次读写一个字节 共耗时:50233毫秒
  • 基本字节流一次读写一个字节数组 共耗时:79毫秒
  • 缓冲字节流一次读写一个字节 共耗时:197毫秒
  • 缓冲字节流一次读写一个字节数组 共耗时:30毫秒

  • 提示:
  • 复制文件的时间计算可以采用System类的方法实现
  • public static long currentTimeMillis():返回以毫秒为单位的当前时间。
public class CopyAviTest {
	public static void main(String[] args) throws IOException {
		//记录开始时间
		long start = System.currentTimeMillis();
		
//		method1();
//		method2();
//		method3();
		method4();
		
		//记录结束时间
		long end = System.currentTimeMillis();
		System.out.println("共耗时:"+(end-start)+"毫秒");
	}
	
	//缓冲字节流一次读写一个字节数组
	private static void method4() throws IOException {
		//封装数据源
		BufferedInputStream bis = new BufferedInputStream(new FileInputStream("d:\\复制图片.avi"));
		//封装目的地
		BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy.avi"));
		
		byte[] bys = new byte[1024];
		int len;
		while((len=bis.read(bys))!=-1) {
			bos.write(bys,0,len);
		}
		
		bos.close();
		bis.close();
	}


//缓冲字节流一次读写一个字节
	private static void method3() throws IOException {
		//封装数据源
		BufferedInputStream bis = new BufferedInputStream(new FileInputStream("d:\\复制图片.avi"));
		//封装目的地
		BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy.avi"));
		
		int by;
		while((by=bis.read())!=-1) {
			bos.write(by);
		}
		
		bos.close();
		bis.close();
	}
	
	//基本字节流一次读写一个字节数组
	private static void method2() throws IOException {
		//封装数据源
		FileInputStream fis = new FileInputStream("d:\\复制图片.avi");
		//封装目的地
		FileOutputStream fos = new FileOutputStream("copy.avi");
		
		byte[] bys = new byte[1024];
		int len;
		while((len=fis.read(bys))!=-1) {
			fos.write(bys,0,len);
		}
		
		fos.close();
		fis.close();
	}

	//基本字节流一次读写一个字节
	private static void method1() throws IOException {
		//封装数据源
		FileInputStream fis = new FileInputStream("d:\\复制图片.avi");
		//封装目的地
		FileOutputStream fos = new FileOutputStream("copy.avi");
		
		int by;
		while((by=fis.read())!=-1) {
			fos.write(by);
		}
		
		fos.close();
		fis.close();
	}
}

==>转换流出现的原因【理解】

原因:
字节流一次读取一个字节的方式读取带有汉字的文件是有问题的,因为你读取到一个字节后就转为字符在控制台输出了,而汉字是由2个字节组成的,所以这里会出问题。
文件复制的时候,字节流读取一个字节,写入一个字节,这个没有出现问题,是因为最终底层会根据字节做拼接,得到汉字。
汉字存储的规则:
左边的字节数据肯定是负数,右边的字节数据可能是负数,也可能是正数,大部分	情况下是负数。	
如:	String s1 = "hello";
		//[104, 101, 108, 108, 111]
		String s2 = "你好";
		//[-60, -29, -70, -61]

&&下面开始阐述字符流的相关知识和应用

十、OutputStreamWriter写数据的5种方式

===》OutputStreamWriter写数据方法

  • public void write(int c):写一个字符
  • public void write(char[] cbuf):写一个字符数组
  • public void write(char[] cbuf,int off,int len):写一个字符数组的一部分
  • public void write(String str):写一个字符串
  • public void write(String str,int off,int len):写一个字符串的一部分
public class OutputStreamWriterDemo {
	public static void main(String[] args) throws IOException {
		//创建字符输出流对象
		OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("b.txt"));
		
		//public void write(int c):写一个字符
//		osw.write(97);
//		osw.write('a');
		//写完数据后,没有发现数据,为什么呢?
		//1字符=2字节
		//文件中的数据存储的基本单位是字节
		
		//public void write(char[] cbuf):写一个字符数组
//		char[] chs = {'a','b','c','d','e'};
//		osw.write(chs);
		
		//public void write(char[] cbuf,int off,int len):写一个字符数组的一部分
//		char[] chs = {'a','b','c','d','e'};
//		osw.write(chs, 1, 3);
		
		//public void write(String str):写一个字符串
//		osw.write("hello");
		
		//public void write(String str,int off,int len):写一个字符串的一部分
		osw.write("hello", 0, 3);
		
//		//void flush():刷新该流的缓冲
//		osw.flush();
//		
//		//释放资源
		osw.close(); //关闭此流,但要先刷新它
	}
}

十一、InputStreamReader读数据的2种方式

===》InputStreamReader读数据方法

  • public int read():一次读取一个字符
  • public int read(char[] cbuf):一次读取一个字符数组
public class InputStreamReaderDemo {
	public static void main(String[] args) throws IOException {
		//创建字符输入流对象
//		InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"));
		InputStreamReader isr = new InputStreamReader(new FileInputStream("OutputStreamWriterDemo.java"));
		
		//public int read():一次读取一个字符
//		int ch;
//		while((ch=isr.read())!=-1) {
//			System.out.print((char)ch);
//		}
		
		//public int read(char[] cbuf):一次读取一个字符数组
		char[] chs = new char[1024];
		int len;
		while((len=isr.read(chs))!=-1) {
			System.out.print(new String(chs,0,len));
		}
		
		//释放资源
		isr.close();
	}
}

十二、字符流的练习之复制Java文件改进版

把当前项目目录下的StringDemo.java内容复制到当前项目目录下的Copy.java中(改进版)

转换流的名字比较长,而我们常见的操作都是按照本地默认编码实现的,所以,为了简化我们的书写,转换流提供了对应的子类。

  • FileWriter:用来写入字符文件的便捷类:
  •  OutputStreamWriter
    
  • FileReader:用来读取字符文件的便捷类:
  •  InputStreamReader
    

  • OutputStreamWriter = FileOutputStream + 编码表(GBK)
  • FileWriter = FileOutputStream + 编码表(GBK)
  • 构造方法:
  • FileWriter(String fileName)

  • InputStreamReader = FileInputStream + 编码表(GBK)
  • FileReader = FileInputStream + 编码表(GBK)
  • 构造方法:
  • FileReader(String fileName)

  • 数据源:
  •  StringDemo.java---读数据---字符流---InputStreamReader---FileReader
    
  • 目的地:
  •  Copy.java---写数据---字符流---OutputStreamWriter---FileWriter
    
public class CopyJavaTest2 {
	public static void main(String[] args) throws IOException {
		//封装数据源
		FileReader fr = new FileReader("StringDemo.java");
		//封装目的地
		FileWriter fw = new FileWriter("Copy.java");
		
		//读写数据
		//一次读写一个字符
//		int ch;
//		while((ch=fr.read())!=-1) {
//			fw.write(ch);
//		}
		
		//一次读写一个字符数组
		char[] chs = new char[1024];
		int len;
		while((len=fr.read(chs))!=-1) {
			fw.write(chs, 0, len);
		}
		
		//释放资源
		fw.close();
		fr.close();
	}
}

十三、字符缓冲区流的概述和使用【应用】

===》字符缓冲区流:
1、BufferedWriter:

  • 将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。
  • 可以指定缓冲区的大小,或者接受默认的大小。在大多数情况下,默认值就足够大了。
  • 构造方法:
  •  BufferedWriter(Writer out) 
    

2、BufferedReader:

  • 从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。
  • 可以指定缓冲区的大小,或者可使用默认的大小。大多数情况下,默认值就足够大了。
  • 构造方法:
  •  BufferedReader(Reader in) 
    
public class BufferedStreamDemo {
	public static void main(String[] args) throws IOException {
//		//创建字符缓冲输出流对象
//		BufferedWriter bw = new BufferedWriter(new FileWriter("a.txt"));
//		//调用写数据的方法
//		bw.write("hello");
//		//释放资源
//		bw.close();
		
		
		//创建字符缓冲输入流对象
		BufferedReader br = new BufferedReader(new FileReader("BufferedStreamDemo.java"));
		
		//方式1:一次读取一个字符
//		int ch;
//		while((ch=br.read())!=-1) {
//			System.out.print((char)ch);
//		}
		
		//方式2:一次读取一个字符数组
		char[] chs = new char[1024];
		int len;
		while((len=br.read(chs))!=-1) {
			System.out.print(new String(chs,0,len));
		}
		
		//释放资源
		br.close();
	}
}

十四、字符缓冲区流的特殊功能【应用】

1、BufferedWriter特殊功能:
void newLine():写入一个行分隔符,这个行分隔符是由系统决定的
2、BufferedReader特殊功能:
String readLine():包含该行内容的字符串,不包含任何行终止符,
如果已到达流末尾,则返回 null

public class BufferedStreamDemo {
	public static void main(String[] args) throws IOException {
//		//创建字符缓冲输出流对象
//		BufferedWriter bw = new BufferedWriter(new FileWriter("bw.txt"));
//		
//		//写数据
//		for(int x=0; x<3; x++) {
//			bw.write("hello");
			bw.write("\r\n");
//			bw.newLine();
//			bw.flush();
//		}
//		
//		//释放资源
//		bw.close();
		
		//创建字符缓冲输入流对象
		BufferedReader br = new BufferedReader(new FileReader("bw.txt"));
		
//		//读取一次
//		String line = br.readLine();
//		System.out.println(line);
//		//在读取一次
//		line = br.readLine();
//		System.out.println(line);
//		line = br.readLine();
//		System.out.println(line);
//		//多读取两次
//		line = br.readLine();
//		System.out.println(line);
//		line = br.readLine();
//		System.out.println(line);
		
		//最终版代码
		String line;
		while((line=br.readLine())!=null) {
			System.out.println(line);
		}
		
		//释放资源
		br.close();
	}
}

十五、字符流的练习之把集合中的学生对象数据存储到文本文件【应用】把ArrayList集合中的学生数据存储到文本文件

每一个学生数据作为文件中的一行数据
===>定义一个学生类。
学生:
学号,姓名,年龄,所在城市
it001,张曼玉,35,北京
it002,王祖贤,33,上海
it003,林青霞,30,西安

===>分析步骤:
A:创建集合对象
B:创建学生对象
C:把学生对象添加到集合中
D:创建字符缓冲输出流对象
E:遍历集合,得到每一个学生对象,然后把该对象的数据拼接成一个指定格式的字符串写到文本文件
F:释放资源

public class ArrayListToFileTest {
	public static void main(String[] args) throws IOException {
		// 创建集合对象
		ArrayList<Student> array = new ArrayList<Student>();

		// 创建学生对象
		Student s1 = new Student("it001", "张曼玉", 35, "北京");
		Student s2 = new Student("it002", "王祖贤", 33, "上海");
		Student s3 = new Student("it003", "林青霞", 30, "西安");

		// 把学生对象添加到集合中
		array.add(s1);
		array.add(s2);
		array.add(s3);

		// 创建字符缓冲输出流对象
		BufferedWriter bw = new BufferedWriter(new FileWriter("students.txt"));

		// 遍历集合,得到每一个学生对象,然后把该对象的数据拼接成一个指定格式的字符串写到文本文件
		for (Student s : array) {
			// it001,张曼玉,35,北京
			StringBuilder sb = new StringBuilder();
			sb.append(s.getSid()).append(",").append(s.getName()).append(",").append(s.getAge()).append(",")
					.append(s.getCity());
			bw.write(sb.toString());
			bw.newLine();
			bw.flush();
		}
		
		//释放资源
		bw.close();
	}
}

十六、个人总结

===>学习IO流这块的话,最好是有一套完整的IO流体系框架图,再配上一些案例分析进行大量练习,一定要把所学的知识运用到实践上,这样才能有效地提升自己的技术,我写这篇博文是为了分享给大家,希望大家可以一步一步的构建自己的知识体系,祝大家可以拿到满意的offer!!!

你可能感兴趣的:(Java,Java学习园,Java,IO流,编程语言,后端)