【JavaSE学习笔记】IO流06_其他常用流(基本数据类型流、内存操作流、打印流、标准流、随机访问流、合并流、序列化与反序列化流)、属性集合类Properties

本文深入讲解Java中的IO流,包括数据流操作、内存操作流、打印流等,并介绍如何使用这些流来处理基本数据类型、合并文件及序列化对象。

IO流06

A.其他常用流

1)操作基本数据类型的流Data

1.DataOutputStream:数据输出流

import java.io.DataOutputStream;
import java.io.FileOutputStream;

public class Demo01 {
	public static void main(String[] args) throws Exception {
		// 创建数据输出流
		DataOutputStream dos = new DataOutputStream(new FileOutputStream("dos.txt"));

		// 写数据
		dos.writeByte(10);
		dos.flush();
		dos.writeShort(100);
		dos.flush();
		dos.writeInt(1000);
		dos.flush();
		dos.writeLong(10000L);
		dos.flush();
		dos.writeFloat(12.34F);
		dos.flush();
		dos.writeDouble(12.56);
		dos.flush();
		dos.writeBoolean(true);
		dos.flush();
		dos.writeChar('A');
		dos.flush();
		
		// 释放资源
		dos.close();
	}
}

最后输出txt里肯定是看不懂的乱码,但只要计算机能看懂就行了

接下来读取到控制台上

2.DataInputStream:数据输入流

import java.io.DataInputStream;
import java.io.FileInputStream;

public class Demo02 {
	public static void main(String[] args) throws Exception {
		// 创建数据输入流对象
		DataInputStream dis = new DataInputStream(new FileInputStream("dos.txt"));
		
		// 读数据
		byte b = dis.readByte();
		short s = dis.readShort();
		int i = dis.readInt();
		long l = dis.readLong();
		float f = dis.readFloat();
		double d = dis.readDouble();
		boolean flag = dis.readBoolean();
		char c = dis.readChar();
		
		dis.close();
		
		System.out.println(b);
		System.out.println(s);
		System.out.println(i);
		System.out.println(l);
		System.out.println(f);
		System.out.println(d);
		System.out.println(flag);
		System.out.println(c);
	}
}

2)内存操作流

用来存储内存中的临时信息,程序结束的时候,内存流就消失了!

1.字节数组操作内存流

ByteArrayInputStream

ByteArrayOutputStream

2.字符数组操作的内存流

CharArrayReader

CharArrayWriter

3.字符串操作的内存流

StringReader

StringWritwe

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

public class Demo01 {
	public static void main(String[] args) throws IOException {
		// 创建针对字节数组操作的内存输出流对象
		ByteArrayOutputStream baos = new ByteArrayOutputStream();

		// 写一个for循环
		for (int i = 0; i < 10; i++) {
			// 写数据
			baos.write(("hello" + i).getBytes());
		}

		// 关闭资源
		// public void close() throws IOException {}
		// 通过查看API原码:发现该流的close方法没有任何 的操作,该流可以不用关闭:
		// 该流本身就是内存流,存储内存中临时信息,当程序结束,跟该流有关的资源也就自动消失了
		// baos.close();

		// 字节内存输入流:public ByteArrayInputStream(byte[] buf)
		// public byte[] toByteArray()创建一个新分配的 byte 数组
		// 将字节数组内存操作输出流对象转换成字节数组
		byte[] bys = baos.toByteArray();
		// 创建字节数组内存操作输入流对象
		ByteArrayInputStream bais = new ByteArrayInputStream(bys);

		// 既然是字节流:就可以使用基本字节流读数据的方法
		// 一次读取一个字节
		int by = 0;
		while ((by = bais.read()) != -1) {
			System.out.print((char) by);
		}
	}
}

3)打印流Print

1.字节打印流

import java.io.IOException;
import java.io.PrintWriter;

public class Demo01 {
	public static void main(String[] args) throws IOException {
		// 创建打印流
		PrintWriter pw = new PrintWriter("a.txt");
		
		// 写数据
		pw.write("hello");
		pw.write("world");
		pw.write("java");
		
		pw.flush();
		pw.close();
	}
}

2.字符打印流

可以写任何数据类型:

PrintStream ps = System.out; 字节打印流

ps.println();

ps.print(数据类型);

字符打印流的一个构造方法:可以进行自动刷新

public PrintWriter(Writer out, boolean autoFlush)

如果想要是自动刷新,那么第二个参数的值为:true

换行还要使用字符打印流中的:println();

import java.io.FileWriter;
import java.io.PrintWriter;

public class Demo02 {
	public static void main(String[] args) throws Exception {
		PrintWriter pw = new PrintWriter(new FileWriter("pw.txt"), true);

		pw.println("hello");
		pw.println(100);
		pw.print(12.34F);
		
		pw.close();
	}
}

该流:不能操作数据源,只能操作目的的数据:该流只能写数据,不能读数据

该流可以自动刷新

该流还可以针对文本数据进行操作

(只要构造方法中有File对象或者String类型的路径都是可以对文本进行操作的)

需求:将当前项目下的a.txt文件复制到当前项目下的b.txt文件中

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

public class Demo03 {
	public static void main(String[] args) throws IOException {
		// 数据源
		BufferedReader br = new BufferedReader(new FileReader("a.txt"));
		
		// 目的
		PrintWriter pw = new PrintWriter(new FileWriter("b.txt"));
		
		// 写数据
		String line = null;
		while ((line = br.readLine()) != null) {
			pw.println(line);
		}
		
		pw.close();
		br.close();
	}
}

4)标准流

1.标准输入流:public static final InputStream in

2.标准输出流:public static final PrintStream out

3.字节数如流:InputStream = System.in

4.字节输出流:PrintStream = System.out

import java.io.PrintStream;

public class Demo01 {
	public static void main(String[] args) {
		// 我们一直使用的输出语句,其实就是采用流的一种进行:字节打印流进行输出
		System.out.println("hello world");

		PrintStream ps = System.out;// 返回的是字节打印流对象
		
		ps.println("hello world");
		
		ps.println();
	}
}
直接使用标准输出流:和刚才使用字符缓冲输入流封装字输入流一样,输出数据

import java.io.BufferedOutputStream;
import java.io.IOException;

public class Demo02 {
	public static void main(String[] args) throws IOException {
		
	// 和刚才使用字符缓冲输入流封装字输入流一样,输出数据
	BufferedOutputStream bos = new BufferedOutputStream(System.out);
	
	bos.write("hello".getBytes());
	bos.write("world".getBytes());
	bos.write("java".getBytes());
	
	bos.close();
	}
}
需求:用字符缓冲输入流包装字节输入流,进行录入数据!(键盘录入)

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Demo03 {
	public static void main(String[] args) throws IOException {
		// 1)使用输入流的方式将标准输入流包装起来,进行录入数据
		// InputStream is = System.in;

		// 字节流对象:
		// 可以获取一行数据吗?
		// 可以:readLine()这个方法哪个流里面的?属于字符缓冲输入流里面的方法,
		// BufferReader():底层应该也是用字节流进行实现,所有可以用它封装一下

		// BufferedReader br = new BufferedReader(is);
		// 字符缓冲流只能针对字符流进行操作,并不直接针对字节流操作
		// 2)哪个流可以将字节流转换成字符流
		// InputStreamReader isr = new InputStreamReader(is);//
		// 转换输入流=字节流+编码格式(gbk)
		// 3)在用字符缓冲输入流封装当前的转换流
		// BufferedReader br = new BufferedReader(isr);

		// 匿名对象一步到位
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

		// 录入数据
		System.out.println("请输入一个字符串:");
		String line = br.readLine();
		System.out.println("字符串的内容:" + line);

		System.out.println("请输入一个整数:");
		String s = br.readLine();
		int i = Integer.parseInt(s);
		System.out.println("整数是:" + i);
	}
}

5)随机访问流RandomAccessFile

并不是实际意义的流

public RandomAccessFile(String name,String mode):

mode:里面的模式:只记住:常用的就:rw:既可以读,也可以写

读数据

import java.io.IOException;
import java.io.RandomAccessFile;

public class Demo01 {
	public static void main(String[] args) throws IOException {
		RandomAccessFile raf = new RandomAccessFile("a.txt", "rw");

		byte b = raf.readByte();
		System.out.println(b);

		// public long getFilePointer(),返回此文件的偏移量
		System.out.println("当前指移动到的位置是:" + raf.getFilePointer());

		char ch = raf.readChar();
		System.out.println(ch);
		System.out.println("当前指移动到的位置是:" + raf.getFilePointer());

		// String s = raf.readUTF();
		// System.out.println(s);
		// System.out.println("当前指移动到的位置是:" + raf.getFilePointer());

		// public void seek(long pos)throws IOException
		// 设置到此文件开头测量到的文件指针偏移量
		
		// 从第四个字符开始
		raf.seek(3);
		char c = raf.readChar();
		System.out.println(c);
		
		raf.close();
	}
}
写数据

import java.io.IOException;
import java.io.RandomAccessFile;

public class Demo02 {
	public static void main(String[] args) throws IOException {
		RandomAccessFile raf = new RandomAccessFile("raf.txt", "rw");
		
		raf.writeByte(100);
		raf.writeChar('A');
		raf.writeUTF("中国");
		
		raf.close();
	}
}

6)合并流SequenceInputStream

合并流,将一些输入流合并起来,对输出流没有作用,表示其他输入流的逻辑串联

复制文件:a.txt--->b.txt,c.txt--->d.txt

合并流:a.txt+b.txt---->c.txt中

1.public SequenceInputStream(InputStream s1,InputStream s2)

需求:将当前项目下的a.txt文件和b.txt文件中的内容复制到当前项目下的c.txt文件中

import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.SequenceInputStream;

public class Demo01 {
	public static void main(String[] args) throws IOException {
		// 将数据源封装成合并流
		SequenceInputStream sis = new SequenceInputStream(new FileInputStream("a.txt"), new FileInputStream("b.txt"));

		// 创建字节缓冲输出流对象
		BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("c.txt"));
		
		// 读写操作
		byte[] bys = new byte[1024];
		int len = 0;
		while ((len = sis.read(bys)) != -1) {
			bos.write(bys, 0, len);
		}
		
		bos.close();
		sis.close();
	}
}

2.public SequenceInputStream(Enumeration e)

可以将两个以上文件合并

需求:将当前目录下的a.txt和b.txt和c.txt文件合并到d.txt文件中

import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.SequenceInputStream;
import java.util.Enumeration;
import java.util.Vector;

public class Demo02 {
	public static void main(String[] args) throws IOException {
		// 观察合并流构造方法中有一个:Enumration这个类型,和Vecotr集合有关系
		// 首先创建Vector集合对象
		Vector<InputStream> v = new Vector<InputStream>();
		
		InputStream s1 = new FileInputStream("a.txt");
		InputStream s2 = new FileInputStream("b.txt");
		InputStream s3 = new FileInputStream("c.txt");
		
		v.add(s1);
		v.add(s2);
		v.add(s3);
		
		Enumeration<InputStream> en = v.elements();
		// 创建合并流对象
		SequenceInputStream sis = new SequenceInputStream(en);
		// 创建字节缓冲流输出对象
		BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("d.txt"));
		
		byte[] bys = new byte[1024];
		int len = 0;
		while ((len = sis.read(bys)) != -1) {
			bos.write(bys, 0, len);
			bos.flush();
		}
		
		bos.close();
		sis.close();
	}
}

7)序列化流和反序列化流

序列化流:

将对象按照流的形式(在网络进行传输等等)封装成流数据:对象--->流数据:ObjectOutputStream

反序列化流:

将网络传输中的流数据又封装成了一个对象: 流数据-->对象   ObjectInputStream

Person类

若没有实现Serializable会怎样?java.io.NotSerializableException: xxx.Person

告诉我们:当前自定义的类并没有实现序列化接口

类通过实现 java.io.Serializable 接口以启用其序列化功能

未实现此接口的类将无法使其任何状态序列化或反序列化。

Serializable接口:

通过查看API:发现Serializable接口中没有字段,构造方法,成员方法

所有如果一个接口中没有任何的方法或者他的字段,将这种接口叫标记接口!

Person

import java.io.Serializable;

public class Person implements Serializable {

	// 默认的:default srial Version ID:默认版本ID
	// Person有黄色警告线,点击,生成下面的代码,获取ID
	private static final long serialVersionUID = 1L;
	// 成员变量
	private String name;
	private int age;
	// transient int age;

	public Person() {
		super();
		// TODO Auto-generated constructor stub
	}

	public Person(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

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

}

测试类

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class Demo01 {
	public static void main(String[] args) throws Exception {
		// 序列化
		ObjectOutputStream oot = new ObjectOutputStream(new FileOutputStream("oot.txt"));

		Person p = new Person("张三", 20);
		oot.writeObject(p);
		
		oot.close();
		
		//反序列化
		ObjectInputStream ois = new ObjectInputStream(new FileInputStream("oot.txt"));
		
		Object obj = ois.readObject();
		
		ois.close();
		
		System.out.println(obj);
	}
}
1.只进行序列化

2.将序列化注释掉,

3.修改Person类中的成员变量private int age ----> int age

4.再进行反序列化

结果:java.io.InvalidClassException: 

local class incompatible: stream classdesc serialVersionUID = -3059867121192605950, 

local class serialVersionUID = -1219396793646518298

原因:

序列化流和反序列化流

刚才已经手动写了,并且运行了一次

每一次在加载Person.class文件的时候,都会生成一个ID

比如:第一次没有修改Person类中文件:class文件

Person.class ---->ID = 100 ;

private String name --->ID =100

prvate int age ------->ID = 100 

后面修改了class文件中的内容

Person.class------>ID= 200

private String name --->ID =100

int age ------->ID = 100

这样的话:ID版本号不匹配:就出现了刚才这个异常:

实际开发中,可能有时候需要用到以前的数据

所以就需要我们将当前的这个class文件要生成固定ID

这样的话就不会报异常了

当前的这个类有黄色警告线,点击黄色警告线生成ID

如果在一个类中,该类的成员变量比较多

有时候为了让成员变量不被序列化

加一个关键字:transient

这样:如果是 String name,输出的是null;int age,输出的是0

B.属性集合类Properties

1)概述

这个类是Hashtbale的子类,而Hashtable也Map下面的双列集合

注意:Properties:不带泛型!

import java.util.Properties;
import java.util.Set;

public class Demo01 {
	public static void main(String[] args) {
		Properties prop = new Properties();
		
		prop.put("1001", "张三");
		prop.put("1002", "李四");
		prop.put("1003", "王五");
		
		// 遍历
		Set<Object> set = prop.keySet();
		for (Object key : set) {
			Object value = prop.get(key);
			System.out.println(key + "---" + value);
		}
	}
}

2)特殊功能

public Object setProperty(String key,String value):和添加相关的

public Set<String> stringPropertyNames():获取所有的键的集合

public String getProperty(String key):获取指定的属性集合中的键的值

import java.util.Properties;
import java.util.Set;

public class Demo02 {
	public static void main(String[] args) {
		Properties prop = new Properties();
		
		prop.setProperty("张三", "20");
		prop.setProperty("李四", "21");
		prop.setProperty("王五", "22");
		
		// 获取所有键集合
		Set<String> set = prop.stringPropertyNames();
		for (String key : set) {
			// 获取指定的值
			String value = prop.getProperty(key);
			System.out.println(key + "---" + value);
		}
		
	}
}

3)功能

public void load(Reader reader):将文本文件的数据读取集合中

public void store(Writer writer, String comments):将集合中的数据保存到文本文件中

comments:属性列表的描述

import java.io.FileReader;
import java.io.FileWriter;
import java.io.Reader;
import java.io.Writer;
import java.util.Properties;

public class Demo03 {
	public static void main(String[] args) throws Exception {
		Properties prop = new Properties();
		
		prop.setProperty("张三", "21");
		prop.setProperty("李四", "22");
		prop.setProperty("王五", "23");
		
		// 将集合中的数据保存到文件中
		Writer w = new FileWriter("name.txt");
		prop.store(w, "Person");
		
		// 将文件中的数据读取集合中
		Reader r = new FileReader("name.txt");
		prop.load(r);

		r.close();
		w.close();
		System.out.println(prop);
	}
}

4)练习

我有一个文本文件(user.txt),我知道数据是键值对形式的,但是不知道内容是什么。

请写一个程序判断是否有“lisi”这样的键存在,如果有就改变其实为”100”

1.先将文件中的数据加载到集合中

2.获取键的集合,遍历键的集合

判断:如果"lisi"就是里面的key

是的话就修改

3.重新将集合中的数据保持到文件中


import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Properties;
import java.util.Set;

public class Demo04 {
	public static void main(String[] args) throws IOException {
		Properties prop = new Properties();
		prop.load(new FileReader("user.txt"));
		
		Set<String> set = prop.stringPropertyNames();
		for (String key : set) {
			if ("李四".equals(key)) {
				prop.setProperty("李四", "100");
				System.out.println("匹配到结果,并完成修改,请查看user.txt");
				break;
			}
		}
		
		prop.store(new FileWriter("user.txt"), "user");
	}
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值