基本IO操作--字节流

一、InputStream与OutputStream
1. 输入与输出

我们编写的程序除了自身会定义一些数据信息外,经常还会引用外界的数据,或是将自身的数据发送到外界。比如,我们编写的程序想读取一个文本文件,又或者我们想将程序中的某些数据写入到一个文件中。这时我们就要使用输入与输出。

输入: 读
输出: 写
流: 单向

什么是输入:输入是一个从外界进入到程序的方向,通常我们需要“读取”外界的数据时,使用输入。所以输入是用来读取数据的。
什么是输出:输出是一个从程序发送到外界的方向,通常我们需要”写出”数据到外界时,使用输出。所以输出是用来写出数据的。

2. 节点流与处理流
    低级    高级

按照流是否直接与特定的地方 (如磁盘、内存、设备等) 相连,分为节点流和处理流两类。
节点流:可以从或向一个特定的地方(节点)读写数据。

处理流:是对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写。
处理流的构造方法总是要带一个其他的流对象做参数。一个流对象经过其他流的多次包装,称为流的链接。

高级流不能独立存在 用来处理其他流的
目的:简化读写操作

3. InputStream与OutputStream常用方法

以字节为单位读取数据的流: 字节输入流

InputStream是所有字节输入流的父类,其定义了基础的读取方法,常用的方法如下:
int read()
读取一个字节,以int形式返回,该int值的”低八位”有效,若返回值为-1则表示EOF。
int read(byte[] d)
尝试最多读取给定数组的length个字节并存入该数组,返回值为实际读取到的字节量。

OutputStream是所有字节输出流的父类,其定义了基础的写出方法,常用的方法如下:
void write(int d)
写出一个字节,写的是给定的int的”低八位”
void write(byte[] d)
void write(byte[] d intoffset intlen)
将给定的字节数组中的所有字节全部写出

这两种流是抽象类 不能实例化

二、 文件流
1. 创建FIS对象

FileInputStream是文件的字节输入流,我们使用该流可以以字节为单位读取文件内容。
FileInputStream有两个常用的构造方法:
FileInputStream(File file):
创建用于读取给定的File对象所表示的文件FIS
例如:
File file = new File("demo.dat");
FileInputStream fis
= new FileInputStream(file);//创建一个用于读取demo.dat文件的输入流
FileInputStream(String name):
创建用于读取给定的文件系统中的路径名name所指定的文件的FIS
例如
FileInputStream fis
//创建一个用于读取demo.dat文件的输入流
= new FileInputStream("demo");
2. 创建FOS对象(重写模式)

FileOutputStream是文件的字节输出流,我们使用该流可以以字节为单位将数据写入文件。
构造方法:
FileOutputStream(File file)
创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
例如:
FIle file = new File("demo.dat");
FileOutputStream fos = new FileOutputStream(file);
FileOutputStream(String filename):
创建一个向具有指定名称的文件中写入数据的输出文 件流。
例如:
FileOutputStream fos = new FileOutputStream("demo.dat");
这里需要注意,若指定的文件已经包含内容,那么当使用FOS对其写入数据时,会将该文件中原有数据全部清除。
3. 创建FOS对象(追加模式)

通过上一节的构造方法创建的FOS对文件进行写操作时会覆盖文件中原有数据。若想在文件的原有数据之后追加新数据则需要以下构造方法创建FOS
构造方法:
FileOutputStream(File file,boolean append)
创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
例如:
File file = new File("demo.dat");
FileOutputStream fos = new FileOutputStream(file,true);
FileOutputStream(String filename,boolean append):
创建一个向具有指定名称的文件中写入数据的输出文 件流。
例如:
FileOutputStream fos = new FileOutputStream("demo.dat",true);
以上两个构造方法中,第二个参数若为true,那么通过该FOS写出的数据都是在文件末尾追加的。

 1 /**
 2  * 使用文件字节输出流向文件中写出数据
 3  * @author Administrator
 4  *
 5  */
 6 public class TestFOS1 {
 7     public static void main(String[] args) throws IOException {
 8         /*
 9          * 创建一个文件字节输出流用于向fos.dat文件中
10          * 写出字节数据
11          */
12         /*
13          * 添加布尔值参数  true  变成追加型操作
14          * FileOutputStream(File file,boolean append)
15            FileOutputStream(String str,boolean append)
16          */
17         FileOutputStream fos = new FileOutputStream("fos.dat",true);
18         //写一个字符串
19         fos.write("hello world".getBytes("GBK"));
20
21         //流用完了要记得关闭
22         fos.close();
23
25     }
26 }

4. read()和write(int d)方法

FileInputStream继承自InputStream,其提供了以字节为单位读取文件数据的方法read。
int read()
从此输入流中读取一个数据字节,若返回-1则表示EOF(End Of File)
FileOutputStream继承自OutputStream,其提供了以字节为单位向文件写数据的方法write。
void write(int d)
将指定字节写入此文件输出流。,这里指写给定的int值的”低八位”
例如

FileOutputStream fos = new FileOutputStream("demo.dat");
fos.write(‘A‘);//这里要注意,char占用2个字节,但这里只写入了1个字节。

5. read(byte[] d)和write(byte[] d)方法

FileInputStream也支持批量读取字节数据的方法:
int read(byte[] b)
从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中
FileOutputStream也支持批量写出字节数据的方法:
void write(byte[] d)
将 b.length 个字节从指定 byte 数组写入此文件输出流中。
例如:

FileOutputStream fos = new FileOutputStream("demo.txt");
byte[] data = "HelloWorld".getBytes();
fos.write(data);//会将HelloWorld的所有字节写入文件。
void write(byte[] d,int offset,int len)

将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此文件输出流。
例如:

FileOutputStream fos = new FileOutputStream("demo.txt");
byte[] data = "HelloWorld".getBytes();
fos.write(data,5,5);//只会将world这5个字节写入文件。
**
 * 使用文件输入流将数据从文件中读取
 * @author Administrator
 *
 */
class TestFIS{
    public static void main(String[] args) throws IOException {
        /*
         * 使用文件字节输入流读取fos.dat文件
         */
        FileInputStream fis =new FileInputStream("fos.dat");

        int d = -1;
        while((d=fis.read())!=-1){
            char c=(char)d;
            System.out.print(c);
        }
    }
}
/**
 * 使用文件的输入流与输出流实现文件复制工作
 */

class TestCopy{
    /*
     * 1.创建文件输入流用于读取原文件
     * 2.创建文件输出流用于向目标文件中写
     * 3.循环从源文件中读取每一个字节
     * 4.将读取到的每一个字节写入目标文件
     * 5.关闭两个流
     */
    public static void main(String[] args) throws IOException {
        FileInputStream fis =new FileInputStream("fos.dat");

        FileOutputStream fos = new FileOutputStream("fos1.dat",true);
        int d=-1;
        while((d=fis.read())!=-1){
            //char c= (char)d;
            fos.write(d);
        }
        fis.close();
        fos.close();

    }
}
/**
 * 使用文件流的批量读和批量写 复制文件
 */

class TestCopy2{
    public static void main(String[] args) throws IOException {
FileInputStream fis =new FileInputStream("fos.dat");

        FileOutputStream fos = new FileOutputStream("fos1.dat",true);
        byte [] b= new byte[10*1024];
        int d=-1;
        while((d=fis.read(b))!=-1){

            fos.write(d);
        }
        fis.close();
        fos.close();
    }
}

三、缓冲流
1. BIS基本工作原理

在读取数据时若以字节为单位读取数据,会导致读取次数过于频繁从而大大的降低读取效率。为此我们可以通过提高一次读取的字节数量减少读写次数来提高读取的效率。
BufferedInputStream是缓冲字节输入流。其内部维护着一个缓冲区(字节数组),使用该流在读取一个字节时,该流会尽可能多的一次性读取若干字节并存入缓冲区,然后逐一的将字节返回,直到缓冲区中的数据被全部读取完毕,会再次读取若干字节从而反复。这样就减少了读取的次数,从而提高了读取效率。
BIS是一个处理流,该流为我们提供了缓冲功能。
2. BOS基本工作原理

与缓冲输入流相似,在向硬件设备做写出操作时,增大写出次数无疑也会降低写出效率,为此我们可以使用缓冲输出流来一次性批量写出若干数据减少写出次数来提高写 出效率。
BufferedOutputStream缓冲输出流内部也维护着一个缓冲区,每当我们向该流写数据时,都会先将数据存入缓冲区,当缓冲区已满时,缓冲流会将数据一次性全部写出。
3. 使用BIS与BOS实现缓冲输入输出

使用缓冲流来实现文件复制:

FileInputStream fis = new FileInputStream("java.zip");
BufferedInputStream bis = new BufferedInputStream(fis);
FileOutputStream fos = new FileOutputStream("copy_java.zip");
BufferedOutputStream bos = new BufferedOutputStream(fos);
int d = -1;
while((d = bis.read())!=-1){
bos.write(d);
}
bis.close();//读写完毕后要关闭流,只需要关闭最外层的流即可
bos.close();

4. flush方法

使用缓冲输出流可以提高写出效率,但是这也存在着一个问题,就是写出数据缺乏即时性。有时我们需要需要在执行完某些写出操作后,就希望将这些数据确实写出,而非在缓冲区中保存直到缓冲区满后才写出。这时我们可以使用缓冲流的一个方法flush。
void flush()
清空缓冲区,将缓冲区中的数据强制写出。

BufferedOutputStream bos
            = new BufferedOutputStream(
                new FileOutputStream("demo.dat")
            );
bos.write(‘a‘);//并没有向磁盘写出,而是写入到了BOS的缓存中
bos.flush();//强制将缓存中的数据一次性写出,这时‘a’才会被写入磁盘
bos.close();//实际上,close()方法在变比缓冲流前也调用了flush()
class TestFlow2{
    public static void main(String[] args) throws IOException {
    FileOutputStream fos =new FileOutputStream("1.txt");
    BufferedOutputStream bos = new BufferedOutputStream(fos);
    String str = "hello";
    bos.write(str.getBytes());
    bos.flush();  //清楚缓存区
    bos.close();

    }
}

四、对象流
对象是存在于内存中的。有时候我们需要将对象保存到硬盘上,又有时我们需要将对象传输到另一台计算机上等等这样的操作。这时我们需要将对象转换为一个字节序列,而这个过程就称为对象序列化。相反,我们有这样一个字节序列需要将其转换为对应的对象,这个过程就称为对象的反序列化。
1. 使用OOS实现对象序列化

ObjectOutputStream是用来对对象进行序列化的输出流。
其实现对象序列化的方法为:
void writeObject(Object o)
该方法可以将给定的对象转换为一个字节序列后写出。
例如:
Emp emp = new Emp("张三",12,"男");
FileOutputStream fos = new FileOutputStream("Emp.obj");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(emp);//将emp对象序列化后写入文件
oos.close();
2. 使用OIS实现对象反序列化

ObjectInputStream是用来对对象进行反序列化的输入流。
其实现对象反序列化的方法为:
Object readObject()
该方法可以从流中读取字节并转换为对应的对象。
例如:
FileInputStream fis = new FileInputStream("Emp.obj");
ObjectInputStream ois = new ObjectInputStream(fis);
Emp emp = (Emp)ois.readObject();//将Emp对象从文件中读取并反序列
....
ois.close();
3. Serializable接口

ObjectOutputStream在对对象进行序列化时有一个要求,就是需要序列化的对象所属的类必须实现Serializable接口。
实现该接口不需要重写任何方法。其只是作为可序列化的标志。
通常实现该接口的类需要提供一个常量serialVersionUID,表明该类的版本。若不显示的声明,在对象序列化时也会根据当前类的各个方面计算该类的默认serialVersionUID,但不同平台编译器实现有所不同,所以若向跨平台,都应显示的声明版本号。
如果声明的类序列化存到硬盘上面,之后随着需求的变化更改了类别的属性(增加或减少或改名),那么当反序列化时,就会出现InvalidClassException,这样就会造成不兼容性的问题。
但当serialVersionUID相同时,它就会将不一样的field以type的预设值反序列化,可避开不兼容性问题。
例如:

/**
 * 每一个实例用于描述一个人的信息
 * @author Administrator
 *
 */
// Serializable  实现这个接口就是打标签 无需在重写任何东西
class person implements Serializable{

    private static final long serialVersionUID = 1L;   //版本号
    private String name;
    private int age;
    private int phoneNumber;
    private int sex;
    private List<String> otherInfo;
    private Date birDay;

    public person(String name, int age, int phoneNumber, int sex,
            List<String> otherInfo, Date birDay) {
        super();
        this.name = name;
        this.age = age;
        this.phoneNumber = phoneNumber;
        this.sex = sex;
        this.otherInfo = otherInfo;
        this.birDay = birDay;
    }

    @Override
    public String toString() {
        return "person [age=" + age + ", birDay=" + birDay + ", name=" + name
                + ", otherInfo=" + otherInfo + ", phoneNumber=" + phoneNumber
                + ", sex=" + sex + "]";
    }

}
/**
 * 使用ObjectOutputStream 将对象写入文件
 * @author Administrator
 *
 */
class TestOOSDemo{

    public static void main(String[] args) throws IOException {
        List<String> otherInfo =new ArrayList<String>();
        otherInfo.add("其他信息1");
        otherInfo.add("其他信息2");
        otherInfo.add("其他信息3");
        person ps =new person("张三",25,1364951,1,otherInfo, null);

        System.out.println(ps);

        /*
         * 将Person序列化后写入文件中
         * 1、向文件中写数据的流:FileOutputStream
         * 2、将对象序列化的流:ObjectOutputStream
         */
        FileOutputStream fos =new FileOutputStream("ps.obj");

        ObjectOutputStream oos =new ObjectOutputStream(fos);
        /*
         * 将对象转为字节
           将数据写入磁盘的过程称之为:持久化
         */
        oos.writeObject(ps);

    }
}
/**
 * 实现数据的反序列化
 * @author Administrator
 *
 */
class TestBisDemo{
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        /*
         * 从文件中读取字节数据后再转换为对象
         * 1.FIS 读取文件
         * 2.OIS 反序列化
         */
        FileInputStream fis = new FileInputStream("ps.obj");
        ObjectInputStream ois =new ObjectInputStream(fis);
        /*
         * 反序列的方法
         * Object readObject();
         */
        person ps =  (person)ois.readObject();
        System.out.println(ps);
        ois.close();

    }

4. transient关键字

对象在序列化后得到的字节序列往往比较大,有时我们在对一个对象进行序列化时可以忽略某些不必要的属性,从而对序列化后得到的字节序列”瘦身”。
关键字 transient
被该关键字修饰的属性在序列化时其值将被忽略

public class Emp implements Serializable{
    private static final long serialVersionUID = 1L;
    private String name;
    private transient int age;//该属性在序列化时会被忽略
    private String gender;
    //getter and setter and other
    ...
}
时间: 2024-10-14 14:42:33

基本IO操作--字节流的相关文章

Java IO操作——字节流(OutputStream、InputStream)和字符流(Writer、Reader)

学习目标 掌握流的概念 掌握字节流与字符流的作用 掌握文件的标准操作步骤 掌握字节与字符操作的区别 流的概念 在程序中所有的数据都是以流的方式进行传输或保存的,程序中需要数据的时候就用输入流读取数据,而当程序需要将一些数据保存起来的时候,就要使用输出流完成. 程序中的输入输出都是以流的形式保存的,流中保存的实际上全部是字节文件. 字节流与字符流 在java.io包中操作文件内容的主要有两大类:字节流和字符流,两类都分为输入和输出操作.在字节流中输出数据主要是使用OutputStream完成,输入

java的IO操作:字节流与字符流操作

流的概念 程序中的输入输出都是以流形式,流中保存的实际上都是字节文件. 字节流与字符流 字节流的操作: 1)输入:inputStream, 2)输出:outPutStream; 字符流的操作: 1)输入主要使用:write类. 2)输出主要使用:reader类. 内容操作就四个类. 操作流程: 使用File类操作一定有路径问题,注意分隔符: 实际上四个操作类都是抽象类(区别接口,抽象类的成员都是抽象,并且只能单继承,接口可以有全局变量,但是接口可以多继承) IO操作属于资源操作,对于资源操作,操

java基础之IO操作

IO操作 ·文件类的使用(File) ·IO中流的基本使用(字符流.字节流.内存流.打印流) ·System类对IO的支持 ·对象序列化的问题 在整个的IO操作中,流的操作分为两种: ·字节流 字节输出流:OutputStream 字节输入流:InputStream ·字符流 字符输出流:Writer 字符输入流:Reader IO操作的基本步骤: 在Java的使用io的话,必须要按照以下的几个步骤进行(对文件操作来说): ·使用File找到一个文件 ·使用字节流或者字符流的子类为OutputS

Java 基本IO操作

1.基本IO操作     有时候我们编写的程序除了自身会定义一些数据信息外,还需要引用外界的数据,或是将自身的数据发送到外界,这时我们需要使用输入与输出. 1)输入与输出       输入:是一个从外界进入到程序的方向,通常我们需要"读取"外界的数据时,使用输入.所以输入是用来读取数据的 输出:是一个从程序发送到外界的方向,通常我们需要"写出"数据到外界时,使用输出.所以输出是用来写出数据的 2)节点流与处理流 按照流是否直接与特定的地方(如磁盘.内存.设备等)相连

Java之IO流---字节流

1.1 IO流的引入 IO流在很多语言已有体现,诸如C语言的stdio.h,C++中的iostream.Java中的IO流大抵是用于在控制台.磁盘.内存上进行数据的读写操作,完成数据的传递. 我们可以对它进行如下分类: 按处理的数据类型可分为字节流与字符流 按流的流向可分为输入流(in)与输出流(out) 按流的功能可分为节点流(Node)和过滤流(Filter) 本篇侧重于梳理字节流相关的知识,毕竟作为字符流的前辈,它还是非常重要的.下篇继续梳理字符流. 1.2 IO流的继承体系图 大概描述了

黑马程序员--Java基础--文件数据IO操作

文件数据IO操作 1. Reader和Writer 1.1. 字符流原理 Reader是所有字符输入流的父类而Writer是所有字符输出流的父类.字符流是以字符(char)为单位读写数据的.一次处理一个unicode.字符流都是高级流,其底层都是依靠字节流进行读写数据的,所以底层仍然是基于字节读写数据的. 1.2. 常用方法 Reader的常用方法: int read() 读取一个字符,返回的int"值低16"位有效. int read(char[] chs) 从该流中读取一个字符数组

Java基础: IO操作

最近抽了点时间回顾了java基础,昨天看了java的IO操作,把大致的总结写在这里. Java的IO操作主要是数据的输入.输出操作和文件管理,输入输出操作可以包括控制台.文件的输入输出和流的输入输出,流的输入输出可以用于文件,也可以用于网络数据. 控制台 控制台的输入输出中,输出大家最熟悉,最常用的就是System.out,那么输入操作可以用System.in,也可以System.console,区别是前者读入字节,后者读入字符串 文件处理 File类:可以实现对文件目录和文件的增删改,查看文件

Java之IO操作总结

所谓IO,也就是Input与Output的缩写.在java中,IO涉及的范围比较大,这里主要讨论针对文件内容的读写 其他知识点将放置后续章节 对于文件内容的操作主要分为两大类 分别是: 字符流 字节流 其中,字符流有两个抽象类:Writer Reader 其对应子类FileWriter和FileReader可实现文件的读写操作 BufferedWriter和BufferedReader能够提供缓冲区功能,用以提高效率 同样,字节流也有两个抽象类:InputStream OutputStream

Java基础梳理之-IO操作

回想最开始学习Java IO相关的操作时, 被各种Reader/Stream绕晕. 现在再回头梳理这一块的知识点,感觉清晰了很多. Java作为编程语言,大部分东西都是从系统层面带来的, 所以学习的知识点虽然在Java, 但是背后的答案却在操作系统层面. 首先理解核心概念:IO, 究竟什么是IO? 所谓IO就是内存与外设相关的数据传输.常用的外设有硬盘,网卡,打印机, 鼠标...我们接触最频繁的IO操作是硬盘上文件的读写,所以学习IO基本上都是以文件操作为例子.IO作为操作系统的核心,知识点相当