引入
io流是操作数据(主要是控制台,和内存中)、文件(硬盘中)的流对象。
引用的包是java.io.*
按流向分为:输入流和输出流。
按操作数据分为:字节流和字符流
字节流的抽象基类:InputStream,OutputStream
字符流的抽象基类:Reader,Writer
注:由这四个类派生出来的子类名称都是以其父类名作为子类名的后缀
如:InputStream的子类FileInputStream。 Reader的子类FileReader。
字符流
简单的字符流。
- 读
//一个一个字符打印
FileReader fr=new FileReader(“hello.txt”);
int ch;
while((ch=fr.read())!=-1){
System.out.println((char)ch);
}
//指定长度字符串打印
FileReader fr=new FileReader(“hello.txt”);
char[] chs=new char[1024];
int length;//一次读了多少长度的字符串
while(length=fr.read(chs));{
//取对应读取长度的字符串长度。
System.out.println(new String(chs,0,length));
}
- 写
//没有该文件则创建,有该文件则覆盖
FileWriter fw=new FileWriter(“hello.txt”);
//没有该文件创建,有该文件则续写。
//FileWriter fw=new FileWriter(“hello.txt”,true);
fw.write(“dddgfwe”);
fw.flush();
fw.close();
在IO中最必要的就是有IO异常,需要对异常处理,或者抛出,下面是对异常完整处理的代码。
- 读加写 完整异常处理
FileDemo.java
import java.io.*;
class FileDemo{
public static void main(String[] args) {
//在try的外面声明对象,使该对象是存在的,如果try中抛出了异常,对象也能被finally被使用!
FileReader fr=null;
FileWriter fw=null;
try{
//这里使用读文件和写文件,所以都会抛异常。
fr=new FileReader("FileReaderDemo.java");
fw=new FileWriter("copy.txt");
char[] ch=new char[1024];
int len=0;
while((len=fr.read(ch))!=-1){
fw.write(ch,0,len);
}
fw.flush();
}catch (IOException e){
System.out.println(e);
}finally{
try{
/*关流也存在异常。但是为了减少异常的出现,我们可以在关流之前对对象进行判空,如果是空那么那么无需关闭。当然需要分别判空*/
if (fr==null) {
fr.close();
}
if (fw==null) {
fw.close();
}
}catch (IOException e){
System.out.println(e);
}
}
}
}
tip.在windows中 换行是”/r/n”
能够对文件进行读写之后,我们希望能够提高速度,所以缓冲流就引入了。
- 读加写 缓冲流 异常处理
可以注意下缓冲流中对换行已经封装好了。
BufferedWriterDemo.java
import java.io.*;
class BufferedWriterDemo{
public static void main(String[] args)throws IOException{
BufferedReaderAndWriter_Method();
}
//写
public static void BufferedWriter_Method()throws IOException{
FileWriter fw=new FileWriter("xx.txt");
BufferedWriter bw=new BufferedWriter(fw);
bw.write("sjhhs");
//封装好了换行,好处,可以跨操作系统使用。
bw.newLine();
bw.write("311");
bw.flush();
bw.close();
}
public static void BufferedReader_Method()throws IOException{
FileReader fr=new FileReader("xx.txt");
BufferedReader br=new BufferedReader(fr);
String aa=null;
//封装了读一整行,而且是不带换行符号的(windows系统中的"\r\n")
//返回类型直接是String类型,便于操作
while((aa=br.readLine())!=null){
System.out.println(aa);
}
br.close();
}
//缓冲流读加写,并且异常处理
public static void BufferedReaderAndWriter_Method(){
BufferedReader br=null;
BufferedWriter bw=null;
try{
br=new BufferedReader(new FileReader("FileDemo.java"));
bw=new BufferedWriter(new FileWriter("copy2.txt"));
String line=null;
while((line=br.readLine())!=null){
bw.write(line);
bw.newLine();
bw.flush();
}
}catch (IOException e){
System.out.println(e);
}finally{
try{
if (bw!=null) {
bw.close();
}
if (br!=null){
br.close();
}
}catch(IOException e){
System.out.println(e);
}
}
}
}
缓冲流的使用其实是一种装饰模式。文章的末尾会浅谈一番。
BufferedWriter中的newLine()封装的是打印换行的转译字符(所以在windows系统和linux系统都可以使用);
BufferedReader中的readLine()封装的是通过read()方法一个一个字符读取,总合一行的字符(直到字符为换行字符即“/r/n”),则返回一行的字符串,不包含转译字符。
我们可以自己试着书写一个缓冲输入流。
MyBufferedReaderDemo.java
import java.io.*;
class MyBufferedReaderDemo{
public static void main(String[] args)throws IOException{
FileReader fr=new FileReader("xx.txt");
MyBufferedReader mbr=new MyBufferedReader(fr);
String line=null;
while((line=mbr.myRead())!=null){
System.out.println(line);
}
if (mbr!=null) {
mbr.myClose();
}
}
}
class MyBufferedReader{
private FileReader f;
//构造函数值得注意一下
MyBufferedReader(FileReader f){
this.f=f;
}
public String myRead()throws IOException{
StringBuilder sb=new StringBuilder();
int ch = 0;
while((ch=f.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 myClose()throws IOException{
f.close();
}
}
字节流
同样看读写,以及使用缓存的例子
- 读
public static void method_1()throws IOException{
FileOutputStream fos=new FileOutputStream("ceshi.txt");
fos.write(("dasgxc").getBytes());
//与字符流不同的是:这里没有刷缓存!
//资源是需要关闭的,是占用资源的
fos.close();
}
在FileOutputStream中的write所写的是最小单位,不需要刷新,直接传输,没有任何转换。
FileWriter所使用的write方法经过封装,底层使用的是FileOutputStream,是将一个字符分解为一个个字节来传输的,所以它是需要刷新缓存。
写
//一个一个字节读取。
public static void method_2()throws IOException{
FileInputStream fis=new FileInputStream("ceshi.txt");
int b=0;
while((b=fis.read())!=-1){
System.out.println((char)b);
}
}
//一串字节读取。
public static void method_3()throws IOException{
FileInputStream fis=new FileInputStream("ceshi.txt");
byte[] b=new byte[1024];
int len=-1;
while((len=fis.read(b))!=-1){
System.out.println(new String(b,0,len));
}
}
//设置一个与复制目标文件相同大小的字节数组,将这串字节数组一起打印,所以只需要一次,不用循环。
//但是,当目标文件过大时,会使内存爆炸,如比内存还大,内存中就装不下这样的数组了。
public static void method_4()throws IOException{
FileInputStream fis=new FileInputStream("ceshi.txt");
//得到文件的大小
int size=fis.available();
byte[] b=new byte[size];
fis.read(b);
System.out.println(new String(b));
}
字节流缓冲读写
public static void method_5()throws IOException{
BufferedInputStream bis=new BufferedInputStream(new FileInputStream("ceshi.txt"));
BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream("ceshi2.txt"));
int by=0;
while((by=bis.read())!=-1){
bos.write(by);
}
bis.close();
bos.close();
}
- 通过字节流复制一个mp3文件。
CopyMp3.java
import java.io.*;
class CopyMp3{
public static void main(String[] args) throws IOException{
long start =System.currentTimeMillis();
// MyBufferedInputStream mbis=new MyBufferedInputStream(new FileInputStream("1.mp3"));
BufferedInputStream mbis=new BufferedInputStream(new FileInputStream("1.mp3"));
BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream("2.mp3"));
int i=0;
while((i=mbis.myRead())!=-1){
bos.write(i);
}
bos.close();
mbis.myClose();
long end =System.currentTimeMillis();
System.out.println((end-start)+"毫秒");
}
}
自己写一个读mp3的字节流,当然是使用缓冲流提速的!
MyBufferedInputStream.java
import java.io.*;
class MyBufferedInputStream{
private InputStream in;
private int pos=0,count=0;
private byte[] b=new byte[1024*1024];
MyBufferedInputStream(InputStream in){
this.in=in;
}
public int myRead()throws IOException{
if (count==0) {
count=in.read(b);
pos=0;
if (count<0) {
return -1;
}
// System.out.println("length:"+b.length+" count:"+count+" pos:"+pos);
byte bt=b[pos];
count--;
pos++;
//注意这里并上了一个255。
return bt&255;
}
else if (count>0) {
// System.out.println("length:"+b.length+" count:"+count+" pos:"+pos);
byte bt=b[pos];
count--;
pos++;
return bt&255;
}
return -1;
}
public void myClose()throws IOException{
in.close();
}
}
由于二进制转换为int的时候,为防止出现连续的八个1(1111 1111 即可以表示255也可以表示-1,而在判断条件是,read()==-1,表示读到了文件的末尾,出现冲突,可能会提前结束读的操作)。
所以转换时,存在着提升的现象,在每个8位二进制的数值前,补上三组八个0,使其区分于“-1”的二进制(32个1)表示。即
一个字节(8位)&0000 0000 0000 0000 0000 0000 1111 1111(255)。
修改CopyMp3.java
import java.io.*;
class CopyMp3{
public static void main(String[] args) throws IOException{
long start =System.currentTimeMillis();
MyBufferedInputStream mbis=new MyBufferedInputStream(new FileInputStream("1.mp3"));
//BufferedInputStream mbis=new BufferedInputStream(new FileInputStream("1.mp3"));
BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream("2.mp3"));
int i=0;
while((i=mbis.myRead())!=-1){
bos.write(i);
}
bos.close();
mbis.myClose();
long end =System.currentTimeMillis();
System.out.println((end-start)+"毫秒");
}
}
装饰设计模式
现在我们来浅谈一番 装饰设计模式
老师的定义:想要对已有的对象进行功能强化时,可以定义类,将已有对象传入,基于已有的功能,并提供加强的功能。那么称这个类为装饰类。
首先,这是装饰,所以他是服务的,而不是自己有的,那么它就没有空的构造函数,它只有有参的构造函数。
并且,前面提到的参数是所要装饰的对象,作为该装饰类的私有属性,通过构造函数来初始化。
提示:参数最好可以使用父类或者基类。使用一个基类,可以使它不同的子类作为入参,可以以多态的形式实现,而且降低了具体的子类和装饰类的耦合关系。
BufferedReader是Reader的装饰类。
而LineNumberReader也是Reader的装饰类,它同样是BufferReader的子类,所以部分代码在BufferedReader基础上面有拓展。