IO流(进阶提高)

 

本篇文章继上一篇IO流(小试牛刀)继续更新JavaIO流的知识。

 

常见IO流的结构图解:

IO流(进阶提高)_第1张图片

 

回顾一下IO的基本接口知识:

IO流(进阶提高)_第2张图片

————————————————————我是不起眼的分割线————————————————————————

 

IO的序列化与反序列化

Java序列化是指把Java对象转换为字节序列的过程;Java反序列化是指把字节序列恢复为Java对象的过程。

直白的说就是把Java对象存入文件时进行一个数据加密压码的过程,反之就是解码的过程。

为什么要进行序列化?

当两个进程进行远程通信时,可以相互发送各种类型的数据,包括文本、图片、音频、视频等, 而这些数据都会以二进制序列的形式在网络上传送。那么当两个Java进程进行通信时,能否实现进程间的对象传送呢?这就需要Java序列化与反序列化了。换句话说,一方面,发送方需要把这个Java对象转换为字节序列,然后在网络上传送;另一方面,接收方需要从字节序列中恢复出Java对象。同时序列化让我们的数据也变得更加的安全:序列化后的文本对象是不能够修改的。

怎么进行序列化?

只有实现了Serializable或Externalizable接口的类的对象才能被序列化,否则抛出异常。序列化必须通过对象流来操作。

如下:

java.io.NotSerializableException: io.Student: io.Student

下面我们看一下实现代码:

package io;

import java.io.Serializable;

/**
 * student封装类
 * @author 
 *
 */
@SuppressWarnings("serial")
public class Student implements Serializable{//Serializable 要序列化的对象必须实现它的接口,才能启用对象序列化或反序列化。
	private String name;
	private String sex;
	private int age;
	private int code;

	public String getName() {
		return name;
	}

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

	public String getSex() {
		return sex;
	}

	public void setSex(String sex) {
		this.sex = sex;
	}

	public int getAge() {
		return age;
	}

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

	public int getCode() {
		return code;
	}

	public void setCode(int code) {
		this.code = code;
	}

	@Override
	public String toString() {//重写toString方法
		return "Student [name=" + name + ", sex=" + sex + ", age=" + age + ", code=" + code + "]";
	}

	public Student(String name, String sex, int age, int code) {//有参构造
		super();
		this.name = name;
		this.sex = sex;
		this.age = age;
		this.code = code;
	}

	public Student() {//无参构造
		super();
	}

}
Serializable 要序列化的对象必须实现它的接口,才能启用对象序列化或反序列化。
	private String name;
	private String sex;
	private int age;
	private int code;

	public String getName() {
		return name;
	}

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

	public String getSex() {
		return sex;
	}

	public void setSex(String sex) {
		this.sex = sex;
	}

	public int getAge() {
		return age;
	}

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

	public int getCode() {
		return code;
	}

	public void setCode(int code) {
		this.code = code;
	}

	@Override
	public String toString() {//重写toString方法
		return "Student [name=" + name + ", sex=" + sex + ", age=" + age + ", code=" + code + "]";
	}

	public Student(String name, String sex, int age, int code) {//有参构造
		super();
		this.name = name;
		this.sex = sex;
		this.age = age;
		this.code = code;
	}

	public Student() {//无参构造
		super();
	}

}

 

public static void main(String[] args) {
		Student stu1 = new Student("张三", "男", 24, 1001);//通过有参构造器创建4个学生对象
		Student stu2 = new Student("李四", "女", 22, 1002);
		Student stu3 = new Student("王五", "男", 23, 1003);
		Student stu4 = new Student("赵六", "男", 21, 1004);

		List list = new ArrayList();//分别将对象放入到list集合中去
		list.add(stu1);
		list.add(stu2);
		list.add(stu3);
		list.add(stu4);

		try {//捕捉异常信息
			ObjectOutputStream oos = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream("D:\\stu.txt")));//创建一个对象的输出流对象
			ObjectInputStream ois = new ObjectInputStream(new BufferedInputStream(new FileInputStream("D:\\stu.txt")));//创建一个输入流的对象
			if (!list.isEmpty()) {//判断集合是否为空
				oos.writeObject(list);//不为空就将存有Student对象的List集合对象序列化写入文件
			}
			list = null;//清空List集合对象
			list = (List) ois.readObject();//反序列化必须强制转换为List集合对象
			for (Student student : list) {//遍历整个集合
				System.out.println(student);//打印结果,结果是一致的。
			}
			ois.close();//关流,先用后关,后用先关的原则
			oos.close();

		} catch (Exception e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
}//结果的话就不在这做演示了。

 

获取某个目录下的所有文件信息

运用File类与递归来获取目录下所有的文件及文件夹地址与最后的修改时间。

实现思路:递归的方法

  1. 用递归来实现循环判断该文件是否为文件夹。
  2. 将找到的文件信息存入集合或数组中。
  3. 将集合遍历打印。

实例:

public File[] filenameFilter(File f) {//递归方法
	File[] file = f.listFiles();// 获取文件对象下的所有文件对象(不包含孙子级以下的)
	if (file != null) {// 避免空指针异常进行判断
		for (File fie : file) {// foreach进行数组遍历
			if (fie.isDirectory()) {// 判断该文件对象是否是文件夹目录对象?如果为true就调用自己,把当前遍历到文件对象作为参数传递
				filenameFilter(fie);// 调用自身,进行递归操作
			}
		}
	}
	/*File[] fie = f.listFiles(new FilenameFilter() {// 获取所有的过滤文件对象
		@Override
		public boolean accept(File dir, String name) {// 实现
			// FilenameFilter的accept接口
			return name.endsWith(".txt");// 返回文件对象后缀为'.txt'的文件对象
		}
	});
	return fie;// 将过滤后的文件对象数组返回*/
        return file;//返回所有的存有文件对象的数组
}
public static void main(String[] args) {//主函数
	File file = new File("D:\\");// 创建文件对象入口
	File[] fi = filenameFilter(file);// 将文件对象传入文件过滤函数中
	if (fi != null) {//判断是否为空
		for (File file2 : fi) {//遍历数组
			long time = file2.lastModified();//获取文件最后的修改时间
			Date date = new Date(time);创建时间对象
			SimpleDateFormat fomat = new SimpleDateFormat("YYYY-MM-dd  HH:mm:ss");//格式化时间对象
			System.out.println("文件:"+file2+",最后修改时间:"+fomat.format(date));//输出文件信息
		}
	}
}			Date date = new Date(time);创建时间对象
			SimpleDateFormat fomat = new SimpleDateFormat("YYYY-MM-dd  HH:mm:ss");//格式化时间对象
			System.out.println("文件:"+file2+",最后修改时间:"+fomat.format(date));//输出文件信息
		}
	}
}

最后的输出便是我们所需要的,结果就不展示了。

 

拷贝一个文件到另一个目录

分析:

所有的文件都是二进制的形式,那么我们拷贝以及存储和传输时都要采用二进制式的操作。

思路:

首先必须使用字节流进行文件拷贝传输操作。较大的文件可以使用设置缓冲区来加快文件的写入等操作(推荐使用)。

其次必须保证所传输的文件或目录是可以读写的。

最后传输过程中必须保证二进制文件的正确存储。

实例:

方法一:直接使用数据流进行拷贝。

package csdn;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;

/**
 * 代码演示
 * 
 * @author
 *
 */
public class JTest {
	public static void main(String[] args) {
		/*
		 * DataOutputStream 可以直接对Java的对象转换二进制流进行输入输出
		 * 数据输入流允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型
		 */
		try {//我在演示时就不设置缓冲流了
			DataOutputStream dos = new DataOutputStream(new FileOutputStream("D:\\ttt.txt", true));//创建一个数据输出流
			byte[] b = "Hello,World!".getBytes();//创建一个字节数组,将Hello,World转换到字节数组中
			dos.write(b);//把字节数组写入文件
			dos.close();//使用完毕关流
			DataInputStream dis = new DataInputStream(new FileInputStream("D:\\ttt.txt"));//创建一个数据输入流
			dos = new DataOutputStream(new FileOutputStream("D:\\ttt_副本.txt"));//创建新的数据输出流对象
			int flag = -1;//创建一个二进制容器
			while((flag = dis.read()) != -1){//循环读入并赋值给flag,-1指的是读取的界限(最小的界限)
				dos.write(flag);//通过数据输出流进行写入
			}
			dis.close();// 关流
			System.out.println("拷贝完成!");//拷贝完成的提示。
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

方法二:使用其他字节流拷贝。

package csdn;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;

/**
 * 代码演示
 * 
 * @author
 *
 */
public class JTest {
	public static void main(String[] args) {
		/*拷贝一个图片到D盘下*/
		try {
			BufferedInputStream bis = new BufferedInputStream(new FileInputStream(new File("C:\\Users\\Administrator\\Desktop\\杭州之旅.png")));
			BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(new File("D:\\杭州之旅.png")));
			int flag = 0;
			while((flag = bis.read())!=-1){
				bos.write(flag);
			}
			bos.close();
			bis.close();
			System.out.println("拷贝完毕!");
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
    }
}

结果不展示了。

 

 

总结:

 

在IO流的使用中务必注意各种格式转换问题,一不小心就可能造成数据格式异常或是数据的损坏。

关于IO流那么多的组合使用方法我们不需要都记下,能够精通最少两种方法,其他的可以作为了解(序列化不包括在内)。

IO流的很多地方都是运用Java的反射机制在进行文件操作,所以Java的反射机制必须好好学。

 

感谢采纳,希望本文能给同本人一样在学习Java的童鞋一些帮助,同时欢迎大家批评建议。

你可能感兴趣的:(Java成长之路,精装Java)