IO
IO就是Input、Output(输入、输出)
凡是涉及App与App以外资源的交互,都需要使用IO,如:文件的读写、输出到屏幕(System.out)、从键盘输入(System.in)等。
IO流
IO流,就是App与外部资源之间进行数据交互的“通道”。
输入流 | 输出流 | |
---|---|---|
字节流 | InputStream | OutputStream |
字符流 | Reader | Writer |
输入流:把数据从外部资源读取到App时,使用输入流
输出流:把数据从App写出到外部资源时,使用输出流
字节流:数据传输的根本是2进制形式,而传输过程中是以8位1个字节为基本单位,所以字节流是最基本的流,一般的数据传输都可以使用字节流,InputStream和OutputStream分别为字节输入流和字节输出流的基类(抽象类)
字符流:传输时还是以字节作为基本单位,但会把对应的字节自动转换为字符,方便文本处理,因此,涉及到文本的读写时最好使用字符流,Reader和Writer分别为字符输入流和字符输出流的基类(抽象类)
为什么流在使用完后一定要关闭?
可以这样理解:因为流是app与外界资源的一个通道,不属于app内部,垃圾回收机制“管不了”JVM外部的资源,所以需要手动关闭。
1. File基本信息的操作
File是文件和目录路径名的抽象表示形式。我们可以通过创建File对象来获取对应的文件信息。
测试:获取io.txt的文件信息
import java.io.File;
public class Main{
public static void main(String[] args){
//相对路径
File f1=new File("io.txt");
fileInfo(f1);
}
public static void fileInfo(File f){
System.out.println("文件名:"+f.getName());
//getPath():参数是相对路径就输出相对路径,参数是绝对路径就输出绝对路径
System.out.println("路径:"+f.getPath());
//getParengt():根据getPath()得到的路径进行截取,若getPath()返回的无父路径,则getParent()为null
System.out.println("父路径:"+f.getParent());
System.out.println("绝对路径:"+f.getAbsolutePath());
if (f.exists()){
System.out.println("文件存在");
System.out.println("是目录?"+f.isDirectory());
System.out.println("可读?"+f.canRead());
System.out.println("可写?"+f.canWrite());
System.out.println("文件的长度为:"+f.length());
System.out.println("文件最后被修改的日期:"+f.lastModified());
}
//如果此路径名表示一个目录,则该目录必须为空才能删除
//对于一个非空文件夹或不存在的文件/文件夹,delete()返回false
if(f.delete()){
System.out.println(f.exists()?"删除失败":"删除成功");
}
}
}
2. FileInputStream和FileOutputStream
FileInputStream是文件读取流,FileOutputStream是文件输出流,都是字节流(也就是说每次只读取或输出一个字节)。
测试:手动创建两个文件io.txt和iocopy.txt,使用FileInputStream类读取一个文件io.txt中的内容,然后使用FileOutputStream类把内容写到另一个文件iocopy.txt中
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class Main{
public static void main(String[] args){
FileInputStream fis=null;
FileOutputStream fos=null;
try{
File f=new File("io.txt");
fis=new FileInputStream(f);
//读取到一个byte数组中
byte[]b=new byte[(int)f.length()];
fis.read(b);
File fc=new File("iocopy.txt");
fos=new FileOutputStream(fc);
//覆盖的方式写入
fos.write(b);
}catch(IOException e){
e.printStackTrace();
}finally{
try{
if (fis!=null){
fis.close(); //关闭输入流
}
if (fos!=null){
fos.close(); //关闭输出流
}
}catch(IOException e){
e.printStackTrace();
}
}
}
}
3.RandomAccessFile
此类的实例支持对随机访问文件的读取和写入。这其中存在一个文件指针,读取时从文件指针开始读取字节,并随着对字节的读取而前移指针;写入操作也同样的从文件指针处开始写入字节,随着对字节的写入而前移指针。该文件指针可以通过 getFilePointer() 方法获取,并通过 seek() 方法设置。
通常,如果此类中的所有读取例程在读取所需数量的字节之前已到达文件末尾,则抛出 EOFException(是一种 IOException)。如果由于某些原因无法读取任何字节,而不是在读取所需数量的字节之前已到达文件末尾,则抛出 IOException,而不是 EOFException。需要特别指出的是,如果流已被关闭,则可能抛出 IOException。
测试:把io.txt中的内容读取出来做个简单的加密操作后再覆盖写回去。
import java.io.File;
import java.io.RandomAccessFile;
public class Main{
public static void main(String [] args){
RandomAccessFile rf=null;
try{
File f=new File("io.txt");
//以读写方式打开
rf=new RandomAccessFile(f,"rw");
//获取文件长度
System.out.println("读取前:文件长度为"+rf.length()+"bytes");
//获取当前文件指针位置
System.out.println("读取前:文件指针位置为"+rf.getFilePointer()+"bytes");
//读取到一个byte数组并处理
byte[]bf=new byte[(int)f.length()];
rf.read(bf);
//进行加密
for (int i=0;i<bf.length;i++){
bf[i]=(byte)(bf[i]+12);
}
//获取文件长度
System.out.println("读取后:文件长度为"+rf.length()+"bytes");
//获取文件指针位置
System.out.println("读取后:指针位置为"+rf.getFilePointer()+"bytes");
//写入前,将指针移到开头,否则会追加
rf.seek(0);
//获取文件长度
System.out.println("写入前:文件长度为"+rf.length()+"bytes");
//获取当前文件指针位置
System.out.println("写入前:指针位置为"+rf.getFilePointer()+"bytes");
rf.write(bf);
//获取文件长度
System.out.println("写入后:文件长度为"+rf.length()+"bytes");
//获取当前文件指针位置
System.out.println("写入后:指针位置为"+rf.getFilePointer()+"bytes");
}catch(Exception e){
e.printStackTrace();
}finally{
try{
if (rf!=null){
rf.close();
}
}catch(Exception e){
e.printStackTrace();
}
}
}
}
4.InputStreamReader和OutputStreamReader
InputStreamReader 是字节流通向字符流的桥梁:它使用指定的 charset 读取字节并将其解码为字符。
OutputStreamWriter 是字符流通向字节流的桥梁:它使用指定的 charset 将要写入流中的字符编码成字节。
它们使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。
为了获得最高效率,可考虑将它们 包装到BufferedReader 或 BufferedWriter 中,以避免频繁调用转换器。例如:
BufferedReader in= new BufferedReader(new InputStreamReader(System.in));
BufferedWriter out= new BufferedWriter(new OutputStreamWriter(System.out));
测试:从键盘读取用户输入,回车后将用户输入显示在屏幕上
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
public class Main{
public static void main(String[] args){
//创建标准输入流(键盘)
InputStreamReader in=new InputStreamReader(System.in);
//创建标准输出流(屏幕)
OutputStreamWriter out=new OutputStreamWriter(System.out);
try{
//read()读取返回的是int类型
int c;;
while ((c=in.read())!='\r'){
out.write(c);
}
}catch(Exception e){
e.printStackTrace();
}finally{
try{
if (in!=null){
in.close();
}
if (out!=null){
out.close();
}
}catch(Exception e){
e.printStackTrace();
}
}
}
}
5.BufferedReader和BufferedWriter
这两个类主要是为各Reader和Writer提供一个缓冲的机制,提高效率。
通常,Reader 所作的每个读取请求都会导致对底层字符或字节流进行相应的读取请求。因此,建议用 BufferedReader 包装所有其 read() 操作可能开销很高的 Reader(如 FileReader 和 InputStreamReader)。
通常 Writer 将其输出立即发送到底层字符或字节流。除非要求提示输出,否则建议用 BufferedWriter 包装所有其 write() 操作可能开销很高的 Writer(如 FileWriters 和 OutputStreamWriters)。
测试:由键盘输入多行字符串,并以“end”字符串结束,然后将输入内容分行存入到一个指定文件夹
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.FileWriter;
import java.io.File;
public class Main{
public static void main(String[]args){
BufferedReader bfr=null;
BufferedWriter bfw=null;
try{
bfr=new BufferedReader(new InputStreamReader(System.in));
bfw=new BufferedWriter(new FileWriter(new File("io.txt")));
System.out.println("请输入字符串,并以end结束");
String input=null;
//readLine,读取一行,每读一行进行一次写入操作
while(!(input=bfr.readLine()).equals("end")){
bfw.write(input);
bfw.newLine();
}
}catch(Exception e){
e.printStackTrace();
}finally{
try{
if (bfr!=null){
bfr.close();
}
if (bfw!=null){
bfw.close();
}
}catch(Exception e){
e.printStackTrace();
}
}
}
}