跳至主要內容

15、IO流

T4makojava基础语法javaI/O大约 20 分钟

IO 流

1、File 类的使用

File 对象主要用来获取文件本身的信息,如所在目录,文件长度,文件读/写权限等,不涉及文件的读写操作

File 类的一个对象代表一个文件目录

File 类的构造方法:

  • File(String filePath)
  • File(String parentPath,String childPath)
  • File(File parentFile,String childPath)

相对路径:相较于某个路径下,相对于「当前工程」
绝对路径:包含盘符在内的文件或文件目录的路径

相关信息

关于 File 类,流类构造器的 String 参数:
假设在桌面,当前工程下,工程下 TXT 目录中都有 test.txt 文件

FileInputStream fis1 = new FileInputStream("C:\\Users\\Tamako\\Desktop\\test.txt");
FileInputStream fis2 = new FileInputStream(".\\test.txt");
FileInputStream fis3 = new FileInputStream("test.txt");
FileInputStream fis4 = new FileInputStream("\\test.txt"); // \test.txt (系统找不到指定的文件。)
FileInputStream fis5 = new FileInputStream(".\\TXT\\test.txt");
FileInputStream fis6 = new FileInputStream(".\\TXT\\..\\test.txt");
  • File 类的常用方法
    public String getName(): 获取名称
    public String getAbsolutePath(): 获取绝对路径
    public String getPath(): 获取路径
    public String getParent(): 获取上层文件目录路径。 若无, 返回 null
    public long length(): 获取文件长度「字节数」。不能获取目录的长度
    public long lastModified(): 获取最后一次的修改时间,1970 至今的毫秒值
    public String[] list(): 字符串形式返回目录下所有文件
    public File[] listFiles(): File 对象形式返回目录下所有文件
    public String[] list(FilenameFilter obj):字符串形式返回目录下指定类型的所有文件
    public File[] list(FilenameFilter obj):File 对象形式返回目录下指定类型的所有文件
    public String toString():返回 file 文件的路径

FilenameFilter 是一个接口,该接口有一个方法:public boolean accept(File dir,String name)

  • File 类的创建功能
    public boolean createNewFile():file 对象调用,若文件不存在则创建文件

  • File 类的删除功能
    public boolean delete(): 文件对象调用该方法,删除文件或者文件夹

相关信息

Java 中的删除不走回收站
要删除一个文件目录, 请注意该文件目录内不能包含文件或者文件目录

  • File 类的重命名功能
    public boolean renameTo(File dest):剪切并重命
    file1.renameTo(file2)
    file1 在硬盘中存在,file2 不存在则执行成功

  • File 类的判断功能
    public boolean isDirectory(): 判断是否是文件目录
    public boolean isFile() : 判断是否是文件
    public boolean exists() : 判断是否存在
    public boolean canRead() : 判断是否可读
    public boolean canWrite() : 判断是否可写
    public boolean isHidden() : 判断是否隐藏

  • 目录创建
    public boolean mkdir() :创建一个目录,如果成功返回 true,如果目录已存在返回 false
    public boolean mkdirs() : 创建文件目录。 如果上层文件目录不存在, 一并创建

File file = new File("test\\hello\\nihao");
file.mkdirs(); // 同时创建三个文件夹

相关信息

注意事项:如果你创建文件或者文件目录没有写盘符路径, 那么, 默认在 项目路径

  • 对于文件在 main 中相较于当前工程
  • 单元测试中相较于当前 model
public static void main(String[] args) {
    File file = new File("hello.txt");//相较于当前工程
    File file1 = new File("day09\\hello.txt");//相较于当前工程
}
public class FileTest {
    @Test
    public void test(){
        //构造器1
        File file1 = new File("hello.txt");//相对于当前module
        File file2 = new File("E:\\Study\\Code\\JavaSenior\\day08\\hi.txt");
        System.out.println(file1);
        System.out.println(file2);

        //构造器2
        File file3 = new File("E:\\Study\\Code","JavaSenior");
        System.out.println(file3);

        //构造器3
        File file4 = new File(file3,"hi.txt");
        System.out.println(file4);
    }
    @Test
    public void test2(){
        File file1 = new File("hello.txt");
        File file2 = new File("E:\\Study\\JavaBackend\\IO\\hi.txt");

        System.out.println(file1.getAbsolutePath());
        System.out.println(file1.getPath());
        System.out.println(file1.getName());
        System.out.println(file1.getParent());
        System.out.println(file1.length());
        System.out.println(new Date(file1.lastModified()));
        System.out.println("----------");
        System.out.println(file2.getAbsolutePath());
        System.out.println(file2.getPath());
        System.out.println(file2.getName());
        System.out.println(file2.getParent());
        System.out.println(file2.length());
        System.out.println(file2.lastModified());
    }
    @Test
    public void test3(){
        File file = new File("E:\\Study\\Code\\JavaSenior");

        String[] list = file.list();
        for(String str : list){
            System.out.println(str);
        }

        File[] files = file.listFiles();
        for(File f : files){
            System.out.println(f);
        }

    }
    @Test
    public void test4(){
        File file1 = new File("hello.txt");
        File file2 = new File("E:\\Study\\JavaBackend\\IO\\hi.txt");
        boolean renameTo = file1.renameTo(file2);
        System.out.println(renameTo);
    }
    @Test
    public void test5(){
        File file1 = new File("hello.txt");
        System.out.println(file1.isFile());
        System.out.println(file1.isDirectory());
        System.out.println(file1.exists());
        System.out.println(file1.canRead());
        System.out.println(file1.canWrite());
        System.out.println(file1.isHidden());
    }
    @Test
    public void test6() throws IOException {
        //文件的创建
        File file1 = new File("hi.txt");
        if(!file1.exists()){
            file1.createNewFile();
            System.out.println("创建成功");
        }else{
            file1.delete();
            System.out.println("删除成功");
        }
    }
    @Test
    public void test7(){
        //文件目录的创建
        File file1 = new File("E:\\Study\\JavaBackend\\IO\\IO1");
        boolean mkdir = file1.mkdir();
        if(mkdir){
            System.out.println("创建成功");
        }

        File file2 = new File("E:\\Study\\JavaBackend\\IO\\IO2");
        boolean mkdir1 = file2.mkdirs();
        if(mkdir1){
            System.out.println("创建成功");
        }
        //要想删除成功,io2文件目录下不能有子目录或文件
        File file3 = new File("E:\\Study\\JavaBackend\\IO\\IO2");
        System.out.println(file3.delete());
    }
}

运行可执行文件

当要执行一个本地可执行文件,可以使用 java.lang 包中的 Runtime 类。使用 Runtime 类声明一个对象:

Runtime rt;
rt = Runtime.getRuntime(); // 获取 Runtime 对象
File file = new File("C:/windows","Typora.exe");
try {
    rt.exec(file.getAbsolutePath());
} catch (IOException e) {
    throw new RuntimeException(e);
}

2、IO 流原理及流的分类

image-20220724104946389

image-20220724105134109

image-20220724105410723

  • 流的分类:
    1. 操作数据单位:字节流、字符流
    2. 数据的流向:输入流、输出流
    3. 流的角色:节点流、处理流

2、流的体系结构

抽象基类节点流(或文件流)缓冲流(处理流的一种)
InputStreamFileInputStream (read (byte[] buffer))BufferedInputStream (read (byte[] buffer))
OutputStreamFileOutputStream (write(byte[] buffer,0,len))BufferedOutputStream (write(byte[] buffer,0,len))
ReaderFileReader (read (char[] cbuf))BufferedReader (read (char[] cbuf))
WriterFileWriter (write(char[] cbuf,0,len))BufferedWriter (write(char[] cbuf,0,len))

相关信息

  • 对于文本文件(.txt,.java,.c,.cpp),使用字符流处理
  • 对于非文本文件,使用字节流处理

3、字节流

FileInputStream 和 FileOutputStream

① FileInputStream

FileInputStream 类的构造方法:

  • FileInputStream(String name)
  • FileInputStream(File file)

参数 name 和 file 指定的文件称为输入流的源,输入流通过调用 read 方法独处源中的数据

建立输入流时,可能会出现错误,比如文件不存在,此时会抛出 IOException 异常

try{
    FileInputStream in = new FileInputStream("hello.txt");
} catch (IOException e){
}

InputStream 的子类都继承了它的 read 方法,该方法的特点是以字节为单位读取源中的数据:

  • int read():顺序读取源中的单个字节数据,返回字节值(0 - 255 的一个整数),如果达到源的末尾,返回 -1
  • int read(byte b[]):把多个字节读到一个字节数组中,返回实际读取的字节数
  • int read(byte b[],int off,int len)
    • off:首字节在数组中存放的位置
    • len:读取 len 字节
    • 返回实际读取的字节数

相关信息

关于 read 的相关问题:

假设 a.txt 存储 abcdef 几个字符

InputStream is = new FileInputStream(file);
while (is.read() != -1){
    char c = (char) is.read();
    System.out.print(c); // 输出 bdf
}
InputStream is = new FileInputStream(file);
int i;
while ((i = is.read()) != -1){
    char c = (char) i;
    System.out.print(c + " "); // 输出 a b c d e f 
}
InputStream is = new FileInputStream(file);
byte[] b = new byte[5];
while (is.read(b) != -1){
    String str = new String(b);
    System.out.print(str + " "); // 输出 abcde fbcde 
}
InputStream is = new FileInputStream(file);
byte[] b = new byte[5];
int len;
while ((len = is.read(b)) != -1){
    String str = new String(b,0,len);
    System.out.print(str + " "); // 输出 abcde f
}
InputStream is = new FileInputStream(file);
byte[] b = new byte[5];
int len;
while ((len = is.read(b,0,2)) != -1){
    String str = new String(b,0,len);
    System.out.print(str + " "); // ab cd ef
}
FileInputStream fis = null;
try {
    //1、造文件
    File file = new File("hello.txt");
    //2、造流
    fis = new FileInputStream(file);
    //3、读数据
    byte[] buffer = new byte[5];
    int len;
    while ((len = fis.read(buffer)) != -1){
        String str = new String(buffer,0,len);
        System.out.println(str);
    }
} catch (IOException e) {
    e.printStackTrace();
} finally {
    //4、关闭资源
    if(fis != null){
        try {
            fis.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

② FileOutputStream

FileOutputStream 类创建的对象被称为字节输出流,文件字节输出流提供了文件的写入能力

如果输出流指向的文件不存在,java 就会创建该文件,如果指向的文件是已存在的文件,输出流将刷新该文件

FileOutputStream 类构造方法

  • FileOutputStream(String name)
  • FilePutputStream(File file)
  • FileOutputStream(String name,boolean append)
  • FilePutputStream(File file,boolean append)

参数 name 和 file 指定的文件称为输出流的目的地
如果参数 append 为 true,输出流不会刷新该文件,write 方法将从文件末尾开始向文件写入数据
如果 apepend 为 false,输出流将刷新该文件

字节流 OutputStream 的子类都继承了 write(byte b[]) 方法,该方法已字节为单位向文件写入数据

  • public void wirte(byte b[])
  • public void write(byte[],int off,int len)
    • off 为数据的起始偏移量
    • len 要写出的字节数

注意

  • FileOutStream 流顺序地写文件,只要不关闭流,每次调用 write 方法就顺序地向文件写入内容,直到流被关闭(写文件时,一定要先关闭流,再执行后续操作)

  • read 方法顺序地读取文件,只要不关闭流,每次调用 read 都会顺序读取文件的其余内容,直到文件的末尾或文件直接输入流被关闭

public class FileInputOutputStreamTest {
    /*
    * 实现对图片的复制
    */
    @Test
    public void testFileInputOutputStream(){
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            //1、创文件
            File scrFile = new File("tamakoTest.jpg");
            File destFile = new File("tamakoTest2.jpg");
            //2、创流
            fis = new FileInputStream(scrFile);
            fos = new FileOutputStream(destFile);
            //3、读数据
            byte[] buffer = new byte[5];
            int len;
            while((len = fis.read(buffer)) != -1){
                fos.write(buffer,0,len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4、关闭流
            try {
                if (fos != null)
                fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (fis != null)
                fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
    //指定路径下文件的复制方法的创建
    public void copyFile(String srcPath,String destPath){
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            //1、创文件
            File scrFile = new File(srcPath);
            File destFile = new File(destPath);
            //2、创流
            fis = new FileInputStream(scrFile);
            fos = new FileOutputStream(destFile);
            //3、读数据
            byte[] buffer = new byte[1024];
            int len;
            while((len = fis.read(buffer)) != -1){
                fos.write(buffer,0,len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4、关闭流
            try {
                if (fos != null)
                    fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (fis != null)
                    fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

4、字符流

FileReader 和 FileWriter

字节流不能很好的操作 Unicode 字符,使用字节流读取不当会出现「乱码」现象

字符流类 FileReader 和 FileWriter 分别是 Reader 和 Writer 类

构造方法:

  • FileReader(String filename)
  • FileReader(File filename)
  • FileWriter(String filename)
  • FileWriter(File filename)
  • FileWriter(File filename,boolean append):append 为 false,对文件覆盖,append 为 true 对文件的追加

字符流的 read() 和 witre() 方法:

  • int read():读取一个字符,返回一个整数(0 - 65535),未读出返回 -1
  • int read(char[] b):从流中读取 b.length 个字符到数组 b 中,返回实际读取字符数目,如果打达到末尾返回 -1
  • int read(char b[],int off,int len):读取 len 个字符到数组中,off 是首字符在数组中存放位置,返回实际读取数。如果到达文件末尾,返回 -1
  • int write(int n):向文件写入「一个」字符
  • int write(char[] b):向文件写入一个字符数组
  • int write(char[] b,int off,int len):将数组中位于 off 出取出 len 个字符写到文件

注意

  • write 方法将数据先写入缓冲区,当缓冲区溢出时,缓冲区内容自动写到目的地,若流关闭,缓冲区内容立即被写入目的地。
  • 调用 flush() 方法可以立即刷新缓冲区,将当前缓冲区的内容写入目的地
public void testFileReader(){
    FileReader fr = null;
    try {
        //1. File 类的实例化
        File file = new File("hello.txt");
        //2、FileReader 流的实例化
        fr = new FileReader(file);
        //3、数据读入
        //方式一:
        /*
        int data = fr.read();
        while (data != -1){
        System.out.print((char) data);
        data = fr.read();
    }*/
        //方式二:语法上的修改
        int data;
        while ((data = fr.read()) != -1){
            System.out.print((char)data);
        }
    } catch (IOException e) {
        e.printStackTrace();
    }finally {
        try {
            if(fr!=null)
                //4、流的关闭操作
                fr.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
public void testFileReader1() {
    FileReader fr = null;
    try {
        //1、File 类的实例化
        File file = new File("hello.txt");
        //2、FileReader 流的实例化
        fr = new FileReader(file);
        //3、数据读入
        char[] cbuf = new char[5];
        int len;
        while ((len = fr.read(cbuf)) != -1){
            String str = new String(cbuf,0,len);// String 的构造方法
            System.out.println(str);
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (fr != null) {
            try {
                //4、资源的关闭
                fr.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
@Test
public void testFileWriter() {
    FileWriter fw = null;
    try {
        //1、File 类的实例化
        File file = new File("hello1.txt");

        //2、FileWriter 的实例化
        fw = new FileWriter(file,true);// 追加

        //3、写出的操作
        fw.write("I have a dream!\n");
        fw.write("you need to have a dream");
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        //4、流的关闭
        if(fw != null)
        try {
            fw.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

共用 FileReader 和 FileWriter

public void testFileReaderFileWriter(){
    FileReader fr = null;
    FileWriter fw = null;
    try {
        //1、File 类的实例化
        File srcFile = new File("hello.txt");
        File destFile = new File("hello2.txt");

        //2、流的实例化
        fr = new FileReader(srcFile);
        fw = new FileWriter(destFile);

        //3、数据的读入和写出的操作
        char[] cbuf = new char[5];
        int len;//记录每次读入到cbuf数组中字符的个数
        while ((len = fr.read(cbuf)) != -1){
            //每次写入 len 个字符
            fw.write(cbuf,0,len);
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        //4、关闭流资源
        //方式一:
        /*try {
            if(fw != null)
            fw.close();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                if(fr !=null )
                fr.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }*/
        //方式二:
        //调用 try-catch 处理完异常,尽管出现了异常,try-catch-finally 的后续代码依旧执行
        try {
            if(fr != null)
            fr.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            if(fw != null)
            fw.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

5、缓冲流

缓冲输入输出流:

  • BufferedInputStream
  • BufferedOutputStream
  • BufferedReader
  • BufferedWriter

FileReader 的局限性:没有读取提供一行的方法
缓冲流:有更强的读/写能力,内部提供了一个缓冲区,提升流的读取、写入速度

BufferedReader 和 BufferedWriter 类的构造方法:

  • BufferedReader(Reader in)
  • BufferedWriter(Writer out)

BufferedReader 流能读取文本行,即 readLine() 方法
BufferedWriter 流向文件写入一个回行符的方法 newLine()

相关信息

把 BufferedREader 和 BufferedWriter 称为「上层流」,把它们指向的流称为「底层流」
Java 使用缓存技术将上层流和底层流连接

  • 底层字符输入流首先将数据输入缓存,BufferedReader 流再从缓存读取数据
  • BufferedWriter 流将数据写入缓存,底层字符输出流会不断将缓存中的数据写入目的地。当 BufferedWriter 流调用 flush() 或 close() 时,底层流立即将缓存的内容写入目的地

关闭外层流的同时,内层流也会自动关闭,关于内层流的关闭代码可以省略

注意下方代码有 bw.close();bw.flush(); 效果相同) 和没有的区别:

  • bw.close(); 输出语句输出文件原先的内容与新增的内容
  • 没有 bw.close(); 只输出文件原先的内容
  • 如果 FileWriter 构造器的 append 为 false,无输出内容
BufferedReader bf = null;
BufferedWriter bw = null;
try {

    bf = new BufferedReader(new FileReader(new File("C:\\Users\\Tamako\\Desktop\\a.txt")));
    bw = new BufferedWriter(new FileWriter(new File("C:\\Users\\Tamako\\Desktop\\b.txt"),true));
    String str = null;
    while((str = bf.readLine()) != null){
        bw.write(str);
        bw.newLine();
    }
    // bw.close();
    bf = new BufferedReader(new FileReader(new File("C:\\Users\\Tamako\\Desktop\\b.txt")));
    char[] c = new char[2];
    int len = 0;
    while((len = bf.read(c)) != -1){
        System.out.print(new String(c,0,len));
    }

} catch (IOException e) {
    throw new RuntimeException();
}
// 操作 BufferedInputStream、BufferedOutputStream
public class BufferedTest {
public void BufferedStreamTest(){
    BufferedInputStream bis = null;
    BufferedOutputStream bos = null;
    try {
        //1、File 实例化
        File src = new File("tamakoTest.JPG");
        File dest = new File("tamakoTest3.JPG");

        //2.1、节点流实例化
        FileInputStream fis = new FileInputStream(src);
        FileOutputStream fos = new FileOutputStream(dest);
        //2.2、缓冲流实例化
        bis = new BufferedInputStream(fis);
        bos = new BufferedOutputStream(fos);
        //3、复制的细节:读取和写入
        byte[] buffer = new byte[10];
        int len;
        while ((len = bis.read(buffer)) != -1){
            bos.write(buffer,0,len);
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        //4、资源关闭
        //先关外层流,再关内层的流
        if(bos != null){
            try {
                bos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if(bis != null){
            try {
                bis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
// 使用 BufferedReader 和 BufferedWriter 实现文本文件的复制
public void testBufferedReaderBufferedWriter(){
    BufferedReader br = null;
    BufferedWriter bw = null;
    try {
        //创建文件和相应资源
        br = new BufferedReader(new FileReader(new File("hello.txt")));
        bw = new BufferedWriter(new FileWriter(new File("hello3.txt")));
        // 读写操作
        // 方式一:使用 char 数组
        // char[] cbuf = new char[1024];
        // int len;
        // while ((len = br.read(cbuf))!=-1){
        //     bw.write(cbuf,0,len);
        // }

        //方式二:使用 String
        String data;
        while((data = br.readLine())!=null){
            //方法一:
            bw.write(data+"\n");// data中不包含换行符
            //方法二:
            bw.write(data);
            bw.newLine();//提供换行的操作
        }
        
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        //关闭资源
        if(bw != null){
            try {
                bw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if(bw != null){
            try {
                br.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        
    }
    
}

6、随机流

RandomAccessFile 类创建的对象叫随机访问文件流
该类创建的流既可以作为「源」也可以作为「目的地」
即要对一个文件进行读写操作时,创建一个随机访问文件流即可

RandomAccessFile 类的构造方法:

  • RandomAccessFile(String name,String mode)
  • RandomAccessFile(File file,String mode)

参数 name 和 file 用来确定一个文件名或 file 对象
参数 mode 取 r(只读)或 rw(可读/写),决定流对文件的访问权限

RandomAccessFile 类常用方法:

方法描述
close()关闭文件
getFilePointer()获取当前读写位置
length()获取文件长度
read()从文件中读取一个字节的数据
readBoolean()从文件中读取一个 bool 值,0 代表 false,其他代表 true
readByte()读取一个字节
readChar()读取一个字符(两个字节)
readDouble()读取一个双精度浮点值(8 个字节)
readFloat()读取一个单精度浮点值(4 个字节)
readFully(byte[] b)读取 b.length 字节放入数组 b,完全填满该数组
readInt()读取一个 int 值(4 字节)
readLine()读取一个文本行
readLong()读取一个 long 值(8 字节)
readShort()读取一个 short 值(2 字节)
readUnsignedShort()读取一个无符号 short
readUTF()读取一个 UTF 字符串
seek(long position)定位读/写位置
setLength(long newlength)设置文件长度
skipBytes(int n)在文件中跳过给定数量的字节
write(byte b[])写 b.length 字节到文件
writeBoolean(boolean v)把一个布尔值作为单字节写入文件
writeByte(int v)写入一个字节
writeBytes(String s)写入一个字符串
writeChar(char c)写入一个字符
writeChars(String s)写入一个作为字符数据的字符串
writeDouble(double v)写入一个 double 值
writeFloat(float v)写入一个 float 值
writeInt(int v)写入一个 int 值
writeLong(long v)写入一个 long 值
writeShort(int v)写入一个 短型 int 值
writeUTF(String s)写入一个 UTF 字符串
public static void main(String[] args) throws IOException {
    RandomAccessFile in = null;
    try{
        in = new RandomAccessFile("test.txt","rw");
        long length = in.length(); // 获取文件的长度
        long position = 0;
        in.seek(position);  // 定位到开始位置
        while (position < length){
            String str = in.readLine(); // 读取一行
            byte[] b = str.getBytes("ISO-8859-1"); // 重新编码
            str = new String(b);
            position = in.getFilePointer(); // 获取当前读写位置
            System.out.println(str);
        }
    }catch (IOException e){

    }
}

7、数据流

DateInputStream 和 DataOutputStream 类创建的对象称为数据输入流和数据输出流。
这两个流读取一个值时,不必在关心这个数值应当是多少字节。

DateInputStream 和 DataOutputStream 类的构造方法:

  • DataInputStream(InputStream in)
  • DataInputStream(OutputStream out)

DateInputStream 和 DataOutputStream 类的常用方法:

方法描述
close()关闭流
readBoolean/Byte/Char/ Double/Float/Int/ Long/Short/UnsignedByte/UnsignedShort()读取
readUTF()读取一个 UTF 字符串
skipBytes(int n)跳过给定一个数量的字节
writeBoolean/Bytes/Chars/Double/Float/Int/Long/Short(类型 s)写入
writeUTF(String s)写入一个 UTF 字符串
File file = new File("test.txt");
try {
    FileOutputStream fos = new FileOutputStream(file);
    DataOutputStream dos = new DataOutputStream(fos);
    dos.writeDouble(123.45);
    dos.writeChars("abcd");
} catch (IOException e) { }
try{
    FileInputStream fis = new FileInputStream(file);
    DataInputStream dis = new DataInputStream(fis);
    System.out.println(dis.readDouble());
    char c;
    while ( (c = dis.readChar()) != '\0'){ // '\0' 表示空字符
        System.out.println(c);
    }
}catch (IOException e){ }

8、对象流

ObjectOutputStream 和 ObjectInputStream 类分别是 InputStream 和 OutputStream 类的子类

  • 对象输出流使用 writeObject(Object obj) 方法将一个对象 obj 写入一个文件
  • 对象输出流使用 readObject() 方法读取一个对象到程序中,读取的返回值类型为 Object

ObjectOutputStream 和 ObjectInputStream 类构造方法:

  • ObjectOutputStream(InputStream in)
  • ObjectInputStream(OutputStream out)

「序列化」与「反序列化」:

  • 序列化: 用 ObjectOutputStream 类保存基本类型数据或对象的机制
  • 反序列化: 用 ObjectInputStream 类读取基本类型数据或对象的机制

对象克隆:

  • 浅拷贝:调用 Object 的 clone() 即可
  • 深拷贝:使用对象序列化

作用:用于存储和读取基本数据类型数据或对象的处理流。将内存中的 java 对象保存到磁盘中或通过网络传输出去

  • 要想一个 java 对象是可序列的,应满足下列要求:
    1. 需要实现 Serializable 接口
    2. 还须保证其内部的所有属性也必须是可序列化的(默认情况下,基本数据类型都是可序列化的)
    3. 序列化机制:对象序列化机制允许把内存中的 Java 对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。

相关信息

  • Serializable 接口中没有方法,接口中的方法对程序是不可见的
  • 当把一个序列化对象写入对象输出流时,JVM 会实现 Serializable 接口中的方法,将一定格式的文本(对象序列化信息)写入目的地。当 ObjectInputStream 对象流从文件读取对时,就会从文件中读取对象的序列化信息,并根据该信息创建对象
  • ObjectOutputStream 和 ObjectInputStream 不能序列化 static 和 transient 修饰的成员变量
public void testObjectOutputStream(){
    ObjectOutputStream oos = null;
    try {
        oos = new ObjectOutputStream(new FileOutputStream("object.dat"));
        oos.writeObject(new String("luv u"));
        oos.flush();
        oos.writeObject(new Person(20,"AA"));
        oos.flush(); 
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if(oos != null){
            //3
            try {
                oos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
public void testObjectInputStream(){
    ObjectInputStream ois = null;
    try {
        ois = new ObjectInputStream(new FileInputStream("object.dat"));
        Object obj = ois.readObject();
        String str = (String)obj;  // 强转
        Person p = (Person) ois.readObject();
        System.out.println(str);
        System.out.println(p);
    } catch (IOException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } finally {
        if(ois != null){
            try {
                ois.close();
            } catch (IOException e) {
                e.printStackTrace();
            }}}}

7、转换流

  • 转换流:属于字符流

    • InputStreamReader:将一个字节的输入流转换为字符的输入流
    • OutputStreamWriter:将一个字符的输出流转换为字节的输出流
  • 作用:提供字节流与字符流之间的转换

  • 解码:字节、字节数组 --> 字符数组、字符串

  • 编码:字符数组,字符串 --> 字节、字节数组

public class InputStreamReaderTest {
    //此时处理异常的话,仍应使用try-catch-finally
    @Test
    public void test1() throws IOException {
        FileInputStream fis = new FileInputStream("dbcp.txt");
        //InputStreamReader isr = new InputStreamReader(fis);//使用系统默认的字符集
        //参数2:指明字符集,具体使用哪个字符集取决于文件保存时使用的字符集
        InputStreamReader isr = new InputStreamReader(fis, "UTF-8");
        char[] cbuf = new char[200];
        int len;
        while((len = isr.read(cbuf)) != -1){
            String str = new String(cbuf,0,len);
            System.out.println(str);
        }
        isr.close();
    }
    @Test
    public void test2() throws IOException {
        //1、造文件、造流
        File file1 = new File("dbcp.txt");
        File file2 = new File("dbcp_GBK.txt");

        FileInputStream fis = new FileInputStream(file1);
        FileOutputStream fos = new FileOutputStream(file2);

        InputStreamReader isr = new InputStreamReader(fis,"utf-8");
        OutputStreamWriter osw = new OutputStreamWriter(fos,"gbk");
        //2、读写过程
        char[] cbuf = new char[200];
        int len;
        while((len = isr.read(cbuf)) != -1){
            //System.out.println(cbuf);
            osw.write(cbuf);
        }
        //3、资源关闭
        isr.close();
        fos.close();
    }
}

8、其他流的使用

1、标准的输入输出流

System.in:标准的输入流,默认从键盘输入
System.out:标准的输出流,默认从控制台输出

System 类的 setIn(InputStream is),setOut(printStream ps) 方式重新指定输入和输出的流

//练习:从键盘输入字符串,要求将读取到的整行字符串转成大写输出。然后继续
//进行输入操作,直至当输入 e 或者 exit 时,退出程序。  
//方法一:Scanner 实现,调用 next() 返回字符串
//方法二:System.in 实现(System.in --> 转换流 --> BufferedReader的readline()   
public class OtherStreamTest {
    public static void main(String[] args){
        BufferedReader br = null;
        try {
            InputStreamReader isr = new InputStreamReader(System.in);
            br = new BufferedReader(isr);
            while (true){
                String data = br.readLine();
                if ("e".equalsIgnoreCase(data)||"exit".equalsIgnoreCase(data)){
                    System.out.println("break");
                    break;
                }
                String upperCase = data.toUpperCase();
                System.out.println(upperCase);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

2、打印流

  • 打印流:PrintStream 和 PrintWriter
  • 提供了一系列重载的 print() 和 println()
//练习:
PrintStream ps = null;
try {
FileOutputStream fos = new FileOutputStream(new File("D:\\IO\\text.txt"));
// 创建打印输出流,设置为自动刷新模式(写入换行符或字节 '\n' 时都会刷新输出缓冲区)
ps = new PrintStream(fos, true);
if (ps != null) {// 把标准输出流(控制台输出)改成文件
System.setOut(ps);
}
for (int i = 0; i <= 255; i++) { // 输出ASCII字符
System.out.print((char) i);
if (i % 50 == 0) { // 每50个数据一行
System.out.println(); // 换行
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
if (ps != null) {
ps.close();
}
}

9、文件锁

若有几个程序同时处理一个文件,可能会发生混乱
JDK 1.4 后 Java 提供了文件锁

FileLock、FileChannel 类分别在 java.nio 和 java.nio.channels 包中。输入流、输出流读/写文件时可以使用文件锁

使用 RandomAccessFile 类说明文件锁的使用方法:

RandomAccessFile in = new RandomAccessFile("test.txt","rw"); // 创建随机流对象,读写属性为 rw
FileChannel channel = in.getChannel(); // 获得一个连接到底层文件的 FileChannel 对象
FileLock lock = channel.tryLock(); // 通过 tryLock() 或 lock() 方法给文件加锁,获得一个 FileLock(锁)对象
lock.release(); // 释放锁

相关信息

文件加锁后,将禁止任何程序对文件进行操作或再加锁
对一个文件加锁后,如果想读/写文件,必须让 FileLock 对象调用 realse() 方法释放锁

评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v2.15.5