Java IO流

目录

 

File

FileOutputStream

FileInputStream

单字节文件复制的实现(效率低)

批量字节文件复制的实现(效率高)

IO流中的序列化和反序列化

存储序列化对象到文件

深入理解ObjectInputStream和FileOutputStream,ObjectInputStream和FileInputStream

不被序列化的成员

序列化版本

字符流OutputStreamWriter和InputStreamReader

BufferedReader和BufferedWriter


File

Java文件类以抽象的方式代表文件名和目录路径名。该类主要用于文件和目录的创建、文件的查找和文件的删除等。

File对象代表磁盘中实际存在的文件和目录。

File中常用的几个方法:

public class Test {

	public static void main(String[] args) throws IOException {
		fileTest();
		
//		createFile();
	}

	private static void createFile() throws IOException {
		
		String pathString="/Users/sujianda/Desktop/";
		//创建文件 
		File dirFile=new File(pathString,"aa");
		dirFile.createNewFile();
		
		//创建文件夹
		File dirFile2=new File("/Users/sujianda/Desktop/bb");
		dirFile2.mkdir();
		
		//创建文件夹 多层
		File dirFile3=new File("/Users/sujianda/Desktop/cc/dd/ee");
		dirFile3.mkdirs();
		
	}

	private static void fileTest() {
		//测试file类型数据的方法
		
		String pathString="/Users/sujianda/Desktop/kotlin.docx";
		
		File file=new File(pathString);

		String []nameStrings=file.list();//获取目录下所有文件名
		File[]files=file.listFiles();//获取目录下所有文件
		
		System.out.println("文件名字:"+file.getName());
		System.out.println("文件父目录:"+file.getParent());
		System.out.println("文件完整路径:"+file.getAbsolutePath());
		System.out.println("文件是否存在:"+file.exists());
		System.out.println("是否是文件:"+file.isFile());
		System.out.println("是否是文件夹:"+file.isDirectory());
		System.out.println("文件字节量:"+file.length());
		System.out.println("文件最后修改时间:"+file.lastModified());
		
		Date date=new Date(file.lastModified());
        SimpleDateFormat sdf =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS" );
        String str = sdf.format(date);
        System.out.println("文件最后修改时间: "+str);

	}
}

FileOutputStream

FileInputStream

字节输出流FileOutputStream和字节输入流FileInputStream,分别继承OutputStream和InputStream,都是以字节的形式进行读写

public class Test4 {

	public static void main(String[] args) throws IOException {
		
		String pathString="/Users/sujianda/Desktop/test";
		
		write(pathString);//写字节数据到文件
		read(pathString);//从文件读取字节
		read2(pathString);//从文件读取字节
	}

	private static void write(String pathString)throws IOException  {
		//写字节到文件
		//文件不存在则创建,存在则覆盖,文件夹不存在则报错
		
		FileOutputStream outputStream=new FileOutputStream(pathString);
		
		outputStream.write(97);
		
		byte a[]= {101,102,103,104,105,106,107,108,109,127};
		outputStream.write(a,0,10);
		
		outputStream.close();//释放资源
	}
	
	private static void read(String pathString) throws IOException {
		FileInputStream inputStream=new FileInputStream(pathString);
		int i;
		//-1表示读取完毕
		while((i=inputStream.read())!=-1) {
			System.out.println(i);
		}
		
		inputStream.close();
	}

	private static void read2(String pathString) throws IOException {
		FileInputStream inputStream=new FileInputStream(pathString);
		
		byte[] a=new byte[1024];
		System.out.println("字节数组长度"+a.length);
		int j;
		while((j=inputStream.read(a))!=-1) {
			inputStream.read(a, 0, j);
		}
		System.out.println("字节数组内容"+Arrays.toString(a));
		
		inputStream.close();
	}
}

这里有两个方法需要解释一下:

  • read()//单个字节读取,读取结束之后,返回-1
  • read(byte[])//按照数组长度进行读取,读取一批数据,存到数组中,并且返回这一批字节的数量,读取结束之后,返回-1

用上面的代码进行举例:

	private static void read2(String pathString) throws IOException {
		FileInputStream inputStream=new FileInputStream(pathString);
		
		byte[] a=new byte[5];
		System.out.println("字节数组长度"+a.length);
		int j;//用来保存每一批读取的数量
		while((j=inputStream.read(a))!=-1) {
			System.out.println(j+"个:"+Arrays.toString(a));
		}
		System.out.println("字节数组内容"+Arrays.toString(a));
		
		inputStream.close();
	}

执行结果:由此可见j表示每次读取数量,直到最后一次

101
102
103
104
105
106
107
108
109
127
字节数组长度5
5个:[97, 101, 102, 103, 104]
5个:[105, 106, 107, 108, 109]
1个:[127, 106, 107, 108, 109]
字节数组内容[127, 106, 107, 108, 109]

如果把数组长度设置大于或者等于字节总数的话:


	private static void read2(String pathString) throws IOException {
		FileInputStream inputStream=new FileInputStream(pathString);
		
		byte[] a=new byte[11];
		System.out.println("字节数组长度"+a.length);
		int j;//用来保存每一批读取的数量
		while((j=inputStream.read(a))!=-1) {
			System.out.println(j+"个:"+Arrays.toString(a));
		}
		System.out.println("字节数组内容"+Arrays.toString(a));
		
		inputStream.close();
	}

执行结果:

97
101
102
103
104
105
106
107
108
109
127
字节数组长度11
11个:[97, 101, 102, 103, 104, 105, 106, 107, 108, 109, 127]
字节数组内容[97, 101, 102, 103, 104, 105, 106, 107, 108, 109, 127]

总结:如果数组长度小于文件的总字节数,按照数组长度进行循环读取,并进行覆盖之前的数据;


单字节文件复制的实现(效率低)

		System.out.println("请输入原始文件");
		String s1=new Scanner(System.in).next();
		File fromFile=new File(s1);
		if(!fromFile.isFile()) {
			System.out.println("请输入正确文件");
			return;
		}
		
		System.out.println("请输入目标文件");
		String s2=new Scanner(System.in).next();
		File toFile=new File(s2);
		if(!toFile.isFile()) {
			System.out.println("请指定具体文件");
			return;
		}
		
		try {
			copy(fromFile,toFile);//但字节文件复制
			System.out.println("复制完成");
		} catch (Exception e) {
			// TODO: handle exception
			System.out.println("操作失败");
			e.printStackTrace();
		}
		
	}

	private static void copy(File fromFile,File toFile)throws IOException  {
		//但字节文件复制,效率低
		FileInputStream fileInputStream=new FileInputStream(fromFile);//原始文件是输入流
		FileOutputStream fileOutputStream=new FileOutputStream(toFile);//输出流到目标文件

		int n;
		while((n=fileInputStream.read())!=-1) {
			fileOutputStream.write(n);
		}
		
		fileInputStream.close();
		fileOutputStream.close();
	}

批量字节文件复制的实现(效率高)

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

		System.out.println("请输入原始文件");
		String s1=new Scanner(System.in).next();
		File fromFile=new File(s1);
		if(!fromFile.isFile()) {
			System.out.println("请输入正确文件");
			return;
		}
		
		System.out.println("请输入目标文件");
		String s2=new Scanner(System.in).next();
		File toFile=new File(s2);
		if(toFile.isDirectory()) {
			System.out.println("请指定具体文件");
			return;
		}
		
		try {
			copy(fromFile,toFile);//但字节文件复制
			System.out.println("完成");
		} catch (Exception e) {
			// TODO: handle exception
			System.out.println("失败");
			e.printStackTrace();
		}
		
	}

	private static void copy(File fromFile,File toFile)throws IOException  {
		//批量字节文件复制,效率高
		FileInputStream fileInputStream=new FileInputStream(fromFile);//原始文件是输入流
		FileOutputStream fileOutputStream=new FileOutputStream(toFile);//输出流到目标文件

		int n;
		byte[]fileByte=new byte[8192];//数组8192个长度是最优数
		
		while((n=fileInputStream.read(fileByte))!=-1) {
			fileOutputStream.write(fileByte,0,n);
		}
		
		fileInputStream.close();
		fileOutputStream.close();
	}

IO流中的序列化和反序列化

在java中,要实现对象的序列化,必须实现Serializable,它是一个空接口(标识接口),只需继承实现;

  • 一个model类只有Serializable接口,才能对其实现序列化
  • 序列化是将对象状态转换为可保持或传输的格式(按照一定的格式,转化成一串字节序列)的过程;
  • 反序列化是把字节序列恢复为对象的过程称为对象;
  • 通过序列化和反序列化,可以实现对象的传递,存储和传输读取数据;
  • 对象的输入流用ObjectInputStream,对象到输出流用ObjectOutputStream

存储序列化对象到文件

深入理解ObjectInputStream和FileOutputStream,ObjectInputStream和FileInputStream

  • 首先对对象进行序列化
public class StudentModel implements Serializable{

	private String name;
	private String gender;
	private int age;
	private int number;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getGender() {
		return gender;
	}
	public void setGender(String gender) {
		this.gender = gender;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public int getNumber() {
		return number;
	}
	public void setNumber(int number) {
		this.number = number;
	}
	
	@Override
	public String toString() {
		return "名字是:"+getName()
		+"  学号是:"+getNumber()
		+"  性别是:"+getGender()
		+"  年龄是:"+getAge();
	}
  • 然后创建文件输出流FileOutputStream outputStream = new FileOutputStream("/Users/sujianda/Desktop/student.txt");
  • 然后创建对象输出流ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);

注:这里需要仔细主要流程

public class Test7 {

	public static void main(String[] args) {

		// 创建文件
		create();

		// 输出对象到文件中
		outToFile();

		// 读取文件内容到控制台
		readFile();
	}

	private static void create() {
		File file = new File("/Users/sujianda/Desktop/student.txt");
		try {
			file.createNewFile();

		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	private static void outToFile() {
		StudentModel studentModel = new StudentModel();
		studentModel.setName("苏苏");
		studentModel.setGender("女");
		studentModel.setAge(23);
		studentModel.setNumber(120331);

		try {
			// 创建文件输出流,到目标文件
			FileOutputStream outputStream = new FileOutputStream("/Users/sujianda/Desktop/student.txt");

			// 创建对象输出流,
			ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);

			// 把Student对象通过对象输出流写到文件
			objectOutputStream.writeObject(studentModel);
			objectOutputStream.close();

		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	private static void readFile() {
		try {
			ObjectInputStream fInputStream = new ObjectInputStream(
					new FileInputStream("/Users/sujianda/Desktop/student.txt"));
			StudentModel s1 = (StudentModel) fInputStream.readObject();
			System.out.println(s1.toString());
			fInputStream.close();
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}
}

执行输出结果:

  • 文件输出流new FileInputStream("/Users/sujianda/Desktop/student.txt")获取文件地址;
  • 对象输出流ObjectInputStream反序列化读取数据赋值给对象;

ObjectInputStream fInputStream = new ObjectInputStream(

new FileInputStream("/Users/sujianda/Desktop/student.txt"));

StudentModel s1 = (StudentModel) fInputStream.readObject();

不被序列化的成员

  • 静态成员 static不会序列化
  • 变量修饰符transient修饰的成员(只在程序运行期间,在内存中临时存在),不随对象一起序列化

假如上面StudentModel中的成员变量这样修饰

	private transient String name;
	private static String gender;
	private static int age;
	private int number;

在另一个Test8中执行输出

public class Test8 {

	public static void main(String[] args) {
		// 读取文件内容到控制台
		readFile();
	}

	private static void readFile() {
		try {
			ObjectInputStream fInputStream = new ObjectInputStream(
					new FileInputStream("/Users/sujianda/Desktop/student.txt"));
			StudentModel s1 = (StudentModel) fInputStream.readObject();
			System.out.println(s1.toString());
			fInputStream.close();
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}
}

执行结果是:



序列化版本

  • 为了保持版本的兼容性,即在版本升级时反序列化仍保持对象的唯一性
  • 如果自己不定义,默认会根据类的定义信息,计算产生添加一个ID值,如下 Add generated serial version ID,它会随着类中数据的变化而变化,也可以自己加,自己修改

举个例子 原始版本是1L,通过运行上面的test7,进行赋值;

public class StudentModel implements Serializable{

	/**
	 * 原始版本
	 */
	private static final long serialVersionUID = 1L;
	
	private String name;
	private String gender;
	private int age;
	private int number;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getGender() {
		return gender;
	}
	public void setGender(String gender) {
		this.gender = gender;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public int getNumber() {
		return number;
	}
	public void setNumber(int number) {
		this.number = number;
	}
	
	@Override
	public String toString() {
		return "名字是:"+getName()
		+"  学号是:"+getNumber()
		+"  性别是:"+getGender()
		+"  年龄是:"+getAge();
	}
	
}

然后手动修改版本成2L,运行Test8,此时就会报错,两边版本不一致,无法恢复数据;


字符流OutputStreamWriter和InputStreamReader

  • 分别继承于Writer和Reader类(抽象父类)
  • 方法1 write(int c);  int是4个字节,末尾2个字节是char类型字符数据,只处理末尾两个字节的输出;
  • 方法2 write(char [],from,length);输出从from开始的length长度的字符数;
  • 方法3 write(String s);输出字符串的全部字节
  • InputStreamReader 读取其他编码转换成java 的Unicode编码;
  • 把java 的Unicode编码转换成其他编码进行输出;

举例,实现按照规定编码格式,把数据进行文件写入,并从文件读取到控制台

//利用字符流进行编码转换,输出到文件上
public class Test9 {
	public static void main(String[] args) {
		String s="AaBbCc我是大帅哥123456";
		
		toGBK(s);//GBK编码格式写入
		toUTF8(s);//UTF-8编码格式写入
		
		readFromGBK();//读取
		System.out.println();
		readFromUTF8();
	}

	private static void toGBK(String s) {
		try {
			File file=new File("/Users/sujianda/Desktop/test_GBK");
			FileOutputStream out = new FileOutputStream(file);
			OutputStreamWriter outputStreamWriter=new OutputStreamWriter(out,"GBK");
			outputStreamWriter.write(s);
			outputStreamWriter.close();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	private static void toUTF8(String s) {

		try {
			File file=new File("/Users/sujianda/Desktop/test_utf8");
			FileOutputStream out = new FileOutputStream(file);
			OutputStreamWriter outputStreamWriter=new OutputStreamWriter(out,"UTF-8");
			outputStreamWriter.write(s);
			outputStreamWriter.close();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	private static void readFromUTF8() {
		try {
			FileInputStream in=new FileInputStream("/Users/sujianda/Desktop/test_utf8");
			
			InputStreamReader inputStreamReader=new InputStreamReader(in,"UTF-8");
			
			int n;
			while((n=inputStreamReader.read())!=-1) {
				System.out.print(n+"  ");
			}
			
			inputStreamReader.close();
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	private static void readFromGBK() {
		try {
			FileInputStream in=new FileInputStream("/Users/sujianda/Desktop/test_GBK");
			
			InputStreamReader inputStreamReader=new InputStreamReader(in,"GBK");
			
			int n;
			while((n=inputStreamReader.read())!=-1) {
				System.out.print(n+"  ");
			}
			
			inputStreamReader.close();
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

BufferedReader和BufferedWriter

  • 为了提高字符流读写的效率,引入了缓冲机制,进行字符批量的读写,提高了单个字符读写的效率。BufferedReader用于加快读取字符的速度,BufferedWriter用于加快写入的速度

    BufferedReader和BufferedWriter类各拥有8192个字符的缓冲区。当BufferedReader在读取文本文件时,会先尽量从文件中读入字符数据并放满缓冲区,而之后若使用read()方法,会先从缓冲区中进行读取。如果缓冲区数据不足,才会再从文件中读取,使用BufferedWriter时,写入的数据并不会先输出到目的地,而是先存储至缓冲区中。如果缓冲区中的数据满了,才会一次对目的地进行写出。

    BufferedReader
    BufferedReader是为了提供读的效率而设计的一个包装类,它可以包装字符流。可以从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。

  • reader.readLine()方法返回的一行字符中不包含换行符,所以输出的时候要自己加上换行符

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小小苏的小小苏

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值