输入输出流及序列化与反序列化

本篇将介绍输入输出相关的文件类(File)和输入输出流,以及序列化与反序列化。

本篇介绍的输入输出流包括四大基类(InputStream,OutputStream,Reader,Writer)及其子类(FileInputStream,FileOutputStream,InputStreamReader,OutputStreamWriter,BufferedReader,BufferedWriter,ObjectInputStream,ObjectOutputStream)和子类的子类(FileReader,FileWriter,DataInputStream,DataOutputStream)

一、File 类

java.io.File 类是 Java 专门用来操作文件的类

File 类的常用方法:

public class TestFile {
	public static void main(String[] args) {
		//创建文件目录(文件夹)对象  D:\IO测试
		//1.创建已有目录的对象
		File fatherFile = new File("D:\\IO测试");
		//2.创建一个新的目录对象及目录
		File newDir1 = new File("D:\\IO测试\\test1");
		newDir1.mkdir();
		//3.创建一个或多个新的目录对象及目录
		File newDir2 = new File("D:\\IO测试\\test1\\test2\\test3");
		newDir2.mkdirs();
		
		//打印文件目录对象(打印的是文件目录的相对路径,同下面getPath()方法)
		System.out.println(fatherFile);
		
		//创建文件对象
		File f1 = new File(fatherFile, "hahaha.txt");
		//打印文件对象(打印的是文件的相对路径,同下面getPath()方法)
		System.out.println(f1);
		try {
			//通过文件对象创建文件:createNewFile()
			boolean isSuccess = f1.createNewFile();
			System.out.println(isSuccess);
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		//判断文件或文件目录是否存在:exists()
		f1.exists();
		fatherFile.exists();
		//判断是否是文件:isFile()
		f1.isFile();
		//判断是否是文件目录:isDirectory()
		fatherFile.isDirectory();
		//删除文件或文件目录:delete()
		f1.delete();
		fatherFile.delete();
		
		//获取并打印文件(或文件目录)路径
		System.out.println("文件名:"+f1.getName());
		System.out.println("相对路径:"+f1.getPath());
		System.out.println("绝对路径:"+f1.getAbsolutePath());
		
		//获取并打印文件长度(单位:字节)
		System.out.println(f1.length());
//		fatherFile.length()也可以打印,但返回值未指定,无意义		
	}
}

补充:递归的方式打印文件夹中的文件名及目录名
(注意:递归调用自己的方法,非常耗内存,一定要有终止条件!一般不建议使用)

public class Test {
    //打印文件下面文件名,如果遇到包含子文件夹,再进入子文件夹打印
    public static void getAllFileNames(String filePath) {
        //1.创建文件对象
        File file = new File(filePath);
        //2.判断该文件是目录还是文件,如果是目录,继续进入
        if (file.isDirectory()) {
            //3.获取该目录下面所有文件对象 File[]
            File[] files = file.listFiles();
            //4.遍历files数组
            for (int i = 0;i < files.length;i++) {
                //5.判断每一个文件是否是目录
                if (files[i].isDirectory()) {
                    System.out.println("目录:" + files[i].getName());
                    getAllFileNames(files[i].getPath());   //递归操作:方法调用自己的方法   压栈操作:当条件不满足的时候,弹栈
                } else {
                    System.out.println("\t文件:" + files[i].getName());
                }
            }
        } else {
            //不是目录,打印文件名
            System.out.println("\t文件:" + file.getName());
        }
    }
}

二、字节输入流

基类(抽象类):InputStream —— 子类:FileInputStream

1. InputStream类常用方法

  • int read()
  • int read(byte[] b)
  • int read(byte[] b,int off,int len)
  • void close()
  • int available():可以从输入流中读取的字节数目

2. 子类FileInputStream常用的构造方法

  • FileInputStream(File file)
  • FileInputStream(String name)

3. read() 方法示例:

public class TestInputStream {
	public static void main(String[] args) {
		//读取txt文件里面的内容
		File file = new File("D:\\IO测试\\hahaha.txt");
		
		//查看文件长度
		System.out.println(file.length());
		
		//创建输入流对象(读取)类似架设管道,指定流向
//		InputStream fis = null;
		try {
			//输入不能自动创建文件和文件目录(文件夹),所以读取文件前要确保文件存在
			InputStream fis = new FileInputStream(file);
//			fis = new FileInputStream(file);
			//读取管道里面数据流内容
			int data = -1;
			while((data = fis.read()) != -1) {
				System.out.print((char) data + "\t");
			}
			
			//关闭流
			fis.close();
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
//		finally {
//			//关闭流是必须执行的操作,所以可以放finally块里,但是需要把fis放到try-catch外面,变成全局变量
//			fis.close();
//		}
	}
}

read(byte[] b) 方法示例:

public class TestInputStream {
	public static void main(String[] args) {
		//读取txt文件里面的内容
		File file = new File("D:\\IO测试\\hahaha.txt");
		
		//查看文件长度
		System.out.println(file.length());
		
		//创建输入流对象(读取)类似架设管道,指定流向
		InputStream fis = null;
		try {
			fis = new FileInputStream(file);
			//读取管道里面数据流内容
			int data = -1;
			byte[] bytes = new byte[1024];
			while((data = fis.read(bytes)) != -1) {
				System.out.print("读取的字节数:" + data + "\t");
				//read(byte[] bytes)方法返回的是读取的字节数量,如果没有读取到,返回-1
				//读取的内容存入bytes数组里面
				for(int i = 0;i < bytes.length;i++) {
					if(bytes[i] != 0) {
						char c = (char) bytes[i];
						System.out.print(c + "\t");
					}
				}
			}
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}finally {
			//关闭流
			try {
				fis.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}

三、字节输出流

基类(抽象类):OutputStream —— 子类:FileOutputStream

1. OutputStream类常用方法

  • void write(int c)
  • void write(byte[] buf)
  • void write(byte[] b,int off,int len)
  • void close()
  • void flush():强制把缓冲区的数据写到输出流中

2. 子类FileOutputStream常用的构造方法

  • FileOutputStream (File file)
  • FileOutputStream(String name)
  • FileOutputStream(String name,boolean append)

3. write(int c) 方法示例:

public class TestOutputStream {
	public static void main(String[] args) {
		int data = 97;
		//构建流对象
		OutputStream fos = null;
		try {
			//输出可以自动创建文件,但不能创建文件目录(文件夹)
//			fos = new FileOutputStream("D:\\IO测试\\hahaha.txt");   //无true表示覆盖原内容
			fos = new FileOutputStream("D:\\IO测试\\hahaha.txt",true);   //有true表示追加,不覆盖原内容
			//写入数据
			fos.write(data);
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}finally {
			//关闭流
			try {
				fos.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}

write(byte[] buf) 方法示例:

public class TestOutputStream {
	public static void main(String[] args) {
		byte[] bytes = {97,98,99,100,101};
/*		String str = "我爱乔乔,乔乔最好!";
*		bytes = str.getBytes();        //String的getBytes()可以将字符串转换成字节数组,然后输出,但是较麻烦,字符串输入输出可以通过字符流
*/		//构建流对象
		OutputStream fos = null;
		try {
			fos = new FileOutputStream("D:\\IO测试\\hahaha.txt");   //无true表示覆盖原内容
//			fos = new FileOutputStream("D:\\IO测试\\hahaha.txt",true);   //有true表示追加,不覆盖原内容
			//写入数据
			fos.write(bytes);   //将数组内容写入文件
//			fos.write(bytes,1,2);   //后面跟2个数字截取数组的一部分写入,第一个作为开始下标,第二个表示截取的字节数
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}finally {
			//关闭流
			try {
				fos.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}

四、字符输入流

基类(抽象类):Reader —— 子类:InputStreamReader —— 子类:FileReader

1. Reader类常用方法

  • int read()
  • int read(char[] c)
  • read(char[] c,int off,int len)
  • void close()

2. 子类InputStreamReader常用的构造方法

  • InputStreamReader(InputStream in)
  • InputStreamReader(InputStream in,String charsetName)

3. FileReader类是InputStreamReader的子类

  • FileReader(File file)
  • FileReader(String name)
  • 该类只能按照本地平台的字符编码来读取数据,用户不能指定其他的字符编码类型 System.out.println(System.getProperty("file.encoding"));获得本地平台的字符编码类型

4. BufferedReader类是Reader类的子类( 基类:Reader —— 子类:BufferedReader )

  • BufferedReader类带有缓冲区,按行读取内容的readLine()方法
  • BufferedReader常用的构造方法:BufferedReader(Reader in)
  • Reader子类BufferedReader特有的方法:readLine()

5. read() 方法示例(包含字符输入缓冲流 BufferedReader 及其特有的 readLine() 方法):

public class TestReader {
	public static void main(String[] args) {
		Reader fr = null;
//		BufferedReader br = null;
		//读取文件
		try {
			fr = new FileReader("古侠今遇.txt");
			
			int data = -1;
			while((data = fr.read()) != -1) {       //挨个读取字符,一次读取一个字符,不方便,借助字符输入缓冲流的方法可以一次读一行
				System.out.print((char)data);
			}
			
//			br = new BufferedReader(fr);       //字符输入缓冲流,包装字符流
//			String line = null;
//			while((line = br.readLine()) != null) {       // BufferedReader类特有的 readLine()方法,一次可以读一行
//				System.out.println(line);
//			}
		
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}finally {
			try {
//				br.close();
				fr.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}

6. 解决读取时中文乱码:使用InputStreamReader并设置编码格式

public class Test {
	public static void main(String[] args) {
		//获取本地平台的字符编码方式
		System.out.println(System.getProperty("file.encoding"));
		
		//编码方式不同导致乱码及其解决方式
		FileReader fr = null;
//		InputStreamReader isr = null;
//		InputStream is = null;
		
		try {
			fr = new FileReader("古侠今遇 utf-8版.txt");
			int data = -1;
			while((data = fr.read()) != -1) {
				System.out.print((char)data);
			}
			
//			is = new FileInputStream("古侠今遇 utf-8版.txt");
			//InputStreamReader的带参构造可以设置编码方式,解决编码方式不同导致的乱码
//			isr = new InputStreamReader(is,"utf-8");
//			data = -1;
//			while((data = isr.read()) != -1) {
//				System.out.print((char)data);
//			}
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}finally {
			try {
				fr.close();
//				isr.close();
//				is.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}

五、字符输出流

基类(抽象类):Writer —— 子类:OutputStreamWriter —— 子类:FileWriter

1. Writer类常用方法

  • write(String str)
  • write(String str,int off,int len)
  • void close()
  • void flush()

2. 子类OutputStreamWriter常用的构造方法

  • OutputStreamWriter(OutputStream out)
  • OutputStreamWriter(OutputStream out,String charsetName)

3. FileWriter类是OutputStreamWriter的子类

  • FileWriter (File file)
  • FileWriter (String name)
  • 该类只能按照本地平台的字符编码来写数据,用户不能指定其他的字符编码类型

4. BufferedWriter类是Writer类的子类( 基类:Writer —— 子类:BufferedWriter )

  • BufferedWriter类带有缓冲区
  • BufferedWriter常用的构造方法:BufferedWriter(Writer out)

5. write(String str) 方法示例:

public class TestWriter {
	public static void main(String[] args) {
		Writer fw = null;
		
		try {
			fw = new FileWriter("输出流自动创建文件.txt",true);   //运行后记得刷新,才会显示在project
			fw.write("北大青鸟\n");
//			fw.flush();            //flush()方法可以强制把缓冲区的数据写到输出流中
		} catch (IOException e) {
			e.printStackTrace();
		}finally {
			try {
				fw.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}

*BufferedWriter 示例(未测试):

六、二进制流(传输图片、音频、视频等文件)

1. DataInputStream类(注意:父类是 FilterInputStream,不是 FileInputStream)

  • 基类(抽象类):InputStream —— 子类:FilterInputStream —— 子类:DataInputStream
  • DataInputStream类与FileInputStream类结合使用读取二进制文件

2. DataOutputStream类(注意:父类是 FilterOutputStream,不是 FileOutputStream)

  • 基类(抽象类):OutputStream —— 子类:FilterOutputStream —— 子类:DataOutputStream
  • DataOutputStream类与FileOutputStream类结合使用写二进制文件

3. 利用二进制流复制音频文件示例:

public class TestDataStream {
	public static void main(String[] args) {	
		FileInputStream fis = null;
		FileOutputStream fos = null;
		
		//二进制流的包装类
		DataInputStream dis = null;
		DataOutputStream dos = null;
		
		try {
			fis = new FileInputStream("观药 - 溯×大雾×坠落星空.mp3");
			fos = new FileOutputStream("憨憨.mp3");
			dis = new DataInputStream(fis);
			dos = new DataOutputStream(fos);
			//复制
			int data = -1;
			while((data = dis.read()) != -1) {
				dos.write(data);
			}
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}finally {
			try {
				fos.close();
				fis.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}

七、序列化与反序列化(关键接口:Serializable

1. 序列化:序列化是将对象的状态写入到特定的流中的过程

步骤:实现 Serializable 接口 → 创建对象输出流 → 调用 writeObject() 方法将对象写入文件 → 关闭对象输出流(ObjectOutputStream 是 OutputStream 的子类)

注意:使用集合保存对象,可以将集合中的所有对象序列化

2. 反序列化:反序列化则是从特定的流中获取数据重新构建对象的过程

步骤:实现 Serializable 接口 → 创建对象输入流 → 调用 readObject() 方法读取对象 → 关闭对象输入流(ObjectInputStream 是 InputStream 的子类)

注意:如果向文件中使用序列化机制写入多个对象,那么反序列化恢复对象时,必须按照写入的顺序读取

3. 示例——创建示例类:

public class Student implements Serializable {   //序列化需要实现 Serializable 接口

	private String name;
	private String id;
	private double score;
	
	public Student(String name, String id, double score) {
		super();
		this.name = name;
		this.id = id;
		this.score = score;
	}
	public Student() {
        super();
    }

	@Override
	public String toString() {
		return "Student [name=" + name + ", id=" + id + ", score=" + score + "]";
	}

	public String getName() {return name;}
	public void setName(String name) {this.name = name;}
	public String getId() {return id;}
	public void setId(String id) {this.id = id;}
	public double getScore() {return score;}
	public void setScore(double score) {this.score = score;}
}

序列化和反序列化对象示例:

public class TestObjectStream {

//	private transient //隐藏属性,序列化后会被隐藏

	public static void main(String[] args) {	
		ObjectInputStream ois = null;   //对象输入流
		ObjectOutputStream oos = null;   //对象输出流
		//字节输入输出流作为对象输入输出流的参数
		InputStream is = null;
		OutputStream os = null;
		
		//1.创建对象
		Student zhangsan = new Student("1001","张三",99);
		try {
			os = new FileOutputStream("student.txt");
			oos = new ObjectOutputStream(os);
			//2.将对象写入文件(序列化)
			oos.writeObject(zhangsan);
			oos.flush();               //flush()方法输出---------------------------
			//3.反序列化读取对象信息
			is = new FileInputStream("student.txt");
			ois = new ObjectInputStream(is);
			//读取并打印对象信息
			Student stu = (Student) ois.readObject();
			System.out.println(stu.toString());
			
			System.out.println("写入对象成功!");
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}finally {
			try {
				oos.close();
				os.close();
				ois.close();
				is.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}

序列化和反序列化集合示例:

public class TestObjectStream {
	public static void main(String[] args) {
		ObjectInputStream ois = null;   //对象输入流
		ObjectOutputStream oos = null;   //对象输出流
		//字节输入输出流作为对象输入输出流的参数
		InputStream is = null;
		OutputStream os = null;
		
		//1.创建对象和集合
		Student zhangsan = new Student("1001","张三",99);
		Student lisi = new Student("1002","李四",96);
		Student wangwu = new Student("1003","王五",97);
		ArrayList<Student> stus = new ArrayList<>();
		stus.add(zhangsan);
		stus.add(lisi);
		stus.add(wangwu);
		
		try {
			os = new FileOutputStream("students.txt");
			oos = new ObjectOutputStream(os);
			//2.将对象写入文件(序列化)
			oos.writeObject(stus);
			oos.flush();               //flush()方法输出---------------------------
			//3.反序列化读取对象信息
			is = new FileInputStream("students.txt");
			ois = new ObjectInputStream(is);
			//读取并打印对象信息
			ArrayList<Student> students = (ArrayList<Student>) ois.readObject();
			for (Student student : students) {
				System.out.println(student.toString());
			}
			
			System.out.println("写入对象成功!");
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}finally {
			try {
				oos.close();
				os.close();
				ois.close();
				is.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值