带你了解Java高级编程-----IO流


I/O是Input/Output的缩写, I/O技术是非常实用的技术,用于处理设备之间的数据传输。如读/写文件,网络通讯等。Java程序中,对于数据的输入/输出操作以“流(stream)” 的方式进行。

1.Java.io 包几乎包含了所有操作输入、输出需要的类。所有这些流类代表了输入源和输出目标。
2.一个流可以理解为一个数据的序列。输入流表示从一个源读取数据,输出流表示向一个目标写数据。

一、File类的使用

1.路径

路径分为绝对路径、相对路径。

绝对路径:是一个固定的路径,从盘符开始;例如:D:\Java_space\TG\info.txt
相对路径:从相对于某个位置开始;例如:info.txt

在这里插入图片描述

2.常用方法

方法方法说明
public String getAbsolutePath()获取绝对路径
public String getParent()获取上层文件目录路径。若无,返回null
public boolean renameTo(File dest)把文件重命名为指定的文件路径
public boolean isDirectory()判断是否是文件目录
public boolean isFile()判断是否是文件
public boolean exists()判断是否存在
public boolean createNewFile()创建文件。若文件存在,则不创建,返回false
public boolean mkdirs()创建文件目录。如果上层文件目录不存在,一并创建
public boolean delete()删除文件或者文件夹
import org.junit.Test;

import java.io.File;
import java.io.IOException;


public class FileDemo {

    /**
     *
     * 1. 利用File构造器,new 一个文件目录file
     *      1)在其中创建多个文件和目录
     *      2)编写方法,实现删除file中指定文件的操作
     * @throws IOException
     */
    @Test
    public void test() throws IOException {
        File file = new File("d:\\io\\io1\\hello.txt");
        //创建一个与file同目录下的另一个文件,文件名为:haha.txt
        File destFile = new File(file.getParent(),"haha.txt");
        //boolean newFile = destFile.createNewFile();
        if (!destFile.exists()){
            destFile.createNewFile();
            System.out.println("创建成功");
        }else {
            destFile.delete();
            System.out.println("删除成功");
        }
    }


    /**
     * 2. 判断指定目录下是否有后缀名为.jpg的文件,如果有,就输出该文件名称
     */
    @Test
    public void test2(){
        File file = new File("d:\\io\\io2");
        File[] list = file.listFiles();
        for (File f :
                list) {
            if (f.getName().endsWith("jpg")){
                System.out.println(f);
            }
        }
        System.out.println("------第二种方法------");
        String[] strings = file.list();
        for (String f1 :
                strings) {
            if (f1.endsWith("jpg")){
                System.out.println(f1);
            }
        }
    }
    /**
     * 3. 遍历指定目录所有文件名称,包括子文件目录中的文件。
     */
    @Test
    public void test3(){
        //递归: 文件目录
        //1.创建目录对象
        File file = new File("d:\\io");
        //2.打印目录的子文件
        printSubFile(file);

    }

    private static void printSubFile(File file) {
        //3.打印目录的子文件
        File[] listFiles = file.listFiles();

        for (File f :listFiles) {
            if (f.isDirectory()) {//文件目录
                printSubFile(f);
            } else { //文件
                System.out.println(f.getAbsolutePath());
            }
        }
    }
}

二、IO流原理、分类

1.原理

①输入input:读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中。
②输出output:将程序(内存)数据输出到磁盘、光盘等存储设备中。

2.抽象基类⭐

抽象基类字节流字符流
输入流InputStreamReader
输出流OutputStreamWriter
  • 【结论】:
  • 1.对于文本文件(.txt , .java , .c , .cpp),使用字符流处理
  • 2.对于非文本文件(.jpg , .mp3 , .mp4 , .avi , .doc , .ppt , …)使用字节流处理

三、节点流(文件流)⭐

1.读取文件(4步法)

1.对File类实例化
2.引入流
3.读取操作
4.关闭资源

【不要用throws抛出异常,防止造完对象后无法关闭流,造成内存的浪费。改用try- catch -finally】
以下代码为更清楚演示步骤:

 @Test
    public void testFileReader1() throws IOException {
        //1.File类的实例化
        File file = new File("hello");
        //2.FileReader流的实例化
        FileReader fr = new FileReader(file);
        //3.读入\写出的操作
        //read(char[] cbuf):返回每次读入cbuf数组中的字符个数。如果达到文件末尾,返回-1
        char[] cbuf = new char[5];  //一次读取五个字符
        int len;
        while ((len = fr.read(cbuf)) != -1){
            //方式一
           /* 错误的写法
           for (int i = 0; i < cbuf.length; i++) {
                System.out.print(cbuf[i]);
            }*/

            for (int i = 0; i < len; i++) {
                System.out.print(cbuf[i]);
            }

            //方式二
            /* 错误的写法
            String str = new String(cbuf);
            System.out.println(str);
            */
            String str = new String(cbuf,0,len);
            System.out.print(str);

        }
        //4.资源的关闭
        try {
            fr.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
//使用字节流FileInputStream 处理文本文件,可能出现乱码
    @Test
    public void testFileInputStream() {
        FileInputStream fis = null;
        try {
            //1.实例化File类的对象,指明要操作的文件
            File file = new File("hello");
            //2.提供具体的流
            fis = new FileInputStream(file);
            //3.读取数据
            byte[] buffer = new byte[5];
            int len;
            while ((len = fis.read(buffer)) != -1){
                String s = new String(buffer, 0, len);
                System.out.print(s);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4.资源的关闭
            try {
                if (fis != null)
                fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

2.写入文件(4步法)

1.实例File类对象
2.提供流
3.写入操作
4.关闭资源

 @Test
    public void testFileWriter() {
        FileWriter fw = null;
        try {
            //1.提供File类的对象,指明写出到的文件
            File file = new File("hello1.txt");
            //2.提供FileWriter的对象,用于数据的写入
            fw = new FileWriter(file,false);
            //3.写出的操作
            fw.write("I have a apple,apple pen ~\n");
            fw.write("I have a dream!");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4.资源的关闭
            if (fw != null){
                try {
                    fw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

进行文件的复制操作(先读取一个原文件中的内容,之后将读取到的内容写入新文件中)【文本文件,字符流】

    @Test
    public void testFileReaderFileWriter() {
        FileReader fr = null;
        FileWriter fw = null;
        try {
            //1.创建File类的对象,指明读入和写出的文件
            File srcFile = new File("hello");
            File destFile = new File("hello2.txt");
            /* 不能使用字符流来处理图片等字节数据
            File srcFile = new File("长春大学图标.jpg");
            File destFile = new File("长春大学图标1.jpg");
            */

            //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 {
                    fr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }*/

            /*方式二 : 二者并列 */
            try {
                if (fw != null) {
                    fw.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

            try {
                fr.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

四、缓冲流⭐

1.目的

提高数据读写的速度
原因:Java API提供了带缓冲功能的流类,在使用这些流类时,会创建一个内部缓冲区数组,缺省使用8192个字节(8Kb)的缓冲区。

2.读取、写入文件(4步法)

使用缓冲流的文件复制操作:【非文本文件,字节流】

 /*
    实现非文本文件的复制(在原来的文件复制的基础加上了缓冲流,提高了复制的速度)
    */
    @Test
    public void BufferedStreamTest(){
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        try {
            //1.造文件
            File srcFile = new File("长春大学图标.jpg");
            File destFile = new File("长春大学图标2.jpg");

            //2.造流
            //2.1  造节点流
            FileInputStream fis = new FileInputStream(srcFile);
            FileOutputStream fos = new FileOutputStream(destFile);
            //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.资源的关闭【关闭四个,先关闭外层流,再关闭内层流】
            try {
                if (bos != null){
                    bos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (bis != null){
                    bis.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        /*说明: 关闭外层流的同时,内层流也会自动的进行关闭。关于内层流的关闭,可以进行省略
        fos.close();
        fis.close();*/
        }

    }

五、转换流⭐

转换流提供了在字节流和字符流之间的转换。

Java API提供了两个转换流:

  1. InputStreamReader:将InputStream转换为Reader
  2. OutputStreamWriter:将OutputStream转换为Writer
public class 转换流 {

    /*
    此时的处理已异常也应使用 try-catch-finally
    InputStreamReader的使用,实现字节的输入流到字符的输入流的转换
    */
    @Test
    public void test1() throws IOException {
        FileInputStream fis = new FileInputStream("路线图.txt");
        //InputStreamReader isr = new InputStreamReader(fis); //使用系统默认的字符集
        //参数2指明了字符集:具体使用那个,取决与文件保存时使用的字符集
        InputStreamReader isr = new InputStreamReader(fis,"UTF-8");

        char[] cbuf = new char[20];
        int len;
        while ((len = isr.read(cbuf)) != -1){
            String str = new String(cbuf, 0, len);
            System.out.println(str);
        }
        isr.close();
    }


    /*
    综合使用InputStreamReader 和 OutputStreamWriter
    */
    @Test
    public void test2() throws IOException {
        File file1 = new File("路线图.txt");
        File file2 = new File("路线图_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");

        char[] cbuf = new char[20];
        int len;
        while ((len = isr.read(cbuf)) != -1){
            osw.write(cbuf,0,len);
        }

        osw.close();
        isr.close();

    }
}

字符集

  • ASCII:美国标准信息交换码。用一个字节的7位可以表示。
  • ISO8859-1:拉丁码表。欧洲码表用一个字节的8位表示。
  • GB2312:中国的中文编码表。最多两个字节编码所有字符
  • GBK:中国的中文编码表升级,融合了更多的中文文字符号。最多两个字节编码
  • Unicode:国际标准码,融合了目前人类使用的所有字符。为每个字符分配唯一的字符码。所有的文字都用两个字节来表示。
  • UTF-8:变长的编码方式,可用1-4个字节来表示一个字符。

六、标准输入、输出流

1.System.in : 标准的输入流,默认从键盘输入
2.System.out : 标准的输出流,默认从控制台输出
3.System类的setIn(InputStream is) / setOut(PrintStream ps)方式重新指输入和输出的流

七、打印流

实现将基本数据类型的数据格式转化为字符串输出
1.PrintStream
2.PrintWriter

 @Test
    public void test2(){
        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();
            }
        }
    }

八、数据流

为了方便地操作Java语言的基本数据类型和String的数据,可以使用数据流。

public void test3(){
        DataOutputStream dos = null;
        try {
            //1.2  提供文件,提供流
            dos = new DataOutputStream(new FileOutputStream("data.txt"));

            //3.流的操作
            dos.writeUTF("我是数据流");
            dos.flush();  //刷新操作,将内存中的数据写入文件
            dos.writeInt(18);
            dos.flush();
            dos.writeBoolean(true);
            dos.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4.关闭资源
            if (dos != null){
                try {
                    dos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

九、对象流⭐

ObjectInputStreamObjectOutputStream

  • 作用: 用于储存和读取基本数据类型数据或对象的数据流。

它的强大之处就是可以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来。

序列化

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

public class ObjectInputOutputStreamTest {

    /*
    序列化过程:将内存中的Java对象保存到磁盘中或通过网络传播出去
    使用ObjectOutputStream实现
    */
    @Test
    public void testObjectOutputStream(){
        ObjectOutputStream oos = null;
        try {
            //1.2.
            oos = new ObjectOutputStream(new FileOutputStream("object.dat"));
            //3.
            oos.writeObject(new String("我爱Java"));
            oos.flush();  //刷新操作

            oos.writeObject(new Person("lxx",18));  //需要Person类中实现Serializable
            oos.flush();

            oos.writeObject(new Person("mjc",18,new Account(9999))); //Account类也需要实现Serializable
            oos.flush();

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4.
            if (oos != null) {
                try {
                    oos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }


    /*
    反序列化: 将磁盘文件中的对象还原为内存中的一个Java对象
    使用ObjectInputStream
    */
    @Test
    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();
            Person p1 = (Person) ois.readObject();

            System.out.println(str);
            System.out.println(p);
            System.out.println(p1);
            //Person{name='lxx', age=18, account=null}
			//Person{name='mjc', age=18, account=Account{balance=9999.0}}
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            if (ois != null){
                try {
                    ois.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}


//创建一个实现Serizlizable的Person类
public class Person implements Serializable {  //标识接口

    public static final long serialVersionUID = 9248178456L;  //UID 的数字随便定义

    private String name;
    private int age;
    private Account account;

    public Person(String name, int age, Account account) {
        this.name = name;
        this.age = age;
        this.account = account;
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", account=" + account +
                '}';
    }
}

class Account implements Serializable{
    public static final long serialVersionUID = 243235556L;  //UID 的数字随便定义

    private double balance;

    public Account(double balance) {
        this.balance = balance;
    }

    public double getBalance() {
        return balance;
    }

    public void setBalance(double balance) {
        this.balance = balance;
    }

    @Override
    public String toString() {
        return "Account{" +
                "balance=" + balance +
                '}';
    }
}

十、随机存取文件流

1.RandomAccessFile直接继承于java.lang.Object类,实现了DataInput 和 DataOutput 接口
2.RandomAccessFile既可以作为一个输入流,又可以作为一个输出流
3.创建 RandomAccessFile 类实例需要指定一个 mode 参数,该参数指定 RandomAccessFile 的访问模式:

r: 以只读方式打开
rw:打开以便读取和写入
rwd:打开以便读取和写入;同步文件内容的更新
rws:打开以便读取和写入;同步文件内容和元数据的更新

4.如果RandomAccessFile作为输出流时,写出到的文件如果不存在,则在执行过程中自动创建
如果写出到的文件存在,则会对原有文件内容进行覆盖。(默认情况下,从头覆盖)

public class RandomAccessFileTest {

    @Test
    public void test1(){
        RandomAccessFile raf1 = null;
        RandomAccessFile raf2 = null;
        try {
            raf1 = new RandomAccessFile(new File("图标.jpg"),"r");
            raf2 = new RandomAccessFile(new File("图标2.jpg"),"rw");

            byte[] buffer = new byte[1024];
            int len;
            while((len = raf1.read(buffer)) != -1){
                raf2.write(buffer,0,len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (raf1 != null){
                try {
                    raf1.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            if (raf2 != null){
                try {
                    raf2.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }

    @Test
    public void test2() throws IOException {
        RandomAccessFile raf1 = new RandomAccessFile(new File("hello"), "rw");

        raf1.seek(3); //将指针调到角标为pos的位置
        raf1.write("看我,把谁都给替换掉了".getBytes());

        raf1.close();
    }

    /* 使用RandomAccessFile实现数据插入的效果 */
    @Test
    public void test3() throws IOException {
        RandomAccessFile raf1 = new RandomAccessFile(new File("hell0"), "rw");

        raf1.seek(3);
        //保存指针pos后面的所有数据
        StringBuilder builder = new StringBuilder((int) new File("hello").length());
        byte[] buffer = new byte[20];
        int len;
        while ((len = raf1.read(buffer)) != -1){
            builder.append(new String(buffer,0,len));
        }
        //调回指针,写入想要的内容
        raf1.seek(3);
        raf1.write("我来插队啦".getBytes(StandardCharsets.UTF_8));

        //将StringBuilder中的数据写入到文件中
        raf1.write(builder.toString().getBytes());

        if (raf1 != null){
            raf1.close();
        }
    }

}

  • 6
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

符工爱奇

欢迎投币支持(●'◡'●)

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值