Java中的IO流

        之前每次系统的学习或者总结一个知识点的时候,总是喜欢整理在记事本中,放到桌面最后被删掉,或者是放到某一个文件夹里面积灰,与其这样,那还不如放到,哪管有一点能帮助到你,也不枉费我浪费的时间.废话不多说,下面我会以一个小白的理解,通俗易懂的整理给大家,如果有不对的地方还请私信我,或者评论,以免误导别人,谢谢;


Java IO(Input,Output) IO是我们的程序与外界交换数据的方式;
Java提供一种能统一的标准方式与外界交流数据;
Java将流按照功能划分为读和写,并用不同的方式来表示,其中输入流用于读取数据,输出流用于写出数据;
Java将流分为两大类:节点流和处理流;
节点流:也称为低级流,是实际链接程序与数据源的"管道",负责实际搬运数据,读写一定是建立在节点流的基础上的;
处理流:也称为高级流,不能独立存在,必须链接在其他的流上,目的是当数据流经当前流时对这些数据做某些处理,这样可以简化我们对数据的操作;

       实际应用中,我们是链接若干高级流,并最终链接低级流,通过低级流读写数据,通过高级流对读写的数据进行某些加工处理,完成一个复杂的读写操作,这个过程称为"流链接",这些是我们学习IO的精髓所在;

文件流:文件流是一对低级流,用于读写文件,就功能而言,它们与RandomAccessFile一致,至于的层的读写方式有本质的区别.
RAF是基于指针进行随机读写的,可以任意读写文件指定位置的数据,可以做到对文件部分数据的编写操作;
流是顺序读写方式,所以不能做到任意读写指定位置的数据,对此我们无法做到对数据进行编辑的操作,但是平日和高级流,可以更轻松的读取数据;

使用文件流向文件中写出字节:FileOutputStream的常用构造方法:
FileOutputStream(String path)或者FileOutputStream(File file)
以上两种方式创建时,默认为覆盖写操作,即:若创建时发现该文件已存在,会先将文件所有数据清除,
然后通过当前流写出的内容作为文件的数据.使用FileOutputStream(String path,boolean append)或者FileOutputStream(File file,boolean append)方式创建时为:追加写模式。
即:若指定的文件存在,那么数据会保留,通过流写出的数据会被追加到文件的最后;

文件输出流进行写数据示例:
FileOutputStream fos = new FileOutputStream("fos.txt");
String line = "我们一起学猫叫,一起喵喵喵喵!";
byte[] data = line.getBytes("UTF-8");
fos.write(data);
System.out.println("写出完毕");
fos.close();
文件输入流进行读文件示例:
//第一种是只读取一定长度的byte数组的字节
FileInputStream fis = new FileInputStream("fos.txt");
byte data[] = new byte[200];
int len = fis.read(data);
System.out.println("实际上读取了"+len+"个字节");
String line = new String(data,0,len,"utf-8");
System.out.println(line);
fis.close();
//第二种是循环读取,直到读取完为止:
int d = -1;
while((d=fis.read())!=-1){
        char c = (char)d;
        System.out.println(c);
}
isr.close();

下面我们来说另一对低级流:字符流
顾名思义,字节流就是按字节进行读写,字符流就是按照字符来进行读写,想想就知道谁快谁慢了;

字符流:Java将流按照读写单位又进行了一种划分方式:字节流与字符流
字节流的读写单位是字节,面字符流的读写单位是字符,所以字符流只适合读文本数据!
java.io.Reader,java.io.Writer这两个类也是抽象类,是所有字符输入流与字符输出流的父类,规定了读写字符的相关的方法.

你们可能会想字符流这么好那谁还会用字节流呢?当时我也是这么想的,但是后来才发现,这是因为有一些时候系统给你提供的只有字节流,比如说System.in标准输入流。就是字节流。你想从他那里得到用户在键盘上的输入,只能是以转换流将它转换为Reader以方便自己的程序读取输入。再比如说Socket里的getInputStream()很明显只给你提供字节流,你要不行直接用,就得给他套个InputStreamReader()用来读取网络传输来的字符,所以说你要想使用字符流还要用字节流来转换,那么久会用到另一对流:转换流;

转换流:
java.io.InputStreamReader和java.io.OutputStreamWriter
他们是一对常用的字符流实现类,经常在我们做字符数据读写操作中使用,并且在流链接中是非常重要的一个环节,但是我们很少直接对它做操作;

下面是字节流通过转换流转换以后进行的写操作:
FileOutputStream fis = new FileOutputStream("osw.txt");
OutputStreamWriter osw = new OutputStreamWriter(fis,"UTF-8");
String line = "摩擦摩擦,在管光滑的地上摩擦!";
osw.write(line);
line="我的滑板鞋,时尚时尚最时尚!";
osw.write(line);
System.out.println("写出完毕!");
osw.close();

字符流的方法:int read()
该方法是一次读取一个字符,实际读取的字节量要根据指定的字符集决定,但是读取到该字符后在java中都是以一个char形式保存(unicode)占两个字节.
下面是字节流通过转换流转换以后进行的读操作:
FileInputStream fis = new FileInputStream("osw.txt");
InputStreamReader isr = new InputStreamReader(fis,"UTF-8");
/*
*int d = -1;    //一个字节一个字节读取,循环读到最后一个位置
* while((d=isr.read())!=-1){    //当读到-1时表示什么没有读到即文件读写完毕
* char c = (char)d;
* System.out.println(c);
* }
* isr.close();
char[] data = new char[200];
int len = isr.read(data);
String str = new String(data,0,len);
System.out.println(str);
isr.close();

说完字符流和转换流,下面这一对流是我自己最喜欢的一对流了:缓冲流
缓冲流是一对处理流(高级流),他是在字符流的基础上用来提升读写效率的,链接它们以后,无论我们进行随机读写还是块读写,当经过缓冲流时都会被转换为块读写操作.

1:缓冲字符输入流:java.io.BufferedReader    特点:可以按行读取字符串
缓冲字符输入流示例:
//将当前源代码输出到控制台
FileInputStream fis = new FileInputStream("/src/io/BrDemo");
InputStreamReader isr = new InputStreamReader(fis);
BufferedReader br = new BufferedReader(isr);
/**
* String readLine()
* 读取一行字符串
* 顺序读取若干字符,当读到了换行符时停止,并将换行符之前的字符组成一个字符串返回, 
* 返回的字符串中是不含有最后的换行符的,若返回值为null,说明流读到了末尾.
*/
String line = null;
while((line = br.readLine())!=null){
        System.out.println(line);
}
br.close();
2:缓冲字符输出流:  缓冲输出流的缓冲区的问题(这里面的一个坑)
FileOutputStream fos = new FileOutputStream("box.txt");
BufferedOutputStream bos = new BufferedOutputStream(fos);
String line = "你说隔壁的泰山,抓住爱情的藤蔓!";
byte[] data = line.getBytes("UTF-8");
/**
* 缓冲流的write方法并不是立即将数据写出,而是先将数据存入其内部的数组中,
* 当数组装满时才会做一次真实的写操作.(转化为块写操作的过程).
*/
bos.write(data);
/**
* flush方法的意义是强制将缓冲流已经缓冲的数据一次性的写出,这样做可以让写出的数据有即时性,
* 但是频繁调用会降低写效率,在更关注写出即时性时应当使用该方法.
*/

bos.flush();
System.out.println("写出完毕!");
/ /close方法中会调用一次flush()方法
bos.close();

以上两种都是通过字节流转换为字符流然后在加上缓冲流实现的,而其实还有一种Java封装好的具有自动换行刷新的缓冲字符输出流,它是实际开发中比较常用的字符高级流,
java.io.PrintWriter特点:可按行写出字符串;
1:PW提供了专门针对写文件的构造方法:PrintWriter(String path)和PrintWriter(File file)
他可以直接使用,并不是因为他不用建立在低级流的基础上,而是已经被封装好了直接用就可以了
下面看一下示例:
PrintWriter pw = new PrintWriter("pw.txt","UTF-8");
pw.println("像一颗海草海草海草海草~");
pw.print("随风飘摇~");
System.out.println("写出完毕!");
pw.close();
2:也可以在任意链接中使用PW
FileOutputStream fos = new FileOutputStream("pw.txt");
//字符集给转换流
OutputStreamWriter osw = new OutputStreamWriter(fos,"UTF-8");
BufferedWriter bw = new BufferedWriter(osw);
PrintWriter pw = new PrintWriter(bw);
pw.println("像一颗海草海草~");
pw.print("浪花里舞蹈~");
System.out.println("写出完毕!");
pw.close();

接下来的这对流也是实际开发中比较常用到的高级流:对象流
对象流:提供的功能不说也知道,就是用来读写Java中的任何对象;
1:对象输出流:java.io.ObjectOutputStream
它可以将给定的java对象转换为一组字节,然后通过其链接的流将这些字节写出.
通过对象流写出对象的方法经历了两个步骤:
1)对象流先将给定的对象转换为一组字节,这组字节包含对象本身保存的数据信息,还包含该对象的结构信息,然后将这字字节通过其连接的流写出,以上操作也被称为对象的序列化;
2)经过文件流时,文件流将这些字节写入到文件中,将数据写入磁盘做长久保存的过程也被称为数据的持久化;
下面的示例:
Person p = new Person();
p.setName("苍老师");
p.setAge(18);
p.setGender("女");
String [] otherInfo ={"是一名演员","促进中日文化交流"};
p.setOtherInfo(otherInfo);
System.out.println(p);
FileOutputStream fos = new FileOutputStream("person.obj");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(p);
System.out.println("写出完毕");
oos.close();
2:对象输入流:可以进行对象的反序列化操作
使用对象流读取的字节必须是通过对象输出流序列化的一组字节才可以;
示例:
FileInputStream fis = new FileInputStream("person.obj");
ObjectInputStream ois = new ObjectInputStream(fis);
Person p = (Person)ois.readObject();
System.out.println(p);
ois.close();
下面来说一下被序列化的对象对应的类的一些相关问题:
1)当一个类的实例希望可以被对象流进行读写,那么该类必须实现:java.io.Serializable接口,于此同时,当前类中所有引用类型的属性,它们对应的类必须实现该接口;
2)当一个类实现了Serializable接口后,要求应当定义一个常量,SerialVersionUID,即序列化版本号.
序列化版本号影响反序列化是否成功,当对象输入流在进行对象反序列化时会检查该对象与当前类的版本号是否一致,不一致则反序列化时会抛出异常导致反序列化失败,一致则可以进行反序列化,原则是对应的属性还原;
如果我们不定义该版本号,编译器会在编译当前类时根据结构生成一个版本号,但是一旦当前类发生改变,那么版本号一定发生改变,这样以前的对象类是不可以反序列化了;
其中有一个关键字:transient被它修饰的属性在对象序列化时会被忽略,忽略不必要的属性可以达到对象瘦身的作用;
实体列示例:
public class Person implements Serializable{
    private static final long serialVersionUID = -6751093704284833019L;
    private String name;
    private int age;
    private String gender;
    private transient String[] otherInfo;
    下面生成get set方法和重写toString方法;
}

最后来测试一个字节流块读写和加上缓冲流以后的读写效率:
(你们可以自己多往文件中放些数据,我这里数据太少,结果就不写了)
1:字符流块读写:
FileInputStream fis = new FileInputStream("fos.txt");
FileOutputStream fos = new FileOutputStream("fos_cp.txt");
long start = System.currentTimeMillis();
int len = -1;
byte[] data = new byte[1024*10];
while((len=fis.read(data))!=-1){
        fos.write(data,0,len);
}
long end = System.currentTimeMillis();
System.out.println("复制完成,耗时:"+(end-start)+"ms");
fis.close();
fos.close();

2:使用缓冲流:
FileInputStream fis = new FileInputStream("fos.txt");
BufferedInputStream bis = new BufferedInputStream(fis);
FileOutputStream fos = new FileOutputStream("fos_cp.txt");
BufferedOutputStream bos = new BufferedOutputStream(fos);
int d = -1;
long start = System.currentTimeMillis();
while((d=bis.read())!=-1){
        bos.write(d);
}
long end = System.currentTimeMillis();
System.out.println("复制完成,耗时:"+(end-start)+"ms");
bis.close();
bos.close();


以上就是Java中IO流的大部分内容了,都是自己手打的,可能有打错的地方,多担待!

你可能感兴趣的:(Java中的IO流)