缓冲流&转换流&序列化流&打印流&Properties

JAVAOOP

1. 【理解】什么是缓冲流
2. 【掌握】缓冲流的使用
3. 【理解】转换流
4. 【理解】序列化流
5. 【理解】打印流
6. 【掌握】Properties集合的使用

缓冲流

​ 前期我们学习了基本的一些流,作为IO流的入门,今天我们要见识一些更强大的流。比如能够高效读写的缓冲流,能够转换编码的转换流,能够持久化存储对象的序列化流等等。这些功能更为强大的流,都是在基本的流对象基础之上创建而来的,就像穿上铠甲的武士一样,相当于是对基本流对象的一种增强。

缓冲流概述

缓冲流,也叫高效流,是对4个基本的`FileXxx` 流的增强,所以也是4个流,按照数据类型分类

通过定义数组的方式确实比以前一次读取一个字节的方式快很多,所以,看来有一个缓冲区还是非常好的。

既然是这样的话,那么,java开始在设计的时候,它也考虑到了这个问题,就专门提供了带缓冲区的字节类。

这种类被称为:缓冲区类(高效类)

写数据:BufferedOutputStream

读数据:BufferedInputStream

构造方法可以指定缓冲区的大小,但是我们一般用不上,因为默认缓冲区大小就足够了。

为什么不传递一个具体的文件或者文件路径,而是传递一个OutputStream对象呢?

 原因很简单,字节缓冲区流仅仅提供缓冲区,为高效而设计的。但是呢,真正的读写操作还得靠基本的流对象实现。
名称
字节缓冲流BufferedInputStream BufferedOutputStream
字符缓冲流BufferedReader BufferedWriter

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

字节缓冲流

构造方法

方法名说明
public BufferedInputStream(InputStream in)创建一个 新的缓冲输入流。
public BufferedOutputStream(OutputStream out)创建一个新的缓冲输出流。

字节缓冲输出流

public class BufferedDemo {
        public static void main(String[] args) throws IOException {

        //相当于把字节流进行了一次装
        //FileOutputStream fos = new FileOutputStream("a.txt");
        //BufferedOutputStream bos = new BufferedOutputStream(fos);

        //简单写法
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("a.txt"));
        //也可以进行追加
        //BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("a.txt",true));

        //写一个字节
        bos.write(97);
        //写一个字节数组
        byte[] by = {97,98,99};
        bos.write(by);
        bos.write("hello".getBytes());

        //指定写一个字节
        bos.write(by,1,2);
        //关闭
        bos.close();
    }
}

字节缓冲输入流

public class BufferedDemo {
    public static void main(String[] args) throws IOException {

        //字节输入缓冲流
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("a.txt"));

        //一次读出一个字节
        int read = bis.read();
        System.out.println((char)read);

        //一次读出一个字节数组
        byte[] by = new byte[1024];
        int len;
        while ((len = bis.read(by)) != -1){
            System.out.println(new String(by,0,len));
        }

    }
}

字节缓冲流复制

	public static void main(String[] args) throws IOException {
		//读
		BufferedInputStream bis = new BufferedInputStream(new FileInputStream("a.txt"));
		//写
		BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("b.txt"));
		//复制
		byte[] bys = new byte[1024];
		int len =0;
		while((len = bis.read(bys)) != -1){
			bos.write(bys,0,len);
		}
		
		bis.close();
		bos.close();
	}

测试

/*
 * 需求:把D:\\工作\\EV已录视频\\01-课程介绍.mp4复制到当前项目目录下的copy.mp4中
 * 
 * 字节流四种方式复制文件:
 * 基本字节流一次读写一个字节:	共耗时:117235毫秒
 * 基本字节流一次读写一个字节数组: 共耗时:156毫秒
 * 高效字节流一次读写一个字节: 共耗时:1141毫秒
 * 高效字节流一次读写一个字节数组: 共耗时:47毫秒
 */
public class CopyMp4Demo {
	public static void main(String[] args) throws IOException {

		long start = System.currentTimeMillis();

		 method1("D:\\工作\\EV已录视频\\01-课程介绍.mp4", "copy1.mp4");
		 method2("D:\\工作\\EV已录视频\\01-课程介绍.mp4", "copy2.mp4");
		 method3("D:\\工作\\EV已录视频\\01-课程介绍.mp4", "copy3.mp4");
		 method4("D:\\工作\\EV已录视频\\01-课程介绍.mp4", "copy4.mp4");	
	
		long end = System.currentTimeMillis();
		System.out.println("共耗时:" + (end - start) + "毫秒");
	}

	// 高效字节流一次读写一个【字节数组】:
	public static void method4(String srcString, String destString)
			throws IOException {
		BufferedInputStream bis = new BufferedInputStream(new FileInputStream(
				srcString));
		BufferedOutputStream bos = new BufferedOutputStream(
				new FileOutputStream(destString));

		byte[] bys = new byte[1024];
		int len = 0;
		while ((len = bis.read(bys)) != -1) {
			bos.write(bys, 0, len);
		}

		bos.close();
		bis.close();
	}

	// 高效字节流一次读写一个【字节】:
	public static void method3(String srcString, String destString)
			throws IOException {
		BufferedInputStream bis = new BufferedInputStream(new FileInputStream(
				srcString));
		BufferedOutputStream bos = new BufferedOutputStream(
				new FileOutputStream(destString));

		int by = 0;
		while ((by = bis.read()) != -1) {
			bos.write(by);

		}

		bos.close();
		bis.close();
	}

	// 基本字节流一次读写一个字节数组
	public static void method2(String srcString, String destString)
			throws IOException {
		FileInputStream fis = new FileInputStream(srcString);
		FileOutputStream fos = new FileOutputStream(destString);

		byte[] bys = new byte[1024];
		int len = 0;
		while ((len = fis.read(bys)) != -1) {
			fos.write(bys, 0, len);
		}

		fos.close();
		fis.close();
	}

	// 基本字节流一次读写一个字节
	public static void method1(String srcString, String destString)
			throws IOException {
		FileInputStream fis = new FileInputStream(srcString);
		FileOutputStream fos = new FileOutputStream(destString);

		int by = 0;
		while ((by = fis.read()) != -1) {
			fos.write(by);
		}

		fos.close();
		fis.close();
	}
}

字符缓冲流

构造方法

方法名说明
public BufferedReader(Reader in)创建一个 新的缓冲输入流。
public BufferedWriter(Writer out)创建一个新的缓冲输出流。

字符缓冲输出流

    public static void main(String[] args) throws IOException {

        //字符输出流
        BufferedWriter bw = new BufferedWriter(new FileWriter("c.txt"));

        bw.write(97);
        bw.write("hello");
        char[] chs = {'j','a','v','a',};
        bw.write(chs);
        bw.close();
    }

字符缓冲输入流

    public static void main(String[] args) throws IOException {

        //字符输入流
        BufferedReader br = new BufferedReader(new FileReader("c.txt"));

        //读一个字节
        int read = br.read();
        System.out.println(read);

        //循环读
        int ch;
        while ( (ch = br.read()) != -1){
            System.out.print((char)ch);
        }

        //循环读字符数组
        char[] c = new char[1024];
        int ch2;
        while ( (ch2 = br.read(c)) != -1){
            System.out.print(new String(c,0,ch2));
        }

    }

字符流缓冲流复制

/*
 * 需求:把当前项目目录下的a.txt内容复制到当前项目目录下的b.txt中
 * 
 * 数据源:
 * 		a.txt -- 读取数据 -- 字符转换流 -- InputStreamReader -- FileReader -- BufferedReader
 * 目的地:
 * 		b.txt -- 写出数据 -- 字符转换流 -- OutputStreamWriter -- FileWriter -- BufferedWriter
 */
public class CopyFileDemo {
	public static void main(String[] args) throws IOException {
		// 封装数据源 读
		BufferedReader br = new BufferedReader(new FileReader("a.txt"));
		// 封装目的地 写
		BufferedWriter bw = new BufferedWriter(new FileWriter("b.txt"));

		// 两种方式其中的一种一次读写一个字符数组
		char[] chs = new char[1024];
		int len = 0;
		while ((len = br.read(chs)) != -1) {
			bw.write(chs, 0, len);
			bw.flush();
		}

		// 释放资源
		bw.close();
		br.close();
	}
}

字符缓冲流特有方法

类名方法名说明
BufferedReaderpublic String readLine()一次读取一行文字
BufferedWriterpublic void newLine()写一行行分隔符,由系统属性定义符号
/*
 * 字符缓冲流的特殊方法:
 * BufferedWriter:
 *        public void newLine():根据系统来决定换行符  写入时换行
 */
public static void main(String[] args) throws IOException {

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

    for (int x = 0; x < 10; x++) {
        bw.write("hello" + x);
        // bw.write("\r\n");//之前换行做法
        bw.newLine();//现在换行做法
        bw.flush();
    }

    bw.close();
}

/*
 * 字符缓冲流的特殊方法:
 * BufferedReader:
 *        public String readLine():一次读取一行数据
 *        包含该行内容的字符串,不包含任何行终止符(不包含换行),如果已到达流末尾,则返回 null
 */
public static void main(String[] args) throws IOException {
    // 创建字符缓冲输入流对象
    BufferedReader br = new BufferedReader(new FileReader("bw2.txt"));

    // String line = br.readLine();//一次读取一行数据
    // System.out.println(line);

    // 最终版代码
    String line = null;
    while ((line = br.readLine()) != null) {
        System.out.println(line);
    }

    //释放资源
    br.close();
}

转换流

OutputStreamWriter(OutputStream out):根据默认编码把字节流的数据转换为字符流
OutputStreamWriter(OutputStream out,String charsetName):根据指定编码把字节流数据转换为字符流
把字节流转换为字符流。
转换流 <==> 字符流 = 字节流 + 编码表。

字符编码和字符集

字符编码

​ 计算机中储存的信息都是用二进制数表示的,而我们在屏幕上看到的数字、英文、标点符号、汉字等字符是二进制数转换之后的结果。按照某种规则,将字符存储到计算机中,称为编码 。反之,将存储在计算机中的二进制数按照某种规则解析显示出来,称为解码 。比如说,按照A规则存储,同样按照A规则解析,那么就能显示正确的文本符号。反之,按照A规则存储,再按照B规则解析,就会导致乱码现象。

编码:字符(能看懂的)–字节(看不懂的)

解码:字节(看不懂的)–>字符(能看懂的)

  • 字符编码Character Encoding : 就是一套自然语言的字符与二进制数之间的对应规则。

    编码表:生活中文字和计算机中二进制的对应规则

字符集

  • 字符集 Charset:也叫编码表。是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等。

计算机要准确的存储和识别各种字符集符号,需要进行字符编码,一套字符集必然至少有一套字符编码。常见字符集有ASCII字符集、GBK字符集、Unicode字符集等。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KoCgVzlt-1683792916949)(assets/1_charset.jpg)]

可见,当指定了编码,它所对应的字符集自然就指定了,所以编码才是我们最终要关心的。

常见的字符集

  • ASCII字符集

    • ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)是基于拉丁字母的一套电脑编码系统,用于显示现代英语,主要包括控制字符(回车键、退格、换行键等)和可显示字符(英文大小写字符、阿拉伯数字和西文符号)。
    • 基本的ASCII字符集,使用7位(bits)表示一个字符,共128字符。ASCII的扩展字符集使用8位(bits)表示一个字符,共256字符,方便支持欧洲常用字符。
  • ISO-8859-1字符集

    • 拉丁码表,别名Latin-1,用于显示欧洲使用的语言,包括荷兰、丹麦、德语、意大利语、西班牙语等。
    • ISO-8859-1使用单字节编码,兼容ASCII编码
  • GBxxx字符集

    • GB就是国标的意思,是为了显示中文而设计的一套字符集。
    • GB2312:简体中文码表。一个小于127的字符的意义与原来相同。但两个大于127的字符连在一起时,就表示一个汉字,这样大约可以组合了包含7000多个简体汉字,此外数学符号、罗马希腊的字母、日文的假名们都编进去了,连在ASCII里本来就有的数字、标点、字母都统统重新编了两个字节长的编码,这就是常说的"全角"字符,而原来在127号以下的那些就叫"半角"字符了。
    • GBK:最常用的中文码表。是在GB2312标准基础上的扩展规范,使用了双字节编码方案,共收录了21003个汉字,完全兼容GB2312标准,同时支持繁体汉字以及日韩汉字等。
    • GB18030:最新的中文码表。收录汉字70244个,采用多字节编码,每个字可以由1个、2个或4个字节组成。支持中国国内少数民族的文字,同时支持繁体汉字以及日韩汉字等。
  • Unicode字符集

    • Unicode编码系统为表达任意语言的任意字符而设计,是业界的一种标准,也称为统一码、标准万国码。
    • 它最多使用4个字节的数字来表达每个字母、符号,或者文字。有三种编码方案,UTF-8、UTF-16和UTF-32。最为常用的UTF-8编码。
    • UTF-8编码,可以用来表示Unicode标准中任何字符,它是电子邮件、网页及其他存储或传送文字的应用中,优先采用的编码。互联网工程工作小组(IETF)要求所有互联网协议都必须支持UTF-8编码。所以,我们开发Web应用,也要使用UTF-8编码。它使用一至四个字节为每个字符编码,编码规则:
      1. 128个US-ASCII字符,只需一个字节编码。
      2. 拉丁文等字符,需要二个字节编码。
      3. 大部分常用字(含中文),使用三个字节编码。
      4. 其他极少使用的Unicode辅助字符,使用四字节编码。
    	/*
    	 * 计算机是如何识别什么时候该把两个字节转换为一个中文呢?
    	 * 在计算机中中文的存储分两个字节:
    	 * 		第一个字节肯定是负数。
    	 * 		第二个字节常见的是负数,可能有正数。但是没影响。
    	 */
    		public static void main(String[] args) {
    //			 String s = "abcde";
    			//[97, 98, 99, 100, 101]  正数不拼
    
    			String s = "我爱你中国";//因为当前我的编码是utf-8 是占三个字节的
    			// [-26, -120, -111, -25, -120, -79, -28, -67, -96, -28, -72, -83, -27, -101, -67]  负数拼
    
    			byte[] bys = s.getBytes();
    			System.out.println(Arrays.toString(bys));
    		}
    	}
    
    

InputStreamReader类

转换流java.io.InputStreamReader,是Reader的子类,是从字节流到字符流的桥梁。它读取字节,并使用指定的字符集将其解码为字符。它的字符集可以由名称指定,也可以接受平台的默认字符集。

构造方法

方法名说明
InputStreamReader(InputStream in)创建一个使用默认字符集的字符流。
InputStreamReader(InputStream in, String charsetName)创建一个指定字符集的字符流。
InputStreamReader isr = new InputStreamReader(new FileInputStream("in.txt"));
InputStreamReader isr2 = new InputStreamReader(new FileInputStream("in.txt") , "GBK");

指定编码读取文件

public class ReaderDemo2 {
	public static void main(String[] args) throws IOException {
		// 创建对象
		// InputStreamReader isr = new InputStreamReader(new FileInputStream(
		// "osw.txt"));

		// InputStreamReader isr = new InputStreamReader(new FileInputStream(
		// "osw.txt"), "GBK");

		InputStreamReader isr = new InputStreamReader(new FileInputStream(
				"osw.txt"), "UTF-8");

		// 读取数据
		// 一次读取一个字符
		int ch = 0;
		while ((ch = isr.read()) != -1) {
			System.out.print((char) ch);
		}

		// 释放资源
		isr.close();
	}

}

OutputStreamWriter类

转换流java.io.OutputStreamWriter ,是Writer的子类,是从字符流到字节流的桥梁。使用指定的字符集将字符编码为字节。它的字符集可以由名称指定,也可以接受平台的默认字符集

构造方法

方法名说明
OutputStreamWriter(OutputStream in)创建一个使用默认字符集的字符输出流。
OutputStreamWriter(OutputStream in, String charsetName)创建一个指定字符集的字符输出流。
OutputStreamWriter isr = new OutputStreamWriter(new FileOutputStream("out.txt"));
OutputStreamWriter isr2 = new OutputStreamWriter(new FileOutputStream("out.txt") , "GBK");

指定编码写出数据

/*
 * OutputStreamWriter(OutputStream out):根据默认编码把字节流的数据转换为字符流
 * OutputStreamWriter(OutputStream out,String charsetName):根据指定编码把字节流数据转换为字符流
 * 把字节流转换为字符流。
 * 字符流 = 字节流 +编码表。
 */
public class OutputDemo {
    public static void main(String[] args) throws IOException {
      	// 定义文件路径
        String FileName = "E:\\out.txt";
      	// 创建流对象,默认UTF8编码
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(FileName));
        // 写出数据
      	osw.write("你好"); // 保存为6个字节
        
        String encoding = osw.getEncoding();
        System.out.println(encoding);//UTF8
        
        osw.close();
      	System.out.println("================");
        
        
		// 定义文件路径
		String FileName2 = "E:\\out2.txt";
     	// 创建流对象,指定GBK编码
        OutputStreamWriter osw2 = new OutputStreamWriter(new FileOutputStream(FileName2),"GBK");
        // 写出数据
      	osw2.write("你好");// 保存为4个字节
        
        String encoding1 = osw2.getEncoding();
        System.out.println(encoding1);//GBK
        
        osw2.close();
    }
}

转换流复制

读写的数据编码一定要保持一致

代码实现

public class TransDemo {
    public static void main(String[] args) throws IOException {

        //编码,你好 转成 字节 utf-8 3个字
        //解码,utf-8 3个字
        //解码,3个字节解,GBK只解到2个字节,所以就出现了乱码问题
        //我们在使用转换流时,一定要注意读和写的字符编码一保持一致

        //读 GBK    占两个字节
        InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"),"GBK");

        //写 ide中的 utf-8   占三个字节
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("b.txt"));

        //复制
        char[] chs = new char[1024];
        int len;
        while ((len = isr.read(chs)) != -1){
            osw.write(chs,0,len);
        }
        isr.close();
        osw.close();

    }
}

流的总结

    public static void main(String[] args) throws IOException {
        //基本字节流
        FileOutputStream fs = new FileOutputStream("a.txt");
        FileInputStream fis = new FileInputStream("a.txt");
        //基本字符流
        FileWriter fw = new FileWriter("a.txt");
        FileReader fr = new FileReader("a.txt");

        //缓冲字节流
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("a.txt"));
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("a.txt"));
        //缓冲字符流
        BufferedWriter bw = new BufferedWriter(new FileWriter("a.txt"));
        BufferedReader br = new BufferedReader(new FileReader("a.txt"));

        //转换输出流  字符流
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("a.txt"),"utf-8");
        //转换输入流  字符流
        InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"));
    }

序列化流

概述

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

反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化对象的数据对象的类型对象中存储的数据信息,都可以用来在内存中创建对象。看图理解序列化: [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UO0kQXzx-1683792916950)(assets/3_xuliehua.jpg)]

ObjectOutputStream类

java.io.ObjectOutputStream 类,将Java对象的原始数据类型写出到文件,实现对象的持久存储。

构造方法

方法名说明
public ObjectOutputStream(OutputStream out)创建一个指定OutputStream的ObjectOutputStream。
FileOutputStream fileOut = new FileOutputStream("employee.txt");
ObjectOutputStream out = new ObjectOutputStream(fileOut);

序列化常用方法

方法名说明
public final void writeObject (Object obj)将指定的对象写出。

序列化

一个对象要想序列化,必须满足两个条件:

  • 该类必须实现java.io.Serializable 接口,Serializable 是一个标记接口,不实现此接口的类将不会使任何状态序列化或反序列化,会抛出NotSerializableException
  • 该类的所有属性必须是可序列化的。如果有一个属性不需要可序列化的,则该属性必须注明是瞬态的,使用transient 关键字修饰。

ObjectInputStream类

ObjectInputStream反序列化流,将之前使用ObjectOutputStream序列化的原始数据恢复为对象。

构造方法

方法名说明
public ObjectInputStream(InputStream in)创建一个指定InputStream的ObjectInputStream。

4.3.2 反序列化常用方法

如果能找到一个对象的class文件,我们可以进行反序列化操作,调用ObjectInputStream读取对象的方法

方法名说明
public final Object readObject ()读取一个对象。

反序列化

  • 注意事项

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

    • 该类的序列版本号与从流中读取的类描述符的版本号不匹配
    • 该类包含未知数据类型
    • 该类没有可访问的无参数构造方法

    Serializable 接口给需要序列化的类,提供了一个序列版本号。serialVersionUID 该版本号的目的在于验证序列化的对象和对应类是否版本匹配。

序列化和反序列化的实现

/**
 * 序列化: 把对象以流的形式进行流化,存储或者在网络中转输        对象 --- 流数据 ObjcetOutputStream
 * 返序列化: 把文本中的数据据以流的形式进行还原成对象            流数据---对象   ObjectInPutStream
 * 
 * 帮助理解:
 *      序列化:将 Java 对象转换成字节流的过程。
 *      反序列化:将字节流转换成 Java 对象的过程。
 */
public static void main(String[] args) throws IOException, ClassNotFoundException {
    // 由于我们要对对象进行序列化,所以我们先自定义一个类
    // 序列化数据其实就是把对象写到文本文件
    write();//序列化

    // read();//反序列化

}

private static void read() throws IOException, ClassNotFoundException {
    // 创建反序列化对象
    ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
            "oos.txt"));

    // 还原对象
    Object obj = ois.readObject();

    // 释放资源
    ois.close();

    // 输出对象
    System.out.println(obj); //Person{name='江一燕', age=27}
}

private static void write() throws IOException {
    // 创建序列化流对象
    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("oos.txt"));

    // 创建对象
    Person p = new Person("江一燕", 27);

    // public final void writeObject(Object obj) 
    oos.writeObject(p); //把对象写入到序列化流中,需要Person实现Serializable接口

    // 释放资源
    oos.close();
}

/**
 * 类通过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。
 * 该接口居然没有任何方法,类似于这种没有方法的接口被称为标记接口。
 */
public class Person implements Serializable {

    private String name;
    private int age;
}

注意事项

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EzS6gKXm-1683792916950)(assets/image-20211109134645535.png)]

注意: 假如我序列化时加了private int age, 我反序列化时改成int age ,这时就会报错,原因是序列化时的序列号 与 反序例化的序列号已经不对称了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R7InI7qz-1683792916950)(assets/image-20211109134634740.png)]

//解决办法,给这个类序列化时,给出一个序列号进行绑定
public class Person implements Serializable {

    private static final long serialVersionUID = 1L;
    
    private String name;
    private int age;

反序列化读取序列化对象的顺序要保持一致

测试成员变量不序列化

transient关键字

/**
 *  注意:
 *     我一个类中可能有很多的成员变量,有些成员变量我不想进行序列化。请问该怎么办呢?
 *        使用transient关键字声明不需要序列化的成员变量
 */
public class Person implements Serializable {

    private static final long serialVersionUID = 1L;

    private String name;
    private transient int age;//transient不会被序例化

Static关键字

/**
 *  注意:
 *     每一个类中可能有很多的成员变量,有些成员变量我不想进行序列化。请问该怎么办呢?
 *        使用static关键字声明不需要序列化的成员变量
 */
public class Person implements Serializable {

    private static final long serialVersionUID = 1L;

    private String name;
    private static int age;// static不会被序例化

· 声明为 static 和 transient 的成员变量,不能被序列化。

· static 成员变量是描述类级别的属性,transient 表示临时数据

测试

先序列化,再反序列化 看到age并没有值

img

序列化集合

案例需求

  1. 将存有多个自定义对象的集合序列化操作,保存到list.txt文件中。
  2. 反序列化list.txt ,并遍历集合,打印对象信息。

案例分析

1. 把若干学生对象 ,保存到集合中。
2. 把集合序列化。
3. 反序列化读取时,只需要读取一次,转换为集合类型。
4. 遍历集合,可以打印所有的学生信息

代码实现

public class SerTest {
    public static void main(String[] args) throws Exception {
        // 创建 学生对象
        Student student = new Student("老王", "laow");
        Student student2 = new Student("老张", "laoz");
        Student student3 = new Student("老李", "laol");

        ArrayList<Student> arrayList = new ArrayList<>();
        arrayList.add(student);
        arrayList.add(student2);
        arrayList.add(student3);
        // 序列化操作
        // serializ(arrayList);

        // 反序列化  
        read();
    }
    
   private static void read() throws IOException, ClassNotFoundException {

        //反序列化
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("list.txt"));

        //进行读取出来
        ArrayList<Student> list = (ArrayList<Student>) ois.readObject();

        for(Student s : list){
            System.out.println(s.getName());
            System.out.println(s.getPwd());
        }
    }

    private static void serializ(ArrayList<Student> arrayList) throws Exception {
        // 创建 序列化流 
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("list.txt"));
        // 写出对象
        oos.writeObject(arrayList);
        // 释放资源
        oos.close();
    }
}

打印流

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

打印流的分类

名称类名
字节打印流PrintStream
字符打印流PrintWriter

打印流的特点

- 只负责输出数据,不负责读取数据
- 永远不会抛出IOException
- 有自己的特有方法

PrintWriter特点

* 自动换行 println()
* 不能输出字节 可以输出字节以外的内容
* 必须是通过配置 自动刷新 (println,printf,format)
	boolean autoFlush: true 自动刷新 false,不自动刷新
* 包装流本身没有写出功能
* 将字节输出流转换字符输出流

PrintWriter构造方法

方法名说明
PrintWriter(String fileName)使用指定的文件名创建一个新的PrintWriter,而不需要自动执行刷新
PrintWriter(Writer out, boolean autoFlush)创建一个新的PrintWriter out:字符输出流 autoFlush: 一个布尔值,如果为真,则println , printf ,或format方法将刷新输出缓冲区

PrintWriter特有方法

方法名说明
void write(String s)使用指定的文件名创建一个新的PrintWriter,而不需要自动执行刷新
void print(String s)输出字符串, 没有换行
void println(String s)输出字符串并换行. 如果启动了自动刷新, 则会执行自动刷新写入数据
void printf(Locale l, String format, Object… args)使用指定格式字符串和参数将格式化的字符串写入输出流. 如果启动了自动刷新, 则会执行自动刷新写入数据
void format(Locale l, String format, Object… args)使用指定格式字符串和参数将格式化的字符串写入输出流. 如果启动了自动刷新, 则会执行自动刷新写入数据

字符打印流特点

/*
 * 打印流
 * 字节打印流   PrintStream
 * 字符打印流   PrintWriter
 *
 * 打印流的特点:
 *        A:只有写数据的,没有读取数据。只能操作目的地,不能操作数据源。
 *        B:可以操作任意类型的数据。
 *        C:如果启动了自动刷新,能够自动刷新。
 *        D:该流是可以直接操作文本文件的。
 *           哪些流对象是可以直接操作文本文件的呢?
 *           FileInputStream
 *           FileOutputStream
 *           FileReader
 *           FileWriter
 *           PrintStream
 *           PrintWriter
 *           看API,查流对象的构造方法,如果同时有File类型和String类型的参数,一般来说就是可以直接操作文件的。
 *
 */

public static void main(String[] args) throws IOException {
    //字符打印流对象
    PrintWriter pw = new PrintWriter("pw.txt");

    //A:只有写数据的,没有读取数据。只能操作目的地,不能操作数据源。
    pw.write("hello");
    pw.write("world");
    pw.write("java");
    //为什么没有写进去呢?
    //字符流并不会像字节流一样直接操作数据,字符流会先存入缓存区中,并没有直接写入文本中,需要刷新

    //close相当于启动了自动刷新,能够自动刷新
    pw.close();
}


字符 打印流自动刷新
/*
 * 1:可以操作任意类型的数据。
 *        print()
 *        println()
 * 2:启动自动刷新: PrintWriter pw = new PrintWriter(new FileWriter("pw2.txt"), true);
 *    调用println()才可以自动刷新
	其实等价于于:
 *            bw.write();
 *            bw.newLine();
 *            bw.flush();
 */
public static void main(String[] args) throws IOException {

    // 创建打印流对象,启动自动刷新:PrintWriter pw = new PrintWriter(new FileWriter("pw2.txt"), true);
    PrintWriter pw = new PrintWriter(new FileWriter("pw2.txt"), true);
    //可以操作任意类型的数据。
	// print()这个方法不会自动刷新
    pw.print(true);
    pw.print(100);
    pw.print("hello");
    pw.close();

    // 创建打印流对象
    PrintWriter pw2 = new PrintWriter(new FileWriter("pw3.txt"), true);
    //println() 这个方法不仅仅自动刷新了,还实现了数据的换行。
    pw2.println("world");
    pw2.println(false);
    pw2.println(200);
    //pw2.close();
}

字节打印流

    public static void main(String[] args) throws IOException {

        //创建字节打印流对象
        PrintStream ps = new PrintStream(new FileOutputStream("c.txt"));

        ps.write(97);
        ps.write("hello".getBytes());

        ps.print("hello");
        ps.print(1314);
        ps.print(true);

        ps.println("hello");
        ps.println("1314");
        ps.println(true);

        //关闭流
        // ps.close();
    }

标准输入输出流

获取标准输出流PrintStream

/*
 * 标准输入输出流
 * System类中的两个成员变量:
 *        public static final InputStream in “标准”输入流。
 *        public static final PrintStream out “标准”输出流。
 *
 *        InputStream is = System.in;
 *        PrintStream ps = System.out;
 */
public static void main(String[] args) throws IOException {

    // 有这里的讲解我们就知道了,这个输出语句其本质是IO流操作,把数据输出到控制台。
    System.out.println("helloworld");

    // 获取标准输出流对象
    PrintStream ps = System.out;
    ps.println("helloworld");

    ps.println();
    // ps.print();//空参方法不存在

    System.out.println();
    // System.out.print();//所以空参不能使用
}

获取标准输入流InputStream

/*
 * System.in 标准输入流。是从键盘获取数据的
 *
 * 键盘录入数据:
 *        A:main方法的args接收参数。
 			java hello.java
 *           java Hello hello world java
 *        B:Scanner
 *           Scanner sc = new Scanner(System.in);
 *           String s = sc.nextLine();
 *           int x = sc.nextInt()
 *        C:通过字符缓冲流包装标准输入流实现
 *           BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
 */
public static void main(String[] args) throws IOException {

    //获取标准输入流
    // InputStream is = System.in;
    // BufferedReader br = new BufferedReader(new InputStreamReader(is));

    //通过字符缓冲流包装标准输入流实现,但只能读字符串,因为有scnner对象,提供了很多方法
    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    System.out.println("请输入一个字符串:");
    String line = br.readLine();
    System.out.println("你输入的字符串是:" + line);

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


Properties集合

Properties集合概述

- 是一个Map体系的集合类
- Properties可以保存到流中或从流中加载
- 属性列表中的每个键及其对应的值都是一个字符串

Properties基本使用

public class PropertiesDemo01 {
    public static void main(String[] args) {
        //创建集合对象
//        Properties<String,String> prop = new Properties<String,String>(); //错误
        Properties prop = new Properties();

        //存储元素
        prop.put("itfxp001", "林青霞");
        prop.put("itfxp002", "张曼玉");
        prop.put("itfxp003", "王祖贤");

        //遍历集合
        Set<Object> keySet = prop.keySet();
        for (Object key : keySet) {
            Object value = prop.get(key);
            System.out.println(key + "," + value);
        }
    }
}

Properties集合的特有方法

方法名说明
Object setProperty(String key, String value)设置集合的键和值,都是String类型,底层调用 Hashtable方法 put
String getProperty(String key)使用此属性列表中指定的键搜索属性
Set stringPropertyNames()从该属性列表中返回一个不可修改的键集,其中键及其对应的值是字符串

代码演示

/*
 * 特殊功能:
 * public Object setProperty(String key,String value):添加元素
 * public String getProperty(String key):获取元素
 * public Set<String> stringPropertyNames():获取所有的键的集合
 */
public static void main(String[] args) throws IOException, ClassNotFoundException {
    // 创建集合对象
    Properties prop = new Properties();

    // 添加元素
    prop.setProperty("张三", "30");
    prop.setProperty("李四", "40");
    prop.setProperty("王五", "50");

    // public Set<String> stringPropertyNames():获取所有的键的集合
    Set<String> set = prop.stringPropertyNames();
    for (String key : set) {
        String value = prop.getProperty(key);//获取元素(value)
        System.out.println(key + "---" + value);
    }
}

Properties和IO流相结合的方法

方法名说明
void load(InputStream inStream)从输入字节流读取属性列表(键和元素对)
void load(Reader reader)从输入字符流读取属性列表(键和元素对)
void store(OutputStream out, String comments)将此属性列表(键和元素对)写入此 Properties表中,以适合于使用 load(InputStream)方法的格式写入输出字节流
void store(Writer writer, String comments)将此属性列表(键和元素对)写入此 Properties表中,以适合使用 load(Reader)方法的格式写入输出字符流

代码示例

/*
 * 这里的集合必须是Properties集合:
 * public void load(Reader reader):把文件中的数据读取到集合中
 * public void store(Writer writer,String comments):把集合中的数据存储到文件
 */
public static void main(String[] args) throws IOException, ClassNotFoundException {

    // myLoad();
    myStore();
}

//读数据
private static void myStore() throws IOException {
    // 创建集合对象
    Properties prop = new Properties();

    prop.setProperty("江一燕", "27");
    prop.setProperty("jack", "30");
    prop.setProperty("肉丝", "18");

    //public void store(Writer writer,String comments) 把集合中的数据存储到文件
    FileWriter w = new FileWriter("name.properties");
    //写
    prop.store(w, "写入文件的说明:因为我要学习所有我要写入");//comments:信息的描述
    w.close();
}

//写数据
private static void myLoad() throws IOException {

    Properties prop = new Properties();
    // public void load(Reader reader) 把文件中的数据读取到集合中
    // 注意:这个文件的数据必须是键值对形式
    FileReader r = new FileReader("name.properties");
    //读
    prop.load(r);
    r.close();		
    System.out.println("prop:" + prop);
}

ents) | 将此属性列表(键和元素对)写入此 Properties表中,以适合于使用 load(InputStream)方法的格式写入输出字节流 |
| void store(Writer writer, String comments) | 将此属性列表(键和元素对)写入此 Properties表中,以适合使用 load(Reader)方法的格式写入输出字符流 |

代码示例

/*
 * 这里的集合必须是Properties集合:
 * public void load(Reader reader):把文件中的数据读取到集合中
 * public void store(Writer writer,String comments):把集合中的数据存储到文件
 */
public static void main(String[] args) throws IOException, ClassNotFoundException {

    // myLoad();
    myStore();
}

//读数据
private static void myStore() throws IOException {
    // 创建集合对象
    Properties prop = new Properties();

    prop.setProperty("江一燕", "27");
    prop.setProperty("jack", "30");
    prop.setProperty("肉丝", "18");

    //public void store(Writer writer,String comments) 把集合中的数据存储到文件
    FileWriter w = new FileWriter("name.properties");
    //写
    prop.store(w, "写入文件的说明:因为我要学习所有我要写入");//comments:信息的描述
    w.close();
}

//写数据
private static void myLoad() throws IOException {

    Properties prop = new Properties();
    // public void load(Reader reader) 把文件中的数据读取到集合中
    // 注意:这个文件的数据必须是键值对形式
    FileReader r = new FileReader("name.properties");
    //读
    prop.load(r);
    r.close();		
    System.out.println("prop:" + prop);
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值