导航

  18天-06-IO流
  字节流和字符流

 
  字节流两个基类
InputStream,FileInputStream,BufferedInputStream
OutputStream,FileOutputStream,BufferedOutputStream
PrintStream (打印字节流,带编码格式)
  字符流两个基类
Reader,FileReader,BufferedReader,InputStreamReader(字节流转换为字符流读取,带编码格式)
Writer,FileWriter,BufferedWriter,OutputStreamWriter(字节流转换为字符流写入,带编码格式)
PrintWriter (打印字符流,带编码格式)
流操作规律讲解
源设备:   键盘 System.in;    硬盘 FileStream;      内存 ArrayStream;
目的设备:  控制台 System.out;   硬盘 FileStream;     内存 ArrayStream;

字符流:(只能用于文本文件)
操作FileWriter写入文件
1.创建一个FileWriter对象,该对象一被初始化就必须要明确被操作的文件。而且该文件会被创建到指定目录下。如果该目录下已经有同名文件,将被覆盖。其实该步骤就是在明确数据要存放的目的地。
FileWriter fw=new FileWriter("filePath",true);  //ture代表不覆盖已有文件,并在已有文件末尾添加数据
2.调用write()方法,将字符串写入到流中。
3.刷新流对象中缓冲的数据到文件里。
4.关闭流资源。在关闭流前,会刷新一次内部缓冲区的数据。

操作FileReader读取文件
1.创建一个文件读取流对象,和指定名称的文件。要保证该文件是已经存在,如果不存在,会发生FileNotFoundException异常。 FileReader fr=new FileReader(FilePath);
2.调用读取流对象的read方法。read();一次读取一个字符,而且会自动往下读取。
第二种通过字符数组进行读取。
定义一个数组,用于存储读取到的字符,读取read(char[])返回的是读取到的字符个数。

复制的原理:其实就是将一个地方的文件数据存储到另外一个指定的地方。

字符流拷贝文件示例:

 1 /**
 2 * 拷贝文件
 3 *
 4 * @param sourceFile 源文件路径
 5 * @param targetFile 目标文件路径
 6 */
 7 public static void copyFile(String sourceFile, String targetFile)
 8 {
 9     FileReader fr = null;   //读文件
10     FileWriter fw = null;   //写文件
11     try
12     {
13         fr = new FileReader(sourceFile);
14         fw = new FileWriter(targetFile);
15 
16         char[] temp = new char[1024];
17         int length = 0;
18         while ((length = fr.read(temp)) != -1)
19         {
20             //length是每次读取文件时返回已读取的字符数量,-1为文件读取完毕
21             fw.write(temp, 0, length);
22         }
23     }
24     catch (IOException e)
25     {
26         e.printStackTrace();
27     }
28     finally
29     {
30         try
31         {
32             if (fr != null)
33             {
34                 fr.close();
35             }
36         }
37         catch (IOException e)
38         {
39             e.printStackTrace();
40         }
41         try
42         {
43             if (fw != null)
44             {
45                 fw.close();
46             }
47         }
48         catch (IOException e)
49         {
50             e.printStackTrace();
51         }
52     }
53 }
字符流拷贝文件

  缓冲区 BufferedWriter,BufferedReader

  缓冲区的出现是为了提高流的操作效率。所以在创建缓冲区之前,必须要先有流对象。
1.创建流对象(FileReader,FileWriter);
2.将需要提高效率的流对象作为参数传递给缓冲区的构造函数即可;
3.读写数据,并即时刷新缓冲区数据;
4.关闭缓冲区,流对象,缓冲区关闭时会检测一下数据是否写完,所以要后关闭流对象。

字符读取流缓冲区:该缓冲区提供了一个一次读一行的方法readLine(),当返回null时,表示读取到文件末尾
readLine方法返回的时候只返回回车符之前的数据内容,并不返回包含回车符
readLine原理:无论是读一行还是读取多个字符,其实最终都是在硬盘上一个一个读取,所以最终使用的还是read()方法一次读一个。
缓冲区提供了一个跨平台的换行符:newLine();

缓冲区拷贝文件示例:

 1 /**
 2 * 缓冲区拷贝文件
 3 *
 4 * @param sourceFile 源文件路径
 5 * @param targetFile 目标文件路径
 6 */
 7 public static void copeFileByBuffer(String sourceFile, String targetFile)
 8 {
 9     BufferedReader bufr = null;
10     BufferedWriter bufw = null;
11     try
12     {
13         bufr = new BufferedReader(new FileReader(sourceFile));
14         bufw = new BufferedWriter(new FileWriter(targetFile));
15         String temp = null;
16         while ((temp = bufr.readLine()) != null)
17         {
18             bufw.write(temp);
19             bufw.newLine();
20             bufw.flush();
21         }
22     }
23     catch (FileNotFoundException e)
24     {
25         e.printStackTrace();
26     }
27     catch (IOException e)
28     {
29         e.printStackTrace();
30     }
31     finally
32     {
33         try
34         {
35             if (bufr != null)
36             {
37                 bufr.close();
38             }
39         }
40         catch (IOException e)
41         {
42             e.printStackTrace();
43         }
44         try
45         {
46             if (bufw != null)
47             {
48                 bufw.close();
49             }
50         }
51         catch (IOException e)
52         {
53             e.printStackTrace();
54         }
55     }
56 }
缓冲区拷贝文件

  装饰设计模式:当想要对已有的对象进行功能增强时,可以定义类将已有对象传入,基于已有的功能,并提供增强功能。那么自定义的该类称为装饰类。

装饰类通常会通过构造方法接收被装饰的对象,并基于被装饰的对象的功能,提供更强的功能
装饰类因为增强了已有的对象,具备的功能和已有的是相同的,只不过提供了更强的功能,所以装饰类和被装饰类通常是都属于一个体系中的。
装饰模式比继承要灵活,避免了继承体系臃肿,而且降低了类与类之间的关系

  字节流:
int read():读取时返回int类型,会把byte字节提升为int类型,提升方式是byte&0xff,在写入时也做了反向操作强转为byte类型。防止类型提升导致原本不是 -1 的数据在类型提升后变成 -1。

字节流复制文件示例:

 1 /**
 2 * 字节流方式拷贝文件
 3 *
 4 * @param sourceFile 源文件路径
 5 * @param targetFile 目标文件路径
 6 */
 7 public static void copyFileByByte(String sourceFile, String targetFile)
 8 {
 9     BufferedInputStream bfis = null;
10     BufferedOutputStream bfos = null;
11     try
12     {
13         bfis = new BufferedInputStream(new FileInputStream(sourceFile));
14         bfos = new BufferedOutputStream(new FileOutputStream(targetFile));
15         byte[] temp = new byte[1024*1024];
16         int length;
17         while ((length = bfis.read(temp)) != -1)
18         {
19             bfos.write(temp, 0, length);
20         }
21     }
22     catch (FileNotFoundException e)
23     {
24         e.printStackTrace();
25     }
26     catch (IOException e)
27     {
28         e.printStackTrace();
29     }
30     finally
31     {
32         try
33         {
34             if (bfis != null)
35             {
36                 bfis.close();
37             }
38         }
39         catch (IOException e)
40         {
41             e.printStackTrace();
42         }
43         try
44         {
45             if (bfos != null)
46             {
47                 bfos.close();
48             }
49         }
50         catch (IOException e)
51         {
52             e.printStackTrace();
53         }
54     }
55 }
字节流方式拷贝文件

  19天-15-IO流读取键盘录入

System.out:  对应的是标准输出设备,控制台。
System.in:    对应的标准输入设备,键盘。

流操作键盘录入示例:

 1 /**
 2  * 键盘录入,over表示结束
 3  */
 4 public static void keyboardEntry() throws IOException
 5 {
 6     BufferedReader br = null;
 7     BufferedWriter bw = null;
 8     try
 9     {
10         br = new BufferedReader(new InputStreamReader(System.in));
11         bw = new BufferedWriter(new OutputStreamWriter(System.out));
12         String temp;
13         while ((temp = br.readLine()) != null)
14         {
15             if ("over".equals(temp))
16             {
17                 break;
18             }
19             bw.write(temp);
20             bw.newLine();
21             bw.flush();
22         }
23     }
24     finally
25     {
26         if (br != null)
27         {
28             br.close();
29         }
30         if (bw != null)
31         {
32             bw.close();
33         }
34     }
35 }
流操作键盘录入

  流操作的基本规律:

通过三方面来确定:
1.明确源和目的
:输入流,InputStream,Reader
目的:输出流,OutputStream,Writer
2.操作的数据是否是纯文本
是:字符流 否:字节流
3.当体系明确后,再明确要使用哪个具体的对象
通过设备来区分:源设备:内存,硬盘,键盘;目的设备:内存,硬盘,控制台

  注:转换流:主要用于字节流与字符流的转换和在涉及字符编码时指定编码。

  20天-01-IO流File类
File类:用来将文件或者文件夹封装成对象,方便对文件与文件夹的属性信息进行操作,File对象可以作为参数传递给流的构造函数。
File类常用方法:
1.创建 boolean createNewFile();//在指定位置创建文件,如果该文件已经存在,则不创建文件返回false
      boolean mkdir();//创建文件夹   boolean mkdirs();//创建多级文件夹
2.删除 boolean delete();//删除失败返回false     void deleteOnExit();//在程序退出时删除指定文件
3.判断 exists();//文件是否存在      isHidden();//判断是否是隐藏文件    canExecute();//判断文件是否可执行
    isFile();//是否是文件   isDirectory();//判断是否是目录       isAbsolute();//判断是否是绝对路径
  注:在判断文件对象是否是文件或者目录时,必须要先判断文件对象封装的内容是否存在,通过exists()判断
4.获取 getName();//获取文件名     getPath();//获取文件封装路径    getParent();//获取文件封装父目录
    getAbsolutePath();//获取文件绝对路径   lastModified();//获取文件最后修改时间    length();//获取文件大小

删除文件操作示例:

 1 /**
 2 * 递归遍历删除所有文件
 3 *
 4 * @param file 文件路径
 5 */
 6 public static void deleteFile(File file)
 7 {
 8     if (!file.exists())
 9     {
10         return;
11     }
12     File[] fileList = file.listFiles();
13     for (File f : fileList)
14     {
15         if (f.isDirectory())
16         {
17             deleteFile(f);
18         }
19         else
20         {
21             f.delete();
22         }
23     }
24     file.delete();
25 }
递归遍历删除所有文件

  20天-11-IO流Properties类

  Properties是hashtable的子类,也就是说它具备map集合的特点,而且它里面存储的键值对都是字符串,是集合和IO技术相结合的集合容器。在加载数据时,需要数据有固定的格式:键=值

  打印流:该流提供了打印方法,可以将各种数据类型的数据都原样打印。
  字节流打印流:PrintStream 构造函数可以接收的参数类型:
1.file对象,File;2.字符串路径,String;3.字节输出流,OutputStream

  字符打印流:PrintWriter 构造函数可以接收的参数类型:
1.file对象,File;2.字符串路径,String;3.字节输出流,OutputStream;4.字符输出流,Writer

序列流SequenceInputStream:将多个数据源的文件流合并到一起。
文件合并流示例:

 1 /**
 2 * 把多个文件里面的数据合并到一个文件里面
 3 *
 4 * @param targetFile  合入文件
 5 * @param sourceFiles 多个原文件
 6 * @throws IOException
 7 */
 8 public static void combineTFiles(String targetFile, String... sourceFiles)
 9     throws IOException
10 {
11     ArrayList<FileInputStream> v = new ArrayList<FileInputStream>();
12     for (String s : sourceFiles)
13     {
14         v.add(new FileInputStream(s));
15     }
16     Iterator<FileInputStream> it = v.iterator();
17     Enumeration<FileInputStream> en = new Enumeration<FileInputStream>()
18     {
19         @Override
20         public boolean hasMoreElements()
21         {
22             return it.hasNext();
23         }
24 
25         @Override
26         public FileInputStream nextElement()
27         {
28             return it.next();
29         }
30     };
31     SequenceInputStream sis = new SequenceInputStream(en);
32     FileOutputStream fos = new FileOutputStream(targetFile);
33     byte[] bf = new byte[1024];
34     int length = 0;
35     while ((length = sis.read(bf)) != -1)
36     {
37         fos.write(bf, 0, length);
38     }
39     sis.close();
40     fos.close();
41 }
文件合并

  对象流ObjectInputStream,ObjectOutputStream:被操作的对象需要实现Serializable(标记接口),对象中的transient和static修饰的成员变量不会被读取和写入

实现Serializable接口的类,需要做一个标记方便后期修改 static final long serialVersionUID = 10L;

  管道流PipedInputStream,PipedOutputStream:输入输出可以直接进行连接,通过结合多线程使用,先把数据写入管道再从管道里面读取出来。
管道流示例:

 1 class Demo
 2 {
 3     public static void main(String[] args)
 4     {
 5         //exit为退出管道示例命令
 6         PipedInputStream pis = new PipedInputStream();
 7         PipedOutputStream pos = new PipedOutputStream();
 8         //读取管道流连接写入管道流,当有数据写入管道时,读取管道线程自动读入数据
 9         pis.connect(pos);
10         new Thread(new ReadPiped(pis)).start();
11         new Thread(new WritePiped(pos, System.in)).start();
12     }
13 }
14 class ReadPiped implements Runnable
15 {
16     private PipedInputStream in;
17     public ReadPiped(PipedInputStream in)
18     {
19         this.in = in;
20     }
21     @Override
22     public void run()
23     {
24         try
25         {
26             byte[] bt = new byte[1024];
27             int len = 0;
28             while (true)
29             {
30                 len = in.read(bt);
31                 String str = new String(bt, 0, len);
32                 if ("exit".equals(str))
33                 {
34                     break;
35                 }
36                 System.out.println(str);
37             }
38             in.close();
39         }
40         catch (IOException e)
41         {
42             e.printStackTrace();
43         }
44     }
45 }
46 class WritePiped implements Runnable
47 {
48     private PipedOutputStream out;
49     private BufferedReader bf = null;
50     public WritePiped(PipedOutputStream out, InputStream in)
51     {
52         this.out = out;
53         bf = new BufferedReader(new InputStreamReader(in));
54     }
55     @Override
56     public void run()
57     {
58         try
59         {
60             String temp;
61             while (true)
62             {
63                 temp = bf.readLine();
64                 out.write(temp.getBytes());
65                 if ("exit".equals(temp))
66                 {
67                     break;
68                 }
69             }
70             bf.close();
71             out.close();
72         }
73         catch (IOException e)
74         {
75             e.printStackTrace();
76         }
77     }
78 }
键盘录入管道流

  随机访问文件RandomAccessFile:该类不算IO体系中的子类,而是直接继承自Object。但是它是IO包中成员,因为它具备读和写功能,内部封装了一个数组,而且通过指针对数组的元素进行操作,可以通过getFilePointer获取指针位置,同时也可以通过seek修改指针的位置。其实内部完成读写的原理是内部封装了字节输入流和输出流。

  如果模式为只读 r,不会创建文件,会去读取一个已存在的文件,如果文件不存在,则会抛异常。
  如果模式为读写 rw,操作的文件不存在,会自动创建,如果文件存在则不会覆盖。
  特点:可以随机的读写文件里面的数据,用于多线程把数据分段存到一个文件里(多线程下载)
  void seek(long pos);  //设置指针位置,方便修改指定位置数据
  int skipBytes(int n);  //指针向前跳n个字节,不能向后跳

  基本数据类型流DataInputStream,DataOutputStream:可以用于操作基本数据类型的数据的流对象

  字节数组流
ByteArrayInputStrean:在构造函数里,需要接收数据源,而且数据源是一个字节数组
ByteArrayOutputStream:在构造函数里,不用定义数据目的,该对象内部已经封装了可变长度的字节数组,就是数据目的地。
因为这两个流对象都操作的数组,并没有使用系统资源。所以不用进行close关闭。
void writeTo(OutputStream out);  //把数组里面的数据写入流里面
  字符数组流   CharArrayReader,CharArrayWriter
  字符串流    StringReader,StringWriter

  21天-07-字符编码
ASCII:美国标准信息交换码,用一个字节的7位可以表示
ISO8859-1:拉丁码表,欧洲码表,用一个字节的8位表示
GB2312:中国的中文编码表
GBK:中国的中文编码表升级,融合了更多的中文文字字符,用两个字节的8位表示,每个字节前面第一位是标识
Unicode:国际标准码,融合了多种文字,所有文字都用两个字节来表示,Java语言使用的就是Unicode
UTF-8:最多用三个字节来表示一个字符

编码:字符串变成字节数组
String-->byte[];   str.getBytes(charsetName);
解码:字节数组变成字符串
byte[]-->String;   new String(byte[],charsetName);
编码转换示例:

 1 import java.io.IOException;
 2 import java.io.UnsupportedEncodingException;
 3 class Demo
 4 {
 5     public static void main(String[] args)throws IOException
 6     {
 7         encodeShow("test", "gbk");
 8         encodeShow("test", "utf-8");
 9         encodeShow("test", "GB2312");
10         encodeShow("test", "ascii");
11         encodeShow("test", "unicode");
12         encodeShow("test", "ISO8859-1");
13     }
14     public static void encodeShow(String param, String charset)
15         throws UnsupportedEncodingException
16     {
17         String str = param;
18         sop("source string: " + str + "     encode is: " + charset);
19         byte[] bt = str.getBytes(charset);
20         for (byte b : bt)
21         {
22             sop("encode binary: " + Integer.toBinaryString(b));
23         }
24         String t = new String(bt, charset);
25         sop("encode string: " + t);
26         sop("");
27     }
28     private static void sop(Object obj)
29     {
30         System.out.println(obj);
31     }
32 }
编码转换

换行符: Linux:\n    windows:\r\n    Mac:\r