java-I/O流

目录

输入流

文件输入流

缓冲输入流 

推回输入流

数据输入流

输出流

文件输出流

缓冲输出流

properties 配置文件的读取

对象序列化和反序列化


“流”是一个抽象的概念,它是对输入输出设备的一种抽象理解,在java中,对数据的输入输出操作都是以“流”的方式进行的。“流”具有方向性,输入流、输出流是相对的。当程序需要从数据源中读入数据的时候就会开启一个输入流,相反,写出数据到某个数据源目的地的时候也会开启一个输出流。数据源可以是文件、内存或者网络等。

流”序列中的数据可以是未经加工的原始二进制数据,也可以是经过一定编码处理后符合某种格式的特定数据,因此java中的“流”分为两种流:

  • 字节流***:数据流中的最小的数据单元是字节***,一次读入读出8位二进制;
  • 字符流***: 数据流中的最小的数据单元是字符***,一次读入读出16位二进制,java中的字符是Unicode编码|AscII编码。

输入流

抽象基本组件是InputStream类

InputStream
 |
 +--FileInputStream 
 |
 +--ByteArrayInputStream 
 |
 +--PipedInputStream
 |
 +--FilterInputStream
 |
 +--BufferedInputStream 
 |
 +--PushbackInputStream 
 |
 +--DataInputStream 
 |
 +--ObjectInputStream

我们有FileInputStream,ByteArrayInputStream和PipedInputStream,FilterInputStream的具体类。

超类InputStream包含从输入流读取数据的基本方法,所有具体类都支持这些方法。

对输入流的基本操作是从其读取数据。 InputStream类中定义的一些重要方法在下表中列出。

ID方法/说明
read()读取一个字节并将读取的字节作为int返回。 当到达输入流的结尾时,它返回-1。
read(byte[] buffer)读取最大值直到指定缓冲区的长度。 它返回在缓冲区中读取的字节数。 如果到达输入流的结尾,则返回-1。
read(byte [] buffer,int offset,int length)读取最大值到指定长度字节。 数据从偏移索引开始写入缓冲区。 它返回读取的字节数或-1,如果到达输入流的结束。
close()关闭输入流
available()返回可以从此输入流读取但不阻塞的估计字节数。

文件输入流

在Java I/O中,流意味着数据流。流中的数据可以是字节,字符,对象等。

要从文件读取,我们需要创建一个FileInputStream类的对象,它将表示输入流。

InputStream(二进制格式操作):抽象类,基于字节的输入操作,是所有输入流的父类。定义了所有输入流都具有的共同特征。

文件字节输入流 FileInputStream 就是其中最常用的子类,该类最常用的方法是 read 方法,该方法每次调用都需要传入一个 byte[] 并返回该次读取到的字节数量,如果读取到达文件末尾则返回 -1。

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

public class FileInputStreamTest {
    public static void main(String[] args) throws IOException {
        File file = new File("C:/file/log.txt");
        InputStream inputStream = new FileInputStream(file);
        byte[] bytes = new byte[1024];
        int n = 0;
        while ((n = inputStream.read(bytes)) != -1) {
            System.out.println(n);
            String s = new String(bytes, 0, n);
            System.out.println(s);
        }
        inputStream.close();
    }
}

字节输入流一般针对文本文件和二进制文件的读取操作。但是需要注意以下几点:

  1. 字节输入流读取文件时,一般前几次返回的读取的字节数量都为字节数组的长度,只有最后一次可能到达不了数组的长度。
  2. 字节输入流可以获取文件的内容,针对字符文件可以将字节数组转为字符串输出 new String(bytes,0,n),但是有时可能会将中文字符切成两半而出现乱码。
  3. UFT-8 编码中,大部分中文占用三个字节,可以使用字符串的 getBytes 方法获取任意字符串在 UTF-8 编码时的字节序列(数组)。
  4. 字节输入流读取二进制文件后转换为字符串后输出为乱码。
  5. 如果文件编码是 ANSI 编码,则输出时使用字符编码 GB2312,否则输出乱码。new String(bytes,0,n,"GB2312")。win7 系统文本文件默认为 ANSI 编码。

缓冲输入流 

BufferedInputStream通过缓冲数据向输入流添加功能。

它维护一个内部缓冲区以存储从底层输入流读取的字节。

我们创建缓冲区输入流如下:

String srcFile =“test.txt";
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile));

以下代码显示如何使用BufferedInputStream从文件读取。

import java.io.BufferedInputStream;
import java.io.FileInputStream;

public class Main {
    public static void main(String[] args) {
        String srcFile = "test.txt";
        try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(
            srcFile))) {
            // Read one byte at a time and display it
            byte byteData;
            while ((byteData = (byte) bis.read()) != -1) {
                System.out.print((char) byteData);
            }
        } catch (Exception e2) {
            e2.printStackTrace();
        }
    }
}

推回输入流

PushbackInputStream向输入流添加功能,允许我们使用其unread()方法推回读取的字节。

有三个版本的unread()方法。一个让我们推回一个字节,另外两个让我们推回多个字节。

import java.io.FileInputStream;
import java.io.PushbackInputStream;

public class Main {
    public static void main(String[] args) {
        String srcFile = "test.txt";

        try (PushbackInputStream pis = new PushbackInputStream(new FileInputStream(
            srcFile))) {
            byte byteData;
            while ((byteData = (byte) pis.read()) != -1) {
                System.out.print((char) byteData);
                pis.unread(byteData);
                // Reread the byte we unread
                byteData = (byte) pis.read();
                System.out.print((char) byteData);
            }
        } catch (Exception e2) {
            e2.printStackTrace();
        }
    }
}

数据输入流

DataInputStream可以从输入流中读取Java基本数据类型值。

DataInputStream类包含读取数据类型值的读取方法。例如,要读取int值,它包含一个readInt()方法;读取char值,它有一个readChar()方法等。它还支持使用readUTF()方法读取字符串。

以下代码显示了如何从文件读取原始值和字符串。

import java.io.DataInputStream;
import java.io.FileInputStream;

public class Main {
    public static void main(String[] args) {
        String srcFile = "primitives.dat";

        try (DataInputStream dis = new DataInputStream(new FileInputStream(srcFile))) {
            // Read the data in the same order they were written 
            int intValue = dis.readInt();
            double doubleValue = dis.readDouble();
            boolean booleanValue = dis.readBoolean();
            String msg = dis.readUTF();

            System.out.println(intValue);
            System.out.println(doubleValue);
            System.out.println(booleanValue);
            System.out.println(msg);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

输出流

在抽象超类OutputStream中定义了三个重要的方法:write(),flush()和close()。

write()方法将字节写入输出流。它有三个版本,允许我们一次写一个字节或多个字节。

flush()方法用于将任何缓冲的字节刷新到数据宿。

close()方法关闭输出流。

文件输出流

要写入文件,我们需要创建一个FileOutputStream类的对象,它将表示输出流。

OutputStream(二进制格式操作):抽象类。基于字节的输出操作。是所有输出流的父类。定义了所有输出流都具有的共同特征。

文件字节输出流 FileOutputStream 就是其中最常用的子类,该类有一个常用的方法 write 用于将字节数组写入文件。如果在写入过程中需要换行可以使用转义字符 \n 表示。在不关闭流的情况下每次 write 的内容都会追加在文件中。如果输出流指定的文件不存在,则文件创建输出流时会自动创建该文件,但是前提是父路径必须存在,不可以将输出流的路径直接指向文件或盘符。

public class FileOutputStreamTest {
    public static void main(String[] args) throws Exception {
        File f = new File("C:\\file\\ss.txt");//直接覆盖写同一个文件
        //字节输出流
        OutputStream outputStream = new FileOutputStream(f);
        String s = "hello,world!\r\n";
        String s1 = "中国人";
        outputStream.write(s.getBytes());
        outputStream.write(s1.getBytes());
        outputStream.close();
    }
}

注意:在程序运行期间如果不手动关闭流,则该文件会一直占用,无法删除。

如果文件包含数据,数据将被擦除。为了保留现有数据并将新数据附加到文件,我们需要使用FileOutputStream类的另一个构造函数,它接受一个布尔标志,用于将新数据附加到文件。

要将数据附加到文件,请在第二个参数中传递true,使用以下代码。

FileOutputStream fos   = new FileOutputStream(destFile, true);

 缓冲输出流

在字符文件操作方面为了提高效率引入了缓冲字符流 BufferedReader 和 BufferedWriter。它们操作文件的基本单位可以升级为一行,无论该文件一行有多少字符都能通过 readLine 读取,同时写入时可以直接指定任意的字符串。

import java.io.*;

public class BufferedCopyTxtFile {
    public static void main(String[] args) throws Exception {
        FileReader reader = new FileReader("C:\\ff\\hsp.txt");
        BufferedReader bufferedReader = new BufferedReader(reader);
        FileWriter writer = new FileWriter("C:\\hsp1.txt");
        BufferedWriter bufferedWriter = new BufferedWriter(writer);
        String s = "";
        while ((s = bufferedReader.readLine()) != null) {
            bufferedWriter.write(s + "\n");
        }
        bufferedReader.close();
        bufferedWriter.close();
        reader.close();
        writer.close();
    }
}

关于输入流与输出流关闭问题需要注意以下几点:

1.输入流如果不关闭。在进程运行期间,该文件一直被占用,无法删除。

2.输出流不关闭文件内容将不能正常保存到该文件中。

3.流的关闭顺序一般要求与打开顺序相反,即:后开先关。

properties 配置文件的读取

在实际开发中经常会把一些易发生改变的配置信息放到配置文件中(如:数据库的连接信息,文件上传的位置信息等),一般在 java 中配置文件常用properties 格式,该文件需要在 src 下创建。如下,在 src 下创建 application.properties 文件。

id=12
name=wpf<br/>

读取配置文件里面值

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

public class PropertiesTest {
    public static void main(String[] args) throws IOException {
        InputStream inputStream = PropertiesTest.class.getClassLoader().getResourceAsStream( "application.properties" );
        //读取src下名字叫thisFile的文件
        Properties properties = new Properties();
        //作用是从文件流里面获取键值对
        properties.load( inputStream );//加载文件流
        System.out.println( properties.getProperty( "name" ));
        stream.close();
    }
}

 轻松使用 properties.getProperty( "key" ) 方法就可以通过健获取文件流里面的值。

对象序列化和反序列化

Java 序列化是指把 Java 对象转换为字节序列 byte[] 的过程;而 Java 反序列化是指把字节序列恢复为 Java 对象的过程。

在程序运行过程中,经常会把对象序列化到到磁盘文件中,以保存对象数据。在需要的时候再从文件中反序列化出来,完成对象的持久化。要想类的对象能够序列化和反序列化,该类必须实现 Serializable 接口,否则会报 NotSerializableException。

案例:

import java.io.*;
import java.util.Scanner;

public class SerializableTest {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        // writeObject();
        readObject();
    }

    private static void readObject() throws IOException, ClassNotFoundException {
        FileInputStream inputStream = new FileInputStream("C:/person.txt");
        ObjectInputStream stream = new ObjectInputStream(inputStream);
        //获取对象,由于无法判断对象的类型,故采用 Object 类型接收。
        Object o = stream.readObject();
        //确定是 Person 类型后可以将之强转为 Person 类型
        Person person = (Person) o;
        System.out.println(person);
        stream.close();
        inputStream.close();
    }

    private static void writeObject() throws IOException {
        //Scanner 专门用于接收控制台输入的数据
        Scanner scanner = new Scanner(System.in);
        FileOutputStream outputStream = new FileOutputStream("C:/person.txt");
        ObjectOutputStream stream = new ObjectOutputStream(outputStream);

        System.out.println("请输入 id");
        //通过 scanner 获取一个整数,程序运行到该位置会自动卡住,直到用户输入内容后回车程序才能继续。
        int id = scanner.nextInt();
        System.out.println("请输入名字");
        String name = scanner.next();
        System.out.println("请输入密码");
        String password = scanner.next();

        Person person = new Person(id, name, password);
        //序列化对象到文件
        stream.writeObject(person);
        //刷新
        stream.flush();
        //关闭资源
        stream.close();
        outputStream.close();
        scanner.close();

    }
}

class Person implements Serializable {
    private int id;
    private String name;
    private String password;

    ...省略 构造方法、get、set 与 toString 方法。
}

注意:对象写入和读出之间不能修改类的结构,否则会报:InvalidClassException。

java.io.InvalidClassException: cn.hx.Person; local class incompatible: 
stream classdesc serialVersionUID = 2431545036432879007, local class serialVersionUID = -2205805426406937374

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值