【JAVA】I/O——2022.1.14准备实习日记

##准备实习日记
这是我准备实习的课程经历,记录的只是学习途中个人认为需要记录和新学习到的知识,所说的也全都是个人的理解,如有错误,欢迎指正讨论。JAVA基础课程是我学习的地方。
2022.01.14 JAVA基础——单例模式
2022.01.15 JAVA基础——异常处理
2022.01.15 JAVA基础——I/O



前言

输入指的是从储存器中读取,输出指的是写入到储存器中。一般的输入输出对象都是文件。这里总结,如何创建和使用文件对象,如何使用各种输入输出流来读取文件内容或写入文件。


一、文件对象

文件和文件夹都是File的代表。可以使用绝对路径或相对路径来创建File对象,但是创建了File对象并不意味着,在此路径下真正创建了这个文件或文件夹。要想真正创建文件夹,需要调用创建方法。

import java.io.File;
  
public class TestFile {  
    public static void main(String[] args) {
        // 绝对路径,代表一个文件夹,可以通过此文件夹来代表路径
        File f1 = new File("d:/Hello world");
        System.out.println("f1的绝对路径:" + f1.getAbsolutePath());
        // 相对路径,工作目录
        File f2 = new File("Helloworld.exe");
        System.out.println("f2的绝对路径:" + f2.getAbsolutePath());  
        // 把f1作为父目录创建文件对象
        File f3 = new File(f1, "Helloworld.exe");  
        System.out.println("f3的绝对路径:" + f3.getAbsolutePath());
    }
}

File对象中内置很多方法,可以查询该文件或文件夹的具体信息,还可以创建文件。

import java.io.File;
import java.util.Date;
  
public class TestFile {  
    public static void main(String[] args) {  
        File f = new File("d:/Hello world/Helloworld.exe");
        System.out.println("当前文件是:" +f);
        //文件是否存在
        System.out.println("判断是否存在:"+f.exists());
         
        //是否是文件夹
        System.out.println("判断是否是文件夹:"+f.isDirectory());
          
        //是否是文件(非文件夹)
        System.out.println("判断是否是文件:"+f.isFile());
          
        //文件长度
        System.out.println("获取文件的长度:"+f.length());
          
        //文件最后修改时间
        long time = f.lastModified();
        Date d = new Date(time);
        System.out.println("获取文件的最后修改时间:"+d);
        //设置文件修改时间为1970.1.1 08:00:00
        f.setLastModified(0);
          
        //文件重命名
        File f2 =new File("d:/Hello world/Hello.exe");
        f.renameTo(f2);
        
        /*注意: 需要在D:\\Hello world确实存在一个Hello.exe
        才可以看到对应的文件长度、修改时间等信息*/;
        
        // 以字符串数组的形式,返回当前文件夹下的所有文件(不包含子文件及子文件夹)
        f.list();
  
        // 以文件数组的形式,返回当前文件夹下的所有文件(不包含子文件及子文件夹)
        File[]fs= f.listFiles();
  
        // 以字符串形式返回获取所在文件夹
        f.getParent();
  
        // 以文件形式返回获取所在文件夹
        f.getParentFile();
        // 创建文件夹,如果父文件夹skin不存在,创建就无效
        f.mkdir();
  
        // 创建文件夹,如果父文件夹skin不存在,就会创建父文件夹
        f.mkdirs();
  
        // 创建一个空文件,如果父文件夹skin不存在,就会抛出异常
        f.createNewFile();
        // 所以创建一个空文件之前,通常都会创建父目录
        f.getParentFile().mkdirs();
  
        // 列出所有的盘符c: d: e: 等等
        f.listRoots();
  
        // 刪除文件
        f.delete();
  
        // JVM结束的时候,刪除文件,常用于临时文件的删除
        f.deleteOnExit();
    }
}

二、输入/输出流

流一般指数据流,就是一系列的数据。
当不同的介质之间有数据交互的时候,JAVA就使用流来实现。例如读取硬盘中的数据到缓存中,站在程序的角度来看,就叫做输入流。
数据源可以是文件,还可以是数据库,网络甚至是其他的程序。

1.字节流

用于以字节的形式读取和写入数据。所有的数据存放在计算机中都是以数字的形式存放的。 所以字母就需要转换为数字才能够存放。常见的编码如ASCII码。

a. 字节输入流

InputStream是字节输入流,同时也是抽象类,只提供方法声明,不提供方法的具体实现。
FileInputStream 是InputStream子类,以FileInputStream 为例进行文件读取

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
  
public class TestStream {  
    public static void main(String[] args) {
        try {
            //准备文件Hello.txt其中的内容是AB,对应的ASCII分别是65 66
            File f =new File("d:/Hello world/Hello.txt");
            //创建基于文件的输入流,注意创建流并不等于开始读取
            FileInputStream fis =new FileInputStream(f);
            //创建字节数组,其长度就是文件的长度
            byte[] all =new byte[(int) f.length()];
            //以字节流的形式读取文件所有内容,这里才是真正开始读取
            fis.read(all);
            for (byte b : all) {
                //打印出来是65 66
                System.out.println(b);
            }             
            //每次使用完流,都应该进行关闭
            fis.close();              
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }          
    }
}

b. 字节输出流

OutputStream是字节输出流,同时也是抽象类,只提供方法声明,不提供方法的具体实现。
FileOutputStream 是OutputStream子类,以FileOutputStream 为例向文件写出数据。
注: 如果文件d:/Hello.txt不存在,写出操作会自动创建该文件。
但是如果是文件 d:/Hello world/Hello.txt,而目录/Hello world又不存在,会抛出异常。

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
 
public class TestStream { 
    public static void main(String[] args) {
        try {
            // 准备文件Hello.txt其中的内容是空的
            File f = new File("d:/Hello.txt");
            // 准备长度是2的字节数组,用88,89初始化,其对应的字符分别是X,Y
            byte data[] = { 88, 89 }; 
            // 创建基于文件的输出流
            FileOutputStream fos = new FileOutputStream(f);
            // 把数据写入到输出流
            fos.write(data);
            // 关闭输出流
            fos.close();             
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } 
    }
}

所有的流在使用后都应该正常关闭,否则会不利于项目的进行。比如,在某些情况下只允许建立一个或几个流,如果每个流在使用后都不关闭,那么很快就无法建立新的流供他人使用。

2.字符流

Reader字符输入流,Writer字符输出流,专门用于字符的形式读取和写入数据。

a. 字符输入流

FileReader 是Reader子类,以FileReader 为例进行文件读取。

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
 
public class TestStream { 
    public static void main(String[] args) {
        // 准备文件Hello.txt其中的内容是AB
        File f = new File("d:/Hello.txt");
        // 创建基于文件的Reader
        //这里将File对象的创建放在了try中,是关闭流的方式之一,省去了调用关闭方法
        try (FileReader fr = new FileReader(f)) {
            // 创建字符数组,其长度就是文件的长度
            char[] all = new char[(int) f.length()];
            // 以字符流的形式读取文件所有内容
            fr.read(all);
            for (char b : all) {
                // 打印出来是A B
                System.out.println(b);
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } 
    }
}

b. 字符输出流

FileWriter 是Writer的子类,以FileWriter 为例把字符串写入到文件。

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
  
public class TestStream {  
    public static void main(String[] args) {
        // 准备文件Hello.txt
        File f = new File("d:/Hello.txt");
        // 创建基于文件的Writer
        try (FileWriter fr = new FileWriter(f)) {
            // 以字符流的形式把数据写入到文件中
            String data="asdfghjkl";
            char[] cs = data.toCharArray();
            fr.write(cs);  
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }  
    }
}

3.缓存流

实际上,字节流和字符流的使用有很大弊端,例如,如果访问介质是硬盘,那么字节流和字符流的每一次读写命令都会访问硬盘,如果读写频率太高的话,性能就不太好。

缓存流就是为了解决这个问题。缓存流在读取的时候,会一次性读较多的数据到缓存中,以后每一次的读取,都是在缓存中访问,直到缓存中的数据读取完毕,再到硬盘中读取。缓存流在写入数据的时候,会先把数据写入到缓存区,直到缓存区满了,才把这些数据一起写入硬盘。

a.缓存流读取

缓存字符输入流 BufferedReader 可以一次读取一行数据。

File f = new File("d:/Hello.txt");
// 创建文件字符流
// 缓存流的参数必须是一个存在的流
try (
    FileReader fr = new FileReader(f);
    BufferedReader br = new BufferedReader(fr);
    )
{
    while (true) {
        // 一次读一行
        String line = br.readLine();
        if (null == line)
            break;
        System.out.println(line);
    }
} catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

b.缓存流写入

PrintWriter 缓存字符输出流, 可以一次写出一行数据

File f = new File("d:/Hello.txt");          
try (      
    FileWriter fw = new FileWriter(f);
    PrintWriter pw = new PrintWriter(fw);              
) {
    pw.println("abcdefg");
    pw.println("higklmn");
    pw.println("opq rst");
} catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

c.flush方法

使用缓存流时,有时也需要立即把数据写入硬盘,不能等缓存满了才写入, 可以使用flush方法。

File f =new File("d:/Hello.txt");
//创建文件字符流
//缓存流必须建立在一个存在的流的基础上
try(
	FileWriter fr = new FileWriter(f);
	PrintWriter pw = new PrintWriter(fr);
) {
    pw.println("abcdefg");
    //强制把缓存中的数据写入硬盘,无论缓存是否已满
    pw.flush();           
    pw.println("hijklmn");
    pw.flush();
    pw.println("opq rst");
    pw.flush();
} catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

4.数据流

当进行字节或字符流写入时,如果需要写入很多int 型数字,比如先写入了258,再写入456,那么读取时,会发现无法分离相应的数字,到底是在258分开还是在25分开,这就有问题了。所有需要写入其他的标识符表示分割例如空格。但是数据流可以解决标识符的问题。数据流的writeUTF()和readUTF() 可以进行格式化的顺序读写。

本例中,通过DataOutputStream 向文件顺序写出 布尔值,整数和字符串。 然后再通过DataInputStream 顺序读入这些数据。

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 TestStream {      
    public static void main(String[] args) {
        write();
        read();
    }
 
    private static void read() {
        File f =new File("d:/Hello.txt");
        try (
            FileInputStream fis  = new FileInputStream(f);
            DataInputStream dis =new DataInputStream(fis);
        ){
            boolean b= dis.readBoolean();
            int i = dis.readInt();
            String str = dis.readUTF();
             
            System.out.println("读取到布尔值:"+b);
            System.out.println("读取到整数:"+i);
            System.out.println("读取到字符串:"+str);
 
        } catch (IOException e) {
            e.printStackTrace();
        }
         
    }
 
    private static void write() {
        File f =new File("d:/Hello.txt");
        try (
            FileOutputStream fos  = new FileOutputStream(f);
            DataOutputStream dos =new DataOutputStream(fos);
        ){
            dos.writeBoolean(true);
            dos.writeInt(1000);
            dos.writeUTF("abcdefg");
        } catch (IOException e) {
            e.printStackTrace();
        }
         
    }
}

5.对象流

对象流是指把一个对象以流的形式整体传输给其他介质。一个对象以流的形式进行传输,叫做序列化。 该对象所对应的类,必须是实现Serializable接口。不举例了。

三、关闭流

1.在try代码块中关闭

在try的作用域里关闭文件输入流,但是这有一个弊端:如果文件不存在,或者读取的时候出现问题而抛出异常,那么就不会执行这一行关闭流的代码,存在巨大的资源占用隐患。

try {
    File f = new File("d:/hello.txt");
    FileInputStream fis = new FileInputStream(f);
    byte[] all = new byte[(int) f.length()];
    fis.read(all);
    for (byte b : all) {
        System.out.println(b);
    }
    // 在try代码段中关闭流
    fis.close();
} catch (IOException e) {
    e.printStackTrace();
}

2.在finally代码块中关闭

finally代码块时放在try catch代码块后面的代码块,在执行完try catch代码块之后,一定会执行的部分。
这种方法是关闭流的标准方法
1. 首先把流的引用声明在try的外面,如果声明在try里面,其作用域无法抵达finally.
2. 在finally关闭之前,要先判断该引用是否为空
3. 关闭的时候,需要再一次进行try catch处理

File f = new File("d:/hello.txt");
FileInputStream fis = null;
try {
    fis = new FileInputStream(f);
    byte[] all = new byte[(int) f.length()];
    fis.read(all);
    for (byte b : all) {
        System.out.println(b);
    }
} catch (IOException e) {
    e.printStackTrace();
} finally {
    // 在finally 代码块中关闭流
    if (fis != null){
        try {
            fis.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

3.使用try(…)的方式

把流定义在try()里,try,catch或者finally结束的时候,会自动关闭。所有的流,都实现了一个接口叫做 AutoCloseable,任何类实现了这个接口,都可以在try()中进行实例化。 并且在try, catch, finally结束的时候自动关闭,回收相关资源。

File f = new File("d:/hello.txt");  
//try,catch或者finally结束的时候,这种方式会自动关闭流
try (FileInputStream fis = new FileInputStream(f)) {
    byte[] all = new byte[(int) f.length()];
    fis.read(all);
    for (byte b : all) {
        System.out.println(b);
    }
} catch (IOException e) {
    e.printStackTrace();
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值