FileWriter,文件字符写流,构造方法为:
FileWriter(File file) 给一个File对象构造一个FileWriter对象。
给一个File对象构造一个FileWriter对象。 FileWriter(File file, boolean append)
FileWriter(String fileName)
构造一个给定文件名的FileWriter对象。
FileWriter(String fileName, boolean append)
构造一个FileWriter对象,给出一个带有布尔值的文件名,表示是否附加写入的数据。
构造函数中:如果filename路径不存在,则会新建,如果这个路径有问题,不如路径不存在,则会抛出异常。
常用的写方法:
write(int c) 写一个字符 要写入的字符包含在给定整数值的16个低位中; 16个高位被忽略。
write(char[] cbuf) 写入一个字符数组
write(char[] cbuf,int off,int len) 写入字符数组的一部分。
write(String str) 写一个字符串
write(String str,int off, int len) 写一个字符串的一部分。
简单伪代码:
char []buf = new char[1024]; int len = 0; //读一个数组大小,写一个数组大小方法。 while((len = fr.read(buf)) != -1){ fw.write(buf, 0, len); }
与之相对的是:
FileReader:文件字符读取流,构造方法是:
FileReader(File file) 创建一个新的 FileReader ,给出 File读取。
FileReader(String fileName) 创建一个新的 FileReader ,给定要读取的文件的名称。
常用的方法是:
read() 读一个字符 该方法将阻塞,直到字符可用,返回结果为读取的该字符对应的作为0到65535( 0x00-0xffff )范围内的整数,如果已经达到流的末尾,则为-1。
read(char[] cbuf) 将字符读入数组。 该方法将阻塞,直到某些输入可用,返回结果为读取到的字符数,如果已经达到流的结尾,则为-1;
简要代码:FileReader filereader = new FileReader("11.txt");
char[] buf = new char[3];
int count = filereader. read(buf);//将读取到的字符保存到数组中,
等价于: char[] buf = new char[3];
int len = 0;
while((len=filereader.read(buf))!= -1){
Syso(new String(buf,0,len));
}
read(char[] cbuf,int off,int len) 将字符读入数组的一部分。 该方法将阻塞,直到某些输入可用,如果已经达到流的结尾,则为-1 read(CharBuffer target)尝试将字符读入指定的字符缓冲区。 缓冲区用作字符存储库:唯一的更改是put操作的结果。 不执行缓冲器的翻转或倒带。如果此字符源结束,则为-1
BufferWriter: 缓冲区写,他只是封装了数组,来提高流中数据的的操作效率的。缓冲区不调用底层资源,真正调用的是比如FileReader。构造方法是:
BufferedWriter(Writer out) 创建使用默认大小的输出缓冲区的缓冲字符输出流。
BufferedWriter(Writer out, int sz) 创建一个新的缓冲字符输出流,使用给定大小的输出缓冲区。
常用方法:
newLine() 写一个行分隔符。
write(char[] cbuf, int off, int len) 写入字符数组的一部分。
write(int c) 写一个字符
write(String s, int off, int len) 写一个字符串的一部分。
BufferReader:缓冲区读;构造函数:
BufferedReader(Reader in) 创建使用默认大小的输入缓冲区的缓冲字符输入流。
BufferedReader(Reader in, int sz) 创建使用指定大小的输入缓冲区的缓冲字符输入流。
常用方法:
readLine() 读一行文字。读一行文字。 一行被视为由换行符('\ n'),回车符('\ r')中的任何一个或随后的换行符终止。 如果已达到流的末尾,则为null
read() 读一个字符
read(char[] cbuf, int off, int len) 将字符读入数组的一部分。
Windows下面的路径用java代码实现是: C:\\0.mp3
GBK中中文是2个字节,一定是。
UTF-8中一个字节能装下那就占一个字节,如果一个字节不够那就2个字节,以此类推
在流中只有转换流的构造方法中才存在让用户制定读取和写入的字符集编码Charset。
Writer在写一个中文的时候,大概是:比如要写“你好”,会首先拿着你字去码表里面找对应的字节,然后再把该字节写入到硬盘。如果出现类似繁体字的中文,那一定是用其他码表编的,然后用GBK解的,因为只有GBK才有中文。
BufferedReader : 构造方法:BufferedReader(Readerin) 创建使用默认大小的输入缓冲区的缓冲字符输入流。
BufferedReader(Reader in, int sz) 创建使用指定大小的输入缓冲区的缓冲字符输入流。
常用方法:
read() 读一个字符(这个歌读取字符是从缓冲区中读取的,不是从硬盘或者其他地区)字符读取,作为0到65535( 0x00-0xffff )范围内的整数,如果已经达到流的结尾,则为-1。
read(char[] cbuf, int off, int len) 将字符读入数组的一部分。
readLine() 读一行文字。读一行文字。 一行被视为由换行符('\n'),回车符('\ r')中的任何一个或随后的换行符终止。结果:
包含行的内容的字符串,不包括任何行终止字符,如果已达到流的末尾,则为null
| - - - LineNumberReader bufferedReader的子类 用来记录和设置读取时候的行号的。
简单演示代码如下:
FileReader fr = new FileReader(“1.txt”);
LineNumberReader lnr = new LineNumberReader(fr);
String ss = null;
While((ss = lnr.readline())!=null){
Syso(lnr. getLineNumber()+”:” +ss);
}
Lnr.close();
结果是:1:asd
2:rtyyyy
3:heeheh
如果上面代码稍微改为:
While((ss = lnr.readline())!=null){
Lnr. setLineNumber(100)
Syso(lnr. getLineNumber()+”:” +ss);
}
结果是:100:asd
101:rtyyyy
102:heeheh
BufferedWriter: 构造方法:BufferedWriter(Writerout) 创建使用默认大小的输出缓冲区的缓冲字符输出流。
BufferedWriter(Writer out, int sz) 创建一个新的缓冲字符输出流,使用给定大小的输出缓冲区。
常用方法:
write(int c) 写一个字符
write(char[]cbuf, int off, int len) 写入字符数组的一部分。
newLine() 写一行行分隔符。
write(Strings, int off, int len) 写一个字符串的一部分。
利用之前加上刚才学到的缓冲区实现简单的文件复制:
代码:
FileReader fr = new FileReader(“buf.txt”);
BufferedReader bufr = new BufferedReader(fr);
FileWriter fw = new FriterWriter(“buf_copy.txt”);
BufferedWriter bufw = new BufferedWriter(fw);
@@begin
Int ch = 0;
While((ch = bufr.read())!=-1){ //注意此处使用的是read()方法,是从缓冲区中读取的,是高效的。注意和其他流中read()的区别,其他流是从硬盘或者其他地方读取的,不是高效的。
Bufw.writer(ch);
}
@@end
Bufr.close(); //把缓冲区close,一同把缓冲区包装的流都关了。
Bufw.close() ;
在@@标记开始和结束的一段代码可以换成更高效的方法:
String line = null;
While((line = bufr.readlinw())!=null){
Bufw.writer(line);
Bufw.newline();//增加一个换行
Bufw.flush();
}
装饰者设计模式的引入以及和继承的区别,还有以及在流中的作用有哪些?
对一组对象的功能进行增强时,就可以使用该模式进行问题的解决;
装饰者设计模式的出现,就是人们在解决一类问题时总结的经验,在设计模式中进行总结,就出现了装饰者模式,这种模式就是为了解决问题而出现的。
先写个demo进行大概的了解,看起来不抽象容易理解和后面引入作用时可以很好理解什么是装饰者模式;
代码如下:
新建类PersonDemo,
Public class PersonDemo{
Public static void main(String[] args){
Person p = new person();
p.chifan();
}
}
Public classperson{
Void chifan(){
Syso(“吃饭”);
}
}
结果是: 吃饭
上面代码是最基本的,但是如果后来的业务不满足之前的实现,不如上面的“吃饭“,然们的生活好了,不在是仅仅满足吃饭的业务,要享受更多的业务,比如吃饭之前喝个开胃酒,饭后吃个甜点什么的,原来的代码就实现不了了,我们不能去改源码,那样会引发灾难性的后果,所以代码改为下面:
新建类PersonDemo,
Public class PersonDemo{
Public static void main(String[] args){
Person p = new person();
//这里更改 进行增强
NewPerson np = new NewPerson(p);
np.chifan();
}
}
Public classperson{
Void chifan(){
Syso(“吃饭”);
}
}
//这个类的出现就是为了增强person的chifan方法
Public class NewPerson{
Person p;
NewPerson(person){
This.p = p;
}
Void chifan(){
Syso(“喝个开胃酒”);
p.chifan(); //调用原来的方法,并在这个方法之前和之后对他进行增强方法
Syso(“吃个甜点”);
}
}
结果是:喝个开胃酒
吃饭
吃个甜点
目前先总结一下:
装饰是什么,就和我们装修房子差不多,开始是毛坯房,我们在毛坯房的基础上进行装修,外观进行改变,实质还是那个房子,这就是装饰。
疑问:
说到这里肯定会有人说,为什么要用装饰,我用继承一样可以实现啊,我写一个类,继承person,然后对chifan的方法进行重新,就可是实现先喝开胃酒,然后吃饭,在然后吃甜点的一系列过程。
代码如下:
Public NewPerson2 extends person{
Void chifan(){
Syso(“喝个开胃酒”);
super.chifan();
Syso(“吃个甜点”);
}
}
通过继承一样可以实现;
那到底是装饰者模式好呢还是继承好呢?下面就进行分析;
|-- 装饰和继承都能实现一样的特点,进行功能的扩展增强。
有什么区别呢;
声明下面写的流只是一种操作数据的形式,操作文本的叫:TextWriter
操作媒体的叫:MediaWriter
这两个对象都有“写“的方法,然后把写的方法抽出来作为父类,就成了下面的设计体系:
Writer
|-- TextWriter //用于操作文本
|-- MediaWriter //用于操作媒体
我们想要对操作动作进行效率的提高
按照面向对象,可以通过继承对具体的进行功能的扩展;
效率提高就需要加入缓冲技术。然后之前的设计体系就变为下面的形式:
Writer
|-- TextWriter //用于操作文本
|-- BufferTextWriter //加入缓冲技术的操作文本的流对象,目前这个流只造作TextWriter
|-- MediaWriter //用于操作媒体
|-- BufferMediaWriter //加入缓冲技术的操作媒体的流对象,目前这个流只造作MediaWriter
这种设计体系,效率是提高了,但是这样做好像有问题,每一种Writer的流对象如果想提高效率都要有一个缓冲技术流存在,试想一下,如果Writer需要功能扩展,又多了一种流对象,这个流需要提高效率,那你也要写一个缓冲技术流,这时就会发现只为提高功能进行的继承,导致继承体系越来越臃肿就不够灵活。
重新思考问题:
既然加入的都是同一种技术------缓冲;
前一种是让缓冲和具体的流对象进行想结合;
可不可以,把缓冲进行单独的封装,哪个对象需要缓冲,就将哪个对象和缓冲关联。
伪代码如下:
Class Buffer{
Buffer(TextWriter w ){}
Buffer(MediaWriter w){}
}
这样实现,就是把每一种流传入缓冲区对象中,但是对于缓冲区类不灵活,所以设计为传入缓冲区一个TextWriter 和 MediaWriter 共同的父类即可。
伪代码:
Class Buffer{
Buffer(Writer w){}
}
Buffer缓冲完writer的写的动作后,它最终体现出来的还是“写“功能,所以它要继承Writer。
分析完上面的问题后,之前的体系就变为下面:
Writer
|-- TextWriter //用于操作文本
|-- MediaWriter //用于操作媒体
|-- BufferWriter //用于提高效率的
所以最终结果是:装饰比继承灵活。
特点:装饰类和被装饰类都必须所属于同一个接口或者父类。
字节流:
FileInputStream :字节输出流
构造函数:
FileInputStream(File file) 通过打开与实际文件的连接创建一个 FileInputStream ,该文件由文件系统中的 File对象 file命名。
FileInputStream(String name) 通过打开与实际文件的连接来创建一个FileInputStream ,该文件由文件系统中的路径名 name命名。
常用的方法:
available();读取流对应的文件的大小(字节数)
read()从该输入流读取一个字节的数据。如果达到文件的末尾, -1
read(byte[]b) 从该输入流读取最多 b.length个字节的数据为字节数组。读入缓冲区的总字节数,如果没有更多的数据,因为文件的结尾已经到达, -1
read(byte[] b, int off, int len) 从该输入流读取最多 len字节的数据为字节数组。读取到缓冲区的总字节数,如果没有更多的数据,因为文件的结尾已经到达, -1。
常用代码:
FileInputStreamfis = new FileInputStream(“1.txt”);
//Fis.read(); //用字节流读取abc可以,读取中文就不行,读取中文用字符流
@@@begin
Int len = 0;
While((len = fis.read())!= -1){
Syso((char)len);
}
@@@end
Fis.close();
其中红色标识位置可以更改为:
Byte[] buf = new Bytep[1024];
Int len = 0;
While((len = fis.read(buf))!=-1){
Syso(new String(buf,0,len));
}
其中红色标识位置还可以更改为:
使用available()方法:
Byte[] buf = new Byte[fis. Available()];
Fis.read(buf);
Syso(new String(buf));
这种方法要少用,因为如果fis关联的文件很大,那么创建的缓冲区也很大,内存就会溢出,没有其他的内存供其它使用。
FileOutPutStream :字节输出流
构造函数:
FileOutputStream(File file) 创建文件输出流以写入由指定的 File对象表示的文件。
FileOutputStream(Filefile, boolean append) 创建文件输出流以写入由指定的 File对象表示的文件。
FileOutputStream(Stringname) 创建文件输出流以指定的名称写入文件。
FileOutputStream(Stringname, boolean append) 创建文件输出流以指定的名称写入文件。
常用的方法:
write(byte[]b) 将 b.length个字节从指定的字节数组写入此文件输出流。
write(byte[]b, int off, int len) 将 len字节从位于偏移量 off的指定字节数组写入此文件输出流。
write(intb) 将指定的字节写入此文件输出流。
常用的代码:
FileOutPutStreamfos = new FileOutPutStream(“1.txt”);
Fos.write(“asdcv”.getbytes());
//Fos.flush(); 其实这一步没用,底层实现为空。
Fos.close();
使用FileInputStream 和 FileOutPutStream完成媒体文件的复制;
代码如下:
FileInputStream fis = newFileInputStream(“C:\\0.mp3”);
FileOutPutStream fos = newFileOutPutStream(“C:\\1.mp3”);
Byte[] buf = new Byte[1024];
Int len = 0;
While((len = fis.read(buf))!=-1){
Fos.write(buf,0,len);
}
Fis.close();
Fos.close();
使用缓冲区完成上面的实现:
FileInputStream fis = newFileInputStream(“C:\\0.mp3”);
BufferedInputStream bufis = newBufferedInputStream(fis);
FileOutPutStream fos = newFileOutPutStream(“C:\\1.mp3”);
BufferedOutputStream bufos = newBufferedOutputStream(fos);
Byte[] buf = new Byte[1024];
Int len = 0;
While((len = bufis.read(buf))!=-1){
bufos.write(buf,0,len);
bufos.flush();// 缓冲区的flush是有用的
}
bufis.close();
bufos.close();
简单修改也可以下面的:
FileInputStreamfis = new FileInputStream(“C:\\0.mp3”);
BufferedInputStreambufis = new BufferedInputStream(fis);
FileOutPutStream fos = newFileOutPutStream(“C:\\1.mp3”);
BufferedOutputStream bufos = newBufferedOutputStream(fos);
Int ch= 0;
While((ch = bufis.read())!=-1){
bufos.write(ch);
}
bufis.close();
bufos.close();
这两种的区别是,第二种不在使用自定义的数组,使用直接都缓冲区一个字节的形式,效率比着第一种不低。
当然第三种,也是不建议使用的,也要说一下:
FileInputStreamfis = new FileInputStream(“C:\\0.mp3”);
FileOutPutStream fos = newFileOutPutStream(“C:\\1.mp3”);
Byte[] buf= new Byte[fis.avaliable()];
Fis.read(buf);
fos.write(buf);
fis.close();
fos.close();
第四种,同样不建议使用:
FileInputStreamfis = new FileInputStream(“C:\\0.mp3”);
FileOutPutStream fos = newFileOutPutStream(“C:\\1.mp3”);
Int ch= 0;
While((ch = fis.read())!=-1){
fos.write(ch);
}
fis.close();
fos.close();
这个和第二中的区别是,这个是从源头不是内存缓冲区中读取一个一个的字节,很慢,强烈不建议使用。
思考一下:
用字符流复制一张图片可不可以?为什么?
不行。读完了字节数据后,并没有直接像目的里面写,而是去查表,如果读到的字节数据在表里查不到内容呢,文字有特定的编码格式,但是媒体是没有的,就会用一一些未知字符代替,写到目的数据里面。
“标准”输入输出流:
InputStream in = System.in;
Int ch = In.read();
Syso(“ch:”+ch);
注解:标准输入的字节流,的read方法,是阻塞的,等待着键盘的输入;
此键盘录入只读取一个字节;
InputStream in = System.in;
Int ch = In.read();
Syso(“ch:”+ch);
In.close();
InputStream in2 = System.in;
Int ch2 = in2.read();
注解:上面运行后会,第一次等待输入后,在等待输入in2后,会报错,从系统中获取的流,标准的流对象只有一个,你在程序中关闭后,就得不到了,其他流关闭后,在重新new,一样是可以用的所以这个标准的流是不需要关的。
上面的输入是针对一个字节,如果想操作整个输入的字符串的结果,我们可以把输入的字节方法一个容器,这个容器StringBuilder 或者StringBuffer都可以,伪代码如下:
StringBuilder sb = new StringBuilder();
InputStreamin = System.in;
Int ch = 0;
While((ch = In.read()) != -1){
Sb.append((char)ch);
String ss = sb.toString();
If(ss.eq(“over”)){
Break;
}
}
Syso(“ch:”+ch);
注解2:之前说了这个标准流读取的是一个字节,如果你输入一个a(代表一个字节),则我们只需要读取(read)一次就可以读完,如果你输入一个中文,则需要读取(read)两次才可以读完。
伪代码:
InputStream in = System.in;
Int ch = in.rrad();
Syso(ch);
Int ch1 = in.read();
Syso(ch1);
输入:你
结果是:196
227
但是:如果我们把这个字节流转化为字符流(下面将会讲到的),则只需要读取一次即可,伪代码如下:
InputStream in = System.in;
InputStreamReaderisr = new InputStreamReader(in);
Intch = isr.read();
Syso(ch);
输入:你
结果是:20320
因为打印结果是int值,你可以用 (char)ch把上面的“你”打印出来。
转换流inputstreamreader OutputStreamWriter:
OutputStreamWriter: 字符的桥梁流以字节流:向其写入的字符编码成使用指定的字节charset 。 它使用的字符集可以由名称指定,也可以被明确指定,或者可以但是接受平台的默认字符集。
Inputstreamreader和是从字节流到字符流的桥:它读取字节,并使用指定的charset将其解码为字符。 它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集。
注释:字符----》字节 是编码
字节---》字符 是解码
简单理解:把看不懂的转换为看得懂的 就是解码
把看得懂的转化为看不懂的 就是编码
Inputstreamreader:简单的伪代码:
InputStream in =System.in;
Inputstreamreader isr = new Inputstreamreader(in);
BufferedReaderbufr = new BufferedReader(isr);
Stringline=null;
While((line=bufr.readLine())!=null){
Syso(line);
}
OutputStreamWriter: 简单伪代码:
OutPutStream out = System.out;
OutputStreamWriter osw = new OutputStreamWriter(out);
BufferedWriter bufw = new BufferedWriter(osw);
Bufw.write(“aasscc”);
Bufw.newline();
Bufw.flush();
Bufw.close();
上面的部分代码可以优化为:
BufferedReader bufr = new BufferedReader(newInputStreamReader(System.in));
BufferedWriterbufw = new BufferedWriter(new OutputStreamWriter(System.out));
三句代码合为一句。
图分析:
看了上面的一个问题,你可能有疑问了,那就是,我们之前操作文本文件的时候,用fileWriter写一个字符到硬盘上,为什么能成功,操作的是字符,但是硬盘上存的是字节数据,为什么? 其实我们看io流的结构图的时候我们会发现,fileWriter有一个父类就是OutPutStreamWriter,我们操作fileWriter的write方法其实是使用的它父类的方法,它父类负责把字符转化成字节保存在硬盘上,现在就明白了吧。FileReader也是同样的道理,它的父类是InputStreamReader,我们操作FileReader的read方法其实就是使用的是它父类的读方法,并且把字节转化为字符,让我们能看懂。
File类:
上面的讲到的流只能操作数据内容,像文件的创建日期,文件大小,文件名字等等是操作不了的,java给我们提供了file类可以做这些事情。
构造方法:
File(File parent, String child)
从父抽象路径名和子路径名字符串创建新的 File实例。
File(Stringpathname)
通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例。
File(String parent, String child)
从父路径名字符串和子路径名字符串创建新的 File实例。
简单伪代码:
Filef1 = new File(“c:\\a.txt”);
Filef2 = new File(“c:\\”,”a.txt”);
Filef = new File(“c:\\”);
Filef3 = new File(f,”a.txt”);//这种方法是把c:\\封装为一个file。
常用方法分隔符:
定义file构造的时候,我们在windows下面写的分隔符是\\,在linux上分隔符是/,如果我们用windows的分隔符运行在linux上,则可能程序会挂掉,所以我们程序要用运行在某个平台上的分隔符动态改变。
static
StringpathSeparator
与系统相关的路径分隔符字符,为方便起见,表示为字符串。
static char
pathSeparatorChar与系统相关的路径分隔符。
static
Stringseparator
与系统相关的默认名称 - 分隔符字符,以方便的方式表示为字符串。
static char
separatorChar与系统相关的默认名称分隔符。
上面的4个方法separator separatorChar,这两个方法会常被用到,一个是返回字符串 一个是返回字符,用的时候根据情况选择即可。pathSeparator,pathSeparatorChar不常被用到,主要用在设置环境变量中的分隔符使用:还是用;举例本机的环境变量是:.;%java_home%;%java_home%\lib\tools.jar;%java_home%\lib\dt.jar
常用方法:
File.
getAbsolutePath
()
结果是绝对路径 c:\\aa\\b.txt
File.
getPath()
结果返回上面路径的b.txt
File f =new File(“c
:\\”);
String[]names = f.list();
For(Stringname : names){
Sys(name);
}
结果是得到c盘下面的文件以及文件夹的名称,包含隐藏文件。
注意:调用list方法的File对象中封装的必须是目录,否则会发生空指针异常。还有一种可能会空指针,你想访问的目录是系统级的目录,我们基本上没权限访问,也会发生空指针异常。
如果我们有个需求是想要获得c盘下的所有.java文件,该怎么做?
Java
给我们提供了一个list的另一个方法,里面传入一个过滤器,list(FilenameFilter filter),其中FilenameFilter是个接口,我们需要用一个类去实现它,伪代码如下:
ClassFilterByJava implements FilenameFilter{
@
重载
Public Boolean accept(File dir,Stringname){
Return name.endwith(“.java”);
}
}
在主函数中写:
Filef = new File(“c
:\\”);
String[]names = f.list(new FilterByJava());
For(Stringname : names){
Sys(name);
}
到这里即可,就可以筛选以java结尾的文件了。
文件和目录的创建:
File ff =new File(“b.txt”);
ff.createNewFile
创建文件,如果文件不存在,则创建,如果存在,则不创建。返回的是boolean
File ff =new File(“b.txt”);
ff.delete();
解释:如果你删除的是一个目录,目录里面有文件存在,则删除此目录失败,windows是从里向外删的。而且这个删除是永久删除不走回收站。而且如果你的File封装是多级目录,比如abc\\q\\ss\\c,调用删除方法,删除的其实只有c目录,因为你file封装的其实只是c,abc q ss都是父目录。现在是abc\\q\\ss\\c,你想删掉abc,删不掉,因为abc下面有内容,删不掉。
真想删掉,需要用到代码实现(递归):
File f = new File(“e:\\demodir”);
removeDir(dir);
public static void removeDir(File dir){
File [] files = dir.listFiles();
For(File file : files){
If(file.isDirectory()){
removeDir(file);
}else{
File.delete();
}
}
}
创建目录:File ff = new File(“abc”);
Boolean b = ff.mkdir(); //mkdir其实就是make directory简写
Mkdir()创建是创建单一目录的,如果想多级目录是mkdirs()方法。
创建目录如果存在则创建失败,如果不存在则创建成功。
判断:
File f = new File(“b.txt”);
Boolean b = f.exists();//判断文件是否存在
File f = new File(“b.txt”);
//判断是否是文件和目录 前提是文件b.txt存在要不然全是false
Boolean b = f.isFile();//判断是否是文件true
Boolean b2 = f.isDirectory();//判断是否是目录false
打印流PrintWriter 和PrintStream:
PrintStream
(
Filefile)
使用指定的文件创建一个新的打印流,而不需要自动换行。
PrintStream(File file, String csn)
使用指定的文件和字符集创建新的打印流,而不需要自动换行。csn - 支持的名称charset
PrintStream(OutputStream out)
创建一个新的打印流。
PrintStream(OutputStream out, boolean autoFlush)
创建一个新的打印流。
autoFlush - 一个布尔值 如果为真,则每当写入一个字节数组时,输出缓冲区将被刷新,其中println方法之一被调用,或换行字符( '\n' )被写入
PrintStream(String fileName)
使用指定的文件名创建新的打印流,无需自动换行。
PrintStream(String fileName, String csn)
创建一个新的打印流,不需要自动换行,具有指定的文件名和字符集。
伪代码:
PrintStream p = new PrintStream(“info.txt”);
p.print(97); //这个print是先把97转化为字符串在写入输出
之前的p.write(97);//结果是a,这个write只写一个字节,保留int值的最低8为,舍弃三个高8位。
System.out返回的就是PrintStream,然后System.out.print()方法其实就是调用的输出流PrintStream的print方法。
序列流SequenceInputStream:
表示其他输入流的逻辑级联。 它从一个有序的输入流集合开始,从第一个读取到文件的结尾,然后从第二个文件读取,依此类推,直到最后一个输入流达到文件的结尾。
构造方法:
SequenceInputStream(Enumeration<?extends InputStream> e)
初始化新创建 SequenceInputStream通过记住参数,它必须是一个 Enumeration产生对象,它们的运行时类型是 InputStream 。
SequenceInputStream(InputStreams1, InputStream s2)
通过记住两个 SequenceInputStream来初始化新创建的SequenceInputStream,这些参数将按顺序读取,首先是 s1 ,然后是 s2 ,以提供要从此 SequenceInputStream读取的字节。
简单使用的代码如下:
Vector<FileInputStream> v =new Vector<FileInputStream>();
v.add(newFileInputStream(“1.txt”));
v.add(newFileInputStream(“2.txt”));
v.add(newFileInputStream(“3.txt”));
Enumeration<FileInputStream>en = v.elements();
SequenceInputStream sis = new SequenceInputStream(en);
FileOutPutStream fos = newFileOutPutStream(“4.txt”);
Byte[] buf = new byte[1024];
Int len = 0;
While((len = sis.read(buf))!=-1){
Fos.write(buf,0,len);
}
Sis.close();
Fos.Close();
结果:就是把1.txt 2.txt 3.txt 4.txt数据写入到4.txt中。
但是对于上面的程序有点需要改进的,(程序还是可以用的)就是在学习集合的时候我们就知道vector要慎用,由于它效率太低,下面进行改进:
ArrayList<FileInputStream>a1 = new ArrayList< FileInputStream >();
For(int I = 0;i<=3;i++){
A1.add(new FileInputStream(x+”.txt”));
}
FinalIterator<FileInputStream> it = a1.iterator();
Enumeration<FileInputStream>en = new Enumeration<FileINputStream>(){
@Override
Public boolean hasMoreElements(){
Return it.hasNext();
}
@Override
Public FileInputStream nextElement(){
Return it.next();
}
}
SequenceInputStream sis = new SequenceInputStream(en);
FileOutPutStream fos = newFileOutPutStream(“4.txt”);
Byte[] buf = new byte[1024];
Int len = 0;
While((len = sis.read(buf))!=-1){
Fos.write(buf,0,len);
}
Sis.close();
Fos.Close();
结果同上。
上面这个自己定义枚举方法 自己实现太麻烦,为什么我要写出来,是因为下面的实现其实底层实现就是通过上面那个实现的,简单的实现代码如下:
ArrayList<FileInputStream>a1 = new ArrayList< FileInputStream >();
For(int I = 0;i<=3;i++){
A1.add(new FileInputStream(x+”.txt”));
}
Enumeration<FileInputStream>en = Collections.enumeration(a1);
SequenceInputStream sis = new SequenceInputStream(en);
FileOutPutStream fos = newFileOutPutStream(“4.txt”);
Byte[] buf = new byte[1024];
Int len = 0;
While((len = sis.read(buf))!=-1){
Fos.write(buf,0,len);
}
Sis.close();
Fos.Close();
结果同上。
针对上面的流,做一个简单的应用:
文件切割和文件合并:
文件切割:
Public static void split(File dir){
FileInputStream fis = newFileInputStream(dir);
FileOutPutStream fos = null;
Byte[] buf = new byte[1024*1024]; //1M的缓冲区
Int len = 0;
Int count = 1;
While((len = fis.read(buf)) != -1){
fos = new FileOutPutStream((count++)+”.part”);
fos .write(buf,0,len);
}
Fis,close();
Fos.close();
}
调用的主函数main:
File dir = new File(“c:\\parts\\0.bmp”);
Split(dir);
结果是:
文件合并:
Public static void mergeFile(File dir){
ArrayList<FileInputStream> a1 =new ArrayList<FileInputStream>();
For(int x = 1; x<3; x++){
a1.add(new FilInputStream(newFile(dir,x+”.part”)));
}
}
Enumeration<FileInputStream> en =Collections.enumeration(a1);
SequenceInputStream sis = newSequenceInputStream(en);
FileOutPutStream fos =new FileOutPutStream(new File(dir,”1.bmp”));
Byte[] buf = new Byte[1024];
Int len = 0;
While((len = sis.read(buf)) != -1){
Fos.write(buf,0,len);
}
Sis.close();
Fos.close();
调用的main函数中:
File dir = new File(“c:\\parts”);
mergeFile(dir);
即可;
结果是:把c:\\parts下存在的几个文件碎片合并为1.bmp;
ObjectInputStream 和 ObjectOutPutStream: 能操作对象的输入输出流。被操作的对象必须实现serializable.也就是说实体类只有实现了serializable才能写到文件中,;
平时我们想保存一个name张三,我们直接就把name张三保存起来,但是如果我们还想在保存name张三对应的age 24,这两个值之间是有联系的,所以就需要把他俩包装成一个对象,并保存起来。简单说就是我们把堆内存new出来的对象保存在硬盘文件中;
对象的序列化:我们把A对象保存在文件中,然后再把B对象也保存在文件中,一次类推,AB都是有顺序的,就叫做序列化,(简单理解就是有顺序的排列对象,向一个文件中写多个对象就产生了序列化);
简单demo:
自定义一个person:
Public person implements serializable{
String name;
String age;
Public person(String name,Stringage){
This.name = name;
This.age = age;
}
Set //略
Get //略
}
Main函数:
ObjectOutPutStream oos = new ObjectOutPutStream(new FileOutPutStream(“1.txt”));
Oos.writeObject(new Person(“zhangsan”,”25”));
Oos.close();
结果就是生成了一个1.txt文件,文件中保存的是我们看不懂的东西,通过查询api文档,文档上写的是:对象的默认序列化机制写入对象的类,类签名以及所有非瞬态和非静态字段的值。(类签名是底层实现的)。针对非瞬态和非静态字段值做出解释:因为静态的值是保存在静态区,供程序共享此变量,不在堆区(放对象的地方),那些临时变量是在堆区,我们把堆区的对象写到硬盘上管静态值屁事,都不在一个区。
如果我们有个需求是:对象中有个值不是公共的static,也不想写到硬盘上,就需要给这个变量添加一个关键字修饰:transient,意思是短暂的,,,,能写到硬盘上是非瞬态的,你用transient修饰就是瞬态,就不写如到硬盘上。
读取上面代码存到硬盘中文件中的person对象:
ObjectInPutStream文档上的解释是:
ObjectInputStream反序列化先前使用ObjectOutputStream编写的原始数据和对象。
简单代码如下:
ObjectInPutStreamois = new ObjectInputStream(new FileInPutStream(“1.txt”));
Person p= (Person)ois.readObject();
Syso(p.getname()+p.getage());ois.close()
RandomAccessFile 随机访问文件类(在IO包里)
特点:
该类在Io包下面,但是它的父类是Object,所以它不属于流的体系;
该类的对象可以读也可以写;
该类内部维护了一个字节byte数组,并通过指针可以操作数组中的元素;
该类通过getFilePointer方法获取指针的位置和通过set方法设置指针的位置
该类因为内部维护的是一个字节数组,又是要操作读写文件,所以该对象就是将字节输入流和输出流进行了封装。(可以写基本类型的数据,或者字符串,可以读取一行等等)
该对象的源或者目的只能是文件,通过构造方法就可以知道。
该对象写入到文件中的数据后,你如果读取, 也要按照顺序来读取;
构造:
RandomAccessFile(File file, String mode);
创建从中读取和向其中写入的随机访问文件流
RandomAccessFile(String name,String mode);
创建从中读取和向其中写入的随机访问文件流
简单demo:
Public static void writeFile(){
RandomAccessFile raf = new RandomAccessFile(“ranaco.txt”,”rw”);
raf.write(“张三”.getBytes());
raf. writeInt (97);
raf.write(“小强”.getBytes());
raf. writeInt (99);
raf.close();
}
在main函数调用:
WriteFile();
结果是
在该程序中生成文件ranaco.txt,文件中内容是:张三a小强b
在文件中保存形式如下图所示:
上面是写,下面代码读取:
简单demo:
RandomAccessFile raf = new RamdomAccessFile(“ranacc.txt”,”r”);
Byte[] buf = new byte[4]; //读取4个字节,文件中“张三”就是4个字节
raf.read(buf); //图中0-3
String name = new String(buf);
Syso(name);
raf.close();
结果是:张三
如果还想接着读张三的年龄97这个数据,代码如下:
RandomAccessFile raf = new RamdomAccessFile(“ranacc.txt”,”r”);
Byte[] buf = new byte[4]; //读取4个字节,文件中“张三”就是4个字节
raf.read(buf); //图中0-3
String name = new String(buf);
Int age = raf.readInt();//图中4-7
Syso(name+age);
raf.close();
结果是:张三97
如果还想读小强的相关数据:
代码:
RandomAccessFile raf = new RandomAccessFile("1.txt","r");
byte[] buf = new byte[4];
raf.read(buf);
String name = new String(buf);
int age = raf.readInt();
System.out.println("---name:"+name+"----age:"+age);
raf.read(buf);
String name2 = new String(buf);
int age2 =raf.readInt();
System.out.println("---name2:"+name2+"----age2:"+age2);
结果是:---name:张三----age:97
---name2:小强----age2:99
如果向直接读小强的相关数据,(隔掉张三的相关数据):
代码:
RandomAccessFile raf = new RandomAccessFile("1.txt","r");
//raf. getFilePointer(); 得到指针位置,
raf.seek(8); //隔8个字节开始读取,这个方法是设置指针的位置
byte[] buf= new byte[4];
raf.read(buf);
String name = new String(buf);
int age = raf.readInt();
System.out.println("---name:"+name+"----age:"+age);
上面的1.txt文件中已经保存了一些数据(张三97小强99),我们可以通过这个类直接在数据99后面空白处直接开始读写。
RandomAccessFile raf = new RandomAccessFile("1.txt","rw");
raf.seek(100);
raf.write("王五".getBytes());
raf.close();
结果是:
管道流PipedInputStream 和 PipedOutPutStream:
管道输入流应连接到管道输出流; 管道输入流然后提供写入管道输出流的任何数据字节。典型地,数据被从一个读PipedInputStream对象由一个线程并且数据被写入到对应的PipedOutputStream通过一些其它线程。 不建议尝试从单个线程使用这两个对象,因为它可能会使线程死锁。
作用:
* 管道流的主要作用就是可以进行两个线程间的通信。一个线程作为管道输出流,另一个线程作为管道输入流,
* 在启动线程前,只需要将这两个线程的管道流连接到一起就可以。
* 这要就很方便的实现了两个线程间的通信。
Demo:
package io;
/*作用
* 管道流的主要作用就是可以进行两个线程间的通信。一个线程作为管道输出流,另一个线程作为管道输入流,
* 在启动线程前,只需要将这两个线程的管道流连接到一起就可以。
* 这要就很方便的实现了两个线程间的通信。
*
*
* */
importjava.io.PipedInputStream;
importjava.io.PipedOutputStream;
class Send implementsRunnable{
private PipedOutputStream pos = null;
public Send(){
this.pos = newPipedOutputStream(); //实例化管道输出流
}
@Override
public void run() {
String str ="zhejianggongshangdaxue";
try {
this.pos.write(str.getBytes());
} catch (Exception e) {
e.printStackTrace();
}
try {
this.pos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public PipedOutputStream getPos(){ //通过线程类获得管道输出流
return pos;
}
}
class Receive implementsRunnable{
private PipedInputStream pis = null;
public Receive(){
this.pis = new PipedInputStream(); //实例化管道输入流
}
@Override
public void run() {
byte b[]=new byte[1024];
int len= 0;
try {
len = this.pis.read(b);
} catch (Exception e) {
e.printStackTrace();
}
try {
pis.close();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("接收内容:"+new String(b,0,len));
}
public PipedInputStream getPis(){ //通过线程类获得管道输入流
return pis;
}
}
public classPipedInputStreamAndPipedOutputStream {
public static void main(String[] args){
Send s = new Send();
Receive r = new Receive();
try {
s.getPos().connect(r.getPis()); //连接两个线程的管道流
} catch (Exception e) {
e.printStackTrace();
}
new Thread(s).start();
new Thread(r).start();
}
}
DataInputStream 与 DataOutPutStream(操作基本数据类型):
DataOutputStream
数据输出流允许应用程序以适当方式将基本 Java 数据类型写入输出流中。然后应用程序可以使用数据输入流将数据读入。
DataInputStream
数据输入流允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型。应用程序可以使用数据输出流写入稍后由数据输入流读取的数据。对于多线程访问不一定是安全的。 线程安全是可选的,它由此类方法的使用者负责。
Demo:
/**
* 必须先使用DataOutputStream写入数据,然后使用DataInputStream读取数据方可。
*
* @author xy
*
*/
public class TestClass
{
public static void main(String[]args) throws Exception
{
TestClass t = newTestClass();
t.write();
t.read();
}
public void write() throwsException
{
String path =this.getClass().getClassLoader().getResource("test.txt").toURI().getPath();
OutputStream os = newFileOutputStream(path);
DataOutputStream dos = newDataOutputStream(os);
dos.writeDouble(Math.random());
dos.writeBoolean(true);
dos.writeInt(1000);
dos.writeInt(2000);
dos.flush();
os.close();
dos.close();
}
public void read() throwsException
{
InputStream instream =this.getClass().getClassLoader().getResourceAsStream("test.txt");
DataInputStream dis = newDataInputStream(instream);
double d = dis.readDouble();
boolean b =dis.readBoolean();
// 先写的先被读出来
int i1 = dis.readInt();
int i2 = dis.readInt();
instream.close();
dis.close();
System.out.println(d);
System.out.println(b);
System.out.println(i1);
System.out.println(i2);
}
}
结果是:
0.4683893857027681
true
1000
2000
ByteArrayInputStream 与 ByteArrayOutputSteam,源和目的都是操作内存的流:
缓冲区就是个byte数组, 因此ByteArrayInputStream构造要有源,那就是需要个byte数组;
ByteArrayOutputStream该类实现了将数据写入字节数组的输出流。 当数据写入缓冲区时,缓冲区会自动增长。
ByteArrayInputStream包含一个内部缓冲区,其中包含可以从流中读取的字节
Demo:
ByteArrayOutputStreambos = new ByteArrayOutputStream();
ByteArrayInputStream bins = new ByteArrayInputStream("abcdefg".getBytes());
int ch = 0;
while((ch = bins.read())!=-1){
bos.write(ch);
}
System.out.println("---"+bos.toString());
bos.close(); //其实操作内存的流不用关
bins.close();//其实操作内存的流不用关
字符读写类CharArrayReader和CharArrayWriter:
CharArrayReader 是字符数组输入流。它和ByteArrayInputStream类似,只不过ByteArrayInputStream是字节数组输入流,而CharArray是字符数组输入流。CharArrayReader 是用于读取字符数组,它继承于Reader。操作的数据是以字符为单位!
Demo:
privatestaticvoid testReader() throws IOException {
String str = "Hello world!";
// 构建字符输入流
CharArrayReader reader = newCharArrayReader(str.toCharArray());
// 从字符输入流读取字符
char[] chars = newchar[1024];
int len = reader.read(chars);
System.out.println(new String(chars, 0, len));
}
CharArrayReader 用于写入数据符,它继承于Writer。操作的数据是以字符为单位!缓冲区会随向流中写入数据而自动增长。可使用 toCharArray() 和 toString() 获取数据。
Demo:
privatestatic void testWriter() throws IOException {
CharArrayWriter writer = newCharArrayWriter(1024 * 1024);
// 将字符串写入到CharArrayWriter
String msg = "hello world!!!22121";
writer.write(msg.toCharArray());
System.out.println(writer.toString());
writer.close();
}
StringReader 和 StringWriter:
这两个类将String类适配到了Reader和Writer接口,在StringWriter类实现的过程中,真正使用的是StringBuffer,前面讲过,StringBuffer是一个可变类,由于Writer类中有许多字符串的操作,所以这个类用起来比较方便;在StringReader类中只定义一个String类即可,因为只涉及到类的读取,而没有修改等的操作,不会创建多个字符串而造成资源浪费。
Demo:
publicclassStringXXXTest {
publicstaticvoid main(String[]args) throws Exception{
// testReader();
testWriter();
}
privatestaticvoid testReader() throws Exception{
String str = "helloworld!!!";
// 将字符串转换成字符输入流
StringReader reader = new StringReader(str);
// 读取字符输入流中的数据
char[] buffer = newchar[1024];
int len = 0;
while ( (len = reader.read(buffer)) != -1 ) {
System.out.println( new String(buffer, 0, len) );
}
}
privatestaticvoid testWriter() throws Exception{
File file=new File("d:"+File.separator+"text.txt");
StringWriter writer = new StringWriter(1024 * 1024);
// 获取执行cmd命令后的信息
BufferedReader reader = new BufferedReader(
new InputStreamReader(newFileInputStream(file)) );
String line;
while ( (line = reader.readLine()) != null ) {
writer.write(line + "\n");
}
// 一次性将信息输出来
System.out.println("一次性:"+writer.toString() );
}
}