【Java基础教程】(四十六)IO篇 · 下:System类对IO的支持:错误输出、信息输出、系统输入,字符缓冲流、扫描流和对象序列化流~

Java基础教程之IO操作 · 下

  • 🔹本节学习目标
  • 1️⃣ System类对 IO 的支持
    • 1.1 错误输出:System.err
    • 1.2 信息输出:System.out
    • 1.3 系统输入:System. in
  • 2️⃣ 字符缓冲流:BufferedReader
  • 3️⃣ 扫描流:Scanner
  • 4️⃣ 对象序列化
    • 4.1 序列化接口:Serializable
    • 4.2 实现序列化与反序列化
    • 4.3 transient 关键字
  • 🌾 总结

在这里插入图片描述

🔹本节学习目标

  • 了解System类对 IO 的支持:错误输出(System.err)、信息输出(System.out)和系统输入(System.in);
  • 掌握缓冲字符流、打印流及扫描流的使用;
  • 掌握对象序列化流的应用场景及使用,掌握接口(Serializable)及transient关键字的作用;

1️⃣ System类对 IO 的支持

System 类是现在为止使用最多的一个类,所有的信息输出都会使用到 “System.out.println()”或 “System.out.print()”两个方法完成。实际上 System 类中也专门提供了与 I0 有关的3个对象常量,如下所示。

常量类型描述
public static final PrintStream err常量显示器上错误输出
public static final PrintStream out常量显示器上信息输出
public static final InputStream in常量键盘数据输入

可以发现, errout 两个对象的类型都属于 PrintStream 类型, in 对象类型属于 InputStream, 而最早使用的"System.out.println()" 代码从本质上来讲就是调用了 System 类中的out 常量,由于此常量类型为 PrintStream, 所以可以继续调用 PrintStream 类中的 print()println()方法,也就证明 Java 的任何输出操作实际上都是 I0操作。

1.1 错误输出:System.err

System.errPrintStream 类对象,此对象专门负责进行错误信息的输出操作。

//	范例 1: 错误输出
package com.xiaoshan.demo;public class TestDemo  {public static void main(String[] args) throws Exception {try{Integer.parseInt("abc");	//此处一定会发生异常} catch (Exception e){System.err.println(e);	//错误输出}}
}

程序执行结果:

java.lang.NumberFormatException: For input string: "abc"

本程序试图将字符串 “abc” 转换为 int 型数据,而由于字符串不是由数字所组成,所以此处一定会产生异常,而本程序在异常处理中利用System.err 实现了错误信息的打印。

从实际的效果来看,如果本程序中使用了System.out, 其信息内容是完全一样的, 那么为什么非要用System.err而不使用System.out呢?

System.errSystem.out都属于PrintStream类型的对象,所以两者的功能肯定完全相同,但是在Java最初设计时,希望某些错误信息不被使用者看见,所以就定义了 System.err,而System.errSystem.out默认情况下都是在屏幕上输出(可以使用 System类中的"setErr(PrintStream err)"或"setOut(PrintStream err)"改变输出位置,但一般不会改变out的输出位置),所以区别就不这么大了。

另外如果是在IDE工具中显示,会发现System.err输出错误信息使用的是红色字体, 而System.out输出错误信息使用的是黑色字体。

1.2 信息输出:System.out

Sysetm.out 是进行屏幕输出的操作对象,是一个 PrintStream 类的实例化对象,并且由JVM 负责该对象的实例化,由于 PrintStream 也是 OutputStream 的子 类,所以可以利用 System.outOutputStream 类进行实例化。

从实际上讲,下边范例的代码意义并不大,因为 OutputStreamPrintStream 相比,明显输出时使用 PrintStream 更加方便 (因为它提供了各种 print()println()方法), 而本程序的意义只是在于验证不同的子类为 OutputStream 的位置也不同这一对象多态性的概念应用。

//	范例 2: 利用 OutputStream 实现屏幕输出
package com.xiaoshan.demo;import java.io.OutputStream;public class TestDemo{public static void main(String[] args) throws Exception{// OutputStream 就为屏幕输出OutputStream out = System.out;out.write("https://lst66.blog.csdn.net".getBytes());}
}

程序执行结果:

https://lst66.blog.csdn.net

由于System.out 属于PrintStream 类的实例,所以可以直接利用其为 OutputStream 对象实例化, 此时调用 write() 方法输出时就变为了屏幕显示。

在JDK 1.8提供的函数式接口里面,有一个消费型的函数式接口,此接口中的方法不返回结果,但是会接收参数。此时就可以利用方法引用的概念,利用System.out.println() 实现 Consumer 接口的实例化。

//	范例 3: 消费型函数式接口与方法引用
package com.xiaoshan.demo;import java.util.function.Consumer;public class TestDemo {public static void main(String[] args) throws Exception{Consumer<String> con = System.out::println;       //方法引用con.accept("更多文章请访问:https://lst66.blog.csdn.net"); 	//   输   出}
}

程序执行结果:

更多文章请访问:https://lst66.blog.csdn.net

程序采用方法引用的概念,将 System.out 对象中的 println() 方法引用到 Consumer 接口中,而后利用accept()方法就可以实现屏幕信息输出。

1.3 系统输入:System. in

在许多编程语言中为了方便用户的交互操作,都会直接提供一种键盘输入数据的操作功能,但是在 Java 中并没有提供这样可以直接使用的键盘输入操作,而要想实现此类操作必须采用 IO 处理的形式完成,操作的核心就是利用 System.in ( 此为 InputStream 类实例化对象)完成。

//	范例 4: 实现键盘的数据输入
package com.xiaoshan.demo;import java.io.InputStream;public class TestDemo  {public static void main(String[] args) throws Exception{//此处直接抛出//为了方便读者理解,本处将System.in使用InputStream接收,但实际上不需要此操作InputStream input = System.in;	 //System.in为InputStream类实例//开辟空间接收数据//信息提示,此处没有换行//读取数据并返回长度byte data[] = new byte[1024];System.out.print("请输入数据:");int len = input.read(data);System.out.println("输入数据为:"+ new String(data, O, len));}
}

程序执行结果:

请输入数据:更多文章请访问:https://lst66.blog.csdn.net
输入数据为:更多文章请访问:https://lst66.blog.csdn.net

本程序利用 System.in 实现了键盘数据的读取操作,与文件读取不同的地方在于,本程序的文件内容已经是固定好的,所以读取时会直接进行加载 (如下图(a) 所示)。通过键盘读取时,由于内容还未输入,所以会出现一个等待用户输入的操作界面,用户输入完成按回车后才会开始读取数据(如下图(b) 所示)。

图1 文件与键盘输入

虽然上边范例的代码实现了键盘输入的操作功能,但是此时就会出现一个问题:本处读取的数据为1024个字节,如果读取的内容超过了1024个字节,就会出现漏读的问题 ,也即超过数组长度的部分不会被接收。

在上边范例程序中,只是读取了一次,所以当出现内容超过预定义数组长度后就会出现漏读的问题。但是也可以利用内存流将其进行改进,例如:可以将所有读取的数据暂时保存在内存输出流中,并且ByteArraryOutputStream类中提供了 “toByteArray()” 方法可以取得全部的字节数据,这样结合循环读取就可以将内容全部读取。此处提供一种思路,不再编写复杂代码演示, 有兴趣的朋友可以自行实现。

而除了上述这种方式,要想解决此时的漏读问题,也可以不使用数组接收,而是采用循环的方式进行读取,并且将每次读取进来的数据利用 StringBuffer 类对象保存。

//	范例 5: 改进输入操作设计
package com.xiaoshan.demo;import java.io.InputStream;public class TestDemo{public static void main(String[] args) throws Exception{InputStream input = System.in;StringBuffer buf = new StringBuffer(); 	//接收输入数据System.out.print("请输入数据:");			//提示信息int temp = 0;							//接收每次读取数据长度while((temp = input.read()) != -1){		//判断是否有输入数据if (temp == '\n'){ 	//判断是否为回车符break;			//停止接收}buf.append((char) temp);	//保存读取数据}System.out.println("输入数据为:" + buf);	//输出内容}
}

程序执行结果(输入英文时):

请输入数据:www.baidu.com
输入数据为:www.baidu.com

程序执行结果(输入中文):

请输入数据:我是小山
输入数据为:我是小山

此程序由于使用了 StringBuffer 类对象保存数据,所以没有数据长度的限制,但是此时的代码在输入英文数据时没有任何问题,而输入中文数据时却会出现乱码。造成乱码的原因也很简单,这是一个中文的编码拆分成了两半(每次读取一个字节) 而造成的编码出错。要想解决此类问题,就可以利用字符缓冲输入流完成。

2️⃣ 字符缓冲流:BufferedReader

为了可以进行完整数据的输入操作,最好的做法是采用缓冲区的方式对输入的数据进行暂存,而后程序可以利用输入流一次性读取内容,如下图所示,这样就可以避免输入中文时的读取错乱问题。

图2 缓冲区操作

如果要使用缓冲区进行数据操作,java.io 包提供了以下两种操作流。

  • 字符缓冲区流: BufferedReaderBufferedWriter;
  • 字节缓冲区流: BufferedInputStreamBufferedOutputStream

以上给出的4个操作类中,最为重要的就是BufferedReader 类,此类是Reader 的子类,属于字符缓冲输入流,而如果要处理中文数据,字符流是最方便的。 BufferedReader 类的常用方法如下所示。

方法类型描述
public BufferedReader(Reader in)构造方法设置字符输入流
public String readLine() throws IOException普通方法读取一行数据,默认以“\n”为分隔符

如果要使用 BufferedReader 类来处理 System.in 的操作就会出现一个问题, BufferedReaderReader 的子类,并且构造方法中也要接收 Reader 类型,而 System.inInputStream 类型,所以此处必须将 InputStream 类型转换为 Reader 类型,那么就可以利用 InputStreamReader 类来实现这一转换操作。字节输入流转字符缓冲输入流结构如下图所示。

图3 字节输入流转字符缓冲输入流结构

//	范例 6: 键盘数据输入的标准格式
package com.xiaoshan.demo;import java.io.BufferedReader;
import java.io.InputStreamReader;public class TestDemo  {public static void main(String[] args) throws Exception{//System.in 是InputStream 类对象,BufferedReader 的构造方法里面需要接收 Reader 类对象//利用InputStreamReader 将字节流对象变为字符流对象BufferedReader buf = new BufferedReader(new InputStreamReader(System.in));System.out.print("请输入数据:");String str = buf.readLine();         //以回车作为换行System.out.println("输入的内容:"+str);}
}

程序执行结果:

请输入数据:小山code
输入的内容:小山code

此程序将 System.in 字节输入流对象转换为了 BufferedReader 类对象,可以利用 readLine() 方法直接读取键盘输入的数据,并且可以很好地进行中文处理。

那么既然System.in属于 InputStream类型对象,并且对于字节流也提供了一个 BufferedInputStream 的字节缓冲输入流对象,为什么不使用这个类,而非要使用 BufferedReader类呢?

原因是字符流方便处理中文,并且支持String返回。打开 BufferedInputStream 类的方法可以发现,在该类中并没有类似 readLine() 功能的方法,也就是说只有 BufferedReader 类可以直接将一行输入数据以字符串的形式返回,这样用户可以进行更多操作,例如:数据类型转换、正则验证、字符串 操作等。

//	范例 7: 判断输入内容
package com.xiaoshan.demo;import java.io.BufferedReader;
import java.io.InputStreamReader;public class TestDemo {public static void main(String[] args)throws Exception{BufferedReader buf = new BufferedReader(new InputStreamReader(System.in));boolean flag = true;						//编写一个循环的逻辑while (flag){								//判断标志位System.out.print("请输入年龄:");			//提示信息String str = buf.readLine();				//以回车作为换行if (str.matches("\\d{1,3}")){			//输入数据由数字组成System.out.println("年龄是:"+ Integer.parseInt(str));flag = false;	//退出循环}else {System.out.println("年龄输入错误,应该由数字所组成。");}}}
}

程序执行结果:

请输入年龄:https://lst66.blog.csdn.net/
年龄输入错误,应该由数字所组成。
请输入年龄:27
年龄是:27

此程序实现了一个简单的逻辑,要求用户在输入年龄时必须输入数字,由于BufferedReader读取数据时使用的readLine()方法返回的是String类型,所以可以利用正则进行判断。当判断通过时会利用包装类将字符串转换为 int型数据,同时会退出while循环;当判断失败时会提示用户错误信息,并且等待用户重新输入。本程序流程如下图所示。

图4 程序执行流程

这个程序实际上是所有实际项目开发的一个缩影,在程序中由于输入数据的返回类型是 String , 就方便利用正则进行判断 ,或者进行数据类型的转换。所以只要是输入数据一般都会采用 String类型,像在 JSP 技术中接收表单参数使用的 request.getParameter() 方法实际上返回的也是 String 类型的对象 。

字符缓冲流除了可以接收输入信息外,也可以利用缓冲区进行文件的读取,此时只需要在实例化
BufferedReader 类对象时传递 FileReader 类(实际上也可以使用 FileInputStream, 但是依然需要 InputStreamReader 转换)。

//	范例 8: 读取文件
package com.xiaoshan.demo;import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;public class TestDemo {public static void main(String[] args) throws Exception{File file = new File("D:" + File.separator + "xiaoshan.txt");//使用文件输入流实例化 BufferedReader类对象BufferedReader buf = new BufferedReader(new FileReader(file));String str = null;		//接收输入数据//读取数据并判断是否存在 //输出读取内容while ((str = buf.readLine()) != null){System.out.println(str);}buf.close();}
}

运行结果:

姓名:小山, 年龄:19, 成绩:59.96

此程序使用 BufferedReader 读取输入的数据信息,以“\n”作为读取数据的分隔符,并在读取时采用循环的模式,将每一行数据读取后直接进行输出,利用这样的处理方式要比直接使用 InputStream 读取更加简单。

3️⃣ 扫描流:Scanner

从 JDK 1.5 开始增加了一个 java.util.Scanner 的程序类,利用这个类可以方便地实现数据的输入操作。 Scanner 类并没有定义在 java.io 包中,而是定义在了 java.util 包中,所以此类是一个工具类,此类的继承结构如下图所示。

图5 Scanner 类的继承结构

在JDK 1.5之前如果要进行数据的输入操作,使用 java.io.BufferedReader 类是最方便的,但是 BufferedReader 类会存在以下两个问题。
(1)它读取数据时只能按照字符串返回:public String readLine() throws IOException
(2)所有的分隔符都是固定的。

为此,从JDK 1.5开始增加了新的Scanner类,而Scanner类增加后对于键盘数据输入的实现也会变得更加简单,这一点大家可以观察到。

Scanner类的定义中可以发现其 实现了Iterator接口,它在整个Java开发中是一个重要的接口,在 Iterator接口里面主要定义了两个抽象方法:hasNext() (判断是否有数据)、next()(取得数据),同时这两个方法也会出现在Scanner类中,并且Scanner类提供了具体数据类型的判断与取得操作。

通过Scanner 类的继承关系可以发现, Scanner 不仅实现了 Iterator (迭代)接口与 Closeable 接口,而且
Scanner 类的构造方法还可以接收 InputStreamFile 等类型以实现输入流操作。Scanner 类中定义的常用方法如下所示。

方法类型描述
public Scanner(InputStream source)构造接收InputStream输入流对象,此为输入源
public boolean hasNext()普通判断是否有数据输入
public String next()普通取出输入数据,以String形式返回
public boolean hasNextXxx()普通判断是否有指定类型数据存在
public 数据类型 nextXxx()普通取出指定数据类型的数据
public Scanner useDelimiter(String pattern)普通设置读取的分隔符
//	范例 9: 利用 Scanner 实现键盘数据输入
package com.xiaoshan.demo;import java.util.Scanner;public class TestDemo {public static void main(String[] args) throws Exception{Scanner scan = new Scanner(System.in);		//准备接收键盘输入数据System.out.print("请输入内容:");	//提示信息if (scan.hasNext()){	//是否有输入数据System.out.println("输入内容:"+ scan.next());	//存在内容则输出}scan.close();}
}

程序执行结果:

请输入内容:小山的博客,https://lst66.blog.csdn.net
输入内容:小山的博客,https://lst66.blog.csdn.net

程序实现了键盘输入数据的操作,通过对比可以发现,利用 Scanner 实现的键盘输入数据操作代码要比 BufferedReader 更加简单。

实际上上边范例的代码属于Scanner使用的标准形式,即先使用 hasNextXxx() 进行判断,有数据之后再进行输入。对于字符串的操作中是否有 hasNextXxx() 方法判断意义不大 (可以直接使用 next(), 但是这样做不标准), 因为即使此时代码不输入任何字符串也表示接收(因为不为NULL, 是一个空字符串), 但是如果是具体的数据类型输入就有意义了。

//	范例 10: 输入一个数字—— double
package com.xiaoshan.demo;import java.util.Scanner;public class TestDemo  {public static void main(String[] args) throws Exception{Scanner scan = new Scanner(System.in);	//准备接收键盘输入数据System.out.print("请输入成绩:");if(scan.hasNextDouble()){	//表示输入的是一个小数double score = scan.nextDouble();	//省略了转型System.out.println("输入内容:"+ score);}else{						//表示输入的不是一个小数System.out.println("输入的不是数字,错误!");}scan.close();}
}

程序执行结果(输入错误):

请输入成绩:xiaoshan
输入的不是数字,错误!

程序执行结果(输入正确):

请输入成绩:100.0
输入内容:100.0

本程序在输入数据时使用了 Scanner 类的 hasNextDouble() 方法来判断输入的数据是否是 double 数值,如果是则利用 nextDouble() 直接将输入的字符串转化为 double 型数据后返回。

Scanner 类中除了支持各种常用的数据类型外,也可以在输入数据时使用正则表达式来进行格式验证。

//	范例 11: 正则验证
package com.xiaoshan.demo;import java.util.Scanner;public class TestDemo{public static void main(String[] args) throws Exception  {Scanner scan = new Scanner(System.in); 	//准备接收键盘输入数据System.out.print("请输入生日:");		//提示文字if (scan.hasNext("\\d{4}-\\d{2}-\\d{2}")){		//正则验证String bir = scan.next("\\d{4}-\\d{2}-\d{2}");		//接收数据System.out.println("输入内容:"+ bir);}else{								//数据格式错误System.out println("输入的生日格式错误!");}scan.close();}
}

程序执行结果:

请输入生日:1997-12-15
输入内容:1997-12-15

此程序利用正则验证了输入的字符串数据,如果数据符合正则规则,则进行接收,如果不符合则提示信息错误。
Scanner 类的构造里面由于接收的类型是 InputStream, 所以依然可以设置一个文件的数据流。

考虑到文件本身会存在多行内容,所以需要考虑读取的分隔符问题(默认是空字符为分隔符,例如:空格或换行), 这样在读取前就必须使用 “useDelimiter()” 方法来设置分隔符。

//	范例 12: 读取文件
package com.xiaoshan.demo;import java.io.File;
import java.io.FilelnputStream;
import java.util.Scanner;public class TestDemo  {public static void main(String[] args) throws Exception{Scanner scan = new Scanner(new FilelnputStream(new File("D:" + File.separator + "xiaoshan.txt")));                         //设置读取的文件输入流scan.useDelimiter("\n");         	//设置读取的分隔符while (scan.hasNext()){     //循环读取       System.out.println(scan.next());   	//直接输出读取数据}scan.close();}
}

此程序利用 Scanner 实现了文件数据的读取操作,在读取前可以使用 useDelimiter() 方法设置指定的读取分隔符,随后就可以利用 while 循环来读取数据。

通过一系列分析,大家应该已经清楚了 InputStreamOutputStream的不足,同时也应该发现利用 PrintStream(或PrintWriter) 可以加强程序输出数据的操作支持, Scanner(或 BufferedReader) 可以加强程序输入数据的操作支持。所以在日后的开发 中,只要操作的是文本数据 (不是二进制数据), 输出时都使用打印流,输入时都使用扫描流(或者使用字符缓冲区输入流)。

4️⃣ 对象序列化

Java 允许用户在程序运行中进行对象的创建,但是这些创建的对象都只保存在内存中,所以这些对象的生命周期都不会超过 JVM 进程。但是在很多时候可能需要在 JVM 进程结束后对象依然可以被保存下来,或者在不同的 JVM 进程中要进行对象传输,那么在这样的情况下就可以采用对象序列化的方式进行处理。

4.1 序列化接口:Serializable

对象序列化的本质实际上就是将内存中所保存的对象数据转换为二进制数据流进行传输的操作。但 是并不是所有类的对象都可以直接进行序列化操作,要被序列化的对象所在的类一定要实现 java.io.Serializable 接口。而通过文档观察可以发现,序列化接口里面并没有任何操作方法存在,因为它是一个标识接口,表示一种能力。

//	范例 13: 定义一个可以被序列化对象的类
package com.xiaoshan.demo;import java.io.Serializable;@SuppressWarnings("serial")		//压制序列化版本号警告信息 
class Book implements Serializable{		//此类对象可以被序列化private String title;private double price;public Book(String title, double price){this.title = title;this.price = price;}@Overridepublic String toString(){return "书名:"+ this.title + ",价格:" + this.price;}
}

4.2 实现序列化与反序列化

实现了Serializable 接口后并不意味着对象可以实现序列化操作。实际上在对象序列化与反序列化的操作中,还需要以下两个类的支持。

  • 序列化操作类: java.io.ObjectOutputStream,将对象序列化为指定格式的二进制数据;
  • 反序列化操作类: java.io.ObjectInputStream,将序列化的二进制对象信息转换回对象内容。

ObjectOutputStream类的常用方法如下所示:

方法类型描述
public ObjectOutputStream(OutputStream out) throws IOException构造指定对象序列化的输出流
public final void writeObject(Object obj) throws IOException普通序列化对象

ObjectInputStream类的常用方法如下所示:

方法类型描述
public ObjectInputStream(InputStream in) throws IOException构造指定对象反序列化的输入流
public final Object readObject() throws IOException,ClassNotFoundException普通从输入流中读取对象

通过 ObjectOutputStreamObjectInputStream 类的方法定义可以发现,序列化对象时
(writeObject()) 接收的参数统一为 Object, 而反序列化对象时 (readObject()) 返回的类型也为 Object, 所以这两个类可以序列化或反序列化Java 中的所有数据类型(Object 可以接收所有引用类型, 将基本类型自动装箱为包装类后接收)。

//	范例 14: 实现序列化对象操作——ObjectOutputStream
package com.xiaoshan.demo;import java.io.File;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;public class TestDemo {public static void main(String[] args) throws Exception {ser();}public static void ser() throws Exception {ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream( new File("D:" + File.separator + "book.ser")));oos.writeObject(new Book("Java开发实战经典",79.8));//序列化对象oos.close();}
}

此程序实现了 Book 类对象序列化的操作,在实例化 ObjectOutputStream 类对象时需要设置一个 OutputStream 类对象,此时设置的为 FileOutputStream 子类,表示对象将被序列化到文件中。book.ser 文件保存内容如图所示。

在这里插入图片描述

下面演示反序列化操作。

//	范例 15: 实现反序列化操作——ObjectInputStream
package com.xiaoshan.demo;import java.io.File;
import java.io.FileInputStream;
import java.io.ObjectInputStream;public class TestDemo {public static void main(String[] args) throws Exception {dser();}public static void dser() throws Exception {ObjectInputStream ois = new ObjectInputStream(new FilelnputStream(new File("D:"+  File.separator + "book.ser")));Object obj= ois.readObject();    	//反序列化对象Book book =(Book) obj;            	//转型System.out.println(book);ois.close();}
}	

程序执行结果:

书名:Java开发实战经典,价格:79.8

此程序首先利用 ObjectInputStream 类通过指定 InputStream 子类对象确定对象读取的输入流为文件,然后利用 readObject() 方法可以将被序列化的对象反序列化回来。由于返回的对象类型都是 Object, 所以如果有需要可以利用向下转型的操作,将返回对象转化为具体的子类类型。

4.3 transient 关键字

Java 中对象最有意义的内容就是对象的属性信息,所以在默认情况下,如果要进行对象的序列化操作,所序列化的一定是对象的属性信息,并且该对象中的所有属性信息都将被序列化。如果某些属性的内容不需要被保存,就可以通过 transient 关键字来定义。

//	范例 16: 定义不需要序列化的属性
package com.xiaoshan.demo;import java.io.Serializable;@SuppressWarnings("'serial")
class Book implements Serializable { 	//此类对象可以被序列化 private transient String title;		//此属性无法被序列化private double price;public Book(String title, double price){this.title = title;this.price = price;}@Overridepublic String toString(){return "书名:"+this.title+",价格:"+ this.price;}
}

此程序中 Book 类的 title 属性上使用了 transient 关键字进行定义,这样当进行对象序列化操作时, 此属性的内容将不能被保存。

既然 java.io.Serializable 接口中没有定义任何抽象方法,那么是不是意味着,开发中所有的类都实现 Serializable接口会比较好呢?

在实际的开发中,并不是所有的类都要求去实现序列化接口,只有需要传输对象所在的类时才需要实现Serializable接口,而这样的类最主要的就是简单Java类。由于简单Java 类的实际定义与数据表结构相似,所以在很多情况下,很少会使用 transient关键字。

🌾 总结

在本文中,我们详细探讨了Java中System类对IO操作的支持,以及字符缓冲流、扫描流和对象序列化流的应用。

首先,我们了解到System类提供了三个重要的I/O流:错误输出(System.err)、信息输出(System.out)和系统输入(System.in)。通过这些流,我们可以方便地处理错误信息、输出结果和用户输入。

接下来,我们研究了字符缓冲流(BufferedReader),它提供了高效读取文本数据的能力。通过使用缓冲机制,字符缓冲流可以减少磁盘或网络读取的次数,从而提高性能。

我们还讨论了扫描流(Scanner),它提供了一种简便的方式来解析基本类型和字符串。通过Scanner类,我们可以方便地从输入源(如键盘或文件)中读取数据,并以各种类型的形式进行处理。

最后,我们探索了对象序列化流,它提供了一种将对象转换为字节流并写入到文件中的机制。我们介绍了序列化接口(Serializable)的作用以及如何实现对象的序列化与反序列化。同时,我们了解到transient关键字的用途,它可以让对象的某些字段在序列化过程中被忽略。

通过掌握System类对IO的支持以及字符缓冲流、扫描流和对象序列化流的应用,我们可以更有效地处理输入输出操作,并实现数据的读取、写入和持久化。这些技术在日常开发中非常实用,特别是在涉及文本处理、数据交互和对象存储等方面。


温习回顾上一篇(点击跳转)
《【Java基础教程】(四十五)IO篇 · 中:转换流、内存流和打印流(探索装饰设计模式与PrintStream类的进阶),文件操作案例实践、字符编码问题~》

继续阅读下一篇(点击跳转)
《【Java基础教程】(四十七)网络编程篇:》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/7453.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

两个数组的dp问题(2)--动态规划

一)交错字符串: 97. 交错字符串 - 力扣&#xff08;LeetCode&#xff09; 一)确定一个状态标识: 如果我选择s1的一段区间&#xff0c;再进行选择s2得一段区间那么s3这个字符串的长度就已经固定了 预处理:在s1字符串s2字符串和s3字符串前面加上一个虚拟字符&#xff0c;让下标从…

【MyBatis-Plus 进阶学习笔记】

MyBatis-Plus 进阶学习笔记记录 一、 MyBatis Plus 七大功能0. 数据准备1. 逻辑删除2. 自动填充2.1 优化1 自动填充 有的类没有更新和创建时间字段2.2 优化2 自己设置时间时填充自己设置的&#xff0c;不设置时自动填充 3. 乐观锁插件 注&#xff1a;wrapper不能服用4. 性能分析…

网安高级笔记1

html实体编码 HTML实体编码&#xff0c;格式 以&符号开头&#xff0c;以;分号结尾的 HTML 中的预留字符必须被替换为字符实体 在 HTML 中不能使用小于号&#xff08;<&#xff09;和大于号&#xff08;>&#xff09;&#xff0c;这是因为浏览器会误认为它们是…

HTML中的焦点管理

前言 焦点作为页面交互中的重要一环&#xff0c;涉及到的知识点也比较多&#xff0c;有必要做一个统一的总结。 HTML 中的可获取焦点的元素 具有 href 属性的 HTMLAnchorElement/HTMLAreaElement非禁用态的 HTMLInputElement/HTMLSelectElement/HTMLTextAreaElement/HTMLBut…

Docker——compose单机容器集群编排

Docker——compose单机容器集群编排 一、Docker-compose概述1.为何需要Docker-compose2.Docker-compose 的特征3.Docker-compose 的优势4.Docker-compose 的劣势5.Docker-compose 的生产环境 二、Docker Compose 环境安装三、YAML 文件格式及编写注意事项四、Docker Compose配置…

vscode使用g++编译.c文件或.cpp文件

vscode是一个跨平台、轻量级、插件非常丰厚的IDE&#xff0c;这里介绍在vscode里使用g来编译.cpp文件。g也叫GCC, 在Window中&#xff0c;是使用MinGW方式实现g的&#xff0c;它分为32位和64位2个版本&#xff0c;其中&#xff0c;MinGW-64是64位的&#xff0c;MinGW-32是32位的…

ConcurrentHashMap 相比于 HashMap 的优势

ConcurrentHashMap 使用每个链表头节点作为锁对象, 把一把大锁转换成多把小锁, 大大缩小了锁冲突的概率 HashTable 是给整个 Hash 表加锁, 因此只要有线程抢到了锁其他线程就得阻塞等待. ConcurrentHashMap 是对每个链表加锁, 因此只要不是对同一个链表进行修改就不会阻塞, 大…

【微信小程序】使用iView组件库的ActionSheet组件实现底部选择功能

效果1 效果2 要在微信小程序中使用iView组件库的ActionSheet组件&#xff0c;可以按照以下步骤进行&#xff1a; 首先&#xff0c;确保已经引入了iView组件库的样式和脚本文件。可以在app.wxss中引入iView的样式文件&#xff1a; import "/path/to/iview/weapp/dist/sty…

Ubuntu22.04部署K8s集群

Ubuntu22.04部署K8s集群 一、基础环境准备1.1 VMware Workstation Pro 17.01.2 Ubuntu22.04 二、系统环境配置2.1 设置Master与工作节点的机器名称及配置2.2 解析主机2.3 虚拟内存swap分区关闭2.4 开启IPv4转发2.5 设置时间同步2.6 开启防火墙的端口&#xff08;可选&#xff0…

linux下 UART串口相关

RS232的串口设备在linux 上会被识别为 /dev/ttyS* 或者 ttymxc* 一、串口简介 操作串口我们一般通过以下指令&#xff1a; 1、查看串口波特率等信息&#xff1a; stty -F /dev/ttyS0 -a #ttyS0为要查看的串口 2、设置串口参数&#xff1a; stty -F /dev/ttyS0 ispeed 115…

微信小游戏个人开发者上架:从注册到上线的详细步骤

微信小游戏个人开发者上架&#xff1a;从注册到上线的详细步骤 一&#xff0c;注册小程序账号1.1 微信公众平台1.2 填写信息1.3 绑定管理 二&#xff0c;打包步骤2.1 工具准备2.2 关于Unity版本2.3 打包详解 三&#xff0c;提包步骤3.1 填写用户隐私3.2 完善开发者自查3.3 游戏…

5.string变量-读取一行

C里面的读一行的用法。getline&#xff08;cin,addr&#xff09;; 从标准输入设备cin&#xff0c;读取一行字符串保存到字符串变量addr中 如果用户直接回车什么都不读取就没有任何数据输入 读一行直到遇到回车符&#xff0c;注意不包括回车符。 判断字符串是不是空的 addr.em…

Cron 选择器

// 定义一个名为 cron 的新组件 Vue.component(cron, {name: cron,props: [data],data() {return {second: {cronEvery: ,incrementStart: 3,incrementIncrement: 5,rangeStart: ,rangeEnd: ,specificSpecific: [],},minute: {cronEvery: ,incrementStart: 3,incrementIncremen…

2023年一建学霸笔记

考点:单方取消或辞去委托承担的民事责任女《民法典》规定&#xff0c;因解除合同造成对方损失的&#xff0c;除不可归责于该当事人的事由外&#xff0c;无偿委托合同的解除方应当赔偿因解除时间不当造成的直接损失&#xff0c;有偿委托合同的解除方应当赔偿对方的直接损失和合同…

简单理解TCP,UDP,HTTP

我们都知道TCP、UDP、HTTP内部有很复杂的过程&#xff0c;很多人没办法理解的那么深&#xff0c;只想知道这是个什么鬼。 1、TCP、UDP、HTTP 是什么? TCP/IP是个协议组&#xff0c;可分为三个层次&#xff1a;网络层、传输层和应用层。在网络层有IP协议、ICMP协议、ARP协议、…

关于云服务器ECS、宝塔的安装配置以及图床的使用

一、阿里云服务器的申请以及宝塔的安装 安装配置服务器的原理&#xff1a; step1&#xff1a;地址栏输入阿里云服务器官网地址 step2&#xff1a;在首页依次点击以下内容&#xff1a; step3&#xff1a;选择立即购买&#xff0c;并填写以下内容&#xff1a; step4&#xff1a…

Postman和Jmeter做接口测试的区别

1. 用例组织方式 Jmeter的组织方式相对比较扁平&#xff0c;它首先没有WorkSpace的概念&#xff0c;直接是TestPlan&#xff0c;TestPlan下创建的Threads Group就相当于TestCase&#xff0c;并没有TestSuite的层级。 Postman功能上更简单&#xff0c;组织方式也更轻量级&#…

opencv 之 外接多边形(矩形、圆、三角形、椭圆、多边形)使用详解

opencv 之 外接多边形&#xff08;矩形、圆、三角形、椭圆、多边形&#xff09;使用详解 本文主要讲述opencv中的外接多边形的使用&#xff1a; 多边形近似外接矩形、最小外接矩形最小外接圆外接三角形椭圆拟合凸包 将重点讲述最小外接矩形的使用 1. API介绍 #多边形近似 v…

Redisson实现简单消息队列:优雅解决缓存清理冲突

在项目中&#xff0c;缓存是提高应用性能和响应速度的关键手段之一。然而&#xff0c;当多个模块在短时间内发布工单并且需要清理同一个接口的缓存时&#xff0c;容易引发缓存清理冲突&#xff0c;导致缓存失效的问题。为了解决这一难题&#xff0c;我们采用Redisson的消息队列…

带你体验stable discussion文生图,实现自己的真人写真工具

前言 Midjourney 由于精致的画图风格备受好评&#xff0c;但由于其网络环境以及会员费&#xff0c;导致入门门槛过高&#xff0c;拦住了很多对AIGC感兴趣的小伙伴。今天带大家体验一下最近很火的Stable Diffusion&#xff0c;满足大家的AI爱好,无需科学上网&#xff0c;本地部…