IO流
IO流用来处理设备之间的数据传输
java对数据的操作是通过流的方式
java碎玉操作流的对象都在IO包中
流按操作数据分为两种:字节流与字符流
流按流向分为:输入流,输出流。
1 常用基类
字节流
InputStream OutputStream
字符流
Reader Writer
注:由这四个类派生出来的子类名称都是 以其父类名作为子类名的后缀。
比如FileInputStream
FileReader
BufferedReader
IO程序的书写
导入IO包中的类 import io.*;
进行IO异常处理
在finally中关闭流
思考
1.有了垃圾回收机制为什么还要调用close方法进行关闭?
垃圾回收器处理的是java虚拟机产生的
内存垃圾,而流所操作的操作系统资源,硬盘,
网络资源等
外部资源不是java虚拟机能管的,也就不是垃圾回收器能处理的。
2.为什么IO异常一定要处理
因为IO类都有抛出异常
2 文件操作
2.1文件写入
import java.io.*; class FileWriterDemo { public static void main(String[] args) throws IOException { //创建一个FileWriter对象。该对象一被初始化就必须要明确被操作的文件。 //而且该文件会被创建到指定下。如果该目录下已有同名文件,将被覆盖 //其实该步就是在明确数据要存放的目的地。 FileWriter fw = new FileWriter("demo.txt"); //调用write方法,将字符串写入到流中。 fw.write("abcde"); //刷新流对象的缓冲中的数据。 //将数据刷到目的地中。 //fw.flush(); //关闭流资源,但是关闭之前会刷新一次内部的缓冲中的数据 //将数据刷到目的地中 //和flush的区别:flush刷新后,流可以继续使用,流可以继续使用, //close刷新后,会将流关闭 fw.close(); fw.write("haha"); } }
2.2 IO异常的处理方式
/* IO异常的处理方式。 */ import java.io.*; class FileWriterDemo2 { public static void main(String[] args) { FileWriter fw = null; try { fw = new FileWriter("demo.txt"); fw.write("abcdefg"); } catch (IOException e) { System.out.println("catch:"+e.toString()); } finally { try { //如果创建成功才需要关闭 if(fw!=null) fw.close(); } catch (IOException e) { System.out.println(e.toString()); } } } }
2.3 文件的续写
//传递一个true参数,代表不覆盖已有的文件。并在已有文件的末尾处进行数据续写。 FileWriter fw = new FileWriter("demo.txt",true);
3.文本文件读取方式
3.1 方式1 一次读一个字符
read方法 遇到文件结束返回-1
import java.io.*; class FileReaderDemo { public static void main(String[] args) throws IOException { //创建一个文件读取流对象,和指定名称的文件相关联。 //要保证该文件是已经存在的,如果不存在,会发生异常FileNotFoundException FileReader fr = new FileReader("demo.txt"); int ch = 0; while((ch = fr.read() )!= -1) { System.out.println("ch = "+(char)ch); } fr.close(); } }
3.2 方式2 通过字符数组读取
/*第二种方式:通过字符数组进行读取*/ import java.io.*; class FileReaderDemo2 { public static void main(String[] args) throws IOException { FileReader fr = new FileReader("demo.txt"); //定义一个字符数组。用于存储读到的字符 //该read(char[])返回的是读到的字符个数 char[] buf = new char[3]; int num = 0; while((num = fr.read(buf)) != -1) { System.out.print(new String(buf,0,num)); } System.out.println(); /* int num = fr.read(buf); System.out.println("num = " + num +"..."+new String(buf,0)); int num2 = fr.read(buf); System.out.println("num = " + num2 +"..."+new String(buf,0)); */ fr.close(); } }
练习
练习1
//读取一个.java文件,并打印在控制台上 import java.io.*; class FileReaderTest { public static void main(String[] args) throws IOException { FileReader fr = new FileReader("DateDemo.java"); char[] buf = new char[1024]; int num = 0; while((num=fr.read(buf)) != -1) { System.out.print(new String(buf,0,num));//注意不用换行 } fr.close(); } }
练习2
//将C盘一个文本文件复制到D盘 /* 复制的原理: * 其实就是将C盘下的文件数据存储到D盘的一个文件中 * * 步骤: * 1.在D盘创建一个文件。用于存储C盘文件中的数据。 * 2.定义读取流和C盘文件关联。 * 3.通过不断地读写完成数据存储。 * //fw.write(buf,0,len); * */ import java.io.*; class CopyText { public static void main(String[] args) { copy_2(); } //第一种方法 //从C盘读一个字符就往D盘写一个字符 public static void copy_1() throws IOException { //创建目的地 FileWriter fw = new FileWriter("RunTimeDemo_copy.txt"); //与已有文件关联 FileReader fr = new FileReader("RunTimeDemo.java"); int ch = 0; while((ch = fr.read())!=-1) { fw.write(ch); } fw.close(); fr.close(); } //第二种 // public static void copy_2() { FileWriter fw = null; FileReader fr = null; try { fw = new FileWriter("SystemDemo_copy.txt"); fr = new FileReader("SystemDemo.java"); char[] buf = new char[1024]; int len = 0; while((len=fr.read(buf))!=-1) { fw.write(buf,0,len); } } catch(IOException e) { throw new RuntimeException("读写失败"); } finally { if(fr!=null) { try { fr.close(); } catch(IOException e) { } } if(fw!=null) { try { fw.close(); } catch(IOException e) { } } } } }
4 字符流的缓冲区
缓冲区的出现提高了对数据的读写效率。
对应类 BufferedWriter
BufferedReader
缓冲区要结合流才可以使用。
在流的基础上对流的功能进行了增强。
4.1 BufferedWriter
/* * 缓冲区的出现是为了提高流的操作效果而出现的。 * 所以在创建缓冲区之前,必须要先有流对象。 * 该缓冲区中提供了一个跨平台的换行符 newLine() 方法 * */ import java.io.*; class BufferedWriterDemo { public static void main(String[] args) throws IOException { //创建一个字符写入流对象 FileWriter fw = new FileWriter("buf.txt"); //为了提高字符写入流效率, 加入了缓冲技术。 //只要将需要被提高效率的流对象作为参数传递给缓冲区的构造函数即可。 BufferedWriter bufw = new BufferedWriter(fw); bufw.write("abc\r\ndef"); for(int i = 1; i < 5; i++) { bufw.write("abcd"+i); bufw.newLine();//跨平台 bufw.flush();//写一次刷新一次 } //记住,只要用到缓冲区,就要记得刷新 bufw.flush(); //其实关闭缓冲区,就是在关闭缓冲区中的流对象。 bufw.close(); } }
4.2 BufferedReader
/* 字符读取流缓冲区: 该缓冲区提供了一个一次读一行的方法 readLine,方便于对文本数据的获取。 当返回null时,表示读到文件末尾。 readLine方法返回的时候只返回回车符之前的数据内容。并不返回回车符。 */ import java.io.*; class BufferedReaderDemo { public static void main(String[] args) throws IOException { //创建一个读取流对象和文件相关联。 FileReader fr = new FileReader("buf.txt"); //为了提高效率。加入缓冲技术。将字符读取流对象作为参数传递给缓冲对象的构造函数。 BufferedReader bufr = new BufferedReader(fr); String line = null; while((line=bufr.readLine())!=null) { System.out.print(line); } bufr.close(); } }
readLine方法的原理
无论是读一行,或者读取多个字符。其实最终都是在硬盘上一个一个读取。所以最终都是在硬盘上一个一个读取。所以最终使用的还是read方法一次读一个的方法。
4.3自定义BufferedReader
MyBufferedReaderDemo.java
/* * 明白了BufferedReader类中持有方法readLine的原理后, * 可以自定义一个类中包含一个功能和readLine一致的方法 * 来模拟一下BufferedReader * * */ import java.io.*; class MyBufferedReader { private FileReader r; MyBufferedReader(FileReader r) { this.r = r; } //可以一次读一行数据的方法 public String myReadLine() throws IOException { //定义一个临时容器,原BufferReader封装的是字符数组 //为了演示方便,定义一个StringBuilder 容器。因为最终还是要将数据变成字符串。 StringBuilder sb = new StringBuilder(); int ch = 0; while((ch = r.read()) != -1) { if(ch == '\r') continue; else if(ch == '\n') return sb.toString(); else sb.append((char)ch); } if(sb.length() != 0) { return sb.toString(); } return null; } public void myClose() throws IOException { r.close(); } } class MyBufferedReaderDemo { public static void main(String[] args) throws IOException { FileReader fr = new FileReader("buf.txt"); MyBufferedReader myBuf = new MyBufferedReader(fr); String line = null; while((line = myBuf.myReadLine()) != null) { System.out.println(line); } myBuf.myClose(); } }
5 装饰设计模式
5.1
当想要对已有的对象进行功能增强时,那么可以定义类,将已有对象传入,基于已有的功能,并提供加强功能。
那么自定义的该类称为装饰类。
装饰类通常会通过构造方法接收被装饰的对象
并基于被装饰的对象的功能,提供更强的功能
/* * 装饰设计模式: * * */ class Person { public void chifan() { System.out.println("吃饭"); } } class SuperPerson { private Person p; SuperPerson(Person p) { this.p = p; } public void superChifan() { System.out.println("开胃汤"); p.chifan(); System.out.println("甜点"); } } class PersonDemo { public static void main(String[] args) { Person p = new Person(); //p.chifan(); SuperPerson sp = new SuperPerson(p); sp.superChifan(); } }
5.2 装饰和继承的区别
装饰模式比继承要灵活。 避免了继承体系臃肿
而且降低了类与类之间的联系。
装饰类因为增强已有对象,具备的功能和已有的是相同的,只不多提供了更强功能。
所以装饰类和被装饰类通常是都属于一个体系中的。
5.3自定义装饰类
装饰
总结:
字符流:
FileReader
FileWriter
BufferedFileReader
BufferedFileWriter
字符流
那么可不可以将 字节流 转成 字符流 ,然后在使用字符流缓冲区的readLine方法。
查找API,InputStreamReader 是字节流通向字符流的桥梁
字节流:
6 LineNumberReader
BufferedReader的子类
可以读取行号
setLineNumber方法
getLineNumber方法
import java.io.*; class LineNumberReaderDemo { public static void main(String[] args)throws IOException { FileReader fr = new FileReader("PersonDemo.java"); LineNumberReader lnr = new LineNumberReader(fr); String line = null; lnr.setLineNumber(100); while((line=lnr.readLine())!=null) { System.out.println(lnr.getLineNumber()+":"+line); } lnr.close(); } }
练习:模拟一个带行号的缓冲区对象。
import java.io.*; class MyLineNumberReader extends MyBufferedReader { private int lineNumber; MyLineNumberReader(Reader r) { super(r); } public String myReadLine()throws IOException { lineNumber++; return super.myReadLine(); } public void setLineNumber(int lineNumber) { this.lineNumber = lineNumber; } public int getLineNumber() { return lineNumber; } } /* class MyLineNumberReader { private Reader r; private int lineNumber; MyLineNumberReader(Reader r) { this.r = r; } public String myReadLine()throws IOException { lineNumber++; StringBuilder sb = new StringBuilder(); int ch = 0; while((ch=r.read())!=-1) { if(ch=='\r') continue; if(ch=='\n') return sb.toString(); else sb.append((char)ch); } if(sb.length()!=0) return sb.toString(); return null; } public void setLineNumber(int lineNumber) { this.lineNumber = lineNumber; } public int getLineNumber() { return lineNumber; } public void myClose()throws IOException { r.close(); } } */ class MyLineNumberReaderDemo { public static void main(String[] args) throws IOException { FileReader fr = new FileReader("copyTextByBuf.java"); MyLineNumberReader mylnr = new MyLineNumberReader(fr); String line = null; mylnr.setLineNumber(100); while((line=mylnr.myReadLine())!=null) { System.out.println(mylnr.getLineNumber()+"::"+line); } mylnr.myClose(); } }
7 字节流File读写操作
/*
字符流:
FileReader
FileWriter。
BufferedReader
BufferedWriter
字节流:
InputStream OutputStream
字符流:
FileReader
FileWriter。
BufferedReader
BufferedWriter
字节流:
InputStream OutputStream
需求:
想要操作图片数据。用字节流。
复制一个图片.
*/
import java.io.*; class FileStream { public static void main(String[] args) throws IOException { readFile_3(); } public static void readFile_3()throws IOException { FileInputStream fis = new FileInputStream("fos.txt"); //int num = fis.available(); byte[] buf = new byte[fis.available()];//定义一个刚刚好的缓冲区。不用在循环了。 fis.read(buf); System.out.println(new String(buf)); fis.close(); } public static void readFile_2()throws IOException { FileInputStream fis = new FileInputStream("fos.txt"); byte[] buf = new byte[1024]; int len = 0; while((len=fis.read(buf))!=-1) { System.out.println(new String(buf,0,len)); } fis.close(); } public static void readFile_1()throws IOException { FileInputStream fis = new FileInputStream("fos.txt"); int ch = 0; while((ch=fis.read())!=-1) { System.out.println((char)ch); } fis.close(); } public static void writeFile()throws IOException { FileOutputStream fos = new FileOutputStream("fos.txt"); fos.write("abcde".getBytes()); fos.close(); } }
练习 拷贝图片
/*
* 复制一个图片
* 思路:
* 1.用字节读取流对象创建一个图片文件。用于存储获取到的图片数据。
* 2.用字节写入流对象创建一个图片文件。用于存储获取到的图片数据。
* 3.通过循环读写,完成数据的存储。
* 4.关闭资源。
*
* */
* 复制一个图片
* 思路:
* 1.用字节读取流对象创建一个图片文件。用于存储获取到的图片数据。
* 2.用字节写入流对象创建一个图片文件。用于存储获取到的图片数据。
* 3.通过循环读写,完成数据的存储。
* 4.关闭资源。
*
* */
import java.io.*; class CopyPic { public static void main(String[] args) { FileOutputStream fos = null; FileInputStream fis = null; try { fos = new FileOutputStream("d:\\2.png"); fis = new FileInputStream("d:\\1.png"); byte[] buf = new byte[1024]; int len = 0; while((len = fis.read(buf)) != -1) { fos.write(buf,0,len); } } catch(IOException e) { throw new RuntimeException("复制文件失败"); } finally { try { if(fis!=null) { fis.close(); } } catch(Exception e) { throw new RuntimeException("读取关闭失败"); } try { if(fos!=null) { fos.close(); } } catch(Exception e) { throw new RuntimeException("写入关闭失败"); } } } }
8 字节流的缓冲区
/*
* 演示mp3的复制。通过缓冲区。
*
*
* */
* 演示mp3的复制。通过缓冲区。
*
*
* */
import java.io.*; class CopyMp3 { public static void main(String[] args) throws IOException { long start = System.currentTimeMillis(); //copy_1(); copy_2(); long end = System.currentTimeMillis(); System.out.println((end - start) + "毫秒"); } //通过字节流的缓冲区完成复制。 public static void copy_1() throws IOException { BufferedInputStream bufis = new BufferedInputStream(new FileInputStream("d:\\1.mp3")); BufferedOutputStream bufos = new BufferedOutputStream(new FileOutputStream("d:\\2.mp3")); int by = 0; while((by = bufis.read()) != -1) { bufos.write(by); } bufos.close(); bufis.close(); } public static void copy_2() throws IOException { MyBufferedInputStream bufis = new MyBufferedInputStream(new FileInputStream("d:\\1.mp3")); BufferedOutputStream bufos = new BufferedOutputStream(new FileOutputStream("d:\\2.mp3")); int by = 0; //System.out.println("第一个字节 :"+bufis.myRead()); //by = bufis.myRead(); //bufos.write(by); while((by = bufis.myRead()) != -1) { bufos.write(by); } bufos.close(); bufis.myClose(); } } class MyBufferedInputStream { private InputStream in; private byte[] buf = new byte[1024]; private int pos = 0, count = 0; MyBufferedInputStream(InputStream in) { this.in = in; } //一次读一个字节,从缓冲区(字节数组)获取。 public int myRead() throws IOException { //通过in对象读取硬盘上的数据,并存储buf中。 if(count == 0) { count = in.read(buf); if(count < 0) return -1; pos = 0; byte b = buf[pos]; count--; pos++; return b&0xff; } else if(count > 0) { byte b = buf[pos]; count--; pos++; return b&0xff; } return -1; } public void myClose() throws IOException { in.close(); } }
9 键盘录入
// 读取转换流
// 将字符流转换成字符流
// 以便在键盘输入时可以使用readLine()方法
import java.io.*; class TransStreamDemo { public static void main(String[] args) throws IOException { //获取键盘录入对象 //InputStream in = System.in; //将字节流对象转成字符流对象,使用转换流。InputStreamReader //InputStreamReader isr = new InputStreamReader(in); //为了提高效率,将字符串进行缓冲区技术高效操作。使用BufferedReader //BufferedReader bufr = new BufferedReader(isr); //简化 //键盘录入 的最常见写法 BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in)); //OutputStream out = System.out; //OutputStreamWriter osw = new OutputStreamWriter(out); //BufferedWriter bufw = new BufferedWriter(osw); //简化 BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out)); String line = null; while((line = bufr.readLine()) != null) { if(line.equals("over")) break; bufw.write(line.toUpperCase()); bufw.newLine(); bufw.flush(); } bufr.close(); } }
1.
源:键盘录入
目的:控制台
2.需求:想把键盘录入的数据存储到一个文件中。
源:键盘
目的:文件
3.需求:想要将一个文件的数据打印在控制台上
源:文件
目的:控制台
10 流操作的基本规律:
流对象有很多,不知道该用哪一个
通过三个步骤来完成。
1.明确源和目的。
源: 输入流 InputStream Reader
目的: 输出流 OutputStream Writer
2.明确操作的数据是否是纯文本。
是:字符流。
不是:字节流
3.当体系明确后,在明确要使用哪个具体的对象。
通过设备来进行区分:
原设备:内存,硬盘,键盘。
目的设备:内存,硬盘,控制台。
1.将一个文件中的数据存储到另一个文件中,即复制文件
源: InputStream Reader 因为是源,所以使用读取流。
是不是操作文本文件
是,这时就可以选择Reader
这样体系就明确了。
接下来明确要使用该体系的那个对象。
明确设备:硬盘。是一个文件
Reader体系中可以操作文件的对象时FileReader
是否需要提高效率? 是。 加入Reader体系中的缓冲区BufferedReader
FileReader fr = new FileReader("a.txt");
BufferedReader bufr = new BufferedReader(fr);
目的:OutputStream Writer
是否是纯文本。
是!Writer。
设备:硬盘 一个文件
Writer体系中可以操作文件的对象时FileWriter
是否需要提高效率? 是。 加入Writer体系中的缓冲区BufferedWriter
FileWriter fw = new FileWriter("b.txt");
BufferedWriter bufw = new BufferedWriter(fw);
练习:将一个图片文件中数据存储到另一个文件中。复制文件。要按照以上格式完成三个明确
2.需求:将键盘录入的数据保存到一个文件中
这个需求中有源和目的都存在。
那么分别分析
源:InputStream Reader
是不是纯文本? 是!Reader
设备:键盘。对应的对象是System.in
不是选择Reader吗?System.in对应的不是字节流吗?
为了操作键盘的文本数据方便。转成字符流按照字符串操作是最方便的。
所以既然明确了Reader, 那么就将System.in转换成Reader
用了Reader体系中转换流 InputSreamReader
InputStreamReader isr = new InputStreamReader(System.in);
需要提高效率吗? 需要BufferedReader
BufferedReader bufr = new BufferedReader(isr);
目的:OutputStream Writer
是否是纯文本 ? 是! Writer
设备: 硬盘, 一个文件 使用FileWriter
FileWriter fw = new FileWriter("c.txt");
BufferedWrite = bufw = new BufferedWriter(fw);
****扩展: 想要把录入的数据按照指定的编码表(utf-8),将数据存在文件中。
目的:OutputStream Writer
是否是纯文本 ? 是! Writer
设备: 硬盘, 一个文件 使用FileWriter
但是存储时,需要加入指定编码表。而指定的编码表只有转换流可以指定。
所以要使用的对象时OutputStreamWriter.
而该转换流对象要接受一个字节输出流。而且还可以操作文件的字节输出流。FileOutputStream
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("d.txt","UTF-8");
需要高效吗?需要
BufferedWrite = bufw = new BufferedWriter(osw);
所以,记住。转换流的使用情况。
字符和字节之间的桥梁。通常,涉及到字符编码转换时,需要用到转换流。
练习:将一个文本数据打印在控制台上 按照以上明确。
11
改变输入输出设备
import java.io.*; class TransStreamDemo2 { public static void main(String[] args) throws IOException { System.setIn(new FileInputStream("PersonDemo.java"); System.setOut(new PrintStream("zzz.txt"); BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in)); BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("d:\\d1.txt"),"GBK")); String line = null; while((line = bufr.readLine()) != null) { if(line.equals("over")) break; bufw.write(line.toUpperCase()); bufw.newLine(); bufw.flush(); } bufr.close(); } }
12 异常的日志信息
import java.io.*; import java.util.*; import java.text.*; class ExceptionInfo { public static void main(String[] args) throws IOException { try { int[] arr = new int[2]; System.out.println(arr[3]); } catch(Exception e) { try { Date d = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); String s = sdf.format(d); PrintStream ps = new PrintStream("exception.log"); ps.println(s); System.setOut(ps); } catch(IOException ex) { throw new RuntimeException("日志文件创建失败"); } e.printStackTrace(System.out); } } } //log4j 用于日志建立的包
13 系统信息
import java.util.*; import java.io.*; class SystemInfo { public static void main(String[] args) throws IOException { Properties prop = System.getProperties(); //System.out.println(prop); //prop.list(System.out); prop.list(new PrintStream("SystemInfo.txt")); } }