JAVA程序设计基础学习笔记——输入输出流(慕课)笔记整理|期末备考|文本文件的写入读取|二进制文件的写入读取|File类介绍

本章将会依次介绍以下几个部分

目录

输入输出流的概念

预定义的I/O流类

从流的方向划分

从流的分工划分

从流的内容划分

文本文件的写入、读取

文本文件的写入

文本文件的读取

二进制文件的写入、读取

二进制文件的写入

二进制文件的读取

File类的介绍

对象的序列化

ObjectInputStream对象输入流

ObjectOutputStream对象输出流

课后任务


输入输出流的概念

Java中,将输入和输出都抽象为信息的流动

输入流:信息从程序空间之外的别的地方流入程序空间里面

输出流:将程序空间中的数据送到程序空间之外的其他地方

预定义的I/O流类

从流的方向划分

输入流

输出流

从流的分工划分

节点流:真正去访问文件,进行输入输出操作的流

处理流:在节点流的基础之上,对信息进行加工转换处理的流

从流的内容划分

java.io包的顶级层次结构

面向字符的流:专门用于字符数据

  • 源或目标通常是文本文件
  • 实现内部格式和文本文件中的外部格式之间的转换
    • 内部格式:16-bit char 数据类型
    • 外部格式:
      • UTF(Universal character set Transformation Format):很多人称之为“Universal Text Format”.
      • 包括ASCII码及非ASCII码字符,比如:斯拉夫(Cyrillic)字符,希腊字符,亚洲字符等

面向字符的抽象流类——Reader和Writer

  • java.io包中所有字符流的抽象超类
  • Reader提供了输入字符的API
  • Writer提供了输出字符的API
  • 它们的子类又可分为两大类
    • 节点流:从数据源读入数据或往目的地写出数据;
    • 处理流:对数据执行某种处理
  • 多数程序使用这两个抽象类的一系列子类来读入/写出文本信息
    • 例如FileReader / FileWriter用来读 / 写文本文件

上图列出了部分面向字符的流,其中阴影部分为节点流,其他为处理流 

面向字节的流:用于一般目的的输入输出

用来处理非文本数据的输入输出,大多数的数据都不是文本数据,比如一些图像、声音、视频等等

即使是有一些数据既可以存储为文本,又可以存储为二进制,那我们把这些数据存储为二进制时它的空间要节省很多,而且我们将数据从内存中送到内存以外的其他地方,比如文件中去,如果是以二进制的形式去存的话,时间上也会节省。所以有时候,如果我们要存的这个数据不是准备给人读的,而是准备给其他的程序继续处理的,这时候尽量把数据存为二进制的,这样存储的过程中或者说输出的过程中就会比较节省时间,在存储介质上也能够比较节省空间

面向字节的抽象流类——InputStream和OutputStream

  • 是用来处理字节流的抽象基类,程序使用这两个类的子类来读写字节信息。
  • 分为两部分
    • 节点流
    • 处理流

标准输入输出流对象

  • System类的静态成员变量
  • 包括
    • System.in:InputStream类型的,代表标准输入流,默认状态对应于键盘输入。
    • System.out:PrintStream类型的,代表标准输出流,默认状态对应于显示器输出。
    • System.err:PrintStream类型的,代表标准错误信息输出流,默认状态对应于显示器输出。

文本文件的写入、读取

文本文件的写入

怎么创建一个文本文件,将信息写到文本文件中去?

看下面代码

package Week13.com.videolearn;

import java.io.FileWriter;
import java.io.IOException;

//创建文件并写入若干行文本
public class FileWriterTester {
    public static void main(String[] args) throws IOException {

        //main方法中声明抛出IO异常
        String fileName = "Hello.txt";
        FileWriter writer = new FileWriter(fileName);
        writer.write("Hello!\n");
        writer.write("This is my first text file,\n");
        writer.write("You can see how this is done.\n");
        writer.write("输入一行中文也可以\n");
        writer.close();
    }
}

//每次运行都会删除旧文件生成新文件
//换行在不同平台可能会有不同效果
/*
注意在该代码中:
1.用“\n”作为回车,会产生小黑格
2.没有对异常进行处理,仅仅抛出
3.没有实现追加方式写文件
* */

运行结果如下:

我们继续对代码进行修改

package Week13.com.videolearn;

import java.io.FileWriter;
import java.io.IOException;

public class FileWriterTester02 {
    public static void main(String[] args) {

        String fileName = "Hello.txt";

        try {//将所有IO操作放入try块中
            //true参数表示是追加
            FileWriter writer = new FileWriter(fileName, true);
            writer.write("Hello!\n");
            writer.write("This is my first text file,\n");
            writer.write("You can see how this is done.\n");
            writer.write("输入一行中文也可以\n");
            writer.close();
        } catch (IOException e) {
            System.out.println("Problem writing" + fileName);
        }
    }
}

/*
* 说明:
运行此程序,会发现在原文件内容后面又追加了重复的内容,
* 这就是将构造方法的第二个参数设为true的效果。
* 如果将文件属性改为只读属性,再运行本程序,就会出现IO错误,
* 程序将转入catch块中,给出出错信息。
*
*
*
* 如果我们要写入文件中的信息比较多,那么写的效率就是个问题
* 这时候我们可以用BufferedWriter类进行缓冲,能够提高写的效率
*
* 
* */

运行结果如下:

BufferedWriter类

  • FileWriter和BufferedWriter类都用于输出字符流,包含的方法几乎完全一样,但BufferedWriter多提供了一个newLine()方法用于换行,这个换行效果是跨平台的。
  • 不同的系统对文字的换行方法不同。newLine0方法可以输出在当前计算机上正确的换行符。

下面代码演示利用BufferedWriter写文件

package Week13.com.videolearn;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;

public class BufferedWriterTester {
    public static void main(String[] args) throws IOException {

        String fileName = "newHello.txt";
        BufferedWriter out = new BufferedWriter(new FileWriter(fileName));
        out.write("Hello!");
        out.newLine();//换行,可以跨平台
        out.write("This is another text file using BufferedWriter,");
        out.newLine();
        out.write("So I can use a common way to start a newline");
        out.close();
    }
}

运行结果如下:

文本文件的读取

读文本文件相关的类

  • FileReader类
    • 从文本文件中读取字符
    • 继承自Reader抽象类的子类InputStreamReader
  • BufferedReader类
    • 读文本文件的缓冲器类
    • 具有readLine0方法,可以对换行符进行鉴别,一行一行地读取输入流中的内容。
    • 继承自Reader。
package Week13.com.videolearn;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

//读文本文件并显示
public class BufferedReaderTester {
    public static void main(String[] args) {

        String fileName = "Hello.txt", line;

        try {
            BufferedReader bufferedReader = new BufferedReader(new FileReader(fileName));
            line = bufferedReader.readLine();       //读取一行的内容
            while(line != null) {
                System.out.println(line);
                line = bufferedReader.readLine();
            }
            bufferedReader.close();
        } catch (IOException e) {

            System.out.println("Problem reading" + fileName);
        }

    }
}

/*
* 运行该程序,屏幕上将逐行显示出Hello.txt文件中的内容。
* FileReader对象:创建后将打开文件,如果文件不存在,会抛出一个IOException
* BufferedReader类的readLine()方法:从一个面向字符的输入流中读取一行文本。如果其中
* 不再有数据,返回null
Reader类的read()方法:也可用来判别文件结束。该方法返回的一个表示某个字符的int型整数,
* 如果读到文件末尾,返回 -1。据此,可修改本例中的读文件部分:
int c
* while((c = in.read())!= -1)
* System.out.print((char)c);
* close()方法:为了操作系统可以更为有效地利用有限的资源,应该在读取完毕后,调用该方收
*
*
* 思考:如何实现文本文件的复制呢?
* */

运行结果如下:

二进制文件的写入、读取

二进制文件的写入

抽象类OutputStream

  • 派生类FileOutputStream
    • 用于一般目的输出(非字符输出 ) ;
    • 用于成组字节输出。
  • 派生类DataOutputStream
    • 具有写各种基本数据类型的方法;
    • 将数据写到另一个输出流;
    • 它在所有的计算机平台上使用同样的数据格式;
    • 其中size方法,可作为计数器,统计写入的字节数。

例如将int数据写入二进制文件中去,代码如下:

package Week13.com.videolearn;

import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;

//将三个int型数字255/0/-1写入数据文件data1.dat
public class FileOutputStreamTester {
    public static void main(String[] args) {

        String filename = "data1.dat";
        int value0 = 255, value1 = 0, value2 = -1;
        try {
            //FileOutputStream是源生字节流,只识别写出去的是一个一个字节
            //没办法识别数据类型,DataOutputStream是处理流,可以将字节首先处理成某种
            //类型的数据
            DataOutputStream dataOutputStream = new DataOutputStream(new FileOutputStream(filename));
            dataOutputStream.writeInt(value0);
            dataOutputStream.writeInt(value1);
            dataOutputStream.writeInt(value2);
            dataOutputStream.close();
        } catch (IOException e) {

            System.out.println("Problem writing" + filename);
        }

    }
}

/*
* 运行结果
运行程序后,生成数据文件datal.dat
用写字板打开没有任何显示
用ultraEdit打开查看其二进制信息,内容为00.00 00 FF 00 0000 00 FF FF FF FF,
* 每个int数字都是32个bit的
说明
FileOutputStream类的构造方法负责打开文件“data1.dat”用于写数据
* FileOutputStream类的对象与DataOutputStream对象连接,写基本类型的数据
*
* 当需要写大量数据时,用缓冲流类可以提高写的效率
*
*
* */

下面我们来看缓冲流类

BufferedOutputStream类

  • 用法示例:
    • DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(fileName)));
    • BufferedOutputStream也是处理流,它是进行缓冲的,但不是直接进行写操作的

向文件写入多种数据,统计字节数,代码如下:

package Week13.com.videolearn;

import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;

//向文件写入多种数据,统计字节数
public class BufferedOutputStreamTester {
    public static void main(String[] args) throws IOException {

        String fileName = "mixedTypes.dat";
        //首先构造输出流对象
        //在源生字节输出流对象基础上构造一个缓冲的BufferedOutputStream,提高写的效率
        //DataOutputStream可以按照类型去写二进制数据
        DataOutputStream dataOutputStream = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(fileName)));
        dataOutputStream.writeInt(0);
        System.out.println(dataOutputStream.size() + "bytes have been written");
        dataOutputStream.writeDouble(31.2);
        System.out.println(dataOutputStream.size() + "bytes have been written");
        dataOutputStream.writeBytes("JAVA");
        System.out.println(dataOutputStream.size() + "bytes have been written");
        dataOutputStream.close();
    }
}

运行结果如下:

  

二进制文件的读取

之前以将数据以二进制方式写入文件

读取二进制文件中的三个int型数字并相加,代码如下:

package Week13.com.videolearn;

import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.IOException;

//读取二进制文件中的三个int型数字并相加
public class DataInputStreamTester {
    public static void main(String[] args) {

        String fileName = "data1.dat";
        int sum = 0;
        try {
            //首先构造输入流对象FileInputStream(源生字节的输入流)
            //BufferedInputStream缓冲流
            //DataInputStream可以按类型读取
            DataInputStream dataInputStream = new DataInputStream(new BufferedInputStream(new FileInputStream(fileName)));
            //问题1:当初这个文件写的是什么类型的数据要知道,不然就没办法了
            //问题2:如果不知道这个文件中总共有多少个整数,怎么判断?
            //可以依赖异常,捕获DataInputStream的文件结束异常
            sum += dataInputStream.readInt();
            sum += dataInputStream.readInt();
            sum += dataInputStream.readInt();
            System.out.println("The sum is:" + sum);
            dataInputStream.close();
        } catch (IOException e) {
            System.out.println("Problem reading" + fileName);
        }

    }
}

运行结果如下:

 

用读写字节的方式来复制文件,不管这个文件是二进制文件还是文本文件,都可以实现

如果需要对任何类型的文件进行复制呢?

代码如下:

package Week13.com.videolearn;

import java.io.*;

//从命令行输入源文件名和目标文件名,将源文件复制为目标文件
public class CopyBytes {
    public static void main(String[] args) {

        DataInputStream instr;
        DataOutputStream outstr;
        if(args.length != 2) {
            System.out.println("Please enter file names");
            return;
        }
        try {
            instr = new DataInputStream(new BufferedInputStream(new FileInputStream(args[0])));
            outstr = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(args[1])));

            try {
                int data;
                while(true) {
                    data = instr.readUnsignedByte();
                    outstr.writeByte(data);
                }
            } catch (EOFException e) {
                //遇到文件尾,会抛出文件结束异常,捕获
                outstr.close();
                instr.close();
                return;
            }
        } catch (FileNotFoundException nfx) {
            System.out.println("Problem opening files");
        } catch (IOException iox) {
            System.out.println("IO Problems");
        }
    }
}

File类的介绍

读写文件程序中String Filename可以用File对象替代

注意:Java中File类只用来操作文件和获取文件信息;文件数据的读取由文件流提供

File类的作用

  • 创建、删除文件 ;
  • 重命名文件;
  • 判断文件的读写权限及是否存在;
  • 设置和查询文件的最近修改时间等;
  • 构造文件流可以使用File类的对象作为参数

接下来看:结合File类,改进之前的二进制文件复制程序

File类的几个常用方法 (参见P194页例7-6):

  • isFile0方法来确定File对象是否代表一个文件
  • isDirectory0方法来确定File对象是否代表一个目录
  • exists0方法判断同名文件或路径是否存在

改进的文件复制程序代码如下:

package Week13.com.videolearn;

import java.io.*;

//改进的文件复制程序
public class NewCopyBytes {
    public static void main(String[] args) {

        DataInputStream instr;
        DataOutputStream outstr;

        if(args.length != 2) {
            System.out.println("Please Enter file names!");
            return;
        }
        File inFile = new File(args[0]);
        File outFile = new File(args[1]);
        //exists方法判断要打开的文件是否存在
        if(outFile.exists()) {
            System.out.println(args[1] + " already exists");
            return;
        }
        if(! inFile.exists()) {
            System.out.println(args[0] + "does not exist");
            return;
        }

        try {
            instr = new DataInputStream(new BufferedInputStream(new FileInputStream(inFile)));
            outstr = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(outFile)));

            try {
                int data;
                while(true) {
                    data = instr.readUnsignedByte();
                    outstr.writeByte(data);
                }
            } catch (EOFException e) {
                outstr.close();
                instr.close();
                return;
            }
        } catch (FileNotFoundException nfx) {
            System.out.println("Problem opening files");
        } catch (IOException iox) {
            System.out.println("IO Problems");
        }
    }
}

对象的序列化

我们在程序内存中的对象能存活多长时间呢?

它最长的寿命也就是到程序运行结束,程序已结束,所占有的空间就都释放了,内存中的对象也就不存在了,如果有些对象中的信息是需要永久保留的,这个时候就要就行对象的序列化,也就是说把这个对象按照整体写到文件中,再整体读出来。

ObjectInputStream/ObjectOutputStream类

实现对象的读写

ObjectInputStream对象输入流

通过ObjectOutputStream把对象写入磁盘文件

ObjectOutputStream对象输出流

通过ObjectInputStream把对象读入程序

不保存对象的transient和static类型的变量

对象要想实现序列化,其所属的类必须实现Serializable接口

ObjectOutputStream(处理流)

必须通过另一个流构造ObjectOutputStream:

FileOutputStream out = new FileOutputStream("theTime");

ObjectOutputStream s = new ObjectOutputStream(out);

s.writeObject("Today");//参数这里都已经实现了Serializable接口

s.writeObject(new Date());//参数这里都已经实现了Serializable接口

s.flush();//清空缓冲区

ObjectInputStream(处理流)

必须通过另一个流构造ObjectInputStream:

FileInputStream in = new FileInputStream("theTime");

ObjectInputStream s = new ObjectInputStream(in):

String today = (String)s.readObject();

Date date = (Date)s.readObject0);

如果我们自己定义的对象也需要整体存文件整体读取出来,那么我们的类就要去实现Serializable接口,实际上就是一个空接口。

Seriealizable

  • Serializable 接口的定义

package java.io;

public interface Serializable {

        // there's nothing in here!
}

  • 实现Serializable接口的语句

public class MyClass implements Serializable {

...

}

  • 使用关键字transient可以阻止对象的某些成员被自动写入文件
  • 实际上这就是一个标记,设计这个类的时候打算允许对象能够整体写入磁盘。

课后任务

请练习教材P203页例7-10

Employee对象的保存(可以在以前练习过的Employee定义上修改)

猜你喜欢

转载自blog.csdn.net/Raider1/article/details/130690938