javaIO流

@stormxue 2016-11-29 10:12 字数 18686 阅读 64

javaIO流

javaIO


IO流对象继承关系

IO流对象继承关系

其他常用与流有关的对象:

字节流 字符流
文件类 File
打印流 PrintStream、PrintWriter
管道流 PipedInputStream、PipedOutputStream
序列流 SequenceInputStream
对象序列化流 SequenceInputStream


IO流:用于处理设备上的数据 
  设备:硬盘,内存,键盘录入

IO流具体分类: 
  1.根据处理的数据类型不同:字节流和字符流 
  2.根据流向不同:输入流和输出流 
  3.根据功能不同:节点流和处理流

  字节流 字符流
输入流 InputStream(读) Reader
输出流 OutputStream(写) Writer


字符流的由来: 
  因为文件编码的不同,而有了对字符进行高效操作的字符流对象。 
  原理:其实就是基于字节流读取字节时,去查了指定的码表。 
字节流和字符流的区别: 
  1.字节流读取的时候,读到一个字节就返回一个字节。字符流使用了字节流读到一个或多个字节(中文对应的字节数是两个,在UTF-8中是3个字节)时,先去查指定的编码表,将查到的字符返回。 
  2.字节流可以处理所有类型数据,如图片,mp3,avi。而字符流只能处理字符数据。

结论:只要是处理纯文本数据,就要优先考虑使用字符流。

节点流和处理流: 
  ①节点流为可以从一个特定的数据源(节点)读写数据。 
  ②处理流是“连接”在已存在的流(节点流或处理流)之上,通过对数据的处理为程序提供更为强大的读写功能。 
IO的体系,所具备的基本功能就有两个:读 和 写。

基本的读写操作方式: 
  因为数据通常都以文件形式存在,所以就要找到IO体系中可以用于操作文件的流对象。通过名称可以更容易获取该对象。因为IO体系中的子类名后缀绝大部分是父类名称。而前缀都是体现子类功能的名字。

Reader
    |--InputStreamReader
        |--FileReader:专门用于处理文件的字符读取流对象。
Writer
    |--OutputStreamReader
        |--FileWriter:专门用于处理文件的字符写入流对象。

Reader中的常见方法(数据单位为字符16bit): 
  int read():读取一个字符。返回的是读到的那个字符。如果读到流的末尾,返回-1。 
  int read(char[]):将读到的字符存入指定的数组中,返回的是读到的字符个数,也就是往数组里装的元素的个数。如果读到流的末尾,返回-1。 
  void close():关闭流释放内存资源 
Writer中常见的方法: 
  write(int c):将一个字符写入到流中。 
  write(char[] cbuf):将一个字符数组写入到流中。 
  write(String str):将一个字符串写入到流中。 
  flush():刷新流,将流中的数据刷新到目的地中,流还存在。 
  close():关闭资源,在关闭前会先调用flush(),刷新流中的数据去目的地。

FileWriter: 
该类没有特有的方法,只有自己的构造函数。 
特点在于: 
  1.用于处理文本文件 
  2.该类中有默认的编码表 
  3.该类中有临时缓冲。 
构造函数:在写入流对象初始化时,必须要有一个存储数据的目的地。 
FileWriter(String filename):调用系统资源,在指定位置,创建一个文件,如果该文件已经存在,会被覆盖。 
FileWriter(String fileName, boolean append):当传入的boolean类型值为true时,会在指定文件末尾处进行数据的续写。

FileReader: 
用于读取文本文件的流对象,用于关联文本文件。

  1. package com.xue.javaIO;
  2. import java.io.FileNotFoundException;
  3. import java.io.FileReader;
  4. import java.io.FileWriter;
  5. import java.io.IOException;
  6. /**
  7. * Created by xue on 2016-10-12.
  8. */
  9. public class DemoIO {
  10. public static void main(String[] args) {
  11. DemoIO demoIO = new DemoIO();
  12. //demoIO.Demo001();
  13. //demoIO.Demo002();
  14. demoIO.Demo003();
  15. }
  16. /**
  17. * 将文本数据存储到一个文件中
  18. * 当指定绝对路径时,定义目录分隔符有两种方式:1.反斜线 \\ 要写两个。2.斜线 / 写一个即可。
  19. */
  20. public void Demo001() {
  21. FileWriter fw = null;
  22. try {
  23. fw = new FileWriter("demo.txt");
  24. fw.write("abc中国人");
  25. fw.flush();//对缓冲区的数据进行刷新,将数据刷到目的地中。
  26. fw.write("KKK");
  27. } catch (IOException e) {
  28. e.printStackTrace();
  29. }finally {
  30. if(fw!=null){
  31. try {
  32. fw.close();
  33. } catch (IOException e) {
  34. e.printStackTrace();
  35. System.out.println();
  36. }
  37. }
  38. }
  39. }
  40. //读取一个已有的文本文件,将文本数据打印出来
  41. public void Demo002(){
  42. FileReader fr = null;
  43. try {
  44. fr = new FileReader("D:/webpro/java001.java");
  45. int ch=0;
  46. while ((ch=fr.read())!=-1){
  47. System.out.print((char)ch);
  48. }
  49. fr.close();
  50. } catch (FileNotFoundException e) {
  51. System.out.println("文件没有找到");
  52. }catch (IOException e){
  53. e.printStackTrace();
  54. }
  55. }
  56. //读一个字符就存入字符数组里,读完1Kb再打印
  57. public void Demo003(){
  58. FileReader fr = null;
  59. try {
  60. fr = new FileReader("D:/webpro/java001.java");
  61. char[] buf = new char[1024];
  62. int len;
  63. while ((len=fr.read(buf))!=-1){
  64. System.out.print(new String(buf,0,len));
  65. }
  66. fr.close();
  67. } catch (FileNotFoundException e) {
  68. System.out.println("文件没有找到");
  69. } catch (IOException e) {
  70. System.out.println(e.toString());
  71. }
  72. }
  73. }

字符流的缓冲区:

缓冲区的出现提高了对流的操作效率。 
原理:其实就是将数组进行封装。

  • 对应的对象: 
    • BufferedWriter:特有方法:newLine():跨平台的换行符。
    • BufferedReader:特有方法:readLine():一次读一行,到行标记时,将行标记之前的字符数据作为字符串返回。当读到末尾时,返回null。

readLine():方法的原理: 其实缓冲区中的该方法,用的还是与缓冲区关联的流对象的read方法。只不过每一次读到一个字符,先不进行具体操作,先进行临时存储,当读取到回车标记时,将临时容器中存储的数据一次性返回。

  在使用缓冲区对象时,要明确,缓冲的存在是为了增强流的功能而存在,所以在建立缓冲区对象时,要先有流对象存在。其实缓冲内部就是在使用流对象的方法,只不过加入了数组对数据进行了临时存储。为了提高操作数据的效率。

代码上的体现:

  1. 写入缓冲区对象。
  2. //建立缓冲区对象必须把流对象作为参数传递给缓冲区的构造函数。
  3. BufferedWriter bufw = new BufferedWriter(new FileWriter("buf.txt"));
  4. bufw.write("abce");//将数据写入到缓冲区
  5. bufw.flush();//对缓冲区的数据进行刷新。将数据刷到目的地中。
  6. bufw.close();//关闭缓冲区,其实关闭的是被包装在内部的流对象。

  上面代码使用了匿名的FileWriter流,这个流无法关闭。之所以需要关闭流资源,是为了释放资源,因为io是操作系统的动作,例如如果没有关闭,那么这个IO资源就一直被java进程占用,知道java应用结束,这期间操作系统的其他进程就不能访问该IO资源了。我的看法是既然是匿名对象,肯定是要被回收的,但是什么时候回收就不一定了,因此建议你自己处理输入输出流的关闭操作。

字节流

抽象基类:InputStream,OutputStream。 
FileInputStream 
FileOutputStream

BufferedInputStream 
BufferedOutputStream

注意:字符流使用的数组是字符数组。char[] chs 
   字节流使用的数组是字节数组。byte[] bt

字节流可以操作任何数据。

转换流

特点: 
1.是字节流和字符流之间的桥梁。 
2.该流对象中可以对读取到的字节数据进行指定编码表的编码转换。 
什么时候使用呢? 
1.当字节和字符之间有转换动作时。 
2.流操作的数据需要进行编码表的指定时。 
具体的对象体现: 
InputStreamReader:字节到字符的桥梁 
OutPutStreamWriter:字符到字节的桥梁 
这两个流对象是字符流体系中的成员,它们有转换作用,而本身又是字符流,所以在构造的时候,需要传入字节流对象进来。 
构造函数: 
InputStreamReader(InputStream):通过该构造函数初始化,使用的是本系统默认的编码表GBK. 
InputStreamReader(InputStream,String charSet):通过该构造函数初始化,可以指定编码表。 
OutputStreamWriter(OutputStream) 
OutputStreamWriter(OutputStream,String charSet)

操作文件的字符流对象是转换流的子类。

Reader  
    |--InputStreamReader  
        |--FileReader  

Writer  
    |--OutputStreamWriter  
        |--FileWriter

转换流中的read方法已经融入了编码表,在底层调用字节流的read方法时将获取的一个或者多个字节数据进行临时存储,并去查指定的编码表,如果编码表没有指定,查的是默认码表,那么转换流的read方法就可以返回一个字符,比如中文。 
转换流已经完成了编码转换的动作,对于直接操作的文本文件的FileReader而言,就不用在重新定义了,只要继承转换流,获取其方法,就可以直接操作文本文件中的字符数据了。

注意:在使用FileReader操作文本数据时,该对象使用的是默认的编码表。如果要使用指定编码表,必须使用转换流。

FileReader fr = new FileReader("a.txt");//操作a.txt的中的数据使用的本系统默认的GBK。 
操作a.txt中的数据使用的也是本系统默认的GBK。 
InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt")); 
这两句的代码的意义相同。 
如果a.txt中的文件中的字符数据是通过utf-8的形式编码,那么在读取时,就必须指定编码表。那么转换流必须使用。 
InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"),"utf-8");

流操作的基本规律

1.明确数据源和数据汇:其实是为了明确输入流还是输出流 
2.明确操作的数据是否是纯文本数据:其实是为了明确字符流还是字节流

数据源:键盘System.in,硬盘File开头的流对象,内存(数组). 
数据汇:控制台System.out,硬盘File开头的流对象,内存(数组)。 
数据源:System.in 
  既然是源,使用的就是输入流,可用的体系有InputStream,Reader。因为键盘录入进来的一定是纯文本数据,所以可以使用专门操作字符数据的Reader。 
发现System.in对应的流是字节读取流。所以要将其进行转换,将字节转成字符即可。 
所以要使用Reader体系中:InputStreamReader 
  接下来,是否需要提高效率呢?如果需要,那么就加入字符流的缓冲区:BufferedReader 
BufferedReader bur = new BufferedReader(new InputStreamReader(System.in)); 
数据汇:一个文件,硬盘 
既然是数据汇,那么一定是输出流,可以用的OutputStream,Writer.往文件中存储的都是文本数据,那么可以使用字符流较为方便:Writer,因为操作的是一个文件。所以使用Writer中的FileWriter。是否要提高效率呢?是,那就使用BufferedWriter. 
BufferedWriter bufr = new BufferedWriter(new FileWriter("a.txt")); 
附加需求:希望将这些文本数据按照指定的编码表存入文件中。既然是文本数据,而且还是写入到文件中,那么使用的体系还是Writer。因为要指定编码表,所以要使用Writer中的转换流,OutputStreamWriter。是否要提高效率,是,选择BufferedWrier。 
注意:虽然最终是文件,介是不可以选择FileWriter。因为该对象是使用默认编码表。输出转换流要接收一个字节输出流进来,所以要用OutputStram体系,而最终输出到一个文件中,那么就要使用OutputStream体系中可以操作的文件的字节流对象:FileOutputStream.

  1. String charSet="utf-8";
  2. BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("a.txt"),charSet));

可以和流相关的集合对象Properties.

Map
    |-Hashtable
        |-Properties

Properties:该集合不需要泛型,因为该集合中的键值对都是String类型。 
1.存入健值对:setProperty(key,value); 
2.获取指定键对应的值:value getProperty(key); 
3.获取集合中所有键元素:Enumeration propertyNames(); 
在jdk1.6版本给该类提供了一个新的方法:Set<String>stringPropertyNames(); 
4.列出该集合中的所有键值对,可以通过参数打印流指定列出到的目的地。 
 list(PrintStream);list(PrintWriter); 
 例:list(System.out):将集合中的键值对打印到控制台。 
   list(new PrintStream("prop.txt")):将集合中的键值对存储到prop.txt文件中。 
5.可以将流中的规则数据加载进行集合,并称为键值对。 
 load(inputStream); 
 jdk1.6版本提供的新方法:load(Reader) 注意:流中的数据要是“键=值”的规则数据。 
6.可以将集合中的数据进行指定目的的存储。 
 store(OutputStram,String comment)方法。 
 store(Writer,String comment):使用该方法存储时,会带着当时存储的时间。

File类:

  该类的出现是对文件系统中的文件以及文件夹进行对象的封装。可以通过对象的思想来操作文件以及文件夹。 
1.构造函数: 
 File(String filename):将一个字符串路径(相对或者绝对)封装成File对象,该路径是可存在的,也可是是不存在。 
 File(String parent,String child); 
 File(File parent,String child); 
2.特别的字段:separator:跨平台的目录分隔符。 
 例子:File file = new File("c:"+File.separator+"abc"+File.separator + "a.txt"); 
3.常见方法 
 1).创建: 
  boolean createNewFile()throws IOException:创建文件,如果被创建的文件已经存在,则不创建。 
  boolean mkdir():创建文件夹。 
  boolean mkdirs():创建多级文件夹。 
 2).删除: 
  boolean delete():可用于删除文件或者文件夹。 
  注意:对于文件夹只能删除不带内容的空文件夹,对于带有内容的文件夹,不可以直接删除,必须要从里往外删除。 
  void deleteOnExit();删除动作交给系统完成。无论是否发生异常,系统在退出时执行删除动作。 
 3).判断: 
  boolean canExecute(); boolean canWrite(); boolean canRead(); 
  boolean exists():判断文件或者文件夹是否存在。 
  boolean isFile():判断File对象中封装的是否是文件。 
  boolean isDirectory():判断File对象中封装的是否是文件夹。 
  boolean isHidden():判断文件或者文件夹是否隐藏。在获取硬盘文件或者文件夹时,对于系统目录中的文件,java是无法访问的,所以在遍历时,可以避免遍历隐藏文件。 
 4).获取: 
  getName():获取文件或者文件夹的名称。 
  getPath():File对象中封装的路径是什么,获取的就是什么. 
  getAbsolutePath():无论File对象中封装的路径是什么,获取的都是绝对路径。 
  getParent():获取File对象封装文件或者文件夹的你目录。注意:如果封装的是相对路径,那么返回的是null. 
  long length:获取文件大小。 
  long lastModified():获取文件最后一次修改时间。 
  Static File[] listRoots():获取系统中的有效盘符。 
  String[] list():获取指定目录下当前的文件以及文件夹名称。 
  String[] list(Filenamefilter):可以根据指定的过滤器,过滤后的文件及文件夹名称。 
  File[] listFiles():获取指定目录下的文件以及文件夹对象。 
 5).重命名: 
  renameTo(File): 
   File f1 = new File("C:\a.txt"); 
   File f2 = new File("C:\b.txt"); 
   f1.renameTo(f2);//将c盘下的a.txt文件改名为b.txt文件。

递归

  其实就是在使用一个功能过程中,又对该功能进行调用,就出现了函数自身调用自身。 
注意:1.一定要限定条件,否则内存溢出。 
   2.使用递归时,调用次数不要过多,否则也会出现内存溢出.

需求:想要列出指定目录下的文件以及文件夹中的文件(子文件).

  1. public class test002 {
  2. public static void main(String[] args) {
  3. test002 s = new test002();
  4. File file = new File("E:"+File.separator+"工具"+File.separator+"Notepad++");
  5. s.listDir(file);
  6. }
  7. public void listDir(File file){
  8. if(file.isDirectory())//判断file是否是目录
  9. {
  10. File [] lists = file.listFiles();
  11. if(lists!=null)
  12. {
  13. for(int i=0;i<lists.length;i++)
  14. {
  15. listDir(lists[i]);//是目录就递归进入目录内再进行判断
  16. }
  17. }
  18. }
  19. System.out.println(file);//file不是目录,就输出它的路径名,这是递归的出口
  20. }
  21. }

需求2:删除一个带内容的目录

  1. public class test002 {
  2. public static void main(String[] args) {
  3. test002 s = new test002();
  4. File dir = new File("E:"+File.separator+"a");
  5. s.removeDir(dir);
  6. }
  7. public void removeDir(File dir){
  8. File[] files = dir.listFiles();
  9. //intellij idea快速生成增强for循环:files.for
  10. for (File file : files) {
  11. if(file.isDirectory()){
  12. removeDir(file);//如果是目录,继续遍历
  13. }else {
  14. file.delete();//如果不是目录,就删除文件
  15. }
  16. }
  17. dir.delete();
  18. }
  19. }

IO包中常见对象

  • 字节流 
    FileInputStream FileOutputStream 
    BufferedInputStream BufferedOutputStream
  • 字符流 
    FileReader FileWriter 
    BufferedReader BufferedWriter 
    • 转换流 
      InputStreamReader OutputStreamWriter
  • 文件对象 
    File
  • 打印流 
    1.PrintStream:是一个字节打印流,System.out对应的类型就是PrintStream. 
            它的构造函数可以接收三种数据类型的值。1)字符串路径;2)File对象;3)OutputStream; 
    2.PrintWriter:是一个字符打印流,构造函数可以接收四种类型的值。 
           1)字符串路径; 
           2)File对象(以上1、2两种还可以指定编码表字符集) 
           3)OutputStream 
           4)Writer(对于3、4类型的数据,可以指定自动刷新;自动刷新为true时,只有三个方法可以用:println,printf,format)
  1. //如果想要既有自动刷新,又可执行编码,要这样完成流对象的包装
  2. PrintWrter pw = new PrintWriter(new OutputSteamWriter(new FileOutputStream("a.txt"),"utf-8"),true);
  3. //实际中不建议这样写,因为这样无法手动关闭流。
  4. //如果想要提高效率,还要使用打印方法
  5. PrintWrter pw = new PrintWriter(
  6. new BufferdWriter(new OutputSteamWriter(new FileOutputStream("a.txt"),"utf-8")),true);

所有的带File的流对象都可以直接操作File对象。

  • 管道流 
    PipedInputStream 
    pipedOutputStream 
    特点:读取管道流和写入管道流可以进行连接。 
    连接方式:1)通过两个流对象的构造函数;2)通过两个对象的connect方法。 
    通常两个流在使用时,需要加入多线程技术,也就是让读写同时运行。 
    注意:对于read方法,该方法是阻塞式的,也就是没有数据的情况,该方法会等待。 
    管道流的主要作用就是可以进行两个线程间的通信。一个线程作为管道输出流,另一个线程作为管道输入流,在启动线程前,只需要将这两个线程的管道流连接到一起就可以。这要就很方便的实现了两个线程间的通信。
  • RandomAccessFile 
    该对象并不是流体系中的一员,对象中封装了字节流,同时还封装了一个缓冲区(字节数组),通过内部的指针来操作数组中的数据。 
    该对象特点:1).该对象只能操作文件,所以构造函数接收两种类型的参数,字符串路径和File对象。 
    2).该对象既可以对文件进行读取,也可以写入。在进行对象实例化时,必须要指定该对象的操作模式,r rw等。该对象中有可以直接操作基本数据类型的方法。 
    比较有特点的方法:skipBytes():跳过指定的字节数。 
    seek();指定指针的位置。 
    getFilePointer():获取指针的位置。 
    通过这些方法,就可以完成对一个文件数据的随机访问,想读那里就读那里,想写那里就写那里。 
    该对象功能,可以读数据,可以写入数据,如果写入位置已有数据,会发生数据覆盖。也就是可以对数据进行修改。 
    在使用该对象时,建议数据都是有规则的,或者是分段的。 
    注意:在实例化时,如果要操作的文件不存在,会自动创建,如果存在,则不会创建,如果存在的文件有数据,那么在没有指定指针位置的情况下,写入数据,会将文件开头的数据覆盖。 
    可用于多线程的下载,也就是通过多线程往一个文件中同时存储数据。
  • 序列流,也称为合并流 
    SequenceInputStream: 
    特点:可以将多个读取流合并成一个流,这样操作起来很方便。 
    原理:其实就是将每一个读取流对象存储到一个集合中,最后一个流对象结尾作为这个流的结尾。 
    两个构造函数: 
    1、SequenceInputStream(InputStream in1,InputStream in2):可以将两个读取流合并成一个流。 
    2、SequenceInputStream(Enumeration en):可以将枚举中的多个流合并成一个流。 
    作用:可以用于多个数据的合并。 
    注意:因为Enumeration是Vector中特有的取出方式。而Vector被ArrayList取代。所以要使用ArrayList集合效率更高一些。那么如何获取Enumeration呢?
  1. /**
  2. * 将1.txt,2.txt,3.txt 通过合并流的方法,将3个文件中的内存存入到4.txt中
  3. * 提示:用Enumeration需要用到Vector的elements()方法,把流对象都加入到Vector集合中
  4. */
  5. public class SequenceInputStreamTest {
  6. public static void main(String[] args) throws Exception {
  7. // TODO Auto-generated method stub
  8. ArrayList<FileInputStream> vec = new ArrayList<FileInputStream>();
  9. vec.add(new FileInputStream("E:\\1.txt"));
  10. vec.add(new FileInputStream("E:\\2.txt"));
  11. vec.add(new FileInputStream("E:\\3.txt"));
  12. Enumeration<FileInputStream> en = Collections.enumeration(vec);
  13. SequenceInputStream sin = new SequenceInputStream(en);
  14. FileOutputStream fos = new FileOutputStream("E:\\4.txt");
  15. byte[] bytes = new byte[1024];
  16. int len = 0;
  17. while ((len = sin.read(bytes)) != -1) {
  18. fos.write(bytes, 0, len);
  19. }
  20. sin.close();
  21. fos.close();
  22. }
  23. }

对文件的切割这里就不写了,百度一下,切割原理:一个读取流对应多个输出流。 
黑马程序员:合并流(序列流)的介绍:SequenceInputStream 
- 对象的序列化 
ObjectInputStream ObjectOutputStream 
可以通过这两个流对象直接操作已有对象并将对象进行本地持久化存储。存储后的对象可以进行网络传输。 
两个对象的特有方法: 
ObjectInputStream 
   Object readObject():该方法抛出异常:ClassNotFountException. 
ObjectOutputStream 
   void writeObject(Object):被写入的对象必须实现一个接口:Serializable,否则会抛出:NotSerializableException 
Serializable:该接口其实就是一个没有方法的标记接口。 
   用于给类指定一个UID。该UID是通过类中的可序列化成员的数字签名算出来的一个long型的值。 
   只要是这些成员没有变化,那么该值每次运算都一样。 
   该值用于判断被序列化的对象和类文件是否兼容。 
   如果被序列化的对象需要被不同的类版本所兼容,可以在类中自定义UID. 
   定义方式:static final long serialVersionUID =42L; 
注意:对应静态的成员变量,不会被序列化。 
   对应非静态也不想被序列化的成员而言,可以通过transient关键字修饰。 
通常,这两个对象成对使用。 
Java对象序列化

  • 操作基本数据类型的流对象 
    DataInputStream 
     DataInputStream(InputStream); 
    方法:int readInt():一次读取四个字节,并将其转成int值。 
       boolean readBoolean():一次读取一个字节。 
       short readShort(); 
       long readLong(); 
    剩下的数据类型一样。 
    String readUTF():按照utf-8修改版读取字符。注意,它只能读writeUTF()写入的字符数据。 
    DataOutputStream 
      DataOutputStream(OutputStream); 
    方法:writeInt(int):一次写入四个字节。 
    注意和write(int)不同。write(int)只将该整数的最低一个8位写入,剩余三个8位丢弃。 
       writeBoolean(boolean); 
       writeShort(short); 
       writeLong(long); 
    剩下的数据类型一样。 
    writeUTF(String):按照utf-8修改版将字符数据进行存储,只能通过readUTF读取。 
    通常只要操作基本数据类型的数据。就需要通过DataStram进行包装。 
    通常成对使用。

  • 操作数组的流对象 
    1.操作字节数组 
      ByteArrayInputStream 
      ByteArrayOutputStream 
        toByteArray(); 
        toString(); 
        writeTo(OutputStream); 
    2.操作字符数组 
      CharArrayReader 
      CharArrayWriter 
    对于这些流,源是内存,目的也是内存。 
    而且这些流并未调用系统资源,使用的就是内存中的数组,所以这些在使用的时候不需要close. 
    操作数组的读取流在构造时,必须要明确一个数据源。所以要传入相对应的数组。 
    对于操作数组的写入流,在构造函数可以使用空参数。因为它内置了一个可变长度数组作为缓冲区。 
    这几个流的出现其实就是通过流的读写思想在操作数组。 
    类似的对象同理:StringReader,StringWriter.

编码转换

在io中涉及到编码转换的流是转换流和打印流。 
但是打印流只有输出。 
在转换流中是可以指定编码表的。默认情况下,都是本机默认的码表,GBK. 
System.getProperty("file.encoding"); 
常见码表: 
ASCII:美国标准信息交换码,使用的是1个字节的7位来表示该表中的字符。 
ISO8859-1:拉丁码表,使用1个字节来表示。 
GB2312:简体中文码表 
GBK:简体中文码表,比GB2312融入更多的中文文件和符号。 
UTF-8:对unicode进行优化,每一个字节都加入了标识头。 
编码转换:字符串>字节数组:编码,通过getBytes(charset); 
字节数组>字符串:解码,通过String类的构造函数,String(byte[],charset); 
如果编错了,没救!解错了有可能还有救!

  1. String s = "你好";
  2. byte[] b = s.getBytes("GBK");//编码
  3. String s1 = new String(b,"iso8859-1");//解码
  4. System.out.println(s1);//????
  5. //想要还原
  6. //对s1先进行一次解码码表的编码,获取原字节数据,
  7. //然后在对原字节数据进行指定编码表的解码
  8. byte[] b1 = s1.getBytes("iso8859-1");
  9. String s2 = new String(b1,"gbk");
  10. System.out.println(s2);//你好

这种情况在tomcat服务器上会出现,老版本的tomcat默认是iso8859-1,tomcat8是utf-8 
对于post提交,request.setCharacterEncoding("utf-8");

代码案例

  1. package com.xue.javaIO;
  2. import java.io.*;
  3. import java.nio.charset.Charset;
  4. import java.util.Scanner;
  5. /**
  6. * Created by xue on 2016-10-11.
  7. */
  8. public class TestJavaIO {
  9. public static void main(String[] args) {
  10. TestJavaIO testJavaIo = new TestJavaIO();
  11. //testJavaIo.testWordByte();
  12. //testJavaIo.testFileInputStream();
  13. //testJavaIo.testFileReader();
  14. //testJavaIo.testFileOutputStream();
  15. //testJavaIo.testFileOutputStream02();
  16. //testJavaIo.testFileWriter();
  17. //testJavaIo.testA001();
  18. //testJavaIo.testA002();
  19. /* String a = readTextFile("D:\\002.txt",Charset.forName("utf-8"));
  20. System.out.println(a);
  21. a = readTextFile("C:\\00.txt",Charset.forName("GBK"));
  22. System.out.println(a);*/
  23. }
  24. /**
  25. * 测试一个汉字占几个字节
  26. * 这个小方法别看这么简单,我测试遇到过很多以外的情况,导致不能得出正确结果
  27. * 经过调试都是由于编码转换乱码引起。
  28. */
  29. public void testWordByte(){
  30. /**start测试一个汉字占几个字节 **/
  31. String s = "火";
  32. String s2 = "火";
  33. byte[] bs = null,bs2 = null;
  34. try {
  35. bs = s.getBytes("UTF-8");
  36. bs2 = s2.getBytes("GBK");
  37. } catch (UnsupportedEncodingException e) {
  38. e.printStackTrace();
  39. }
  40. System.out.println("UTF-8:" + bs.length);
  41. System.out.println("GBK:" + bs2.length);
  42. System.out.println("******************************");
  43. //通过读入文件来测试,准备两个文件,分别用GBK和UTF-8保存一个中文汉字
  44. File file = new File("D:\\002.txt");
  45. System.out.println("UTF-8:" + file.length());
  46. file = new File("GBK:" + "C:\\00.txt");
  47. System.out.println(file.length());
  48. /**end测试一个汉字占几个字节 **/
  49. }
  50. /**
  51. * 字节流读取文件数据
  52. * 这种读取方式无法读取中文,会有乱码
  53. */
  54. public void testFileInputStream(){
  55. int b;
  56. FileInputStream in = null;
  57. try {
  58. in = new FileInputStream("D:\\webpro\\java001.java");
  59. // 说明:如果文件中有汉字,汉字占两个字节,回车符也是占两个字节的.
  60. } catch (FileNotFoundException e) {
  61. e.printStackTrace();
  62. System.out.println("找不到指定文件");
  63. System.exit(-1);
  64. }
  65. try {
  66. long num = 0;
  67. while ((b = in.read()) != -1) {
  68. System.out.print((char) b);
  69. num++;
  70. }
  71. in.close();
  72. System.out.println();
  73. System.out.println("共读取了" + num + "个字节");
  74. } catch (IOException e) {
  75. e.printStackTrace();
  76. System.out.println("文件读取错误");
  77. System.exit(-1);
  78. }
  79. }
  80. /**
  81. * 字符流读取文件
  82. */
  83. public void testFileReader(){
  84. File f = new File("D:\\webpro\\java001.java");
  85. FileReader reader = null;
  86. System.out.println("以字符为单位读取文件");
  87. try {
  88. reader = new FileReader(f);
  89. int tempchar;
  90. while((tempchar = reader.read()) != -1){
  91. System.out.print((char)tempchar);
  92. }
  93. } catch (FileNotFoundException e) {
  94. e.printStackTrace();
  95. System.out.println("文件没有找到");
  96. }catch(IOException e){
  97. e.printStackTrace();
  98. }finally{
  99. if(reader != null){
  100. try {
  101. reader.close();
  102. } catch (IOException e) {
  103. e.printStackTrace();
  104. System.out.println("输出流异常");
  105. }
  106. }
  107. }
  108. }
  109. /**
  110. * 字节流复制文件
  111. */
  112. public void testFileOutputStream(){
  113. int b;
  114. FileInputStream in = null;
  115. FileOutputStream out = null;
  116. try {
  117. in = new FileInputStream("D:/webpro/java001.java");
  118. out = new FileOutputStream("D:/webpro/java001fz.java");
  119. while((b=in.read())!=-1){
  120. out.write(b);
  121. }
  122. in.close();
  123. out.close();
  124. } catch (FileNotFoundException e) {
  125. e.printStackTrace();
  126. System.out.println("找不到指定文件");
  127. System.exit(-1);
  128. }catch(IOException e){
  129. System.out.println("文件复制错误");
  130. System.exit(-1);
  131. }
  132. System.out.println("文件已复制");
  133. }
  134. /**
  135. * 字节流复制文件
  136. * 这种方式的好处在于避免了对内存多次读取
  137. */
  138. public void testFileOutputStream02(){
  139. FileInputStream fis = null;
  140. FileOutputStream fos = null;
  141. File srcFile = new File("D:/webpro/java001.java");
  142. File copyFile = new File("D:/webpro/java001fz.java");
  143. try {
  144. fis = new FileInputStream(srcFile);
  145. fos = new FileOutputStream(copyFile);
  146. byte b[] = new byte[1024];
  147. int count = 0;
  148. while((count = fis.read(b))>0){
  149. fos.write(b, 0, count);
  150. }
  151. fis.close();fos.close();
  152. System.out.println("文件已复制");
  153. } catch (FileNotFoundException e) {
  154. e.printStackTrace();
  155. System.out.println("文件没有找到");
  156. }catch(IOException e){
  157. e.printStackTrace();
  158. System.out.println("文件读取出现问题");
  159. }
  160. }
  161. /**
  162. * 字符流复制文件
  163. */
  164. public void testFileWriter(){
  165. File file = new File("D:/webpro/java001.java");
  166. File copyFile = new File("D:\\webpro\\java001fz001.txt");
  167. FileReader fr = null;
  168. BufferedReader br = null;//构建一个字符输入流
  169. FileWriter fw = null;
  170. BufferedWriter bw = null;
  171. String message="";
  172. try {//创建输入流实例
  173. fr = new FileReader(file);
  174. br = new BufferedReader(fr);
  175. fw = new FileWriter(copyFile);
  176. bw = new BufferedWriter(fw);
  177. while((message = br.readLine())!=null){
  178. bw.write(message);
  179. bw.newLine();//换行
  180. System.out.println("message="+message);
  181. }
  182. bw.flush();
  183. fr.close();br.close();
  184. fw.close();bw.close();
  185. System.out.println("文件已复制");
  186. } catch (FileNotFoundException e) {
  187. e.printStackTrace();
  188. System.out.println("文件没有找到");
  189. }catch(IOException e){
  190. System.out.println("文件读取出现问题");
  191. }
  192. }
  193. /**
  194. * 从控制台输入保存文件
  195. * exit退出
  196. *第一种方法
  197. */
  198. public void testA001(){
  199. InputStreamReader isr = new InputStreamReader(System.in);
  200. BufferedReader br = new BufferedReader(isr);
  201. File file = new File("D:\\webpro\\M.txt");
  202. try {
  203. FileWriter fw = new FileWriter(file);
  204. BufferedWriter bw = new BufferedWriter(fw);
  205. int count = 0;
  206. int result;
  207. String message;
  208. System.out.println("Please input message=");
  209. do{
  210. message = br.readLine();
  211. message = (++count)+":"+message;
  212. bw.write(message);
  213. bw.newLine();
  214. result = message.indexOf("exit");
  215. }while(result == -1);
  216. bw.flush();
  217. bw.close();
  218. } catch (IOException e) {
  219. e.printStackTrace();
  220. }
  221. }
  222. /**
  223. * 第二种控制台输出
  224. */
  225. public void testA002(){
  226. File file = new File("D:\\webpro\\M.txt");
  227. Scanner input = new Scanner(System.in);
  228. int count = 0;
  229. int result;
  230. try {
  231. FileWriter fw = new FileWriter(file);
  232. BufferedWriter bw = new BufferedWriter(fw);
  233. String message;
  234. System.out.println("输入字符串");
  235. do{
  236. message = input.next();
  237. message = (++count)+":"+message;
  238. bw.write(message);
  239. bw.newLine();
  240. result = message.indexOf("exit");
  241. }while(result == -1);
  242. bw.flush();
  243. bw.close();
  244. } catch (IOException e) {
  245. e.printStackTrace();
  246. }
  247. }
  248. /**
  249. * 根据指定编码读取纯文本文件的内容
  250. * @param fileName 文件名
  251. * @param charset 编码
  252. * @return 文件内容
  253. */
  254. public static String readTextFile(String fileName,Charset charset){
  255. InputStreamReader in = null;
  256. File file = new File(fileName);
  257. StringBuffer sb = new StringBuffer((int) file.length());
  258. try{
  259. in = new InputStreamReader(new FileInputStream(file),charset);
  260. char[] b = new char[1024];
  261. int n = -1;
  262. while((n=in.read(b))!=-1){
  263. sb.append(b,0,n);
  264. }
  265. }catch(Exception e){
  266. e.printStackTrace();
  267. }finally{
  268. try {
  269. if (in != null) {
  270. in.close();
  271. }
  272. }catch(IOException e){
  273. e.printStackTrace();
  274. }
  275. }
  276. return sb.toString();
  277. }
  278. }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章