——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-
IO流是用来处理设备之间的数据传输。
Java对数据的操作是通过流的形式。用于操作流的对象都在IO包中。
流按操作数据分为两种:字节流和字符流
流按流向分为:输入流和输出流
字符流的抽象基类:
Reader
常用共性方法:
void close()
关闭该流并释放与之关联的所有资源。
int read()
读取单个字符。
int read(char[] cbuf)
将字符读入数组。
int read(char[] cbuf, int off, int len)
将字符读入数组的某一部分。
int read(CharBuffer target)
读一个字符串
|--FileReader //一初始化就要关联一个文件,如果不存在,会发生异常FileNotFoundException
FileReader(File file)
FileReader(String fileName)
|--BufferedReader //装饰类,提供了更强的功能
特有方法:
String readLine() readLine原理? ★★★通过read读取一个文件
|--LineNumberReaderint //输出带行号的文件
getLineNumber()
setLineNumber(int lineNumber)
|--InputStreamReader 转换流字节流通向字符流的桥梁
读文件的两种方式(不使用缓冲区):
方法1:读一个写一个
int ch=0;
while((ch=fr.read())!=-1)//read():一次读一个,而且会自动往下读 如果已经读到末尾,则返回-1
{
System.out.print((char)ch);
}
方法2:读一个数组大小在打印
char[] buf=new char[1024];
int num=0;
while((num=fr.read(buf))!=-1) //read(char[] buf)返回的是读到的字符的个数
{
System.out.println(new String(buf,0,num));
}
使用缓冲区读取文件:
public static void main(String[] args)throws Exception
{
FileReader fr=new FileReader("buf.txt");
BufferedReader bufr=new BufferedReader(fr);
String line=null;
while((line=bufr.readLine())!=null)
{
System.out.println(line);
}
bufr.close();
}
BufferedReader中的readLine原理:
无论是读一行,或者读取多个字符其实最终都是在硬盘上一个一个读,所以readLine方法最终调用的还是read()方法。readLine内部其实有一个封装好的数组,readLine()调用read()方法得到一个字符后,并没有直接返回,而是把它临时存到了数组当中,当读到\r时就不在存了,而是继续向下读读到\n代表一行结束,那么该方法就把数组中存好的数据变成字符串返回。
根据readLine自定义readLine练习:
import java.io.*;
class MyBufferedReader extends Reader
{
private Reader r;
MyBufferedReader(Reader 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;
if(ch=='\n')
return sb.toString();
else
sb.append((char)ch);
}
if(sb.length()!=0)
return sb.toString();
return null;
}
public int read(char[] cbuf,int off,int len)throws IOException
{
return r.read(cbuf,off,len);
}
public void close()throws IOException
{
r.close();
}
public void myClose()throws IOException
{
r.close();
}
}
Writer类
Writer
常用共性方法
close()
关闭此流,但要先刷新它。
abstract void flush()
刷新该流的缓冲。
void write(char[] cbuf)
写入字符数组。
void write(char[] cbuf, int off, int len)
写入字符数组的某一部分。
void write(int c)
写入单个字符。
void write(String str)
写入字符串。
void write(String str, int off, int len)
flush()
|--FileWriter
new FileWriter("Demo.txt")创建文件
new FileWriter("Demo.txt",true);不覆盖文件,将字符写到文件的末尾处
|--BufferedWriter //装饰类
特有方法
newLine() 跨平台方法
|--OutputStreamWriter //转换流 字符流通向字节流的桥梁
close和flush方法的区别:
close()和flush()的区别:flush刷新后,流可以继续被使用,close刷新后,会将流关闭
/*通过缓冲区向文件中写入数据*/
public static void main(String[] args)throws IOException
{
//创建一个字符写入流对象
FileWriter fw=new FileWriter("buf.txt");
//为了提高效率加入了缓冲技术
//只要将需要被提高效率的流对象作为参数传递给缓冲区的构造函数即可
BufferedWriter bufw=new BufferedWriter(fw);
for (int i=0; i<5; i++)
{
bufw.write("hehe"+i);
bufw.newLine();
bufw.flush();
}
//记住,只要用到缓冲区,就要记得刷新
// bufw.flush();
//其实关闭缓冲区就是在关闭缓冲区中的流对象
bufw.close();
}
IO处理异常比较专业的写法:
/*
IO异常的处理方式。
*/
import java.io.*;
class FileWriterDemo2
{
public static void main(String[] args)
{
FileWriter fw=null;
try
{
fw=new FileWriter("Demo.txt");
fw.write("abcde");
}
catch (IOException e)
{
e.printStackTrace();
}
finally
{
try
{
if(fw!=null)
fw.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
}
字节流的抽象基类:
InputStream
共性方法
int read()
从输入流中读取数据的下一个字节。
int read(byte[] b)
从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。
int read(byte[] b, int off, int len)
close();
available() //独有的取出方式
|--FileInputStream //以创建就要关联一个文件
FileInputStream(String name)
FileInputStream(File file)
|--BufferedInputStream //装饰类,提高效率
InputStream的三种读取方式:
import java.io.*;
class FileStream
{
public static void main(String[] args) throws Exception
{
readFile_3();
}
//如果文件过大,容易造成内存溢出
public static void readFile_3() throws IOException
{
FileInputStream fis=new FileInputStream("fos.txt");
int num=fis.available();//通过此方法获取缓冲数组大小
byte[] buf=new byte[num];
fis.read(buf);
System.out.println(new String(buf,0,buf.length));
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();
}
}
OutputStream
常用共性方法:
void close()
关闭此输出流并释放与此流有关的所有系统资源。
void flush()
刷新此输出流并强制写出所有缓冲的输出字节。
void write(byte[] b)
将 b.length 个字节从指定的 byte 数组写入此输出流。
void write(byte[] b, int off, int len)
将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。
|--FileOutputStream
FileOutputStream(File file)
FileOutputStream(File file, boolean append)
FileOutputStream(String name)
FileOutputStream(String name, boolean append)
|--BufferedOutputStream //装饰类
flush()
字节流中write()和read()的特点:
read()方法读取的是一个字节,而返回的类型是int型,当在读文件时,读到文件中连续8个1(-1,read方法结束标记)时,返回int型就被提升了,但是被提升后值还是-1,依然是结束标记,所以,可以在保留原字节8个二进制位的基础上,再转成int型时,给8个二进制位前面补上0(正数)而不是补上1(负数),这样既可以保留原字节数据不变,又能避免-1。
read()方法就是做了个动作,给原字节数据在保留后8位不变的情况下,给它前边的位数补上0。自己的做法:给这个数&255
write()方法就是做了个动作,只去这个int型数据的后8位。这样就能保证这个被读取的字节数据不变。
/*
根据read()和write()的特点自定义一个字节流缓冲区
*/
import java.io.*;
class MyBufferedInputStream
{
private InputStream in;
private byte[] buf=new byte[1024];
private int count=0,pos=0;
MyBufferedInputStream(InputStream in)
{
this.in=in;
}
public int myRead()throws IOException
{
if(count==0)
{
count=in.read(buf);
if(count==-1)
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();
}
}
字节流综合应用:
/*
字节流综合练习copyMp3
*/
import java.io.*;
class CopyMp3
{
public static void main(String[] args) throws Exception
{
long start =System.currentTimeMillis();
copyMp3_3();
long end=System.currentTimeMillis();
System.out.println("Time:+"+(end-start)+"毫秒");
}
//通过自定义的缓冲区复制
public static void copyMp3_3()throws Exception
{
MyBufferedInputStream bufis=new MyBufferedInputStream(new FileInputStream("1.mp3"));
BufferedOutputStream bufos=new BufferedOutputStream(new FileOutputStream("D:\\3.mp3"));
int by=0;
//为什么read()方法返回的是-1呢?为了避免-1的发生
while((by=bufis.myRead())!=-1)//read方法将byte类型提升为int类型,byte1个字节,前面三个字节为被填补了0
{
bufos.write(by);//而write方法将int型转换为了字节,只取其最后8为的有效位数。
}
bufos.close();
bufis.myClose();
}
//通过字节流的缓冲区完成复制
public static void copyMp3_1()throws Exception
{
BufferedInputStream bufis=new BufferedInputStream(new FileInputStream("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 copyMp3_2()throws Exception
{
BufferedInputStream bufis=new BufferedInputStream(new FileInputStream("1.mp3"));
BufferedOutputStream bufos=new BufferedOutputStream(new FileOutputStream("D:\\2.mp3"));
byte[] buf=new byte[1024];
int len=0;
while((len=bufis.read(buf))!=-1)
{
bufos.write(buf,0,len);
}
bufos.close();
bufis.close();
}
}
转换流:
获取键盘录入:
最常用的获取键盘录入的方法
BufferedReader bufr=
new BufferedReader(new InputStreamReader(System.in));
转换流使用时通常涉及到字符编码,IO中也经常用到。
转换流练习:
/*
从键盘上输入行字母,打印出其大写形式
*/
import java.io.*;
class TransStreamDemo
{
public static void main(String[] args) throws Exception
{
//获取键盘录入对象
InputStream in=System.in;
//将字节流对象转换成字符流
InputStreamReader isr=new InputStreamReader(in);
//为了提高效率,将字符串进行缓冲区技术高效操作,使用BufferedReader
BufferedReader bufr=new BufferedReader(isr);
OutputStream out=System.out;
OutputStreamWriter osw=new OutputStreamWriter(out);
BufferedWriter bufw=new BufferedWriter(osw);
String line=null;
while((line=bufr.readLine())!=null)
{
if("over".equals(line))
break;
bufw.write(line.toUpperCase());
bufw.newLine();
bufw.flush();
}
bufr.close();
}
}
IO流的使用规律:
1.
源:键盘录入
目的:控制台
2.想把键盘录入的数据存储到文件中
源:键盘
目的:文件
3.想要将文件的数据打印在控制台
源:文件
目的:控制台
流操作的基本规律:
通过三个明确来完成。
1.明确源和目的
源:输入流
目的:输出流
2.明确操作的数据是否是纯文本。
是:字符流
不是:字节流
3.当体系明确后,在明确要使用那个具体的对象
通过设备来区分:
源设备:内存,硬盘,键盘
目的设备:内存,硬盘,控制台
想使用编码表就要使用转换流
OutPutStreamWrite
InputStreamReader
可以改变默认的输入设备
System.setIn(InputStream in);
装饰设计模式
当想要对已有对象进行功能增强时,可以定义类,将已有对象传入,基于已有的功能,并提供加强功能,那么自定义的该类称为装饰类。
装饰和继承的区别:
如果是继承的话:
MyReader
|--MyTextReader
|--BufferedMyTextReader
|--MyMediaReader
|--BufferMyMediaReader
|--My ....
|-Buffer...
这种设计方式扩展性很差
找到其参数的共同类型。通过多态的形式可以提高扩展性。
class BufferedMyReader extends MyReader //BufferedMyReader类即继承了MyReader类,且里面也有MyReader类对象
{
private MyReader mr;
BufferedMyReader(MyReader mr)//装饰类一初始化就要有被装饰的类
{
this.mr=mr;
}
}
如果是装饰的话,体系结构就变为:
MyReader
|--MyTextReader
|--MyMediaReader
|--BufferMyReader
装饰设计模式比继承要灵活。避免了继承的臃肿性。降低了类与类之间的关系。
装饰类,因为增强已有对象,具备的功能和已有的对象是相同的,只不过提供了更强的功能,所以装饰类和被装饰类通常属于同一个体系。
装饰模式将类与类之间的继承结构变为组合结构
装饰类示例:
eg:
class MyReader{
MyReader(){}
}
继承:
class BufferedMyReader extends MyReader
{
BufferedMyReader(){}
}
装饰:
class BufferedMyReader extends MyReader //BufferedMyReader类即继承了MyReader类,且里面也有MyReader类对
象
{
private MyReader mr;
BufferedMyReader(MyReader mr)//装饰类一初始化就要有被装饰的类
{
this.mr=mr;
}
}
*/
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();
SuperPerson sp=new SuperPerson(p);
sp.superChifan();
}
}