java基础——IO流

IO流

概念

数据最终持久化到硬盘上,体现就是文件,想要对数据进行持久化操作,这时就要用到IO技术

IO流用来处理设备之间的数据传输
Java对数据的操作是通过流(系统资源)的方式
Java用于操作流的对象都在java.io包中

对文件进行了解
文件有名称,大小,创建时间等属性,既然有这么多信息,最好将其封装成对象,操作更容易,找对象,通过API文档搜索file,找到file类

String pathName = "e:\\java_code\\day22e\\hello.java";
        File f1 = new File(pathName);//将文件封装成File对象。注意;有可以封装不存在文件或者文件夹,变成对象。

递归

递归:其实就是功能的重复调用,但是每次该功能被调用的参数都变化(使用了上一次运算的结果)
特点:
1、函数自身调用自身。
2、一定要定义条件,否则容易发生栈溢出错误 Stack OverFlowError
3、注意:递归次数过多容易溢出

public static int getSum(int num){
        if(num == 1){
            return 1;
        }
        return num + getSum(num - 1);
    }

复制文件

原理:读取一个已有的数据,并将这些读到的数据写入另一个文件中

       //1,明确源和目的。
        File srcFile = new File("E:\\1.mp3");
        File destFile = new File("E:\\copy_2.mp3");

        //2,明确字节流 输入流和源相关联,输出流和目的关联。
        FileInputStream fis = new FileInputStream(srcFile);
        FileOutputStream fos = new FileOutputStream(destFile);

        //3, 使用输入流的读取方法读取字节,并将字节写入到目的中。
        int ch = 0;
        while((ch=fis.read())!=-1){
            fos.write(ch);
        }


        //4,关闭资源。
        fos.close();
        fis.close();

- IO流的分类

流按操作数据分为两种:字节流与字符流。
流按流向分为:输入流(读),输出流(写)。

字节输入流 available();

byte [] buf=new byte[fis.avaiable()];//定义一个刚刚好的数组,注意,文件过大,容易溢出,建议缓冲区的长度最好还是1024的整数倍

flush()和close()的区别

flush():将流中的缓冲区缓冲的数据刷新到目的地中,刷新后,流 还可以继续使用
close():关闭资源,但在关闭之前会将缓冲区中的数据先刷新到目的地,否则丢失数据,然后再关闭流,流不可以使用

建议:如果写入的数据很多,一定要一边写一边刷新,最后一次可以不刷新,由close完成刷新并关闭

/*
        继承关系是这样的。
        OutputStreamWriter:
            |--FileWriter:

        InputStreamReader:
            |--FileReader;

        父类和子类的功能有什么区别呢?

        OutputStreamWriter和InputStreamReader是字符和字节的桥梁:也可以称之为字符转换流。
        字符转换流原理:字节流+编码表。

        FileWriter和FileReader:作为子类,仅作为操作字符文件的便捷类存在。
        当操作的字符文件,使用的是默认编码表时可以不用父类,而直接用子类就完成操作了,简化了代码。

        InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"));//默认字符集。
        InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"),"GBK");//指定GBK字符集。
        FileReader fr = new FileReader("a.txt");
        这三句代码的功能是一样的,其中第三句最为便捷。

        注意:一旦要指定其他编码时,绝对不能用子类,必须使用字符转换流。
        什么时候用子类呢?
        条件:
        1,操作的是文件。
        2,使用默认编码。

        字节--->字符 : 看不懂的--->看的懂的。  需要读。输入流。 InputStreamReader
        字符--->字节 : 看的懂的--->看不懂的。  需要写。输出流。 OutputStreamWriter


        */

什么时候用子类呢?

条件:1、操作的是文件2、使用默认编码

自定义一个字符流缓冲区。 用于缓冲字符数据,从而提高操作效率。
* 并提供了更多操作缓冲区数据的方法。 需要使用具体的流对象来完成数据的获取。
*
* 分析: 缓冲区应该具备什么? 1,必须要有数组。 2,需要对数组进行操作,对数组操作一定要有角标。

字符流中是否有提供缓冲区?
注意:其实自定义数组就可以解决缓冲区文体,并提高效率,那为什么还要使用流中的缓冲区对象呢?因为缓冲区对象中除了封装数组外,还提供了更多的操作缓冲区数据的方法
bufferedReader Bufferedwriter

读取一个字节先不要操作,将其存储起来,转成一个字符串,能不能一次就读一行字符串呢?readerline();
可是readerline是BufferedReader中的方法,BufferedReader使用时必须接收字符流对象
字符流——桥梁InputStreamReader——字符流

字符流缓冲区中特有的方法:
操作字符数据时,有一个文本特有的表现形式:行
操作行的方法:
BufferedReader:readerline();一次读一行
Bufferedwriter

需求:将键盘录入的数据存储到文件中。
* 1,键盘录入。
* 2,目的是文件。
* 3,这个示例中既要用到输入流,也要用到输出流。
* 而且操作的数据都是文本数据,可以使用字符流。
* 而且目的是文件可以使用操作文件的字符输出流。

//读取键盘录入的字节输入流。
        // InputStream in = System.in;
        // //通过桥梁,将字节输入流转成字符输入流。
        // InputStreamReader isr = new InputStreamReader(in);
        // //对字符流进行效率提高,而且使用缓冲区对象的特有方法readLine();
        // BufferedReader bufr = new BufferedReader(isr);

        //记住:以后但凡提到了键盘录入就写这句,一行一行的读取,除非要对读取每一个字节操作。
        BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));

练习:

// 需求:合并碎片文件。
        /* * 思路: 1,碎片文件有很多,每一个碎片都需要和读取流关联。 2,每一个读取流读取到的数据都需要通过一个输出流写入到一个文件中。 * 3,原理:多个源--->一个目的地。 * * * 如下代码的问题: 碎片过多,会产生很多的输入流对象,这是正常的,不正常在于,面对每一个输入流对象去操作。 * 当流对象过多时,必须先存储起来。面的流对象的容器操作更容易。 1,需要容器。 2,将流对象和碎片文件关联后存储到容器中。 * 3,遍历容器获取其中的流对象,在进行频繁的读写操作。 4,即使关流也是遍历容器对每一个流对象进行close的调用。 * 参阅MergeFileTest2.java */
public static void main(String[] args) throws IOException {
        List<FileInputStream> list = new ArrayList<FileInputStream>();

        for (int i = 1; i < 7; i++) {
            list.add(new FileInputStream("E:\\PartFiles\\" + i + ".part"));
        }

        FileOutputStream fos = new FileOutputStream("E:\\PartFiles\\00.mp3");

        byte[] buf = new byte[1024 * 1024];
        // 遍历集合,获取流对象。
        for (FileInputStream fis : list) {

            int len = fis.read(buf);
            fos.write(buf, 0, len);
        }

        fos.close();
        // 关闭所有流对象。
        for (FileInputStream fis : list) {

            fis.close();
        }
    }

}
/* class MyMergeStream{ MyMergeStream(List<? extends InputStream> list){ } public void close(){ for (InputStream fis : list) { fis.close(); } } }
/* * 练习:定义功能记录程序运行次数,满足试用次数后,给出提示:试用次数已到,请注册。 * * 思路: * 1,需要计数器。这个软件使用一次计数一次。每使用一次,就进行计数累计。 * 2,计数器是程序中的一个变量,程序启动计数器计数,可是程序结束这个计数器就消失了。 * 下次启动会重新进行计数,原来计数的值没有保留下来。咋办? * 3,让这个计数器持久化。存储到文件中,为了标识数据可读性,数据起个名字。出现键值对。 * 而且还是一个持久化的键值对,Properties集合正好符合这个要求。 * */
public static void main(String[] args) throws IOException {


        if(isStop()){
            System.out.println("试用次数已到,请注册");
            return;
        }
        runcode();


    }

    private static boolean isStop() throws IOException {

        //1,配置文件。
        File configFile = new File("tempfile\\app.properties");
        if(!configFile.exists()){//如果配置文件不存在,就创建。
            configFile.createNewFile();
        }

        //2,创建属性集。
        Properties prop = new Properties();

        //3,定义读取流和配置文件关联。
        FileInputStream fis = new FileInputStream(configFile);

        //4,将流关联的数据读取到属性集中。
        prop.load(fis);

        //5,通过属性集的指定键count,获取具体的次数。
        String value = prop.getProperty("count");
        int count = 0;
        //6, 对value进行判断,如果存在就对其自增。
        if(value!=null){
            count = Integer.parseInt(value);
            if(count>=5){
                return true;
            }

        }
        count++;//对其值进行自增。
        //7,将自增后的值和指定的键重新存储到属性集中,键相同,值覆盖。
        prop.setProperty("count", Integer.toString(count));

        //8,将属性集存储到配置文件中。对配置文件中的信息进行更新。
        FileOutputStream fos = new FileOutputStream(configFile);
        prop.store(fos, "app run count");

        //9,关闭资源。
        fos.close();
        fis.close();



        return false;
    }

    //程序主体。
    public static void runcode(){
        System.out.println("程序运行....play");
    }
}

Serializable

标记接口 ,用于启动类的序列化功能

序列化接口的作用:没有方法,不需覆盖,是一个标记接口,为了启动一个序列化 功能
唯一作用:给每个需要序列化的类都分配一个序列版本号
这个版本号与这个类相关联的
这个版本号有什么用呢?
在序列化时,会将这个序列号也一同保持到文件中
在反序列化时,会读取到这个序列号和本类序列号进行匹配,如不匹配会抛出异常

静态数据是不会被序列化的
对于一个非静态的数据也不想序列化怎么办?
需要一个关键字修饰,
transent 瞬态关键字

字符流和字节流已经掌握了很多对象
1、IO流的基础流对象,直接调用底层资源,操作数据的对象,File开头的流对象
2、根据IO流的学习规律吗,后面出现的流对象无非是增加额外的功能

需求:
写一个整数到文件中,可以通过将整数转成字符串,变成字节数组写入目的地,字节流的write方法只能将一个整数的最低字节写入到目的地

ps.write(97);//只能写入最低字节
ps.print(97);//将数据转成字符串再写入,保证数值的表现形式

简化方式,之前学习输出语句发现要输出的内容都原样不变的体现出来,输出语句对应的对象是PrintStream

对象提供了很多打印的方法,打印方法的好处在于保证数据值得表现形式
Printwrinter:字符打印流

———————————————————————————————————
小结:

IO流中的对象很多,解决问题(处理设备上的数据时),到底该用哪个对象呢?
把IO流的规律进行总结:四个明确

明确一:要操作的数据是数据源还是数据目的
源:InputStream Reader
目的:OutputStream Writer

先根据需求明确是要读还是要写

明确二:要操作的设备上的数据时字节还是文本

源:
字节:InputStream
文本: Reader

目的:
字节:OutputStream
文本: Writer:

已经明确到了具体的体系上

明确三:明确数据所在的具体设备
源设备:
硬盘:文件以File开头
内存:数组、字符串
键盘:System.in
网络:Socket

目的设备:

硬盘:文件以File开头
内存:数组、字符串
屏幕:System.out
网络:Socket

完全可以明确具体要使用哪个流对象

明确四:是否需要额外的功能

额外的功能
需要转换吗?
转换流 InputStreamReader, OutputStreamReader
需要高效吗?
缓冲区对象:BufferedXXX
有多个源(字节流)吗?
序列流:SequenceInputStream
对象需要序列化吗?
ObjectInputStream, ObjectoutputStream
需要保证数据输出的表现形式吗?
打印流: PrintStream , PrintWriter
需要操作基本类型数据保证字节原样性吗?
DateOutputStream DateInputStream

你可能感兴趣的:(java基础——IO流)