Java基础之IO流

1.流的概念

Java使用流的概念进行文件的读取和写入。流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。

2.流的分类

2.1 常用IO流结构图

在这里插入图片描述

2.2 字节流和字符流

按照处理数据的对象(操作数据)不同分为字节流和字符流。

(1)字符流

  • 字符流的由来: 因为数据编码的不同,而有了对字符进行高效操作的流对象。本质其实就是基于字节流读取时,去查了指定的码表。是以字符为单位进行数据交换,底层还是使用的是字节流, 只不过会在流动的过程中去查编码表将多个字节转换成一个字符。
  • 使用场景:一般文本文件使用字符流进行操作。

(2)字节流

  • 在数据流动的时候是以字节为单位进行数据交换的。
  • 使用场景:基本上所有文件都可以使用字节流,包括:二进制文件(音频,视频,图片,word文档等)。

(3)字节流和字符流的区别

  • 读写单位不同:字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节。
  • 处理对象不同:字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符类型的数据。

结论:只要是处理纯文本数据,就优先考虑使用字符流。 除此之外都使用字节流。在对文件进行读写操作的时候,字节流可用于读写二进制文件,字符流用于读取文本文件。所谓二进制文件,指文件无字符编码格式,由字节(Byte)组成,图片文件、word文档都属于二进制文件。文本文件也属于特殊的二进制文件,也是由字节组成,但是需要通过特定的字符编码格式读取或者写入,否则会出现乱码,txt文件就是典型的文本文件。

2.3 输入流和输出流

按照流向可以分为输入流和输出流。输入流只能进行读操作,输出流只能进行写操作,其中输入和输出是针对程序内存来说的,如果将内存以外的数据读取到内存中都成为输入,将内存中的数据写出去都属于输出。

2.4 字节流的操作

2.4.1 文件输入输出流

【文件输入流的使用】
使用文件输入流(FileInputStream)读取文件分为以下几个步骤:
(1)打开文件(new File());
(2)构建一个文件输入流(new FileInputStream);
(3)通过循环根据字节读取文件:使用read()方法进行读取,一次读取一个字节;
(4)关闭流,释放资源,使用close()方法。
例如

        InputStream is=null;
		try {
			//构建一个输入流
			is=new FileInputStream("f:/a.txt");
			//通过循环根据字节读取文件
			//在读取的过程当中,每循环一次读取一个字节,返回int类型的值,
			//当int类型的数据为-1的时候代表文件读取结束
			//System.out.println();
			long start=System.currentTimeMillis();
			int i=is.read();
			while(i!=-1){
				//读取出来的是数据的ASCII码值,所以要强转成char类型
				//System.out.print((char)i);
				i=is.read();
			}	
			long end=System.currentTimeMillis();
			System.out.println("FileInputStream所用的时间为:"+(end-start)+"毫秒");
		} catch (FileNotFoundException e) {
			System.out.println("文件不存在!");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
			try {
				if(is!=null){
					is.close();
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

◣ 注意
(1)构建输入流的时候通过需要传入一个File对象或者文件名,如果文件不存在则抛出非运行时异常FileNotFountException异常。
(2)通过输入流的read方法进行字节的读取,返回int类型的值,返回的是字符ASCII值,在-1~255之间,如果返回-1说明已经读到了文件的末尾。当读取的过程中发生错误会抛出IOException异常。
(3)FileInputStream提供的read方法每执行一次均会发出一次IO读取操作,一次读取一个字节,效率低,所以java提供了BufferedInputStream,能够在一次IO读取操作中读取多个字节,降低了IO读取的频率,提高了代码的效率。

例如:使用BufferedInputStream

public static void main(String[] args) {
	InputStream is=null;
	BufferedInputStream bis=null;
	try {
		//构建一个字节输入流
		is = new FileInputStream("f:/a.txt");
		//创建一个BufferedInputStream对象,该对象创建必须要依靠inputStream对象
		bis=new BufferedInputStream(is);
		
		//循环读取
		long start=System.currentTimeMillis();
		int i=0;
		while((i=bis.read())!=-1){
			//
		}
		long end=System.currentTimeMillis();
		System.out.println("BufferedInputStream所用时间为:"+(end-start));
		
	} catch (FileNotFoundException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	} catch (IOException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}finally{
		try {
			bis.close();
			is.close();

		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
			}
}

说明:缓存输入流不能独立使用,需要通过文件输入流进行构建。

【文件输出流的使用】
在进行文件输出的时候,也就是写文件的时候也分为以下几步:
(1)构建一个输出流;
(2)将数据写入到磁盘中,调用writer()方法;
(3)关闭流,释放资源,使用close()方法。

例如

OutputStream os=null;
	try {
		//构建一个输出流,如果该文件已经存在了,则默认覆盖该文件中的内容
		//可以将FileOutputStream中的append参数设置为true,代表在原来的数据上追加数据
		os=new FileOutputStream("f:/b.txt",true);
		//将数据写入到磁盘中
		for (int i = 0; i < 10; i++) {
			os.write('c');
		}
		
	} catch (FileNotFoundException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	} catch (IOException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}finally{
		try {
			os.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

说明:在使用文件输出流时,如果目标文件不存在,会创建该文件,如果目标文件存在,会覆盖文件的内容,如果当目标存在不想被覆盖的时候,而是进行追加输出,可以在构建的构造方法中添加true参数。

2.4.2 缓存输入输出流

缓存输入流:BufferedInputStream
缓存输出流:BufferedOutputStream

缓存输入输出流属于包装流,在基础流的基础上提供了缓存机制,所以效率远远高于文件输入输出流。(具体见前与文件输入输出流的对比)

◣注意:
(1)创建缓存流的时候依赖一个基础流的对象,所以通常将FileInputStream对象做为他依赖的对象。
(2)由于使用缓存输出流写数据先将数据写入到缓存中,所以在写完成之后需要调用flush()方法将缓存中的数据清到磁盘,这样才能写入成功。一般情况调用了缓存输出流的close(),系统会默认调用flush()方法。

2.4.3 对象输入输出流

对象输入流:ObjectInputStream
对象输出流:ObjectOutputStream

对象流属于字节流,是针对对象进行读写操作。
例如:将对象通过对象流写入到f:/a.bin中,并读取出来。

public static void main(String[] args) throws IOException, ClassNotFoundException {
		
		//创建一个对象输出流
		FileOutputStream fos=new FileOutputStream("f:/d.bin");
		ObjectOutputStream oos=new ObjectOutputStream(fos);
		//List<Student> list=new ArrayList<Student>();
		for(int i=0;i<5;i++){
			Student stu=new Student(i+1, "zhangsan"+i, "123456");
			//list.add(stu);
			oos.writeObject(stu);//将一个学生对象存放到文件中
		}
		//oos.writeObject(list);//将一个学生对象存放到文件中
		
		//读取对象创建输入流
		FileInputStream fis=new FileInputStream("f:/d.bin");
		ObjectInputStream ois=new ObjectInputStream(fis);
		//读取对象
		//for(int i=0;i<6;i++){
			//List<Student> s=(List<Student>) ois.readObject();
			//System.out.println(s.getName());
			/*for (Student student : s) {
				System.out.println(student.getName());
			}*/
		//}
		try {
			while (true) {
				Student s=	(Student) ois.readObject();
					System.out.println(s.getName());
				}
		} catch (EOFException e) {
			System.out.println("数据读取完毕!");
		}
		ois.close();
		oos.close();
	}

学生类:

//学生类
public class Student implements Serializable{
	private int id;
	private String name;
	private String password;
//get/set方法省略

◣说明:
(1)将对象保存到文件中的时候,对象的类必须实现序列化(Serializable)接口才能将实例转换为流进行操作。(因为将对象保存到文件当中的过程叫做持久化的过程,持久化的对象的类必须实现Serializable接口)。
(2)通过writeObject()方法将实现序列化接口的类写入到流中。
(3)通过readObject()方法将实现序列化接口的类从文件中读取出来。
◣使用序列化的场景:
(1)将对象保存到文件中。
(2)将对象保存到数据库中。
(3)对象进行一些网络传输。
◣注意:
使用对象流读取对象的时候,判断是否读取完成不能用-1和null来判断,有两种方案可以进行判断:
(1)将对象保存到一个容器中(例如List集合),读取的时候直接去读取集合;
(2)在读取的过程当中用try-catch捕获EOFException异常,当出现这个异常代表数据读取完毕。

2.5 字符流的操作

字符流的操作方式与字节流基本相同,字符流会根据当前的操作系统与语言环境选择适当的字符编码方式读写文件,适合读写文本文件。因为字符流会对文件内容编码,所以不能适用于读取二进制文件。

2.5.1 字符输入输出流

常用的字符输入流有:FileReader和BufferedReader
常用的字符输出流有:FileWriter和BufferedWriter

写入文件的方法:writer()
换行的方法:newLine();
读取文件的方法:readLine();按行读取当读取到的数据返回null代表读取完成。
例如 : 使用FileReader和FileWriter读写文本文件

public static void main(String[] args) throws IOException {
		//构建一个字符输入流
		Reader rd=new FileReader("f:/a.txt");
		//FileInputStream fis=new FileInputStream("f:/a.txt");
		//循环读取文件中的数据
		int i=0;
		while ((i=rd.read())!=-1) {
			System.out.print((char)i);
			
		}
		//构建一个字符输出流
		Writer wr=new FileWriter("f:/c.txt");
		wr.write("张三李四");
		wr.close();
		
		rd.close();
	}

【说明:FileReader提供了一次读取一个字符的功能,读到末尾同样的返回-1】

例如 :使用BufferedReader和BufferedWriter读写文本文件

public static void main(String[] args) throws IOException {
		/**
		 * 使用bufferedReader读取文件
		 */
		//构建一个输入流
		Reader re=new FileReader("f:/a.txt");
		BufferedReader br=new BufferedReader(re);
		//进行读取
		//当读取一行返回null的时候就代表文本读取完成
		String str="";
		while((str=br.readLine())!=null){
			System.out.println(str);
		}
		
		//将文本写入到新的文本中
		
		//构建一个输出流
		Writer wr=new FileWriter("f:/c.txt");
		BufferedWriter bw=new BufferedWriter(wr);
		//进行写入
		bw.write("时间的哈空间上的号卡死\r\n");
		//bw.newLine();//插入一个换行符
		bw.write("时代发生的发生时代发生地方\r\n");
		//bw.newLine();
		bw.write("的国际分工几乎覆盖巨化股份黄金分割\r\n");
		//bw.newLine();
		bw.write("合肥公交大概还能赛出风格黑色风格好地方\r\n");
		bw.flush();
		bw.close();
		br.close();
	}

注意
(1)BufferedReader提供了按照行读取文本的功能,一次读取一行,读到末尾返回null;
(2)使用BufferedWriter 写数据的时候一定要注意关闭流,或者是调用flush()方法,因为缓存输出流是将输出的内容缓存在内存中,如果忘记调用flush()方法,内容将不会被输出到磁盘中,另外,调用的缓存输出流的close()方法以后默认调用了flush()方法。
(3)如果要输出换行符,不能简单的使用”\n”,因为不同的操作系统的换行符是不一样的,所以推荐使用BufferedWriter提供的newLine()方法输出换行。

2.5.2 字节转换流

字节转换流 :InputStreamReader
将一个字节输入流转换成字符输入流;
在创建对象的时候可以在第二个参数的位置设置读取的编码格式。
常用的编码格式有:UTF-8 GBK GB2312
例如

public static  void test1() throws IOException {
		File file=new File("f:/demo.txt");
		InputStreamReader isr=new InputStreamReader(new FileInputStream(file),"UTF-8");
		BufferedReader br=new BufferedReader(isr);
		String s="";
		while((s=br.readLine())!=null) {
			System.out.println(s);
		}
		br.close();
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值