(一)什么是IO?

Java高级学习篇之IO流_IO流

   IOInputOutput的缩写,表示的是输入输出流

(1)   I :Input,表示的是将数据从硬盘的文件中输入到内存,称为Read操作。(2)  O :Output,表示的是将数据从内存输出到硬盘的文件,也称为write操作

          Java程序中,对于数据的操作是以"流“的方式进行的。

说明:

①  通过IO流来完成硬盘上数据的读与写(作用)

②  java.io包下提供了各种”流“类和接口,用以获取不同种类的数据,并通过标准的方          法输入或输出数据

(二)流的分类

Java高级学习篇之IO流_IO流_02

   (1)根据操作数的基本单位:字节流(8位)、字符流(16位)

   (2)根据数据的流向: 输入流、输出流

   (3)根据流的对象: 节点流、处理流

①如何区分输入和输出流的流向呢??

站在以内存为核心的角度上:

         若是读取数据,就是 从  磁盘文件---->内存

         若是写入数据,就是 从  内存 ------>磁盘文件                         

②字节流  VS 字节流??

Java高级学习篇之IO流_IO流_03

      字节流在操作时本身不会用到缓冲区(内存),是文件本身直接进行操作的,而字符流在操作时使用了缓冲区,通过缓冲区再操作文件。

体系结构如下:

Java高级学习篇之IO流_IO流_04

表格如下:

          抽象基类

          节点流

          缓冲流

        InputStream

      FileInputStream

BufferedInputStream

       OutputStream

      FileOutputStream

BufferedOutputStream

          Writer

      FileWriter

      BufferedWriter

          Reader

      FileReader

      BufferedReader

说明:

(1)FileInputStream和FileOutputStream是用来处理字节的

(2)FileWriter和FileReader是用来处理字符的


使用场景

字符流:文本文件 <.txt  .java  .c  .cpp等>

字节流:非文本文件 <.jpg  .mp3  .mp4  .avi  .doc  .ppt等>

(三)使用介绍

I. <FileWriter>&<FileReader>

(1)基本介绍

        FileWriter指的是从内存向文件写入字符数据

       FileReader指的是从文件向内存读出字符数据

(2)使用步骤

    ①实例化File类的对象(指明要读出或写入的文件路径)

   ②创建一个具体的流对象(操作具体数据)

   ③通过循环进行数据的读入或读出

   ④流的关闭(必要操作)

(3)实例代码
FileReader测试
@Test
public void testFileReader() {
FileReader tr= null;
try {
//实例化File类的对象,指明路径
File file = new File("hello1.txt");
//创建具体的流对象,用来进行操作数据
tr = new FileReader(file);
//读写数据
int data;
while((data=tr.read())!=-1){
System.out.print((char)data);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭对应的流
try {
if(tr!=null) {
//条件判断是为了防止出现空指针异常
tr.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

Java高级学习篇之IO流_IO流_05

说明:

①通过read读入的是对应字符的ASCILL码值

②异常处理使用的是结构: try---catch---finally(流的关闭

      如果使用throws方法,可能造成内存泄漏

③流的关闭

      数据库连接、输入输出流、Socket连接无法·通过垃圾回收机制进行回收,所以需要手手动关闭。

FileWriter测试
@Test
public void testFileWriter() throws IOException {
//创建文件类对象
File file=new File("hello1.txt");
//创建流对象
FileWriter tr = new FileWriter(file,true);
//写入数据
tr.write("Welcome to China".toCharArray());
tr.write(" Jerry");
//关闭流
tr.close();
}

说明:

①再通过写入流向文件中写入数据时,可以直接使用write方法

说明:

②输出操作,对应的File可以不存在的。并不会报异常

③ File对应的硬盘中的文件如果不存在,在输出的过程中,会自动创建此文件。

    File对应的硬盘中的文件如果存在:

       若流使用的构造器是:FileWriter(file,false) / FileWriter(file):对原有文件的                                       覆盖

       若流使用的构造器是:FileWriter(file,true):不会对原有文件覆盖,而是在原有                                       文 件基础上追加内容

常用的write()重载方法

(1)write(String str):将整个字符串写入

内部调用的是如下函数:

Java高级学习篇之IO流_IO流_06

(2)write(char[]str,int offset,int end):将字符数组写入到文件中

Java高级学习篇之IO流_IO流_07

FileWriter&FileReader测试
@Test
public void testFileRaW() {
FileReader du= null;
FileWriter wr= null;
try {
//创建File类的对象,指明读入和读出的文件
File file=new File("hello.txt");
File file1=new File("hello1.txt");
//创建输入流和输出流的对象
du = new FileReader(file);
wr = new FileWriter(file1,true);
//读数据并且写入数据
char [] tu=new char[5];
int number;
while((number=du.read(tu))!=-1) {
String sr = new String(tu, 0, number);
wr.write(sr);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
//关闭输入输出流
if(du!=null) {
du.close();
}
if(wr!=null){
wr.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

II.<FileOutputStream>&<FileInputStream>

  (1)基本介绍

Java高级学习篇之IO流_IO流_08

FileInputStream指的是<磁盘文件-->内存>读取数据

FileOutPutStream指的是<内存-->磁盘文件>写出数据

(2)使用步骤

    ① 创建File文件类对象

    ② 创建字节流(FileOutputStream/FileInputStream)

    ③ 文件内容的读取和写入

    ④ 关闭输入&输出字节流

      异常处理: try...catch...finally(关闭流)

(3)实例代码
FileOutputStream测试
@Test
public void testFileOutputStream(){
FileOutputStream fos= null;
try {
File fs=new File("hell5.txt");
fos = new FileOutputStream(fs);
fos.write(97);
//说明:97对应的字符是 a,将字符a写入到hell5.txt文件中
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(fos!=null) {
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

write的重载方法:

Java高级学习篇之IO流_IO流_09

FileInputStream测试
@Test
public void testInputStream(){
FileInputStream in= null;
try {
File file=new File("hello.txt");
in = new FileInputStream(file);
byte[]bu=new byte[5];
int len;
while ((len=in.read(bu))!=-1) {
String tr=new String(bu);
System.out.print(tr);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//输出结果:hello world tu?腑鍥?
}

说明:

输出结果中出现了乱码原因如下:

      字节流是用来处理非文本文件,而.txt是非文本文件,所以在读取时并不是直接将一个字符读入,可能是将一个字符分两次读入,从而产生了乱码问题。

FileInputStream&FileOutputStream测试
@Test
public void copyFileIaO() {
FileInputStream input= null;
FileOutputStream out = null;
try {
//创建File类对象
File file1=new File("海豚.png");
File file2=new File("海豚1.png");
//创建输入流和输出流
input = new FileInputStream(file1);
out = new FileOutputStream(file2);
//数据的读入和读出
byte []buf=new byte[5];
int len;
while((len=input.read(buf))!=-1){
out.write(buf,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//输入输出流的关闭
if(input!=null) {
try {
input.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(out!=null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
应用:文件复制
public void copyFile(String initPath,String desPath){
FileInputStream input= null;
FileOutputStream out = null;
try {
//创建File类对象
File file1=new File(initPath);
File file2=new File(desPath);
//创建输入流和输出流
input = new FileInputStream(file1);
out = new FileOutputStream(file2);
//数据的读入和读出
byte []buf=new byte[10];
int len;
while((len=input.read(buf))!=-1){
out.write(buf,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//输入输出流的关闭
if(input!=null) {
try {
input.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(out!=null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
@Test
public void testCopyFile(){
long start=System.currentTimeMillis();
String srcPath="海豚.png";
String desPath="海豚1.png";
copyFile(srcPath,desPath);
long end = System.currentTimeMillis();
System.out.println("复制所花费的时间是:"+(end-start));
//运行结果为:复制所花费的时间是:265

}

III.Buffer flow(缓冲流)

(1)是什么?

   缓冲流有名增强流,主要对节点流的读写读写速度进行增强。

(2)作用是?

    提高流的读取和写入速率。

(3)分类

      BufferedInputStream 

     BufferedOutputStream

     BufferedWriter

     BufferedReader

为何有如此功能?

Java高级学习篇之IO流_IO流_10

    主要是因为其在内部提供了一个缓存区(8kb),当读取数据时,先将数据读取到该缓冲区,当达到一定数量时,全部输出,依次进行,直到文件内容全部输出。

(3)如何用?

 使用缓冲流将字节流对象包装一下就可以进行使用,读写速度更快。

public void copyFileWithBuffered(String srcPath,String desPath){
BufferedInputStream bis= null;
BufferedOutputStream bos= null;
try {
//(1)造文件
File srcfile=new File(srcPath);
File desfile=new File(desPath);
//(2)造流
// 2.1 造节点流
FileInputStream fis=new FileInputStream(srcfile);
FileOutputStream fos =new FileOutputStream(desfile);
// 2.2 造缓冲流
bis = new BufferedInputStream(fis);
bos = new BufferedOutputStream(fos);

//(3)复制的细节:读取、写入
byte[]buffer =new byte[10];
int len;
while((len=bis.read(buffer))!=-1){
bos.write(buffer,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
//(4)资源关闭(先关闭外层的流,再关闭内层的流)
if(bis!=null) {
bis.close();
}
if(bos!=null) {
bos.close();
}
//说明:关闭外层流的同时,内层流也会自动关闭(无需再手动关闭内层流)
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Test
public void testCopyBuffered(){
long start =System.currentTimeMillis();
String srcPath="海豚.png";
String desPath="海豚3.png";
copyFileWithBuffered(srcPath,desPath);
long end = System.currentTimeMillis();
System.out.println("执行复制所需要的时间是:"+(end-start));
//运行结果是:执行复制所需要的时间是:8
}

对比可得:使用缓冲流对文件进行读写后,其速度提高了数倍。

IV.The conversion stream(转换流)
(1)作用

  实现    字节流<---->字符流    的相互转化

补充:

  ①解码:字节、字节数组   ---->  字符数组、字符串*    

  ②编码:字符数组、字符串  ---->  字节、字节数组

(2)字符集

①  ASCILL:美国标准的信息交换码用一个字节的7位可以表示。

②  ISO8859-1:拉丁码表。欧洲码表用一个字节的8位表示。

③  GB2312:中国的中文编码表。最多两个字节编码所有字符。

④  GBK:中国的中文编码表的升级,融合更多的中文文字字符,最多两个字节编码。

⑤  Unicode:国际标准码,融合了目前人类使用的所有字符。

                  为每个字符分配了唯一的字符码,所有的文字都使用两个字节来表示。

⑥  UTF-8:变长的编码方式,可以使用1-4个字节来表示一个字符。

(3)实现代码
InputStreamReader&OutputStreamWriter测试

 ASCII:美国标准信息交换码。*         用一个字节的7位可以表示。*      ISO8859-1:拉丁码表。欧洲码表*         用一个字节的8位表示。*      GB2312:中国的中文编码表。最多两个字节编码所有字符*      GBK:中国的中文编码表升级,融合了更多的中文文字符号。最多两个字节编码*      Unicode:国际标准码,融合了目前人类使用的所有字符。为每个字符分配唯一的字符码。所有的文字都用两个字节来表示。*      UTF-8:变长的编码方式,可用1-4个字节来表示一个字符。

@Test
public void test(){
InputStreamReader isr= null;
OutputStreamWriter osw= null;
try {
//创建文件类
File file1=new File("hello1.txt");
File file2=new File("hello2.txt");
//创建输入和输出流
FileInputStream fis = new FileInputStream(file1);
FileOutputStream fos= new FileOutputStream(file2);

isr = new InputStreamReader(fis);
osw = new OutputStreamWriter(fos,"GBK");

//读写过程
char[] tr=new char[20];
int len=0;
while((len=isr.read(tr))!=-1){
osw.write(tr,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
isr.close();
osw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//运行结果:Welcome to China Jerry�й�(由于编码格式的不同所产生)
}

IV.RandomAccessFile

(1)介绍

      直接继承于java.lang.Object类,实现了DataInput和DataOutput接口

既可以作为输入流也可以作为输出流。

(2)优点

      可以使用该流对指定位置进行添加数据的操作。

(3)实例代码
@Test
public void test() {
RandomAccessFile raf= null;
RandomAccessFile raf1= null;
try {
raf = new RandomAccessFile(new File("hello1.txt"),"r");
raf1 = new RandomAccessFile(new File("hello9.txt"),"rw");

byte[]buf=new byte[1024];
int len;
while ((len=raf.read(buf))!=-1) {
raf1.write(buf,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (raf != null) {
try {
raf.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (raf1 != null) {
try {
raf1.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
实际应用:

     在指定位置插入数据(不能直接覆盖)

@Test
public void test3() {
RandomAccessFile raf1= null;
try {
raf1 = new RandomAccessFile("hello5.txt","rw");
raf1.seek(3);
//将指针调到角标为3的位置

//保存指针3后面的所有数据到builder中
StringBuffer builder=new StringBuffer((int) new File("hello5.txt").length());
byte[] buffer=new byte[20];
int len;
while((len=raf1.read(buffer))!=-1){
builder.append(new String(buffer,0,len));
}
//调回指针,写入“xyz”
raf1.seek(3);
raf1.write("xyz".getBytes());
//将builder中的内容写入
raf1.write(builder.toString().getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(raf1!=null) {
raf1.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

V.ObjectStream

(1)介绍

       用于存储和读取基本数据类型或对象的处理流,其强大之处在于可以将Java对象写入到数据源中,还可以将对象从数据源中还原回来。

(2)分类

     ObjectInputStream 和 ObjectOutputStream

(3)序列化和反序列化


序列化:用ObjectOutputStream 类保存基本数据类型或对象的机制。

             (数据从内存到文件)

反序列化:用ObjectInputStream类读取基本数据类型或对象的机制。

              (数据从文件到内存)

补充:对象的序列化机制

允许把内存中的Java对象转换成为与平台无关的二进制流,从而允许把这种二进制持久的保存在磁盘上,或通过网络将这种二进制传输到另一个网络节点。

(4)实例代码
@Test
public void test1(){
ObjectOutputStream oos= null;
try {
//创建对象流
oos = new ObjectOutputStream(new FileOutputStream(new File("object.dat")));
//将内容写入文件
oos.writeObject(new String("欢迎来到北京!"));
//刷新
oos.flush();
oos.writeObject(new Person("张学良",43,new Account(5600)));
oos.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
//关闭对象流
if(oos!=null) {
oos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 反序列化:将磁盘文件中的对象还原为内存中的一个Java对象(使用ObjectInputStream)
* */
@Test
public void testObjectInputStream(){
ObjectInputStream ois= null;
try {
ois = new ObjectInputStream(new FileInputStream("object.dat"));
Object obj=ois.readObject();
//强制转化
String str=(String)obj;
Person p2=(Person)ois.readObject();
System.out.println(str);
System.out.println(p2);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
//关闭对象流
if(ois!=null) {
ois.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

如果创建了类,也需要满足序列化机制才可以使用

* Person类 需要满足如下要求,方可序列化
* (1)需要实现接口:Serializable
* (2)当前类提供一个全局常量(为了用于标识类)
* public static final long serialVersionUID = 4244567L;
* 说明:
* 如果类没有显示定义这个静态常量,它的值是Java运行时环境根据类的内部细节自
* 动生成的。若类的实例变量做了修改,serialVersionUID 可能发生变化。故建议,
* 显式声明。
* (3)除了当前Person类需要实现Serializable接口之外,还必须保证其内部所有属性
* 也必须是可序列化的(默认情况下,基本数据类型均是可序列化的)
*
* 补充说明:
* ObjectOutputStream和ObjectInputStream
* 不能序列化static和transient修饰的成员变量
*
*/
public class Person implements Serializable {
public static final long serialVersionUID = 4244567L;
/**
* 成员变量
* name:用来记录人的名字
* age:用来记录人的年龄
* */
private String name;
private int age;
private Account acct;
//构造器
public Person(String name, int age) {
this.name = name;
this.age = age;
}

public Person(String name, int age, Account acct) {
this.name = name;
this.age = age;
this.acct = acct;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", acct=" + acct +
'}';
}
}
class Account implements Serializable{

public static final long serialVersionUID = 424567L;
private double balance;

public Account(int balance) {
this.balance = balance;
}

public double getBalance() {
return balance;
}

public void setBalance(int balance) {
this.balance = balance;
}

@Override
public String toString() {
return "Account{" +
"balance=" + balance +
'}';
}

Ⅵ.OtherStream

(1)介绍

①标准的输入输出流

②打印流

③数据流

/**
* 1.标准的输入和输出流
* 1.1
* System.in:标准的输入流(默认的是从控制台输入)
* System.out:标准的输出流(默认的是从控制台输出)
* 1.2
* System类的setIn(InputStream is)/setOut(PrintStream)方式指定输出和输出流
* 练习:
* 从键盘输入字符串,要求将读取到的整行字符串转成大写输出。然后继续
* 进行输入操作,直至当输入“e”或者“exit”时,退出程序
* 方法一:使用Scanner实现,调用next()方法返回一个字符串
* 方法二:使用System.in实现。System.in---->转换流---->BufferedReader中的readline()
*/
public static void main(String[] args) {
BufferedReader br = null;
try {
InputStreamReader isr = new InputStreamReader(System.in);
br = new BufferedReader(isr);
while (true) {
System.out.println("请输入字符串:");
String data = br.readLine();
if ("e".equalsIgnoreCase(data) || "exit".equalsIgnoreCase(data)) {
System.out.println("程序已经结束!!");
break;
}
String upperCase=data.toUpperCase();
System.out.println(upperCase);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if(br!=null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 2.打印流: PrintStream 和 PrintWriter
* 2.1 提供了一系列重载的print()和println()
* */
@Test
public void testPrintStream(){
PrintStream ps = null;
try {
//创建一个输出流
FileOutputStream fos = new FileOutputStream(new File("D:\\Hello\\hello.txt"));
// 创建打印输出流,设置为自动刷新模式(写入换行符或字节 '\n' 时都会刷新输出缓冲区)
ps = new PrintStream(fos,true);
if (ps != null) {
// 把标准输出流(控制台输出)改成文件
System.setOut(ps);
}
for (int i = 0; i <= 255; i++) {
// 输出ASCII字符
System.out.print((char) i);
if (i % 50 == 0) {
System.out.println(); // 换行
} }
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
if (ps != null) {
ps.close();
} }
}
/**
* 3.数据流
* 3.1 DataInputStream和DataOutputStream
* 3.2 作用: 用于读取或写出基本数据类型的变量或字符串
* 练习:写入并读取数据
*
* 说明:
* ①通过数据流写入数据后(通过DataOutputStream),查看(通过DataInputStream)来进行查看,并不是通过双击文件来进行查看
* ②写入和读出数据的顺序要是一致的,不然会报错EOFException
* */
@Test
public void test() {
DataOutputStream dos = null;
try {
dos = new DataOutputStream(new FileOutputStream("hello3.txt"));
dos.writeUTF("姚明");
dos.writeInt(3);
dos.flush();
dos.writeBoolean(true);
dos.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (dos != null) {
dos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Test
public void test4(){
DataInputStream str= null;
try {
str = new DataInputStream(new FileInputStream("hello3.txt"));
String tr=str.readUTF();
System.out.println(tr);
int a=str.readInt();
System.out.println(a);
Boolean isFale = str.readBoolean();
System.out.println(isFale);
} catch (IOException e) {
e.printStackTrace();
} finally {
if(str!=null) {
try {
str.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}