JavaIO流

IO流的分类

在这里插入图片描述

  1. 按照流的方向进行分类

以内存作为参照物:输入流,输出流

  • 往内存中去,叫做输入(Input)。或者叫做读(Read)
  • 从内存中出来,叫做输出(Output)。或者叫做写(write)

  1. 按照读取数据方式不同进行分类

以读取字符的不同:字节流,字符流

  • 有的流是按照字节的方式读取数据,一次读取1个字节byte,等同于一次读取8个二进制位。这种流是万能流,什么类型的文件都可以读取。包括:文本文件,图片,声音文件,视频文件等.
  • 有的流是按照字符的方式读取数据的,一次读取一个字符,这种流是为了方便读取普通文本文件而存在的,这种流不能读取:图片,声音,视频等文件。只能读取纯文本文件,连word文件都无法读取

假设文件file1.txt,采用字符流的话是这样读的:
a中国bc张三fe
第一次读:'a’字符('a’字符在windows系统中占用1个字节)
第二次读:'中’字符('中’字符在windows系统中占用2个字节)


假设文件file1.txt,采用字节流的话是这样读的:
a中国bc张三fe
第一次读:一个字节,正好读到’a’
第二次读:一个字节,正好读到’中’字符的一半
第三次读:一个字节,正好读到’中’字符的另外一半

’a’英文字母,在Windows操作系统当中是一个字节,但是’a’字符在Java中占用两个字节;file1.txt文本和Java没关系,它是Windows操作系统上的普通文件;字节流直接读取的是8个二进制位,字符流可以一个字符一个字符检测出来

Java中的IO流都已经写好了,我们程序员不需要关心;Java中的所有流都是在:java.io.*;下

Java IO流有四大家族:(都是抽象类abstract
java.io.InputStream 字节输入流
java.io.OutputStream 字节输出流

java.io.Reader 字符输入流
java.io.Writer 字符输出流

所有的流都实现了:java.io.Closeable接口,都是可关闭的,都有close()。流毕竟是一个管道,这个是内存和硬盘之间的通道,用完之后一定要关闭,不然会耗费(占用)很多资源。养成好习惯,用完流一定要关闭。

注意:在Java中只要“类名”以Stream结尾的都是字节流。以“Reader/Writer”结尾的都是字符流

所有的输出流都实现了:java.io.Flushable接口,都是可刷新的,都有flush()方法。养成一个好习惯,输出流在最终输出之后,一定要记得flush()刷新一下。这个刷新表示将通道/管道当中剩余未输出的数据强行输出完(清空管道!)刷新的作用就是清空管道
注意:如果没有flush()可能会导致丢失数据

java.io包下需要掌握的流有16个:

文件专属:

  • java.io.FileInputStream
  • java.io.FileOutputStream
  • java.io.FileReader
  • java.io.FileWriter

转换流:(将字节流转换成字符流)

  • java.io.InputStreamReader
  • java.io.OutputStreamWriter

缓冲流专属:

  • java.io.BufferedReader
  • java.io.BufferedWriter
  • java.io.BufferedInputStream
  • java.io.BufferedOutputStream

对象专属流:

  • java.io.ObjectInputStream
  • java.io.ObjectOutputStream

标准输出流:

  • java.io.PrintWriter
  • java.io.PrintStream

数据流专属:

  • java.DataInputStream
  • java.io.DataOutputStream

java.io.FileInputStream

  • 文件字节输入流,万能的,任何类型的文件都可以采用这个流来读。
  • 字节的方式,完成输入的操作,完成读的操作(硬盘 —> 内存)

首先在硬盘某处创建一个文件,然后用FileInputStream去读取文件里面的内容

	public static void main(String[] args) throws IOException {
		// 创建文件字节输入流对象
		// 文件路径:D:\text.txt (Java编译器会自动把\变成\\,因为Java中\表示转义)
		// 以下都是采用了:绝对路径的方式
		// 写成这个 / 也是可以的
		FileInputStream fis = new FileInputStream("D:\\bbb\\test");
//		int readData = fis.read(); // 这个方法的返回值是:读取到的“字节”本身
//		System.out.println(readData);// 97
		int readData = 0;
		while(readData!=-1) {// 如果读到文件内容的末尾,后面没有值了,则返回-1
			readData = fis.read();
			System.out.println(readData);
		}
	}

分析上面的缺点:一次读取一个字节byte,这样内存和硬盘交互太频繁,基本上时间/资源耗费在交互上面了

// 相对路径:一定是从当前所在的位置作为起点开始找!
/*
	int read(byte[] b)
		一次最多读取 b.length个字节。
		减少硬盘和内存的交互,提高程序的执行效率。
		往byte[]数组当中读。
*/

	public static void main(String[] args) throws IOException {
		// 开始读,采用byte数组,一次读取多个字节。最多读取“数组.length”个字节
		byte[] b = new byte[4];// 准备一个4个长度的byte数组,一次最多读取4个字节
		// 这个方法的返回值是:读取到的字节数量。(不是字节本身)
		FileInputStream fis = new FileInputStream("D:\\bbb\\test");
		int readData = fis.read(b); // 这个方法的返回值是:读取到的“字节”本身
		System.out.println(readData);// 第一次读取到了4个字节
		readData = fis.read(b); // 这个方法的返回值是:读取到的“字节”本身
		System.out.println(readData);// 第二次只能读取到3个字节
		readData = fis.read(b); 
		System.out.println(readData);// 第三次一个字节都没有读取到,返回-1

		// 将字节数组全部转换成字符串
		System.out.println(new String(b,0,readData));// asdc
		// 不应该全部都转换,应该读取了多少字节,就转换多少字节

		readData = fis.read(b); // 这个方法的返回值是:读取到的“字节”本身
		System.out.println(readData);// efg
		System.out.println(new String(b,0,readData));
	}

最终版

	public static void main(String[] args) throws IOException {
		byte[] b = new byte[4];
		FileInputStream fis = new FileInputStream("D:\\bbb\\test.txt");
		int readCount = 0;
		while((readCount = fis.read(b))!=-1) {
			// 把byte数组转换成字符串,读到多少个就转多少个
			System.out.print(new String(b,0,readCount));
		}
		fis.close();
	}
}

FileInputStream类中的其他常用方法:

  • int available():返回流中当中剩余的没有读到的字节数量
  • long skip(long n):跳过几个字节不读。
public static void main(String[] args) throws IOException {
		FileInputStream fis = new FileInputStream("D:\\bbb\\test.txt");
		System.out.println("总字节数量:"+fis.available());
		byte[] bytes = new byte[fis.available()];// 这种方式不太适合太大的文件,因为byte[]数组不能太大。
		// 不需要循环了
		// 直接读一次就行了
//		int readCount = fis.read(bytes);
//		System.out.println(new String(bytes));
//		int readByte = fis.read();
//		System.out.println("剩下多少个字节:"+fis.available());
		
		// skip跳过几个字节不读取,这个方法也可能以后会用
		fis.skip(3);
		System.out.println(fis.read());
	}
	public static void main(String[] args){
		Flie file = new File("D://aaa");
		System.out.println(file.getName());// 返回由此抽象路径名表示的文件或目录的名称。(得到最后的文件名)(返回String类型)
		
		System.out.println(file.getPath());// 将此抽象路径名转换为一个路径名字符串。(得到完整的路径名)(返回String)
		
		System.out.println(file.exists());// 测试此抽象路径名表示的文件或目录是否存在。(new的文件名是否存在)(boolean类型)
		
		System.out.println(file.isFile());// 测试此抽象路径名表示的文件是否是一个标准文件。(boolean类型)
		
		System.out.println(file.isDirectory());// 测试此抽象路径名表示的文件是否是一个目录。
		
		if(!file.exists()){// 判断new里面文件是否存在
			file.createNewFile();// 当且仅当不存在具有此抽象路径名指定名称的文件时,不可分地创建一个新的空文件。
		}
		
		file.mkdir();// 创建此抽象路径名指定的目录。
		file.mkdirs();// 创建此抽象路径名指定的目录,包括所有必需但不存在的父目录。

		String[] list = file.list();
		for(String string : list) {
			System.out.println(string);
		}

		File[] listFiles = file.listFiles();
		for(File file2 : listFiles) {
			System.out.println(file2.getName());
		}

		System.out.println(file.length()); //得到文件里面内容的字符串长度
	}

java.io.FileIOutputStream

  • 文件字节输出流,负责写
  • 从内存到硬盘
public static void main(String[] args) throws IOException {
		// "D:\\bbb\\test1.txt" 文件不存在的时候会自动创建!
		// 这种方式谨慎使用,这种方式会先将原文件清空,然后重新写入
		FileOutputStream fos = new FileOutputStream("D:\\bbb\\test.txt");
		byte[] bytes = {97,98,99,100,101};
		fos.write(bytes);// 将bytes数组全部写出
		
		// 以追加的方式在文件末尾写入。不会清空原文件内容
		FileOutputStream fos1 = new FileOutputStream("D:\\bbb\\test1.txt",true);
		byte[] bytes1 = {97,98,99,100,101};
		fos1.write(bytes1);// 将bytes数组全部写出
		String s = "飒飒飒飒";
		byte[] b = s.getBytes();// 将字符串转化为byte数组
		fos1.write(b);// 将数组内容写入文件中
	}

exists(),isFile()和isDirectory()的区别

exists()

// 当这个aaa无论是文件夹还是文件
Flie file = new File("D://aaa");
// 只要存在这个aaa,输出就是true
System.out.println(file.exists());

isFile()

// 当这个aaa是文件
Flie file = new File("D://aaa");
// 存在的这个aaa是文件时输出才是true
System.out.println(file.isFile());

isDirectory()

// 当这个aaa是文件夹时
File file = new File("D://aaa");
// 存在的这个aaa是文件夹时输出才是true
System.out.println(file.isDirectory());

mkdir()和mkdirs()的区别

mkdirs()可以建立多级文件夹,mkdir()只会建立一级的文件夹,如下:
对于mkdir()

new File("/tmp/one/two/three").mkdir();

执行后,不会建立任何目录,因为找不到/tmp/one/two目录,结果返回false

对于mkdirs()

new File("/tmp/one/two/three").mkdirs();

执行后,会建立/tmp/one/two/three四级目录

字符流和字节流

字符流的由来: 因为数据编码的不同,而有了对字符进行高效操作的流对象。本质其实就是基于字节流读取时,去查了指定的码表。 字节流和字符流的区别:

  • 读写单位不同:字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节。
  • 处理对象不同:字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符类型的数据。
  • 字节流:一次读入或读出是8位二进制。
  • 字符流:一次读入或读出是16位二进制。

设备上的数据无论是图片或者视频,文字,它们都以二进制存储的。二进制的最终都是以一个8位为数据单元进行体现,所以计算机中的最小数据单元就是字节。意味着,字节流可以处理设备上的所有数据,所以字节流一样可以处理字符数据。

结论:只要是处理纯文本数据,就优先考虑使用字符流。 除此之外都使用字节流。

输入流和输出流

输入流只能进行读操作,输出流只能进行写操作,程序中需要根据待传输数据的不同特性而使用不同的流。

输入字节流 InputStream

  • InputStream 是所有的输入字节流的父类,它是一个抽象类。
  • ByteArrayInputStream、StringBufferInputStream、FileInputStream
    是三种基本的介质流,它们分别从Byte 数组、StringBuffer、和本地文件中读取数据。
  • PipedInputStream 是从与其它线程共用的管道中读取数据,与Piped 相关的知识后续单独介绍。
  • ObjectInputStream 和所有FilterInputStream 的子类都是装饰流(装饰器模式的主角)。

输出字节流 OutputStream

  • OutputStream 是所有的输出字节流的父类,它是一个抽象类。
  • ByteArrayOutputStream、FileOutputStream 是两种基本的介质流,它们分别向Byte数组、和本地文件中写入数据。
  • PipedOutputStream 是向与其它线程共用的管道中写入数据。
  • ObjectOutputStream 和所有FilterOutputStream 的子类都是装饰流。

总结:
输入流:InputStream或者Reader:从文件中读到程序中;
输出流:OutputStream或者Writer:从程序中输出到文件中;

字节流

使用FileInputStream + FileOutputStream完成文件的拷贝。拷贝的过程应该是一边读,一边写。使用以上的字节流拷贝文件的时候,文件类型随意,万能的,什么类型的文件都能拷贝

public class Demo03 {
	public static void main(String[] args) throws IOException {
		File f1 = new File("D:\\a\\a.png");
		File f2 = new File("D:\\b\\b.png");
		if(!f2.exists()) {
			f2.createNewFile();
		}
		
		FileInputStream fis = new FileInputStream(f1);// 创建一个输入流对象
		FileOutputStream fos = new FileOutputStream(f2);// 创建一个输出流对象
		byte[] b = new byte[1024];// 1KB(一次最多拷贝1KB)
//		System.out.println(Arrays.toString(b));
//		int i = fis.read(b); // i代表读取了多少有效字节
//		System.out.println(i + Arrays.toString(b));
//		i = fis.read(b); // i代表读取了多少有效字节
//		System.out.println(i + Arrays.toString(b));
		
		int i = 0;
		while((i = fis.read(b))!=-1) { // 读取输入流的数据,放到b中
			fos.write(b, 0, i); //从0~i,将读到的字节放入b中
		}
		fis.close();// 关闭数据源
		fos.close();// 关闭数据源
	}
}

字符流

FileReader:文件字符输入流,只能读取普通文本。读取文本内容时,比较方便,快捷
一次读取一个字符,用强制转换(int 转 char)

public class Demo04 {
	public static void main(String[] args) throws IOException {
		File file = new File("D:\\a\\aa.txt");
		FileReader fr = new FileReader(file);
		
		int i = -1;
		while((i = fr.read())!=-1) {
			char c = (char)i;
			System.out.print(c);// 一次读取一个字符
		}
		fr.close();
//		int i = fr.read();
//		char c = (char)i;
//		System.out.println(c);
	}
}

创建一个byte类型数组,一次读取4个字符

public static void main(String[] args) throws IOException {
		FileReader fr = new FileReader("D:\\a\\aa.txt");
		char[] cr = new char[4];
//		fr.read(cr);// 按照字符的方式读取
//		for(char c : cr) {
//			System.out.println(c);
//		}
		
		int readCount = 0;
		while((readCount = fr.read(cr))!=-1) {
			System.out.println(new String(cr,0,readCount));// 一次读取4个字符fr.close();
		}
		fr.close();
	}

在创建好的文本里面加上内容,且在控制界面显示出来

public class Demo05 {
	public static void main(String[] args) throws IOException {
		File file = new Flie("D:\\aaa\\b.txt");
		if(!file.exists()){
			file.createNewFile();
		}
		FileWriter fw = new FileWriter(file);
		String s = "啊吧啊吧";
		fw.write();
		fw.close();
		FileReader fr = new FileReader(file);
		int i = 0;
		while((i = fr.read())!=-1){
			char c = (char)i;
			System.out.print(c);
		}
		fr.close();
	}
}

FileWriter:文件字符输出流,写。只能输出普通文本。

public static void main(String[] args) throws IOException {
		FileWriter fw = new FileWriter("D:\\a\\aa.txt");
		char[] chars = {'我','是','中','国','人'};
		fw.write(chars);
		fw.write(chars, 2, 3);
		// 写入一个换行符
		fw.write("\n");
		fw.write("中国人");
		String s = "asd";
		fw.write(s);
		fw.close();
	}

BufferedReader:带有缓冲区的字符输入流。使用这个流的时候不需要自定义char数组,或者说不需要自定义byte数组,自带缓冲。

public static void main(String[] args) throws IOException {
		FileReader fr = new FileReader("F:\\java\\代码\\com.blb.cn\\src\\com\\blb\\text7\\Demo10.java");
		// 当一个流的构造方法中需要一个流的时候,这个被传进来的流叫做:节点流。
		// 外部负责包装的这个流,叫做:包装流,还有一个名字叫做:处理流。
		// 像当前这个程序来说:FileReader就是一个节点流。BufferedReader就是包装流/处理流。
		BufferedReader br = new BufferedReader(fr);
		
		// 读一行
//		String firstLine = br.readLine();
//		System.out.println(firstLine);
		
		String s =null;
		while((s = br.readLine())!=null) {
			System.out.println(s);
		}
		
		// 关闭流
		// 对于包装流来说只需要关闭最外层流就行,里面的节点流会自动关闭。
		br.close();
	}
public class Demo06 {
	public static void main(String[] args) throws IOException {
		File file = new File("D:\\aaa\\c.txt");
//		if(!file.exists()) {
//			file.createNewFile();
//		}
//		FileWriter fw = new FileWriter(file);
//		String s = "飒飒飒飒";
//		fw.write(s);
//		fw.close();
		FileReader fr = new FileReader(file);
		BufferedReader br = new BufferedReader(fr);
		String s = null;
		while((s = br.readLine())!=null) {
			System.out.println(s);
		}
//		关闭流,先关闭外面,再关闭里面
		br.close();
		fr.close();
		
	}
}

转换流:InputStreamReader

public static void main(String[] args) throws IOException {
		
//		// 字节流
//		FileInputStream in = new FileInputStream("F:\\java\\代码\\com.blb.cn\\src\\com\\blb\\text7\\Demo10.java");
//		
//		// 通过转换流转换(InputStreamReader将字节流转换成字符流。)
//		// in是节点流。reader是包装流。
//		InputStreamReader reader = new InputStreamReader(in);
//		
//		// 这个构造方法只能传一个字符流。不能传字节流
//		// reader是节点流。br是包装流
//		BufferedReader br = new BufferedReader(reader);
		
		// 将上述情况合并起来
		BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("F:\\java\\代码\\com.blb.cn\\src\\com\\blb\\text7\\Demo10.java")));
		
		String s = null;
		while((s = br.readLine())!=null) {
			System.out.println(s);
		}
		
		// 关闭最外层
		br.close();
	}

BufferedWriter:带有缓冲的字符输出流

public static void main(String[] args) throws IOException {
		// 带有缓冲区的字符输出流
		BufferedWriter bw = new BufferedWriter(new FileWriter("D:\\a\\aaa.txt"));
		bw.write("hello world");
		bw.write("\n");
		bw.write("hello kitty!");
		
		bw.close();
	}

OutputStreamWriter:转化流

public static void main(String[] args) throws IOException {
		BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("D:\\a\\aaa.txt")));
		bw.write("hello world11");
		bw.write("\n");
		bw.write("hello kitty!");
		
		bw.close();
	}

FileReader + FileWriter:一边读一边写

能用记事本打开的都是普通文本文件

public static void main(String[] args) throws IOException {
		FileReader fr = new FileReader("F:\\java\\代码\\com.blb.cn\\src\\com\\blb\\text7\\Demo02.java");
		FileWriter fw = new FileWriter("F:\\java\\代码\\com.blb.cn\\src\\com\\blb\\text7\\Demo10.java");
		char[] cr = new char[1024 * 512];
		int readCount = 0;
		while((readCount = fr.read(cr))!=-1){
			System.out.println(new String(cr, 0 ,readCount));
			fw.write(cr, 0, readCount);
		}
		fw.close();
		fr.close();
	}

java.io.DataOutputStream:数据专属的流

这个流可以将数据连同数据的类型一并写入文件。(注意:这个文件不是普通文本文档(这个文件使用记事本打不开))

public static void main(String[] args) throws IOException {
		// 创建数据专属的字节输出流
		DataOutputStream dos = new DataOutputStream(new FileOutputStream("D:\\a\\data"));
		// 写数据
		byte b = 100;
		short s = 200;
		int i = 300;
		long l = 400L;
		float f = 3.0F;
		double d = 3.14;
		boolean sex = false;
		char c = 'a';
		// 写
		dos.writeByte(b);// 把数据以及数据类型一并写入到文件当中
		dos.writeShort(s);
		dos.writeInt(i);
		dos.writeLong(l);
		dos.writeFloat(f);
		dos.writeDouble(d);
		dos.writeBoolean(sex);
		dos.writeChar(c);
		
		dos.close();
	}

DataInputStream:数据字节输入流(自带加密文件)

DataOutputStream写的文件,只能使用DataInputStream去读。并且读的时候你需要提前知道写入的顺序。

读的顺序需要和写的顺序一致,才可以正常取出数据
读取对象里面的内容,可在控制面板显示出来

public static void main(String[] args) throws IOException {
		DataInputStream dis = new DataInputStream(new FileInputStream("D:\\a\\data"));
		// 开始读
		byte b = dis.readByte();
		short s = dis.readShort();
		int i = dis.readInt();
		long l = dis.readLong();
		float f = dis.readFloat();
		double d = dis.readDouble();
		boolean sex = dis.readBoolean();
		char c = dis.readChar();
		
		System.out.println(b);
		System.out.println(s);
		System.out.println(i+1000);
		System.out.println(l);
		System.out.println(f);
		System.out.println(d);
		System.out.println(sex);
		System.out.println(c);
	}

java.io.PrintStream:标准的字节输出流。默认输出到控制台

public static void main(String[] args) throws IOException {
		
		// 联合起来写
		
		System.out.println("hello World!");
		
		// 分开写
		PrintStream ps = System.out;
		ps.println("ddddd");
		ps.println("asdas");
		ps.println(1000);
		
		// 标准输出流不需要手动close关闭流
		// 可以改变标准输出流的输出方向
		/*
		 *	这些是之前System类使用过的方法和属性
		 *	System.gc();
		 *	System.currentTimeMillis();
		 *	PrintStream ps2 = System.out;
		 *	System.exit(0);
		 *	System.arraycopy(......);
		*/
		// 标准输出流不再指向控制台,指向‘a’文件
		PrintStream printStream = new PrintStream("D:\\a\\a");
		// 修改输出方法,将输出方法修改到‘a’文件
		System.setOut(printStream);
		// 再输出
		System.out.println("ddasdas");
		System.out.println("dasdasdas");
		System.out.println(123);
	}

java.io.PrintStream:日志工具

记录日志的方法

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

	public static void log(String msg) throws FileNotFoundException {
		// 指向一个日志文件
		PrintStream out = new PrintStream(new FileOutputStream("D:\\a\\log.txt",true));
		// 改变输出方向
		System.setOut(out);
		// 当前日期时间
		Date nowTime = new Date();
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss sss");
		String strTime = sdf.format(nowTime);
		System.out.println("");
		System.out.println(strTime + ":" + msg);
	}

测试代码

public static void main(String[] args) throws IOException {
		// 测试工具类是否好用
		Demo02.log("调用了System类的gc方法,建议启动垃圾回收");
		Demo02.log("用户尝试登录,验证失败");
	}

File
1.File类和四大家族没有关系,所以File类不能完成文件的读和写

2.File对象代表什么

  • 文件和目录路径名的抽象形式
  • C:\Drivers 这是一个File对象
  • C:\Drivers\Lan\Realtek\Readme.txt 也是File对象
  • 一个File对象有可能对应的是目录,也可能是文件
  • File只是一个路径名的抽象表示形式

3.需要掌握File类中常用的方法

public static void main(String[] args) throws Exception {
		// 创建一个File对象
		File f1 = new File("D:\\file");
		
		// 判断是否存在!
		System.out.println(f1.exists());
		
		// 如果D:\file不存在,则以文件的形式创建出来
//		if (!f1.exists()) {
//			f1.createNewFile();
//		}
		
		// 如果D:\file不存在,则以目录的形式创建出来
//		if (!f1.exists()) {
//			// 以目录的形式新建
//			f1.mkdir();
//		}
		
		// 可以创建多重目录
		File f2 = new File("D:\\A\\b\\c\\d\\e");
		if (!f2.exists()) {
			f2.mkdirs();
		}
		
		File f3 = new File("D:\\a\\b");
		// 获取文件的父路径
		String parentPath = f3.getParent();
		System.out.println(parentPath);// D:\a
		File parentFile = f3.getParentFile();
		System.out.println("获取绝对路径:"+parentFile.getAbsolutePath());// 获取绝对路径:D:\a
		File f4 = new File("asd.txt");
		System.out.println(f4.getAbsolutePath());// F:\java\代码\com.blb.con04\asd.txt
		
	}
public static void main(String[] args) throws Exception {
		File f1 = new File("D:\\a\\b\\c\\d\\e\\asd.txt");
		// 获取文件名
		System.out.println("文件名:"+f1.getName());// asd.txt
		
		// 判断是否是一个目录
		System.out.println(f1.isDirectory());// false
		
		// 判断是否是一个文件
		System.out.println(f1.isFile());// true
		
		// 获取文件最后一次修改时间
		long haomiao = f1.lastModified();// 这个毫秒是从1970年到现在的总毫秒数
		// 将毫秒数转换成日期
		Date time = new Date(haomiao);
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
		String strtime = sdf.format(time);
		System.out.println(strtime);
		
		// 获取文件大小
		System.out.println(f1.length());// 0
		
		
		// File[] listFiles()
		// 获取当前目录下所有的子文件
		File f = new File("D:\\物联网");
		File[] files = f.listFiles();
		// foreach
		for (File file : files) {
			System.out.println(file.getName());
		}
	}

对象的序列化和反序列化

在这里插入图片描述
ObjectInputStream和ObjectOutputStream是包装流

  • 1.Student对象不支持序列化
  • 2.参与序列化和反序列化的对象,必须实现Serializable接口
  • 3.注意:通过源码发现,Serializable只是一个标志接口:接口代码中什么都没有;作用是标志作用,Java虚拟机看到这个类实现了这个接口,可能会对这个类进行特殊待遇。Serializable这个标志接口是给Java虚拟机参考的,Java虚拟机看到这个接口之后,会为该类自动生成一个序列化版本号

一次序列化多个对象,可以将对象放到集合当中,序列化集合

序列化一个对象

public class Demo07 {
	public static void main(String[] args) throws Exception {
//		序列化
//		write();
		read();
	}
	
	public static void read() throws IOException, Exception {
		FileInputStream fos = new FileInputStream(new File("D:\\aaa\\c.txt"));
		ObjectInputStream in = new ObjectInputStream(fos);
		Student student = (Student) in.readObject();
		// Object obj = in.readObject();
		// System.out.println(obj);
		System.out.println(student);
		in.close();
		fos.close();
	}
	
	public static void write() throws IOException {
		Student s = new Student();
		s.setAge(18);
		s.setName("seven");
		
		FileOutputStream fos = new FileOutputStream(new File("D:\\aaa\\c.txt"));
		ObjectOutputStream out = new ObjectOutputStream(fos);
		out.writeObject(s);
		out.close();
		fos.close();
	}
}

Student对象

package com.blb.text7;

import java.io.Serializable;

public class Student implements Serializable{
	/**
	 * Student类不支持序列化,所以需要加上接口 --- Serializable
	 * Java虚拟机看到Serializable接口之后,会自动生成一个序列化版本号
	 * 这里没有手动写出来,Java虚拟机会默认提供这个序列化版本号。
	 */
	private static final long serialVersionUID = 1L;// 版本号
	private String name;
//	private transient int age = 18;// 被transient修饰的字段表示不进行序列化
	private int 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;
	}
	@Override
	public String toString() {
		return "Student [name=" + name + ", age=" + age + "]";
	}
	
}

序列化多个对象

参与序列化的ArrayList集合以及集合中的元素Demo2都需要实现 java.io.Serializable接口

package com;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

public class Demo1 implements Serializable{
	public static void main(String[] args) throws Exception, Exception {
		List<Demo2> userList = new ArrayList<Demo2>();
		userList.add(new Demo2(0, "zhangsan"));
		userList.add(new Demo2(1, "lisi"));
		userList.add(new Demo2(2, "wangwu"));
		
		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\aaa\\e.txt"));
		
		// 序列化一个集合,这个集合对象中放了很多其他对象。
		oos.writeObject(userList);
		
		oos.flush();
		oos.close();
	}
}

package com;

import java.io.Serializable;

public class Demo2 implements Serializable{
	private int no;
	private String name;
	
	public Demo2() {
	}
	
	public Demo2(int no, String name) {
		this.no = no;
		this.name = name;
	}
	
	public int getNo() {
		return no;
	}
	public void setNo(int no) {
		this.no = no;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}

	@Override
	public String toString() {
		return "Demo2 [no=" + no + ", name=" + name + "]";
	}
	
}

package com;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.List;


// 反序列化
public class Demo3 {
	public static void main(String[] args) throws Exception, Exception {
		ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\aaa\\e.txt"));
//		Object obj = ois.readObject();
//		System.out.println(obj instanceof List);
//		System.out.println(obj);
		List<Demo2> useList = (List<Demo2>)ois.readObject();
		for (Demo2 demo2 : useList) {
			System.out.println(demo2);
		}
		ois.close();
	}
}

transient:关键字表示游离的,不参与序列化

private transient String name;
  • Java虚拟机看到Serializable接口之后,会自动生成一个序列版本号。
  • 这里没有手动写出来,Java虚拟机回默认提供这个序列化版本号。
  • 过了很久,Student这个类源代码改动了
  • 源代码改动后,需要重新编译,编译之后生成了全新的字节码文件
  • 并且class文件再次运行的时候,Java虚拟器生成的序列化版本号也会发生相应的改变

java.io.InvalidClassException:
com.Demo2;
local class incompatible:
stream classdesc serialVersionUID = -4790717219540779585,(十年后)
local class serialVersionUID = -829247365871225098(十年前)

Java语言中采用什么机制来区分类的

  • 第一:首先通过类名进行比对,如果类名不一样,肯定不是同一个类
  • 第二:如果类名一样,再靠序列化版本号进行区别

甲编写了一个类:com.bjpowernode.java.bean.Student implements Serializable

乙编写了一个类:com.bjpowernode.java.bean.Student implements Serializable

不同的人编写了同一个,但“这两个类确实不是同一个类”。这个时候序列化版本就起上了作用了。对于Java虚拟机来说,Java虚拟机是可以区分开这两个类的,因为这两个类都实现了Serializable接口,都有默认的序列化版本号,他们的序列化版本号不一样。所以区分开了。(这是自动生成序列化版本号的好处)

自动生成版本号的缺陷是:一旦代码确定之后,不能进行后续的修改,因为只要修改,必然会重新编译,此时会生成全新的序列化版本号,这个时候Java虚拟机会认为这是一个全新的类

最终结论:凡是一个类实现了Serializable接口,建议给该类提供一个固定不变的序列化版本号。这样,以后这个类即使代码修改了,但是版本号不变,Java虚拟机会认为是同一个类。

private static final long SeriaVersionUID = 1561651856165L;
// Java虚拟机识别一个类的时候先通过类名,如果类名一致,则识别版本号

IO + Properties联合使用

  • IO流:文件的读和写
  • Properties:是一个Map集合,key和value都是String类型
  • 以后经常改变的数据,可以单独写到一个文件中,使用程序动态读取。将来只需要修改这个文件的内容,Java代码不需要改动,不需要重新编译,服务器也不需要重启。就可以拿到动态信息。
  • 类似于以上机制的这种文件被称为配置文件。并且当配置文件中的内容格式是:key=value 的时候,我们把这种配置文件叫做属性配置文件(key=value格式不要有空格)
  • Java规范中有要求:属性配置文件建议以.properties结尾,但不是必须的。这种以.properties结尾的文件在Java中被称为:属性配置文件。其中properties对象是专门存放属性配置文件内容的一个类。
    -属性配置文件 key值如果重复了,name值会覆盖

Properties

public class Demo08 {
	public static void main(String[] args) throws IOException {
		File file = new File("D:\\aaa\\d.properties");
		if(!file.exists()) {
			file.createNewFile();
		}
		
		Properties p = new Properties();
		p.setProperty("name", "seven"); // 设置值
		p.setProperty("age", "18"); // 设置值
		p.setProperty("sex", "男"); // 设置值
		
		FileOutputStream fos = new FileOutputStream(file);
		OutputStreamWriter osw = new OutputStreamWriter(fos,"utf-8");
		
		p.store(osw, "comment"); // 保存到文件
		fos.close();
		osw.close();	
	}
}

将Properties的值显示在控制面板上

public class Demo09 {
	public static void main(String[] args) throws IOException {
		File file = new File("D:\\aaa\\d.properties");
		FileInputStream fis = new FileInputStream(file);
		InputStreamReader ir = new InputStreamReader(fis,"utf-8");
		Properties p = new Properties();
		p.load(ir);
		System.out.println(p);
		ir.close();
		fis.close();
	}
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值