25、JAVA进阶——输入输出流

简介: 25、JAVA进阶——输入输出流

一、认识Java的流


(1)读文件是指把文件中的数据读取到内存中。

(2)写文件是把内存中的数据写到文件中。

(3)Java程序通过流来读写文件。

(4)流是指一连串流程的字符,是以先进先出的方式发送和接收数据的通道。

(5)流分为输入流和输出流,输入/输出流是相对于计算机内存来说的,如果数据输入到内存,则为输入流,如果从内存中输出则为输出流。

1.png

(6)Java的输出流主要有OutputStream类和Writer类作为基类,输入流主要由InputStream类和Reader类作为基类。

(7)在java.io包中,封装了许多输入/输出流的API,在程序中,这些输入/输出流类的对象称为流对象。

(8)可以通过流对象将内存中的数据以流的方式写入文件,也可以通过流对象将文件中的数据以流的方式读取到内存。

(9)构造流对象时往往会和数据源(如文件)联系起来。数据源分为源数据源和目标数据源。输入流关联的是源数据源;输出流关联的是目标数据源。

(10)输入/输出流又分为字节流和字符流两种形式

       字节流是8位通用字节流,其基本单位是字节。字节流的基类是InputStream类和OutputStream类,它们是抽象类。

       字符流是16位Unicode字符流,基本单位是Unicode字符。字符流最适合用来处理字符串和文本,因为它们支持国际上大多数的字符集和语言。字符流的基类是Reader类和Writer类,它们是抽象类。

(11)一个被访问的流基本特征都是通过实现InputStream类、OutputStream类、Reader类和Writer类这4个抽象类的方法建立的。

1) InputStream类 常用方法如下:

       InputStream类的常用子类有 FileInputStream ,用于从文件中读取数据。

2) OutputStream类 常用方法如下:

       OutputStream类的常用子类有 FileOutputStream ,用于向文件写数据。

3) Reader类 常用方法如下:

       Reader类的常用子类有 BufferedReader ,接受Reader对象作为参数,并对其添加字符缓冲器。


4) Write类 常用方法如下:

       Write类的常用子类有 BufferedWrite ,用于将数据缓冲到字符输出流。

(12)在操作上字节流和字符流还有一个区别,字符流在操作时使用了缓冲区(内部存储器),而字节流在操作时直接操作文件,不会使用缓冲区。

(13)流中的方法在出现错误时都会抛出IOException异常。


二、使用字节流读写文本文件


1、使用字节流类FileInputStream读文本文件

       FileInputStream称为文件输入流,它是字节输入流InputStream抽象类的一个子类,它的作用是将文件中的数据输入到内部存储器(简称内存)中,可以利用它来读取文本文件中的数据,具体实现步骤如下:


(1)导入相关的类


       import java.io.IOException;


       import java.io.InputStream;


       import java.io.FileInputStream;


(2)构造一个文件输入流对象


       InputStream fileObject = new FileInputStream("text.txt");//此时的文件输入流对象fileObject就和源数据源(text.txt)关联起来。


(3)利用文件输入流类的方法读取文本文件中的数据


       fileObject.available();//可读取的字节


       fileObject.read();//读取文件的数据


(4)关闭文件输入流对象


       fileObject.close();


2、使用字节流类FileOutputStream写文本文件

       FileOutputStream称为文件输出流,它是字节输出流OutputStream抽象类的子类,它的作用是把内存中的数据输出到文件中,可以利用它把内存中的数据写入到文本文件中,具体实现步骤如下:


(1)导入相关的类


       import java.io.IOException;


       import java.io.OutputStream;


       import java.io.FileOutputStream;


(2)构造一个文件输出流对象


       OutputStream fos = new FileOutnputStream("text.txt");//此时的文件输出流对象fos就和目标数据源(text.txt)关联起来。


(3)利用文件输出流类的方法把数据写入到文本文件中。


       String str = "好好学习Java";


       byte[] words = str.getBytes();


       fos.write(words,0,words.length);//利用write方法将数据写入到文件中去


(4)关闭文件输入流对象


       fos.close();


package cn.bdqn.demo06;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
public class OutputStreamWriterDemo01 {
  public static void main(String[] args) {
  OutputStream os=null;
  OutputStreamWriter osw =null;
  try {
    os = new FileOutputStream("F:/c.txt",true);
    osw = new OutputStreamWriter(os,"UTF-8");
    osw.write("abcdefg");
    //刷新流的缓冲
    osw.flush();
    System.out.println("文件写入完毕");
  } catch (FileNotFoundException e) {
    e.printStackTrace();
  } catch (UnsupportedEncodingException e) {
    e.printStackTrace();
  } catch (IOException e) {
    e.printStackTrace();
  }  
  }
}


三、使用字符流读写文本文件


1、使用字符流类BufferedReader和FileReader读文本文件

       BufferedReader和FileReader两个类都是Reader抽象类的子类,它们可以通过字符流的方式读取文件,并使用缓冲区,提高了读文本文件的效率。读取文本文件的具体步骤如下:


(1)导入相关的类


       import java.io.FileReader;


       import java.io.BufferedReader;


       import java.io.IOException;


(2)构造一个BufferedReader对象


       FileReader fr = new FileReader("mytest.txt");


       BufferedReader br = new BufferedReader(fr);


(3)利用BufferedReader类的方法读取文本文件中的数据


       br.readLine();//读取一行数据,返回字符串


(4)关闭相关的流对象


       br.close();//后开的流先关


       fr.close();//先开的流后关


2、使用字符流类BufferedWrite和FileWrite写文本文件

       BufferedWrite和FileWrite都是字符输出流Write抽象类的子类,它们可以通过字符流的方式并通过缓冲区把数据写入文本文件,提高了写文本文件的效率。把数据写入文本文件的具体操作步骤如下:


(1)导入相关的类


       import java.io.FileWriter;


       import java.io.BufferedWriter;


       import java.io.IOException;


(2)构造一个BufferedWriter对象


       FileWriter fw = new FileWriter("mytest.txt");


       BufferedWriter bw = new BufferedWriter(fw);


(3)利用BufferedWriter类的方法读取文本文件中的数据


       br.write("hello");//读取一行数据,返回字符串


(4)相关流对象的清空和关闭


       bw.flush();


       bw.close();//后开的流先关


       fw.close();//先开的流后关


package cn.bdqn.demo07;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
public class BufferedWriterDemo01 {
  public static void main(String[] args) {
  Writer writer = null;
  BufferedWriter bw = null;
  try {
    writer = new FileWriter("F:/d.txt");
    bw = new BufferedWriter(writer);
    bw.write("abcd");
    bw.write("qwert");
    //插入一个换行符
    bw.newLine();
    bw.write("asdfg");
    bw.flush();
  } catch (IOException e) {
    e.printStackTrace();
  }finally{
    try {
    bw.close();
    writer.close();
    } catch (IOException e) {
    e.printStackTrace();
    }
  }  
  }
}

四、读写二进制文件

读写二进制文件常用的类有DataInputStream和DataOutputStream。


1、 使用字节流读二进制文件

       利用DataInputStream类读二进制文件,其实与利用FileInputStream类读文本文件及其相似,也要用到FileInputStream类关联二进制文件。具体操作步骤如下:


(1)导入相关的类


       import java.io.FileInputStream;


       import java.io.DataInputStream;


       import java.io.IOException;


(2)构造一个数据输入流对象


       FileInputStream fis = new FileInputStream("HelloWorld.class");


       DataInputStream dis = new DataInputStream(fis);


(3)利用数据输入流类的方法读取二进制文件中的数据


       dis.readInt();//读取出来的是整数


       dis.readByte();//读取出来的数据是Byte


(4)关闭数据输入流


       dis.close();//后开的流先关


       fis.close();//先开的流后关


2、 使用字节流写二进制文件

利用DataOutputStream类写二进制文件,其实与利用FileOutputStream类写文本文件及其相似,也要用到FileOutputStream类关联二进制文件。具体操作步骤如下:


(1)导入相关的类


       import java.io.FileOutputStream;


       import java.io.DataOutputStream;


       import java.io.IOException;


(2)构造一个数据输出流对象


       FileOutputStream fos = new FileOutputStream("temp.class");


       DataOutputStream dos = new DataOutputStream(fos);


(3)利用数据输出流类的方法把数据写入二进制文件


       dos.write(1); //把数据写入二进制文件


(4)关闭数据输入流


       dos.close();//后开的流先关


       fos.close();//先开的流后关


package cn.bdqn.demo07;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class DataInputStreamAndDataOutputStreamDemo01 {
  public static void main(String[] args) {
  InputStream is = null;
  DataInputStream dis =null;
  OutputStream os = null;
  DataOutputStream dos =null;
  try {
    is = new FileInputStream("F:/guoqing.jpg");
    dis = new DataInputStream(is);
    os = new FileOutputStream("F:/qizi.jpg");
    dos  = new DataOutputStream(os);
    int num;
    while((num=dis.read())!=-1){
    dos.write(num);
    }
    System.out.println("ͼƬش׆Ϊҏ");
  } catch (FileNotFoundException e) {
    e.printStackTrace();
  } catch (IOException e) {
    e.printStackTrace();
  }
  }
}



五、序列化和反序列化


1、认识序列化

(1)序列化是将对象的状态存储到特定存储介质中的过程,也就是将对象状态转换为可保持或可传输格式的过程。


(2)在序列化过程中,会将对象的公有成员、私有成员包括类名,转换为字节流,然后再把字节流写入数据,存储到存储介质中,这里说的存储介质通常指的是文件。


(3)使用序列话的意义在于将Java对象序列化后,可以将其转换为字节序列,这些字节序列可以被保存在磁盘上,也可以借助网络进行传输,同时序列化后的对象保存的是二进制状态,这样实现了平台无关性。即可以将在Windows操作系统中实现序列化的一个对象,传输到UNIX操作系统的机器上,再通过反序列化后得到相同对象,而无需担心数据因平台问题显示异常。


2、 序列化保存对象信息

(1)序列化介质允许将实现了序列化的Java对象转换为字节序列,这个过程需要借助于I/O流来实现。


(2)Java中只有实现了java.io.Serializable接口类的对象才能被序列化。


(3)Serializable表示可串行的、可序列化的,所以,对象序列化在某些文献上也称为串行化。


(4)JDK类库中有些类,如String类、包装类和Date类等都实现了Serializable接口。


(5)对象序列化的主要步骤如下所示:


       1)创建一个对象输出流(ObjectOutputStream),它可以包装一个其他类型的输出流,如文件输出流FileOutputStream。


       2)通过对象输出流的writeObject()方法写对象,也就是输出可序列化对象。


3、 反序列化获取对象信息

(1)反序列化,顾名思义就是与序列化相反,序列化是将对象的状态信息保存到存储介质中,反序列化则是从特定存储介质中读取数据并重新构建成对象的过程。


(2)通过反序列化,可以将存储在文件上的对象信息读取出来,然后重新构建为对象。这样不需要将文件上的信息一一读取、分析再组织为对象。


(3)反序列化的主要步骤如下所示:


       1)创建一个对象输入流(ObjectInputStream),它可以包装一个其他类型的输入流,如文件输入流FileInputStream。


       2)通过对象输入流的readObject()方法读取对象,该方法返回一个Object类型的对象,如果程序知道该Java对象的类型,则可以将该对象强制转换成其真实的类型。


(4)如果向文件中使用序列化机制写入多个对象,那么反序列化恢复对象时,必须按照写入的顺序读取。


(5)如果一个可序列化的类,有多个父类(包括直接父类或间接父类),则这些父类要么是可序列化的,要么有无参的构造器;否则会抛出异常。


(6)他通常,对象中的所有属性都会被序列化,但是对于一些比较敏感的信息,比如用户密码,一旦序列化后,人们完全可以通过读取文件或拦截网络传输数据的方式获得这些信息。因此,从安全考虑,某些属性应该限制被序列化,解决的办法是使用transient来修饰。


4、 对象引用的序列化

(1)如果一个类的成员包含其他类的对象,那么序列化这个类的对象时,也要保证该类中的引用类型的对象也是可以序列化的。即当需要序列化某个特定对象时,它的各个成员对象也必须是可序列化的。


(2)序列化的算法规则如下:


       1)所有保存到磁盘中的对象都有一个序列号。


       2)当程序视图序列化一个对象时,将会检查是否已经被序列化,只有序列化后的对象才能被转换成字节序列输出。


       3)如果对象已经被序列化,则程序直接输出一个序列化编号,而不再重新序列化。


       例子:


       定义一个Student类实现序列化接口


public class Student implements Serializable {
  private String name;
  //transient关键字修饰的属性,不会进行序列化操作,那么反序列化得到的数据就是属性的默认值
  private transient int age;
  public Student(String name, int age) {
  super();
  this.name = name;
  this.age = age;
  }
  @Override
  public String toString() {
  return "Student [name=" + name + ", age=" + age + "]";
  }
}


       写入数据


public static void main(String[] args) {
  Student stu1 = new Student("张三", 22);
  OutputStream os= null;;
  ObjectOutputStream oos =null;
  try {
    os = new FileOutputStream("F:/student.txt");
    oos = new ObjectOutputStream(os);
    oos.writeObject(stu1);
    System.out.println("对象写入完毕");
  } catch (FileNotFoundException e) {
    e.printStackTrace();
  } catch (IOException e) {
    e.printStackTrace();
  }
  }


        读取数据


public static void main(String[] args) {
  InputStream is;
  try {
    is = new FileInputStream("F:/student.txt");
    ObjectInputStream ois = new ObjectInputStream(is);
    Object object =ois.readObject();
    Student stu = (Student)object;
    System.out.println(stu);
  } catch (FileNotFoundException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
  } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
  } catch (ClassNotFoundException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
  }
  }


六、总结


1、InputStream类(抽象类)

1.1、FileInputStream类(InputStream类子类)

               FileInputStream(File file)

               FileInputStream(String name)



2、OutputStream类(抽象类)

2.1、FileOutputStream类(OutputStream类子类)

               FileOutputStream(File file)

               FileOutputStream(File file, boolean append)

               FileOutputStream(String name)

               FileOutputStream(String name, boolean append)

3、Reader类(抽象类)

3.1、InputStreamReader类(Reader类子类)

               InputStreamReader(InputStream in)

               InputStreamReader(InputStream in, String charsetName)

3.2、FileReader类(InputStreamReader类子类)

               FileReader(File file)

               FileReader(String fileName)

3.3、BufferedReader类(Reader类子类)

               BufferedReader(Reader in)

4、Writer类(抽象类)

4.1、OutputStreamWriter类(Writer类子类)

               OutputStreamWriter(OutputStream out)

               OutputStreamWriter(OutputStream out, String charsetName)

4.2、FileWriter类(OutputStreamWriter类子类)

               FileWriter(File file)

               FileWriter(File file, boolean append)

               FileWriter(String fileName)

               FileWriter(String fileName, boolean append)

4.3、BufferedWriter类(Writer类子类)

               BufferedWriter(Writer out)

5、DataInputStream类(FilterInputStream类的子类)

6、DataOutputStream类(FilterOutputStream类的子类)

7、ObjectInputStream类(InputStream类子类)

       ObjectInputStream(InputStream in)

8、ObjectOutputStream类(OutputStream类子类)

       ObjectOutputStream(OutputStream)        


   


相关文章
|
3月前
|
Java Perl
Java进阶之正则表达式
【7月更文挑战第17天】正则表达式(RegEx)是一种模式匹配工具,用于在字符串中执行搜索、替换等操作。它由普通字符和特殊元字符组成,后者定义匹配规则。
25 4
|
3月前
|
设计模式 Java
Java进阶之代理
Java进阶之代理
23 4
|
3月前
|
设计模式 Java
Java进阶之代理
Java进阶之代理
25 3
|
3月前
|
设计模式 Java
Java进阶之代理
【7月更文挑战第16天】Java动态代理通过`java.lang.reflect.Proxy`和`InvocationHandler`实现,无需编译期定义代理类。与静态代理相比,它更灵活,代码更简洁,适用于方法数量变化或未知接口代理。
22 2
|
3月前
|
Java 编译器 API
Java进阶之标准注解
【7月更文挑战第15天】Java标准注解包括标记注解(如@Deprecated)、@Override(检查方法重写)、@SuppressWarnings(抑制警告)。多值注解如@RequestMapping在Spring中用于HTTP请求映射。元注解如@Retention控制注解保留策略,@Target指定应用位置。Java8引入类型注解(@FunctionalInterface、@SafeVarargs)和重复注解(@Repeatable)。自定义注解可通过反射读取,如示例中的MyMarkerAnnotation等。
22 2
|
3月前
|
Java
Java进阶之内部类
【7月更文挑战第13天】Java内部类增进代码组织与封装,允许直接访问外部类成员,包括私有成员。主要有四种类型:成员、静态、局部和匿名内部类。匿名内部类常用于一次性实现接口或扩展类。内部类可隐藏实现细节,减少命名冲突,并在特定上下文中定义辅助类。示例展示了静态和非静态内部类如何在Shape类中封装Circle和Rectangle。使用内部类能提升代码可读性,但可能增加复杂性。
34 6
|
3月前
|
Java 编译器 API
Java进阶之标准注解
Java进阶之标准注解
26 1
|
3月前
|
安全 Java
Java进阶之枚举
【7月更文挑战第11天】Java枚举是Java 5引入的特性,用于定义固定常量集合,如星期。枚举是继承自`java.lang.Enum`的特殊类,编译后成为final类,每个枚举值是静态final实例。定义枚举用`enum`关键字,如`public enum Weekday {MONDAY, TUESDAY, ...}`。枚举可包含方法和变量,能实现接口但不能继承其他类。例如,`Weekday`枚举可实现`Describe`接口,提供`describe()`方法。在实际应用中,枚举常用于表示如响应状态等固定选项,便于类型安全和代码阅读。
34 8
|
3月前
|
IDE Java 测试技术
Java进阶之反射
【7月更文挑战第14天】Java反射机制允许在运行时动态获取类信息、创建对象及调用其方法。它基于`Class`类,让我们能访问类的属性、方法、构造器。例如,通过`Class.forName()`加载类,`Class.newInstance()`创建对象,`Method.invoke()`执行方法。反射广泛应用于动态代理、单元测试、序列化及框架中,提供灵活性但牺牲了性能,且可破坏封装性。IDE的代码补全也是反射的应用之一。在使用时需谨慎,避免对私有成员的不当访问。
33 1
|
3月前
|
Java
Java进阶之函数式编程
Java进阶之函数式编程
29 3