介绍
- IO流用来处理设备之间的数据传输。
- Java对数据的操作时通过流的方式。
- Java用于操作流的对象都在IO包中。
- 流按操作数据分为两种:字节流(通用。二进制)与字符流(一般用于文本)。
- 流按流向分为:输入流与输出流。
IO流常用基类
字节流的抽象基类:
InputStream、OuputStream
字符流的抽象基类:
Reader、Writer
注意:这四个抽象类派生出来的子类名称都是以其父类名作为子类名的后缀。 如:InputStream的子类FileInputStrream、Reader的子类FileReader;名称前半部分代表功能,后半部分代表父类名。
IO字符流写的基本流程
package com.sergio.IO;
import java.io.FileWriter;
import java.io.IOException;
/**
* IO字符流中文件写的基本流程
* Created by Sergio on 2015-03-01.
*/
public class FileWriterDemo {
public static void main(String[] args) throws IOException {
//创建FileWriter对象,必要有初始化的一个文件
//该文件会被创建到指定目录下,如果有同名文件,就会被覆盖
FileWriter fw = new FileWriter("demo.txt'");
//将字符串写入到流当中,
fw.write("abcdef");
//刷新流对象中的缓冲中的数据,将数据刷到目的地中
fw.flush();
//关闭流资源,但是关闭之前会刷新一次内部的缓存中的数据,将数据刷到目的地中
//和flush区别:此刷新后,流可以继续使用;close刷新后,会将流关闭
fw.close();
}
}
IO流的异常处理方式
package com.sergio.IO;
import java.io.FileWriter;
import java.io.IOException;
/**
*IO异常的处理方式
* Created by Sergio on 2015-03-01.
*/
public class IOExceptionProcess {
public static void main(String[] args) {
//定义在try块里面后面的finally找不到此变量,又因为全部定义在try外面又因为要扑捉异常,
// 所以定义在外边并定义为null,需要用的时候进行对象变量创建。
FileWriter fw = null;
try {
fw = new FileWriter("demo.txt");
fw.write("abcdef");
fw.close();
}catch (IOException o)
{
System.out.println(o.toString());
}finally {
if (fw != null) //判断不为null非常需要{
try {
fw.close();//最后必须执行的步骤
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
IO字符流文件的写入和续写
fw = new FileWriter("demo.txt", true);//在当前路径的文件中添加新内容,不抹除旧内容,追加在旧内容之后
fw.write("ni\r\nhao!");//在windows平台上换行的操作\r\n
IO字符流文件的读取
- 第一种读取方式。文件读取方式。每次读取文件是读取一个字符打印一次,直到读取打印完毕。
package com.sergio.IO;
import java.io.FileReader;
import java.io.IOException;
/**
* 文件读取方式。每次读取文件是读取一个字符打印一次,直到读取打印完毕。
* Created by Sergio on 2015-03-09.
*/
public class FileReaderDemo {
public static void main(String[] args) throws IOException {
//创建一个文件读取对象,和指定名称的文件相关联
//需要保证文件是已经存在的,如果不存在,会发生异常:FileNotFoundException
FileReader fr = new FileReader("demo.txt");
int ch = 0;
//每次读取一个字符。文件读到末尾会有个一个结束标志,平台不同标志不同,而调用java的会返回 -1这个值告诉文件读取完毕了。
while ((ch = fr.read()) != -1)
{
System.out.println((char)ch);
}
fr.close();
}
}
- 第二种方式。定义一个数组用来存储读取字符,读取一次存一次,直到读取完成后在一次性打印。
package com.sergio.IO;
import java.io.FileReader;
import java.io.IOException;
/**
* 文件读取的第二种方式。定义一个数组用来读取一个字符存一次,读取一次存一次,知道读取完成后在一次性打印
* Created by Sergio on 2015-03-22.
*/
public class FileReaderDemo2 {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("demo.txt");
//定义一个字符数组,用于存储独到的字符一般建议为1024的倍数
//读read(char[])返回的是读到的字符个数
char[] buf = new char[1024];
int num = 0;
while ((num = fr.read(buf)) != -1)
{
System.out.print(new String(buf, 0 ,num));//打印字符串从0角标开始打印num个,也就是读取到的字符串的长度
}
fr.close();
}
}
文件的复制
- 原理:将需要复制的文件存储到目标存储路径下。
- 步骤为:在将要存储的路径下创建一个文件,用来存储需要复制的文件;定义读取流和需要复制的文件关联;通过不断的读写完成数据存储;关闭资源。
package com.sergio.IO;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
/**
* 文件复制的基本流程步骤
* Created by Sergio on 2015-03-22.
*/
public class FileCopyDemo {
public static void main(String[] args) throws IOException {
fileCopyByOneChar();
fileCopyByCharArray();
}
//读取一个字符写一次的方式
public static void fileCopyByOneChar() throws IOException {
FileWriter fw = new FileWriter("ImageHelper.txt");
FileReader fr = new FileReader("ImageHelper.java");
int ch = 0;
//循环读取单个字符
while ((ch = fr.read()) != -1) {
fw.write(ch);
}
//关闭写入读出流
fw.close();
fr.close();
}
//读取完文件再一次性写入文件中
public static void fileCopyByCharArray() {
FileWriter fw = null;
FileReader fr = null;
try {
fw = new FileWriter("copfile2.txt");
fr = new FileReader("ImageHelper.java");
//声明读取文件的数组大小
char[] buf = new char[1024];
//定义读取文件的长度变量
int len = 0;
//读取文件并一次性写入目标文件中
while ((len = fr.read(buf)) != -1) {
//按读取到的文件大小写入目标文件中
fw.write(buf, 0, len);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fr != null) {
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fw != null) {
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
缓冲区字符流
- 按设定的大小来读取或者写入文件,这些动作都是一次性的,而不是读一次写一次比较浪费资源。出现是为了数据的读写效率。
- 字符流对应的缓存类为:BufferedWriter和BufferedReader。
- 缓冲区的出现视为了提高流的操作效率而出现的,必须要和流对象使用。
- 底层原理还是数组来存储数据,最后封装成数组对象来使用。
- 写入流缓冲区:
FileWriter fw = new FileWriter("abc.txt");
//将流对象作为参数传递给缓冲区的构造函数即可
BufferedWriter bufw = new BufferedWriter(fw);
bufw.write("afsdfsd");
//跨平台的换行方法,
bufw.newLine();
//缓冲区读取时一定要有刷新动作
bufw.flush();
//关闭缓冲区就是关闭缓冲区中的流对象
bufw.close();
- 读取流缓冲区:
FileReader fr = new FileReader("demo1.txt");
//缓冲区读取流
BufferedReader bufr = new BufferedReader(fr);
//创建文件读取到末尾标志值变量
String line = null;
//按行读取数据。读到文件末尾返回null表示。readline方法返回的时候只返回回车符之前的数据内容,并不返回回车符。
while ((line = bufr.readLine()) != null) {
System.out.println(line);
}
bufr.close();
- 缓冲区复制数据:
package com.sergio.IO;
import java.io.*;
/**
* 缓冲区复制数据
* Created by Sergio on 2015-03-24.
*/
public class CopyTextByBuffered {
public static void main(String[] args) {
BufferedReader bufr = null;
BufferedWriter bufw = null;
try {
bufr = new BufferedReader(new FileReader("ImageHelper.java"));
bufw = new BufferedWriter(new FileWriter("abcd.txt"));
String line = null;
while ((line = bufr.readLine()) != null) {
bufw.write(line);
//按行写入数据
bufw.newLine();
bufw.flush();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
//关闭资源流
finally {
if (bufr != null) {
try {
bufr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bufw != null) {
try {
bufw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
readLine()方法原理:底层还是
raed()
方法一个字符一个字符来读取数据,只不过读取到的数据都缓冲到缓冲区中,碰到\r\n
这个转义字符不存入,确定为换行符后,将读取到的字符串数据一次性读取出来。模拟BufferedReader的功能:
package com.sergio.IO;
import java.io.FileReader;
import java.io.IOException;
/**
* Created by Sergio on 2015-03-24.
*/
public class MyBufferedDemo {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("abc.txt");
MyBuffered mf = new MyBuffered(fr);
String line = null;
while ((line = mf.myReaderLine()) != null) {
System.out.println(line);
}
mf.myClose();
}
}
//模仿BufferedReader中的功能
class MyBuffered {
//底层使用到的还是FileReader的读取方法
private FileReader r;
MyBuffered(FileReader r) {
this.r = r;
}
//按行读取数据
public String myReaderLine() throws IOException {
//定义临时容器,原BufferRead封装的是字符数组。在此定义个StringBuilder
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();
//读取完返回null即可。
return null;
}
public void myClose() throws IOException {
r.close();
}
}
装饰设计模式
- 当想要对已有的对象进行功能增强时,可以定义类,将已有对象传入,基于已有的功能,并提供加强功能,那么自定义的该类称为装饰类。
- 装饰类特点:通常会通过构造方法接受被装饰的对象,并基于被装饰的对象的功能,提供更强的功能。
package com.sergio.io;
/**装饰设计模式
* Created by Administrator on 2015/4/6.
*/
public class PersonDemo {
public static void main(String[] args) {
Person p = new Person();
SuperPerson su = new SuperPerson(p);
su.superEat();
}
}
//被装饰类
class Person
{
public void eat()
{
System.out.println("吃饭");
}
}
//装饰类
class SuperPerson
{
private Person p;
SuperPerson(Person p)
{
this.p = p;
}
//装饰增强方法
public void superEat()
{
System.out.println("kaiweijiu");
p.eat();
System.out.println("hetan");
}
}
装饰设计模式和继承的区别
- 装饰设计模式比继承要灵活,避免了继承体系臃肿。而且降低了类与类之间的关系。
- 装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强的功能,所以装饰类和被装饰类通常都属于一个体系中。
- 通常情况下装饰类和被装饰类同属于一个接口或者同一个类。
* MyReader//专门用于读取数据的类。这种结构是继承结构
* |--MyTextReader//用于扩展的功能类
* |--MyBufferTextReader//用于对Text读取的类扩展类
* |--MyMediaReader
* |--MyBufferMediaReader
* |--MyDataReader
* |--MyBufferDataReader
*继承关系的类的扩展写法。扩展性较差
*class MyBufferReader
*{
* MyBufferReader(MyTextReader text)
* {}
* MyBufferReader(MyMediaReader text)
* {}
* MyBufferReader(MyDataReader text)
* {}
*}
*
* MyReader//专门用于读取数据的类。这种结构是装饰设计模式
* |--MyTextReader//用于扩展的功能类
* |--MyMediaReader
* |--MyDataReader
* |--MyBufferReader//用于增强读取各种数据功能类的方法类
*
* 设计模式的写法
* class MyBufferReader extends MyReader//增强类还是用于读取数据,继承自这个接口,对原有方法进行增强
* {
* private MyReader r;
//需要进行增强的功能方法,传入对象参数
* MyBufferReader(MyReader r)
* {}
* }
LineNumberReader
- 跟踪行号的缓冲区字符输入流。默认情况下从0开始。带行号的装饰类。
package com.sergio.io;
import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;
/**
* LineNumberReader类介绍
* Created by Administrator on 2015/4/6.
*/
public class LineNumberReaderDemo {
public static void main(String[] args) throws IOException{
FileReader fr = new FileReader("PersonDemo.java");
LineNumberReader lnr = new LineNumberReader(fr);
String line = null;
//设置行号从50开始计数
lnr.setLineNumber(50);
while ((line = lnr.readLine()) != null)
{
//带行号打印
System.out.println(lnr.getLineNumber() + line);
}
lnr.close();
}
}
- 自定义实现带行号的MyLineNumberReader的功能
package com.sergio.IO;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
/**
* 自定义实现带行号读取文本的功能
* Created by Sergio on 2015-04-12.
*/
public class MyLineNumberReaderDemo {
public static void main(String[] args) throws IOException{
FileReader fr = new FileReader("abc.txt");
MyLineNumberReader mlnr = new MyLineNumberReader(fr);
String line = null;
while ((line = mlnr.myReadLine()) != null)
{
System.out.println(mlnr.getLineNumber() + line);
}
mlnr.myClose();
}
}
//有一些功能在MyBufferedReader中有实现,可以继承自它,减少代码量
class MyLineNumberReader extends MyBufferedReader
{
private int LineNumber;
MyLineNumberReader(Reader r) {
super(r);
}
public String myReadLine()throws IOException
{
LineNumber++;
return super.myReaderLine();
}
public int getLineNumber() {
return LineNumber;
}
public void setLineNumber(int lineNumber) {
LineNumber = lineNumber;
}
}
字节流IO读写操作
- 以字节的方式来读取操作文件,常操作的文件为媒体性文件。
package com.sergio.IO;
import javax.print.DocFlavor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* 字节流的文件的读取方式
* Created by Sergio on 2015-04-12.
*/
public class FileStream {
public static void main(String[] args) {
}
//字节流写入文件
public static void writeFile() throws IOException {
FileOutputStream fos = new FileOutputStream("abc.txt");
fos.write("abdr".getBytes());//转换成字节
fos.close();
}
//单个读取文件
public static void readFile_1() throws IOException {
FileInputStream fis = new FileInputStream("abc.txt");
int ch = 0;
while ((ch = fis.read()) != -1) {
System.out.println((char) ch);
}
fis.close();
}
//按定义的数组的大小来读取文件,推荐的读取方式
public static void readFile_2() throws IOException {
FileInputStream fis = new FileInputStream("abc.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_3() throws IOException {
FileInputStream fis = new FileInputStream("abc.txt");
//定义的数组刚好为文件的大小数值.但是一般不推荐使用,容易造成内存溢出
byte[] buf = new byte[fis.available()];
fis.read(buf);
System.out.println(new String(buf));
fis.close();
}
}
字节流拷贝图片
package com.sergio.IO;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* 字节流拷贝图片文件
* Created by Sergio on 2015-04-13.
*/
public class CopyPic {
public static void main(String[] args) {
FileOutputStream fos = null;
FileInputStream fis = null;
try {
fos = new FileOutputStream("git-1.jpg");
fis = new FileInputStream("git.jpg");
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 (IOException e) {
throw new RuntimeException("读取关闭失败");
}
try {
if (fos != null) {
fos.close();
}
} catch (IOException e) {
throw new RuntimeException("写入关闭失败");
}
}
}
}
读取键盘录入
- System.in:标准的输入流,默认为键盘。
package com.sergio.IO;
import java.io.IOException;
import java.io.InputStream;
/**
* 读取键盘录入
* Created by Sergio on 2015-04-14.
*/
public class ReadIn {
public static void main(String[] args) {
InputStream in = System.in;//读取键盘录入流
//装在容器
StringBuilder sb = new StringBuilder();
while (true) {
int ch = 0;
try {
ch = in.read();
//度读取录入的数据进行判断
if (ch == '\r')
continue;
if (ch == '\n') {
String s = sb.toString();
//碰到quit就结束录入
if ("quit".equals(s))
break;
//转换成大写
System.out.println(s.toUpperCase());
//清空缓冲区
sb.delete(0, sb.length());
} else
sb.append((char) ch);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
读取转换流(字节流转换成字符流)
- 将字节流转换成字符流,可以使用字符流的功能。转换流的方法定义在字符流的体系当中。
package com.sergio.IO;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
/**
* 转换流,字节流转换成字符流
* Created by Sergio on 2015-04-14.
*/
public class TransStreamDemo {
public static void main(String[] args) {
//获取键盘录入对象
InputStream in = System.in;
//字节流转换成字符流
InputStreamReader isr = new InputStreamReader(in);
//提高效率将字符串进行缓冲区技术搞笑操作,使用BufferedReader
BufferedReader bufr = new BufferedReader(isr);
String line = null;
try {
while ((line = bufr.readLine()) != null) {
//判断结束标记
if ("quit".equals(line))
break;
System.out.println(line.toUpperCase());
}
} catch (IOException e) {
e.printStackTrace();
}
try {
bufr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
字符流转换成字节流
package com.sergio.IO;
import java.io.*;
/**
* 字符流转换成字节流
* Created by Sergio on 2015-04-14.
*/
public class TransStringDemo {
public static void main(String[] args) {
// InputStream in = System.in;
// InputStreamReader isr = new InputStreamReader(in);
// BufferedReader bufr = new BufferedReader(isr);三聚简化成一句
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
OutputStream out = System.out;
OutputStreamWriter osw = new OutputStreamWriter(out);
//使用BufferedWriter功能中的换行方法
BufferedWriter bufw = new BufferedWriter(osw);
//BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
String line = null;
try {
while ((line = bufr.readLine()) != null) {
if ("quit".equals(line))
break;
bufw.write(line.toUpperCase());
bufw.newLine();
bufw.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
bufr.close();
bufw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
流操作规律
三个要点规律:
- 明确源和目的。(源:输入流。目的:输出流)
- 操作的数据是否是文本。(是:字符流。不是:字节流)
- 当体系明确后,在明确要使用那个具体的对象。(通过设备来进行区分。源设备:内存、硬盘、键盘。目的设备:内存、硬盘、控制台)
改变标准输入输出设备
- 改变标准输入流:
System.setIn(InputStream in)
。 - 改变数准输出流:
System.setOut(PrintStream out)
。