Java IO 流

548 阅读8分钟

这是我参与 8 月更文挑战的第 27 天,活动详情查看: 8月更文挑战

文件对象

  • 文件对象常用方法
import java.io.File;

public class Demo{
    public static void main(String[] args){
        File f1 = new File("/home/cunyu/soft/Java");
        
        // 绝对路径
        System.out.println("f1 绝对路径 :" + f1.getAbsolutePath());
        
        // 将 f1 作为父目录创建文件对象
        File f2 = new File(f1, "Demo.java");
        System.out.println("f2 绝对路径 :" + f2.getAbsolutePath());
        
        File f3 = new File("/home/cunyu/soft/Java/Demo.java");
        
        // 判断文件是否存在
        System.out.println(f3.exists());
        
        // 判断文件是否是文件夹
        System.out.println(f3.isDirectory());
        
        // 判断是否是文件
        System.out.println(f3.isFile());
        
        // 文件长度
        System.out.println(f3.length());
        
        // 最后修改时间
        long time = f3.lastModified();
        Date date = new Date(time);
        System.out.println(d)
        
        // 设置文件修改时间,从 1970.1.1 08:00:00 开始
        f3.setLastModified(0);
        
        // 文件重命名
        File f4 = new File("/home/cunyu/soft/Java/Test.java");
        f3.renameTo(f4);
        
        // 以字符串数组形式返回当前文件夹下所有文件(不含子文件及子文件夹)
        String[] filesListStr = f3.list();
        
        // 以文件数组形式返回当前文件夹下所有文件(不含子文件及子文件夹)
        File[] filesListFile = f3.listFiles();
        
        // 以字符串形式返回文件所在文件夹
        String pathStr = f3.getParent();
        
        // 创建文件夹
        f3.mkdir(); // 父文件夹不存在则无效
        f3.mkdirs(); // 父文件夹不存在则创建
        
        // 创建空文件
        f3.createNewFile();
        
        // 列出盘符
        f3.listRoots();
        
        // 删除文件
        f3.delete();
        
        // JVM 结束时,删除文件,常用于删除临时文件
        f3.deleteOnExit();
    }
}

流(Stream)

  • 定义 :当数据在不同介质间交互时,Java 则通过流来实现,数据源可以是文件、数据库、网络等。从不同的角度,可以分为 输入流(InputStream输出流(OutputStream
  • 实例
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class Demo{
    public static void main(String[] args){
        try{
            File file = new File("/home/cunyu/soft/Java/Demo.java");
            
            // 输入流
            FileInputStream inputStream = new FileInputStream(file);
            // 输出流
            FileOutputStream outputStream = new FileOutputStream(file);
        } catch (IOException e){
            e.printStackTrace();
        }
    }
}

字节流 VS 字符流 VS 缓存流 VS 数据流 VS 对象流

字节流

  • 定义 : 分为 输入流(InputStream输出流(OutputStream,主要用于 以字节的形式读取和写入数据
  • 可见的 ASCII 码对应的十进制和十六进制;
字符十进制数字十六进制数字
!3321
"3422
#3523
$3624
%3725
&3826
'3927
(4028
)4129
*422A
+432B
,442C
-452D
.462E
/472F
04830
14931
25032
35133
45234
55335
65436
75537
85638
95739
:583A
;593B
<603C
=613D
623E
@6440
A6541
B6642
C6743
D6844
E6945
F7046
G7147
H7248
I7349
J744A
K754B
L764C
M774D
N784E
O794F
P8050
Q8151
R8252
S8353
T8454
U8555
V8656
W8757
X8858
Y8959
Z905A
[915B
\925C
]935D
945E
_955F
`9660
a9761
b9862
c9963
d10064
e10165
f10266
g10367
h10468
i10569
j1066A
k1076B
l1086C
m1096D
n1106E
o1116F
p11270
q11371
r11472
s11573
t11674
u11775
v11876
w11977
x12078
y12179
z1227A
{1237B
|1247C
}1257D
~1267E
  • 实例 :字节流形式读取和写入数据
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class Demo{
    public static void main(String[] args){
        // 读取数据
        try{
            File fileIn = new File("/home/cunyu/soft/Java/Demo.java");
            FileInputStream fis = new FileInputStreaam(fileIn);
            
            // 创建字节数组,长度为文件长度
            byte[] fileContext = new byte[(int)file.length()];
            
            // 以字节流的方式读取文件内容,使用后关闭流
            fis.read(fileContext);
            fis.close();
        } catch(IOException e){
            e.printStackTrace();
        }
        
        // 写入数据
        try{
            File fileOut = new File("/home/cunyu/soft/Java/Demo.java");
            FileOutputStream fos = new FileOutputStream(fileOutput);
            
            // 写入数据对应的字节数组
            byte[] data = {65, 66, 67};
            
            // 以字节流的方式写入数据,然后关闭流
            fos.write(data);
            fos.close()
        } catch(IOException e){
            e.printStackTrace();
        }
    }
}

字符流

  • 定义:分为 字符输入流(Reader字符输出流(Writer,用于 字符形式 的读取和输入数据;
  • 实例:字符流读取和输入数据
import java.io.IOException;
import java.io.FileReader;
import java.io.File;

public class Demo{
    public void main(String[] args){
        
        // 读取
        File file = new File("/home/cunyu/soft/Java/Demo.java");
        
        try(FileReader fileReader = new FileReader(file)){
            char[] all = new char[(int) file.length()];
            fileReader.read(all);
        } catch (IOException e){
            e.printStackTrace();
        }
        
        // 写入
        try(FileWriter fileWriter = new FileWriter(file)){
            String data = "System.out.println("hello world");";
            char[] strArray = data.toCharArray();
            fileWriter.write(strArray);
        } catch (IOException e){
            e.printStackTrace();
        }
    }
}

缓存流

  • 定义 :针对 字节流字符流 在每次读写都会访问硬盘的特点,若读写频率较高时,性能较弱的 弊端,因而采用 缓存流缓存流 一次性读取较多数据到缓存,后面的每次读取都是在缓存中访问,直到缓存中数据读取完毕,再到硬盘读取。
  • 实例 :缓存流读取和写入数据;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;

public class Demo{
    public static void main(String[] args){
        File file = new File("/home/cunyu/soft/Java/Demo.java");
        
        // 读取
        try(
            FileReader fr = new FileReader(file);
            BufferedReader br = new BufferedReader(fr);
       		)
        {
            while(true){
                // 每次一行
                String line = br.readLine();
                if(line != null){
                    System.out.println(line)
                }else{
                    break;
                }
            }
        } catch (IOException){
            e.printStackTrace();
        }
        
         // 写入
        try(
            FileWriter fw = new FileWriter(file);
            BufferedWriter wr = new BufferedWriter(fw);
       		)
        {
            while(true){
                // 每次一行
                String line = wr.writeLine();
                if(line != null){
                    System.out.println(line)
                }else{
                    break;
                }
            }
        } catch (IOException){
            e.printStackTrace();
        }
    }
}
  • flush :缓存无论读写都需要先等缓存满,但有时需要立即读取写入时,可以利用 flush 立即读取写入;
package stream;
   
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

public class Demo {
    public static void main(String[] args) {
        File file = new File("/home/cunyu/soft/Java/Demo.java");
        
        // 创建文件字符流
        // 缓存流必须建立在一个存在的流的基础上
        try(FileWriter fr = new FileWriter(f);PrintWriter pw = new PrintWriter(fr);) {
            pw.println("garen kill teemo");
            // 强制把缓存中的数据写入硬盘,无论缓存是否已满
            pw.flush();            
            pw.println("teemo revive after 1 minutes");
            pw.flush();
            pw.println("teemo try to garen, but killed again");
            pw.flush();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

数据流

  • 定义 :分为 数据输入流(DataInputStream)数据输出流(DataOutputStream)
  • 实例 :直接进行字符串的读写;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class Demo{
    public static void main(String[] args){
        File file = new File("/home/cunyu/soft/Java/Demo.java");
        write();
        read();
    }
    
    /**
    * 数据流写入
    * @param: file 待写入的文件
    */
    public static viod write(File file){
        try(FileOutputStream fos = new FileOutputStream(file);
           DataOutputStream dos = new DataOutputStream(fos);){
            dos.writeBoolean(true);
            dos.writeInt(250);
            dos.writeUTF("你是 250 ?");
        } catch (IOException e){
            e.printStackTrace();
        }
    }
    
    /**
    * 数据流读取
    * @param: file 待写入的文件
    */
    public static viod read(File file){      
        try(
        	FileInputStream fis = new FileInputStream(file);
            DataInputStream dis = new DataInputStream(fis);
        ){
            boolean b = dis.readBoolean();
            int i = dis.readInt();
            String str = dis.readUTF();
        } catch(IOException e){
            e.printStackTrace();
        }
    }
}

对象流

  • 定义 :指直接 将一个对象以流的形式 传输给其他介质。一个对象以流的形式进行传输叫做 序列化,该对象所属类必须实现 Serializable 接口;
  • 实例 :序列化一个对象;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

class Hero implements Serializable{
    private static final long serialVersionUID = 1L;
    public String name;
    public float hp;
}

public class Demo{
    public static void main(String[] args){
        Hero hero = new Hero();
        hero.name = "铠";
        hero.hp = 1000;

        // 用于保存对象的文
        File file = new File("/home/cunyu/soft/Java/k.honor");

        try(
            // 对象输入流
            FileInputStream fis = new FileInputStream(file);
            ObjectInputStream ois = new ObjectInputStream(fis);

            // 对象输出流
            FileOutputStream fos = new FileOutputStream(file);
            ObjectOutputStream oos = new ObjectOutputStream(fos);
        ){
            oos.writeObject(hero);
            Hero hero2 = (Hero) ois.readObject();

            System.out.println(hero2.name + " : " + hero2.hp);
        } catch (IOException e){
            e.printStackTrace();
        } catch (ClassNotFoundException e){
            e.printStackTrace();
        }      
    }
}

中文问题

  • 常用编码
    1. ISO-8859-1 ASCII : 数字和西欧字母;
    2. GBK GB2312 BIG5 : 中文;
    3. UNICODE : 统一编码;
  • 实例:读取中文;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
 
public class Demo {
 
    public static void main(String[] args) throws UnsupportedEncodingException, FileNotFoundException {
        File f = new File("/home/cunyu/tmp/test.txt");
        System.out.println("默认编码方式: " + Charset.defaultCharset());
        //FileReader得到的是字符,所以一定是已经把字节根据某种编码识别成了字符了
        //而FileReader使用的编码方式是Charset.defaultCharset()的返回值,
        try (FileReader fr = new FileReader(f)) {
            char[] cs = new char[(int) f.length()];
            fr.read(cs);
            System.out.println("默认编码方式 : %s",Charset.defaultCharset());
            System.out.println(new String(cs));
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
       	// FileReader 无法手动设置编码,因此用 InputStreamReader 代替
        try (InputStreamReader isr = new InputStreamReader(new FileInputStream(f),Charset.forName("UTF-8"))) {
            char[] cs = new char[(int) f.length()];
            isr.read(cs);
            System.out.printf("InputStreamReader 指定编码方式UTF-8,识别出来的字符是:%n");
            System.out.println(new String(cs));
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }         
    }
}

关闭流的方式

无论是什么流,在我们使用之后,都应该对其关闭,否则将会造成资源的浪费,严重时可能会影响后续业务的展开,此处主要介绍 3 种关闭流的方式;

  • try 中关闭 :此时存在一个 弊端 :当文件不存在或读取时出现问题会抛出异常,从而引起无法触发关闭流的代码,存在巨大的资源占用隐患,不推荐 使用;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class Demo{
    public static void main(String[] args){
        // 读取数据
        try{
            File fileIn = new File("/home/cunyu/soft/Java/Demo.java");
            FileInputStream fis = new FileInputStreaam(fileIn);
            
            // 创建字节数组,长度为文件长度
            byte[] fileContext = new byte[(int)file.length()];
            
            // 以字节流的方式读取文件内容,使用后关闭流
            fis.read(fileContext);
            // try 中关闭
            fis.close();
        } catch(IOException e){
            e.printStackTrace();
        }
    }
}
  • finally 中关闭 :关闭流的标准方式:
  1. 先将流的引用声明在 try 的外面,以方便 finally 访问;
  2. 关闭 finally 之前,判断该引用是否为 null
  3. 关闭时,再次进行 try catch 处理;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class Demo{
    public static void main(String[] args){
        
        File fileIn = new File("/home/cunyu/soft/Java/Demo.java");
        FileInputStream fis = null;
        // 读取数据
        try{
            fis = new FileInputStream(fileIn);
            // 创建字节数组,长度为文件长度
            byte[] fileContext = new byte[(int)file.length()];
            
            // 以字节流的方式读取文件内容,使用后关闭流
            fis.read(fileContext);
            // try 中关闭
            fis.close();
        } catch(IOException e){
            e.printStackTrace();
        } finally{
            if(fis != null){
                try{
                    fis.close();
                } catch (IOException e){
                    e.printStackTrace();
                }
            }
        }
    }
}
  • try() 的方式 :将流定义在 try() 中,当 try、catch、finally 结束时,会自动关闭流;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
  
public class TestStream {
  
    public static void main(String[] args) {
        File fileIn = new File("/home/cunyu/soft/Java/Demo.java");
  
        //把流定义在try()里,try,catch或者finally结束的时候,会自动关闭
        try (FileInputStream fis = new FileInputStream(fileIn)) {
            byte[] all = new byte[(int) f.length()];
            fis.read(all);
        } catch (IOException e) {
            e.printStackTrace();
        }
  
    }
}

输入输出

  • 定义System.in 用于从控制台输入数据,System.out 用于在控制台输出数据;

流关系图

graph RL
B[字节流] --> A[流]
C[字符流] --> A[流]

D[缓存流] --> C[字符流]
数据流 --> B[字节流]
对象流 --> B[字节流]

总结

以上就是关于 IO 流的所有内容了,如果你觉得本文对你有所帮助,那就点赞关注吧!