Java基础学习(六)——I/O

1.流的概念

在java中,流(stream)是从源到目的地的字节的有序序列。流中的字节依据先进先出,具有严格顺序,因此流式I/O是一种顺序存取方式。

2.两种基本的流

在java中有两种基本——输入流(InputStream)与输出流(OutputStream),对于这两种流都采用相同的顺序读写方式,其读写操作过程如下:

流的读操作过程,打开流—>当流中还有数据时执行读操作—>关闭流

流的写操作过程,打开流—>当有数据需要输出时执行写操作—>关闭流。

Java中实现上述流式I/O的类都在java.io包中。

这些流根据流相对于程序的另一个端点的不同,分为节点流(Node Stream)和过滤流(Filter Stream)

节点流:以特定流如磁盘文件、内存某区域或线程之间的管道为端点构造的输入/输出流,它是一种最基本的流。

过滤流:以其他已经存在的流为端点构造的输入/输出流,称为过滤流或者处理流,它要对其相连的另一种流进行某种转换。

流根据数据单位不同又分为字节流和字符流:字节流(8位)和字符流(16位)。

3.字节流(其中带阴影的内是节点流,其他为过滤流

1.输入字节流

InputStream是输入字节流的抽象顶层父类。

2.输出字节流

4.字符流

1.输入字符流

2.输出字符流

5.经常使用的流

节点流类型常见的有:

对文件操作的字节流有FileInputStream/FileOutputStream,

字符流有FileReader/FileWriter。

处理流类型常见的有:

缓冲流:缓冲流要“套接”在相应的节点流之上,对读写的数据提供了缓冲的功能,提高了读写效率,同事增加了一些新的方法。

  字节缓冲流有BufferedInputStream/BufferedOutputStream,

字符缓冲流有BufferedReader/BufferedWriter,字符缓冲流分别提供了读取和写入一行的方法ReadLine和NewLine方法。

  对于输出地缓冲流,写出的数据,会先写入到内存中,再使用flush方法将内存中的数据刷到硬盘。所以,在使用字符缓冲流的时候,一定要先flush,然后再close,避免数据丢失。

转换流:用于字节数据到字符数据之间的转换。

  仅有字符流InputStreamReader/OutputStreamWriter。其中,InputStreamReader需要与InputStream“套接”,OutputStreamWriter需要与OutputStream“套接”。

数据流:提供了读写Java中的基本数据类型的功能。

  DataInputStream和DataOutputStream分别继承自InputStream和OutputStream,需要“套接”在InputStream和OutputStream类型的节点流之上。

对象流:用于直接将对象写入写出。

  流类有ObjectInputStream和ObjectOutputStream,本身这两个方法没什么,但是其要写出的对象有要求,该对象必须实现Serializable接口,来声明其是可以序列化的。否则,不能用对象流读写。

6.使用实例

  • 输入字节流:FileInputStream
    --------| InputStream 所有输入字节流的基类 抽象类
    ------------| FileInputStream 读取文件数据的输入字节流

    使用FileInputStream读取文件数据的步骤:
    1. 找到目标文件
    2. 建立数据的输入通道。
    3. 读取文件中的数据。
    4. 关闭 资源.

在使用时经常会使用自定义的缓冲数组以加快读取速度。

 1 public class MyFileInputStream {
 2
 3     public static void main(String[] args) throws IOException {
 4         readTest4();
 5     }
 6
 7     //使用缓冲数组配合循环一起读取。 推荐使用
 8     public static void readTest4() throws IOException{
 9         long startTime = System.currentTimeMillis();
10         //找到目标文件
11         File file = new File("F:\\81179-106.jpg");
12         //建立数据的输入通道
13         FileInputStream fileInputStream = new FileInputStream(file);
14         //建立缓冲数组配合循环读取文件的数据。
15         int length = 0; //保存每次读取到的字节个数。
16          //存储读取到的数据    缓冲数组 的长度一般是1024的倍数,因为与计算机的处理单位。  理论上缓冲数组越大,效率越高
17         byte[] buf = new byte[1024];
18
19         while((length = fileInputStream.read(buf))!=-1){ // read方法如果读取到了文件的末尾,那么会返回-1表示。
20             System.out.print(new String(buf,0,length));
21         }
22
23         //关闭资源
24         fileInputStream.close();
25         long endTime = System.currentTimeMillis();
26         System.out.println("读取的时间是:"+ (endTime-startTime)); //103
27     }
28 }

  • 输出字节流:FileOutStream
    --------| OutputStream 是所有输出字节流 的父类。 抽象类
    -----------| FileOutStream 向文件输出数据的输出字节流。

FileOutputStream要注意的细节:
1. 使用FileOutputStream 的时候,如果目标文件不存在,那么会自动创建目标文件对象。
2. 使用FileOutputStream写数据的时候,如果目标文件已经存在,那么会先清空目标文件中的数据,然后再写入数据。
3.使用FileOutputStream写数据的时候, 如果目标文件已经存在,需要在原来数据基础上追加数据的时候应该使用new FileOutputStream(file,true)构造函数,第二参数为true。
4.使用FileOutputStream的write方法写数据的时候,虽然接收的是一个int类型的数据,但是真正写出的只是一个字节的数据,只是
把低八位的二进制数据写出,其他二十四位数据全部丢弃。

 1 public class MyFileOutStream {
 2
 3     public static void main(String[] args) throws IOException {
 4         writeTest3();
 5     }
 6
 7
 8     //使用字节数组把数据写出。
 9     public static void writeTest3() throws IOException{
10         //找到目标文件
11         File file = new File("F:\\b.txt");
12         //建立数据输出通道
13         FileOutputStream fileOutputStream = new FileOutputStream(file);
14         //把数据写出。
15         String data = "abc";
16         byte[] buf = data.getBytes();
17         fileOutputStream.write(buf, 0, 3); // 0 从字节数组的指定索引值开始写, 2:写出两个字节。
18
19         //关闭资源
20         fileOutputStream.close();
21     }
22 }

  •  输入字符流:FileReader 

    ----------| Reader 输入字符流的基类 抽象类
    -------------| FileReader 读取文件的输入字符流。

 1 public class MyFileReader {
 2
 3     public static void main(String[] args) throws IOException {
 4         readTest2();
 5     }
 6     public static void readTest2() throws IOException{
 7         //找到目标文件
 8         File file = new File("F:\\Demo1.java");
 9         // 建立数据的输入通道
10         @SuppressWarnings("resource")
11         FileReader fileReader = new FileReader(file);
12         //建立缓冲字符数组读取文件数据
13         char[] buf = new char[1024];
14         int length = 0 ;
15         while((length = fileReader.read(buf))!=-1){
16             System.out.print(new String(buf,0,length));
17         }
18         fileReader.close();
19     }
20
21 }

  • 输出字符流:FileWriter

    ------| Writer 输出字符流的基类。 抽象类
    -----------| FileWriter 向文件数据数据的输出字符流

FileWriter要注意的事项:
1. 使用FileWriter写数据的时候,FileWriter内部是维护了一个1024个字符数组的,写数据的时候会先写入到它内部维护的字符数组中,如果需要
把数据真正写到硬盘上,需要调用flush或者是close方法或者是填满了内部的字符数组。
2. 使用FileWriter的时候,如果目标文件不存在,那么会自动创建目标文件。
3.使用FileWriter的时候, 如果目标文件已经存在了,那么默认情况会先情况文件中的数据,然后再写入数据 , 如果需要在原来的基础上追加数据,
需要使用“new FileWriter(File , boolean)”的构造方法,第二参数为true。

FileWriter

  • 缓冲输入字节流:BufferedInputStream

    ----| InputStream 输入字节流的基类。 抽象
    ----------| FileInputStream 读取文件数据的输入字节流
    ----------| BufferedInputStream 缓冲输入字节流 缓冲输入字节流的出现主要是为了提高读取文件数据的效率。
    其实该类内部只不过是维护了一个8kb的字节数组而已。

    注意: 凡是缓冲流都不具备读写文件的能力。

BufferedInputStream

  • 缓冲输出字节流:Bufferedoutputstream

    -------| OutputStream 所有输出字节流的基类 抽象类
    ------------| FileOutputStream 向文件 输出数据 的输出字节流
    ------------| Bufferedoutputstream 缓冲输出字节流 BufferedOutputStream出现的目的是为了提高写数据的效率。
    内部也是维护了一个8kb的字节数组而已。

BufferedOutputStream 要注意的细节
1. 使用BufferedOutStream写数据的时候,它的write方法是是先把数据写到它内部维护的字节数组中。
2. 使用BufferedOutStream写数据的时候,它的write方法是是先把数据写到它内部维护的字节数组中,如果需要把数据真正的写到硬盘上面,需要
调用flush方法或者是close方法、 或者是内部维护的字节数组已经填满数据的时候。

 1 public class MyBufferedOutputStream {
 2
 3     public static void main(String[] args) throws IOException {
 4         //找到目标文件
 5         File file = new File("F:\\a.txt");
 6         //建立数据的输出通道
 7         FileOutputStream  fileOutputStream = new FileOutputStream(file);
 8         //建立缓冲输出字节流对象
 9         BufferedOutputStream bufferedOutputStream  = new BufferedOutputStream(fileOutputStream);
10         //把数据写出
11         bufferedOutputStream.write("hello world".getBytes());
12         //把缓冲数组中内部的数据写到硬盘上面。
13         //bufferedOutputStream.flush();
14         bufferedOutputStream.close();
15     }
16
17 }

BufferedOutputStream

  • 缓冲输入字符流:BufferedReader

    输入字符流:
    -------| Reader 所有输入字符流的基类。 抽象类
    ----------| FileReader 读取文件字符串的输入字符流。
    ----------| BufferedReader 缓冲输入字符流 。 缓冲 输入字符流出现的目的是为了提高读取文件 的效率和拓展了FileReader的功能。 其实该类内部也是维护了一个字符数组

  • 缓冲输出字符流:BufferedWriter

    ----------| Writer 所有输出字符流的基类, 抽象类。
    --------------- | FileWriter 向文件输出字符数据的输出字符流。
    ----------------| BufferedWriter 缓冲输出字符流 缓冲输出字符流作用: 提高FileWriter的写数据效率与拓展FileWriter的功能。
    BufferedWriter内部只不过是提供了一个8k长度的字符数组作为缓冲区而已,拓展了FileWriter的功能。
  • 对象输出流:ObjectOutputStream

对象输入输出流要注意的细节:
1. 如果对象需要被写出到文件上,那么对象所属的类必须要实现Serializable接口。 Serializable接口没有任何的方法,是一个标识接口而已。
2. 对象的反序列化创建对象的时候并不会调用到构造方法的、
3. serialVersionUID 是用于记录class文件的版本信息的,serialVersionUID这个数字是通过一个类的类名、成员、包名、工程名算出的一个数字。
4. 使用ObjectInputStream反序列化的时候,ObjeectInputStream会先读取文件中的serialVersionUID,然后与本地的class文件的serialVersionUID
进行对比,如果这两个id不一致,那么反序列化就失败了。
5. 如果序列化与反序列化的时候可能会修改类的成员,那么最好一开始就给这个类指定一个serialVersionUID,如果一类已经指定的serialVersionUID,然后
在序列化与反序列化的时候,jvm都不会再自己算这个 class的serialVersionUID了。
6. 如果一个对象某个数据不想被序列化到硬盘上,可以使用关键字transient修饰。
7. 如果一个类维护了另外一个类的引用,那么另外一个类也需要实现Serializable接口。

 1 class Address implements Serializable{
 2
 3     private static final long serialVersionUID = 1L;
 4
 5     String country;
 6
 7     String city;
 8
 9     public Address(String country,String city){
10         this.country = country;
11         this.city = city;
12     }
13
14 }
15
16 class User implements Serializable{
17
18     private static final long serialVersionUID = 1L;
19
20     String userName ;
21
22     String password;
23
24     transient int age;  // transient 透明
25
26     Address address ;
27
28
29     public User(String userName , String passwrod) {
30         this.userName = userName;
31         this.password = passwrod;
32     }
33
34
35     public User(String userName , String passwrod,int age,Address address) {
36         this.userName = userName;
37         this.password = passwrod;
38         this.age = age;
39         this.address = address;
40     }
41
42     @Override
43     public String toString() {
44         return "用户名:"+this.userName+ " 密码:"+ this.password+" 年龄:"+this.age+" 地址:"+this.address.city;
45     }
46 }
47 public class ObjectOutputStream1 {
48
49     public static void main(String[] args) throws IOException, Exception {
50         writeObj();
51 //        readObj();
52     }
53
54     //把文件中的对象信息读取出来-------->对象的反序列化
55     public static void readObj() throws  IOException, ClassNotFoundException{
56         //找到目标文件
57         File file = new File("F:\\obj.txt");
58         //建立数据的输入通道
59         FileInputStream fileInputStream = new FileInputStream(file);
60         //建立对象的输入流对象
61         ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
62         //读取对象信息
63         User user = (User) objectInputStream.readObject(); //创建对象肯定要依赖对象所属 的class文件。
64         System.out.println("对象的信息:"+ user);
65     }
66     //定义方法把对象的信息写到硬盘上------>对象的序列化。
67     public static void writeObj() throws IOException{
68         //把user对象的信息持久化存储。
69         Address address = new Address("中国","广州");
70         User user = new User("admin","123",15,address);
71         //找到目标文件
72         File file = new File("F:\\obj.txt");
73         //建立数据输出流对象
74         FileOutputStream fileOutputStream = new FileOutputStream(file);
75         //建立对象的输出流对象
76         ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
77         //把对象写出
78         objectOutputStream.writeObject(user);
79         //关闭资源
80         objectOutputStream.close();
81
82
83     }
84 }

  • 序列流:SequenceInputStream

 1 /*
 2
 3  需求: 把一首mp3先切割成n份,然后再把这些文件合并起来。
 4
 5  */
 6 public class SequenceInputStream2 {
 7
 8     public static void main(String[] args) throws IOException {
 9 //        cutFile();
10         mergeFlile();
11     }
12
13     //合并
14     public static void mergeFlile() throws IOException{
15         //找到目标文件
16         File dir = new File("F:\\music");
17         //通过目标文件夹找到所有的MP3文件,然后把所有的MP3文件添加到vector中。
18         Vector<FileInputStream> vector = new Vector<FileInputStream>();
19         File[] files = dir.listFiles();
20         for(File file : files){
21             if(file.getName().endsWith(".mp3")){
22                 vector.add(new FileInputStream(file));
23             }
24         }
25         //通过Vector获取迭代器
26         Enumeration<FileInputStream> e = vector.elements();
27         //创建序列流
28         SequenceInputStream inputStream = new SequenceInputStream(e);
29         //建立文件的输出通道
30         FileOutputStream fileOutputStream = new FileOutputStream("F:\\合并.mp3");
31         //建立缓冲数组读取文件
32         byte[] buf = new byte[1024];
33         int length = 0 ;
34         while((length =  inputStream.read(buf))!=-1){
35             fileOutputStream.write(buf,0,length);
36         }
37         //关闭资源
38         fileOutputStream.close();
39         inputStream.close();
40
41     }
42
43     //切割MP3
44     public static void cutFile() throws IOException{
45         File file = new File("F:\\美女\\1.mp3");
46         //目标文件夹
47         File dir = new File("F:\\music");
48         //建立数据的输入通道
49         FileInputStream fileInputStream = new FileInputStream(file);
50         //建立缓冲数组读取
51         byte[] buf = new byte[1024*1024];
52         int length = 0;
53         for(int i = 0 ;  (length = fileInputStream.read(buf))!=-1 ; i++){
54             FileOutputStream fileOutputStream =    new FileOutputStream(new File(dir,"part"+i+".mp3"));
55             fileOutputStream.write(buf,0,length);
56             fileOutputStream.close();
57         }
58         //关闭资源
59         fileInputStream.close();
60     }
61
62 }

时间: 2024-08-27 04:50:53

Java基础学习(六)——I/O的相关文章

Java基础学习(六)&mdash;List

一.List 1.List集合特有功能 /* * List集合的特有功能: * A:添加功能 * void add(int index,Object element):在指定位置添加元素 * B:获取功能 * Object get(int index):获取指定位置的元素 * C:列表迭代器 * ListIterator listIterator():List集合特有的迭代器 * D:删除功能 * Object remove(int index):根据索引删除元素,返回被删除的元素 * E:修改

java基础学习总结——GUI编程(二)

永不放弃,一切皆有可能!!! 只为成功找方法,不为失败找借口! java基础学习总结——GUI编程(二) 一.事件监听 测试代码一: 1 package cn.javastudy.summary; 2 3 import java.awt.*; 4 import java.awt.event.*; 5 6 public class TestActionEvent { 7 public static void main(String args[]) { 8 Frame f = new Frame("

java基础学习总结——网络编程

永不放弃,一切皆有可能!!! 只为成功找方法,不为失败找借口! java基础学习总结——网络编程 一.网络基础概念 首先理清一个概念:网络编程 != 网站编程,网络编程现在一般称为TCP/IP编程. 二.网络通信协议及接口 三.通信协议分层思想 四.参考模型 五.IP协议 每个人的电脑都有一个独一无二的IP地址,这样互相通信时就不会传错信息了. IP地址是用一个点来分成四段的,在计算机内部IP地址是用四个字节来表示的,一个字节代表一段,每一个字节代表的数最大只能到达255. 六.TCP协议和UD

java基础学习总结——流

永不放弃,一切皆有可能!!! 只为成功找方法,不为失败找借口! java基础学习总结——流 一.JAVA流式输入/输出原理 流是用来读写数据的,java有一个类叫File,它封装的是文件的文件名,只是内存里面的一个对象,真正的文件是在硬盘上的一块空间,在这个文件里面存放着各种各样的数据,我们想读文件里面的数据怎么办呢?是通过一个流的方式来读,咱们要想从程序读数据,对于计算机来说,无论读什么类型的数据都是以010101101010这样的形式读取的.怎么把文件里面的数据读出来呢?你可以把文件想象成一

JAVA基础学习流程

JAVA基础学习: 第一步:学习JAVA的开发环境配置.开发第一个Java程序.也建议大家开始使用eclipse等IDE,不必纠结是不是一定要从记事本开始. 第二步:学习数据类型.运算符.变量.这是编程的基础,是程序的“砖块”.这些内容大多数编程语言都有,而且非常类似. 第三步:学习控制语句.这是编程的基础,是程序的“混凝土”.有了控制语句+变量,理论上你就可以写任意的程序了.因此,这是进入程序的门槛,需要大量的练习. 第四步:学习面向对象基础.通过类.对象.包等基本概念讲解.学习的时候,一定要

Java基础学习——数组初识(1)

Java基础学习--数组初识(1) 1什么是数组 Java中常见的一种数据结构就是数组,数组可以分为一维数组.二维数组和多维数组. 数组是由一组相同的变量组成的数据类型,数组中每个元素具有相同的数据类型,数组中的每个元素都可以用一个统一的数组名和下标来确定. 2 数组的使用 数组的一般使用步骤: 声明数组 分配内存给该数组 下面是一维数组为例: 数据类型  数组名 []: 数组名 = new 数据类型 [数据个数]: 2.1一维数组的声明与赋值 1.数组的声明 int  num [];    

JAVA基础学习笔记(2)

看了几天的视频了,都没时间来写下学习笔记,今天来写下第二次的学习笔记,前几天看的给忘记了,就写最新看到的吧 主要内容:1.类的变量与函数(方法) 2.对象的存储方式 3.新建一个对象及对象的赋值与调用 4.空对象 5.匿名对象 1.类的变量与函数(方法) class Dog      //类名 { String name;  //变量的声明 int age; String color; void bark()   //方法的定义(返回值为空,不带参数) { System.out.println(

Java基础学习--抽象类与抽象函数

Java基础学习--抽象类与抽象函数 abstract class 抽象类不能制造对象,但是可以定义变量,赋给这个变量的一定是他非抽象子类的对象: 抽象类中的抽象函数没有函数体,例如:public abstract void move(); 一个抽象类可以没有任何抽象方法,所有的方法都有方法体,但是整个类是抽象的. 抽象类中所有的的抽象函数必需子类的覆盖,而非抽象函数不需要覆盖.因为子类会继承父类的函数,如果不去覆盖继承来的抽象函数,那么子类就含有抽象函数,含有抽象函数的类必须要声明为抽象类.

JAVA基础学习笔记(1)

今天第一天开始学JAVA,时间:2014年6月17日 学习内容:1.java环境的架设 2.JAVA基本数据类型 1.JAVA环境的架设       1.要先去下载JDK,下载地址 2.安装完成后,设置环境变量 1.1环境变量的设置        1.右键-我的电脑-属性-高级-环境变量-系统变量,找到PATH,在里面加入jdk里bin目录的地址 如:c:\java\bin; 2.新建-名为classpath,值为. 1.2测试JAVA是否配置正确        1.在cmd里面输入javac.

Java基础学习总结——Java对象的序列化和反序列化

一.序列化和反序列化的概念 把对象转换为字节序列的过程称为对象的序列化. 把字节序列恢复为对象的过程称为对象的反序列化. 对象的序列化主要有两种用途: 1) 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中: 2) 在网络上传送对象的字节序列. 在很多应用中,需要对某些对象进行序列化,让它们离开内存空间,入住物理硬盘,以便长期保存.比如最常见的是Web服务器中的Session对象,当有 10万用户并发访问,就有可能出现10万个Session对象,内存可能吃不消,于是Web容器就会把一些s