Java--IO流

Java中的流分为字节流和字符流,包括输入流和输出流,用于数据传输。字节流如InputStream和OutputStream,处理单个字节数据;字符流如Reader和Writer,处理字符数据。文件流如FileInputStream和FileOutputStream用于读写文件。过滤流如BufferedInputStream和BufferedOutputStream提供缓冲功能,提高读写效率。对象流实现对象的序列化和反序列化,实现持久化。管道流用于线程间通信,实现生产者和消费者模式。装饰器模式在IO中用于扩展原始类功能,如BufferedInputStream。适配器模式在字符流和字节流间转换起作用。
摘要由CSDN通过智能技术生成

一、流的概念

1、流的概念:Java对流是用对象方式处理的,具体的说,流对象就是数据传输的管道。

2、输入流和输出流

  • 读或输入:把数据从外部通过输入流对象传输到内部

内部—可以是内存,可以是数组、变量、对象等

外部:可以是文件、键盘、网络等

  • 写或输出:把数据从内部通过输出流对象传输到外部

流一般分为输入流和输出流两类,但这种划分不是绝对的。例如,一个文件,在读取它的数据时,它就是输入流;而在保存数据到文件时,它就是一个输出流

4、文本文件和二进制文件

  • 文本文件 :把内存中的数据以字符的形式存放的文件叫文本文件。键盘和显示器也属于文本文件

优点:可见

缺点:由于文件的数据存储方式和内存不同需要转换,影响效率

  • 二进制文件 :直接把内存中的数据存放在文件中,这样的文件叫二进制文件

优点:由于二进制文件的数据存储方式和内存相同不需要转换,执行效率较高

缺点:不可见

二、字节流

2.1、概念

1、流对象的种类:从整体可以划分为字节流和字符流

  • 字节流:一次传输一个字节的数据
  • 字符流:一次传输一个字符的数据(需从字节流转换)

2、基类

Java中每一个字节流的基本功能依赖于基本类 InputStream 和 OutputStream ,它们都是抽象类,不能直接使用。

  • InputStream类的方法有:

read():从流中读入数据

skip():跳过流中若干字节数

available() : 返回流中可用字节数

mark() : 在流中标记一个位置

reset() : 返回标记过的位置

markSupport() : 是否支持标记和复位操作

close() : 关闭流

  • OutputStream类的方法有:

write() : 将数据输出到流中

flush(): 刷空输出流,并将缓冲区中的数据强制送出

close():关闭流

2.2、文件流

1、使用代码:

从file1.text读取内容写入到file2.text


import java.io.*;

  public class Filestream
 {
    public static void main(String args[])
    {
        try
        {
             File inFile=new File("file1.txt");
             File outFile=new File("file2.txt");
             FileInputStream fis=new FileInputStream(inFile);
             FileOutputStream fos=new  FileOutputStream(outFile);
	         int c;
             while((c=fis.read())!=-1)
             {System.err.println(c);
             fos.write(c);
             }
             fis.close();
             fos.close();
	    }catch(FileNotFoundException e) {//InputStream用来打开一个输入文件,若要打开文件不存在,会产生异常
           System.out.println("FileStreamsTest: "+e);
	    }catch(IOException e) {//在进行读/写操作时可能会有IO异常发生,必须捕获
	    System.err.println("FileStreamsTest: "+e);
	     }
    }
}

File inFile=new File(“file1.txt”);

创建文件对象

FileInputStream fis=new FileInputStream(inFile);

创建文件输入流,用来打开指定的输入文件。当输入文件不存在时会报异常

FileOutputStream fos=new FileOutputStream(outFile);

创建文件输出流,用来打开指定的输出文件。当输出文件不存在时,会新建这个名字的输出文件

fis.read()

读取输入文件的数据

fos.write©;

将数据写入到文件中

2、随机流

对于InputStream和OutputStream来说,它们的实例都是顺序访问流,即只能进行顺序读/写。而类RandomAccessFile则允许对文件内容同时完成读和写操作,它直接继承object ,并且同时实现了接口DataInput和DataOutput,提供了支持随机文件操作的方法:

  • readXXX()或writeXXX(): 如ReadInt(), ReadLine(), WriteChar(), WriteDouble()等。
  • int skipBytes(int n):将指针向下移动若干字节
  • length():返回文件长度
  • long getFilePointer():返回指针当前位置
  • void seek(long pos):将指针调到所需位置

在生成一个随机文件对象时,除了要指明文件对象和文件名之外,还需要指明访问文件的模式。

import java.io.*;
public class random_file
{
    public static void main(String args[])
    {
       int[] data_arr = {12,31,56,23,27,1,43,65,4,99};
        try            //99,4,65,43,1,27,23,56,31,12
         {
          RandomAccessFile randf = new RandomAccessFile("temp.dat","rw");
           for (int i = 0; i < data_arr.length; i++) {
               //将数组元素写到二进制文件
               randf.writeInt(data_arr[i]);
           }

           for(int i=data_arr.length-1; i>=0; i--)
            {
                //调节指针位置  一个元素占用4个字节 ,读的是二进制文件
                randf.seek(i*4);//9*4=36,从最后一个元素开始读,这就是倒着把数组读出来
                System.out.println(randf.readInt());
            }

           //读完后指针指向12
            randf.seek(8); //12  指针调8个字节---2个元素
            System.out.println(randf.readInt());//56
            System.out.println(randf.readInt());//23

            randf.skipBytes(8);//23,跳过八个字节,跳过两个元素
            System.out.println(randf.readInt()); //43
            randf.close();
          }catch (IOException e){
             System.out.println("File access error: "+e);
          }
    }
}

2.3、过滤流

也就是套接流,需要套接其他的流才能使用

  • java.io中提供类FilterInputStream和FilterOutputStream分别对其他输入/输出流进行特殊处理,它们在读/写数据的同时可以对数据进行特殊处理。另外还提供了同步机制,使得某一时刻只有一个线程可以访问一个输入/输出流。
    • 要使用过滤流,首先必须把它连接到某个输入/输出流上,通常在构造方法的参数中指定所要连接的流 :FilterInputStream(InputStream in); FilterOutputStream(OutputStream out);

1、缓冲流

BufferedInputStream 和 BufferedOutputStream

  • 为输入和输出流创建和维持缓冲

  • 缓冲是一种提高读写效率的机制

  • 将缓冲流与文件流相接

  • 字节缓冲流采用了装饰器模式来增强InputStream、OutputStream子类对象的功能

例如,通过BufferedInputStream增强FileInputStream的功能

BufferedInputStream bufferedInputStream = new BufferedInputStream(newFileInputStream("input.txt"));
  • 字节缓冲流如何提高性能?

主要体现在调用write()和read()方法的时候,由于字节缓冲流内部有缓冲区(字节数组),因此,字节缓冲流会先将读取到的字节存放在缓冲区,大幅减少io操作,提高读取效率。

FileInputStream in = new   FileInputStream(“file1.txt”);

FileOutputStream out = new FileOutputStream (“file2.txt”);

//套接。将文件流in套接缓存流,还可以设置缓冲区的大小

BufferedInputStream bin = new BufferedInputStream(in,256) 

BufferedOutputStream bout = new BufferedOutputStream(out,256);

int len;

byte bArray[]=new byte[256];

len=bin.read(bArray);  //len中得到是长度, bArray中得到的是数据

2、数据流

数据流类DataInputStream和DataOutputStream的处理对象除了是字节或字节数组外,还可以实现对文件的不同数据类型的读写。

  • 数据流可以通过如下方式建立
FileInputStream fis = new  FileInputStream("file1.txt");
FileOutputStream fos = new  FileOutputStream("file2.txt");
DataInputStream dis = new DataInputStream(fis);
DataOutputStream dos = new DataOutputStream(fos);

可以用readLine()方法读取一行信息。适合于文本文件。

2.4、标准流

1、标准流

java.lang包中的System类管理标准输入/输出流和错误流

  • System.in ,为InputStream类对象,用于从标准输入设备中获取输入数据(通常是键盘)
  • System.out,为PrintStream类对象,把输出送到缺省的显示设备(通常是显示器)
  • System.err,也为PrintStream类对象,把错误信息送到缺省的显示设备

每当main方法被执行,就自动生成上述三个对象

//类 System字段 In “标准”输入流。 
class System{
public static final InputStream in  //“标准”输入流。 
public static final PrintStream out //"标准”输出流 
public static final PrintStream err //“标准”错误流
  
}

System类中,是用静态final修饰,输入流、输出流、错误流

  • static:静态变量。可以直接通过类System调用。这就是为什么后面使用可以直接System.in
  • final:被final修饰的变量,需要赋值并且一旦赋值不能改变。在这里是如何赋值呢?

创建InputStream对象,通过底层,将对象set进变量in里,所以in里包含InputStream对象

2.5、对象流

1、对象流

  • 对象的持续性

能够记录自己的状态以便将来再生的能力,叫对象的持续性

  • 对象的串行化(序列化)

对象通过写出描述自己状态的数值来记录自己的过程叫串行化。

  • 对象流

能够输入输出对象的流称为对象流。可以将对象串行化后通过输入输出流写入文件或传送到其他地方

只有实现Serializable接口的类才能被串行化,Serializable接口中没有任何方法,当一个类声明实现Seriazable接口时,只是表明该类加入对象串行化协议。

如:

public class Student implements Serializable
{
  int id;
  String name;
  int age;
  String department;

  public Student()
	{System.out.println("无参构造方法被调用");}

  public Student(int id, String name, int age, String department)
  {  System.out.println("有参构造方法被调用");
     this.id = id;
     this.name = name;
     this.age = age;
     this.department = department;
  }

  @Override
  public String toString(){
  	return "id = " + id + " name = " + name+" age = " + age+" department = " + department;
  }
}

Student类实现了Serializable接口 ,表明该类可以被序列化----保存对象的状态

2、序列化

代码如下:

public class TestStudent
{
     public static void main(String[] args){
     	 Student t = new Student(123, "李华", 23, "开发部门");

     	 try{
    	    FileOutputStream fout = new FileOutputStream("student.data");
    	    ObjectOutputStream out = new ObjectOutputStream(fout);

    	    out.writeObject(t);

    	    out.close();
    	    fout.close();
    	}catch(FileNotFoundException e){//文件不存在抛出异常
    	    System.out.println("File Not Found!");
    	}catch(IOException e){//传输过程中io问题抛出异常
    	    System.out.println("I/O Error!");
    	}
     }
}

将把对象的状态保存到网络或者二进制文件中: 条件如下

  • 这个对象的类要实现序列化协议(实现Serializable接口)
  • 创建类的对象,并给对象的属性赋值。—创建对象的状态
  • 先创建一个文件输出流,再套接在对象输出流上。
  • 将t所指的对象的状态 ,通过对象输出流,输出到student.txt文件中

序列化就是将对象的状态通过输出流写入到一个二进制文件的过程

3、反序列化

代码如下:

public class TestStudent1
{
     public static void main(String[] args){
     	 try{
    	    FileInputStream fin = new FileInputStream("student.data");
    	    ObjectInputStream in = new ObjectInputStream(fin);

    	    Student t = (Student)in.readObject();
    	    System.out.println("Student : " + t);
    	    in.close();
    	    fin.close();
    	}catch(FileNotFoundException e){
    	    System.out.println("File Not Found!");
    	}catch(ClassNotFoundException e){
    	    System.out.println("ClassNotFoundException!");
    	}catch(IOException e){
    	    System.out.println("I/O Error!");
    	}catch(Exception e){
    	    System.out.println("I/O Error!");
    	}
     }
}

从二进制文件中读取对象的状态(属性值)

  • 创建文件输入流,指向存放对象状态的文件,再创建对象输入流套接在文件输入流
  • 从流所指的二进制文件中读取对象的状态,再生出对象。
  • 这也是创建对象,但是并没有调用构造方法来创建

反序列化就是指从流所指的文件中读取对象的状态

4、注意:

  • 串行化只能保存对象的非静态成员变量,而不能保存任何成员方法和静态成员变量,并且保存的只是变量的值,对于变量的任何修饰符都不能保存。
  • 对于某些类型的对象,状态是瞬时的,这样的对象无法保存其对象,如Thread对象和流对象。对于这样的变量必须用transient关键字 。加上这个关键字的成员变量都不会被保存。
  • 串行化可能涉及将对象存放到磁盘上或在网络上发送数据,这会带来安全问题,所以对于一些需要保密的数据,不应保存在永久介质中,为了保证安全,应在这些变量前加上tranient关键字

5、目前学到的三种创建对象方法:

  • 通过new来创建对象–调用构造方法
  • 通过反射来创建关联类的对象 --调用无参构造方法
  • 通过反序列化来创建对象—不调用构造方法

2.6、管道流

1、管道用来把一个程序、线程和代码块的输出连接到另一个程序、线程和代码块的输入。java.io中提供了类PipedInputStream和PipedOutputStream 作为管道的输入/输出流。

2、实现生产者和消费者模式

/**
 * 用管道模拟生产者和消费者线程
 */
public class ThreadWork {
    public static void main(String[] args) throws IOException {
        //创建管道输出流
        PipedOutputStream pos = new PipedOutputStream();
        //创建管道输入流,并和输出流管道连接
        PipedInputStream pis = new PipedInputStream(pos);

        //创建两个线程
        PipeProducer producer = new PipeProducer(pos, "生产者线程");
        PipeConsumer consumer = new PipeConsumer(pis, "消费者线程");
        producer.start();
        consumer.start();


    }

}

/**
 * 生产者线程,与输出流有关
 */
class PipeProducer extends Thread{
    private PipedOutputStream pos;
    public PipeProducer(PipedOutputStream pos,String name){
        super(name);
        this.pos = pos;

    }

    @Override
    public void run(){
        int i = 0;
        try {
          while (true){
              Thread.sleep(2000);
              if (i==0){
                  i=1;
                  System.out.println(Thread.currentThread().getName()+"生产:"+i);
                  pos.write(i);
              }else {
                  pos.close();
                  break;

              }

          }

        }catch (Exception e){
            e.printStackTrace();
        }
    }

}

/**
 * 消费者线程(与管道输入流)
 */
class PipeConsumer extends Thread{
    private PipedInputStream pis;
    public PipeConsumer(PipedInputStream pis,String name){
        super(name);
        this.pis = pis;
    }

    @Override
    public void run(){
        try{
            while (true){
                int i = pis.read();
                if (i==1){
                    System.out.println(Thread.currentThread().getName()+"消费了"+i);
                    i=0;
                }else {
                    pis.close();
                    break;
                }

            }

        }catch (IOException e){
            e.printStackTrace();
        }

    }

}

2.7、顺序输入流

将多个不同的输入流统一为一个输入流。

FileInputStream f1,f2;
String s;
f1 = new FileInputStream(“file1.txt”);
f2 = new FileInputStream(“file2.txt”);
SequenceInputStream fs = new SequenceInputStream(f1, f2);
DataInputStream ds = new DataInputStream(fs);
while( (s = ds.readLine()) != null )
   System.out.println(s);

只适用于连接多个文件输入流

三、字符流

为什么I/O操作要分字节流和字符流?

  • 字符流是由Java虚拟机将字节转换得到的,这个过程比较耗时
  • 如果不知道编码类型用字节流很容易出现乱码

因此直接加入用于字符流处理的类

1、从jdk1.1开始,java.io包中加入了专门用于字符流处理的类,它们是以Reader和Writer为基础派生的一系列类

2、InputStreamReader和OutputStreamWriter 是java.io包中用于处理字符流的最基本的类,用来在字节流和字符流之间作为中介。

3、同样提供了字符流的缓冲流。java.io中也提供了缓冲流BufferedReader和BufferedWriter。其构造方法与BufferedInputStream和BufferedOutputStream相类似。另外,除了read()和write()方法外,它还提供了整行字符处理方法。

class FileTOUnicode 
{
   public static void main(String args[])
   {
        try
        {
          //文件输入流
             FileInputStream fis = new FileInputStream(“file1.txt");
             //字符输入流                                          
             InputStreamReader dis = new InputStreamReader(fis);
             // InputStreamReader dis = new InputStreamReader(System.in);
             BufferedReader reader = new BufferedReader(dis);
             String s;
             while( (s = reader.readLine()) != null )  
             {
                 System.out.println("read: "+s);
             }
             dis.close();
         }catch(IOException e)
         {
             System.out.println(e);
         }
   }//main()
}//class

4、其他字符流

  • 对字符数组进行处理:CharArrayReader , CharArrayWriter
  • 对文本文件进行处理:FileReader,FileWriter
  • 对字符串进行处理:StringReader ,StringWriter
  • 过滤字符流:FilterReader,FilterWriter
  • 管道字符流:PipedReader,PipedWriter
  • 行处理字符流:LineNumberReader
  • 打印字符流:PrintWriter

四、Java IO 设计模式总结

1、装饰器模式

装饰器模式通过组合替代继承来扩展原始类的功能 ,在一些继承关系比较复杂的场景(IO 这一场景各种类的继承关系就比较复杂)更加实用。

如,对于字节流来说,FilterInputStream和FilterOutStream是装饰器的核心,分别用于增强InputStream和OutputStream子类对象的功能

例如,字节缓冲流、对象流等都是用来增强InputStream和OutputStream子类对象的功能

  • 通过BufferInputStream()来增强FileInputStream的功能
BufferedInputStream bufferedInputStream = new BufferedInputStream(newFileInputStream("input.txt"));

IO操作中像这样用到装饰器模式的地方还有很多。

2、适配器模式

适配器(Adapter Pattern)模式 主要用于接口互不兼容的类的协调工作,你可以将其联想到我们日常经常使用的电源适配器。

适配器模式中存在被适配的对象或者类称为 适配者(Adaptee) ,作用于适配者的对象或者类称为适配器(Adapter) 。适配器分为对象适配器和类适配器。类适配器使用继承关系来实现,对象适配器使用组合关系来实现。

  • 在IO流中,字符流和字节流的接口不同,但他们之间可以互相转换,他们之间的协调工作就是通过适配器模式来做的。通过适配器,我们可以将字节流对象适配成一个字符流对象 ,这样我们可以直接通过字节流对象来读取或者写入字符数据。
//InputStreamReader是适配器,FileInputStream是被适配的类--适配者
InputStreamReader isr = new InputStreamReader(new FileInputStream(fileName),"UTF-8");

五、小结

1、在Java中有数据传输的地方都用到I/O流(通常是文件,网络,内存和标准输入输出等)。

2、InputStream 和OutputStream是所有字节流的祖先(只有RandomAccessFile类是一个例外 ),read和write是它们最基本的方法,读写单位是字节。

3、Reader 和Writer是所有字符流的祖先,read和write是它们最基本的方法,读写单位是字符。

4、File, File(Input/Output)Stream, RandomAccessFile是处理本地文件的类。

5、Data(Input/Output)Stream是一个过滤流的子类(套接流),借此可以读写各种基本数据 ,在文件和网络中经常使用。如: readByte, writeBoolean等。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Zero摄氏度

感谢鼓励!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值