带妹学Java第二十天(IO流结束篇)

重点

1.字节数组输出流

1.1概述

 ByteArrayOutputStream 字节数组输出流
 ByteArrayOutputStream 不需要关联文件
 此类实现了一个输出流,其中的数据被写入一个 byte 数组 缓冲区。
 缓冲区会随着数据的不断写入而自动增长。
 可使用 toByteArray() 和 toString() 获取数据。
在这里插入图片描述

1.2ByteArrayOutputStream的使用

//1.创建字节数组输出流对象
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		//2.输入流
		FileInputStream fis = new FileInputStream("a.txt");
		//3.写入输出流
		int b = 0;
		while((b = fis.read()) != -1){
			baos.write(b);
		}
		//自动把字节数组 转成 字符串
		System.out.println(baos.toString());
		System.out.println(baos.toString("UTF-8"));//指定编码格式的字符串
		
		//获取文件数据
		byte[] bytes = baos.toByteArray();
		System.out.println(new String(bytes));
		System.out.println(new String(bytes, "UTF-8"));//指定编码格式的字符串
		//4.关流
		fis.close();

2.对象操作流

2.1ObjectOutputStream

 这个类是将一个对象写入文件
 如果使用这个类写入对象,这个对象需要序列化
 序列化就是让这个对象实现一个Serializable接口
 如果没实现Serializable接口,会抛异常NotSerializableException
 对象写入文件时,乱码没有关系,取出来正确就行了

2.2ObjectInputStream

 这个类是从文件中读取对象

3.序列化相关概念

 归档(序列化) :将对象存在一个文件的过程
 解归档(反序列化):把一个文件解析出对象

4.Serializable接口的ID讲解

1.要归档或者序列化的对象必须实现Serializable接口才能被序列化
2.Serializable 中有个id,但ID不是一定要加的
3.SerialVersionUid,简言之,其目的是以序列化对象进行版本控制,有关各版本反序列化时是否兼容。
4.如果在新版本中这个值修改了,新版本就不兼容旧版本,反序列化时会抛出InvalidClassException异常。
5.如果修改较小,比如仅仅是增加了一个属性,我们希望向下兼容,老版本的数据都能保留,那就不用修改;
6.如果我们删除了一个属性,或者更改了类的继承关系,必然不兼容旧数据,这时就应该手动更新版本号,即SerialVersionUid。
7.一般不会添加ID,就算添加了ID,版本号最好不要修改

public class Demo01 {

	public static void main(String[] args) throws IOException, ClassNotFoundException{
		// TODO Auto-generated method stub
		
		//save();
		//取学生
		ObjectInputStream ois = new ObjectInputStream(new FileInputStream("stu.data"));
		Student stu = (Student) ois.readObject();
		System.out.println(stu);
		
	}

	public static void save() throws IOException, FileNotFoundException {
		//1.创建学生对象
		Student stu = new Student("霍建华");
		
		//2.把学生存入文件
		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("stu.data"));
		
		/**
		 * 存的学生的版本号是1
		 */
		oos.writeObject(stu);
		
		//3.关流
		oos.close();
	}

}


class Student implements Serializable{

	/**
	 * 序列化版本ID
	 */
	private static final long serialVersionUID = 1L;
	
	private String name;
	
	private String hometown;

	public Student(String name) {
		super();
		this.name = name;
	}

	@Override
	public String toString() {
		return "Student [name=" + name + "]";
	}
	
	
	
	
}

5.打印流

5.1PrintStream 打印字节流

 System.out就是一个PrintStream, 其默认向控制台输出信息
 该流可以很方便的将对象的toString()结果输出, 并且自动加上换行

5.2PrintWriter 打印字符流

 这个流是向文件打印信息,也就是将打印的内容写入文件

	//1.打印的字节流【打印到控制台】
		PrintStream ps = System.out;
		ps.println("你好,元宵节快乐");//打印字符串
		ps.println(19.6);
		
		//2.打印的字符流
		/**
		 * 1.PrintWriter调用打印方法,控制台是没有内容,它是把内容写到文件中
		 * 2.如果打印了内容,没有调用flush或者close,内容在文件中也不存在
		 */
		PrintWriter pw = new PrintWriter("test.txt");
		pw.println("吃汤圆了...");
		pw.print("超市一包汤圆10几块钱");
		pw.print(13.01);
		//pw.flush();
		pw.close();

6.标准输入输出流

 System.in是InputStream, 标准输入流, 默认可以从键盘输入读取字节数据
 System.out是PrintStream, 标准输出流, 默认可以向Console中输出字符和字节数据

7.随机访问流

 RandomAccessFile类不属于流,是Object类的子类。
 但它融合了InputStream和OutputStream的功能。
 支持对随机访问文件的读取和写入。

   /**
		 * 构造方法两个参数:
		 * name:文件名称、路径
		 * mode:模式 ,r=read 只读、 w=write
		 */
		//1.创建一个随机访问流对象,以读写的方式打开文件
		RandomAccessFile raf = new RandomAccessFile("a.txt", "rw");
		
		//2.读字符
		/**
		 * 使用RandomAccessFile的readChar/readLine方法读文件有乱码问题
		 */
/*		System.out.println(raf.readChar());
		System.out.println(raf.readChar());
		System.out.println(raf.readChar());
		System.out.println(raf.readLine());*/
		
		//使用字节数组来读比较好
/*		byte[] buf = new byte[1024];
		int len;
		while((len = raf.read(buf)) != -1){
			System.out.println(new String(buf,0,len));
		}*/
		
		//3.写数据
		//raf.writeChars("abc");
		//raf.writeBytes("abc");
		raf.seek(4);//指定位置
		raf.write(97);
		raf.write(98);
		raf.write(99);
	}

8.数据输入输出流

 DataInputStream, DataOutputStream可以按照基本数据类型大小读写数据
 例如按Long大小写出一个数字, 写出时该数据占8字节.
 读取的时候也可以按照Long类型读取, 一次读取8个字节.

	long a = 997;
		long b = 998;
		long c = 999;
		
		//使用FileOutputStream没法写入long类型数据
		FileOutputStream fos = new FileOutputStream("a.txt");
		//byte -128~127 0~255
/*		fos.write(997);只会写一个字节,不会写8个字节
		fos.write(998);
		fos.write(999);*/
		
		//fos.wr
		DataOutputStream dos = new DataOutputStream(fos);
		dos.writeLong(a);//写8个字节
		dos.writeLong(b);
		dos.writeLong(c);
		
		dos.close();

		//读3个long数据
		DataInputStream dis = new DataInputStream(new FileInputStream("a.txt"));
		System.out.println(dis.readLong());//读8个字节
		System.out.println(dis.readLong());//读8个字节
		System.out.println(dis.readLong());//读8个字节

9.Properties类【掌握-经常用】

 Properties:属性,与Map的使用有点类似
 Properties 类表示了一个持久的属性集。
 Properties 可保存在流中或从流中加载,这个类可以读写文件。
 属性列表中每个键及其对应值都是一个字符串。
 存中文时,会转成Unicode编译存储

扫描二维码关注公众号,回复: 8898883 查看本文章
public class Demo01 {

	public static void main(String[] args) throws IOException, IOException {
		//Properties的概述和作为Map集合的使用【掌握-经常用】

		/**
		 * Properties:属性
			1.Properties 类表示了一个持久的属性集。
			2.Properties 可保存在流中或从流中加载。
			3.属性列表中每个键及其对应值都是一个字符串。
		 */
		
		//遍历properties所有属性key和值value
		//1.创建属性对象
		Properties p = new Properties();
		
		//2.关联文件
		p.load(new FileInputStream("info.properties"));
		
		//3.遍历一
		Set<Object> keys = p.keySet();
		for(Object key : keys){
			System.out.println(key + "=" + p.get(key));
		}
		//System.out.println(keys);
		
		//4.遍历二
		System.out.println("=====================");
		//p.entrySet();
		for(Entry<Object, Object> entry :p.entrySet()){
			System.out.println(entry.getKey() + "=" + entry.getValue());
		}
	}

	public static void test2() throws IOException, FileNotFoundException {
		//使用Properties读取数据
		//1.创建属性对象
		Properties p = new Properties();
		
		//2.关联文件
		p.load(new FileInputStream("info.properties"));
		
		
		//3.通过key读数据
		String name = p.getProperty("name");
		String city = p.getProperty("city");
		String hometown = p.getProperty("hometown");
		System.out.println(name);
		System.out.println(city);
		System.out.println(hometown);
	}

	/**
	 * 使用Properties来存储数据
	 */
	public static void test1() throws IOException, FileNotFoundException {
		//1.创建属性对象
		Properties p = new Properties();
		
		//2.存数据
		p.setProperty("name", "gyf");
		p.setProperty("city", "广州");
		p.setProperty("hometown", "梅州");
		
		//3.关联文件
		/**
		 * 当Properties把key和value存入文件,把中文转成unicode编码
		 */
		p.store(new FileOutputStream("info.properties"), null);
	}
}

在这里插入图片描述

练习题

1.1直接把对象存在文件中

public static void test1() throws FileNotFoundException, IOException {
		//案例:将对象直接存入文件
		//1.创建女朋友对象
		GirlFriend gf1 = new GirlFriend("林志玲", 1.78);
		GirlFriend gf2 = new GirlFriend("林心如", 1.68);
		
		//2.创建对象输出流
		FileOutputStream fos = new FileOutputStream("gf.data");
		ObjectOutputStream oos = new ObjectOutputStream(fos);
		
		//3.往文件存入对象
		oos.writeObject(gf1);
		oos.writeObject(gf2);
		
		//4.关流
		oos.close();
	}

}

class GirlFriend implements Serializable{
	String name;
	double height;
	public GirlFriend(String name, double height) {
		super();
		this.name = name;
		this.height = height;
	}
	@Override
	public String toString() {
		return "GirlFriend [name=" + name + ", height=" + height + "]";
	}
}

1.2从文件中直接取出对象

//案例:从文件中取出女朋友对象
		//1.创建一个对象输入流
		FileInputStream fis = new FileInputStream("gf.data");
		ObjectInputStream ois = new ObjectInputStream(fis);
		
		//2.读数据
		GirlFriend gf1 = (GirlFriend) ois.readObject();
		System.out.println(gf1);
		GirlFriend gf2 = (GirlFriend) ois.readObject();
		System.out.println(gf2);
		
		/**
		 * 如果没有数据可读了,强制读的时候报错了EOFException,
		 */
		System.out.println(ois.readObject());

2.对象操作流优化

把女朋友对象存在List中,再把List写入文件【序列化/归档】

public class Demo01 {

	public static void main(String[] args) throws IOException, IOException {
		//对象操作流优化-思路,存多个对象时,把对象存在list里面
		
		
		
		//1.创建集合
		List<GirlFriend> list = new ArrayList<GirlFriend>();
		
		//2.添加女朋友
		list.add(new GirlFriend("林志玲", 1.78));
		list.add(new GirlFriend("林心如", 1.68));
		list.add(new GirlFriend("林嘉欣", 1.58));
		
		//3.把list存入文件
		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("list.data"));
		oos.writeObject(list);
		
		//4.关流
		oos.close();
	}
}

class GirlFriend implements Serializable{
	String name;
	double height;
	public GirlFriend(String name, double height) {
		super();
		this.name = name;
		this.height = height;
	}
	@Override
	public String toString() {
		return "GirlFriend [name=" + name + ", height=" + height + "]";
	}
}

从list.data 读取所有的女朋友对象【反序列化、解归档】

public class Demo02 {

	public static void main(String[] args) throws IOException, ClassNotFoundException {
		// TODO Auto-generated method stub

		//1.创建对象输入流
		ObjectInputStream ois = new ObjectInputStream(new FileInputStream("list.data"));
		
		//2.读取List对象
		Object obj = ois.readObject();
		
		//把obj转list
		@SuppressWarnings("unchecked")
		List<GirlFriend> list = (List<GirlFriend>)obj;
		System.out.println(obj.getClass());
		
		//遍历
		for(GirlFriend gf : list){
			System.out.println(gf);
		}
		
		
		ois.close();
	}

}

面试题

1.找Bug

public class Demo01 {
	public static void main(String[] args) throws IOException {
/*		面试题:找bug
		定义一个文件输入流,调用read(byte[] b)方法,
		将a.txt文件中的内容打印出来(byte数组大小限制为5)*/

		//1.文件输入流
		FileInputStream fis = new FileInputStream("a.txt");
		
		//2.字节数组输出流
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		
		//3.字节数组【缓冲区思想,杯子思想】
		byte[] arr = new byte[5];
		int len;
		while((len = fis.read(arr)) != -1) {
			//通过字节缓冲数组写入
			baos.write(arr, 0, len);
			
			//把字节转成字符串
			/**中文乱码问题*/
			System.out.println(new String(arr,0,len));
		}		
		//内部会调用toString方法,把字节数组转成字符串
		System.out.println(baos);	
		fis.close();
	}
}

  • UTF-8存储的中文字符占3个字节,用5个字节数组当“杯子”,会出现中文乱码。

总结

IO流学了几天了,学了很多流的使用方法。这里做一个归纳:

public static void main(String[] args) {
		/**
		 * 一、IO流-输入输出流
		 * I:input
		 * O:out
		 * IO流 操作文件,读取文件内容,往文件写内容
		 * 
		 * 二、字节流
		 * 
		 * >InputStream
		 *  -FileInputStream 文件输入流,读的单位是字节
		 *  -BufferedInputStream 缓冲输入流,内部有个byte[]字节数组
		 *  -SequenceInputStream 序列流,把多个字节流整合成一个流
		 *  -ObjectInputStream 对象输入流,直接从文件中读取一个对象,这个对象要实现serilazable接口
		 *  -Sytem.in 标准输入流-指键盘
		 *  -DataInputStream 数据输入流,按基本数据类型的大小(long) 读取文件
		 *  
		 * >OutputStream
		 *  -FileOutputStream 文件输出流,写的单位是字节
		 *  -BufferedOutputStream 缓冲输出流,内部有个byte[]字节数组
		 *  -ByteArrayOutputStream 字节数组输出流,把数据读取到内存中,这个类不需要关联文件
		 *  -ObjectOutputStream 对象输出流,直接把一个对象存入文件,
		 *  -PrintStream 打印流,把内容打印到控制台
		 *  -System.out 标准输出流-指控制台
		 *  -DataOutputStream 数据输出流,按基本数据类型的大小(long) 写入文件
		 *  
		 * 三、字符流
		 * >Reader
		 *  -FileReader 文件读取流,读取的单位是字符
		 *  -BufferedReader 缓冲读取流,内部有个char[] 字符数组
		 *  -InputStreamReader 指定字符编码读取文件
		 * 
		 * >Writer
		 *  -FileWriter 文件写入流,写入的单位是字符
		 *  -BufferedWriter,缓冲写入流,内部有个char[] 字符数组
		 *  -OutputStreamWriter 指定字符编码写入文件
		 *  -PrintWriter 打印流,把内容打印一个文件
		 *  
		 *  
		 * RandomAccessFile 随机访问流,特点:读和写都在一个类中
		 * 
		 * Properties 相当于Map一样使用,这个类把数据存在一个后缀名为.properties文件
		 */
	}

附加学了序列化/反序列化,以及Properties(内部继承的是Hashtable,可以当作Map来使用),这么多的流,掌握常用的字节流FileInputStream/FileOutputStream/BufferInputStream/BufferOutputStream;常用的字符流FileReader/FileWriter/BufferedReader /BufferedWriter.另外还需掌握流的读写方法,杯字思想(缓冲思想)、字节数组(缓冲区)。其他的流到时候可以查API文档,了解即可。

发布了24 篇原创文章 · 获赞 4 · 访问量 608

猜你喜欢

转载自blog.csdn.net/qq_43488797/article/details/103929719