Day09+10流相关内容

Day09+Day10

一、IO

​ IO分别代表Input输入,和Output输出。数据的传输需要借助流,可以理解为数据的流动

  • 根据流动的方向不同,分为输入流(由设备到内存)和输出流(由内存到设备)。
  • 根据传输的数据类型不同,分为字节流和字符流。

流的顶级父类:

顶级父类输入流输出流
字节流InputStreamOutputStream
字符流ReaderWriter

二、 字节流

数据的传输,无论体现出来是哪种形式,最底层一定是字节的传输

字节输出流:

2.1OutputStream

java.io.OutputStream是所有字节输出流的父类,是一个抽象类,它定义了字节输出流的基本方法。

  • public void close() :关闭此输出流并释放与此流相关联的任何系统资源。 (必须要)
  • public void flush() :刷新此输出流并强制任何缓冲的输出字节被写出。
  • public void write(byte[] b):将 b.length字节从指定的字节数组写入此输出流。
  • public void write(byte[] b, int off, int len) :从指定的字节数组写入 len字节,从偏移量 off开始输出到此输出流。
  • public abstract void write(int b) :将指定的字节输出流。

2.2 FileOutputStream

java.io.FileOutputStream类是文件输出流,用于将数据写出到文件。

构造方法有两种形式:

//当前路径下以字符串形式创建
FileOutputStream fout = new FileOutputStream("a.txt");
//以文件作为参数构造
File f= new File("a.txt");
FileOutputStream fout2 = new FileOutputStream(f);

写出数据write(int n)、写出数组write(byte[] n)、写出指定长度数组write (byte[] n , int off ,int lenth)、写出换行write("\r\n")

public static void main(String[] args) throws IOException {
		//写出数据,默认追加为false,如果要追加创建的时候应为...("a.txt",true);
			FileOutputStream file = new FileOutputStream("a.txt");
			file.write(97);
			byte[] s = "abcde".getBytes();
			//写出全部数组
			file.write(s);
			//写出换行
			file.write("\r\n".getBytes());
			//写出数组中从0位置开始,三个长度的字节数据
			file.write(s, 0, 3);
			file.close();	
		}
  • 虽然参数为int类型四个字节,但是只会保留一个字节的信息写出。

  • 流操作完毕后,必须释放系统资源,调用close方法,千万记得。

2.3 FileInputStream

java.io.FileInputStream类是文件输入流,从文件中读取字节。

构造方法:

public static void main(String[] args) throws IOException {
		FileInputStream fis = new FileInputStream("a.txt");
		int len;
    //读单个字节
		while((len=fis.read())!=-1) {
			System.out.println((char)len);
		}
		fis.close();
		//读字节数组,需要获取每次读到的长度,如果直接输出数组b,例如数组长度是5,这一次只读到3个字节,那么有两个字节是上一次的,会导致错误的输出,用len可以保证读了多少输出多少
		FileInputStream fis2 = new FileInputStream("a.txt");
		byte [] b = new byte[5];
		while((len=fis2.read(b))!=-1) {
			System.out.println(new String(b,0,len));
		}
		fis.close();
	}

中间需要再次打开的时候,是因为之前单个打印的时候已经全部读完了,需要重头再来。

2.4 读取图片练习

public static void main(String[] args) throws IOException {
		FileInputStream fis = new FileInputStream("copy.jpg");
		FileOutputStream fos = new FileOutputStream("test.jpg");
		int len;
    //采用数组的方式读写,数组长度越长,IO次数就越少,效率越高
		byte [] b = new byte[10];
    //读取数组
		while((len=fis.read(b))!=-1) {
            //写出数组,用len保证数组的准确性
			fos.write(b,0,len);
		}
    //读写完之后一定要关闭,流的关闭原则:先开后关,后开先关,类似于离开一栋房子,先关上房间里面的门,最后再关大门
		fos.close();
   		fis.close();
	}

三、字符流

3.1 输入流Reader

java.io.Reader抽象类是表示用于读取字符流的所有类的超类,可以读取字符信息到内存中。它定义了字符输入流的基本共性功能方法。

  • public void close() :关闭此流并释放与此流相关联的任何系统资源。
  • public int read(): 从输入流读取一个字符。
  • public int read(char[] cbuf): 从输入流中读取一些字符,并将它们存储到字符数组 cbuf中 。

3.2 FileReader

java.io.FileReader类可以直接读取字符文件,构造时,使用系统默认的字符编码(Windows是GBK)和默认字节缓冲区(一个字节数组,用来临时储存数据)。

构造与读取字符:

public static void main(String[] args) throws IOException {
    //采用路径直接构造
		FileReader f = new FileReader("a.txt");
		int len;
		char[] cubf = new char[10];
    //读取单个字符,提升为int类型
		while((len=f.read())!=-1) {
			System.out.println((char)len);
		}
		f.close();
    //读取字符数组
		FileReader f2 = new FileReader("a.txt");
		while((len=f2.read(cubf))!=-1) {
			System.out.println(new String(cubf,0,len));
		}
	}

3.3 FileWriter

java.io.Writer抽象类是表示用于写出字符流的所有类的超类,将指定的字符信息写出到目的地。它定义了字节输出流的基本共性功能方法。

  • 在这里插入图片描述写入方法
  • void flush()刷新该流的缓冲。
  • void close() 关闭此流,但要先刷新它。
  • 构造方法:直接用地址构造,或以文件构造
    • public FileWriter(File file)
    • public FileWriter("a.txt")
public static void main(String[] args) throws IOException {
		FileWriter fw = new FileWriter("aa.txt");
    //写字符
		fw.write('A');
    //写字符串
		fw.write("大会上年度涉农打算把");
		String s = "不然赛排毒素大数据库";
    //写出换行
		fw.write("\r\n");
    //写出字符串中指定位置+指定长度内容
		fw.write(s, 1, 3);
		char[] cubf = "一个字节数组".toCharArray();
    //写出字符数组
		fw.write(cubf);
    //写出字符数组中指定位置+指定长度内容
		fw.write(cubf, 2, 2);
		fw.close();
	}

四、 缓冲流

4.1 简介

缓冲流是对输入输出流的"增强"。其基本原理,是在创建流对象时,会创建一个内置的默认大小的缓冲区数组,通过缓冲区读写,减少系统IO次数,从而提高读写的效率。

4.2 字节缓冲流

构造方法:

BufferedInputStream bis = new BufferedInputStream(new FileInputStream("copy.jpg"));//以基本输入流作为参数构造
BufferedInputStream bos = new BufferedInputStream(new FileInputStream("buffer.txt"));
读取效率对比
public static void main(String[] args) throws IOException, ClassNotFoundException {
			BufferedInputStream bis = new BufferedInputStream(new FileInputStream("copy.jpg"));
//			BufferedInputStream bos = new BufferedInputStream(new FileInputStream("buffer.txt"));
			int len=0;
			byte [] b = new byte[10];
			FileInputStream filein = new FileInputStream("copy.jpg");
			long time = System.currentTimeMillis();
			while((len=filein.read(b))!=-1) {}
			time =System.currentTimeMillis()-time;
			System.out.println("读取时间:"+time);
			time = System.currentTimeMillis();
			while((len=bis.read(b))!=-1) {}
			time =System.currentTimeMillis()-time;
			System.out.println("读取时间:"+time);
			filein.close();
			bis.close();
		}	
输出结果:192
    	3

4.4字符缓冲流

  • 构造方法

    BufferedReader br = new BufferedReader(new FileReader("a.txt"));
    //以基本字符流的作为构造参数,实际上是对基本流的“增强”
    BufferedWriter bw = new BufferedWriter(new FileWriter("a.txt");
    
  • 特有方法

    • 读取一行br.readLine();
    • 写出系统对应的行分隔符bw.newLine();

4.5 文本读取排序分析

按照文本前的序号进行升序排序:

3.侍中、侍郎郭攸之、费祎、董允等,此皆良实,志虑忠纯,是以先帝简拔以遗陛下。愚以为宫中之事,事无大小,悉以咨之,然后施行,必得裨补阙漏,有所广益。
8.愿陛下托臣以讨贼兴复之效,不效,则治臣之罪,以告先帝之灵。若无兴德之言,则责攸之、祎、允等之慢,以彰其咎;陛下亦宜自谋,以咨诹善道,察纳雅言,深追先帝遗诏,臣不胜受恩感激。
4.将军向宠,性行淑均,晓畅军事,试用之于昔日,先帝称之曰能,是以众议举宠为督。愚以为营中之事,悉以咨之,必能使行阵和睦,优劣得所。
2.宫中府中,俱为一体,陟罚臧否,不宜异同。若有作奸犯科及为忠善者,宜付有司论其刑赏,以昭陛下平明之理,不宜偏私,使内外异法也。
1.先帝创业未半而中道崩殂,今天下三分,益州疲弊,此诚危急存亡之秋也。然侍卫之臣不懈于内,忠志之士忘身于外者,盖追先帝之殊遇,欲报之于陛下也。诚宜开张圣听,以光先帝遗德,恢弘志士之气,不宜妄自菲薄,引喻失义,以塞忠谏之路也。
9.今当远离,临表涕零,不知所言。
6.臣本布衣,躬耕于南阳,苟全性命于乱世,不求闻达于诸侯。先帝不以臣卑鄙,猥自枉屈,三顾臣于草庐之中,咨臣以当世之事,由是感激,遂许先帝以驱驰。后值倾覆,受任于败军之际,奉命于危难之间,尔来二十有一年矣。
7.先帝知臣谨慎,故临崩寄臣以大事也。受命以来,夙夜忧叹,恐付托不效,以伤先帝之明,故五月渡泸,深入不毛。今南方已定,兵甲已足,当奖率三军,北定中原,庶竭驽钝,攘除奸凶,兴复汉室,还于旧都。此臣所以报先帝而忠陛下之职分也。至于斟酌损益,进尽忠言,则攸之、祎、允之任也。
5.亲贤臣,远小人,此先汉所以兴隆也;亲小人,远贤臣,此后汉所以倾颓也。先帝在时,每与臣论此事,未尝不叹息痛恨于桓、灵也。侍中、尚书、长史、参军,此悉贞良死节之臣,愿陛下亲之信之,则汉室之隆,可计日而待也。

public static void main(String[] args) throws IOException, ClassNotFoundException {
    //创建缓冲字符输入与输出流
			BufferedReader br = new BufferedReader(new FileReader("test.txt"));
			BufferedWriter bw = new BufferedWriter(new FileWriter("test2.txt"));
    //创建字符串集合
			ArrayList<String> list = new ArrayList<String>();
			String line =null;
    //逐行读入,添加至集合list中
			while((line=br.readLine())!=null) {
				list.add(line);
			}
    //根据字符串的第一个字,也就是序号升序排列,采用了Lambda表达式
			Collections.sort(list, (o1,o2) -> o1.charAt(0)-o2.charAt(0));
    //将排序后的list写入新的txt文件中
			for(String s : list) {
				bw.write(s);
				bw.newLine();
			}
			bw.close();
			br.close();
		}

五、转换流

5.1 转换流的作用

字符在计算机中都是以二进制数据存储的,但是我们不能直接看的明白二进制数据,需要对二进制数据进行“翻译”。

由二进制数据 -> 我们能够识别的字符,这一过程称为解码

字符 -> 二进制数据 这一过程称为编码

字符编码Character Encoding : 就是一套自然语言的字符与二进制数之间的对应规则。对于不同的编码,有不同的规则,编码对应的规则称为字符集

在这里插入图片描述

  • 编码和解码必须采用相同的字符集,如果一个文本是以ACSII码进行编码,却用GBK字符集进行解码,则会读到乱码

5.2 InputStreamReader&&OutputStreamWriter

构造方法:以文件输入流作为构造参数,还可以选择对应的解码字符集,默认为UTF-8编码。其余读写功能与FileInputStream一致。

public static void main(String[] args) throws IOException, ClassNotFoundException {	
			InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"));
			InputStreamReader isr2 = new InputStreamReader(new FileInputStream("a.txt"),"GBK");
			InputStreamReader isr3 = new InputStreamReader(new FileInputStream("a.txt"),"UTF-8");
			int len;
			while((len=isr.read())!=-1) {
				System.out.print((char)len);
			}
			System.out.println();
			while((len=isr2.read())!=-1) {
				System.out.print((char)len);
			}
			System.out.println();
			while((len=isr3.read())!=-1) {
				System.out.print((char)len);
			}
		isr.close();
		isr2.close();
		isr3.close();
		}
输出:dasniondiaosniof大设计师大教室的kxzcln2nkldsanl
dasniondiaosniof大设计师大教室的kxzcln2nkldsanl
dasniondiaosniof????????????kxzcln2nkldsanl  

输出转换流的构造与输入流一致,其区别是一个指定读取的解码字符集,一个指定写入的编码字符集。

5.3文本的读入与输出案例

以出师表为例,用GBK码读出,用UTF-8码编入。

public static void main(String[] args) throws IOException, ClassNotFoundException {
			InputStreamReader isr = new InputStreamReader(new FileInputStream("test.txt"),"GBK");
			OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("a.txt"),"UTF-8");
			int len;
			char b[] = new char[10];
			while((len=isr.read(b))!=-1) {
				osw.write(b,0,len);
			}	
			isr.close();
			osw.close();
		}
查看转换后的文本大小,发现UTF-8码的文件稍大一些

五、序列化流

Java 提供了一种对象序列化的机制。用一个字节序列可以表示一个对象,该字节序列包含该对象的数据对象的类型对象中存储的属性等信息。字节序列写出到文件之后,相当于文件中持久保存了一个对象的信息。

反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化对象的数据对象的类型对象中存储的数据信息,都可以用来在内存中创建对象。

序列化流有两个必须的条件:

  • 类要实现java.io.Serializable接口,不实现此接口的类将不会使任何状态序列化或反序列化,会抛出NotSerializableException
  • 该类的所有属性必须是可序列化的,不需要序列化的属性要加transient修饰符
  • 构造方法:
    • public ObjectOutputStream(OutputStream out)
    • public ObjectIntputStream(InputStream in)

5.1 序列化与反序列化

public class Day10 {

	public static void main(String[] args) throws IOException, ClassNotFoundException {
        //序列化、反序列化流的构造
			ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.txt"));
			ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.txt"));
   			//创建类的一个对象e
			Employee e = new Employee("ddd","aaa",7);
        //序列化对象e
			oos.writeObject(e);
			oos.close();
        //反序列化对象e,因为读取对象后变为Object类,需要向下造型
			Employee object = (Employee)ois.readObject();
			object.addressCheck();	
		}		
	}
class Employee implements java.io.Serializable{
	public String name;
	public String address;
	public int eid;
    //瞬态成员变量,不会被序列化
	public transient int member;
    //增加一个版本号,验证序列化的对象和对应类是否版本匹配
	private static final long serialVersionUID = 1L;
	public Employee(String name, String address, int eid) {
		super();
		this.name = name;
		this.address = address;
		this.eid = eid;
	}
	public void addressCheck() {
		System.out.println("Employee [name=" + name + ", address=" + address + ", eid=" + eid+ "]");
	}
		
}
  • 对于JVM可以反序列化对象,它必须是能够找到class文件的类。如果找不到该类的class文件,则抛出一个 ClassNotFoundException 异常。

  • 当JVM反序列化对象时,能找到class文件,但是class文件在序列化对象之后发生了修改,那么反序列化操作也会失败,抛出一个InvalidClassException异常。这是由于:

    • 该类的序列版本号与从流中读取的类描述符的版本号不匹配

    • 包含未知的数据类型

    • 没有可以访问的无参构造

5.1 练习

  1. 将存有多个自定义对象的集合序列化操作,保存到list.txt文件中。
  2. 反序列化list.txt ,并遍历集合,打印对象信息。
//Employee类同上,不再给出
public static void main(String[] args) throws IOException, ClassNotFoundException {
			ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.txt"));
			ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.txt"));
			ArrayList<Employee> list = new ArrayList<Employee>();
			//创建Employee类对象
			Employee e = new Employee("first","diyi",1);
			Employee e2 = new Employee("second","dier",2);
			Employee e3 = new Employee("third","disan",2);
			//将对象添加进集合
			Collections.addAll(list, e,e2,e3);
			//将集合序列化
			oos.writeObject(list);
			oos.close();
			//反序列化集合并输出
			ArrayList<Employee> list2 = (ArrayList<Employee>)ois.readObject();
			for(Employee ee: list2) {
				ee.addressCheck();
			}
		}

六、打印流

​ 平时我们在控制台打印输出,是调用print方法和println方法完成的,这两个方法都来自于java.io.PrintStream类,该类能够方便地打印各种数据类型的值,是一种便捷的输出方式。

6.1PrintSystem类

  • 构造方法:public PrintStream(String filename)
public static void main(String[] args) throws IOException, ClassNotFoundException {
    //创建打印流的对象,会覆盖文件
			PrintStream ps = new PrintStream("a.txt");
    //将System.out.println的目标从控制台改变为ps对象关联的文件
			System.setOut(ps);
			System.out.println("6666667打算的撒。。**77");
		}

特点:可以打印各种类型的数据

七、IO的异常处理

7.1 JDK之前的处理

采用try-catch-finally的形式,保证流一定关闭。

public static void main(String[] args) throws IOException {
		FileWriter fw = null;
		try{fw = new FileWriter("aa.txt");
		fw.write('A');}
		catch(IOException e) {
			System.out.println("抓住了");
		}
		finally{
            if(fw != null){
			fw.close();}
		}	
	}
}

7.2 JDK7的处理

JDK7优化后的try-with-resource 语句,该语句确保了每个资源在语句结束时关闭。所谓的资源(resource)是指在程序完成后,必须关闭的对象。

public static void main(String[] args)  {
try(FileWriter fw = new FileWriter("aa.txt");
   FileWriter fw2 = new FileWriter("bb.txt");
   ){
		fw.write('A');
		fw2.write('Q');				}
		catch(IOException e) {
			System.out.println("抓住了");
		}
}//即把创造流对象的语句放在try()中,如果有多个,用分号隔开

7.3 JDK9的处理

JDK9中try-with-resource 的改进,对于引入对象的方式,支持的更加简洁。被引入的对象,同样可以自动关闭,无需手动close,我们来了解一下格式。

public static void main(String[] args)  {
    FileWriter fw = new FileWriter("aa.txt");
   FileWriter fw2 = new FileWriter("bb.txt");
try(fw; fw2 ){
		fw.write('A');
		fw2.write('Q');				}
		catch(IOException e) {
			System.out.println("抓住了");
		}
}//即只要把创建好的流对象放在try()中即可

八、 属性集

java.util.Properties 继承于Hashtable ,来表示一个持久的属性集。它使用键值结构存储数据,每个键及其对应值都是一个字符串

基本方法:

  • public Properties() :创建一个空的属性列表。

  • public Object setProperty(String key, String value) : 保存一对属性。

  • public String getProperty(String key) :使用此属性列表中指定的键搜索属性值。

  • public Set<String> stringPropertyNames() :所有键的名称的集合。

public static void main(String[] args) throws  {
		Properties p = new Properties();
		p.setProperty("一个键", "一个值");
		System.out.println(p.getProperty("一个键"));//输出:一个值
   		System.out.println(p);
		Set<String> s = p.stringPropertyNames();
		}

比较特别的是它可以与流结合使用:

  • public load(InputStream in)

加载一个输入流的信息,流对应一个文本,可以使属性集加载文本中的数据,前提是文本中数据的格式必须是键值对的形式,可以使用空格、等号、冒号等符号分隔。

public static void main(String[] args) throws IOException {
		Properties p = new Properties();
		p.load(new FileInputStream("KV.txt"));
		Set<String> s= p.stringPropertyNames();
		for(String ss : s) {
		System.out.println(ss+p.getProperty(ss));
		}
	}

输出:

???filename-KV.txt
date-2020.6.3
time-1531

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值