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)) |
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):根据键获取值
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是否存在使用)
浙公网安备 33010602011771号