首页 Java程序设计精讲六 第七章 输入和输出流
文章
取消

Java程序设计精讲六 第七章 输入和输出流

Java程序设计精讲六 第七章 输入和输出流1

1 数据流的基本概念

数据流是指一组有顺序的、有起点和终点的字节集合。

Java.io包中提供了表示数据流的4个基本抽象类∶

InputStream、OutputStream、Reader和Writer。 在涉及数据流操作的程序中,几乎都要使用引入语句:`import java. io.* ;`

image-20230322213135758

image-20230323142705058

1.1. 输入数据流

输入数据流是指只能读不能写的数据流,用于向计算机内输入信息使用。从数据流中读取数据时,必须有一个数据源与该数据流相连。 java.io包中所有输入数据流都是从抽象类InputStream继承而来。

主要数据操作方法:

image-20230322172744817

1.2. 输出数据流

输出数据流是指只能写不能读的流,用于从计算机中输出数据。 java.io包中所有输出数据流大多是从抽象类OutputStream继承而来的。

主要数据操作方法:

主要数据操作方法功能
void write(int i)将字节i写入到数据流中,它只输出所读入参数的最低8位。该方法是抽象方法,需要在其输出流子类中加以实现,然后才能使用。
void write(byte b[])将数组b[]中的全部b.length个字节写入数据流。
void write(byte b[], int off,int len)将数组b[]中从下标off开始的len个字节写入数据流。元素b[off]是此操作写入的第一个字节,b[off + len-1]是此操作写入的最后一个字节。
void close()当结束对输出数据流的操作时应该将其关闭。
void flush()刷新此输出流并强制写出所有缓冲的输出字节。

tips:tipping_hand_man::

在目前通用的存储介质中内存访问的速度是最快的,因此,为加快数据传输速度,提高数据输出效率,有时输出数据流会在提交数据之前把所要输出的数据先暂时保存在内存缓冲区中,然后成批进行输出,每次传输过程都以某特定数据长度为单位进行传输。在这种方式下,数据的末尾一般都会有一部分数据由于数量不够一个批次,而存留在缓冲区里,调用方法 flush()可以将这部分数据强制提交,如图所示。

image-20230322213921896

2 基本字节数据流类

文件数据流包括FileInputStreamFileOutputStream,这两个类用来进行文件的I/O处理,其数据源或数据终点都应当是文件。通过所提供的方法可以对本机上的文件进行操作,但是不支持方法mark( )和reset()。

2.1. 文件数据流

1
2
// 例如︰(在构造文件数据流时,可以直接给出文件名。)
FilelnputStream fis = new FilelnputStream("myFile");//这样,便把文件myFile作为该数据流的数据源。同样可以使用FileOutputStream向文件中输出字节。

2.2. 过滤器数据流

2.2.1. 缓冲区数据流

缓冲区数据流有BufferedInputStreamBufferedOutputStream,它们是在数据流上增加了一个缓冲区,都属于过滤器数据流。这两个流还提供了对mark()、reset()和skip()等方法的支持。 创建该类的实例对象的方法︰

  1. 使用默认缓冲区的大小
  2. 自行设置缓冲区的大小。
1
2
3
4
5
6
7
8
9
10
static void bufferedStream() {
    try {
        InputStream is = new BufferedInputStream(new FileInputStream("filePath"));
        OutputStream os = new BufferedOutputStream(new FileOutputStream("filePath"));
        InputStream is2 = new BufferedInputStream(new FileInputStream("filePath"),1024);
        OutputStream os2 = new BufferedOutputStream(new FileOutputStream("filePath"),1024);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

注意:一般在关闭一个缓冲区输出流之前,应先使用flush()方法,强制输出剩余数据,以确保缓冲区内的所有数据全部写入输出流。

2.2.2.数据数据流

DatalnputStreamDataOutputStream。允许通过数据流来读写Java基本类型,包括布尔型( boolean)、浮点型( float)等。

  1. 创建方式

    1
    2
    
    DataInputStream dis = new DataInputStream( new FileInputStream("filePath"));
    DataOutputStream dos = new DataOutputStrearn(new FileOutputStream("filePath"));
    
  2. 方法比较

    DataInputStream类中的方法DataOutputStream类中的方法
    byte readByte()void writeByte(int aByte)
    long readLong()void writeLong(long aLong)
    double readDouble()void writeDouble(double aDouble)
    boolean readBoolean()void writeBoolean(boolean aBool)
    String readUTF()void writeUTF(String aString)
    int readInt()void writeInt(int anInt)
    float readFloat()void writeFloat(float aFloat)
    short readShort()void writeShort(short aShort)
    char readChar()void writeChar(Char aChar)

    DataInputStream的方法与DataOutputStream的方法都是成对出现的。当对字符串进行操作时应该使用Reader和Writer两个系列类中的方法。

2.3. 对象流

java. io包中的ObjectInputStreamObjectOutputStream两个类实现把对象写入文件数据流或从文件数据流中读出的功能。能够输入/输出对象的流称为对象流。

2.3.1. 写对象数据流

1
2
3
4
5
6
7
8
9
Date date = new Date();	//将date对象写入文件
try {
    FileOutputStream fos = new FileOutputStream("date.ser");
    ObjectOutputStream oos = new ObjectOutputStream(fos);
    oos.writeObject(date);
    oos.close();
} catch (IOException e) {
    e.printStackTrace();
}

2.3.2. 读对象数据流

1
2
3
4
5
6
7
8
void readObjectStream() throws IOException, ClassNotFoundException {
    Date date =null;
    FileInputStream fis = new FileInputStream("date.ser");
    ObjectInputStream ois = new ObjectInputStream(fis);
    date = (Date) ois.readObject();
    System.out.println(date);
    ois.close();
}

注意∶方法readObject()把数据流以Object类型返回,返回内容应该在转换为正确的类名之后再执行该类的方法。

2.4. 序列化

2.4.1. 序列化的概念

  • 序列化:对象通过数值来描述自己的状态,记录对象也就是记录下这些数值。把对象转换为字节序列的过程称为对象的序列化
  • 反序列化:把字节序列恢复为对象的过程称为对象的反序列化。
  • 序列化的主要任务:写出对象实例变量的数值。

注意:

  • 只有实现Serializable接口的类才能被序列化。
  • 要序列化一个对象,必须与特定的对象输出/输入流联系起来,通过对象输出流将对象状态保存下来,或是将对象保存到文件中,之后再通过对象输人流将对象状态恢复。
  • 序列化是通过java.io包中的ObjectOutputStreamObjectlnputStream两个类实现的。前者用writeObject()方法可以直接将对象保存到输出流中,而后者用readObject()方法可以直接从输人流中读取一个对象。
1
2
3
4
5
6
7
8
9
10
11
// 序列化
static void serializeStudent() {
    try {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(STUDENT_FILE_PATH));
        oos.writeObject(new Student(11, "高启强", 45, "高盛集团"));
        oos.flush();
        oos.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}
1
2
3
4
5
6
7
8
9
10
11
// 反序列化
static void deserializationStudent() {
    try {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(STUDENT_FILE_PATH));
        Object o = ois.readObject();
        if (o instanceof Student)
            System.out.println((Student) o);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

2.4.2. 对象结构表

  1. 任何用transient头键字标明的成员变量,都不会被保存。

  2. 对象的结构表:当数据变量是一个对象时,该对象的数据成员也可以被持久化。对象的数据结构或结构树,包括其子对象树在内,构成了这个对象的结构表。

    如果一个可持久化对象中包含一个指向不可持久化元素的引用,则整个持久化操作将失败。 如果一个对象结构表中包含了一个对不可持久化对象的引用,而这个引用已用关键字transient加以标记,则这个对象仍可以被持久化。
  3. 如果对象的成员数据不适合进行序列化,则可以使用关键字transient以防止数据被序列化。

1
2
3
4
5
6
class MyClass implements Serializable {
    private static final long serialVersionUID = 8302145052259253534L;
    private transient Thread myThread;
    private transient String customerID;
    private int total;
}

3 基本字符流

从JDK1.1开始,java.io包中加入了专门用于字符流处理的类:以ReaderWriter为基础派生的一系列类。

3.1. 读者和写者

3.1.1. Unicode字符集

Java使用Unicode字符集来表示字符串和字符。ASCII字符集以一个字节(8bit)表示一个字符,可以认为一个字符就是一个字节( byte)。但Java使用的Unicode是一种大字符集,用两个字节(16bit)来表示一个字符,这时字节与字符就不再相同。

3.1.2. 读者(Reader)和写者(Writer)

Java提供一种新的数据流处理方案,称作读者(Reader)和写者( Writer)。像数据流一样,在java.io包中有许多不同类对其进行支持,其中最重要的是InputStreamReaderOutputStreamWriter类。这两个类是字节流和读者、写者的接口,用来在字节流和字符流之间作为中介。 构造方法如下所示。

  • InputStreamReader ( InputStream in):默认规范。
  • lnputStreamReader(InputStream in,String enc):指定规范enc。o
  • OutputStreamWriter( OutputStream out):默认规范。
  • OutputStreamWriter( OutputStream out,String enc):指定规范enc。

3.3.3. 构造映射到ASCII码的标准的InputStreamReader的方法:

1
ir = newlnputStreamReader( System.in, "8859_1);

3.3.4. 读者提供的方法:

1
2
3
4
5
6
7
8
9
void close() ;
void mark ( int readAheadLimit);
boolean markSupported ( );
int read();
int read( char[ ] cbuf);
int read( char[ ] cbuf , int off , int len);
boolean ready();
void reset();
long skip(long n);

3.3.5. 写者提供的方法:

1
2
3
4
5
6
7
void close();
void flush();
void write ( char[ ] cbuf);
void write(char[ ] cbuf ,int off, int len);
void write( int c);
void write ( String str);
void write (String str, int off , int len);

3.2. 缓冲区读者和缓冲区写者

  1. 为了格式转换以较大数据块为单位进行,提高效率,java.io中提供了缓冲流BufferedReaderBufferedWriter

  2. 整行字符的处理方法。

    • public String readLine( ) : BufferedReader的方法,从输入流中读取一行字符,行结束标志为’\n’ 、 ‘ \r’或两者一起。

    • public void newLine( ) : BufferedWriter的方法,向输出流中写入一个行结束标志。

    把BufferedReader或BufferedWriter正确连接到InputStreamReader或OutputStreamWriter的末尾是一个很好的方法。但是要在BufferedWriter中使用flush()方法,以强制清空缓冲区中的剩余内容,防止遗漏。

    采用缓冲式输入数据文件的程序的主要目的是用readLine()方法按行输入字符

4 文件的处理

java.io.File类提供了获得文件基本信息及操作文件的一些方法。使用File类可以达到与系统无关的目的,这里使用的是抽象的路径表示法。

File对象是File类的实例。File对象对应一个目录或文件,对象的属性包括文件路径、名字、文件长度、可否读写等。File对象只用来命名文件、查询文件属性和处理目录,不提供读写文件操作。

4.1. File类

创建一个新的File对象的3种构造方法∶

1
2
3
4
5
6
7
File myFile;
File myFile = new File( "mymotd");

myFile = new File( "/" ,"mymotd");

File myDir = new File( "/” );
myFile = new File ( my Dir, " mymotd");

4.1.1. 与文件名相关的方法

1
2
3
4
5
String getName( );//获取文件名。
String getPath();//获取文件路径。
String getAbsolutePath();//获取文件绝对路径。
String getParent();//获取文件父目录名称。
boolean renameTo(File newName);//更改文件名,成功返回true,否则返回false。

4.1.2. 文件测定方法

1
2
3
4
5
6
boolean exists();//文件对象是否存在。
boolean canWrite();//文件对象是否可写。
boolean canRead();//文件对象是否可读。
boolean isFile();//文件对象是否是文件。
boolean isDirectory();//文件对象是否是目录。
boolean isAbsolute();//文件对象是否是绝对路径。

4.1.3. 常用文件信息和方法

1
2
3
long lastModified();//获取文件最后修改时间。
long length();//获取文件长度。
boolean delete( );//删除文件对象指向的文件,成功则返回true,否则返回false。

4.1.4. 目录工具

1
2
3
boolean mkdir();//创建新目录。
boolean mkdirs();//创建新目录。
String[] list();//列出符合模式的文件名。

Tips :tipping_hand_man::

File类同样可以用来描述一个目录。可以用mkdir()和mkdirs()来生成该目录。两者的区别在于用mkdirs()可以一次生成多个层次的子目录

4.2. 随机访问文件

Java语言提供了RandomAccessFile类来处理到一个位置读一条记录的输入/输出。 创建一个随机访问文件的方法∶

  1. 使用文件名

    1
    
    RandomAccessFile myRAFile = new RandomAccessFile (String name , String mode);
    
  2. 使用文件对象

    1
    
    RandomAccessFile myRAFile = new RandomAccessFile(File file, String node));
    

    参数mode决定是以只读方式(“r”)还是以读写方式(“rw”)访问文件。对象RandomAccessFile读写信息的方法可以访问DatalnputStream和DataOutputStream类中的所有read()和write()方法。

4.2.1. 移动文件读写指针的方法

1
2
3
   long getFilePointer();//返回文件指针的当前位置。
   void seek( long pos);//将文件指针置于指定的绝对位置。位置值以从又件开始处的字节偏移量pos来计算,pos为0代表文件的开始。
   long length();//返回文件的长度。位置值为length(),代表文件的结尾。
本文由作者按照 CC BY 4.0 进行授权

Java程序设计精讲五 第六章 继承与多态

Java程序设计精讲八 第八章 图形界面设计