JavaSE之IO

本文详细介绍了Java中的IO流,包括FileInputStream、FileOutputStream、FileReader、FileWriter等,以及字节流与字符流的区别。文章还讨论了转换流InputStreamReader/OutputStreamWriter在处理不同编码的文本文件中的作用,BufferedReader和BufferedWriter的缓冲机制,以及对象序列化与反序列化。同时,提到了File类用于文件和文件夹的操作。
摘要由CSDN通过智能技术生成

概述

流是有起点和终点的有序字节序列

流的分类:
输入流/输出流, 是当前程序为参照点, 程序从外面读数据这是输入流, 把程序的数据保存到外面是输出流
字节流/字符流, 如果是以字节为单位处理流中的数据就是字节流, 如果是以字符为单位处理流中的数据就是字符流
节点流/处理流, 如果直接从设备(数据源)上读写数据就是节点流, 处理流是对节点流的包装

Stream单词结尾就是字节流类, 如果是以Reader结尾就是字符输入流, 以Writer单词结尾就是字符输出流

FileInputStream/FileOutputStream

以字节为单位读写文件内容

FileInputStream

读取文件的步骤

  1. 在当前程序和指定文件之间建立流通道
  2. 读取文件内容
  3. 关闭流通道

两种读取方式:一次读取一个字节,一次读取一个字节数组
三种异常处理方式:抛异常,try catch finally,try catch
实例1:一次读取一个字节,循环读取

public class Test03 {

	public static void main(String[] args) throws IOException {
		//1)在当前程序与指定的文件之间建立流通道,
		//读取d:/abc.txt文件的内容, 通过构造方法指定要访问的文件,如果文件不存在会抛出异常
		FileInputStream fis = new FileInputStream("d:/abc.txt");
		//文件内容: ABCabc
		
		//2) 读取文件内容, 
		//read()方法从文件中读取一个字节, 并把读到的字节返回, 读到文件末尾返回-1
		int cc = fis.read();		//65, A的码值
		while( cc != -1 ){
			//把读到的字节cc进行处理,  把cc转换为字符再打印, 因为当前文件中只有英文字符,一个字节就对应一个字符
			System.out.print(  (char)cc  );
			//继续读下个字节
			cc = fis.read( );
		}
		
		
		//3)关闭流通道
		fis.close();
	}

实例2:一次读取一个字节数组,读取很多字节保存到字节数组中

public static void main(String[] args) throws IOException {
		//1)在程序与读取的文件之间建立流通道
		FileInputStream fis = new FileInputStream("d:/abc.txt");
		//文件ABCabcABC
		
		byte[] bytes = new byte[4];
		
		//从流中读取很多字节, 保存到字节数组中, 返回读到的字节数,如果读到文件末尾,返回-1
		int len = fis.read(bytes);
		
		while(  len != -1 ){
			//从文件中读取了len个字节保存到了bytes数组中, 对len个字节进行处理
			//把读到的len个字节转换为字符串  new String(byte[]bytes , 0 , len)
			System.out.print( new String(bytes , 0 , len ));
			//继续读
			len = fis.read(bytes);
		}
		
		fis.close();
	}

实例3:处理异常的方式

	public static void main(String[] args) {
//		m1(); 			//一次读取一个字节, 手动关闭流, 异常处理
		m2();			//从文件中读取字节保存到字节数组中, 异常处理, 自动 关闭流
	}

	//从JDK7开始, 流可以自动关闭
	private static void m2() {
		try(		//try资源块,自动释放
				FileInputStream fis = new FileInputStream("d:/abc.txt");
				) {
			byte[] bytes = new byte[4];
			int len = fis.read(bytes);
			while( len != -1){
				System.out.print( new String(bytes , 0 ,len));
				len = fis.read(bytes);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}		
	}

	private static void m1() {		
		FileInputStream fis = null;
		try {
			fis = new FileInputStream("d:/abc.txt");
			
			int cc = fis.read();
			while( cc != -1 ){
				System.out.print( (char)cc );
				cc = fis.read();
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (fis != null) {
				try {
					fis.close();	//关闭流,释放系统资源	
				} catch (IOException e) {
					e.printStackTrace();
				}  			
			}
		}
		
	}

FileOutputStream

以字节为单位把数据保存到文件步骤

  1. 建立流通道, 通过构造方法的参数指定要访问的文件
  2. 保存数据
  3. 关闭流

可以一次写一个字节,也可以一次写一个字节数组,也可以写字节数组的某部分

	public static void main(String[] args) throws IOException {
		//1)建立流通道, 通过构造方法的参数指定要访问的文件
		//如果访问的文件不存在, 系统会创建一个新文件; v如果文件已存在, 会覆盖文件原来的内容
//		FileOutputStream fos = new FileOutputStream("d:/def.txt");
		//如果文件不存在就创建文件,如果文件已存在, 把内容保存到文件的后面, 即以追加的方式打开 文件
		FileOutputStream fos = new FileOutputStream("d:/def.txt" , true);
		
		//2) 保存数据
		//一次写一个字节
		fos.write( 65 ); 		//把65对应的字符A保存到文件中
		fos.write( 66 ); 		//把66对应的字符A保存到文件中
		fos.write( 67 ); 		//把67对应的字符A保存到文件中
		//换行, 在Windows操作系统中,换行需要使用\r\n两个字符
		fos.write('\r'); 		//回车, 13
		fos.write('\n');		//换行, 10
		//一次写一个字节数组
		byte[] bytes = "helloworld".getBytes();
		fos.write(bytes);    	//把bytes数组中所有的字节保存到文件中
		//换行
		fos.write(13);
		fos.write(10);
		//把字节数组的部分字节保存到文件中
		fos.write(bytes, 0, 2);
		
		//3) 关闭流
		fos.close();
	}

文件的复制

在进行文件复制时,一般都会选择字节数组进行复制,并且字节数组的长度是1024的偶数,因为如果复制的文件较大,以一个字节的方式复制较慢

FileReader/FileWriter

FileReader/FileWriter在读写方式上和FileInputStream/FileOutputStream是一样的套路

FileReader

以字符为单位读写文件内容,只能读写纯文本文件, 并且要求文本文件的编码格式与当前环境编码格式兼容,如果读写的文件都是英文,则不管编码格式是否一致,都可以读取,因为英文字符不管是GBK还是UTF8都是一个字节,
如果文件是GBK格式, 文件中有中文, 如果和当前编码格式不一致,读取时会出现乱码 ,因为汉字在GBK中占两个字节,在UTF-8中占三个字节,一般使用FileReader读取与当前环境编码一致的文件
一次读取一个字符,一次读取一个字符数组

FileWriter

保存数据时,只能把数据保存到和当前编码格式一致的文件中
和FileOutputStream一致:如果文件不存在, 会创建一个新的文件, 如果文件存在会覆盖原来的内容,文件不存在就创建, 文件已存在也可以用追加的方式打开
可以一次写一个字符,也可以一次写一个字符串,也可以一次写一个字符数组,也可以把字符串的一部分保存到文件中

InputStreamReader/OutputStreamWriter

FileReader/FileWriter只能读写与当前环境编码兼容的文本文件
如果文本文件与当前环境编码不兼容, 使用InputStreamReader/OutputStreamWriter转换流读写
InputStreamReader把字节流以指定的编码转换为字符流
OutputStreamWriter可以把字符转换为指定格式的字节流
转换流采用了适配器设计模式

public static void main(String[] args) throws IOException {
//		m1();  //使用InputStreamReader读取文件内容
		m2(); 	//使用OutputStreamWriter保存数据
	}

	//当操作的文件编码与当前环境编码不兼容, 使用OutputStreamWriter把字符以指定的编码转换为字节
	private static void m2() throws IOException {
		//把字符保存到d:/def.txt文件, 该文件是GBK编码, 当前环境是UTF8编码, 把字符转换为GBK格式再保存
		OutputStream out = new FileOutputStream("d:/def.txt", true );
		OutputStreamWriter osw = new OutputStreamWriter(out, "GBK");
		
		osw.write("\r\n");
		osw.write("当前的内容是使用转换流保存到文件中的, 工作区编码是UTF8, 该文件使用GBK编码 ");
		
		osw.close();
	}

	//当文本文件的编码与当前环境编码不兼容时, 使用InputStreamReader类读取
	private static void m1() throws IOException {
		//读取d:/def.txt文件, 该文件使用GBK编码,当前环境使用UTF8编码
		//在当前程序与指定的文件之间建立字节流通道
		InputStream in = new FileInputStream("d:/def.txt");
		//使用GBK编码,把in字节流中的数据转换为字符流
		InputStreamReader isr = new InputStreamReader(in, "GBK");
		
		int cc = isr.read();
		while( cc != -1){
			System.out.print((char)cc);
			cc = isr.read();
		}
		
		isr.close();
	}

BufferedReader/BufferedWriter

在这里插入图片描述


	public static void main(String[] args) throws IOException {
//		m1(); 		//使用BufferedReader读取文本文件内容
//		m2(); 		//使用BufferedWriter保存文本到文件
		m3();		//从键盘上输入文本,把这些文本保存到文件中
	}

	private static void m3() throws IOException {
		BufferedWriter bw = new BufferedWriter(new FileWriter("d:/xyz.txt"));
		//使用BufferedReader对键盘输入流缓冲
//		System.in是标准的输入设备,即键盘
//因为System.in是InputStream字节流,应该将字节流转换成字符流,然后作为BufferedReader缓冲对象
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		
		String line = br.readLine();
		while( line.length() > 0 ){
			bw.write(line);
			bw.newLine();
			line = br.readLine();
		}
	
		br.close();
		bw.close();
	}

	private static void m2() throws IOException {
		Writer out = new FileWriter("d:/abc.txt", true 	);
		BufferedWriter bw = new BufferedWriter(out);
		
		bw.write("我送你99朵玫瑰花儿");
		
//		bw.flush(); 			//清空缓冲区,把数据保存到文件中
		bw.close();
	}

	private static void m1() throws IOException {
		Reader in = new FileReader("d:/test08.java");
		BufferedReader br = new BufferedReader(in);
		
		//从缓冲字符流中读取一行,读到文件末尾返回null
		String line = br.readLine();
		while( line != null ){
			System.out.println( line  );
			line = br.readLine();
		}
		
		br.close(); 		//把包装流关闭后, 被包装的流也会关闭
	}

ObjectInputStream/ObjectOutputStream

对象序列化:
把对象转换为01二进制序列就是对象序列化
对象反序列化
把一组01二进制序列转换为对象

注意:
对象序列化/反序列化前提是对象的类要实现Serializable接口,该接口是一个标志性接口,没有任何方法

ObjectOutputStream类 可以把对象序列化,把序列化后二进制保存到文件中
ObjectInputStream类可以从文件读取01序列,把 这组01序列转换为对象(反序列化)
实例:对象序列化

public static void main(String[] args) throws IOException {
		Person p1 = new Person("yongge", 36);
		//对象序列化, 把p1对象保存到d:/obj.txt文件中 
		OutputStream out = new FileOutputStream("d:/obj.txt");
		ObjectOutputStream oos = new ObjectOutputStream(out);
		
		oos.writeObject(p1);
		oos.close();
	}

实例:对象反序列化

public static void main(String[] args) throws IOException, ClassNotFoundException {
		InputStream in = new FileInputStream("d:/obj.txt");
		ObjectInputStream ois = new ObjectInputStream(in);
		//从文件中读取一个对象, obj是Object类型的,引用的是Person对象
		Object obj = ois.readObject();
		
		System.out.println( obj ); 
		ois.close();

注意的问题
场景描述:
把对象序列化到文件后, 又在Person类中添加了一个字段, 再进行反序列化时,产生以下异常:
java.io.InvalidClassException:
com.bjpowernode.chapter06.objectinputoutput.Person; local class incompatible:
stream classdesc serialVersionUID = 6372825075440148724,
local class serialVersionUID = 5707827319367398195
分析:
Person类实现了Serializable接口后,系统会给类自动添加一个serialVersionUID序列化版本号字段
当在Person类添加/删除了一个字段,重新编译,serialVersionUID字段会生成一个新的值
解决方法:
保证 对象序列化时与反序列化时serialVersionUID字段的值要相同
可以手动的添加一个serialVersionUID字段
一般情况下, 类实现了Serializable接口后, 手动的添加一个序列化版本号字段:
private static final long serialVersionUID = 2332956456465L;

PrintStream/PrintWriter

打印字节流/打印字符流

字节打印流实例:

public static void main(String[] args) throws IOException {
		//1)
		OutputStream out = new FileOutputStream("d:/log.txt", true);
		PrintStream pStream = new PrintStream(out);
		
		pStream.print("hello");
		pStream.println(" world");
		
		//2)System类的out成员就是PrintStream类型的打印流
		//System.out默认系统的标准输出设备是显示器
		System.out.println("默认在显示器上打印信息");
		//可以修改System.out的打印方向
		System.setOut(pStream);
		System.out.println("这一行信息就不是在屏幕上打印, 而是打印到pStream流中,即log.txt文件");
		
		//3)经常把异常信息保存到日志文件中
		try {
			FileInputStream fis = new FileInputStream("f:/asdfsaf.tt");
		} catch (Exception e) {
			// 在开发时, 一般调用e.printStackTrace()把异常信息打印到屏幕上方便程序员调试
			//在部署后, 会把异常信息打印到日志文件中
			e.printStackTrace(pStream);
		}
		
		pStream.close();
	}

字符打印流实例:

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

		PrintWriter pw = new PrintWriter(new FileWriter("d:/def.txt"));
		pw.print("这是字符打印流");
		
		try {
			FileInputStream fis = new FileInputStream("f:/sadf.dsf");
		} catch (Exception e) {
			e.printStackTrace(pw);
		}
		
		pw.close();
	}

File类

读取文件内容使用IO流, 操作文件/文件夹使用File类, 如创建/遍历/删除文件夹, 查看文件的相关属性等操作
构造方法:

public static void main(String[] args) throws IOException {
		File f1 = new File("d:/java");
		f1.mkdir(); 			//创建文件夹
		
		File f2 = new File("d:/java/sub1");
		f2.mkdir();
		
		File f3 = new File("d:/java", "sub2");
		f3.createNewFile(); 		//创建文件
		
		File f4 = new File(f2, "sub3");
		f4.createNewFile();
		
	}

查看文件相关属性:

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

//		File  f1 = new File("d:/hehe.avi");
		File f1 = new File("hehe.avi");		//相对路径,在项目目录下的文件
		/*
		如果在创建文件时构造方法中的内容是相对路径,返回的绝对路径是当前项目的路径 + 相对路径,会去当前项目的路径下判断是否存在,
		*/
		f1.createNewFile();
		
		System.out.println( f1.getAbsolutePath() );	//返回绝对路径(从根目录开始的路径 )
		System.out.println( f1.getPath() );			//返回路径 ,构造方法中写的什么就会返回什么
		System.out.println( f1.getParent() );		//返回上一级文件夹,如果不存在则返回null
		System.out.println( f1.getName() );			//对象名
		System.out.println( f1.length());			//文件大小
		System.out.println( f1.exists() );			//是否存在
		System.out.println( f1.isFile() );			//是否为文件
		System.out.println( f1.isAbsolute() );		//是否绝对路径
		System.out.println( f1.lastModified() );	//最后一次修改的时间
		

对文件夹的操作:

	public static void main(String[] args) {
		listSub3("e:/Java");
	}

	//显示指定文件 夹的内容
	public static void listSub1(String  dirname) {
		File dir = new File(dirname);
		
		String[] subs = dir.list();
		for (String string : subs) {
			System.out.println( string );
		}
	}
	
	//显示绝对路径 
	private static void listSub2(String dirname) {
		File dir = new File(dirname);
		
		File[] listFiles = dir.listFiles();
		for (File file : listFiles) {
			System.out.println(file.getAbsolutePath());
		}
	}
	
	//显示绝对路径 , 包括子文件夹的内容
	private static void listSub3(String dirname) {
		File dir = new File(dirname);
		
		File[] listFiles = dir.listFiles();
		for (File file : listFiles) {
			System.out.println(file.getAbsolutePath());
			//如果file对象是文件夹, 显示该子文件夹的内容
			if (file.isDirectory()) {
				listSub3( file.getAbsolutePath() ) ;			//递归调用
			}
		}
	}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值