Java IO(上)

 捕获

IO中基本概念和开发过程中如何确定该使用到哪些类.

 

 

 

1,基本概念

输入和输出的定义是对于计算机中的内存而言的,从内存到外围设备(比如硬盘)是输出,从外围设备到内存,是输入.

字节:计算机可以认识的数据.

字符:通过是控制台输出的内容,人可以认识的.

字符流的由来:计算机读取到的其实是字节数据,然后在根据提供的各种编码表(每个国家都有自己特有文字的编码表),去获取对应的文字,通俗的来将字符流就是字节流+编码表.

     JavaIO操作只要是指Java进行输入和输出的操作,所涉及到的类和接口都存放在java.io包中,使用时需要导包,从上图可以看出,JavaIO中最重要的有5个类和一个接口,

InputStream,Reader,OutputStream,Writer,File,和接口Serializable.一下我将用文字简单的总结各个类的特点和类中特有的方法.

    在IO中,主要有字节流和字符流两大类,这两大类都具有输入和输出的功能,在字节流中,输入(InputStream),输出(OutputStream),在字符流中,输入(Reader),输出(Writer)

2,在开发的过程中,需要明确到时是使用以上到哪些类?我们可以通过4个步骤(熟练后只需要确定最后源和目的的设备即可),就能快速的判断出来

2.1,明确源和目的(汇)   源:InputStream  Reader    目的:OutputStream  Writer
     2.2明确数据是否是纯文本数据   源:如果是纯文本,Reader 否:InputStream
                                      目的:是纯文本  Writer       否 :OutputStream
      到这里,就可以明确需求中具体要使用到哪个体系
     2.3明确具体的设备     源设备: 硬盘:File 键盘:System.in  内存:数组  网络:Socket流
                             目的设备:  硬盘:File  控制台:System.out 内存:数组  网络:Socket

2.4是否需要其他额外功能  1,是否需要高效(缓冲区)   是,就加上buffered

 

 

2.5需求与代码(通过四部曲来判断使用到的类),一下4个需求,我都会加入额外功能,缓冲区(BufferedReader和BufferedWriter)

需求1:复制一个文本文件 

分析:源和目的在硬盘设备上的纯文本文件, FileReader和FileWriter

需求2:读取键盘存入信息,并存入一个文件中

分析:源是键盘,目的是硬盘上的纯文本信息, (源需要进行转化,字节->字符)InputStreamReader is = System.in     FileWriter

需求3:将文本文件的数据显示到控制台上

分析:源是硬盘,目的是控制台(System.out)的纯文本信息   FileReader   (计算机到控制台显示,字符->字节)OutputStreamWriter ops = System.out

需求4:读取键盘录入信息,显示在控制台上

分析:源是键盘(System.in),目的是控制台(System.out)     InputStreamReader ips = System.in  OutputStreamWriter = System.out

需求5:将一个中文字符串数据按照指定的码表写入到一个文本本件中去

分析:按照指定的码表,这个一定得想到转化流

源(硬盘File):转化流的话,查阅API,那么使用的是OutputStreamWriter中的构造方法传入2个参数OutputStream和Charset

目的(硬盘File):按照源的思路,InputStreamReader中的构造方法传入两个参数InputStream和Charset

上述5个是IO中最基本的操作,其中设计到一个比较关键的问题,我们社么时候需要考虑使用转换流?

1,源和目的对应的设备都是字节流,但操作却是文本数据

2,一旦操作涉及到指定编码的时候,一定得使用转换流

/*
 * 需求:复制一个文本文件
 */
public class demo01 {
    public static void main(String[] args) throws IOException {
        BufferedReader bufr = new BufferedReader(new FileReader("a.txt"));
        BufferedWriter bufw = new BufferedWriter(new FileWriter("b.txt"));
        String line = null;
        while((line=bufr.readLine())!=null){
            bufw.write(line);
            bufw.flush();
        }
    }
}

//需求:读取键盘录入信息,并存入文件
public class demo02 {
    public static void main(String[] args) throws IOException {
        BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bufw = new BufferedWriter(new FileWriter("c.txt"));
        String line = null;
        while((line=bufr.readLine())!=null){
            bufw.write(line);
            bufw.flush();
        }
    }
}

//需求:将文本文件数据显示到控制台
public class demo03 {
    public static void main(String[] args) throws IOException {
        BufferedReader bufr = new BufferedReader(new FileReader("a.txt"));
        BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
        String line = null;
        while((line=bufr.readLine())!=null){
            bufw.write(line);
            bufw.flush();
        }
    }
}

//需求  读取键盘录入数据,显示到控制台
public class demo04 {
    public static void main(String[] args) throws IOException {
        BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
        String line = null;
        while((line=bufr.readLine())!=null){
            bufw.write(line);
            bufw.flush();
        }
    }
}

关于BufferedReader和BufferWriter,缓冲区的理解

缓冲区的原理:从源中先拿到一部分数据,方法缓冲区中先使用,当缓冲区的数据使用完毕后,再从源中取出数据到缓冲区,直到源中没有数据,为-1或者的null作为结尾,这样能提高效率.

这两个类都是为了提高访问效率,构造方法中需要要传入各自对应的Reader和Writer的对象,没有无参的构造方法.

源BufferedReader中,有两个读取方法,捕获,一般最后一个比较常用,这里要注意下返回值的不同,int的为-1,String的为null.

对于读取文件中的数据,都是用读取文本行的方法来完成的,这样能大大的提高提取的效率,tips:使用while循环读取的时候,它的返回值是null.

目的BufferWriter中,有个方法是特有的,捕获

Java中的递归

递归的含义:函数自身直接或者间接的调用自身.

什么时候使用递归:一个功能被重复使用,参与运算的结果与上次函数调用有关,这类问题都可以利用递归来解决

递归的注意事项:一个功能被设计成递归,那么这个功能的运算必须要有入口,也比较要有出口,一定要明确条件,否则会发生栈溢出

                   注意递归的次数,假如没明确结束条件,会出现异常

public class DiGuiDemo {
    public static void main(String[] args) {
        int sum = getSum(5);
        System.out.println("sum:" + sum);
    }

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

以上例子就是一个递归的运用,去实现连续的自然数的相加,这里的if语句中的代码,就把递归的次数明确好了,从5开始,连续的到2,这里是连续的在调用getSum的函数,然后到1的时候,直接返回的是1,这个时候递归结束,注意,从5到4,这个时候只是在调用函数,这个时候并没有进行加法的循环,知道1结束后,就需要弹栈,这个时候从1开始,从后向前进行加法的运算,这个是重点,该开始我想不同的原因,就是没想想明白,递归的时候,函数是什么时候弹栈的.

File类

1 基本概念

它是在IO中,唯一与文件操作有关的类,通过构造方法,传入字符串的参数,将已经存在或者不存在的文件或者目录封装成对象,以后只要操作到具体的文件,我们就应该把它进行封装,然后再进行操作.

在File类中提供了两个非常重要的常量:2

捕获

在操作文件的时候,一定要使用File.separator来表示分隔符,因为在不同的操作系统上,它会显示对于该操作系统的分割符.

 

2 下面将介绍几个比较常用的File类中的方法

列出指定目录的全部文件(比如列出D盘下的文件,它只会到D的一层目录,不会到D目录下的子目录或者子子(子子....)目录)

12

以上2个方法,一个返回的是String字符串形式,一个返回的是File[]数组对象,数组对象的输出需要遍历,你懂的,一般我喜欢第二种方式的输出.

3 那么我们如何可以将D盘目录下的所有的文件的路径都显示出来?这个时候就需要使用到递归了.

深度遍历的思路:1,创建一个显示所有目录的方法,该方法需要传入一个File对象的参数(需要深度遍历的文件)

                    2,使用方法二对File对象进行遍历

                    3,在遍历的过程中需要进行判断,使用捕获 判断传入的File对象有子目录,那么就递归在调用自身的方法

                    4,弹栈的动作,递归到最后了,然后在输出目录.具体代码如下

public class FileTest1 {
    public static void main(String[] args) {
        File f = new File("d:");
        listAll(f);
    }

    public static void listAll(File f) {
        System.out.println("dir:" + f.getAbsolutePath());
        File[] file = f.listFiles();
        for (int i = 0; i < file.length; i++) {
            if (file[i].isDirectory())
                listAll(file[i]);
            else
                System.out.println("file:" + file[i].getAbsolutePath());
        }
    }

}

public class FileTest2 {

    /*
     * 需求:删除里面带有内容的目录 原理:从最里面往外面删除 需要进行深度遍历
     * 
     */
    public static void main(String[] args) {
        File f = new File("d:\\a");
        removeDir(f);
    }

    public static void removeDir(File f) {
        File[] file = f.listFiles();
        for (File file2 : file) {
            if (file2.isDirectory()) {
                removeDir(file2);
            } else {
                System.out.println(file2 + ":" + file2.delete());
            }
        }
        System.out.println(f + ":" + f.delete());
    }

}

上述两个应用涉及到的File方法123

上面的删除动作,加入文件夹中是有数据或者内容的话,该文件夹是不能被该方法所删除的,所以一般进行删除操作,都需要从里面到外面来进行操作.

4,关键文件名过滤的问题

123

通过上面3张图中的内容可以完成过滤,用法和Java类集中的比较器原理类似.

SequenceInputStream类

该类是将几个输入流连在一起,提供两个构造方法,捕获从API上可以看出,如果需要传入2个以上的输入流的话,我们需要拿到输入流的枚举对象,关于枚举,必须要想到集合框架中的Collections这个工具类,它其中有个方法可以获取集合对象的枚举类型捕获

需求:将a.txt  b.txt  c.txt三个文本文件中的内容合并到一个文本文件abc.txt中

分析:该需求是将3个输入流合并到一起,应该使用下面那个构造方法,通过Collections中的方法来获取3个小输入流的枚举

      1,将a,b,c3个在硬盘(File)上的文本文件进行输入流的封装  FileInputStream

      2,创建ArrayList集合,将3个流装入(add)该集合

      3,通过Collections中的方法,从该集合中获取枚举

      4,SequenceInputStream中的构造方法进行封装

      5,创建输出流,确认源目的(abc.txt)    FileOutputStream   定义长度,byte数组,进行读写操作

      6,关闭各个流.

package cn.wjd.sequence;

import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.SequenceInputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Vector;

public class SequenceDemo {

    /*需求:将a.txt,b.txt,c.txt中的数据合并到一个文件中去
     * 
     */
    public static void main(String[] args) throws IOException {
        ArrayList<FileInputStream> al = new ArrayList<FileInputStream>();
        al.add(new FileInputStream("a.txt"));
        al.add(new FileInputStream("b.txt"));
        al.add(new FileInputStream("c.txt"));
        Enumeration<FileInputStream> en = Collections.enumeration(al);
        SequenceInputStream sis = new SequenceInputStream(en);
        FileOutputStream fos = new FileOutputStream("abc.txt");
        byte[] buf = new byte[1024];
        int len = 0;
        while((len=sis.read(buf))!=-1){
            fos.write(buf, 0, len);
        }
        sis.close();
        fos.close();
    }

}

该类还有一个具体的应用,是文件的切割和文件的合拼,这个会在我下一个博客的由思路出代码的时候,再详细分析.

java.util中的Properties类

它是用来保存配置文件信息相关的类,表示了一个持久的属性集,该类中数据都是以字符串的形式出现的,并有key->value的关系,集合中的数据可以保存在流中,也可以从流中获取.下面先看API中几个常用的方法.

123

捕获捕获

需求:修改某个配置文件的信息

分析:1,将这个配置文件用File进行封装,创建Properties对象

      2,创建输入流  FileInputStream

      3,将流中的信息存储到配置文件 load

      4,修改配置文件信息   setProperty

      5,在修改好的配置文件写出输出流   FileOutputStream  store

需求:将一些配置信息写入一个新的配置文件

     1,创建Properties对象,  设置配置信息 setProperty

     2,创建输出流,确定新配置文件的名称和位置   FileOutputStream

     3,将信息集合储存到配置文件,关流   store

需要:输出某个配置文件的信息

1,创建Properties对象,  设置配置信息 setProperty

2,stringPropertyNames()得到一个set集合  得到的是配置文件中的key

3,对set集合进行遍历,然后通过key---->value

package cn.wjd.properties;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Properties;
import java.util.Set;

public class PropertiesDemo {
    /*
     * Map -Hashtable -Properties Properties集合的特点: 1,该集合中的key和value都是字符串类型
     * 2,集合中的数据可以保存在流中,或者从流中获取 
     * 3,通常该集合操作以键值对存在的配置文件
     */
    public static void main(String[] args) throws IOException {
        // propertiesDemo();
        // propertiesMethod_2();
        // propertiesMethod_2();
        //propertiesMethod_3();
        //propertiesMethod_4();
        test();
    }
    // 对已有的配置文件中的信息进行修改
    /*
     * 读取这个文件,并将这个文件中的键值存储到集合中 在集合中对数据进行修改 再通过流将修改后的输出
     */
  private static void test() throws IOException {
      File file = new File("info.txt");
      Properties pro = new Properties();
      if(!file.exists()){
          file.createNewFile();
      }
      //创建一个输入流
      FileInputStream fis = new FileInputStream(file);
      //将流中的信息存储在配置文件
      pro.load(fis);
      //修改配置文件的数据
      pro.setProperty("zhangsan", "1");
      FileOutputStream fos = new FileOutputStream(file);
      pro.store(fos, "name+age");
      pro.list(System.out);
      fis.close();
      fos.close();
    }


    public static void propertiesMethod_4() throws IOException {
        Properties pro = new Properties();
        //集合中的数据来源与文件,注意:必须保证文件中的数据是键值对
        //需要使用到读取流
        FileInputStream fis = new FileInputStream("info.txt");
        pro.load(fis);
        pro.list(System.out);
        fis.close();
    }


    public static void propertiesMethod_3() throws IOException {
        // 建立properties集合
        Properties pro = new Properties();
        // 往集合中添加元素
        pro.setProperty("zhangsan", "21");
        pro.setProperty("zhangsan2", "22");
        pro.setProperty("zhangsan3", "23");
        // 将集合中的信息持久化的存储在文件中,需要关联输出流
        FileOutputStream fos = new FileOutputStream("info.txt");
        //将集合信息存储到配置文件中
        pro.store(fos, "name+age");
        fos.close();
    }

    /*
     * 演示Properties与流相结合的方法
     */
    public static void propertiesMethod_2() {
        // 建立properties集合
        Properties pro = new Properties();
        // 往集合中添加元素
        pro.setProperty("zhangsan", "21");
        pro.setProperty("zhangsan2", "22");
        pro.setProperty("zhangsan3", "23");
        pro.list(System.out);// 这个方法在程序调试的时候,使用的频率会比较高
    }

    public static void propertiesDemo() {
        // 建立properties集合
        Properties pro = new Properties();
        // 往集合中添加元素
        pro.setProperty("zhangsan", "21");
        pro.setProperty("zhangsan2", "22");
        pro.setProperty("zhangsan3", "23");
        // 修改集合中的元素
        pro.setProperty("zhangsan", "26");
        // 取出集合中的元素
        Set<String> set = pro.stringPropertyNames();
        for (String string : set) {
            String value = pro.getProperty(string);
            System.out.println("key:" + string + "  value:" + value);
        }
    }

}
posted @ 2014-07-06 21:22  木有杂念  阅读(189)  评论(0编辑  收藏  举报