出差的这几天又开始将IO流的一些内容进行整理学习

IO流

1、简介

1.1、Java IO原理

I/O是Input/Outuput的缩写,用于处理设备之间的数据传输,如读/写文件,网络通讯等

Java程序中,对于数据的输入/输出操作以“流(Stream)”的方式进行

Java.io包下提供了各种“流”类和接口,用于获取不同种类的数据,并通过标准的方法输入或输出数据

read、write

站在程序的角度,对应流入程序和流出程序

输入input:读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中

输出output:将程序(内存)数据输出到磁盘、光盘等存储设备中

1.2、流的分类

按操作数据单位不同分为:字节流(8bit)【适合图片、视频等非文本数据】、字符流(16bit)【适合文本】

按数据流的流向不同分为:输入流、输出流

按流的角色的不同分为:节点流(直接进行传输,直接作用在文件)【水管】、处理流(对流进行控制处理,直接作用在流)【水泵、热水器】

(抽象基类)字节流字符流
输入流 InputStream Reader
输出流 OutputStream Writer

IO流涉及到40多个类,都是从这4个抽象基类派生的

由这四个类派生出来的子类名称都是以其父类名作为子类名后缀

1.3、IO流体系

分类字节输入流字节输出流字符输入流字符输出流流类型
抽象基类 InputStream OutputStream Reader Writer  
访问文件(节点流) FileInputStream FileOutputStream FileReader FileWriter 节点流
访问数组 ByteArrayInputStream ByteArrayOutputStream CharArrayReader CharArrayWriter 节点流
访问管道 PipedInputStream PipedOutputStream PipedReader PipedWriter 节点流
访问字符串     StringReader StringWriter 节点流
缓冲流 BufferedInputStream BufferedOutputStream BufferedReader BufferedWriter 处理流
转换流     InputStreamReader OutputStreamWriter 处理流
对象流 ObjectInputStream ObjectOutputStream     处理流
过滤流 FilterInputStream FilterOutputStream FilterReader FilterWriter 处理流
打印流   PrintStream   PrintWriter 处理流
推回输入流 PushbackInputStream   PushbackReader   处理流
数据流 DataInputStream DataOutputStream     处理流

一、流的分类:

1.操作数据单位:字节流、字符流

2.数据的流向:输入流、输出流

3.流的角色:节点流、处理流

二、流的体系结构

抽象基类节点流(文件流)效率较差缓冲流(处理流的一种)效率较高
InputStream FileInputStream(read(byte[] buffer)) BufferedInputStream(read(byte[] buffer))
OutputStream FileOutputStream(write(byte[] byte,0,len)) BufferedOutputStream(write(byte[] byte,0,len)/flush())
Reader FileReader(read(char[] cbuf)) BufferedReader(read(char[] cbuf)/readline())
Writer FileWriter(write(char[] cbuf,0,len)) BufferedWriter(write(char[] cbuf,0,len)/flush())

2、字符流

FileReader & FileWriter

需要通过try...catch...finally...处理异常,不要使用throws IOException

  1  package com.zhou.io;
  2   3  import java.io.*;
  4   5  /**
  6   * @Name FileReaderWriterTest
  7   * @Description 测试FileReader & FileWriter
  8   *
  9   * @Author 88534
 10   * @Date 2021/8/29 13:25
 11   */
 12  public class FileReaderWriterTest {
 13      public static void main(String[] args) {
 14          /**
 15            * 将hello.txt文件内容读入程序中,并输出到控制台
 16            * 输出:helloworld
 17            * 说明点:
 18            * 1.read()的理解:返回读入的一个字符,如果达到文件末尾,返回-1
 19            * 2.异常的处理:为了保证流资源一定可以执行关闭操作,需要使用try-catch-finally
 20            * 3.读入的文件一定要存在,否则就会报FileNotFoundException
 21            */
 22      //    FileReader fr = null;
 23      //    try {
 24      //
 25      //        //1.实例化File类的对象,指明要操作的文件
 26      //        File file = new File("hello.txt");
 27      //        //寻找路径
 28      //        //System.out.println(file.getAbsolutePath());
 29      //        //输出:C:\Users\88534\Desktop\Java入门\IOStream\hello.txt
 30      //        //2.提供具体的流
 31      //        fr = new FileReader(file);
 32      //        //3.数据的读入
 33      //        //read():返回读入的一个字符,如果达到文件末尾,返回-1
 34      //        //int data = fr.read();
 35      //        //while (data!=-1){
 36      //        //    System.out.print((char) data);
 37      //        //    data = fr.read();
 38      //        //}
 39      //        //简化操作,语法上针对上述的修改
 40      //        int data;
 41      //        while ((data = fr.read())!=-1){
 42      //            System.out.print((char) data);
 43      //        }
 44      //    } catch (IOException e) {
 45      //        e.printStackTrace();
 46      //    } finally {
 47      //        //4.流的关闭操作(重要)
 48      //        try {
 49      //            if (fr != null) {
 50      //                fr.close();
 51      //            }
 52      //        } catch (IOException e) {
 53      //            e.printStackTrace();
 54      //        }
 55      //    }
 56  57          /**
 58           * 对read()操作升级,使用read的重载方法
 59           */
 60          //FileReader fr = null;
 61          //
 62          //try {
 63          //    //1.File类的实例化
 64          //    File file = new File("hello.txt");
 65          //    //2.FileReader流的实例化
 66          //    fr = new FileReader(file);
 67          //    //3.读入的操作
 68          //    //read(char[] cbuf):返回每次读入cbuf数组中的字符个数
 69          //    char[] cbuf = new char[5];
 70          //    int len;
 71          //    while (((len = fr.read(cbuf))!= -1)) {
 72          //        //错误写法,只是将原有元素覆盖,输出helloworld123ld
 73          //        //for (int i = 0; i < cbuf.length; i++) {
 74          //        //    System.out.print(cbuf[i]);
 75          //        //}
 76          //        //方式一
 77          //        for (int i = 0; i < len; i++) {
 78          //            System.out.print(cbuf[i]);
 79          //        }
 80          //        //方式二
 81          //        //错误写法,结果同上
 82          //        //String str = new String(cbuf);
 83          //        //System.out.print(str);
 84          //        String str = new String(cbuf,0,len);
 85          //        System.out.print(str);
 86          //    }
 87          //} catch (IOException e) {
 88          //    e.printStackTrace();
 89          //} finally {
 90          //    //4.资源的关闭
 91          //    try {
 92          //        if (fr != null) {
 93          //            fr.close();
 94          //        }
 95          //    } catch (IOException e) {
 96          //        e.printStackTrace();
 97          //    }
 98          //}
 99 100          /**
101           * 从内存中写出数据到硬盘的文件里
102           * 说明:
103           * 1.输出操作,对应的file可以不存在,并不会报异常
104           * 2.如果不存在,在输出的过程中,会自动创建此文件
105           *   如果存在,如果流使用的构造器FileWriter(file,false)/默认FileWriter(file):对原有文件的覆盖
106           *   如果FileWriter(file,true):不会对原有文件覆盖,而是在原有文件基础上追加内容
107           */
108          //  //1.提供File类的对象,指明写出到达的文件
109          //FileWriter fw = null;
110          //try {
111          //    File file = new File("hello1.txt");
112          //    //2.提供FilterWriter的对象,用于数据的写出
113          //    fw = new FileWriter(file);
114          //    //3.写出的操作
115          //    fw.write("I have a dream!\n");
116          //    fw.write("You need to have a dream!");
117          //} catch (IOException e) {
118          //    e.printStackTrace();
119          //} finally {
120          //    //4.资源的关闭
121          //        try {
122          //            if (fw != null) {
123          //                fw.close();
124          //            }
125          //        } catch (IOException e) {
126          //            e.printStackTrace();
127          //        }
128          //}
129 130          /**
131           * 复制文件,写入后写出
132           */
133          FileReader fr = null;
134          FileWriter fw = null;
135          try {
136              //1.创建File类的对象,指明读入和写出的文件
137              File srcFile = new File("hello.txt");
138              File destFile = new File("hello2.txt");
139              //2.创建输入流和输出流的对象
140              fr = new FileReader(srcFile);
141              fw = new FileWriter(destFile);
142              //3.数据的读入和写出操作
143              char[] cbuf = new char[5];
144              //len:记录每次读入到cbuf数据中的字符个数
145              int len;
146              while((len = fr.read(cbuf))!=-1){
147                  //每次写出读入的len个字符
148                  fw.write(cbuf,0,len);
149              }
150          } catch (IOException e) {
151              e.printStackTrace();
152          } finally {
153              //4.关闭流资源
154              try {
155                  if (fw != null){
156                      fw.close();
157                  }
158              } catch (IOException e) {
159                  e.printStackTrace();
160              }
161              try {
162                  if (fr != null){
163                      fr.close();
164                  }
165              } catch (IOException e) {
166                  e.printStackTrace();
167              }
168          }
169      }
170  }

 

不能使用字符流处理图片、视频等字节数据

也不能使用字节流处理文本数据(ASCII码2字节除外,中文需3字节)

字节流能强行处理ASCII范围内的文本,但处理中文等超过2字节的会产生乱码。

字符流:.txt,.java,.c,.cpp,...

字节流:.jpg,.mp3,.mp4,.avi,.doc,.ppt,...

3、字节流

FileInputStream & FileOutputStream

主要区别在使用的buffer为byte字节类型,文本流为char字符类型

 
 1 package com.zhou.io;
 2  3  import java.io.*;
 4  5  /**
 6   * @Name FileInputOutputStreamTest
 7   * @Description 测试FileInputStream & FileOutputStream
 8   * @Author 88534
 9   * @Date 2021/8/29 14:58
10   */
11  public class FileInputOutputStreamTest {
12      public static void main(String[] args) {
13          long start = System.currentTimeMillis();
14          copyFile("Java.jpg","Java2.jpg");
15          long end = System.currentTimeMillis();
16          System.out.println("复制操作花费的时间为:" + (end - start) + "ms");
17      }
18 19      /**
20       * 指定路径下的文件复制
21       */
22      public static void copyFile(String srcPath, String destPath){
23          /**
24           * 实现对图片的复制操作
25           */
26          FileInputStream fis = null;
27          FileOutputStream fos = null;
28          try {
29              //1.创建File类的对象,指明读入和写出的文件
30              File srcFile = new File(srcPath);
31              File destFile = new File(destPath);
32              //2.创建输入流和输出流的对象
33              fis = new FileInputStream(srcFile);
34              fos = new FileOutputStream(destFile);
35              //3.数据的读入和写出操作
36              byte[] buffer = new byte[1024];
37              //len:记录每次读入到cbuf数据中的字符个数
38              int len;
39              while((len = fis.read(buffer))!=-1){
40                  //每次写出读入的len个字符
41                  fos.write(buffer,0,len);
42              }
43              System.out.println("复制成功!");
44          } catch (IOException e) {
45              e.printStackTrace();
46          } finally {
47              //4.关闭流资源
48              try {
49                  if (fos != null){
50                      fos.close();
51                  }
52              } catch (IOException e) {
53                  e.printStackTrace();
54              }
55              try {
56                  if (fis != null){
57                      fis.close();
58                  }
59              } catch (IOException e) {
60                  e.printStackTrace();
61              }
62          }
63      }
64  }

 

4、处理流

BufferedInputStream & BufferedOutputStream

通过提供缓冲区,提高读取、写入速度

readline读取一行(读取到换行终止)

nextline换行符

 1  package com.zhou.io;
 2  3  import java.io.*;
 4  5  /**
 6   * @Name BufferedTest
 7   * @Description 缓冲流的使用
 8   * 1.缓冲流
 9   * BufferedInputStream
10   * BufferedOutputStream
11   * BufferedReader
12   * BufferedWriter
13   *
14   * 2.作用:提升流的读取、写入的速度
15   * 原因:内部提供了一个缓冲区
16   *
17   * @Author 88534
18   * @Date 2021/8/29 15:27
19   */
20  public class BufferedTest {
21      public static void main(String[] args) {
22          copyFileWithBuffered("Java.jpg","Java2.jpg");
23          copyTextFileWithBuffered("hello.txt","hello1.txt");
24      }
25 26      public static void copyFileWithBuffered(String srcPath, String destPath){
27          /**
28           * 实现非文本文件的复制
29           */
30          BufferedInputStream bis = null;
31          BufferedOutputStream bos = null;
32          try {
33              //1.创建File类的对象,指明读入和写出的文件
34              File srcFile = new File(srcPath);
35              File destFile = new File(destPath);
36              //2.创建输入流和输出流的对象
37              //2.1造节点流
38              FileInputStream fis = new FileInputStream(srcFile);
39              FileOutputStream fos = new FileOutputStream(destFile);
40              //2.2造缓冲流
41              bis = new BufferedInputStream(fis);
42              bos = new BufferedOutputStream(fos);
43              //3.数据的读入和写出操作
44              //方式一、使用char[]数组
45              byte[] buffer = new byte[1024];
46              //len:记录每次读入到cbuf数据中的字符个数
47              int len;
48              while((len = bis.read(buffer))!=-1){
49                  //每次写出读入的len个字符
50                  bos.write(buffer,0,len);
51 52                  //bos.flush();//刷新清空缓冲区
53              }
54              System.out.println("复制成功!");
55          } catch (IOException e) {
56              e.printStackTrace();
57          } finally {
58              //4.关闭流资源
59              //要求:先关闭外层的流,再关闭内层的流,与穿衣服类似,与创建过程相反
60              //关闭外层流的同时,内层流也会自动的进行关闭,关于内层流的关闭可以省略
61              try {
62                  if (bos != null){
63                      bos.close();
64                  }
65              } catch (IOException e) {
66                  e.printStackTrace();
67              }
68              try {
69                  if (bis != null){
70                      bis.close();
71                  }
72              } catch (IOException e) {
73                  e.printStackTrace();
74              }
75          }
76      }
77 78      

 

 1 public static void copyTextFileWithBuffered(String srcPath, String destPath){
 2          /**
 3           * 实现文本文件的复制
 4           */
 5          BufferedReader br = null;
 6          BufferedWriter bw = null;
 7          try {
 8              //合并简写创建文件和相应的流
 9              br = new BufferedReader(new FileReader(new File(srcPath)));
10              bw = new BufferedWriter(new FileWriter(new File(destPath)));
11              //数据的读入和写出操作
12              //方式一、使用char[]数组
13              //char[] cbuf = new char[1024];
14              ////len:记录每次读入到cbuf数据中的字符个数
15              //int len;
16              //while((len = br.read(cbuf))!=-1){
17              //    //每次写出读入的len个字符
18              //    bw.write(cbuf,0,len);
19              //
20              //    //bw.flush();//刷新清空缓冲区
21              //}
22 23              //方式二、使用String
24              String data;
25              while ((data = br.readLine())!=null){
26                  //data中不包含换行符
27                  //方法一
28                  //bw.write(data + "\n");
29                  //方法二
30                  bw.write(data);
31                  //提供换行的操作
32                  bw.newLine();
33              }
34              System.out.println("复制成功!");
35          } catch (IOException e) {
36              e.printStackTrace();
37          } finally {
38              //4.关闭流资源
39              //要求:先关闭外层的流,再关闭内层的流,与穿衣服类似,与创建过程相反
40              //关闭外层流的同时,内层流也会自动的进行关闭,关于内层流的关闭可以省略
41              try {
42                  if (bw != null){
43                      bw.close();
44                  }
45              } catch (IOException e) {
46                  e.printStackTrace();
47              }
48              try {
49                  if (br != null){
50                      br.close();
51                  }
52              } catch (IOException e) {
53                  e.printStackTrace();
54              }
55          }
56      }
57  }

 

同理,处理流就是“套接”在已有的流的基础上

5、实现图片加密操作

异或的特性,和一个数异或后,再异或一次变回原数

 1  package com.zhou.exer;
 2  3  import java.io.FileInputStream;
 4  import java.io.FileOutputStream;
 5  import java.io.IOException;
 6  7  /**
 8   * @Name PicTest
 9   * @Description 图片的加密
10   * @Author 88534
11   * @Date 2021/8/29 16:11
12   */
13  public class PicTest {
14      public static void main(String[] args) {
15          FileInputStream fis = null;
16          FileOutputStream fos = null;
17          try {
18              //解密时直接将两者路径对调即可(异或的特性)
19              //fis = new FileInputStream("Java_Secret.jpg");
20              //fos = new FileOutputStream("Java.jpg");
21              fis = new FileInputStream("Java.jpg");
22              fos = new FileOutputStream("Java_Secret.jpg");
23 24              byte[] buffer = new byte[20];
25              int len;
26              while ((len = fis.read(buffer)) != -1){
27                  //对字节数据进行修改
28                  //错误写法,只是对新定义的局部变量b进行复制,本质上buffer并没有进行修改
29                  //for (byte b : buffer) {
30                  //    b = (byte) (b ^ 5);
31                  //}
32                  
33                  //加密,异或运算
34                  for (int i = 0; i < len; i++) {
35                      buffer[i] = (byte) (buffer[i] ^ 5);
36                  }
37 38                  fos.write(buffer,0,len);
39              }
40          } catch (IOException e) {
41              e.printStackTrace();
42          } finally {
43              try {
44                  if (fos != null){
45                      fos.close();
46                  }
47              } catch (IOException e) {
48                  e.printStackTrace();
49              }
50              try {
51                  if (fis != null){
52                      fis.close();
53                  }
54              } catch (IOException e) {
55                  e.printStackTrace();
56              }
57          }
58      }
59  }

 

6、获取文本上每个字符出现的次数

 1  package com.zhou.exercise;
 2  3  import java.io.BufferedWriter;
 4  import java.io.FileReader;
 5  import java.io.FileWriter;
 6  import java.io.IOException;
 7  import java.util.HashMap;
 8  import java.util.Map;
 9  import java.util.Set;
10 11  /**
12   * @Name WordCountTest
13   * @Description 文本字母个数出现统计
14   * @Author 88534
15   * @Date 2021/8/29 20:39
16   */
17  public class WordCountTest {
18      public static void main(String[] args) {
19          FileReader fr = null;
20          BufferedWriter bw = null;
21          try {
22              //1.创建Map集合
23              Map<Character,Integer> map = new HashMap<Character,Integer>();
24              //2.遍历每一个字符,每一个字符出现的次数放在map中
25              fr = new FileReader("hello.txt");
26              int c = 0;
27              while((c = fr.read())!=-1){
28                  //int还原char
29                  char ch = (char) c;
30                  //判断char是否在map中第一次出现
31                  if (map.get(ch) == null){
32                      map.put(ch,1);
33                  } else {
34                      map.put(ch,map.get(ch)+1);
35                  }
36              }
37              //3.把map中数据存在文件count.txt
38              //3.1创建writer
39              bw = new BufferedWriter(new FileWriter("wordcount.txt"));
40              //3.2遍历map,再写入数据
41              Set<Map.Entry<Character,Integer>> entrySet = map.entrySet();
42              for (Map.Entry<Character, Integer> entry : entrySet) {
43                  switch (entry.getKey()){
44                      case ' ':
45                          bw.write("空格=" + entry.getValue());
46                          break;
47                      case '\t':
48                          bw.write("tab键=" + entry.getValue());
49                          break;
50                      case '\r':
51                          bw.write("回车=" + entry.getValue());
52                          break;
53                      case '\n':
54                          bw.write("换行=" + entry.getValue());
55                          break;
56                      default:
57                          bw.write(entry.getKey()+"="+entry.getValue());
58                          break;
59                  }
60                  bw.newLine();
61              }
62              System.out.println("编写完毕!");
63          } catch (IOException e) {
64              e.printStackTrace();
65          } finally {
66              //4.关闭流资源
67              try {
68                  if (bw != null){
69                      bw.close();
70                  }
71              } catch (IOException e) {
72                  e.printStackTrace();
73              }
74              try {
75                  if (fr != null){
76                      fr.close();
77                  }
78              } catch (IOException e) {
79                  e.printStackTrace();
80              }
81          }
82      }
83  }

 

7、转换流

作用:提供字节流与字符流之间的转换

属于字符流,Input和Output以字符流为对照

InputStreamReader:将一个字节的输入流转换为字符的输入流

字节、字节数组 ---> 字符数组,字符串(解码,看不懂变看得懂)

字节流 ---> 字符流

OutputStreamWriter:将一个字符的输出流转换为字节的输出流

字符数组、字符串 ---> 字节、字节数组(编码,看得懂变看不懂)

字符流 ---> 字节流

示例代码:

InputStreamReader

 package com.zhou.io;
 ​
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStreamReader;
 ​
 /**
  * @Name InputStreamReaderTest
  * @Description 转换流InputStreamReader的使用
  * 字节的输入流到字符的输入流的转换
  * @Author 88534
  * @Date 2021/8/29 20:59
  */
 public class InputStreamReaderTest {
     public static void main(String[] args) {
         InputStreamReader isr = null;
         try {
             FileInputStream fis = new FileInputStream("hello.txt");
             //参数2指明了字符集,具体使用哪个字符集,取决于引用文件保存时所使用的字符集
             //无第2个参数,使用系统默认的字符集
             //InputStreamReader isr = new InputStreamReader(fis);
             isr = new InputStreamReader(fis,"UTF-8");
             char[] cbuf = new char[20];
             int len;
             while ((len = isr.read(cbuf))!=-1){
                 String str = new String(cbuf,0,len);
                 System.out.print(str);
             }
         } catch (IOException e) {
             e.printStackTrace();
         } finally {
             try {
                 if (isr != null){
                     isr.close();
                 }
             } catch (IOException e) {
                 e.printStackTrace();
             }
         }
     }
 }

 

OutputStreamWriter

 package com.zhou.io;
 ​
 import java.io.*;
 ​
 /**
  * @Name OutputStreamWriterTest
  * @Description 转换流OutputStreamWriter的使用,综合InputStreamReader
  *
  * @Author 88534
  * @Date 2021/8/29 21:15
  */
 public class OutputStreamWriterTest {
     public static void main(String[] args) {
         InputStreamReader isr = null;
         OutputStreamWriter osw = null;
         try {
             //1.造文件,造流
             File file1 = new File("hello.txt");
             File file2 = new File("hello3.txt");
 ​
             FileInputStream fis = new FileInputStream(file1);
             FileOutputStream fos = new FileOutputStream(file2);
 ​
             isr = new InputStreamReader(fis,"utf-8");
             osw = new OutputStreamWriter(fos,"gbk");
 ​
             //2.读写过程
             char[] cbuf = new char[20];
             int len;
             while ((len = isr.read(cbuf))!=-1){
                 osw.write(cbuf,0,len);
             }
         } catch (IOException e) {
             e.printStackTrace();
         } finally {
             try {
                 if (isr != null){
                     isr.close();
                 }
             } catch (IOException e) {
                 e.printStackTrace();
             }
             try {
                 if (osw != null){
                     osw.close();
                 }
             } catch (IOException e) {
                 e.printStackTrace();
             }
         }
     }
 }

 

8、扩展:字符集、字符编码

计算机只能识别二进制数据,为了方便应用计算机,让它可以识别各个国家的文字,就将各个国家的文字用数字来表示,并一一对应,行程一张表。

8.1、常见的编码表

ASCII:美国标准信息交换码,用一个字节的7位可以表示

ISO8859-1:拉丁码表,欧洲码表,用一个字节的8位可以表示

GB2312:中国的中文编码表,最多两个字节编码所有字符

GBK:中国的中文编码表升级,融合了更多的中文文字符号,最多两个字节编码

Unicode:国际标准码,融合了目前人类使用的所有字符,为每个字符分配唯一的字符码,所有的文字都用两个字节来表示

UTF-8:变长的编码方式,可用1-4个字节来表示一个字符

8.2、Unicode和UTF-8的转换

Unicode只是定义了一个庞大的、全球通用的字符集,并为每个字符规定了唯一确定的编号,具体存储成什么样的字节流,取决于字符编码方案。推荐的Unicode编码是UTF-8和UTF-16,这里扩展说明Unicode和UTF-8的转换

字节个数Unicode编码Unicode符号范围(十六进制)UTF-8编码方式(二进制)
1 0-127 0000 0000-0000 007F 0xxxxxxx(兼容原来的ASCII)
2 128-2047 0000 0080-0000 07FF 110xxxxx 10xxxxxx
3 2048-85535 0000 0800-0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx
4 85536-1114111 0001 0000-0010 FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

每一段为1字节,x为需要填入的Unicode编码值转为二进制的结果

举例:如“商”,Unicode编码值查表为23578,判断属于三字节类型,换算成十六进制为5C1A,二进制为0101 1100 0001 1010

此时:x全部换为二进制后的结果,得到UTF-8编码

1110xxxx  10xxxxxx   10xxxxxx

1110 0101 1011 0000 1001 1010 BIN

e 5              b 0              9 a          HEX:e5 b0 9a

[-27, -80, -102]

在标准UTF-8编码中,超出基本多语言范围(BMP-Basic Multilingual Plane)的字符被编码为4字节格式,但是在修整的UTF-8编码中,由代理编码对(surrogatepairs)表示,然后这些代理编码对在序列中分别重新编码,结果标准UTF-8编码需要4个字符,在修正后的UTF-8编码中将需要6个字节。

8.3、其他解释

ANSI编码,通常指的是平台的默认编码,例如英文操作系统中是ISO-8859-1,中文系统是GBK

Unicode字符集只是定义了字符的集合和唯一编号,Unicode编码则是对UTF-8、UCS-2/UTF-16等具体编码方案的统称,并不是具体的编码方案。

9、标准输入、输出流(了解)

  • System.in和System.out分别代表了系统标准的输入和输出设备

  • 默认输入设备是键盘,输出设备是显示器

  • System.in的类型是InputStream

  • System.out的类型是OutputStream,其是OutputStream的子类,FilterOutputStream的子类

  • System.in:标准的输入流,默认从键盘输入 System.out:标准的输出流,默认从显示器输出

  • 重定向:通过System类的setIn和setOut方法对默认设备进行改变

    System类的setIn(InputStream is)/setOut(PrintStream ps)方式重新指定输入和输出的流

    • public static void setIn(InputStream in)

    • public static void setOut(PrintStream out)

示例代码:

 package com.zhou.exercise;
 ​
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStreamReader;
 ​
 /**
  * @Name UpperCaseOutputTest
  * @Description 从键盘输入字符串,要求将读取到的整行字符串转成大写输出,然后继续进行输入操作,直至当输入“e”或者“exit”时,退出程序
  * @Author 88534
  * @Date 2021/8/29 22:35
  */
 public class UpperCaseOutputTest {
     /**
      * 方法一:使用Scanner实现,调用next()返回一个字符串
      * 方法二:使用System.in实现,经过转换流,调用BufferedReader的readline()
      */
     public static void main(String[] args) {
         BufferedReader br = null;
         try {
             System.out.println("请输入字符串:");
             InputStreamReader isr = new InputStreamReader(System.in);
             br = new BufferedReader(isr);
             while (true){
                 String data = br.readLine();
                 if ("e".equalsIgnoreCase(data)||"exit".equalsIgnoreCase(data)){
                     System.out.println("程序结束!");
                     break;
                 }
                 String upperCase = data.toUpperCase();
                 System.out.println(upperCase);
             }
         } catch (IOException e) {
             e.printStackTrace();
         } finally {
             if (br != null){
                 try {
                     br.close();
                 } catch (IOException e) {
                     e.printStackTrace();
                 }
             }
         }
     }
 }

 

 package com.zhou.exercise;
 ​
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStreamReader;
 ​
 /**
  * @Name MyInput
  * @Description 仿造Scanner构建一个指定类型的输入类
  * @Author 88534
  * @Date 2021/8/29 22:37
  */
 public class MyInput {
     public static String readString( ){
         BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
         String string = "";
         try {
             string = br.readLine();
         } catch (IOException e) {
             System.out.println(e);
         }
         return string;
     }
 ​
     /**
      * 可将String转换为其他类
      */
     public static int readInt(){return Integer.parseInt(readString());}
 ​
     public static double readDouble(){return Double.parseDouble(readString());}
 ​
     public static double readByte(){return Byte.parseByte(readString());}
 ​
     public static double readShort(){return Short.parseShort(readString());}
 ​
     public static double readLong(){return Long.parseLong(readString());}
 }

 

10、打印流(了解)

实现将基本数据类型的数据格式转换为字符串输出

打印流:PrintStream和PrintWriter

  • 提供了一系列重载的print()和println()方法,用于多种数据类型的输出

  • PrintStream和PrintWriter的输出不会抛出IOException异常

  • PrintStream和PrintWriter有自动flush功能

  • PrintStream打印的所有字符都使用平台的默认字符编码转换为字节,在需要写入字符而不是写入字节的情况下,应该使用PrintWriter类。

  • System.out返回的是PrintStream的实例

示例代码:

 package com.zhou.exercise;
 ​
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.PrintStream;
 ​
 /**
  * @Name PrintStreamTest
  * @Description 打印流,将控制台输出信息打印为文件
  * @Author 88534
  * @Date 2021/8/29 22:49
  */
 public class PrintStreamTest {
     public static void main(String[] args) {
         PrintStream ps = null;
         try {
             FileOutputStream fos = new FileOutputStream(new File("test.txt"));
             //创建打印输出流,设置为自动刷新模式(写入换行或字节'\n'时都会刷新输出缓冲区
             ps = new PrintStream(fos,true);
             //把标准输出流(控制台输出)改成文件
             if (ps != null){
                 System.setOut(ps);
             }
             for (int i = 0; i <= 255; i++) {
                 //输出ASCII字符
                 System.out.print((char) i);
                 if (i % 50 == 0){
                     //每50个数据为一行
                     //换行
                     System.out.println();
                 }
             }
         } catch (FileNotFoundException e) {
             e.printStackTrace();
         } finally {
             if (ps != null){
                 ps.close();
             }
         }
     }
 }

 

11、数据流(了解)

  • 为了方便地操作Java语言的基本数据类型和String的数据,可以使用数据流

  • 作用:用于读取或写出基本数据类型的变量或字符串

  • 数据流有两个类,用于读取和写出基本数据类型,String类的数据

    • DataInputStream和DataOutputStream

    • 分别套接在InputStream和OutputStream子类的流上

  • DataInputStream中的方法

    • boolean readBoolean()

    • byte readByte()

    • char readChar()

    • float readFloat()

    • double readDouble()

    • short readShort()

    • long readLong()

    • int readInt()

    • String readUTF()

    • void readFully(byte[] b)

  • DataOutputStream中的方法

    • 将上述方法的read改成相应的write即可

示例代码:

 package com.zhou.io;
 ​
 import java.io.*;
 ​
 /**
  * @Name DataStreamTest
  * @Description 数据流
  * @Author 88534
  * @Date 2021/8/29 23:05
  */
 public class DataStreamTest {
     public static void main(String[] args) {
         /**
          * 写入文件
          */
         DataOutputStream dos = null;
         try {
             dos = new DataOutputStream(new FileOutputStream("test1.txt"));
             dos.writeUTF("zhou");
             //刷新操作,将内存中的数据写入文件
             dos.flush();
             dos.writeInt(23);
             dos.flush();
             dos.writeBoolean(true);
             dos.flush();
         } catch (IOException e) {
             e.printStackTrace();
         } finally {
             if (dos != null){
                 try {
                     dos.close();
                 } catch (IOException e) {
                     e.printStackTrace();
                 }
             }
         }
 ​
         /**
          * 读取文件
          * 将文件中存储的基本数据类型变量和字符串读取到内存中,保存在变量中
          */
         DataInputStream dis = null;
         try {
             dis = new DataInputStream(new FileInputStream("test1.txt"));
             String name = dis.readUTF();
             int age = dis.readInt();
             boolean isMale = dis.readBoolean();
             //注意:读取数据必须与写入数据的顺序一致,否则会报EOFException异常
             System.out.println("name = " + name);
             System.out.println("age = " + age);
             System.out.println("isMale = " + isMale);
         } catch (IOException e) {
             e.printStackTrace();
         } finally {
             if (dis != null){
                 try {
                     dis.close();
                 } catch (IOException e) {
                     e.printStackTrace();
                 }
             }
         }
     }
 }

 

输出结果:

name = zhou

age = 23

isMale = true

12、对象流(了解)

序列化和反序列化

不光需要保存数据值,还要保存数据类型,还要能够从文件恢复

1.序列化就是在保存数据时,保存数据的值和数据类型

2.反序列化就是在恢复数据时,恢复数据的值和数据类型

3.需要让某个对象支持序列化机制,则必须让其类是可序列化的,必须实现两个接口之一

  • Serializable //这是一个标记接口,没有方法,只做声明

  • Externalizable //该接口有方法需要实现,一般不使用

ObjectInputStream和ObjectOutputStream

功能:提供了对基本类型或对象类型的序列化和反序列化的方法

对流进行修饰包装

ObjectOutputStream提供序列化功能:ObjectOutputStream(OutputStream)

ObjectInputStream提供反序列化功能:ObjectInputStream(inputStream)

注意:

  • 读写顺序要一致

  • 要求实现序列化或反序列化对象,需要实现Serializable接口

  • 序列化的类中建议添加SerialVersionUID,为了提高版本的兼容性

  • 序列化对象时,默认将里面所有属性都进行序列化,但除了static或transient修饰的成员

  • 序列化对象时,要求里面属性的类型也需要实现序列化接口

  • 序列化具备可继承性,也就是如果某类已经实现了序列化,则它的所有子类也已经默认实现了序列化

    • 如Integer本身不实现序列化,但父类Number实现了序列化

示例代码:

ObjectOutputStream

 package com.zhou.io;
 ​
 import java.io.FileOutputStream;
 import java.io.ObjectOutputStream;
 import java.io.Serializable;
 ​
 /**
  * @Name ObjectOutputStreamTest
  * @Description 演示ObjectOutputStreamTest的使用,完成数据的序列化
  * @Author 88534
  * @Date 2021/8/30 10:34
  */
 public class ObjectOutputStreamTest {
     public static void main(String[] args) throws Exception{
         //序列化后,保存的文件格式,不是村文本,而是按照它的格式来保存
         String filePath = "hello.dat";
 ​
         ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath));
 ​
         //序列化数据类型
         //int --> Integer(实现了Serializable接口)
         oos.writeInt(100);
         //boolean --> Boolean(实现了Serializable接口)
         oos.writeBoolean(true);
         //char --> Character(实现了Serializable接口)
         oos.writeChar('a');
         //double --> Double(实现了Serializable接口)
         oos.writeDouble(9.5);
         //String(实现了Serializable接口)
         oos.writeUTF("IO流");
         //保存一个自定义对象
         oos.writeObject(new Dog("旺财",10,"中国", "白色"));
 ​
         oos.close();
         System.out.println("数据保存完毕(序列化形式)");
     }
 }
 ​
 /**
  * 如果需要序列化某个类的对象,实现Serializable接口
  */
 class Dog implements Serializable {
     private String name;
     private int age;
     /**
      * 序列化的类中建议添加SerialVersionUID,为了提高版本的兼容性
      */
     private static final long serialVersionUID = 1L;
     /**
      * 序列化对象时,默认将里面所有属性都进行序列化,但除了static或transient修饰的成员
      */
     private static String nation;
     private transient String color;
 ​
     /**
      * 序列化对象时,要求里面属性的类型也需要实现序列化接口
      */
     private Master master = new Master();
 ​
     public Dog(String name, int age, String nation, String color) {
         this.name = name;
         this.age = age;
         Dog.nation = nation;
         this.color = color;
     }
 ​
     public String getName() {
         return name;
     }
 ​
     public void setName(String name) {
         this.name = name;
     }
 ​
     public int getAge() {
         return age;
     }
 ​
     public void setAge(int age) {
         this.age = age;
     }
 ​
     @Override
     public String toString() {
         return "Dog{" +
                 "name='" + name + '\'' +
                 ", age=" + age +
                 ", color='" + color + '\'' +
                 ", nation='" + nation + '\'' +
                 '}';
     }
 }
 ​
 class Master implements Serializable{
 ​
 }

 

OutputStreamWriter

 package com.zhou.io;
 ​
 import java.io.FileInputStream;
 import java.io.ObjectInputStream;
 ​
 /**
  * @Name ObjectInputStreamTest
  * @Description 演示ObjectInputStreamTest的使用,完成数据的反序列化
  * @Author 88534
  * @Date 2021/8/30 10:45
  */
 public class ObjectInputStreamTest {
     public static void main(String[] args) throws Exception{
         //指定反序列化的文件
         String filePath = "hello.dat";
         ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath));
         //读取(反序列化)的顺序和保存数据(序列化)的顺序一致,否则会出现异常
         System.out.println(ois.readInt());
         System.out.println(ois.readBoolean());
         System.out.println(ois.readChar());
         System.out.println(ois.readDouble());
         System.out.println(ois.readUTF());
 ​
         //dog的编译类型是Object,dog的运行类型是Dog
         Object dog = ois.readObject();
         System.out.println("运行类型:" + dog.getClass());
         System.out.println(dog);
 ​
         //如果需要调用Dog的方法,需要向下转型,或者将Dog类公有化
         //需要将Dog类的定义,拷贝到可以引用的位置
         Dog dog1 = (Dog) dog;
         System.out.println(dog1.getAge());
 ​
         //关闭外层流即可,底层会关闭InputStream
         ois.close();
     }
 }

 

13、Properties类

配置文件xxx.properties

创建一个配置文件mysql.properties

 ip=192.168.100.100
 user=root
 pwd=123456

普通的读取方法

 public class Properties01 {
     public static void main(String[] args) throws IOException{
 ​
         //读取mysql.properties文件,并得到ip、user和pwd
         BufferedReader br = new BufferedReader(new FileReader("mysql.properties"));
         String line = "";
         while ((line = br.readLine())!=null){
             //按“=”切割
             String[] split = line.split("=");
             System.out.println(split[0] + "值是:" + split[1]);
         }
         br.close();
     }
 }

 

输出结果:

ip值是:192.168.100.100 user值是:root pwd值是:123456

Properties类

专门用于读写配置文件的集合类

配置文件的格式:

键=值

键=值

注意:键值对不需要有空格,值不需要用引号,默认类型是String

常见方法

load:加载配置文件的键值对到Properties对象

list:将数据显示到指定设备

getProperty(key):根据键获取值

setProperty(key,value):设置键值对到Properties对象

store:将Properties中的键值对存储到配置文件,在IDEA中,保存信息到配置文件,如果含有中文,会存储Unicode编码

应用案例:

1.完成对mysql.properties的读取

 package com.zhou.properties;
 ​
 import java.io.FileReader;
 import java.util.Properties;
 ​
 /**
  * @Name Properties02
  * @Description 完成对mysql.properties的读取处理
  * @Author 88534
  * @Date 2021/8/30 20:36
  */
 public class Properties02 {
     public static void main(String[] args) throws IOException {
         //1.创建properties对象
         Properties properties = new Properties();
         //2.加载指定配置文件
         properties.load(new FileReader("mysql.properties"));
         //3.把k-v显示控制台
         properties.list(System.out);
         //4.根据key来获取指定属性值
         String user = properties.getProperty("user");
         String pwd = properties.getProperty("pwd");
         System.out.println("用户名:" + user);
         System.out.println("密码:" + pwd);
     }
 }

 

显示结果:

-- listing properties --

user=root

pwd=123456

ip=192.168.100.100

用户名:root

密码:123456

2.添加key-val到文件mysql2.properties

3.完成对mysql.properties的读取,并修改某个key-val

 package com.zhou.properties;
 ​
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.util.Properties;
 ​
 /**
  * @Name Properties03
  * @Description 添加key-val到文件mysql2.properties
  * @Author 88534
  * @Date 2021/8/30 20:42
  */
 public class Properties03 {
     public static void main(String[] args) throws IOException {
         //使用Properties类来创建配置文件,修改配置文件内容
         Properties properties = new Properties();
         //创建k-v
         //如果该文件没有key就是创建,有就是修改(替换)
         properties.setProperty("charset","utf8");
         //注意保存中文的Unicode
         properties.setProperty("user","周");
         properties.setProperty("pwd","zhou");
         //将k-v存储入文件,注释为空(配置文件开头)
         properties.store(new FileOutputStream("mysql2.properties"),null);
         System.out.println("保存配置文件成功!");
     }
 }

 

此时mysql2.properties

 #Mon Aug 30 20:47:10 CST 2021
 user=\u5468
 pwd=zhou
 charset=utf8

Properties父类是HashTable,底层就是HashTable核心方法

14、File的一些用法

file.exist():文件是否存在

file.mkdirs():创建文件路径(配合exist是否存在使用)

file.createNewFile():判断文件是否创建成功

posted on 2021-08-30 21:31  zrm0612  阅读(37)  评论(0)    收藏  举报