Java学习——I/O输入输出流


I/O就是Java可以读取磁盘中的文件,也可以将数据写到磁盘中的文件里

1 File类

java.io.File类表示磁盘上的文件或目录(文件夹)

1.1 特点

  • 文件和目录都通过File类表示(目录实际上就是特殊的文件)
  • File类提供了对文件和目录的基本操作,如查看文件名、文件大小、新建、删除文件等
  • File类只能看文件的属性,而不能查看文件内容

1.2 构造方法

1.2.1 路径分类

路径分隔符:
Windows使用 “\” 反斜杠表示,Linux和MacOS使用 “/” 正斜杠表示。需要注意的是一个反斜杠在Java中代表转义符,例如 \n 表示回车,所以在Java中表示Windows路径时需要使用 “\ \” 或使用 “/” 表示路径分隔符

  • 绝对路径:以根开始的路径
    在Windows中根就是盘符,也就是平常说的C盘D盘。例如 D:\ \目录\ \文件名
    在Linux和MacOS中根就用 “/” 表示。例如 /目录/文件名

  • 相对路径:不是以根开始的路径,相对于某个路径的路径,例如 目录/文件名
    . 表示当前目录, . . 表示上一级目录。Java中是相对于项目的根目录的路径

1.2.2 构造

直接new一个File对象即可,构造时参数为路径。需要注意的是这个路径所指向的文件、目录可以存在,也可以不存在,如果要判断文件是否存在可以调用exists()方法查看。创建一个File对象只是创建一个指向某个路径的对象,并不表示在硬盘中创建文件。

构造方法作用
File(String pathname)指定文件的路径
File(String parent, String child)指定父目录的路径和文件名
File(File parent , String child)指定父目录的路径(由File对象指定)和文件名

1.3 常用方法

  • 访问属性:
方法作用
getName()文件名
gtePath()路径名(和创建对象时的参数一致)
getAbsolutePath()绝对路径名
getParent()父目录(看创建对象时的参数,若没有写出则为null)
getParentFile()父目录文件对象(返回File对象)
length()文件大小(返回字节大小)
lastModified()最后一次修改时间
exists()文件是否存在
canRead()是否可读
canWrite()是否可写(可通过右击文件选择属性,在其中可以修改文件是否可写可读)
isFile()是否为普通文件(若文件不存在则为false)
isDirectory()是否为目录
isHidden()是否为隐藏文件
  • 文件目录的操作:
方法作用
createNewFile()创建空文件
mkdir()创建目录,如果父目录不存在则会创建失败
mkdirs()创建目录,如果父目录不存在会一同创建
renameTo(File dest)修改文件名,新名字用File对象传入
delete()删除文件
list()获取目录下的所有文件和目录名称,返回一个字符串数组
listFile()获取目录下的所有文件和目录对象,返回一个File数组

1.4 文件过滤

使用方法list(FilenameFilter filter)实现过滤,FilenameFilter是文件名字过滤接口,实现其中的accepty(File dir, String name)方法自定义过滤规则便可进行过滤
在这里插入图片描述

File a = new File("d:\\java") ;
		String[] list = a.list(new FilenameFilter() {
			public boolean accept(File dir, String name) {
				if(name.endsWith(".txt"))	//自定义过滤规则
					return true ;
				return false;
			}
			
		}) ;

2 IO流

  • I :input,输入
  • O :output,输出
  • :stream,可理解为一组有顺序的、有起点和终点的动态数据集合。数据传输通过流的形式传输,例如将磁盘中的文件读取到Java程序中,那么起点就是磁盘文件,终点就是Java程序,数据按这个方向不断流动。文件是数据在磁盘中的静态存储,而流是数据在传输时的动态形态

通过IO流可以实现文件的输入输出功能(用于对文件进行读写操作)

2.1 文件的分类

  • 文本文件 : 由字符组成的文件,可以使用记事本编辑的文件(.txt , .java)
  • 二进制文件: 除了文本文件以外的所有文件都属于二进制文件(.jpg)

2.2 流的分类

  • 按流的方向分(站在Java程序的角度看)
名字作用
输入流读取数据,从磁盘文件读取数据到Java程序(父类InputStream、Reader)
输出流用于写出数据,将Java程序中的数据写出到磁盘文件中(父类OutputStream、Writer)
  • 按流中数据的单位分

一个英文字符占1个字节,一个汉字占2个字节(GBK)或3个字节(UTF-8)

名字作用
字节流byte所操作的最小数据单元为字节(父类InputStream、OutputStream)
字符流char所操作的最小数据单位为字符(父类Reader、Writer)
  • 按数据的来源分
名字作用
节点流直接对数据源进行操作,如操作文件
包装流对一个节点流进行操作

3 字节流

3.1 简介

  • InputStream是字节输入流的顶层父类,常用子类有FileInputStream、ByteArrayInputStream、ObjectInputStream
  • OutputStream是字节输出流的顶层父类,常用子类有FileOutputStream、ByteArrayOutputStream、ObjectOutputStream

3.2 文件输入输出流

  • FileInputStream:文件输入流,以字节为单位,从文件中读取数据
方法作用
构造方法参数可直接传入文件路径,或者传入一个File对象
read()读取一个字节,返回int类型的字节值,每调用一次就往后读取一次,如果到文件末尾则返回-1
read(byte [] b)根据传入的字节数组控制每次读取的数据长度(b.length),返回每次读取字节数,如果到文件末尾则返回-1
close()关闭输入流
available()流中可读取的字节数

需要注意的点:

  • 字节流读取数据是按字节读取,那么在读取中文时可能会出现乱码
  • 只要程序打开了外部资源(文件、数据库、网络连接),使用完后一定要关闭资源,所以关闭资源一般要放在finally中
  • 在使用close()方法之前一定要保证对象不为空,否则会出现空指针异常
  • 使用字节数组控制一次读取长度,可减少对硬盘的读取次数,提高效率
public void input() {
		FileInputStream fis = null ;		
		try {
			fis = new FileInputStream("src\\data.properties") ;
//			int data = -1 ;
//			while((data=fis.read()) != -1) {	//遍历文件内,按单个字节读取
//				System.out.println((char)data);
//			}
			int num = -1 ;
			byte [] buffer = new byte[3] ;		//字节数组控制读取长度
			while((num=fis.read(buffer)) != -1) {	//num为读取的字节个数
				System.out.println(new String(buffer, 0, num));	
			}
		} 
		catch (FileNotFoundException e) {
			e.printStackTrace();
		} 
		catch (IOException e) {
			e.printStackTrace();
		} 
		finally {
			if(fis != null) {	//只有 不为空才能关闭资源
				try {
					fis.close() ;
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
}
  • FileOutputStream:文件输出流,以字节为单位,从将数据写入文件中
    用法和文件输入流类似,也要用try-catch-finally的格式
方法作用
write(byte [] b)将字节数组中的数据写入
flush()刷新输出流,完成数据的输出
close()关闭输入流,会自动调用flush()方法
available()流中可读取的字节数

需要注意的点:

  • 使用write()方法只是将数据写入到内存的缓冲区中,并没有真正写入到文件中
  • 当关闭资源时,会自动调用flush()方法
  • 若写入文件不存在则会自动创建文件,若文件存在,则默认覆盖原有内容
		FileOutputStream fos = null ;
		try {
			fos = new FileOutputStream("src\\data.properties", true) ;	
			//第二个参数代表写入数据是追加(true)还是覆盖(false),默认是覆盖
			byte[] data = "hello world".getBytes() ;	//要写入的数据用字节数组保存
			fos.write(data);	//写数据
		} 
		catch (FileNotFoundException e) {
			e.printStackTrace();
		} 
		catch (IOException e) {
			e.printStackTrace();
		}
		finally {
			if(fos != null) {
				try {
					fos.close();	//自动调用flush()方法
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}

3.3 字节数组输入输出流

数据的来源、目的地不一定是文件,也可以是内存中的一块空间

  • ByteInputStream:字节数组输入流,从字节数组中读取数据
  • ByteOutputStream:字节数组输出流,将数据写出到内置的字节数组中

需要注意的点:

  • 字节数组输入输出流的数据不是外部资源,是内存中的数组。没有必要调用close()方法
  • 与文件输入输出流的区别就是数据的位置不一样,其他的操作类似
public class 字节数组输入输出流 {

	public static void main(String[] args) {
		input() ;
		output() ;
	}
	public static void input() {	//输入
		byte[] data = "hello world".getBytes() ;	//要读的数据来源
		try {
			InputStream bis = new ByteArrayInputStream(data) ;
			int x = -1 ;
			while((x=bis.read()) != -1) {
				System.out.print((char)x);
			}
		} 
		catch (IOException e) {
			e.printStackTrace();
		}
	}
	public static void output() {	//输出
		try {
			ByteArrayOutputStream bos = new ByteArrayOutputStream() ;
			bos.write("world hello".getBytes());
			bos.flush(); 
			byte[] buffer = bos.toByteArray() ;	//获取内置的字节数组中的数据
			System.out.println(new String(buffer)) ;
		} 
		catch (IOException e) {
			e.printStackTrace();
		}
	}
}

3.4 对象输入输出流(对象的序列化和反序列化)

如果希望将Java对象写入IO流中,或从IO流中读取Java对象,则要使用对象输入输出流

  • 序列化:将Java对象写入IO流,实现将对象保存在磁盘或在网络中传递对象。由于你的电脑不认识Java对象,所以要用序列化处理之后存储在磁盘中
  • 反序列化:从IO流中读取Java对象,实现从磁盘或网络中恢复对象

对象必须要实现Serializable接口c才能被序列化,转换成二进制流,通过网络传输
在这里插入图片描述
这个接口中什么都没有,只是标志着实现该接口的类是可序列化的。
在这里插入图片描述
当类实现了Serializable接口之后会提示可以创建一个UID,用来判断对象序列化版本的一致性:在反序列化时会将流中的serialVersionUID与本地相应实体对象的serialVersionUID进行比较,如果相同则认为版本一致可进行反序列化,否则会出现版本不一致的异常。但是如果不创建这个UID还是可以执行序列化和反序列化。

  • ObjectInputStream: 对象输入流,对应反序列化。属于包装流。
public class 对象输入输出流 {

	public static void main(String[] args) {
		User user = new User("小明", 123) ;	//要写入的对象必须是可序列化的
		ObjectOutputStream oos = null ;
		try {
			FileOutputStream  fos = new FileOutputStream("user.data");	//作为包装流的参数
			oos = new ObjectOutputStream(fos) ;
			oos.writeObject(user);	//将对象写出
			oos.flush();
		} 
		catch (FileNotFoundException e) {
			e.printStackTrace();
		} 
		catch (IOException e) {
			e.printStackTrace();
		}
		finally {
			if(oos != null) {	//只需要关闭主要的流即可
				try {
					oos.close() ;
				} 
				catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		
	}

}

在这里插入图片描述
打开写入的文件,可以看到里面的内容都是乱码,这就是二进制文件

  • ObjectOutputStream: 对象输出流,对应序列化
public class 对象输入输出流 {

	public static void main(String[] args) {
		ObjectInputStream ois = null ;
		try {
			ois = new ObjectInputStream(new FileInputStream("user.data")) ;
			//读取出来是Object类型要强转,读取顺序和写入顺序一致
			User user = (User)ois.readObject() ;
			System.out.println(user.getName()) ;
		} 
		catch (FileNotFoundException e) {
			e.printStackTrace();
		} 
		catch (IOException e) {
			e.printStackTrace();
		}	
		catch (ClassNotFoundException e) {
				e.printStackTrace();
			}
		finally {
			if(ois != null) {	//只需要关闭主要的流即可
				try {
					ois.close() ;
				} 
				catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

ObjectInputStream和ObjctOutputStream都属于包装流,用于对节点流进行功能扩展和包装。在创建包装流时要传入操作的节点流对象;关闭流时,只需要关闭包装流即可,节点流会自动关闭。

3.5 随机读写流

可以对文件进行随机读写,即可以定位到文件的任意位置进行读写操作,原理其实就是通过移动指针实现。该流既能读也能写
在这里插入图片描述
在创建随机读写流对象时,要指定具体的模式:

  • “r”: 只读,如果文件不存在则会报错,不会自动创建文件
  • “rw”: 读写,如果文件不存在会自动创建新文件;不会覆盖原文件中的内容
方法功能
getFilePointer()获取当前指针位置
seek(long pos)将指针移动到指定位置(以字节为单位)
skipBytes(int n)将指针往后移动n个位置(以字节为单位)

4 字符流

4.1 简介

  • Reader是字符输入流的顶层父类,常用子类有:FileReader、 BufferedReader、 InputStreamReader
  • Writer是字符输出流的顶层父类,常用子类有:FileWriter、BufferedWriter、 PrintWriter、 OutputStreamWriter

4.2 文件输入输出流

  • FileReader: 文件字符输入流,以字符为单位从文件中读取数据。
  • FileWriter: 文件字符输出流,以字符为单位将数据写出到文件中。

用法和FileInputStream、FileOutputStream类似,但由于每次操作的都是以字符为单位,所以在处理中文字符时会更方便一些,同时由于不以字节为单位,所以无法读写二进制文件

4.3 转换流

用于将字节流转换为字符流(Java不支持将字符流转换为字节流),同时可以实现编码的转换。在转换时需要指定使用的字符集,如果不指定的话默认使用JVM的字符集。

  • InputStreamReader: 将字节输入流转换为字符输入流,属于包装流
  • OutputStreamWriter: 将字节输出流转换为字符输出流
//首先要有一个被转换的字节流
FileInputStream fis = new FileInputStream("src//data.properties") ;	
//然后再创建一个转换流,其中需要被转换的就是上面的字节流
InputStreamReader isr = new InputStreamReader(fis) ;
//最后用一个字符流得到转换的结果
BufferedReader reader = new BufferedReader(isr) ;

上面的写法也可以合并

BufferedReader reader = new BufferedReader(	//缓冲流,包装流
		new InputStreamReader(		//转换流,包装流
				new FileInputStream("src//data.properties") 	//字节流
				, "gbk"			//要转换的编码字符集
		)
) ;
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值