java io - yaokun123/php-wiki GitHub Wiki

IO流

一、输入流&输出流

输入流和输出流相对于内存设备而言。
将外设中的数据读取到内存中:输入
将内存中的数据写入到外设中:输出

二、字节流&字符流

字符流的由来:
其实就是:字节流读取文字字节数据后,不直接操作而是先查找指定的编码表,获取对应的文字。
再对这个文字进行操作。简单说:字节流+编码表

三、IO常用的基类

字节流的抽象基类:InputStream/OutputStream
字符流的抽象基类:Reader/Write

注:由这四个类派生出来的子类名称都是以其父类名作为子类名的后缀。
如:InputStream的子类FileInputStream
如:Reader的子类FileReader

四、FileWrite

public static void main(String[] args) throws IOException{
    //创建一个可以往文件中写入字符数据的字符输出流对象
    //既然是往一个文件中写入文字数据,那么再创建对象时,就必须明确该文件(用于存储数据的目的地)

    //如果文件不存在,则会自动创建。如果文件存在,则会被覆盖
    FileWrite fw = new FileWrite("demo.txt");

    //调用write对象中的write(string)方法,写入数据。
    //其实数据写到临时存储缓冲区中
    fw.write("abcde");

    //进行刷新,将数据直接写到目的地中
    fw.flusg();

    //关闭流,关闭资源,在关闭前会先调用flush刷新缓冲区中的数据到目的地。
    fw.close();
    
}
  • 换行和续写
//换行
fw.write("abcd\r\nhahah");
private static final String LINE_SEPARATOR = System.getProperty("line.separator");

//续写
//如果构造函数中加入true,可以实现对文件进行续写。
FileWrite fw = new FileWrite("demo.txt",true);
  • 异常处理
public static void main(String[] args){
    FileWrite fw = null;
    
    try{
        fw = new FileWrite("demo.txt",true);
        fw.write("abcde"+LINE_SEPARATOR+"hahaha");
    }catch(IOException e){
        System.out.println(e.toString());
    }finally{
        try{
            fw.close();
        }catch(IOException e){
            throw new RuntimeException("关闭失败");
        }
    }
}

五、FileReader

public static void main(String[] args) throws IOException{
    //创建读取字符数据的流对象
    //在创建读取流对象是,必须要明确被读取的文件。一定要确定该文件是存在的。
    FileReader fr = new FileReader("demo.txt");

    //用reader中的read方法读取字符
    int ch = fr.read();
    System.out.println(ch);
    while(ch=fr.read()!=-1){
        System.out.println(ch);
    }

    fr.close();
}

//使用read(char[])读取文本文件数据
//先创建字符数组
char[] buf = new char[3];
//将读取到的字符存储到数组中
int num = fr.read(buf);
System.out.println(new String(buf));

int len = 0;
while(len=fr.read(buf)!=-1){
    System.out.println(new String(buf,0,len));
}

六、示例

1、将C盘的一个文本文件复制到d盘

第一种方式:
public static void main(String[] args)throws IOException{
    //1、读取一个已有的文本文件,使用字符读取流和文件相关联
    FileReader fr = new FileReader("c:/io.txt");

    //2、创建一个目的,用于存储读到的数据
    FileWrite fw = new FileWrite("d:/io.txt");

    //3、频繁的读写操作
    int ch = 0;
    while((ch = fr.read())!=-1){
        fw.write(ch);
    }

    //4、关闭流资源
    fw.close();
    fr.close();
}

第二种方式:
public static void main(String[] args){
    FileReader fr = null;
    FileWrite fw = null;

    try{
        fr = new FileReader("c:/io.txt");
        fw = new FileWrite("d:/io.txt");

        //创建一个临时容器,用于缓存读到的字符
        char[] buf = new char[1024]

        //定义一个变量记录读取到的字符数(其实就是往数组里装的字符个数)
        int len = 0;
        while(len=fr.read(buf) != -1){
            fw.write(buf,0,len);
        }
    }catch(Exception e){
        
    }finally{
        if(fw!=null){
            try{fw.close()}catch(){}
        }

        if(fr!=null){
            try{fr.close()}catch(){}
        }
    }
}

七、字符流-缓冲区

缓冲区的出现提高了对数据的读写效率。

对应类

BufferedWriter
BufferedReader

缓冲区要结合流才可以使用

在流的基础上对流的功能进行了增强

public static void main(String[] args) throws IOException{
    FileWrite fw = new FileWrite("buf.txt");
    
    //为了提高写入的效率。使用字符流的缓冲区
    //创建了一个字符写入流的缓冲区对象,并和指定要被缓冲的流对象相关联
    BufferedWriter bufw = new BufferedWriter(fw);

    //使用缓冲区的写入方法将数据先写入到缓冲区中
    bufw.write("abcdef");

    //使用缓冲区的刷新方法将数据刷到目的地
    bufw.flush();

    //关闭缓冲区(其实关闭的就是被缓冲的流对象)
    bufw.close();

    //fw.write("abcdef");

    //fw.close();
}

public static void main(String[] args)  throws IOException{
    FileReader fr = new FileReader("buf.txt");

    BufferedReader bufr = new BufferedReader(fr);
 
    String line1 = bufr.readLine();
    System.out.println(line1);

    String line2 = bufr.readLine();
    System.out.println(line2);

    //或
    String line = null;
    while(line=bufr.readLine() !=null){
        System.out.println(line);
    }

    bufr.close();


    //char[] buf = new char[1024];
    //int len = 0;
    //while(len=fr.read(buf) != -1){
    //}
}

八、字符流-缓冲区-装饰设计模式

对一组对象的功能进行增强时,就可以使用该模式进行问题的解决。

public class PersonDemo{
    public static void main(String[] args){
        Person p = new Person();
        p.chifan();

        NewPerson p1 = new NewPerson();
        p1.chifan();

        NewPerson2 p2 = new NewPerson2();
        p2.chifan();
    }
}

class Person{
    void chifan(){
        System.out.println("吃饭");
    }
}

//这个类的出现是为了增强Person而出现的。
class NewPerson{
    private Person p;
    NewPerson(NewPerson p){
        this.p = p;
    }

    public void chifan(){
        System.out.println("开胃菜");
        p.chifan();
        System.out.println("甜点");
    }
}

//继承和覆盖
class NewPerson2 extends Person{
    public void chifan(){
        System.out.println("开胃菜");
        super.chifan();
        System.out.println("甜点");
    }
}

装饰和继承都能实现一样的特点:进行功能的扩展增强

有什么区别呢?

首先有一个继承体系:
Writer
    |--TextWriter:用于操作文本
        |--BUfferTextWriter:加入了缓冲技术的操作文本的对象
    |--MediaWriter:用于操作媒体
        |--BufferMediaWriter:加入了缓冲技术的操作媒体的对象

但是这样做好像并不理想,如果这个体系进行功能扩展,有多个流对象。
那么这个流要提高效率,是不是也要产生子类呢?
是,这时就会发现只为提高功能,进行的继承,导致继承体系越来越臃肿,不够灵活。

重新思考这个问题?
既然加入的都是同一种技术-缓冲。
前一种是让缓冲和具体的对象相结合。
可不可以将缓冲进行单独的封装,哪个对象需要缓冲就将哪个对象和缓冲关联。
class BufferWriter TextWriter{
    BufferWriter(Writer w){
        
    }
}

Writer
    |--TextWriter:用于操作文本
    |--MediaWriter:用于操作媒体
    |--BufferWriter:加入了缓冲技术的操作写的对象

装饰要比继承灵活

特点:装饰类和别装饰类都必须所属同一个接口或者父类。

九、字符流-缓冲区-LineNumberReader

//跟踪行号的缓冲字符输入流 LineNumberReader extends BufferedReader

class LineNumberReaderDemo{
    public static void main(String[] args){
        FileRead fr = new FileRead("test.txt");

        LineNumberReader lnr = new LineNumberReader(fr);

        String line = null;
        lnr.setLineNumber(100)
        while(line=lnr.readLine()!=null){
            System.out.println(lnr.getLineNumber()+":"+line);
        }
        lnr.close();
        fr.close();
    }
}

十、字节流

基本操作于字符流类相同

但它不仅可以操作字符,还可以操作其他媒体文件

public static void demo_write() throws IOException{
    //1、创建字节流输出流对象。用于操作文件
    FileOutputStream fos = new FileOutputStream("bytedemo.txt");

    //2、写数据。直接写入目的地中。不用flush
    fos.write("abcde".getBytes());

    //3、关闭资源
    fos.close();
}

public static void demo_read() throws IOException{
    //1、创建一个读取流对象。和指定文件关联
    FileInputStream fis = new FileInputStream("bytedemo.txt");

    /*byte[] buf = new byte[1024];
    int len = 0;
    while(len=fis.read(buf) != -1){
        System.out.println(new String(buf,0,len));
    }*/

    System.out.println(fis.available());//5
    byte[] buf = new byte[fis.available()]
    fis.read(buf);
    System.out.println(new String(buf));
}

示例:复制mp3

class CopyMp3Test{
    public static void main(String[] args) throws IOException{
        copy_1();//第一种方式
        copy_2();//第二种方式
        copy_3();//第三种方式
        copy_4();//第四种方式
    }

    public static void copy_1() throws IOException{
        FileInputStream fis = new FileInputStream("test.mp3");

        FileOutputStream fos = new FileOutputStream("test1.mp3");

        byte[] buf = new byte[1024];
        int len = 0;
        while(len=fis.read(buf) != -1){
            fos.write(buf,0,len);
        }

        fis.close();
        fos.close();
    }

    public static void copy_2() throws IOException{
        FileInputStream fis = new FileInputStream("test.mp3");
        BufferedInputStream bufis = new BufferedInputStream(fis);

        FileOutputStream fos = new FileOutputStream("test1.mp3");
        BufferedOutputStream bufos = new BufferedOutputStream(fos);

        
        int ch = 0;
        while(ch=bufis.read() != -1){
            bufos.write(ch);
        }

        bufis.close();
        bufos.close();
    }

    //不建议
    public static void copy_3() throws IOException{
        FileInputStream fis = new FileInputStream("test.mp3");

        FileOutputStream fos = new FileOutputStream("test1.mp3");

        byte[] buf = new byte[fis.available()];
        fis.read(buf);
        fos.write(buf);

        fis.close();
        fos.close();
    }

    //千万不要用,效率没有
    public static void copy_3() throws IOException{
        FileInputStream fis = new FileInputStream("test.mp3");

        FileOutputStream fos = new FileOutputStream("test1.mp3");

        int ch = 0;
        while(ch = fis.read() != -1){
            fos.write(ch);
        }

        fis.close();
        fos.close();
    }
}

十一、标准输入、输出

System.in;//标准输入(键盘)

System.out;//标准输出

public static void main(String[] args) throws IOException{
    InputStream in = System.in;//标准输入(键盘)

    int ch = in.read();//阻塞方法

    System.out.println(ch);
}

十二、转换流

//InputStreamReader是字节流通向字符流的桥梁,解码

//OutputStreamWrite是字符流通向的字节流桥梁,编码

public static void main(String[] args) throws IOException{
    //字节流
    InputStream in = System.in;

    //字符流
    //BufferedReader bufr = new BufferedReader(in);//字符流不能操作字节流对象,因此需要转换流

    //InputStreamReader是字节流通向字符流的桥梁
    //OutputStreamWrite是字符流通向的字节流桥梁
    //将字节转换为字符的桥梁,转换流
    InputStreamReader isr = new InputStreamReader(in);

    //字符流
    BufferedReader bufr = new BufferedReader(isr);
    
    String line = null;

    while(line=bufr.readLine()!=null){
        if("over".equals(line)){
            break;
        }
        System.out.println(line.toUpperCase());
    }
}

流的操作规律

之所以要弄清楚这个规律,是因为流对象太多,开发时不知道用哪个对象合适。

想要知道开发时候用到哪个对象。只要通过四个明确即可。

1、明确源和目的
    源:InputStream  Read
    目的:OutputStream  Write

2、明确数据是否是纯文本数据
    源:是纯文本:Reader
      :否:InputStream
    目的:是纯文本:Write
      :否:OutputStream

到这里,就可以明确需求中具体要使用哪个体系。

3、明确集体的设备
    源设备:
        硬盘:File
        键盘:System.in
        内存:数组
        网络:Socket流

    目的设备:
        硬盘:File
        键盘:System.out
        内存:数组
        网络:Socket流

4、是否需要其他额外功能
    是否需要高效(缓冲区)
        是:就加上buffer

十三、转换流的编码解码

OutputStreamWrite是字符流通向字节流的桥梁,可使用指定的charset将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定 否则将接受平台默认的字符集。

public static void writeText() throws IOException{
    //这两句代码的功能是等同的。
    //FileWriter:其实就是转换流指定了本机默认码表的体现。而且这个转换流的子类对象。可以方便操作文本文件。
    //简单说:操作文件的字节流+本机默认的编码表。这是按照默认码表来操作文件的便捷类

    //如果操作文本文件需要明确具体的编码。FileWrite就不行了。必须要用转换流
    OutputStreamWrite osw = new OutputStreamWrite(new FileOutputStream("gbk_1.txt"));
    FileWriter fw = new FileWriter("gbk_1.txt")


    osw.write("你好");
    osw.close();

    //指定编码
    OutputStreamWrite osw = new OutputStreamWrite(new FileOutputStream("utf8.txt","UTF-8"));
}

public static void readText() throws IOException{
    FileReader fr = new FileReader("gbk_1.txt");

    char[] buf = new char[10];
    int len = fr.read(buf);
    String str = new String(buf,0,len);
    System.out.println(str);
    fr.close();

    FileReader fr = new FileReader("utf8.txt");//读utf-8的会乱码
//修改如下:

    InputStreamReader isr = new InputStreamReader(new FileInputStream("utf8.txt"),"UTF-8");
    char[] buf = new char[10];
    int len = isr.read(buf);
    String str = new String(buf,0,len);
    System.out.println(str);
    fr.close();
}

十四、File对象-构造函数&字段

  • 用来将文件或者文件夹封装成对象

  • 方便对文件与文件夹的属性信息进行操作

  • File对象可以作为参数传递给流的构造函数

  • 了解File类中的常用方法

public static void constructorDemo(){
    //可以将一个已存在的,或者不存在的文件或者目录封装成File对象
    File f1 = new File("c:\\a.txt");

    File f2 = new File("c:\\","a.txt");

    File f = new File("c:\\");
    File f3 = new File(f,"a.txt");

    File f4 = new File("c:"+System.getProperty("file.separator")+"abc\\a.txt");//分隔符
    File f5 = new File("c:"+File.separator+"abc"+File.separator+"a.txt");//分隔符
    System.out.println(f4);
    System.out.println(f4);
}