美文网首页
Java基础之IO流

Java基础之IO流

作者: 小任务大梦想 | 来源:发表于2019-05-30 15:11 被阅读0次

    什么是IO流?

     IO是指应用程序对设备数据的输入输出操作。流的本质是数据传输。例如:键盘是输入设备,而显示器则是输出设备。在Java中定义了各种各样的输入输出方式,它们都被存放在java.io包中。

    IO流的分类

    • 根据处理数据类型分为:字符流和字节流,字符流操作的是16位二进制,字节流操作的是8位二进制。
    • 根据数据流向分为:输入流和输出流

    IO包中类层次关系图

    I层次关系图

    File类

    File类是IO包中唯一代表磁盘文件本身的对象。通过File来创建,删除,重命名文件,判断文件的读写权限以及文件是否存在,设置和查询文件的最近修改时间等。但File类不是InputStream、OutputStream或Reader、Writer的子类,因为它不负责数据的输入输出,而专门用来管理磁盘文件与目录。File类主要用于命名文件、查询文件属性和处理文件目录。

    生成File对象的构造方法:
    1)File (String directorypath)

     例:File file=new File("test.txt"); //根据路径获得相应的File对象
     相对路径:相对于某个文件的路径。
     绝对路径:一个固定的路径,具体到某个盘。

    2)File(URI uri)

    3)File (String parent , String child)

     例:File file=new File(“/Users/wq/Desktop/outInFile","test.txt") ;

    4)File (File parent , String child)

      例: File file=new File("/Users/wq/Desktop/outInFile");
        File file1=new File(file,"test.txt"); //在如果/Users/wq/Desktop/outInFile目录不存在则需要先使用file.mkdir()先创建。

    一个对应于某磁盘文件或目录的File对象一经创建, 就可以通过调用它的方法来获得文件或目录的属性。

    1)boolean exists( ) 判断文件或目录是否存在
    2)boolean isFile( ) 判断是文件还是目录
    3)boolean isDirectory( ) 判断是文件还是目录
    4)String getName( ) 返回文件名或目录名
    5)String getPath( ) 返回文件或目录的路径
    6)long length( ) 获取文件的长度
    7)String getParent( ) 获得父文件夹名称
    8)long lastModified( ) 获取文件的最后修改时间
    9)String getAbsolutePath() 返回文件或目录的绝对路径
    10) ......

    File类中还定义了一些对文件或目录进行管理、创建、删除:

    1) boolean renameTo( File newFile ); 重命名文件
    2) void delete( ); 删除文件
    3) boolean mkdir( ); 创建目录
    4)boolean createNewFile(); 创建文件

    RandomAccessFile类

      RandomAccessFile类支持随机访问,可以跳转到文件的任意位置处读写数据。当要访问一个文件时,不想把文件从头访问到尾,RandomAccessFile类就是最佳的选择。但该类仅限于操作文件啊,不能访问其他的IO设备,如网络,内存映像等。

    RandomAccessFile不属于InputStream和OutputStream类。

     创建RandomAccessFile类对象的构造方法:

     RandomAccessFile(File file, String mode);
     RandomAccessFile(String name, String mode);

     mode值:
     r: 以只读方式打开指定文件 。
     rw :以读写方式打开指定文件 。
     rws: 读写方式打开,并对内容或元数据都同步写入底层存储设备 。
     rwd: 读写方式打开,对文件内容的更新同步更新至底层存储设备 。

     类对象的一些常用方法:
     1 ) void seek(long pos):将文件记录的指针定位到pos位置。
     2 )long getFilePointer( ):返回文件记录指针的当前位置,指针默认位置为0。
     3 )int skipBytes(int n):指针跳过的字节数。
     4 )void setLength(long newLength) :设置此文件的长度。
     5 )......

    e.g.

    package IO;
    
    import java.io.File;
    import java.io.IOException;
    import java.io.RandomAccessFile;
    
    public class RandomAccessFileTest {
    
        public static void main(String[] args) throws IOException {
            RandomAccessFile accessFile = new RandomAccessFile(new File("/Users/wq/Desktop/outInFile/test.txt"), "r");
            
    //      accessFile.seek(8);//指定记录指针的位置
            
            accessFile.skipBytes(9);//跳过多少个字节
            
            System.out.println(accessFile.getFilePointer());//当前位置
            
            byte[] buff = new byte[1024];
            int len = 0;
            while ((len = accessFile.read(buff))!=-1){
                System.out.println(new String(buff,0,len));
                System.out.println(new String(buff,0,len).getBytes("UTF-8").length);//UTF-8编码长度
                
                
            }
            accessFile.close();
    
        }
    
    }
    
    
    

    运行结果:

    9
    候放学了
    12
    

     文件中的内容是“这个时候放学了”,但是指针跳过了9个字节,而不同的编码格式占字节数是不同的,UTF-8编码下一个中文所占字节也是不确定的,可能是2个、3个、4个字节。由运行结果可以看出在UTF-8的编码格式下,一个汉字占了3个字节,所以最后打印出来的结果是跳了三个汉字。


     Java的输入输出流建立在4个抽象类的基础上:InputStream,OutputStream,Reader和Writer,它们无法直接创建实例。一般来说,处理字符串或者字符时应使用字符流,处理字节或二进制对象时应使用字节流。

    字节流

     字节流为处理字节的输入输出提供了丰富的环境,一个字节流可以和其他任意类型的对象合并,包括二进制数据。

    输入字节流(InputStream)

    InputStream是字节输入模式的抽象类,该类的所有方法在出错时都会抛出IOException异常。InputStream的常用方法:

    方法 描述
    int read() 如果下一个字节可读,则返回一个整型,遇见文件尾时,则返回-1
    int read(byte[] b) 从输入流中最多读取b.length个字节的数据,并将其存储在字节数组b中,返回实际读取的字节数,遇见文件尾时返回-1
    int read(byte[] b,int off,int len) 从输入流中读取len个字符的数据,并将其存储在数组b中,从off位置开始,返回实际读取的字符数,遇见文件尾时返回-1。
    void close() 关闭输入流,关闭之后如果再读取则会抛出IOException 异常
    void reset() 重新设置输入指针到先前设置的标志处
    void mark(int numBytes) 在输入流的当前位置放置一个标志,在该流读取numBytes 个byte前都有效
    boolean markSupported() 判断输入流是否支持mark()/reset()操作,如果支持则返回true
    long skip(long n) 忽略n个字节,返回实际忽略的字节
    输出字节流(OutputStream)

     该类的所有方法返回一个void 值,并且在出错的情况下抛出一个IOException异常。OutputStream的常用方法:

    方法 描述
    void write(int b) 将指定的字节/字符输出到输出流中
    void write(byte[]/char[] buf) 将字节数组/字符数组中的数据输出到指定输出流中
    void write(byte[]/char[] buf, int off,int len ) 将字节数组/字符数组中从off位置开始,长度为len的字节/字符输出到输出流中
    void close() 关闭输出流
    void flush() 定制输出状态以使每个缓冲器都被清除,也就是刷新输出缓冲区

    一般操作文件流时的步骤

    • 使用File类找到一个文件。
    • 通过File类的对象去实例化字节流或字符流的子类。
    • 进行字节流或字符流的读写操作。
    • 关闭文件流。

     下面就以文件输入输出流为例,书写例子

    package IO;
    
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    
    public class FileStreamTest {
    
        public static void main(String[] args) throws IOException {
            //文件输出流
            FileOutputStream fileOutputStream = new FileOutputStream("/Users/wq/Desktop/outInFile/test.txt");//空白文件
            //文件输入流
            FileInputStream  fileInputStream  = new FileInputStream("/Users/wq/Desktop/outInFile/test.txt");
            
            byte[] outB = "今天是个好日子".getBytes();
                fileOutputStream.write(outB);
                
                
            byte[] b = new byte[1024];//接收从输入流中读取出来的数据
            if(fileInputStream.read(b) > 0) {
                String result = new String(b);
                System.out.println(result);
            }
            
            fileInputStream.close();//关闭文件输入流
            fileOutputStream.close();//关闭文件输出流
        }
    
    }
    
    

     在执行写入文件的时候,将“今天是个好日子”内容写入到test.txt文件中,在执行读取文件的时候将内容读取了出来。

    • FileInputStream在读取文件的时候,传入的文件路径不存在,那么在执行read方法时会报FileNotFoundException异常。
    • FileOutputStream在写入文件的时候,传入的文件路径不存在, 那么在执行write方法时, 会默认创建一个该路径下的文件并且不会报错。

    字符流

     字符流提供了处理任何类型的输入输出操作功能,但是它们不能直接操作Unicode字符。其层次结构的顶层是Reader和Writer。

    字符输入流(Reader)

    Reader类中的常用方法:

    • int read(),int read(char[] c),abstract int read(char[] b,int off,int len),abstract void close(),long skip(long numChars)和InputStream中的方法用法相似,这里就不一一描述了。
    • Boolean ready():如果下一个输入请求不等待则返回true,否则返回false。
    字符输出流(Writer)

    该类的所有方法都返回void的值并且在出错的时候抛出IOException异常。
    -abstract void close(),abstract void fush(), void write(int char),void write(char ch[]),abstract void write(char[] buf, int off,int len ), void write(String str),void write(String str, int off,int len )其用法和OutputStream中方法的用法相似。

    e.g. FileReader/Filewrite为例

    package IO;
    
    import java.io.FileReader;
    import java.io.FileWriter;
    import java.io.IOException;
    
    public class FileReadAndWrite {
    
        public static void main(String[] args) throws IOException {
            FileWriter fileWriter = new FileWriter("/Users/wq/Desktop/outInFile/test.txt");
            FileReader fileReader = new FileReader("/Users/wq/Desktop/outInFile/newtest.txt");
            
            String write = "这个时候放学了";
            fileWriter.write(write);//写入文件
            
            char[] reader = new char[1024];
            int i =fileReader.read(reader);
            if(i >0) {
                System.out.print(new String(reader,0,i));//读出文件
            }
            
            fileWriter.close();
            fileReader.close();
    
        }
    
    }
    
    

    几种流的使用

    1)管道流

     管道流主要用于两个线程间的通信。管道流分为管道字节流(PipedInputStream,PipedOutputStream)和管道字符流(PipedReader,PipedWriter)。
    e.g.

    package IO;
    
    import java.io.IOException;
    import java.io.PipedInputStream;
    import java.io.PipedOutputStream;
    
    public class PipedStreamDemo {
    
        public static void main(String[] args) {
            try {
            Sender sender = new Sender();
            Receiver receiver = new Receiver();
            
            PipedOutputStream out = sender.getPipedOut();//写入
            PipedInputStream in = receiver.getPipedin();//读出
            out.connect(in);//将输出发送到输入
            
            Thread senderThread = new Thread(sender);
            Thread receiverThread = new Thread(receiver);
            
            senderThread.start();//启动线程
            receiverThread.start();
            
            } catch (IOException e) {
                e.printStackTrace();
            }
            
        }
    
    }
    class Sender implements Runnable{
        private PipedOutputStream pipedOut = new PipedOutputStream();
    
        public PipedOutputStream getPipedOut() {
            return pipedOut;
        }
    
        @Override
        public void run() {
            String message = "那谁,收到消息的人,你好!";
            try {
                pipedOut.write(message.getBytes());//写入内容
                pipedOut.close();//关闭流
            } catch (IOException e) {
                e.printStackTrace();
            }
            
        }
    }
    class Receiver implements Runnable{
        private PipedInputStream pipedin = new PipedInputStream();
        
        public PipedInputStream getPipedin() {
            return pipedin;
        }
    
        @Override
        public void run() {
            byte[] b = new byte[1024];
            try {
                pipedin.read(b);//读出数据
                
                String messageIn = new String(b);
                System.out.println("这是收到的信息: " +messageIn);
                pipedin.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            
        }
    }
    

    运行结果

    这是收到的信息: 那谁,收到消息的人,你好!
    
    2 )对象流(ObjectInputStream,ObjectInputStream)

     使用对象流写入或读入对象时,要保证对象是序列化的。这是为了保证能把对象写入到文件,并能再把对象读回到程序中的缘故。一个类如果实现了Serializable接口,那么这个类创建的对象就是所谓序列化的对象。
    e.g.

    package IO;
    
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    
    public class ObjectStreamDemo {
    
        public static void main(String[] args) throws IOException, ClassNotFoundException {
            
            FileOutputStream fileOutputStream = new FileOutputStream("/Users/wq/Desktop/outInFile/student.txt");
            
            ObjectOutputStream objectOutputStream =new ObjectOutputStream(fileOutputStream);
            Student student = new Student("琪琪", 23);
            objectOutputStream.writeObject(student);
            
            
            FileInputStream fileInputStream=new FileInputStream("/Users/wq/Desktop/outInFile/student.txt");
            ObjectInputStream objectInputStream=new ObjectInputStream(fileInputStream);
            Student tempStudent=(Student)objectInputStream.readObject();
            System.out.println("Student对象为:"+tempStudent);
            
            //关闭流
            objectInputStream.close();
            objectOutputStream.close();
    
        }
    
    }
    
    

     ObjectOutputStream对象输出流在写入文件student.txt的时候,因为对象是被序列化了的,所以看不到你想让它写进去的内容,只有通过ObjectInputStream把文件读出来显示。

    注意事项
     1.读取顺序和写入顺序一定要一致,不然会读取出错。
     2.保证对象是序列化的,必须实现Serializable接口。

    3 )转换流

     InputStreamReader和OutputStreamWriter这两个类是字节流和字符流之间相互装转换的类,其中InputStreamReader用于讲一个字节流中的字节解码成字符,OutputStreamWriter用于将写入的字符解码成字节后写入一个字节流。
    e.g.

    package IO;
    
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    
    public class BufferStream {
        
         public static void main(String[] args) throws IOException {
                InputStreamReader reader=new InputStreamReader(System.in);
               
                BufferedReader bufferedReader=new BufferedReader(reader);
                
                    System.out.println("请输入 : ");
                    String str = bufferedReader.readLine();//获取键盘输入的数据
                    if(str != null) {
                         System.out.println("打印输入的内容 :" +str);
                    }
                    
                    bufferedReader.close();//关闭流
        }
    }
    

     为了达到最高效率,避免频繁的进行字符与字节之间的转换,最好使用BufferedWriter类包装OutputStreamWriter类,用BufferedReader类包装InputStreamReader类。

    BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(System.in));
    BufferedWriter bufferedWriter=new BufferedWriter(new OutputStreamWriter(System.out));

    4 )打印流

     主要包含字节打印流PrintStream,字符打印流PrintWriter。PrintStream类提供了一系列的print和println方法,可以实现将基本数据类型的格式转换成字符串输出。

     PrintStream类中构造方法:
     1 ) PrintStream(OutputStream out)
     2 ) PrintStream(OutputStream out,Boolean autoflush);//autoflush遇到换行符时是否自动清空缓冲区。
     3 ) PrintStream(OutputStream out,Boolean autoflush,String econding);//econding编码方式

    package IO;
    
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.OutputStream;
    
    public class PrintStream {
    
        public static void main(String[] args) throws FileNotFoundException {
            
            OutputStream out = new FileOutputStream("/Users/wq/Desktop/outInFile/PrintStreamTest.txt");//将内容输出到制定文件中
            
            java.io.PrintStream printStream = new java.io.PrintStream(out);
            
            printStream.print("对面的女孩看过来");
            printStream.close();
            
        }
    
    }
    
    
    运行结果图

      PrintWriter类中构造方法:
     1 ) PrintWriter(File file);//使用指定文件创建不具有自动行刷新的新 PrintWriter
     2 ) PrintWriter(OutputStream out);
     3 ) PrintWriter(OutputStream out,boolean autoFlush);
     4 ) PrintWriter(String fileName);//创建具有指定文件名称的 PrintWriter
     5 ) PrintWriter(Writer out,boolean autoFlush);
     6 ) ......
       //autoflush遇到换行符时是否自

    e.g.

    package IO;
    
    public class PrintWriter {
    
        public static void main(String[] args) {
            java.io.PrintWriter printWriter = new java.io.PrintWriter(System.out);
            printWriter.println("这是在屏幕上面输出的内容");
            
            printWriter.close();
        }
    
    }
    
    
    5 )合并流(SequenceInputStream)

     将两个文件合并在一起,主要操作的是内容

     合并流的操作步骤:

     1 )创建输入流
     2 )建立一个FileOutputStream实例,用于写入合并的文件的内容。
     3 )通过SequenceInputStream类,把文件的内容合并起来,放到这个类实例流中。但如果我们有超过两个输入流需要加入到合并之后,就不能直接传递输入流的引用到合并流,我们需要将输入流封装到一个枚举类型的对象中,将该对象的引用传递给合并流的构造函数
     4 )把SequenceInputStream实例的内容读取出来。

    package IO;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.SequenceInputStream;
    
    public class SequenceInputStreamTest {
    
        public static void main(String[] args) throws IOException {
            
            FileInputStream fileInputStream1 = new FileInputStream(new File("/Users/wq/Desktop/outInFile/test.txt"));//要合并的两个文件
            FileInputStream fileInputStream2 = new FileInputStream(new File("/Users/wq/Desktop/outInFile/newtest.txt"));
            
            SequenceInputStream sequenceInputStream = new SequenceInputStream(fileInputStream2, fileInputStream1);//执行合并操作
            
            FileOutputStream mergefile = new FileOutputStream(new File("/Users/wq/Desktop/outInFile/mergefile.txt"));//合并之后的文件
         
            int len;
            while ((len = sequenceInputStream.read()) != -1) {
                    mergefile.write(len);
                }
            
            
            
            FileInputStream fileInputStream=new FileInputStream(new File("/Users/wq/Desktop/outInFile/mergefile.txt"));
            byte[] b = new byte[1024];
            if(fileInputStream.read(b) > 0) {
                String result = new String(b);
                System.out.println(result);
            }
            
            
            sequenceInputStream.close();//关闭合并流
            fileInputStream2.close();//关闭输入流
            fileInputStream1.close();
            mergefile.close();//关闭输出流
            fileInputStream.close();
            
        }
    
    }
    
    

    最后输出的结果是:把test.txt,newtest.txt,中的内容合并到了mergefile.txt中

    Qiqi这个时候放学了
    
    文件合并

    但是如果要合并的文件数超过了两个应该怎样进行合并呢?
      SequenceInputStream提供了合并多个文件的构造方法:
      SequenceInputStream(Enumeration<? extends InputStream> e)

    Vector<InputStream> vector = new Vector<>();
    vector.add(inputStream1);
    vector.add(inputStream2);
    vector.add(inputStream3);
    //获取迭代器
    Enumeration<InputStream> elements = vector.elements();
    //构建合并源,把三个文件读到一起,
    SequenceInputStream sis = new SequenceInputStream(elements);

    package IO;
    
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.SequenceInputStream;
    import java.util.Enumeration;
    import java.util.Vector;
    
    public class MoreSequenceInputStreamTest {
    
        public static void main(String[] args) throws IOException {
            FileInputStream fileInputStream1 = new FileInputStream(new File("/Users/wq/Desktop/outInFile/test.txt"));//要合并的文件
            FileInputStream fileInputStream2 = new FileInputStream(new File("/Users/wq/Desktop/outInFile/newtest.txt"));
            FileInputStream fileInputStream3 = new FileInputStream(new File("/Users/wq/Desktop/outInFile/test1.txt"));
            FileInputStream fileInputStream4 = new FileInputStream(new File("/Users/wq/Desktop/outInFile/test2.txt"));
            
            
            
            Vector<FileInputStream> fileInputStreams = new Vector<FileInputStream>();
            fileInputStreams.add(fileInputStream3);
            fileInputStreams.add(fileInputStream4);
            fileInputStreams.add(fileInputStream2);
            fileInputStreams.add(fileInputStream1);
            
             Enumeration<FileInputStream> enumeration = fileInputStreams.elements();//SequenceInputStream(Enumeration<? extends InputStream> e)用来执行多个文件合并操作
            
            SequenceInputStream sequenceInputStream = new SequenceInputStream(enumeration);
            
            FileOutputStream mergefile = new FileOutputStream(new File("/Users/wq/Desktop/outInFile/mergefile.txt"));//合并之后的文件
         
            int len;
            while ((len = sequenceInputStream.read()) != -1) {
                    mergefile.write(len);
                }
            
            
            
            FileInputStream fileInputStream=new FileInputStream(new File("/Users/wq/Desktop/outInFile/mergefile.txt"));
            byte[] b = new byte[1024];
            if(fileInputStream.read(b) > 0) {
                String result = new String(b);
                System.out.println(result);
            }
            
            
            sequenceInputStream.close();//关闭合并流
            fileInputStream2.close();//关闭输入流
            fileInputStream1.close();
            mergefile.close();//关闭输出流
            fileInputStream.close();
    
        }
    
    }
    
    

    运行结果

    SequenceInputStream —> 两个文件合并在一起,主要操作的是内容 —> 例如:Qiqi这个时候放学了
    

    代码以及概念描述有问题的地方请及时指正

    相关文章

      网友评论

          本文标题:Java基础之IO流

          本文链接:https://www.haomeiwen.com/subject/ntresftx.html