一.IO(Input Output)流
Io流用来处理设备之间的数据传输,Java对数据的操作是通过流的方式,Java用于操作流的对象都在Io包中。流按操作数据分为两种:字节流与字符流。流按流向分为:输入流,输出流。
(1).IO流常用基类
字节流的抽象基类:InputStream , OutputStram.
字符流的抽象基类:Reader ,Writer.
注:由这四个类派生出来的子类名称都是以其父类名作为子类名的后缀
如:InputStream的子类FileInputStream.
如:Reader的子类Filereader.
字节流的抽象基类:InputStream , OutputStram.
字符流的抽象基类:Reader ,Writer.
注:由这四个类派生出来的子类名称都是以其父类名作为子类名的后缀
如:InputStream的子类FileInputStream.
如:Reader的子类Filereader.
为什么产生字符流????
在内部融合编码表,可以指定用哪个编码表(即读到的数据是查GBK的码表,还是查UTF-8的码表呢)
先学习一下字符流的特点,既然IO流是用于操作数据的。那么熟觉得最常见体现形式是:文件。那么先以操作文件为主来演示。
需求:在硬盘上,创建一个文件并写入一些文字数据。
找到一个专门用于操作文件的Writer子类对象。FileWriter.后缀名是父类名。前缀名是该流对象的功能。
import java.io.*;
class IoTextDemo
{
public static void main(String[] args)throws IOException //new Filewriter如果不正常会抛出异常
{
//创建一个FileWriter对象。该对象已被初始化就必须要明确被操作的文件
//而且该文件会被创建到指定目录下。如果该目录下已有同名文件,将被覆盖。
//其实该步就是在明确数据要存放的目的地
//可以在当前路径下看看发生什么!!!!
FileWriter fw = new FileWriter("demo.txt");
fw.write("daxiong2");
//刷新流对象中的缓冲中的数据。
//将数据刷到目的地中。
//fw.flush();
//和flush区别:flush刷新后,流可以继续使用,closes刷新后,会将流关闭
fw.close();
}
}
二.演示对已有文件数据续写
windows 下识别\n\r两个在一起才是回车键记事本不能识别\nlinux \n是换行,有一个跨平台的换行函数 newline()。
import java.io.*;
class IoTextDemo
{
public static void main(String[] args) throws IOException
{
//FileWriter(String fileNmae , boolean append)
//根据给定的文件名以及指示是否附加写入数据的 boolean 值来构造 FileWriter 对象。
//传递一个true参数,代表不覆盖已有的文件。并在已有文件的末尾处进行数据续写。
FileWriter fw = new FileWriter("demo.txt",true);
fw.write("daxiong\r\njava");//windows识别\r\n为换行符
fw.close();
}
}
三.Io异常处理
import java.io.*;
class IoTextDemo
{
public static void sop(Object obj)
{
System.out.println(obj);
}
public static void main(String[] args)
{
FileWriter fw = null;//后面有用到fw,所以要定义为全局变量
try
{
fw = new FileWriter("demo.txt");
//fw = new FileWriter("k:\\demo.txt");
fw.write("IOException"); //如果fw出现问题,那么write也没有意义,所以放一起
}
catch (IOException e)
{
System.out.println("cathe"+"-----"+e.toString());
}
finally
{
try
{
//不同资源要分别关闭,不能用一个解决
if(fw != null) //如果没有这段话,如果这么写:fw = new FileWriter(“k:\\demo.txt”);
//根本就没有产生fw,怎么关闭就会报错
{
fw.close(); //关闭之后如果在调用write或者flush会抛出异常
}
}
catch (IOException e)
{
sop(e.toString()); //toString返回此对象本身(它已经是一个字符串!)!!!!。
}
}
}
}
四:文本文件读取方式1(单个单个读取)
import java.io.*;
class IoTextDemo
{
public static void sop(Object obj)
{
System.out.println(obj);
}
public static void main(String[] args) throws IOException
{
int ch = 0;
//如果文件不存在,或者它是一个目录,而不是一个常规文件,
//抑或因为其他某些原因而无法打开进行读取,会抛出
//FileNotFoundException
FileReader fr = new FileReader("demo.txt");
//调用读取流对象(InputStreamReader )的read方法,
//如果已到达流的末尾,则返回 -1!!!
while((ch= fr.read())!=-1)
{
sop((char)ch);//<span style="color:#000099;"><strong>转型成字符类型,否则打印都是ASK码</strong></span>
}
fr.close();
}
}
五.文本文件读取方式2
(1)(通过字符串进行读取,存满了,就一起打出来)
用到从reader继承的public int read(char[] cbuf),阻塞式方法,返回读取的字符数
import java.io.*;
class IoTextDemo
{
public static void sop(Object obj)
{
System.out.println(obj);
}
public static void main(String[] args) throws IOException
{
//现在demo.txt中是IOException
FileReader fr = new FileReader("demo.txt");
char [] buf = new char[3];
int num = fr.read(buf);
sop("字符个数1="+num+"----"+new String(buf)); //打印IOE
//char [] buf1 = new char[3];
//int num1 = fr.read(buf1);
//sop("字符个数2="+num1+"----"+new String(buf1));
int num1 = fr.read(buf);
sop("字符个数1="+num1+"----"+new String(buf));
//char [] buf2 = new char[3];
//int num2 = fr.read(buf2);
//sop("字符个数3="+num2+"----"+new String(buf2));
int num2 = fr.read(buf);
sop("字符个数3="+num2+"----"+new String(buf));
//char [] buf3 = new char[3];
//int num3 = fr.read(buf3);
//sop("字符个数4="+num3+"----"+new String(buf3));
int num3 = fr.read(buf);
sop("字符个数4="+num3+"----"+new String(buf));
//char [] buf4 = new char[3];
//int num4 = fr.read(buf4);
//sop("字符个数5="+num4+"----"+new String(buf4));
int num4 = fr.read(buf);
sop("字符个数5="+num4+"----"+new String(buf));
fr.close();
//如果有buf1 buf2,buf3,buf4
//字符个数1=3----IOE
//字符个数2=3----xce
//字符个数3=3----pti
//字符个数4=2----on
//字符个数5=-1----
//没有buf1 buf2,buf3,buf4
//字符个数1=3----IOE
//字符个数1=3----xce
//字符个数3=3----pti
//字符个数4=2----oni
//字符个数5=-1----oni
//为什么会这样
//因为<span style="color:#000099;"><strong>读的是同一个buf</strong></span>先是读取ioe(取三个),然后又读到xce(取三个),把前面三个覆盖了,然后读到n后面就没有了,只把t覆盖了,后面的i没有覆盖,所以读取长度2,
//打印字符个数4=2----oni,最后面写的是字符个数5=-1----oni
}
}
(2)循环读取
import java.io.*;
class IoTextDemo
{
public static void sop (Object obj)
{
System.out.println(obj);
}
public static void main(String[] args) throws IOException
{
FileReader fr = new FileReader("demo.txt");
char[] buf = new char[2];//一个char是2个字节,这里就开了1024*2 = 2K
//如果实际的小于1024就打印实际的
int num = 0;
while((num = fr.read(buf))!= -1)
{
//写new String(buf)打印全部2K的数据,没有的就空格
//new String(buf,0,num)读到几个打印几个
sop("个数="+num+"----"+new String(buf));
//sop("个数="+num+"----"+new String(buf,0,num));
}
fr.close();//别忘了关闭资源!!!
}
}
/<span style="color:#ff0000;"><strong>/注意!!!</strong></span>
//之前有想过如果数据超出1024怎么办,这里把长度改成char[2],用new String(buf)
//打印结果
//个数=2----IO
//个数=2----Ex
//个数=2----ce
//个数=2----pt
//个数=2----io
//个数=1----no
//因为只要读到数据的结尾(-1),while((num = fr.read(buf))!= -1)就会不断地读,
六.IO流文本文件读取练习
需求:读取一个 .java文件,并打印在控制台上
import java.io.*;
class IoTextDemo
{
public static void sop(Object obj)
{
System.out.println(obj);
}
public static void main(String[] args) throws IOException
{
FileReader fr = new FileReader("day4.java");
char [] buf = new char[2];
int num =0 ;
while((num = fr.read(buf)) != -1)//先读满buf,如果没满不会往下走(除了读到-1),有缓冲的感觉
{
//sop(new String(buf,0,num));
//System.out.print("*");
System.out.print(new String(buf,0,num));//如果写println,如果实际数据超过1024,
//可能会出现在某一位置回车换行(即超过1024的位置)
}
fr.close();
}
}
七.IO拷贝文本文件 这个很重要!!!!!!!
这里我用E盘到D盘
需求:将E盘一个文本文件复制到D盘、
原理:其实就是将E盘下的文件数据存储到D盘的一个文件中。
步骤:
1.在D盘创建一个文件。用于存储E盘文件中的数据。
2.定义读取流和E盘文件关联
3.通过不断地读写完成数据存储。
4.关闭资源
import java.io.*;
class IoTextDemo
{
public static void main(String[] args)
{
FileWriter fw = null;
FileReader fr = null;
try
{
fw = new FileWriter("D:\\a.txt"); //没有这个文件就自己创建一个
fr = new FileReader("E:\\b.txt"); //读不到文件就抛异常
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
{
try
{
if(fw != null)
{
fw.close();
}
}
catch (IOException e)
{
throw new RuntimeException("关闭失败1");
}
try
{
if(fr != null)
{
fr.close();
}
}
catch (IOException e)
{
throw new RuntimeException("关闭失败2");
}
}
}
}
八.IO流其他对象bufferedWritter
字符流的缓冲区,缓冲区的出现提高了对数据的读写效率
(1)对应类
BufferedWriter
BufferedReader
缓冲区要结合流才可以使用,在流的基础上对流的功能进行了增强
(2)注意事项
缓冲区的出现是为了提高流的操作效率而出现的,所以在创建缓冲区之前,必须要先有流对象。
·····该缓冲去中提供了一个跨平台的换行符,newLine(); //windows中写的其实就是bufw.write(“\n\r”);,linux中bufw.write(“\n”),只有他和他自己的子类才能用,
存的差不多,再一次写完,不用存一下写一下,这样比较高效
import java.io.*;
class IoTextDemo
{
public static void main(String[] args) throws IOException
{
//创建一个字符写入流对象。
FileWriter fw = new FileWriter("hc.txt");
//为了提高字符写入效率。加入了缓冲技术。
//只要将需要被提高效率的流对象作为参数传递给缓冲区的构造函数即可。
BufferedWriter bufw = new BufferedWriter(fw);
for(int x = 1;x<5;x++)
{
bufw.write("abcdef"+x);
bufw.newLine(); //跨平台的换行符!!!
bufw.flush();//<span style="color:#000099;"><strong>只要用到缓冲区,一定要记得刷新!!!</strong></span>
}
//其实关闭缓冲区,就是在关闭缓冲区中的流对象,不用再单独关闭fw.flush()
bufw.close();
}
}
//hc.txtd的打印
//abcdef1
//abcdef2
//abcdef3
//abcdef4
九.IO流其他对象bufferedReader
字符读取流缓冲区:
该缓冲区提供了一个一次读一行的方法 readLine,方便于对文本数据的获取,只有他和他的子类才能用(比如LineNumberReader)
当返回null时,表示读到文件末尾。
import java.io.*;
class IoTextDemo
{
public static void main(String[] args) throws IOException
{
//创建一个读取流对象和文件相关联。
FileReader fr = new FileReader("hc.txt");
//为了提高效率。加入缓冲技术。将字符读取流对象作为参数传递给缓冲对象的构造函数。
BufferedReader bufr = new BufferedReader(fr);
String line = null ;
//读到\r\n才会停止
//包含该行内容的字符串,不包含任何行终止符,如果已到达流末尾,则返回 null
while((line = bufr.readLine()) != null)//这里写的是null,不是-1
{
System.out.println(line);
}
bufr.close();
}
}
十.IO流通过缓冲区赋值文本文件
需求:通过缓冲区复制一个 .java文件
readLine方法返回的时候只返回回车符(\r\n)之前的数据内容,并不返回 回车符!!!
import java.io.*;
class IoTextDemo
{
public static void main(String[] args)
{
BufferedReader bufr = null;
BufferedWriter bufw = null;
try
{
bufr = new BufferedReader(new FileReader("day4.java"));
bufw = new BufferedWriter(new FileWriter("copy_day4_java.txt"));
String line = null; // <span style="color:#000099;"><strong>String readLine(),返回的是String类型!!!</strong></span>
while((line = bufr.readLine()) != null)
{
bufw.write(line); //这个write(String s)是继承writer的
bufw.newLine();
bufw.flush();
}
}
catch (IOException e)
{
throw new RuntimeException("读写失败");
}
finally
{
try
{
if(bufr != null)
{
bufr.close();
}
}
catch (IOException e)
{
throw new RuntimeException("关闭失败1");
}
try
{
if(bufw != null)
{
bufw.close();
}
}
catch (IOException e)
{
throw new RuntimeException("关闭失败2");
}
}
}
}
十一.MyBufferedReader模拟BufferedReader
需求:明白了bufferedReader类中特有方法readLine的原理后。(其实也就是读字符,读到回车符就停止\n\r)
可以自定义一个类中包含一个功能和readLine一致的方法。
来模拟一下BufferedReader
import java.io.*;
class MyBufferedReader
{
private FileReader r;
MyBufferedReader(FileReader r)
{
this.r = r;
}
public String myReadLine() throws IOException
{
StringBuilder sb = new StringBuilder();
int ch = 0;
while((ch = r.read())!=-1)//回车符不存
{
if(ch == '\r')
{
continue;
}
if(ch == '\n')
{
//toString返回的是自己本身
return sb.toString();//把缓冲区数据变成字符串返回来
}
else
{
sb.append((char)ch); //添加进StringBuilder
}
}
//!!!!!!!!
if(sb.length()!= 0 )<span style="color:#000099;"><strong> //sb.length(),表示缓冲区还有数据,最后一行如果没有换行符,虽然存进去了,没有\n,</strong></span>
<span style="color:#000099;"><strong>//就读到-1,返回空的,但是没有返回来</strong></span>
{
return sb.toString();
}
//正常打印
//abcdef1
//abcdef2
//abcdef3
//abcdef4
//如果不写这个打印下面
//abcdef1
//abcdef2
//abcdef3
return null; //像readLine()一样读到文件结尾处返回
}
public void myClose() throws IOException
{
r.close();
}
}
class IoTextDemo
{
public static void main(String[] args) throws IOException
{
FileReader fr = new FileReader("hc.txt");
MyBufferedReader myBuf = new MyBufferedReader(fr);
String line = null;
while((line = myBuf.myReadLine())!= null)
{
System.out.println(line);
}
myBuf.myClose();
}
}
十二.装饰设计模式!!!
当想要对已有的对象进行功能增强时,可以定义类,将已有对象传入,基于已有的功能,并提供加强功能。那么自定义的该类称为装饰类,装饰类通常会通过构造方法接受被装饰的对象,并基于被装饰的对象的功能,提供更强的功能。
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 IoTextDemo
{
public static void main(String[] args)
{
Person p = new Person();
SuperPerson sp = new SuperPerson(p);
sp.superChifan();
}
}
十三.IO装饰和继承的区别
(1)使用继承模式
MyReader//专门用于读取数据的类,使用继承的方式
|--MyTextReader
|--MyBufferTextReader
|--MyMediaReader
|--MyBufferMediaReader
|--MyDataReader
|--MyBufferDataReader
class MyBufferReader //都是用的缓冲技术,但是每次有加子类就都要改,然后发现不管操作什么父类都是MyReader
{
MyBufferReader(MyTextReader text)
{}
MyBufferReader(MyMediaReader media)
{}
}
上面这个类扩展性很差,找到其参数的共同类型(MyReader)。通过多态的形式。可以提高扩展性。
(2)使用装饰模式
class MyBufferReader extends MyReader
{
private MyReader r;
MyBufferReader(MyReader r)
{}
}
MyReader//专门用于读取数据的类
|--MyTextReader//被装饰类
|--MyMediaReader//被装饰类
|--MyDataReader //被装饰类
|--MyBufferReader //装饰类
装饰模式比继承要灵活。避免了继承体系臃肿。而且降低了类与类之间的关系。(以前是必须继承)
装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强的功能。所以装饰类和被装饰类通常是都属于一个体系中的。(同一个父类或者同一接口)
十四.IO流LineNumberReader
import java.io.*;
class IoTextDemo
{
public static void main(String[] args) throws IOException
{
FileReader fr = new FileReader("hc.txt");
LineNumberReader inr = new LineNumberReader(fr);
String line = null;
inr.setLineNumber(100); //从一百开始算
while((line = inr.readLine()) != null) //readLine是BufferedReader才有得方法,!!!!!!
//LineNumberReader是他的子类所以才能用
//fr不能用
{
System.out.println(inr.getLineNumber()+":"+line);
}
inr.close(); //只要关闭inr
}
}
十五.IO流MYlineNumberReader
import java.io.*;
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;
}
else
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 IoTextDemo
{
public static void main(String[] args) throws IOException
{
FileReader fr = new FileReader("hc.txt");
MyLineNumberReader mlnr = new MyLineNumberReader(fr);
String line = null;
while((line = mlnr.myReadLine()) != null)
{
System.out.println(mlnr.getLineNumber()+":"+line);
}
mlnr.myClose();
}
}
十六.IO流字节流file读写操作
(1)字符流:
FileReader
FileWriter
BufferedReader
BufferedWriter
(2)字节流:
InputStream OutputStream
import java.io.*;
class IoTextDemo
{
public static void main(String[] args) throws IOException
{
//readFile_1();
readFile_3();
}
//读一个写一个
public static void readFile_1() throws IOException
{
FileInputStream fis = new FileInputStream("hc.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("hc.txt");
byte[] buf = new byte[1024]; //跟字符流不同,字符流用char,char [] buf = new char[n];,字节流用byte,
int num = 0;
while((num = fis.read(buf)) != -1)
{
System.out.println(new String(buf, 0 , num));
}
fis.close();
}
//相对第二种,开辟了最小的空间,但是不适合大数据,所以用第二种最好
public static void readFile_3()throws IOException
{
FileInputStream fis = new FileInputStream("hc.txt");
//int num = fis.avaiable(); //可以获得数据长度,包括回车符
byte[] buf = new byte[fis.available()]; //定义一个刚刚好的缓冲区
fis.read(buf);//如果不写,根本就没有读取数据,这句话是把数据读出来,然后存到buf中
System.out.println(new String(buf));
fis.close();
}
}