JAVA 笔记(十二)
- 什么情况下使用哪种流呢?
- 如果数据所在的文件通过windows自带的记事本打开并能读懂里面的内容,就用字符流。其他用字节流。
如果你什么都不知道,就用字节流
字节流的抽象基类(最大父类):
InputStream(读取) ,OutputStream。(写出)
字符流的抽象基类:
Reader , Writer。
注:由这四个类派生出来的子类名称都是以其父类名作为子类名的后缀。
如:InputStream的子类FileInputStream。
如:Reader的子类FileReader。
- 创建字节输出流的时候做了(调用系统功能去创建文件,创建对象,该对象指向这个文件)
- 流的操作后一定要关闭【让流对象变成垃圾,这样就可以被垃圾回收器回收了,同时也是通知系统释放跟该流创建文件相关的资源】。
- 实现数据的换行,windows[\r\n] linux[\n]mac[\r],而一些高级记事本,是可以识别任意换行符的。
- 实现数据的追加写入,把构造方法的第二个参数设置ture即可。
- 赋值不一定会成功,后面异常处理过,如果输出,要做初始化。
- 执行close(),判断一下是否对象为null,否则会出现空指针都异常。当然明显不是null就可以不写。
- 如果已经读到文件的末尾,返回值为-1.
- 先读取一次再循环。更好控制条件。
- 读入数据一定要有文件源,而写入数据没有文件源可以自动创建。【记住二者的区别】
- 计算机是如何识别什么时候该把两个字节转换成中文呢?在计算机中中文的存储是两个字节,第一个字节肯定是负数,第一个字节常见的是负数,也可能是正数,但是没有影响,只要一出现负数就进行拼接,不管第二个是正数与否。
- 字节流是万能的,不清数用何种流,该流为首选。
- Int read(byte[] b)一次读取一个数组,数组的长度最好为1024或者1024的整数倍,该方法返回的是实际读取的字节数。所以最后转换时是0到length,不写成这样可能被覆盖。
- 虽然我们有两种方法读取数据,但是这两种方法针对同一个对象在一个代码中只能出现一个。
* 通过定义数组的方式确实比以前一次读取一个字节的方式快很多,所以,看来有一个缓冲区还是非常好的。
* 既然是这样的话,那么,java开始在设计的时候,它也考虑到了这个问题,就专门提供了带缓冲区的字节类。
* 这种类被称为:缓冲区类(高效类)
* 写数据:BufferedOutputStream
* 读数据:BufferedInputStream
* 构造方法可以指定缓冲区的大小,但是我们一般用不上,因为默认缓冲区大小就足够了。
* 为什么不传递一个具体的文件或者文件路径,而是传递一个OutputStream对象呢?
* 原因很简单,字节缓冲区流仅仅提供缓冲区,为高效而设计的。但是呢,真正的读写操作还得靠基本的流对象实现。
- 四种方式的复制代码及时间
package cn.itcast_06; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; /* * 需求:把e:\\哥有老婆.mp4复制到当前项目目录下的copy.mp4中 字节流四种方式复制文件: * 基本字节流一次读写一个字节: 共耗时:117235毫秒 * 基本字节流一次读写一个字节数组: 共耗时:156毫秒 * 高效字节流一次读写一个字节: 共耗时:1141毫秒 * 高效字节流一次读写一个字节数组: 共耗时:47毫秒 */ public class CopyMp4Demo { public static void main(String[] args) throws IOException { long start = System.currentTimeMillis(); // method1("e:\\哥有老婆.mp4", "copy1.mp4"); // method2("e:\\哥有老婆.mp4", "copy2.mp4"); // method3("e:\\哥有老婆.mp4", "copy3.mp4"); method4("e:\\哥有老婆.mp4", "copy4.mp4"); long end = System.currentTimeMillis(); System.out.println("共耗时:" + (end - start) + "毫秒"); } // 高效字节流一次读写一个字节数组: public static void method4(String srcString, String destString) throws IOException { BufferedInputStream bis = new BufferedInputStream(new FileInputStream( srcString)); BufferedOutputStream bos = new BufferedOutputStream( new FileOutputStream(destString)); byte[] bys = new byte[1024]; int len = 0; while ((len = bis.read(bys)) != -1) { bos.write(bys, 0, len); } bos.close(); bis.close(); } // 高效字节流一次读写一个字节: public static void method3(String srcString, String destString) throws IOException { BufferedInputStream bis = new BufferedInputStream(new FileInputStream( srcString)); BufferedOutputStream bos = new BufferedOutputStream( new FileOutputStream(destString)); int by = 0; while ((by = bis.read()) != -1) { bos.write(by); } bos.close(); bis.close(); } // 基本字节流一次读写一个字节数组 public static void method2(String srcString, String destString) throws IOException { FileInputStream fis = new FileInputStream(srcString); FileOutputStream fos = new FileOutputStream(destString); byte[] bys = new byte[1024]; int len = 0; while ((len = fis.read(bys)) != -1) { fos.write(bys, 0, len); } fos.close(); fis.close(); } // 基本字节流一次读写一个字节 public static void method1(String srcString, String destString) throws IOException { FileInputStream fis = new FileInputStream(srcString); FileOutputStream fos = new FileOutputStream(destString); int by = 0; while ((by = fis.read()) != -1) { fos.write(by); } fos.close(); fis.close(); } }
- 字符流=字节流+编码表。【字节流通过某种编码转变成字符流,没有指定编码,就是默认的编码,通过OutPutStreamWriter()的构造完成的】。
ASCII:美国标准信息交换码。
用一个字节的7位可以表示。
ISO8859-1:拉丁码表。欧洲码表
用一个字节的8位表示。
GB2312:中国的中文编码表。
GBK:中国的中文编码表升级,融合了更多的中文文字符号。
GB18030:GBK的取代版本
BIG-5码 :通行于台湾、香港地区的一个繁体字编码方案,俗称“大五码”。
Unicode:国际标准码,融合了多种文字。
所有文字都用两个字节来表示,Java语言使用的就是unicode
UTF-8:最多用三个字节来表示一个字符。
UTF-8不同,它定义了一种“区间规则”,这种规则可以和ASCII编码保持最大程度的兼容:
它将Unicode编码为00000000-0000007F的字符,用单个字节来表示
它将Unicode编码为00000080-000007FF的字符用两个字节表示
它将Unicode编码为00000800-0000FFFF的字符用3字节表示
- 编码:就是将看懂的变成看不懂的 string—byte[],解码:就是将看不懂的变成看懂的 byte[]------String.
- 编码问题不是很难,只要编码和解码一致就没有问题。
- 写入和读取的编码一直就好了,中间的过度存储地不用管,尽管生成的字符我们看不懂【是因为编码不一致】,但是不要紧,对应读取数据的流能够读懂。
- 特别注意:字符流写数据的时候最后要flush()一下,就是清一下缓存,否则“拥挤”出不去。【因为计算机的数据是按字节来的,,字符一下两个字节就显得拥挤】。但是之后依然要关闭。
- Flush()和colse()的区别:close()关闭流对象,但是先刷新一次缓冲区,关闭之后,流对象就不可以继续使用了。Flush()仅仅刷新缓冲区,刷新之后,流对象还可以继续使用。
- 读取文件一定要存在,写入数据文件可以由代码实现。
转换流的名字比较长,而我们常见的操作都是按照本地默认编码实现的,所以,为了简化我们的书写,转换流提供了对应的子类。
FileWriter
FileReader
但是如果要使用指定的编码,还是要用父类转换流。
- 输出控制台可能要强转一下,但是写入到文件中就不用了。
- 字符缓冲流有自己的换行方法。newLine() 【根据系统的不同来换行 readLIne()【一次读取一行数据,输出的时候也还是要带ln的,该读取数据没有的时候返回的是null,而不是-1,所以先要定义一个string类型的变量】。
- 复制文件字节流有4【普通两种,缓冲两种】种方法,字符流有5【除了那四种,还有自己特有的换行符的一种】种方法,
- 字符流复制文件的五种方式代码
-
package cn.itcast_01; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; /* * 复制文本文件 * 分析: * 复制数据,如果我们知道用记事本打开并能够读懂,就用字符流,否则用字节流。 * 通过该原理,我们知道我们应该采用字符流更方便一些。 * 而字符流有5种方式,所以做这个题目我们有5种方式。推荐掌握第5种。 * 数据源: * c:\\a.txt -- FileReader -- BufferdReader * 目的地: * d:\\b.txt -- FileWriter -- BufferedWriter */ public class CopyFileDemo { public static void main(String[] args) throws IOException { String srcString = "c:\\a.txt"; String destString = "d:\\b.txt"; // method1(srcString, destString); // method2(srcString, destString); // method3(srcString, destString); // method4(srcString, destString); method5(srcString, destString); } // 字符缓冲流一次读写一个字符串 private static void method5(String srcString, String destString) throws IOException { BufferedReader br = new BufferedReader(new FileReader(srcString)); BufferedWriter bw = new BufferedWriter(new FileWriter(destString)); String line = null; while ((line = br.readLine()) != null) { bw.write(line); bw.newLine(); bw.flush(); } bw.close(); br.close(); } // 字符缓冲流一次读写一个字符数组 private static void method4(String srcString, String destString) throws IOException { BufferedReader br = new BufferedReader(new FileReader(srcString)); BufferedWriter bw = new BufferedWriter(new FileWriter(destString)); char[] chs = new char[1024]; int len = 0; while ((len = br.read(chs)) != -1) { bw.write(chs, 0, len); } bw.close(); br.close(); } // 字符缓冲流一次读写一个字符 private static void method3(String srcString, String destString) throws IOException { BufferedReader br = new BufferedReader(new FileReader(srcString)); BufferedWriter bw = new BufferedWriter(new FileWriter(destString)); int ch = 0; while ((ch = br.read()) != -1) { bw.write(ch); } bw.close(); br.close(); } // 基本字符流一次读写一个字符数组 private static void method2(String srcString, String destString) throws IOException { FileReader fr = new FileReader(srcString); FileWriter fw = new FileWriter(destString); char[] chs = new char[1024]; int len = 0; while ((len = fr.read(chs)) != -1) { fw.write(chs, 0, len); } fw.close(); fr.close(); } // 基本字符流一次读写一个字符 private static void method1(String srcString, String destString) throws IOException { FileReader fr = new FileReader(srcString); FileWriter fw = new FileWriter(destString); int ch = 0; while ((ch = fr.read()) != -1) { fw.write(ch); } fw.close(); fr.close(); } }
字节流复制文件的4种方式代码 package cn.itcast_01; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; /*复制图片 * 分析: * 复制数据,如果我们知道用记事本打开并能够读懂,就用字符流,否则用字节流。 * 通过该原理,我们知道我们应该采用字节流。 * 而字节流有4种方式,所以做这个题目我们有4种方式。推荐掌握第4种。 * 数据源: * c:\\a.jpg -- FileInputStream -- BufferedInputStream * 目的地: * d:\\b.jpg -- FileOutputStream -- BufferedOutputStream */ public class CopyImageDemo { public static void main(String[] args) throws IOException { // 使用字符串作为路径 // String srcString = "c:\\a.jpg"; // String destString = "d:\\b.jpg"; // 使用File对象做为参数 File srcFile = new File("c:\\a.jpg"); File destFile = new File("d:\\b.jpg"); // method1(srcFile, destFile); // method2(srcFile, destFile); // method3(srcFile, destFile); method4(srcFile, destFile); } // 字节缓冲流一次读写一个字节数组 private static void method4(File srcFile, File destFile) throws IOException { BufferedInputStream bis = new BufferedInputStream(new FileInputStream( srcFile)); BufferedOutputStream bos = new BufferedOutputStream( new FileOutputStream(destFile)); byte[] bys = new byte[1024]; int len = 0; while ((len = bis.read(bys)) != -1) { bos.write(bys, 0, len); } bos.close(); bis.close(); } // 字节缓冲流一次读写一个字节 private static void method3(File srcFile, File destFile) throws IOException { BufferedInputStream bis = new BufferedInputStream(new FileInputStream( srcFile)); BufferedOutputStream bos = new BufferedOutputStream( new FileOutputStream(destFile)); int by = 0; while ((by = bis.read()) != -1) { bos.write(by); } bos.close(); bis.close(); } // 基本字节流一次读写一个字节数组 private static void method2(File srcFile, File destFile) throws IOException { FileInputStream fis = new FileInputStream(srcFile); FileOutputStream fos = new FileOutputStream(destFile); byte[] bys = new byte[1024]; int len = 0; while ((len = fis.read(bys)) != -1) { fos.write(bys, 0, len); } fos.close(); fis.close(); } // 基本字节流一次读写一个字节 private static void method1(File srcFile, File destFile) throws IOException { FileInputStream fis = new FileInputStream(srcFile); FileOutputStream fos = new FileOutputStream(destFile); int by = 0; while ((by = fis.read()) != -1) { fos.write(by); } fos.close(); fis.close(); } }
高级复制 单级文件夹并改名的代码 package cn.itcast_04; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FilenameFilter; import java.io.IOException; /* * 需求:复制指定目录下的指定文件,并修改后缀名。 * 指定的文件是:.java文件。 * 指定的后缀名是:.jad * 指定的目录是:jad * 数据源:e:\\java\\A.java * 目的地:e:\\jad\\A.jad * * 分析: * A:封装目录 * B:获取该目录下的java文件的File数组 * C:遍历该File数组,得到每一个File对象 * D:把该File进行复制 * E:在目的地目录下改名 */ public class CopyFolderDemo { public static void main(String[] args) throws IOException { // 封装目录 File srcFolder = new File("e:\\java"); // 封装目的地 File destFolder = new File("e:\\jad"); // 如果目的地目录不存在,就创建 if (!destFolder.exists()) { destFolder.mkdir(); } // 获取该目录下的java文件的File数组 File[] fileArray = srcFolder.listFiles(new FilenameFilter() { @Override public boolean accept(File dir, String name) { return new File(dir, name).isFile() && name.endsWith(".java"); } }); // 遍历该File数组,得到每一个File对象 for (File file : fileArray) { // System.out.println(file); // 数据源:e:\java\DataTypeDemo.java // 目的地:e:\\jad\DataTypeDemo.java String name = file.getName(); File newFile = new File(destFolder, name); copyFile(file, newFile); } // 在目的地目录下改名 File[] destFileArray = destFolder.listFiles(); for (File destFile : destFileArray) { // System.out.println(destFile); // e:\jad\DataTypeDemo.java // e:\\jad\\DataTypeDemo.jad String name =destFile.getName(); //DataTypeDemo.java String newName = name.replace(".java", ".jad");//DataTypeDemo.jad File newFile = new File(destFolder,newName); destFile.renameTo(newFile); } } private static void copyFile(File file, File newFile) throws IOException { BufferedInputStream bis = new BufferedInputStream(new FileInputStream( file)); BufferedOutputStream bos = new BufferedOutputStream( new FileOutputStream(newFile)); byte[] bys = new byte[1024]; int len = 0; while ((len = bis.read(bys)) != -1) { bos.write(bys, 0, len); } bos.close(); bis.close(); } }
- 多级文件夹的复制代码,用到了递归
package cn.itcast_05; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; /* * 需求:复制多极文件夹 * 数据源:E:\JavaSE\day21\code\demos * 目的地:E:\\ * 分析: * A:封装数据源File * B:封装目的地File * C:判断该File是文件夹还是文件 * a:是文件夹 * 就在目的地目录下创建该文件夹 * 获取该File对象下的所有文件或者文件夹File对象 * 遍历得到每一个File对象 * 回到C * b:是文件 * 就复制(字节流) */ public class CopyFoldersDemo { public static void main(String[] args) throws IOException { // 封装数据源File File srcFile = new File("E:\\JavaSE\\day21\\code\\demos"); // 封装目的地File File destFile = new File("E:\\"); // 复制文件夹的功能 copyFolder(srcFile, destFile); } private static void copyFolder(File srcFile, File destFile) throws IOException { // 判断该File是文件夹还是文件 if (srcFile.isDirectory()) { // 文件夹 File newFolder = new File(destFile, srcFile.getName()); newFolder.mkdir(); / /获取该File对象下的所有文件或者文件夹File对象 File[] fileArray = srcFile.listFiles(); for (File file : fileArray) { copyFolder(file, newFolder); } } else { // 文件 File newFile = new File(destFile, srcFile.getName()); copyFile(srcFile, newFile); } } private static void copyFile(File srcFile, File newFile) throws IOException { BufferedInputStream bis = new BufferedInputStream(new FileInputStream( srcFile)); BufferedOutputStream bos = new BufferedOutputStream( new FileOutputStream(newFile)); byte[] bys = new byte[1024]; int len = 0; while ((len = bis.read(bys)) != -1) { bos.write(bys, 0, len); } bos.close(); bis.close(); } }
录入学生信息到文件的代码 package cn.itcast_06; public class Student { // 姓名 private String name; // 语文成绩 private int chinese; // 数学成绩 private int math; // 英语成绩 private int english; public Student() { super(); } public Student(String name, int chinese, int math, int english) { super(); this.name = name; this.chinese = chinese; this.math = math; this.english = english; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getChinese() { return chinese; } public void setChinese(int chinese) { this.chinese = chinese; } public int getMath() { return math; } public void setMath(int math) { this.math = math; } public int getEnglish() { return english; } public void setEnglish(int english) { this.english = english; } public int getSum() { return this.chinese + this.math + this.english; } } package cn.itcast_06; import java.io.BufferedWriter; import java.io.FileWriter; import java.io.IOException; import java.util.Comparator; import java.util.Scanner; import java.util.TreeSet; /* * 键盘录入5个学生信息(姓名,语文成绩,数学成绩,英语成绩),按照总分从高到低存入文本文件 * * 分析: * A:创建学生类 * B:创建集合对象 * TreeSet<Student> * C:键盘录入学生信息存储到集合 * D:遍历集合,把数据写到文本文件 */ public class StudentDemo { public static void main(String[] args) throws IOException { // 创建集合对象 TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() { @Override public int compare(Student s1, Student s2) { int num = s2.getSum() - s1.getSum(); int num2 = num == 0 ? s1.getChinese() - s2.getChinese() : num; int num3 = num2 == 0 ? s1.getMath() - s2.getMath() : num2; int num4 = num3 == 0 ? s1.getEnglish() - s2.getEnglish() : num3; int num5 = num4 == 0 ? s1.getName().compareTo(s2.getName()) : num4; return num5; } }); // 键盘录入学生信息存储到集合 for (int x = 1; x <= 5; x++) { Scanner sc = new Scanner(System.in); System.out.println("请录入第" + x + "个的学习信息"); System.out.println("姓名:"); String name = sc.nextLine(); System.out.println("语文成绩:"); int chinese = sc.nextInt(); System.out.println("数学成绩:"); int math = sc.nextInt(); System.out.println("英语成绩:"); int english = sc.nextInt(); // 创建学生对象 Student s = new Student(); s.setName(name); s.setChinese(chinese); s.setMath(math); s.setEnglish(english); // 把学生信息添加到集合 ts.add(s); } // 遍历集合,把数据写到文本文件 BufferedWriter bw = new BufferedWriter(new FileWriter("students.txt")); bw.write("学生信息如下:"); bw.newLine(); bw.flush(); bw.write("姓名,语文成绩,数学成绩,英语成绩"); bw.newLine(); bw.flush(); for (Student s : ts) { StringBuilder sb = new StringBuilder(); sb.append(s.getName()).append(",").append(s.getChinese()) .append(",").append(s.getMath()).append(",") .append(s.getEnglish()); bw.write(sb.toString()); bw.newLine(); bw.flush(); } // 释放资源 bw.close(); System.out.println("学习信息存储完毕"); } }
- 对于循环里面的返回值问题,再循环外面也要加一个返回值,因为假如循环条件第一个就不满足,那么就造成没有返回值的情况,再循环外面加这个返回值就很好的避免了这样的问题。
- LineNumberReader是BufferedReader的子类。该类换行最好加在读取中,因为读取之后换行更合理。
(4)IO流中的编码问题
A:OutputStreamWriter
OutputStreamWriter(OutputStream os):默认编码,GBK
OutputStreamWriter(OutputStream os,String charsetName):指定编码。
B:InputStreamReader
InputStreamReader(InputStream is):默认编码,GBK
InputStreamReader(InputStream is,String charsetName):指定编码
C:编码问题其实很简单
编码只要一致即可
(5)字符流
Reader
|--InputStreamReader
|--FileReader
|--BufferedReader
Writer
|--OutputStreamWriter
|--FileWriter
|--BufferedWriter
- IO流的总结
|--字节流
|--字节输入流
InputStream
int read():一次读取一个字节
int read(byte[] bys):一次读取一个字节数组
|--FileInputStream
|--BufferedInputStream
|--字节输出流
OutputStream
void write(int by):一次写一个字节
void write(byte[] bys,int index,int len):一次写一个字节数组的一部分
|--FileOutputStream
|--BufferedOutputStream
|--字符流
|--字符输入流
Reader
int read():一次读取一个字符
int read(char[] chs):一次读取一个字符数组
|--InputStreamReader
|--FileReader
|--BufferedReader
String readLine():一次读取一个字符串
|--字符输出流
Writer
void write(int ch):一次写一个字符
void write(char[] chs,int index,int len):一次写一个字符数组的一部分
|--OutputStreamWriter
|--FileWriter
|--BufferedWriter
void newLine():写一个换行符
void write(String line):一次写一个字符串
- 为了使文件在类创建的时候就创建,所以就要使用静态代码块。父类没有抛异常,子类就不能抛,但是可以try.子类的异常不能比父类更大。
- Io版登录注册核心代码(就是能够将用户信息保留下来,不至于一关掉程序,下次就无法登陆实现)-------->待续