文章目录
一、File类
1.访问文件和目录
/**
* @ClassName: FileTest
* @description: 本节代码主要讨论FIle类的相关访问文件和目录
* @author: FFIDEAL
* @Date: 2020年4月10日 上午10:17:48
*/
package M15;
import java.io.File;
import java.io.IOException;
public class FileTest {
public static void main(String[] args) throws IOException{
//以当前===路径===来创建一个File对象
File file = new File(".");
//获取文件名
System.out.println(file.getName()); //输出:newFile.txt
//获取相对路径的父路径(可能会出错,输出null)
System.out.println(file.getParent());
//获取绝对路径
System.out.println(file.getAbsolutePath()); //输出:E:\JavaCode\Code\newFile.txt
//获取上一级路径
System.out.println(file.getAbsoluteFile().getParent()); //输出:E:\JavaCode\Code
//在当前路径下创建一个临时文件
File tmpFile = File.createTempFile("tempFile", ".txt");
//输出当前临时文件的绝对路径
System.out.println(tmpFile.getParent());
//指定JVM退出时删除该文件
tmpFile.deleteOnExit();
//以系统当前时间来命令新文件
File newFile = new File(System.currentTimeMillis()+"");
System.out.println("newFile对象是否存在:" + newFile.exists());
newFile.createNewFile();
//以newFile创建一个目录,因为newFile已经存在
//所以下面方法返回false,即无法创建该目录
newFile.mkdir();
//使用list()方法列出当前路径下的所有文件
String[] fileList = file.list();
System.out.println("===当前路径下的所有文件和路径===");
for(String fileName : fileList){
System.out.println(fileName);
}
//listRoots()静态方法列出所有磁盘根路径
File[] roots = File.listRoots();
System.out.println("===系统过有根目录===");
for(File root : roots) {
System.out.println(root);
}
//listFiles是获取该目录下所有文件和目录的绝对路径
File[] fs = file.listFiles();
for(File f:fs) {
System.out.println(f);
}
}
}
2.文件过滤器
/**
* @ClassName: FileNameFilterTest
* @description: 文件过滤器
* @author: FFIDEAL
* @Date: 2020年4月14日 上午10:07:43
*/
package M15;
import java.io.File;
public class FileNameFilterTest {
public static void main(String[] args) {
File file = new File(".");
//文件以“.java”结尾或者文件对应一个路径,则返回true
String[] nameList = file.list((dir , name) -> name.endsWith(".java") || new File(name).isDirectory());
for(int i = 0; i < nameList.length; i++) {
System.out.println(i);
System.out.println(nameList[i]);
}
for(String name : nameList) {
System.out.println(name);
}
}
}
二、理解Java的IO流
1.流的分类
①按数据流向分类
从程序所在内存角度划分
输出流:只能向其写入数据,而不能从中读取数据(内存 → 硬盘)------- 基类为:OutputStream类和Writer类
输入流:只能从中读取数据,而不能向其写入数据(cache → 内存)--------基类为:InputStream类和Reader类
②按数据单元的长度划分
字节流:数据单元为8位的字节 ------- 基类为:InputStream类和OutputStream类
字符流:数据单元为16位的字节------- 基类为:Writer类和Reader类
③按流的角色来分类
节点流:从特定的IO设备(如磁盘、网络等)读/写的数据的流
处理流:用一个已存在的流进行连接或封装,通过封装后的流来实现读/写的数据的功能
处理流的好处:不能直接连接到实际的数据源,可以采用完全相同的输入/输出代码来访问不同的数据源,消除不同节点流带来的差异(只要使用相同的处理流)————变压器?
2.流的概念模型
Reader/Writer:是对人来说,Reader读者,获取知识,输入;Writer写着,输出知识,输出
InputStream/Reader:所有输入流的基类,前者是字节输入流,后者是字符输入流
OutputStream/Writer:所有输出流的基类,前者是字节输出流,后者是字符输出流
三、字节流和字符流
1.输入流中的字节流和字符流
read():读取一个“水滴(字节或字符)”
read(byte[] b)/read(char[] ch):读取多个"水滴"
read(char[] ch,int off,int len)/read(byte[] ch,int off,int len):同上,规定了长度
字节流
/**
* @ClassName: FileInputStreamTest
* @description: 本节代码讨论了使用FileInputStream (字节流而非字符流)输入流读取数据的一种实现方法
* 注意:在本节代码中,如果设置的“水管”不长,也就是说,不能一次性取完文件中所有的数据,要分多批次
* 此时,若文件中时有中文的话,意味着可能会发生乱码 ———— 原因是,中文占2个字符,而读取时,只能制度去一个字符
* @author: FFIDEAL
* @Date: 2020年4月14日 下午2:06:07
*/
package M15;
import java.io.FileInputStream;
import java.io.IOException;
public class FileInputStreamTest {
public static void main(String[] args) throws IOException{
//创建一个字节输入流
FileInputStream fis = new FileInputStream("E:\\JavaCode\\Code\\src\\M15\\FileNameFilterTest.java");
//创建一个字节流水管,用来取水
byte[] bbuf = new byte[1024];
//用于保存实际读取的字节数
int hasRead = 0;
//当水管里还有水的时候
while((hasRead = fis.read(bbuf)) > 0) {
System.out.println(new String(bbuf , 0 , hasRead));
}
//关闭水管
fis.close();
}
}
字符流
/**
* @ClassName: FileInputStreamTryCatchTest
* @description: 本节代码主要讨论使用try-catch和FileReader(字符输入流) 字符流 来实现输入流的实现过程
* 另外,也是使用了自动关闭资源的try语句来关闭文件输入流
* @author: FFIDEAL
* @Date: 2020年4月14日 下午2:18:59
*/
package M15;
import java.io.FileReader;
import java.io.IOException;
public class FileInputStreamTryCatchTest {
public static void main(String[] args) throws IOException{
try(FileReader fis =
new FileReader("E:\\JavaCode\\Code\\src\\M15\\FileNameFilterTest.java")){
//创建一个字符流水管
char[] chbuf = new char[32];
//取水珠计数器
int hasRead = 0;
while((hasRead = fis.read(chbuf)) > 0) {
System.out.println(new String(chbuf, 0 , hasRead));
}
}
catch(IOException ex) {
ex.printStackTrace();
}
}
}
2.输入流中的字节流和字符流
write(int a):写入一个字节/字符
write(byte[] b/char[] ch):写入一组数据
write(byte[] b/char[] ch,int off,int len):写入一组由off开始,长度为lend数据
字节流
/**
* @ClassName: FileOutputStreamTest
* @description: 本节代码主要讨论==字节输出流==的实现类,配合字节输入流,将一个文档里面的数据转移到另一个文档中去
* @author: FFIDEAL
* @Date: 2020年4月14日 下午2:32:35
*/
package M15;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileOutputStreamTest {
public static void main(String[] args) throws IOException{
try(
//创建一个字节输入流
FileInputStream fis =
new FileInputStream("E:\\JavaCode\\Code\\src\\M15\\FileNameFilterTest.java");
//创建一个字节输出流
FileOutputStream fos =
new FileOutputStream("E:\\JavaCode\\Code\\src\\M15\\FileNameFilterTest.txt")
){
//创建一根字节流水管
byte[] bbuf = new byte[1024];
//取水计数器
int hasRead = 0;
while((hasRead = fis.read(bbuf)) > 0) {
//每读一次,即写入文件输出流,读了多少就写多少
fos.write(bbuf, 0, hasRead);
}
}
catch(IOException ex) {
ex.printStackTrace();
}
}
}
字符流
/**
* @ClassName: FileWriterTest
* @description: 本节讨论了使用==字符输出流==来实现将一首诗写入到文件中,同时也将一个文件里的数据写入到另一个文件里
* @author: FFIDEAL
* @Date: 2020年4月14日 下午2:41:50
*/
public class FileWriterTest {
public static void main(String[] args) throws IOException {
try(//创建一个字符输出流
FileWriter fw = new FileWriter("E:\\JavaCode\\Code\\src\\M15\\pom.txt");
){
fw.write("床前明月光,\t\n");
fw.write("疑是地上霜。\t\n");
fw.write("举头望明月。\t\n");
fw.write("低头思故乡。\t\n");
}
catch(IOException ex) {
ex.printStackTrace();
}
//创建一个字符输入流
FileReader fr = new FileReader("E:\\JavaCode\\Code\\src\\M15\\pom.txt");
//创建一个字符输出流
FileWriter fw = new FileWriter("E:\\JavaCode\\Code\\src\\M15\\pomWriter.txt");
//创建一根水管
char[] chbuf = new char[1024];
//取水计数器
int hasRead = 0;
while((hasRead = fr.read(chbuf)) > 0) {
fw.write(chbuf, 0, hasRead);
}
//关闭输入流和输出流
fr.close();
fw.close();
}
}
四、输出/输出流体系
1.处理流用法
处理流的典型思路是:使用处理流来包装节点流,程序通过处理流来执行输入、输出功能,让节点流和底层IO设备以及文件交互
识别处理流很简单:只要流的构造器参数不是一个物理节点,而是已经存在的流,那么这种流就是处理流
/**
* @ClassName: PrintStreamTest
* @description: 本节代码讨论的是PrintStream处理流,
* @author: FFIDEAL
* @Date: 2020年4月15日 下午1:46:30
*/
public class PrintStreamTest {
public static void main(String[] args) throws IOException{
try(
//创建一个字节流
FileOutputStream fos = new FileOutputStream("E:\\JavaCode\\Code\\src\\M15\\print.txt");
//创建一个PrintStream流,把FileOutputStream字节流包装起来
PrintStream ps = new PrintStream(fos)){
//使用PrintStream执行输出
ps.println("PrintStream执行输出");
//直接使用PrintStream对象输出对象
ps.println(new PrintStreamTest());
}
catch(IOException e) {
e.printStackTrace();
}
}
}
2.输入/输出流体系
字节流以字节数粗为节点,字符流以字符数组为节点;
字符流可以使用字符串作为物理节点,用于实现从字符串读取内容,或将内容写入字符串
/**
* @ClassName: StringNodeTest
* @description: 本节代码主要讨论输入输出的体系问题
* @author: FFIDEAL
* @Date: 2020年4月15日 下午2:09:39
*/
public class StringNodeTest {
public static void main(String[] args) throws IOException{
String src ="一去二三里,烟村四五家。 \r\n" +
"亭台六七座,八九十枝花。";
char[] chbuf = new char[1024];
int hasRead = 0;
try(
StringReader sr = new StringReader(src)){
//以循环的方式读取字符串
while((hasRead = sr.read(chbuf)) > 0) {
System.out.println(new String(chbuf,0,hasRead));
}
}
catch(IOException ioe) {
ioe.printStackTrace();
}
try(
//创建StringWriter时,是加上以一个StringBuffer作为输出节点
//一面指定的20就是StringBuffer的初始长度
StringWriter sw = new StringWriter()
){
sw.write(src);
System.out.println("===下面是sw字符串节点里面的内容===");
System.out.println(sw.toString());
}
catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
3.转换流
/**
* @ClassName: KeyinTest
* @description: 本节代码主要讨论System.in对象转换成Reader对象(利于包装为BufferReader),
* 将普通的Reader包装为BufferReader(使用BufferReader对象的方法利于读取)
* @author: FFIDEAL
* @Date: 2020年4月15日 下午4:29:34
*/
package M15;
public class KeyinTest {
public static void main(String[] args) throws IOException{
try(
//将System.in对象转换成Reader对象
InputStreamReader isr = new InputStreamReader(System.in);
//将普通的Reader包装为BufferReader
BufferedReader br = new BufferedReader(isr)
){
String line =null;
//采用循环的方式来读取
while((line = br.readLine()) != null) {
//如果读取到字符串“exit”,程序就退出
if(line.equals("exit")) {
System.exit(1);
}
System.out.println("输出内容为:" + line);
}
}
catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
4.推回输入流
就是输出到—传入的参数----输出传参以前的字符
五、重定向标准输入输出
将System.out的输出重定向到指定文件输出,而非在屏幕上输出
/**
* @ClassName: RedirectOut
* @description: 本节代码主要讨论将系统的标准输出重定向到该printStream输出流
* 运行上面程序时将看不到任何输出 ———— 这意味着标准输出不再输出到屏幕,而是输出到out.txt
* 将System.in重定义到指定文件里的内容,正好与程序中的输出一致
* @author: FFIDEAL
* @Date: 2020年4月15日 下午6:05:58
*/
public class RedirectOut {
public static void main(String[] args) throws IOException{
try(
//一次性创建PrintStream输出流
PrintStream ps = new PrintStream(
new FileOutputStream("E:\\JavaCode\\Code\\src\\M15\\out.txt"))
){
//将标准输出重定向到ps输出流
System.setOut(ps);
//向标准输出输出一个字符串
System.out.println("这是一个字符串");
//向标准输出输出一个对象
System.out.println(new RedirectOut());
}
catch(IOException ioe) {
ioe.printStackTrace();
}
}
}
将System.in的重定向到指定文件,而不是键盘输入
/**
* @ClassName: RedirectIn
* @description: 创建一个FileInputStream输入流,并使用System的setIn()方法将系统标准输入重定向到该文件输入流
* 运行本程序,程序不会等用户输入,而是直接输出RedirectOut.java文件的内容,这表明程序不再使用键盘作为标准输入
* @author: FFIDEAL
* @Date: 2020年4月15日 下午9:42:45
*/
public class RedirectIn {
public static void main(String[] args) throws IOException{
try(
//创建一个FileInputStream输入流
FileInputStream fis = new FileInputStream("E:\\JavaCode\\Code\\src\\M15\\RedirectOut.java")
){
//将标准输入重定向到fis输出流
System.setIn(fis);
//使用System.in创建Scanner对象,用于获取标准输入
Scanner sc = new Scanner(System.in);
//增加下面一行只把回车作为分隔符
sc.useDelimiter("\n");
//判断是否还有下一个输入项
while(sc.hasNext()) {
System.out.println("键盘输入的内容是:" + sc.next());
}
}
catch(IOException ioe) {
ioe.printStackTrace();
}
}
}
六、Java虚拟机读写其他进程的数据
/**
* @ClassName: ReadFromProcess
* @description: 本节代码主要讨论使用Runtime对象的exec()方法可以运行平台上的其他程序,
* 该方法产生一个Process对象,
* Process对象代表由该Java程序启动的子进程
* @author: FFIDEAL
* @Date: 2020年4月16日 上午10:50:18
*/
public class ReadFromProcess {
public static void main(String[] args) throws IOException{
//运行javac命令,返回运行该命令的子进程
Process p = Runtime.getRuntime().exec("javac");
try(
//以p进程的错误流创建BufferReader对象,这个错误流对本程序是输入流,对p进程是输出流
BufferedReader br = new BufferedReader(new InputStreamReader(p.getErrorStream()))
){
String buff = null;
while((buff = br.readLine()) != null) {
System.out.println(buff);
}
}
}
}
/**
* @ClassName: WriteToProcess
* @description: 本节代码讨论通过Process的getOutStream()方法获得向进程输入数据的流,
* 如下程序实现了在java程序中启动Java虚拟机运行另一个java程序,并向另一个程序输入数据
* @author: FFIDEAL
* @Date: 2020年4月16日 下午12:50:20
*/
public class WriteToProcess {
public static void main(String[] args) throws IOException{
//运行Java ReadStandard命令,返回该命令的子进程
Process p = Runtime.getRuntime().exec("Java ReadStandard");
try(
//以p进程的输出流创建PrintStream对象
//对于程序来说这是输出流,对于子进程来说,这是输入流
PrintStream ps = new PrintStream(p.getOutputStream());
){
//向ReadStandard程序写入内容,这些内容被ReadStandard读取
ps.println("这是ReadStandard读取的内容");
ps.println(new WriteToProcess());
}
}
}
//定义一个ReadStandard类,该类可以接收标准输入
//并将标准输入写入ReadStandard.txt文件中d
class ReadStandard{
public static void main(String[] args) throws IOException{
try(
//使用System.in创建Scanner对象用于标准输入
Scanner sc = new Scanner(System.in);
PrintStream ps = new PrintStream("E:\\JavaCode\\Code\\src\\M15\\ReadStandard.txt")
){
//增加下面一行只把回车作为分隔符
sc.useDelimiter("\n");
while(sc.hasNext()) {
//输出键盘输入的内容
System.out.println("键盘输入的内容是:" + sc.next());
}
}
catch(Exception e) {
e.printStackTrace();
}
}
}
七、RandomAccessFile
RandomAccessFile(任意访问文件):是功能最丰富的文件访问类,它既==可以读文件,也可以写文件,同时还有**“任意访问”==**文件。若只需要访问文件的部分内容,而非把文件从头到尾读取,采用RandomAccessFile类将是一个很好的选择。
但是她有一个缺陷,也就是说他只能读写文件但**不能读写其他IO节点**。
以下使用RandomAccessFile来访问指定的中间部分数据
public class RandomAccessFiletest {
public static void main(String[] args) throws IOException{
try(
//创建一个RandomAccessFile对象
RandomAccessFile raf =
new RandomAccessFile("E:\\JavaCode\\Code\\src\\M15\\pomWriter.txt", "r");
){
//获取RandomAccessFile对象指针位置,初始位置为0
System.out.println("RandomAccessFile的文件指针的初始地址" + raf.getFilePointer());
//移动文件记录指针位置
raf.seek(6);
byte[] bbuf = new byte[1024];
//记录实际读取的字节数
int hasRead = 0;
while((hasRead = raf.read(bbuf)) > 0) {
//取出竹筒中的水滴(“字节”),将字节数组转换成字符串输入
System.out.println(new String(bbuf,0,hasRead));
}
}
catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
下面代码是写入文件
public class AppendContent {
public static void main(String[] args) throws IOException{
try(
//以“读写”的方式打开一个RandomAccessFile对象
RandomAccessFile raf = new RandomAccessFile("E:\\JavaCode\\Code\\src\\M15\\pomWriter.txt", "rw");
){
//将指针移动到最后
raf.seek(raf.length());
raf.write("\n这是使用RandomAccessFile对象追加的内容".getBytes());
}
catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
下面程序实现了向指定文件、指定位置插入内容的功能
/**
* @ClassName: InsertContent
* @description: 本节代码向指定位置插入数据,在此位置之后的数据放入缓存区,
* 当全部写完输入后,缓存区里的数据接在插入数据之后
* @author: FFIDEAL
* @Date: 2020年4月16日 下午5:42:01
*/
public class InsertContent {
public static void insert(String fileName, long position , String insertContent)
throws IOException{
//创建一个临时文件,用作缓存区
File tmp = File.createTempFile("tmp", null);
tmp.deleteOnExit();
try(
RandomAccessFile raf = new RandomAccessFile(fileName, "rw");
//使用临时文件来保存插入点数据
FileOutputStream tmpOut = new FileOutputStream(tmp);
FileInputStream tmpIn = new FileInputStream(tmp)){
raf.seek(position);
//下面代码将插入点后的内容读入临时文件中保存
byte[] bbuf = new byte[20];
//用于保存实际读取的字节
int hasRead = 0;
while((hasRead = raf.read(bbuf)) > 0) {
//将读取的数据写入 临时文件
tmpOut.write(bbuf, 0, hasRead);
}
//下面代码用于插入内容
//把文件记录指针重新定位到pos位置
raf.seek(position);
//追加需要插入的内容
raf.write(insertContent.getBytes());
//追加临时文件中的内容
while((hasRead = tmpIn.read(bbuf)) > 0) {
raf.write(bbuf,0,hasRead);
}
}
}
public static void main(String[] args) throws IOException {
insert("E:\\JavaCode\\Code\\src\\M15\\print.txt", 45, "=========================");
}
}
八、对象序列化
1.序列化的含义和意义
序列化机制:将实现序列化的Java对象转换成字节序列,这些字节序列可以保存在磁盘上,或通过网络传输,以备以后重新恢复成原来的对象 —— 序列化机制使得对象可以脱离程序存在
对象的序列化:讲一个Java对象希尔IO流中,与此对应的四,对象的反序列化则指从IO流中恢复该Java对象
实现对象的序列化的前提就是对象的类要序列化,类的序列化就是必须实现Serialiable或Externalizable接口
2.使用对象流实现序列化
使用Serializable来实现序列化非常简单,主要让目标实现Serializable标记接口即可,无需事先任何方法
1.创建ObjectOutputStream,这是一个输出流
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.txt"));
2.调用ObjectOutputStream对象的writeObject()方法输出可序列化对象
oos.writeObject(per);
序列化
实例代码
//创建一个普通类,继承Serializable接口。当然Externalizable接口也行
public class Person implements Serializable{
private String name;
private int age;
public Person(String name, int age) {
System.out.println("有参数构造器");
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;
}
}
使用objectOutputStream(对象输出流)讲一个Person类的对象写入文件中
public class WriteObject {
public static void main(String[] args) throws IOException{
try(
//创建一个ObjectoutputStream输出流
ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream("E:\\JavaCode\\Code\\src\\M15\\Seriliable.txt"));
){
Person per = new Person("张三", 18);
//将这个对象写入输出流
oos.writeObject(per);
}
catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
反序列化
1.创建一个ObjectInputStream输入流,这个输入流是一个处理流,必须建立在其他节点流基之上
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("out.txt"));
2.调用ObjectInputStream对象的readObject()方法读取流中的对象,该方法返回一个Object类型的java对象
Person p = (Person)ois.readObject();
以下代码示范了刚刚生成的文件中读取Person对象的步骤
public class ReadObjectTest {
public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
try(
ObjectInputStream ois = new ObjectInputStream(
new FileInputStream("E:\\JavaCode\\Code\\src\\M15\\Seriliable.txt"))
){
Person p = (Person)ois.readObject();
System.out.println("p的姓名:" + p.getName() + ",年龄:" + p.getAge());
}
catch(Exception e) {
e.printStackTrace();
}
}
}
注意:反序列化读取的仅仅使Java对象的数据,而非Java类
若使用序列化机制向文件中写入多个Java对象,使用反序列化机制恢复对象时必须按实际写入的顺序读取
3.对象引用的序列化
若一个类的成员变量不是基本类型或者String型,而是另一种引用类型,那么这个引用类必须是可序列化的
此外,序列化机制会采用一种特殊的序列化算法
1.所有保存到磁盘中的对象都有一个序列化编号
2.在程序试图序列化一个对象的时候,程序会优先检查该对象是否被序列化过,若没有,系统才会将该对象转换成字节序列并输出
3.程序将只是直接输出一个序列化编号,而不是再次重新序列化该对象
public class Teacher implements Serializable{
private String name;
private Person student;
public Teacher(String name,Person student) {
this.name = name;
this.student = student;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Person getStudent() {
return student;
}
public void setStudent(Person student) {
this.student = student;
}
}
public class WriteTeacher {
public static void main(String[] args) throws FileNotFoundException, IOException {
try(
ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream("E:\\JavaCode\\Code\\src\\M15\\teacher.txt"))){
Person per = new Person("小明", 13);
Teacher t1 = new Teacher("王老师", per);
Teacher t2 = new Teacher("张老师", per);
//依次将4个对象写入输出流
oos.writeObject(t1);
oos.writeObject(t2);
oos.writeObject(per);
oos.writeObject(t2);
}
catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
上面writeObject()方法仿佛写入了四个对象,但实际上只写入三个对象,而且序列的两个Teacher对象的student引用实际是同一个Person对象。下面程序读取序列化文件中的对象即可证明
public class ReadTeacher {
public static void main(String[] args)
throws FileNotFoundException,IOException, ClassNotFoundException{
try(
ObjectInputStream ois = new ObjectInputStream(
new FileInputStream("E:\\JavaCode\\Code\\src\\M15\\teacher.txt"))){
//依次读取ObjectInputStream输入流中的4个对象
Teacher t1 = (Teacher)ois.readObject();
Teacher t2 = (Teacher)ois.readObject();
Person per = (Person)ois.readObject();
Teacher t3 = (Teacher)ois.readObject();
//输出true
System.out.println("t1的student引用和p是否相同:" + (t1.getStudent()==per));
//输出true
System.out.println("t2的student引用和p是否相同:" + (t2.getStudent()==per));
//输出true
System.out.println("t2和t3是否相同:" + (t2==t3));
}
catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
注意:序列化机制当程序序列化一个可变对象时,只有第一次使用writeObject()方法输出时才会将该对象转换成字节序列并输出,再次调用只会输出前面序列化的编号
4.自定义序列化
在一些特殊的场景下不希望系统将实例变量值进行序列化
通过在实例变量前用transient关键词修饰,可以指定Java序列化无需理会该实例变量。
private transient int age;
下面程序先序列化一个Person对象,然后再反序列化该Person对象,得到反序列化兑现的Person对象之后程序输出该对象的age实例变量值
public class TransientTest {
public static void main(String[] args)
throws FileNotFoundException, IOException, ClassNotFoundException {
try(
ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream("E:\\JavaCode\\Code\\src\\M15\\transient.txt"));
ObjectInputStream ois = new ObjectInputStream(
new FileInputStream("E:\\JavaCode\\Code\\src\\M15\\transient.txt"))){
Person per = new Person("哪吒", 7);
oos.writeObject(per);
Person p = (Person)ois.readObject();
System.out.println(p.getAge());
}
catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
九、NIO
新IO采用内存映射文件的方式来处理输入/输出,新IO将文件或文件的一段区域映射到内存中,这样就可以像访问内存一样来访问文件。Channel(通道)和Buffer(缓冲)是新IO的两个核心对象。
Channel比InputStream、OutputStream最大的区别在于他提供了一个map方法,通过这个方法可以直接将“一块数据”映射到内存中。Buffer可以被理解成一个容器,它的本质是一个数组,发送到Channel中的所有对象都必须首先放到Buffer中去。
打个比方:Buffer既可以像“竹筒”一样一次次从Channel中取水,也允许使用Channel直接将文件袋某块数据映射成Buffer
1.Buffer
Buffer有三个重要的概念:容量(capacity)、界限(limit)和位置(position)
/**
* @ClassName: BufferTest
* @description:
* @author: FFIDEAL
* @Date: 2020年4月17日 下午9:44:47
*/
package M15;
import java.nio.CharBuffer;
public class BufferTest {
public static void main(String[] args) {
//创建Buffer,通过CharBuffer的一个静态方法allocate创建一个capacity为8的CharBuffer
CharBuffer buff = CharBuffer.allocate(8);
System.out.println("capacity:" + buff.capacity()); //输出:capacity:8
System.out.println("limit:" + buff.limit()); //输出:limit:8
System.out.println("position:" + buff.position()); //输出:position:0
//放入元素
buff.put('a');
buff.put('d');
buff.put('e');
System.out.println("加入三个元素之后:position:"
+ buff.position()); //输出:加入三个元素之后:position:3
//调用flip(),把limit设为position处,把position设为0
buff.flip();
System.out.println("执行flip()方法之后,limit = "
+ buff.limit()); //输出:执行flip()方法之后,limit = 3
System.out.println("执行flip()方法之后,position = "
+ buff.position()); //输出:执行flip()方法之后,limit = 3
//取出第一个元素
System.out.println("第一个元素position = 0:" + buff.get()); //输出:第一个元素position = 0:a
System.out.println("取出一个元素后的position = "
+ buff.position()); //输出:取出一个元素后的position = 1
//调用clear方法,将position归0,limit = capacity
System.out.println("执行clear()方法之后,limit = "
+ buff.limit()); //输出:执行clear()方法之后,limit = 3
System.out.println("执行clear()方法之后,position = "
+ buff.position()); //输出:执行clear()方法之后,position = 1
System.out.println("执行clear()后,缓冲区内容并没有被清除,第三个元素为"
+ buff.get(2)); //输出:执行clear()后,缓冲区内容并没有被清除,第三个元素为e
System.out.println("执行绝对读取后,position = " + buff.position()); //输出:执行绝对读取后,position = 1
}
}
2.Channel
Channel有以下两个特点
- Channel可以直接将指定的文件部分或全部映射成Buffer
- 程序不能直接访问Channel,读写都不行,Channel只能与Buffer交互
Channel有三个主要的方法:map() read() write()
public class FileChannelTest {
public static void main(String[] args) throws FileNotFoundException, IOException {
File f = new File("E:\\JavaCode\\Code\\src\\M15\\print.txt");
try(
//创建FileInputStream,以文件输入流创建FileChannel
FileChannel inChannel = new FileInputStream(f).getChannel();
//文件输出流创建FileChannel,用以控制输出
FileChannel outChannel =
new FileOutputStream("E:\\JavaCode\\Code\\src\\M15\\ChannelOut.txt").getChannel();
){
//将FileChannel里的全部数据映射成ByteBuffer,将FileChannel里面的输出全部改为ByteChannel
MappedByteBuffer buffer = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, f.length());
//使用GBK的字符集来创建解码器
Charset charset = Charset.forName("GBK");
//直接将buffer里的数据全部输出,将整个ByteBuffer的全部数据写入一个输出FileChannel
outChannel.write(buffer);
//在调用buffer的clear()方法,复原limit和position的位置
buffer.clear();
//创建解码器(CharsetDecoder)对象
CharsetDecoder cd = charset.newDecoder();
//使用解码器将ByteBuffer转换成charBuffer
CharBuffer cb = cd.decode(buffer);
//CharBuffer的toString方法可以获取对应的字符串
System.out.println(cb);
}
catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}