GVKun编程网logo

Java IO详解(七)------随机访问文件流(java随机访问文件功能)

30

在本文中,您将会了解到关于JavaIO详解的新资讯,同时我们还将为您解释七)------随机访问文件流的相关在本文中,我们将带你探索JavaIO详解的奥秘,分析七)------随机访问文件流的特点,并

在本文中,您将会了解到关于Java IO详解的新资讯,同时我们还将为您解释七)------随机访问文件流的相关在本文中,我们将带你探索Java IO详解的奥秘,分析七)------随机访问文件流的特点,并给出一些关于c – 随机访问文件无法正常工作、Java IO流详解(七)——对象流(序列化与反序列化)、java IO详解、Java IO详解那点事的实用技巧。

本文目录一览:

Java IO详解(七)------随机访问文件流(java随机访问文件功能)

Java IO详解(七)------随机访问文件流(java随机访问文件功能)

1、什么是 随机访问文件流 RandomAccessFile?

  该类的实例支持读取和写入随机访问文件。 随机访问文件的行为类似于存储在文件系统中的大量字节。 有一种游标,或索引到隐含的数组,称为文件指针 ; 输入操作读取从文件指针开始的字节,并使文件指针超过读取的字节。 如果在读/写模式下创建随机访问文件,则输出操作也可用; 输出操作从文件指针开始写入字节,并将文件指针提前到写入的字节。 写入隐式数组的当前端的输出操作会导致扩展数组。 文件指针可以通过读取getFilePointer方法和由设置seek方法。

  通俗来讲:我们以前讲的 IO 字节流,包装流等都是按照文件内容的顺序来读取和写入的。而这个随机访问文件流我们可以再文件的任意地方写入数据,也可以读取任意地方的字节。

 

我们查看 底层源码,可以看到:

1
public  class  RandomAccessFile  implements  DataOutput, DataInput, Closeable {

  实现了 DataOutput类,DataInput类,那么这两个类是什么呢?

 

2、数据流:DataOutput,DataInput

  ①、DataOutput:提供将数据从任何Java基本类型转换为一系列字节,并将这些字节写入二进制流。 还有一种将String转换为modified UTF-8格式(这种格式会在写入的数据之前默认增加两个字节的长度)并编写结果字节系列的功能。

  ②、DataInput:提供从二进制流读取字节并从其中重建任何Java原语类型的数据。 还有,为了重建设施String从数据modified UTF-8格式。 

下面我们以其典型实现:DataOutputSteam、DataInputStream 来看看它的用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//数据输出流
         File file =  new  File( "io" +File.separator+ "a.txt" );
         DataOutputStream dop =  new  DataOutputStream( new  FileOutputStream(file));
         //写入三种类型的数据
         dop.write( 65 );
         dop.writeChar( ''哥'' );
         dop.writeUTF( "帅锅" );
         dop.close();
         
         //数据输入流
         DataInputStream dis =  new  DataInputStream( new  FileInputStream(file));
         System.out.println(dis.read());   //65
         System.out.println(dis.readChar());  //哥
         System.out.println(dis.readUTF());   //帅锅
         dis.close();

  

 

3、通过上面的例子,我们可以看到因为 RandomAccessFile 实现了数据输入输出流,那么 RandomAccessFile 这一个类就可以完成 输入输出的功能了。

  

这里面第二个参数:String mode 有以下几种形式:(ps:为什么这里的值是固定的而不弄成枚举形式,不然很容易写错,这是因为随机访问流出现在枚举类型之前,属于Java 历史遗留问题)

  

 

 第一种:用 随机流顺序读取数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public  class  RandomAccessFileTest {
     public  static  void  main(String[] args)  throws  Exception {
         File file =  new  File( "io" +File.separator+ "a.txt" );
         write(file);
         read(file);
     }
     
     /**
      * 随机流读数据
      */
     private  static  void  read(File file)  throws  Exception {
         //以 r 即只读的方法读取数据
         RandomAccessFile ras =  new  RandomAccessFile(file,  "r" );
         byte  b = ras.readByte();
         System.out.println(b);  //65
         
         int  i = ras.readInt();
         System.out.println(i);  //97
         
         String str = ras.readUTF();  //帅锅
         System.out.println(str);
         ras.close();
     }
 
     /**
      * 随机流写数据
      */
     private  static  void  write(File file)  throws  Exception{
         //以 rw 即读写的方式写入数据
         RandomAccessFile ras =  new  RandomAccessFile(file,  "rw" );
         ras.writeByte( 65 );
         ras.writeInt( 97 );
         ras.writeUTF( "帅锅" );
         
         ras.close();
     }
 
}

  

第二种:随机读取,那么我们先介绍这两个方法

这里所说的偏移量,也就是字节数。一个文件是有N个字节数组成,那么我们可以通过设置读取或者写入的偏移量,来达到随机读取或写入的目的。

我们先看看Java 各数据类型所占字节数:

下面是 随机读取数据例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
      * 随机流读数据
      */
     private  static  void  read(File file)  throws  Exception {
         //以 r 即只读的方法读取数据
         RandomAccessFile ras =  new  RandomAccessFile(file,  "r" );
         
         byte  b = ras.readByte();
         System.out.println(b);  //65
         //我们已经读取了一个字节的数据,那么当前偏移量为 1
         System.out.println(ras.getFilePointer());   //1
         //这时候我们设置 偏移量为 5,那么可以直接读取后面的字符串(前面是一个字节+一个整型数据=5个字节)
         ras.seek( 5 );
         String str = ras.readUTF();  //帅锅
         System.out.println(str);
         
         //这时我们设置 偏移量为 0,那么从头开始
         ras.seek( 0 );
         System.out.println(ras.readByte());  //65
         
         //需要注意的是:UTF 写入的数据默认会在前面增加两个字节的长度
         
         ras.close();
     }

  

 随机流复制文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/**
      * 随机流复制文件
      * @param fileA
      * @param B
      * @throws Exception
      */
     private  static  void  copyFile(File fileA,File fileB)  throws  Exception{
         
         RandomAccessFile srcRA =  new  RandomAccessFile(fileA,  "rw" );
         RandomAccessFile descRA =  new  RandomAccessFile(fileB,  "rw" );
         
         //向 文件 a.txt 中写入数据
         srcRA.writeByte( 65 );
         srcRA.writeInt( 97 );
         srcRA.writeUTF( "帅锅" );
         //获取 a.txt 文件的字节长度
         int  len = ( int ) srcRA.length();
         srcRA.seek( 0 );
         System.out.println(srcRA.readByte()+srcRA.readInt()+srcRA.readUTF());
         
         //开始复制
         srcRA.seek( 0 );
         //定义一个数组,用来存放 a.txt 文件的数据
         byte [] buffer =  new  byte [len];
         //将 a.txt 文件的内容读到 buffer 中
         srcRA.readFully(buffer);
         //再将 buffer 写入到 b.txt文件中
         descRA.write(buffer);
         
         //读取 b.txt 文件中的数据
         descRA.seek( 0 );
         System.out.println(descRA.readByte()+descRA.readInt()+descRA.readUTF());
         //关闭流资源
         srcRA.close();
         descRA.close();
     }

  

ps:一般多线程下载、断点下载都可以运用此随机流

c – 随机访问文件无法正常工作

c – 随机访问文件无法正常工作

我正在尝试编写这个程序,将员工数据库保存在随机访问文件中,它必须具有添加员工和删除员工的功能(通过写入记录中的所有空格).这是我到目前为止所做的,但它不能正常工作.在阅读员工时,它会读取正确记录的工资,但会记录下一条记录的名称.此外,当我删除最后一条记录并在该记录中添加员工时,我无法查看员工信息,我收到异常错误.

我不是在寻找解决方案,只是在正确的方向上轻推.谢谢.

#include "stdafx.h"
#include <iostream>
#include <iomanip>
#include <fstream>
#include <sstream>
#include "ccc_empl.h"

using namespace std;

const int NEWLINE_LENGTH = 2; 
const int RECORD_SIZE = 30 + 10 + NEWLINE_LENGTH;

/** 
   converts a string to a floating-point value   
   @param s a string representing a floating-point value
   @return the equivalent floating-point value
*/   
double string_to_double(string s)
{  
   istringstream instr(s);
   double x;
   instr >> x;
   return x;
}


/*
   reads an employee record from the input file
   @param e  the employee
   @param in the file to read from
*/
Employee read_employee(istream& in)
{   
string line;
getline(in,line);  
string input_name = line.substr(0,30);
double input_salary = string_to_double(line.substr(30,10));
Employee e(input_name,input_salary);
return e;
}

/*
gets input for an Employee object
@param input_name the name of the employee
@param input_salary the salary of the employee
@param e the Employee object
@return returns the Employee object
*/
Employee input_employee()
{
string input_name;
cout << "Name: ";
cin.ignore();
getline(cin,input_name);
cout << "Salary: ";
double input_salary;
cin >> input_salary;
Employee e(input_name,input_salary);
return e;
}

/**
   adds an employee record to a file
   @param e the employee record to write
   @param out the file to write to
*/
void add_employee(Employee e,ostream& out)
{
out << e.get_name()
  << setw(30)
  << fixed << setprecision(2)
  << e.get_salary();
}

/**
   removes an employee record from a file
   @param e the employee record to remove
   @param out the file to remove from
*/
void remove_employee(ostream& out)
{
out << " " << setw(42) << fixed << setprecision(2) << " \n";
}

int main()
{  
   cout << "Please enter the data file name: ";
   string filename;
   cin >> filename;
   fstream fs;
   fs.open(filename);

   fs.seekg(0,ios::end); // Go to end of file
   int nrecord = fs.tellg() / RECORD_SIZE; // determine number of records in the file
   int menu_input = 0;
   string input_name;
   double input_salary = 0;

   while (menu_input != 4)
   {
   cout << "Please enter the record to update: (0 - " << nrecord - 1 << ") exit to quit ";
   int pos = 0;
   cin >> pos;
   if(cin.fail())
   {
       cout << "Exiting..." << endl;
       system("pause");
       return 0;
   }

   // menu for user input
   cout << "\nWhat action would you like to perform?" << endl;
   cout << "Add employee.....1" << endl;
   cout << "Remove employee..2" << endl;
   cout << "View employee....3" << endl;       
   cin >> menu_input;

   switch(menu_input)
   {
   case 1:  fs.seekg(pos * RECORD_SIZE,ios::beg);
       add_employee(input_employee(),fs);
       break;           
   case 2:  fs.seekg(pos * RECORD_SIZE,ios::beg);
       remove_employee(fs);
       break;
   case 3:  fs.seekg(pos * RECORD_SIZE,ios::beg);
       cout << "\nName: " << read_employee(fs).get_name() << "Salary: " << read_employee(fs).get_salary() << endl << endl;
       break;
   default: cout << "Invalid entry" << endl;
       break;
   }
   }

   fs.close();
   system("pause");
   return 0;
}

好的,这是新的和改进的(好吧,我想是的)代码.我唯一的问题是当添加一个员工时,我可以让程序将它添加到第一个打开的记录就好了,但是如果没有打开的记录,我似乎无法将它添加到最后文件没有搞乱添加到第一个空记录.我的意思是,如果有空记录,它会将员工添加到记录中,但如果没有空记录,则不会将员工添加到文件末尾.如果我添加代码添加到文件末尾,则会发生以下两种情况之一:要么添加到空记录,要么请求另一名员工并将其添加到结尾,或者只是跳过空记录并添加到文件的结尾.

不知道我在这里做错了什么,但任何提示都会受到赞赏.

#include "stdafx.h"
#include <iostream>
#include <iomanip>
#include <fstream>
#include <sstream>
#include "ccc_empl.h"

using namespace std;

const int NEWLINE_LENGTH = 2; 
const int RECORD_SIZE = 30 + 10 + NEWLINE_LENGTH;

/** 
converts a string to a floating-point value   
@param s a string representing a floating-point value
@return the equivalent floating-point value
*/   
double string_to_double(string s)
{  
   istringstream instr(s);
   double x;
   instr >> x;
   return x;
}


/*
reads an employee record from the input file
@param e  the employee
@param in the file to read from
*/
Employee read_employee(istream& in)
{   
string line;
getline(in,input_salary);
return e;
}

/**
adds an employee record to a file
@param e the employee record to write
@param out the file to write to
*/

void add_employee(Employee e,ostream& out)
{
out << e.get_name()
  << setw(10 + 30 - e.get_name().length())
  << fixed << setprecision(2)
  << e.get_salary() << "\n";
}

/**
removes an employee record from a file
@param e the employee record to remove
@param out the file to remove from
*/

void remove_employee(ostream& out)
{
out << " " << setw(40) << fixed << setprecision(2) << " \n";
}


int main()
{  
   cout << "Please enter the data file name: ";
   string filename;
   cin >> filename;
   fstream fs;
   fs.open(filename);

   fs.seekg(0,ios::end); // Go to end of file
   int nrecord = fs.tellg() / RECORD_SIZE; // determine number of records in the file
   int menu_input = 1;
   string input_name;
   double input_salary = 0;

   while (menu_input)
   {       
   // menu for user input
   cout << "\nWhat action would you like to perform?" << endl;
   cout << "Add employee.....1" << endl;
   cout << "Remove employee..2" << endl;
   cout << "View employee....3" << endl;
   cout << "Exit.............4" << endl;
   cin >> menu_input;
   if (menu_input == 4)
   {
       cout << "\nExiting..." << endl << endl;
       system("pause");
       return 0;
   }
   // switch statment to perform selected menu_input task
   switch(menu_input)
   {
   case 1:  // adds an employee in the first empty record
            // or at the end of the file if no records are empty          
            {
                int count = 0;
                string s;
                for (int i = 0; i < nrecord; i++)
                {
                    fs.seekp(count,ios::beg);
                    getline(fs,s);
                    if (isspace(s[0]))
                    {
                        fs.seekp(count,ios::beg);
                        add_employee(input_employee(),fs);
                        nrecord++;
                        break;
                    }else
                        count += 42;
                }
            }           
       break;           
   case 2:  {
            cout << "Please enter the record to remove: (0 - " << nrecord -1 << ") ";
            int pos = 0;
            cin >> pos;
            fs.seekp(pos * RECORD_SIZE,ios::beg);
            remove_employee(fs);
            }
       break;
   case 3: {
            cout << "Please enter the record to view: (0 - " << nrecord -1 << ") ";
            int pos = 0;
            cin >> pos;
            fs.seekg(pos * RECORD_SIZE,ios::beg);
            Employee e(read_employee(fs));        
            cout << "\nName: " << e.get_name() << "Salary: " << e.get_salary() << endl << endl;
           }
       break;     
   default: cout << "Invalid entry" << endl;
       break;
   }
}

fs.close();
system("pause");
return 0;
}

解决方法

你两次调用read_employee(fs).首先读取你想要的记录,第二个读取下一个记录(标准没有指定它们中的哪一个是’第一个’).

Java IO流详解(七)——对象流(序列化与反序列化)

Java IO流详解(七)——对象流(序列化与反序列化)

       对象流的主要用作是对Java对象的序列化和反序列化的操作。在Java IO流中提供了两个对象流:ObjectInputStream和ObjectOutputStream,这两个类都属于字节流。其中ObjectOutputStream将Java对象以字节序列的形式写出到文件,实现对象的永久存储,它继承自OutputStream。ObjectInputStream是将之前使用ObjectOutputStream序列化的字节序列恢复为Java对象,它继承自InputStream。

 

   序列化与反序列化

      序列化 : 把Java对象转换成字节序列的过程。

      反序列化:把序列化成字节序列的数据恢复为Java对象的过程。

 

    为什么需要序列化?

       ①、把对象的字节序列永久地保存到硬盘上:对于一个存在JVM中的对象来说,其内部的状态只是保存在内存中。当JVM退出之后,内存资源也就被释放,Java对象的内部状态也就丢失了。而在很多情况下,对象内部状态是需要被持久化的,将运行中的对象状态保存下来(最直接的方式就是保存到文件系统中),在需要的时候可以还原,即使是在Java虚拟机退出的情况下。

       ②、在网络上传送对象的字节序列:对象序列化机制是Java内建的一种对象持久化方式,可以很容易实现在JVM中的活动对象与字节数组(流)之间进行转换,使用得Java对象可以被存储,可以被网络传输,在网络的一端将对象序列化成字节流,经过网络传输到网络的另一端,可以从字节流重新还原为Java虚拟机中的运行状态中的对象。

 

1、ObjectOutputStream类

      ObjectOutputStream代表对象输出流,即序列化,将Java对象以字节序列的形式写出到文件,实现对象的永久存储。

      它的writeObject(Object obj)方法可对指定的obj参数对象进行序列化。

 

      首先需要明确的一点是:一个对象要想序列化,该对象必须要实现Serializable接口,否则会抛出NotSerializableException异常。

      定义一个Person类,实现Serializable接口:

package com.thr;

import java.io.Serializable;

/**
 * @author Administrator
 * @date 2020-02-28
 * @desc Person对象
 */
public class Person implements Serializable {
    private int id;
    private String name;
    private int age;

    public Person() {
    }

    public Person(int id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }
    //getter、setter、toString方法省略(自己测试需要加上)
}

   序列化操作:

package com.thr;

import java.io.*;

/**
 * @author Administrator
 * @date 2020-02-28
 * @desc 使用ObjectOutputStream序列化对象
 */
public class ObjectOutputStreamTest {

    public static void main(String[] args) {
        //定义对象流
        ObjectOutputStream oos = null;

        try {
            //创建对象流
            oos = new ObjectOutputStream(new FileOutputStream("D:\\IO\\person.txt"));
            //序列化对象
            oos.writeObject(new Person(10001,"张三",20));
            oos.writeObject(new Person(10002,"李四",21));
            //刷新缓冲区
            oos.flush();
            System.out.println("序列化成功...");
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //释放资源
            if (oos!=null){
                try {
                    oos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

   序列化之后打开的文件我们是看不懂的,因为它是字节序列文件,只有计算机懂。所以接下来需要将它反序列化成我们能看懂的。

 

2、ObjectInputStream类

     ObjectOutputStream代表对象输入流,即反序列化,将之前使用ObjectOutputStream序列化的字节序列恢复为Java对象。

     它的readObject()方法读取指定目录下的序列化对象。

 

     反序列化操作:

package com.thr;

import java.io.*;

/**
 * @author Administrator
 * @date 2020-02-28
 * @desc 使用ObjectInputStream反序列化对象
 */
public class ObjectInputStreamTest {

    public static void main(String[] args) {
        //定义对象流
        ObjectInputStream ois = null;
        try {
            //创建对象输入流对象
            ois = new ObjectInputStream(new FileInputStream("D:\\IO\\person.txt"));
            //反序列化对象
            Person person1 = (Person) ois.readObject();
            Person person2 = (Person) ois.readObject();
            System.out.println(person1);
            System.out.println(person2);
            System.out.println("反序列化成功...");
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            if (ois!=null){
                try {
                    ois.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

      在使用ObjectInputStream反序列化时需要注意一点:

      ①、在完成序列化操作后,如果对序列化对象进行了修改,比如增加某个字段,那么我们再进行反序列化就会抛出InvalidClassException异常,这种情况叫不兼容问题。

      解决的方法是:在对象中手动添加一个 serialVersionUID 字段,用来声明一个序列化版本号,之后再怎么添加属性也能进行反序列化,凡是实现Serializable接口的类都应该有一个表示序列化版本标识符的静态变量。

public class Person implements Serializable {
    //序列化版本号
    private static final long serialVersionUID = 5687485987455L;

    private int id;
    private String name;
    private int age;
    //getter、setter、toString、构造方法省略(自己测试需要加上)
}

 

    注意:ObjectInputStream和ObjectOutputStream不能序列化transient修饰的成员变量。

 

    Transient 关键字

    transient修饰符仅适用于变量,不适用于方法和类。在序列化时,如果我们不想序列化特定变量以满足安全约束,那么我们应该将该变量声明为transient。执行序列化时,JVM会忽略transient变量的原始值并将默认值保存到文件中。因此,transient意味着不要序列化。

       假如Person类中的age属性不需要序列化,在age属性上添加transient关键字。private transient int age;

package com.thr;

import java.io.*;

/**
 * @author Administrator
 * @date 2020-02-29
 * @desc transient的使用
 */
public class Test {
    public static void main(String[] args) {
        serialization(new Person(10001, "赵六", 20));
        deserialization();
    }

    //序列化
    public static void serialization(Person person){
        ObjectOutputStream oos = null;
        try {
            //创建输出对象流
            oos = new ObjectOutputStream(new FileOutputStream("D:\\IO\\object.txt"));
            //序列化对象
            oos.writeObject(person);
            oos.flush();
            System.out.println("序列化成功...");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //释放资源
            try {
                if (oos!=null){
                    oos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    //反序列化
    public static void deserialization (){
        ObjectInputStream ois = null;
        try {
            //创建输入对象流
            ois = new ObjectInputStream(new FileInputStream("D:\\IO\\object.txt"));
            //反序列化对象
            Person person = (Person) ois.readObject();
            System.out.println(person);
            System.out.println("反序列化成功...");
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            try {
                ois.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}


transient的使用示例
transient的使用示例

      运行的结果为:Person{id=10001, name=''赵六'', age=0},可以发现,尽管age属性没有序列化,但是它是有默认值的。

java IO详解

java IO详解

1.     Abstract:
Java将I/O分为高阶I/O与低阶I/O,高阶I/O在使用上提供更多的读写方法,如读写int、double、String的资料型态,而低阶的I/O大部份只提供write、read的byte[]存取,因为程式大部份的资料都是以字串或其它主要型态资料来运算,因此低阶的I/O在使用上不利於程式设计,所以Java将许多好用的方法全部集合成高阶I/O; 换言之,低阶I/O的主要工作是负责与媒体资料作存取,高阶I/O类别主要作资料型态的转换及提供一些特殊的功能。在使用Java I/O时要谨记的一个重要原则是,在建立一个I/O之前必需先用低阶I/O类别来存取媒体资料(如档案或pipe),之後再使用高阶I/O来控制低阶I/O类别的动作,这种一层又一层的架构称I/O Chain。底下为Java的I/O架构图,第一个为以byte为单位的I/O,第二个则是以char为单位。
 
2.     File I/O:
A.     FileInputStream & FileOutputStream
FileInputStream是读取档案用的类别,其建构式有叁个:
public FileInputStream(String strFilename) throws FileNotFoundException
public FileInputStream(File fIn) throws FileNotFoundException
public FileInputStream(FileDescriptor fdObj)
   在这里我只讲第一个,这是最直觉的方式,如下的范例1,会一次从e:\test.txt读10个bytes,将读入的结果输出到标准输出设备,直到档案结束。在这个范例中要注意的是,available会传回输入串流中还有多少个bytes,read则会根据buffer的大小来决定一次读几个bytes,并将实际读到的byte数传回。
===== 范例 1 =====
import java.io.*;
public class FIn {
  public FIn() {
    try {
      FileInputStream fis = new FileInputStream("e:/in.txt");
      while (fis.available() > 0) {
        byte[] b = new byte[10];
        int nResult = fis.read(b);
        if (nResult == -1) break;
        System.out.println(new String(b));
      }
      fis.close();
    }
    catch (IOException e) {
      e.printStackTrace();
    }
  }
  public static void main(String[] args) {
    FIn fIn = new FIn();
  }
}
        FileOutputStream是写入档案用的类别,其建构式有四个:
                Public FileOutputStream(String strFilename) throws FileNotFoundException
                Public FileOutputStream(File fOut) throws FileNotFound Exception
                Public FileOutputStream(FileDescriptor fdObj)
public FileOutputStream(String name, boolean append) throws FileNotFoundException
        第四个和第一个的差别只在於当档案存在时,第一个会将原来的档案内容覆盖,第四个则可以选择覆盖或将新内容接在原内容後面。范例2以建构式一讲解如何写入一个档案…在这个范例中要注意的是,fIn每个读10个bytes,但是最後一次不一定会读10个bytes,因此,fOut在write时,要指明要写几个bytes到档案中,否则最後一次仍会写入10个bytes,因Java在new byte时会先将内容先填0,所以後面的几个bytes会是0。
===== 范例2 =====
import java.io.*;
public class FOut {
  public FOut() {
    try {
      FileInputStream fIn = new FileInputStream("e:/in.txt");
      FileOutputStream fOut = new FileOutputStream("e:/out.txt");
      while (fIn.available() > 0) {
        byte[] b = new byte[10];
        int nResult = fIn.read(b);
        if (nResult == -1) break;
        fOut.write(b, 0, nResult);
      }
      fIn.close();
      fOut.close();
    }
    catch (IOException e) {
      e.printStackTrace();
    }
  }
  public static void main(String[] args) {
    FOut FOut1 = new FOut();
  }
}
B.     FileReader & FileWriter
FileReader和FileInputStream最大不同在於,FileInputStream读取的单位是byte,FileReader读取的单位是char。另外要注意的是,在FileInputStream中以available来判断是否还有资料可读取,在FileReader中是以ready来判断,
但是,available是传回还有多少个bytes可以读取,ready则传回true或false,当传回true时表示,下次read时保证不会停顿,当传回false时,表示下次read时”可能”停顿,所谓可能是指不保证不会停顿。
Ps. 测试时,in.txt里放些中文字就可以看出以byte和以char为单位有什麽不同。
===== 范例 3 =====
import java.io.*;
public class chFIn {
  public chFIn() {
    try {
      FileReader rdFile = new FileReader("e:/in.txt");
      while (rdFile.ready()) {
        char[] chIn = new char[10];
        int nResult = rdFile.read(chIn);
        if (nResult == -1) break;
        System.out.println(chIn);
      }
rdFile.close();
    }
    catch (IOException e) {
      e.printStackTrace();
    }
  }
  public static void main(String[] args) {
    chFIn chFIn1 = new chFIn();
  }
}
        FileWriter和FileOutputStream的最大不同也在於写入单位的不同,FileOutputStream为byte,FileWriter为char。
===== 范例 4 =====
import java.io.*;
public class chFOut {
  public chFOut() {
    try {
      FileReader rdFile = new FileReader("e:/in.txt");
      FileWriter wrFile = new FileWriter("e:/out.txt");
      while (rdFile.ready()) {
        char[] chIn = new char[10];
        int nResult = rdFile.read(chIn);
        if (nResult == -1) break;
        wrFile.write(chIn, 0, nResult);
      }
      rdFile.close();
      wrFile.close();
    }
    catch (IOException e) {
      e.printStackTrace();
    }
  }
  public static void main(String[] args) {
    chFOut chFOut1 = new chFOut();
  }
}

C.     BufferedReader & BufferedWriter
File I/O是相当耗时的作业,通常电脑在做处理时,者会建立一个缓冲区,一次读取或写入一个区块,借由减少I/O次数,来节省时间 ,在Java中的BufferedReader和BufferedWriter就是提供这样的缓冲功能。
在范例5中,我们将FileReader导向BufferedReader,将FileWriter导向BufferedWriter,来达到区块读取、写入的目的。BufferedReader提供的readLine一次可以读取一行,当遇到档尾时,会传回null。BufferedWriter提供的newLine会产生列尾符号,这个列尾符号随作业系统的不同而不同,在Windows上为\r\n,在Unix上为\n,在Mac上为\r,这个符号是依据line.separator系统性质而来的。需注意的是,如果将BufferedWriter应用到网路程式时,绝对不要使用newLine,因为绝大多数的网路协定都是以\r\n为列尾,不会因作业系统不同而异。
===== 范例 5 =====
import java.io.*;
public class bufIn {
  public bufIn() {
    try {
      FileReader rdFile = new FileReader("e:/in.txt");
      BufferedReader brdFile = new BufferedReader(rdFile);
       FileWriter wrFile = new FileWriter("e:/out.txt");
      BufferedWriter bwrFile = new BufferedWriter(wrFile);
      String strLine;
      while ((strLine = brdFile.readLine()) != null) {
        bwrFile.write(strLine);
        bwrFile.newLine();
      }
      brdFile.close();
      bwrFile.close();
    }
    catch (IOException e) {
      e.printStackTrace();
    }
  }
  public static void main(String[] args) {
    bufIn bufIn1 = new bufIn();
  }
}
 D.     File
在档案处理方面,程式不只是要对档案做读、写,有时也需要得知档案的属性,或删除、移动、更名,有时则是要找出或列出某目录下的某些档案,针对这些运作,Java提供了File这个类别。底下的范例,说明如何使用File类别。
a.       如何得知档案属性:
在范例6中需注意的是lastModified传回的最後更改时间是自1970/1/1 00:00:00算起的时间,单位为毫秒,所以要用Date将它转换成日期、时间; 另外getCanonicalPath和getAbsolutePath得到的值在Windows上会是一样的,在Unix可能就会不一样。
===== 范例 6 =====
import java.io.*;
import java.util.*;
public class FileSpy {
  public FileSpy(String strFilename) {
    File fFile = new File(strFilename);
    if (fFile.exists()) {
      System.out.println("Name: " + fFile.getName());
      System.out.println("Absolute path: " + fFile.getAbsolutePath());
      try {
        System.out.println("Canonical path: " + fFile.getCanonicalPath());
      }
      catch (IOException e) {
        e.printStackTrace();
      }
      if (fFile.canWrite()) System.out.println(fFile.getName() + " is writable");
      if (fFile.canRead()) System.out.println(fFile.getName() + " is readable");
      if (fFile.isFile()) {
        System.out.println(fFile.getName() + " is a file");
      }
      else if (fFile.isDirectory()) {
        System.out.println(fFile.getName() + " is a directory");
      }
      else {
        System.out.println("What is this?");
      }
      long lngMilliseconds = fFile.lastModified();
      if (lngMilliseconds !=0) System.out.println("last modified at " + new Date(lngMilliseconds));
      long lngLen = fFile.length();
      if (lngLen !=0) System.out.println("size: " + lngLen);
    }
    else
      System.out.println("file not found");
  }
  public static void main(String[] args) {
    if (args.length == 1) {
      FileSpy fileSpy1 = new FileSpy(args[0]);
    }
    else
      System.out.println("Usage: java FileSpy Filename");
  }
}
 
b.      建立、删除、移动、更名:
File类别提供了createNewFile、renameTo、delete作为建立(createNewFile)、删除(delete)、移动、更名(renameTo)之用,使用方式如下: (移动和更名都用renameTo,就如在Unix上档案搬移和更名都用mv一样)
===== 范例 7 =====
import java.io.*;
public class OperateFile {

  public OperateFile() {
    //create new file
    File fNewFile = new File("C:/newfile.txt");
    try {
      if (fNewFile.exists() == false) {
        if (fNewFile.createNewFile() == true) {
          System.out.println("create c:/newfile.txt success");
        }
        else {
          System.out.println("create c:/newfile.txt fail");
        }
      }
      else {
        System.out.println("file already exists");
      }
    }
    catch (IOException e) {
      e.printStackTrace();
    }

//rename file
    File fRenameFile = new File("c:/renamefile.txt");
    fNewFile.renameTo(fRenameFile);
    //remove file
    File fRemoveFile = new File("d:/" + fRenameFile.getName());
    fRenameFile.renameTo(fRemoveFile);
    //delete file
    try {
      File fDelFile = new File(fRemoveFile.getCanonicalPath());
      fDelFile.delete();
    }
    catch (IOException e) {
      e.printStackTrace();
    }
  }
  public static void main(String[] args) {
    OperateFile operateFile1 = new OperateFile();
  }
}
c.       找出某特定目录里的所有档案:
File类别提供的list和listFiles都可以列出某特定目录里的所有档案,其中list传回的是String[],listFiles传回的是File[],这两个函式都会传回所有的档案和目录。
===== 范例 8 =====
import java.io.*;
public class ListAllFiles {
  public ListAllFiles(String strDir) {
    File fDir = new File(strDir);
    File[] fAllFiles = fDir.listFiles();
    for(int i=0; i
      if (fAllFiles.isFile())
        System.out.println("File: " + fAllFiles.getName());
      else
        System.out.println("Dir: " + fAllFiles.getName());
    }
  }
  public static void main(String[] args) {
    ListAllFiles listAllFiles1 = new ListAllFiles(args[0]);
  }
}
3.     Network I/O:
Java对网路的支援只有TCP/IP和UDP/IP,提供的类别有URL、URLConnection、Socket、ServerSocket,在这里我只打算用ServerSocket、Socket为例,来说明Network I/O。
基本上,Java的I/O不管在任何的媒体上都是将它们视为stream,所以,网路I/O和档案I/O原理也是一致的,底下的两个程式分别为server socket及client socket。在看范例之前,可以再复习一下前面的abstract…
===== 范例 9 =====
import java.net.*;
import java.io.*;
public class myServer {
  public myServer(String strPort) {
    int nPort = new Integer(strPort).intValue();
    try {
      ServerSocket ss = new ServerSocket(nPort);
      Socket s = ss.accept();
      OutputStream out = s.getOutputStream();
      PrintStream psOut = new PrintStream(out);
      String strResponse = "Hello " + s.getInetAddress() + " on port " + s.getPort() + "\r\n";
      strResponse += "This is " + s.getLocalAddress() + " on port " + s.getLocalPort() + "\r\n";
      psOut.print(strResponse);
      s.close();
      ss.close();
    }
    catch (IOException e) {
      e.printStackTrace();
    }
  }
 
  public static void main(String[] args) {
    myServer myServer1 = new myServer(args[0]);
  }
}
===== 范例 10 =====
import java.net.*;
import java.io.*;
public class myClient {
  public myClient(String strIP, String strPort) {
    int nPort = new Integer(strPort).intValue();
    try {
      Socket s = new Socket(strIP, nPort);
      InputStream in = s.getInputStream();
      BufferedInputStream bisIn = new BufferedInputStream(in);
      while (bisIn.available() > 0) {
        byte[] b = new byte[30];
        int nLen = bisIn.read(b);
        System.out.println(new String(b, 0, nLen));
      }
    }
    catch (UnknownHostException e) {
      e.printStackTrace();
    }
    catch (IOException e) {
      e.printStackTrace();
    }
  }
  public static void main(String[] args) {
    myClient myClient1 = new myClient(args[0], args[1]);
  }
}

4.     Object Serialization:
A.     所谓Object Serialization就是把物件的”状态”储存成一系列的位元组,而这些位元组在稍候可用来恢复物件。更简单的说,Object Serialization是让物件可以以物件为储存单位。在Java中,任何物件要能Serialization,必须implements Serializable这个Interface,以下是一个简单的程式范例,可以将物件储存到e:\point.ser,或从e:\point.ser将物件恢复原值。
===== 范例 11 =====
import java.io.*;
public class ThreeDPoint implements Serializable
{
  private double m_dblX, m_dblY, m_dblZ;

  public ThreeDPoint(double x, double y, double z)
  {
    m_dblX = x;
    m_dblY = y;
    m_dblZ = z;
  }
 public void PrintXYZ()
  {
    System.out.println("X: " + m_dblX);
    System.out.println("Y: " + m_dblY);
    System.out.println("Z: " + m_dblZ);
  }
  public static void main(String[] args)
  {
    if (args[0].equalsIgnoreCase("w")) {
      ThreeDPoint threeDPoint1 = new ThreeDPoint(10 ,20, 30);
      try {
        FileOutputStream fout = new FileOutputStream("e:\\point.ser");
        ObjectOutputStream oout = new ObjectOutputStream(fout);
        oout.writeObject(threeDPoint1);
        oout.close();
         System.out.println("write:");
        threeDPoint1.PrintXYZ();
      }
      catch (Exception e) {
        e.printStackTrace();
      }
    }
    else if (args[0].equalsIgnoreCase("r")) {
      try {
        FileInputStream fin = new FileInputStream("e:\\point.ser");
        ObjectInputStream oin = new ObjectInputStream(fin);
        Object o = oin.readObject();
        ThreeDPoint threeDPoint1 = (ThreeDPoint) o;
        oin.close();
        System.out.println("read:");
        threeDPoint1.PrintXYZ();
      }
      catch (Exception e) {
      }
    }
  } //end of main
}
B.     在Java中,一个实作某特定介面的类别,其子类别也因继承的原故而被视为实作了该介面,因此,许多没有明确宣告实作Serializable介面的类别,事实上也是可以被Serialization的。
C.     并非每个实作了Serializable介面的物件都可以被Serialization,如果这个物件继承图上的祖先,有其中一个是不可以被Serialization,那麽这个物件就不可以被Serialization。
5.     Formated I/O:
在Java的I/O里,并没有所谓的型别,不管是int、long、double…最後都是以String输出,所以如果要让数字以特定格式输出,需透过Java提供的两个类别java.text.NumberFormat和java.text.DecimalFormat将数字格式化後再输出。
范例12简要说明NumberFormat如何使用,在开始使用NumberFormat时,应先用getInstance取得NumberFormat的实体,范例12中的setMaximumIntegerDigits和setMinimumFractionDigits是用来设定整数和小数的位数,另外还有setMinimumIntegerDigits和setMaximumFractionDigits也是同样功能。这些设定如有冲突,Java以最後设定的为准。
===== 范例 12 =====
import java.text.*;
public class myFormat {
  public myFormat() {
    NumberFormat nf = NumberFormat.getInstance();
    double dblNum = Math.PI;
   System.out.println(dblNum);
    nf.setMaximumIntegerDigits(5);
    nf.setMinimumFractionDigits(4);
    System.out.println("PI: " + nf.format(dblNum));
  }
  public static void main(String[] args) {
    myFormat myFormat1 = new myFormat();
  }
}
另一个类别DecimalFormat是继承NumberFormat的子类别,它提供了更强的格式化功能,透过设定pattern,可以使我们的输出更多样化,至於Java提供的pattern有那些? 在API Document中有详细说明! 范例13仅举其一,说明DecimalFormat如何使用。
===== 范例 13 =====
import java.text.*;
public class myDecimalFormat {
  public myDecimalFormat() {
    DecimalFormat df = new DecimalFormat("0000.000");
    double dblNum = 123.45;
    System.out.println("dblNum: " + dblNum);
    System.out.println("dblNum: " + df.format(dblNum));
  }
  public static void main(String[] args) {
    myDecimalFormat myDecimalFormat1 = new myDecimalFormat();
  }

Java IO详解那点事

Java IO详解那点事

阅读文本大概需要15分钟。

0x01:字节流


字节流基类

1. InputStream

InputStream:字节输入流基类,抽象类是表示字节输入流的所有类的超类。

 常用方法:
  1. // 从输入流中读取数据的下一个字节

  2. abstract int read()

  3. // 从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b中

  4. int read(byte[] b)

  5. // 将输入流中最多 len 个数据字节读入 byte 数组

  6. int read(byte[] b, int off, int len)



  7. // 跳过和丢弃此输入流中数据的 n个字节

  8. long skip(long n)


  9. // 关闭此输入流并释放与该流关联的所有系统资源

  10. void close()

    

2. OutputStream

OutputStream:字节输出流基类,抽象类是表示输出字节流的所有类的超类。

 常用方法:
  1. // 将 b.length 个字节从指定的 byte 数组写入此输出流

  2. void write(byte[] b)

  3. // 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流

  4. void write(byte[] b, int off, int len)

  5. // 将指定的字节写入此输出流

  6. abstract void write(int b)


  7. // 关闭此输出流并释放与此流有关的所有系统资源

  8. void close()


  9. // 刷新此输出流并强制写出所有缓冲的输出字节

  10. void flush()

字节文件操作流

1. FileInputStream

FileInputStream:字节文件输入流,从文件系统中的某个文件中获得输入字节,用于读取诸如图像数据之类的原始字节流。

 构造方法:
// 通过打开一个到实际文件的连接来创建一个FileInputStream,该文件通过文件系统中的File对象file指定
FileInputStream(File file)
// 通过打开一个到实际文件的连接来创建一个FileInputStream,该文件通过文件系统中的路径name指定
FileInputStream(String name)

常用方法:覆盖和重写了父类的的常用方法。

  1. // 读取f盘下该文件f://hell/test.txt

  2. //构造方法1

  3. InputStream inputStream = new FileInputStream(new File("f://hello//test.txt"));

  4. int i = 0;

  5. //一次读取一个字节

  6. while ((i = inputStream.read()) != -1) {


  7. // System.out.print(i + " ");// 65 66 67 68

  8. //为什么会输出65 66 67 68?因为字符在底层存储的时候就是存储的数值。即字符对应的ASCII码。

  9. System.out.print((char) i + " ");// A B C D

  10. }

  11. //关闭IO流

  12. inputStream.close();


  1. // 读取f盘下该文件f://hell/test.txt

  2. //构造方法2

  3. InputStream inputStream2 = new FileInputStream("f://hello/test.txt");

  4. // 字节数组

  5. byte[] b = new byte[2];

  6. int i2 = 0;

  7. // 一次读取一个字节数组

  8. while ((i2 = inputStream2.read(b)) != -1) {


  9. System.out.print(new String(b, 0, i2) + " ");// AB CD

  10. }

  11. //关闭IO流

  12. inputStream2.close();

注: 一次读取一个字节数组,提高了操作效率,IO流使用完毕一定要关闭。

2. FileOutputStream

FileOutputStream:字节文件输出流是用于将数据写入到File,从程序中写入到其他位置。

 构造方法:
  1. // 创建一个向指定File对象表示的文件中写入数据的文件输出流

  2. FileOutputStream(File file)

  3. // 创建一个向指定File对象表示的文件中写入数据的文件输出流

  4. FileOutputStream(File file, boolean append)

  5. // 创建一个向具有指定名称的文件中写入数据的输出文件流

  6. FileOutputStream(String name)

  7. // 创建一个向具有指定name的文件中写入数据的输出文件流

  8. FileOutputStream(String name, boolean append)


常用方法:覆盖和重写了父类的的常用方法。

  1. OutputStream outputStream = new FileOutputStream(new File("test.txt"));

  2. // 写出数据

  3. outputStream.write("ABCD".getBytes());

  4. // 关闭IO流

  5. outputStream.close();


  6. // 内容追加写入

  7. OutputStream outputStream2 = new FileOutputStream("test.txt", true);

  8. // 输出换行符

  9. outputStream2.write("\r\n".getBytes());

  10. // 输出追加内容

  11. outputStream2.write("hello".getBytes());

  12. // 关闭IO流

  13. outputStream2.close();


注;输出的目的地文件不存在,则会自动创建,不指定盘符的话,默认创建在项目目录下;输出换行符时一定要写\r\n不能只写\n,因为不同文本编辑器对换行符的识别存在差异性。


字节缓冲流(高效流)

1. BufferedInputStream

BufferedInputStream:字节缓冲输入流,提高了读取效率。

     构造方法:
// 创建一个 BufferedInputStream并保存其参数,即输入流in,以便将来使用。
BufferedInputStream(InputStream in)
// 创建具有指定缓冲区大小的 BufferedInputStream并保存其参数,即输入流in以便将来使用

BufferedInputStream(InputStream in, int size)

  1. InputStream in = new FileInputStream("test.txt");

  2. // 字节缓存流

  3. BufferedInputStream bis = new BufferedInputStream(in);

  4. byte[] bs = new byte[20];

  5. int len = 0;

  6. while ((len = bis.read(bs)) != -1) {


  7. System.out.print(new String(bs, 0, len));

  8. // ABCD

  9. // hello

  10. }

  11. // 关闭流

  12. bis.close();

2. BufferedOutputStream

BufferedOutputStream:字节缓冲输出流,提高了写出效率。

     构造方法:
  1. // 创建一个新的缓冲输出流,以将数据写入指定的底层输出流

  2. BufferedOutputStream(OutputStream out)

  3. // 创建一个新的缓冲输出流,以将具有指定缓冲区大小的数据写入指定的底层输出流

  4. BufferedOutputStream(OutputStream out, int size)


  5. 常用方法:

  6. // 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此缓冲的输出流

  7. void write(byte[] b, int off, int len)

  8. // 将指定的字节写入此缓冲的输出流

  9. void write(int b)

  10. // 刷新此缓冲的输出流

  11. void flush()


  1. BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("test.txt", true));

  2. // 输出换行符

  3. bos.write("\r\n".getBytes());

  4. // 输出内容

  5. bos.write("Hello Android".getBytes());

  6. // 刷新此缓冲的输出流

  7. bos.flush();

  8. // 关闭流

  9. bos.close();


0x02:字符流


字符流基类

1. Reader

Reader:读取字符流的抽象类.

  常用方法:
  1. // 读取单个字符

  2. int read()

  3. // 将字符读入数组

  4. int read(char[] cbuf)

  5. // 将字符读入数组的某一部分

  6. abstract int read(char[] cbuf, int off, int len)

  7. // 跳过字符

  8. long skip(long n)


  9. // 关闭该流并释放与之关联的所有资源

  10. abstract void close()


2. Writer

Writer:写入字符流的抽象类.

 常用方法:
  1. // 写入字符数组

  2. void write(char[] cbuf)

  3. // 写入字符数组的某一部分

  4. abstract void write(char[] cbuf, int off, int len)

  5. // 写入单个字符

  6. void write(int c)

  7. // 写入字符串

  8. void write(String str)

  9. // 写入字符串的某一部分

  10. void write(String str, int off, int len)


  11. // 将指定字符添加到此 writer

  12. Writer append(char c)

  13. // 将指定字符序列添加到此 writer

  14. Writer append(CharSequence csq)

  15. // 将指定字符序列的子序列添加到此 writer.Appendable

  16. Writer append(CharSequence csq, int start, int end)


  17. // 关闭此流,但要先刷新它

  18. abstract void close()

  19. // 刷新该流的缓冲

  20. abstract void flush()

字符转换流

1. InputStreamReader

InputStreamReader:字节流转字符流,它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。

 构造方法:
  1. // 创建一个使用默认字符集的 InputStreamReader

  2. InputStreamReader(InputStream in)

  3. // 创建使用给定字符集的 InputStreamReader

  4. InputStreamReader(InputStream in, Charset cs)

  5. // 创建使用给定字符集解码器的 InputStreamReader

  6. InputStreamReader(InputStream in, CharsetDecoder dec)

  7. // 创建使用指定字符集的 InputStreamReader

  8. InputStreamReader(InputStream in, String charsetName)

 特有方法:

  1.     //返回此流使用的字符编码的名称

    String getEncoding()

  2. //使用默认编码

  3. InputStreamReader reader = new InputStreamReader(new FileInputStream("test.txt"));

  4. int len;

  5. while ((len = reader.read()) != -1) {

  6. System.out.print((char) len);//爱生活,爱Android


  7. }

  8. reader.close();


  9. //指定编码

  10. InputStreamReader reader = new InputStreamReader(new FileInputStream("test.txt"),"utf-8");

  11. int len;

  12. while ((len = reader.read()) != -1) {

  13. System.out.print((char) len);//????????Android

  14. }

  15. reader.close();


注:Eclipse默认使用GBK编码,test.txt文件所以是GBK编码,当指定utf-8编码时所以会乱码。


2. OutputStreamWriter

OutputStreamWriter:字节流转字符流。

 构造方法:
  1. // 创建使用默认字符编码的 OutputStreamWriter

  2. OutputStreamWriter(OutputStream out)

  3. // 创建使用给定字符集的 OutputStreamWriter

  4. OutputStreamWriter(OutputStream out, Charset cs)

  5. // 创建使用给定字符集编码器的 OutputStreamWriter

  6. OutputStreamWriter(OutputStream out, CharsetEncoder enc)

  7. // 创建使用指定字符集的 OutputStreamWriter

  8. OutputStreamWriter(OutputStream out, String charsetName)

  9. 特有方法:

  10. //返回此流使用的字符编码的名称

  11. String getEncoding()

字符缓冲流(高效流)

1. BufferedReader

BufferedReader:字符缓冲流,从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。

 构造方法:
  1. // 创建一个使用默认大小输入缓冲区的缓冲字符输入流

  2. BufferedReader(Reader in)

  3. // 创建一个使用指定大小输入缓冲区的缓冲字符输入流

  4. BufferedReader(Reader in, int sz)

 特有方法:
// 读取一个文本行

String readLine()


  1. //生成字符缓冲流对象

  2. BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream("test.txt")));

  3. String str;

  4. //一次性读取一行

  5. while ((str = reader.readLine()) != null) {

  6. System.out.println(str);// 爱生活,爱Android

  7. }


  8. //关闭流

  9. reader.close();


2. BufferedWriter

BufferedWriter:字符缓冲流,将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。

 构造方法:
  1. // 创建一个使用默认大小输出缓冲区的缓冲字符输出流

  2. BufferedWriter(Writer out)

  3. // 创建一个使用给定大小输出缓冲区的新缓冲字符输出流

  4. BufferedWriter(Writer out, int sz)

  5. 特有方法:

  6. // 写入一个行分隔符

  7. void newLine()

FileReader、FileWriter

 FileReader:InputStreamReader类的直接子类,用来读取字符文件的便捷类,使用默认字符编码。
FileWriter:OutputStreamWriter类的直接子类,用来写入字符文件的便捷类,使用默认字符编码。

0x03: 高效流效率比对

读取f盘下的一个视频文件到项目中:文件大小29.5 MB

读取方式一:

  1. FileInputStream inputStream = new FileInputStream("d://苍井空那点事.mp4");

  2. FileOutputStream outputStream = new FileOutputStream("苍井空那点事.mp4");

  3. int len;

  4. // 开始时间

  5. long begin = System.currentTimeMillis();

  6. // 一次读取一个字节

  7. while ((len = inputStream.read()) != -1) {

  8. outputStream.write(len);

  9. }

  10. // 用时毫秒

  11. System.out.println(System.currentTimeMillis() - begin);// 213195

  12. //关闭流释放资源

  13. inputStream.close();

  14. outputStream.close();

读取方式二

  1. FileInputStream inputStream = new FileInputStream("d://苍井空那点事.mp4");

  2. FileOutputStream outputStream = new FileOutputStream("苍井空那点事.mp4");

  3. int len;

  4. byte[] bs = new byte[1024];

  5. // 开始时间

  6. long begin = System.currentTimeMillis();

  7. // 一次读取一个字节数组

  8. while ((len = inputStream.read(bs)) != -1) {

  9. outputStream.write(bs, 0, len);

  10. }

  11. // 用时毫秒

  12. System.out.println(System.currentTimeMillis() - begin);// 281


  13. inputStream.close();

  14. outputStream.close();

读取方式三:

  1. FileInputStream inputStream = new FileInputStream("d://苍井空那点事.mp4");

  2. BufferedInputStream bis = new BufferedInputStream(inputStream);

  3. FileOutputStream outputStream = new FileOutputStream("苍井空那点.mp4");

  4. BufferedOutputStream bos = new BufferedOutputStream(outputStream);

  5. int len;

  6. byte[] bs = new byte[1024];

  7. // 开始时间

  8. long begin = System.currentTimeMillis();

  9. while ((len = bis.read(bs)) != -1) {

  10. bos.write(bs, 0, len);

  11. }

  12. // 用时毫秒

  13. System.out.println(System.currentTimeMillis() - begin);// 78


  14. bis.close();

  15. bos.close();


注:由此可以看出高效缓冲流读写速度是非常快的,建议使用。




往期精彩



01 漫谈发版哪些事,好课程推荐

02 Linux的常用最危险的命令

03 互联网支付系统整体架构详解

04 优秀的Java程序员必须了解的GC哪些

05 IT大企业有哪些病,别被这些病毁了自己?

关注我每天进步一点点

你点的在看,我都当成了喜欢



本文分享自微信公众号 - JAVA乐园(happyhuangjinjin88)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

关于Java IO详解七)------随机访问文件流的介绍现已完结,谢谢您的耐心阅读,如果想了解更多关于c – 随机访问文件无法正常工作、Java IO流详解(七)——对象流(序列化与反序列化)、java IO详解、Java IO详解那点事的相关知识,请在本站寻找。

本文标签:

上一篇java Integer 大数据运算(java大数运算biginteger)

下一篇PHP 实例 AJAX 与 MySQL(php和ajax用哪个调用数据)