java基础之IO流

来源:互联网 发布:贵州6频道网络电视 编辑:程序博客网 时间:2024/05/17 06:05

所谓的IO流,即Input/Output流,主要是用来处理设备之间的数据传输的。Java对数据的传输就是通过流的方式,它对于流的操作都封装在了java.io.*包中。
对于流来说,如果谈到其操作的数据,一般就分为字符流和字节流,而谈到流向的话,那就是输入输出流。
字节流的抽象基类:InputStream,OutputStream;
字符流的抽象基类:Reader,Writer;
它们的子类均是以父类名为后缀,如FileInputStream,前缀为功能。

如何区别字节流和字符流

字符流处理的单元为2个字节的Unicode字符,分别操作字符、字符数组或字符串,而字节流处理单元为1个字节,操作字节和字节数组。所以字符流是由Java虚拟机将字节转化为2个字节的Unicode字符为单位的字符而成的,所以它对多国语言支持性比较好!如果是音频文件、图片、歌曲,就用字节流好点,如果是关系到中文(文本)的,用字符流好点。
所有文件的储存是都是字节(byte)的储存,在磁盘上保留的并不是文件的字符而是先把字符编码成字节,再储存这些字节到磁盘。在读取文件(特别是文本文件)时,也是一个字节一个字节地读取以形成字节序列。
字节流可用于任何类型的对象,包括二进制对象,而字符流只能处理字符或者字符串;字节流提供了处理任何类型的IO操作的功能,但它不能直接处理Unicode字符,而字符流就可以。
==================我们还可以看到:============
Reader类的read()方法返回类型为int :作为整数读取的字符(占两个字节共16位),范围在 0 到 65535 之间 (0x00-0xffff),如果已到达流的末尾,则返回 -1。
InputStream的read()虽然也返回int,但由于此类是面向字节流的,一个字节占8个位,所以返回 0 到 255 范围内的 int 字节值。如果因为已经到达流末尾而没有可用的字节,则返回值 -1。因此对于不能用0-255来表示的值就得用字符流来读取!比如说汉字。

FileWriter

由于IO流是用于操作数据的,而我们最常见的体现形式就是文件。
对于FileReader和FileWriter来说,创建对象时,必须要明确被操作的文件。其中FileWriter会将文件创建到指定的目录,如果该目录下已经有文件存在,就会对其进行覆盖;如果没有的话,就会立即创建。

public static void main(String[] args) throws IOException {        FileWriter fw=null;        try {            //必须得指定数据要存的地方            fw=new FileWriter("D:\\1.txt");            //将字符串写入到流中            fw.write("adajll的含量");            //刷新流对象中的缓冲的数据,将数据刷到目的地中            fw.flush();        } catch (IOException e) {            System.out.println(e.toString());        }        finally{            try {                if(fw!=null)                    //关闭资源,但关闭后会刷新一次内部的缓冲中的数据                    //flush刷新完后流可以继续使用,但close刷新后关闭                    fw.close();            } catch (IOException e2) {                System.out.println(e2.toString());            }        }    }

可以看到,对于IO流,会经常抛异常,如IOException,FileNotFoundException….所以会建立如上代码所示的,在外建立引用,在内建立异常。
对于fw=new FileWriter(“D:\1.txt”),流是将数据刷到指定的地址,如果该地址中已存在1.txt文件,那么流会将内容进行覆盖,而如果没有存在,则直接在该地址下创建文件。也可以设置其为不覆盖。
fw=new FileWriter(“D:\1.txt”,true);传递参数true即为不覆盖原有内容,并在文件的末尾继续写入数据。
这里写图片描述

这里写图片描述

FileReader

说到了文件的写,自然也就会相对应的谈到文件的读,与FileWriter对应的为FileReader。
FileReader有两种读取方式,一种是以单个字符进行读取,一种是以字符数组进行读取。

第一种方式:单个字符进行读取
public static void main(String[] args) throws IOException {        FileReader fr=null;        try {        //必须得指定数据从哪儿获取,若无该文件,发生FileNotFoundExeption            fr=new FileReader("D:\\1.txt");            int ch=0;            //结束标志为ch=-1            while((ch=fr.read())!=-1){                System.out.print((char)ch);            }        } catch (IOException e) {            System.out.println(e.toString());        }        finally{            try {                if(fr!=null)                //关闭资源,但关闭后会刷新一次内部的缓冲中的数据                    fr.close();            } catch (IOException e2) {                System.out.println(e2.toString());            }        }    }

对于Reader来说,读的是字符流,可以读中英文,是一个字符一个字符读的,读取的结果是字符的ASCII码值,由于read()方法是自动往下读的,因此设置了一个int ch=0来获得当前读到的字符,只要不遇到结束符-1就可以一直读取数据。读取到的码值ch要将其转化为字符输出
这里写图片描述

第二种方式:以数组进行读取
public static void main(String[] args) throws IOException {        FileReader fr=null;        try {            //必须得指定数据存储的地方            fr=new FileReader("D:\\1.txt");            char[] chs=new char[1024];            int num=0;            while((num=fr.read(chs))!=-1){                System.out.print(new String(chs,0,num));            }        } catch (IOException e) {            System.out.println(e.toString());        }        finally{            try {                if(fr!=null)                    fr.close();            } catch (IOException e2) {                System.out.println(e2.toString());            }        }    }

如上代码所示,在读取之前定义了一个字符数组chs,初始化容量为1024,相当于每次读取到的是1kb的大小,比较优化。read(char[])的返回值是读取到的字符的个数。所以在循环当中输出的是new String(chs,0,num)。它的结束标志也是num!=-1,其内部实现其实就是一个字符一个字符的读。
这里写图片描述

Buffer提高读写效率

缓冲区要结合流来进行使用,在流的基础上对流的功能进行增强。它的出现,提高了对数据的读写效率。和读写相对应的,缓冲区也分为BufferedReader和BufferedWriter两类,一个针对读,一个针对写。
由于是针对流的操作,因此在使用缓冲区时,先要进行流的读写,再放入缓存区,进行操作。

BufferedReader

public static void main(String[] args) throws IOException {        FileReader fr=null;        BufferedReader br=null;        try {            //缓冲区是基于流的,因此要先使用流            fr=new FileReader("D:\\1.txt");            br=new BufferedReader(fr);            String line=null;            while((line=br.readLine())!=null){                System.out.println(line);            }        } catch (IOException e) {            System.out.println(e.toString());        }        finally{            try {                if(br!=null)                    br.close();            } catch (IOException e2) {                System.out.println(e2.toString());            }        }    }

如上代码所示,对于缓存区来说,它提供了一个一次可以读一行的方法readLine,方便于对文本数据的获取,当返回null时,表示读到了文件末尾。由于while循环判断了当前的行的内容,相当于运行了一次readLine,所以在这之前定义一个字符串,将当前行赋值给字符串,然后对字符串进行输出,否则容易出现隔行读。
在最后关闭的时候,关闭的是缓冲区,而没有关闭流对象,这是因为,关闭缓冲区,即是关闭缓冲区的流对象,所以不用再写fw.close()。
这里写图片描述

BufferedWriter

public static void main(String[] args) throws IOException {        FileWriter fw=null;        BufferedWriter bw=null;        try {            //必须得指定数据要存的地方            fw=new FileWriter("D:\\1.txt");            bw=new BufferedWriter(fw);            int index=0;            while(index<3){                bw.write("缓冲区写入"+index);                bw.newLine();//换行                bw.flush();                index++;            }        } catch (IOException e) {            System.out.println(e.toString());        }        finally{            try {                if(bw!=null)                    bw.close();            } catch (IOException e2) {                System.out.println(e2.toString());            }        }    }

这里写图片描述

BufferedWriter提供了一个换行方法newLine。其余的和writer区别不大。同时,BufferedWriter和BufferedReader可以进行交互,读取到文件中的数据,再写入到另外一个文件。写的方式大致和读类似,可以一个字符一个字符的写,也可以字符数组形式进行写入新文件,还可以利用缓冲区一行一行的读,再写入。如下,利用缓冲区来进行读写交互。

public static void main(String[] args) throws IOException {        FileReader fr=null;        FileWriter fw=null;        BufferedWriter bw=null;        BufferedReader br=null;        try {            fr=new FileReader("D:\\1.txt");            br=new BufferedReader(fr);            //必须得指定数据要存的地方            fw=new FileWriter("D:\\2.txt");            bw=new BufferedWriter(fw);            String line=null;            while((line=br.readLine())!=null){                bw.write(line);                bw.newLine();                bw.flush();            }                   } catch (IOException e) {            System.out.println(e.toString());        }        finally{            try {                if(bw!=null && br!=null)                {   //关闭两流资源,关闭缓冲区即可                    bw.close();                    br.close();                }            } catch (IOException e2) {                System.out.println(e2.toString());            }        }    }

这里写图片描述
****readLine方法原理:无论是读一行还是读取多个字符,最终底层实现都是在硬盘上一个一个读取。所以底层还是使用的是read方法,一次读取一个。

字节流InputStream OutputStream

如上的字符流,只用来处理文本信息。对于字节流,就使用的是InputStream和OutputStream,与之相对应的就是Flie,Buffer,加起来也是四个。

public static void main(String[] args) throws IOException {        FileOutputStream out=new FileOutputStream("D:\\1.txt");        out.write("测试字节流输出".getBytes());        FileInputStream in=new FileInputStream("D:\\1.txt");        int ch=0;        while((ch=in.read())!=-1)        {            System.out.print((char)ch);        }    }

这里写图片描述
如上代码所示,字节输出流在写入的时候,要将字符串变成byte数组,其他和字符流的没啥差别,也可以单个字节写入,也可以字符数组写入,但是buffer对应的没有了换行的newLine方法。
字节输入流和reader的区别也不大,也是单字符读和多字符读,buffer对应的方式少了读取一行的方法。

InputStreamReader OutputStreamWriter

InputStreamReader,字节输入流向字符输入流转化的桥梁。
将inputStream转化以后,就可以使用bufferedReader的方法了。
同理,outputStreamWriter是字节输出流转字符输出流的桥梁,转换完成就可以使用bufferedwriter的方法,如newLine等。

BufferedReader br    =new BufferedReader(new InputStreamReader(System.in));BufferedWriter bw    =new BufferedWriter(new OutputStreamWriter(System.out));

存储时需要制定编码表时,可以使用转换流。因为只有转换流才可以指定、FileWriter默认使用编码表GBK。

public static void main(String[] args) throws IOException {        OutputStreamWriter out            =new OutputStreamWriter(System.out, "utf-8");        BufferedWriter bw=new BufferedWriter(out);        InputStreamReader in            =new InputStreamReader(System.in, "utf-8");        BufferedReader br=new BufferedReader(in);    }

文件File类

流在操作数据的时候,最大的体现就是文件。
File类就是将文件或文件夹封装为对象,方便对其属性信息进行操作。

文件的创建

File file=new File(“a.txt”);
File file=new File(“C:\”,”b.txt”);
file.createFile();
文件的创建如上所示,如未指定路径,则在当前项目下创建文件,如果指定则在指定目录下创建。
若文件不存在,则创建;若已存在,则不创建,并返回false,和输出流不一样,文件不会对已存在对象进行覆盖。

删除文件

boolean delete();删除失败返回false,
void deleteOnExit(),程序结束时才删除文件

判断文件是否存在

boolean exists();文件是否存在。
创建文件夹 File f=new File(“abc”);
boolean mkdir();创建文件夹。 file.mkdir();
Boolean mkdirs(),创建多级文件夹。

判断是否是目录

boolean isDirectory,在判断之前先判断文件是否存在, isFile();

获取信息

getPath, getAbsolutePath, getName,getLength….
getParent返回绝对路径中的父目录,否则返回null.如果是相对路径,有上一层目录的话,返回上一层目录。
File[] files=File.listRoots();打印盘符
String[] names=file.list();//打印某路径下的所有文件及文件夹
过滤文件,代码如下:

public static void main(String[] args) throws IOException {        File dirFile=new File("D:\\WorkSpace\\test\\src\\test");        String[] names=dirFile.list(new FilenameFilter() {            @Override            public boolean accept(File dir, String name) {                return name.startsWith("Test");            }        });        for(String name:names){            System.out.println(name);        }    }

此处使用了过滤文件名的匿名内部类,结果如下:
这里写图片描述
File[] files=dir.listFiles();//列出某目录下的所有文件。
为了代码的可移植性,一般采用File.separator来代替手写的磁盘分隔符。

Properties

Properties是hashtable的子类,具备map集合的特点,里面存储的是字符串类型的键值对。一般用来匹配文件。
Properties pro=new Properties();
pro.setPorperty(“xxx”,”xxxx”);
pro.getProperty(“xxx”);//类似于map的put 和get。
用其存取配置文件,通过流读取到该配置文件,然后用”=”切割。其中左边为键,右边为值。
对于非缓冲区流来说,必须得在pro里面加载流。pro.load(fileinputStream);加载数据时,需要固定格式:键=值。

打印流PrintStream PrintWriter

打印流,即提供了打印方法,可以将各种数据类型都原样打印。
字节打印流PrintStream,构造函数可接收的类型,如:file对象,String字符串,outputStream字节输出流。
字符打印流,PrintWriter,在字节的几种类型上多一个,Writer字符输出流。

操作对象

ObjectInputStream,ObjectOutputStream,被操作的对象必须实现Serializable标记接口。无方法的接口,即给类一个资格以序列化。其中包含uid,如果类改变,则uid也会发生变化。即对象的序列化。

write:
public static void main(String[] args) throws IOException {        ObjectOutputStream oos=new ObjectOutputStream(                    new FileOutputStream("obj.txt"));        oos.writeObject(new Person("liming",30));        oos.close();    }

将对象序列化以后存入内存当中,对我们来说可能看不懂,但主机会识别。其中,Person类必须实现Serializable接口。

read
public static void main(String[] args) throws IOException {        ObjectInputStream ois=new ObjectInputStream(                    new FileInputStream("obj.txt"));        Person p=(Person)ois.readObject();        ois.close();    }

类一改变,UID即改变,obj.txt不对应,就不允许读写。序列号是根据成员来的,改变私有化等也会改变UID。可以自定义UID来解决上述问题。

public static final long serialVersionUID=42L;

注意:静态是不能被序列化的,因为在方法区,而对象在堆中。非静态的,若以transient修饰,也不会被序列化,但仍会在堆内存中存在。

管道流

管道流,PipedInputStream,PipedOutputStream。输入和输出可以直接相连,通过应用在多线程中,单线程会出现死锁。

public static void main(String[] args) throws IOException {        PipedInputStream in=new PipedInputStream();        PipedOutputStream out=new PipedOutputStream();        in.connect(out);        //多线程中传入in和out,然后启动线程    }

字符编码

ASCII; ISO8859-1; GB2312; GBK;Unicode,UTF-8
GBK:4字节,UTF-8:6字节
String –>byte[] : str.getBytes();
打印byte: Arrays.toString(b1);
byte[]–>String: new String(byte[],charsetName);(“GBK”,“UTF-8”)
解码错误的时候,先按错误的再编码一次,再按原编码对其解码。

原创粉丝点击