Java基础_IO流
来源:互联网 发布:淘宝违禁词查询工具 编辑:程序博客网 时间:2024/04/28 23:22
概述
按流向分为输入流和输出流
按操作数据分为字节流和字符流
字符流,基于字节流,但是字符流对象融合了编码表
文字用字符流,图片用字节流
IO体系的四个基类字节流的抽象基类
InputStream OutputStream
字符流的抽象基类
Reader Writer
由这四个类派生出来的子类名称都是以其父类名作为后缀,比如FileInputStream,FileReader
前缀名是流对象的功能
字符流
字符写入流FileWriter
//创建一个FileWriter对象,该对象一被创建就必须明确被操作的文件,而且该文件会被创建到指定的目录下,如果该目录下有同名文件,会被覆盖:FileWriter fw = new FileWriter("demo.txt"); //注意会抛出异常:比如文件路径不存在//调用write方法,将字符串写入到流中fw.write("abcde");//刷新流对象中的缓冲,将其中的数据刷到目的地中fw.flush();//关闭流资源,但是关闭之前会刷新一次内部的缓冲中的数据。因为Java会调用系统中的内容来完成数据的写入,完成之后必须将资源释放,所以close//将数据刷到目的地中。//和flush区别:flush刷新后,流可以继续使用,close刷新后,会将流关闭。fw.close();
IO异常
FileWriter fw = null;//在外面建立引用,在try里初始化,如果都写在try里,关闭流对象会发生编译错误,因为fw引用作用不到try以外try{fw = new FileWriter("demo.txt");fw.write("abcdefg");}catch (IOException e){System.out.println("catch:"+e.toString());}finally //流关闭,必须执行{try//close也会发生异常,需要单独用try处理{ if(fw!=null)//不判断会发生空指针异常fw.close();}catch (IOException e){System.out.println(e.toString());}}
文件的续写
/*演示对已有文件的数据续写。*/import java.io.*;class FileWriterDemo3{public static void main(String[] args) throws IOException{//传递一个true参数,代表不覆盖已有的文件。并在已有文件的末尾处进行数据续写。FileWriter fw = new FileWriter("demo.txt",true);fw.write("nihao\r\nxiexie");//为了在windows记事本下显示换行,用到了\r\n,linux系统为\nfw.close();}}
字符读取流FileReader
方式一:读取单个字符
//创建一个文件读取流对象,和指定名称的文件相关联。//要保证该文件是已经存在的,如果不存在,会发生异常FileNotFoundExceptionFileReader fr = new FileReader("demo.txt");//调用读取流对象的read方法。//read():一次读一个字符。而且会自动往下读。返回的是作为整数读取的字符int ch = 0;while((ch=fr.read())!=-1) //返回-1表示文件已经读到末尾{System.out.println("ch="+(char)ch);}fr.close();
方式二:通过字符数组进行读取
FileReader fr = new FileReader("demo.txt");//定义一个字符数组。用于存储读到字符。//该read(char[])返回的是读取的字符数。char[] buf = new char[1024]; //通常定义1024的整数倍,这里定义了一个2k大小的字符数组,因为一个字符是两个字节int len = 0;while((len=fr.read(buf))!=-1){System.out.println(new String(buf,0,len));//读到多少就打出多少,因为最后一次循环可能不会读满整个数组,如果只打整个数组就会出现多余的字符}fr.close();总结:方式二好,读一串存一下,再全部打印,效率高
拷贝文本文件
//将C盘一个文本文件复制到D盘。/*复制的原理:其实就是将C盘下的文件数据存储到D盘的一个文件中。步骤:1,在D盘创建一个文件。用于存储C盘文件中的数据。2,定义读取流和C盘文件关联。3,通过不断的读写完成数据存储。4,关闭资源。 */import java.io.*;class CopyText {public static void main(String[] args) throws IOException {copy_2();}//方式一:从C盘读一个字符,就往D盘写一个字符。(未加入IO异常处理,所以需要抛出异常对象)public static void copy_1()throws IOException {//创建目的地。FileWriter fw = new FileWriter("RuntimeDemo_copy.txt");//与已有文件关联。FileReader fr = new FileReader("RuntimeDemo.java");int ch = 0;while((ch=fr.read())!=-1) {fw.write(ch);}fw.close();fr.close();}//方式二:利用缓存数组读写(加入了IO异常处理)public static void copy_2() {FileWriter fw = null;FileReader fr = null;try{fw = new FileWriter("SystemDemo_copy.txt");fr = new FileReader("SystemDemo.java");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{if(fr!=null)try{fr.close();}catch (IOException e){ throw new RuntimeException("读取流关闭失败");}if(fw!=null)try{fw.close();}catch (IOException e){throw new RuntimeException("写入流关闭失败");}}}}
要点:read(buf)使用数组进行读取,write(buf,0,len)使用数组进行写入
字符流的缓冲区
BufferedWriter
该缓冲区对象提供了一个跨平台的换行符 newLine()方法
//创建一个字符写入流对象。FileWriter fw = new FileWriter("buf.txt");//为了提高字符写入流效率。加入了缓冲技术。//只要将需要被提高效率的流对象作为参数传递给缓冲区的构造函数即可。BufferedWriter bufw = new BufferedWriter(fw);for(int x=1; x<5; x++){bufw.write("abcd"+x);bufw.newLine();bufw.flush(); //防止因为断电导致数据丢失,所以刷新一句,写入一句}//记住,只要用到缓冲区,就要记得刷新。//bufw.flush();//其实关闭缓冲区,就是在关闭缓冲区中的流对象,不用再关闭流对象了bufw.close();
BufferedReader
字符读取流缓冲区//创建一个读取流对象和文件相关联。FileReader fr = new FileReader("buf.txt");//为了提高效率。加入缓冲技术。将字符读取流对象作为参数传递给缓冲对象的构造函数。BufferedReader bufr = new BufferedReader(fr);String line = null;while((line=bufr.readLine())!=null){System.out.println(line);//注意这里的打印加了换行符,因为readLine不返回回车符}bufr.close();
BufferedReader bufr = null;BufferedWriter bufw = null;bufr = new BufferedReader(new FileReader("BufferedWriterDemo.java"));bufw = new BufferedWriter(new FileWriter("bufWriter_Copy.txt"));
readLine()方法原理
MyBufferedReader
/*明白了BufferedReader类中特有方法readLine的原理后,可以自定义一个类中包含一个功能和readLine一致的方法。来模拟一下BufferedReader*/import java.io.*;class MyBufferedReader extends Reader //需要继承Reader,因为装饰类和被装饰类属于同一体系{private Reader r;//技巧:定义一个 是的在整个类中都有效MyBufferedReader(Reader r){this.r = r;}//可以一次读一行数据的方法。public String myReadLine()throws IOException //这里不用try和catch异常,而只将异常抛出,因为这里定义的是供调用者使用的功能,应该让调用者去处理异常{//定义一个临时容器。原BufferReader封装的是字符数组。//为了演示方便。定义一个StringBuilder容器。因为最终还是要将数据变成字符串。StringBuilder sb = new StringBuilder();int ch = 0;while((ch=r.read())!=-1){if(ch=='\r')//这里不需要强转continue; //读到该字符,不存入,继续while的下一循环读取下一个字符if(ch=='\n')return sb.toString();//读到该字符返回数组容器中的字符串形式elsesb.append((char)ch);}if(sb.length()!=0) //缓冲区里面只要有数据,就把剩下的数据返回来,避免了因为最后一行末尾没有换行符而不能将最后一行输出的问题return sb.toString();return null;}/*覆盖Reader类中的抽象方法,因为继承了该类*/public int read(char[] cbuf, int off, int len) throws IOException{return r.read(cbuf,off,len) ; //自己覆写该类麻烦,可以用传进来的子类来实现}public void close()throws IOException//需要覆写抽象的close方法,因为每个流对象关闭实现不同,直接关闭资源的和调用流对象close方法的两种{r.close();}public void myClose()throws IOException //关闭资源{r.close();}}
LineNumberReader
跟踪行号的缓冲字符输入流。有setLineNumber(int num)方法和getLineNumber方法设置开始行号和获取行号自定义跟踪行号的缓冲字符输入流MyLineNumberReader
import java.io.*;class MyLineNumberReader extends MyBufferedReader//使用装饰设计模式,继承父类的功能,利用关键字super引用父类对象{private int lineNumber;MyLineNumberReader(Reader r){super(r);//父类中已经进行了传递,直接调用父类的功能,因为<span style="font-family: Arial, Helvetica, sans-serif;">当父类中没有空参数的构造函数时,子类中必须手动通过super语句来显示指定要访问的父类中的构造函数</span>}public String myReadLine()throws IOException{lineNumber++;//每读取一行,就调用一次myReadLine方法,行号就自增一次。return super.myReadLine();//父类中已经有了实现,直接调用}public void setLineNumber(int lineNumber){this.lineNumber = lineNumber;}public int getLineNumber(){return lineNumber;}}
装饰设计模式
当想要对已有的对象进行功能增强时,可以定义类,将已有对象传入,基于已有的功能,并提供加强功能。
装饰类通常会通过构造方法接收被装饰的对象。
并基于被装饰的对象的功能,提供更强的功能。所以该装饰类需要继承同一个父类,因为只是功能增强,事物没有变。
装饰和继承的区别
|--MyTextReader
|--MyBufferTextReader
|--MyMediaReader
|--MyBufferMediaReader
|--MyDataReader
|--MyBufferDataReader
未使用多态的装饰设计:
class MyBufferReader
{
MyBufferReader(MyTextReader text)
{}
MyBufferReader(MyMediaReader media)
{}
}
上面这个类扩展性很差。
找到其参数的共同类型。通过多态的形式。可以提高扩展性:
class MyBufferReader extends MyReader
{
private MyReader r;
MyBufferReader(MyReader r)
{}
}
使用装饰设计:
MyReader//专门用于读取数据的类。
|--MyTextReader
|--MyMediaReader
|--MyDataReader
|--MyBufferReader
以前是通过继承将每一个子类都具备缓冲功能。
那么继承体系会复杂,并不利于扩展。
现在优化思想。单独描述一下缓冲内容。
将需要被缓冲的对象。传递进来。也就是,谁需要被缓冲,谁就作为参数传递给缓冲区。
这样继承体系就变得很简单。优化了体系结构。
而且降低了类与类之间的关系。
装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强功能。所以装饰类和被装饰类通常是都属于一个体系中的。
字节流
字节流下File的读写操作
import java.io.*;class FileStream{public static void main(String[] args) throws IOException{readFile_3();}public static void readFile_3()throws IOException{FileInputStream fis = new FileInputStream("fos.txt");//int num = fis.available();//available方法返回文件中字节数byte[] buf = new byte[fis.available()];//定义一个刚刚好的缓冲区。不用在循环了。该方法慎用,当文件数据过大时,会造成内存溢出!!fis.read(buf);System.out.println(new String(buf));fis.close();}public static void readFile_2()throws IOException{FileInputStream fis = new FileInputStream("fos.txt");byte[] buf = new byte[1024]; //为readFile_1和readFile_3之间的折中方式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)//read方法返回的是int型,如果是byte型,可能读到8个1时就结束循环{System.out.println((char)ch);}fis.close();}public static void writeFile()throws IOException{FileOutputStream fos = new FileOutputStream("fos.txt");fos.write("abcde".getBytes()); //getBytes方法将字符串转换为字节数组fos.close();}}
字节流的缓冲区
/*演示mp3的复制。通过缓冲区。BufferedOutputStreamBufferedInputStream*/import java.io.*;class CopyMp3{public static void main(String[] args) throws IOException{long start = System.currentTimeMillis();copy_2();long end = System.currentTimeMillis();System.out.println((end-start)+"毫秒");//打印复制时间} //通过自定义的字节流缓冲区(见下)完成复制public static void copy_2()throws IOException{MyBufferedInputStream bufis = new MyBufferedInputStream(new FileInputStream("c:\\9.mp3"));BufferedOutputStream bufos = new BufferedOutputStream(new FileOutputStream("c:\\3.mp3"));int by = 0;//System.out.println("第一个字节:"+bufis.myRead());while((by=bufis.myRead())!=-1){bufos.write(by);}bufos.close();bufis.myClose();}//通过字节流的缓冲区完成复制。public static void copy_1()throws IOException{BufferedInputStream bufis = new BufferedInputStream(new FileInputStream("c:\\0.mp3"));BufferedOutputStream bufos = new BufferedOutputStream(new FileOutputStream("c:\\1.mp3"));int by = 0;while((by=bufis.read())!=-1)//read方法返回的是int型,如果是byte型,可能读到8个1时就结束循环{bufos.write(by);}bufos.close();bufis.close();}}
自定义字节流缓冲区
import java.io.*;class MyBufferedInputStream{private InputStream in; //注意这里传的不是FileInputStreamprivate byte[] buf = new byte[1024*4];private int pos = 0,count = 0;MyBufferedInputStream(InputStream in){this.in = in;}//一次读一个字节,从缓冲区(字节数组)获取。public int myRead()throws IOException{//通过in对象读取硬盘上数据,并存储buf中。if(count==0) //count等于0表示buf中无数据,需要重新从硬盘读取数据{count = in.read(buf);if(count<0)//表示硬盘数据读取完毕return -1;pos = 0;//将指针归零,返回数组第一个元素byte b = buf[pos];count--;pos++;return b&255;//与上255,在原字节数据的前面补上了24个0,将读取的byte转为int}else if(count>0){byte b = buf[pos];count--;pos++;return b&0xff;//0xff是255的16进制表示}return -1;//这个return语句为了保证有返回值,无其他意义}public void myClose()throws IOException{in.close();}}结论:
自定义字节流缓冲区的读一个字节的read方法为什么返回值类型不是byte,而是int。
因为有可能会读到连续8个二进制1的情况,8个二进制1对应的十进制是-1.
那么就会数据还没有读完,就结束的情况。因为我们判断读取结束是通过结尾标记-1来确定的。
所以,为了避免这种情况将读到的字节进行int类型的提升。
并在保留原字节数据的情况前面了补了24个0,变成了int类型的数值。
而在写入数据时,只写该int类型数据的最低8位。写入的write方法自动做了强转操作,否则文件会变大。
读取转换流
引子:读取键盘录入
System.in:对应的标准输入设备:键盘。
/*需求:通过键盘录入数据。当录入一行数据后,就将该行数据进行打印。如果录入的数据是over,那么停止录入。*/import java.io.*;class ReadIn{public static void main(String[] args) throws IOException{InputStream in = System.in;StringBuilder sb = new StringBuilder();while(true){int ch = in.read();//调用read方法一次就会将键盘录入的字符读取一次!if(ch=='\r')continue;if(ch=='\n'){String s = sb.toString();if("over".equals(s))break;System.out.println(s.toUpperCase());sb.delete(0,sb.length());//StringBuilder的清空}elsesb.append((char)ch);}}}
InputStreamReader和OutputStreamWriter
也就是readLine方法。
能不能直接使用readLine方法来完成键盘录入的一行数据的读取呢?
readLine方法是字符流BufferedReader类中的方法。
而键盘录入的read方法是字节流InputStream的方法。
那么能不能将字节流转成字符流在使用字符流缓冲区的readLine方法呢?
import java.io.*;class TransStreamDemo{public static void main(String[] args) throws IOException{//获取键盘录入对象。//InputStream in = System.in;//将字节流对象转成字符流对象,使用转换流。InputStreamReader//InputStreamReader isr = new InputStreamReader(in);//为了提高效率,将字符串进行缓冲区技术高效操作。使用BufferedReader//BufferedReader bufr = new BufferedReader(isr);//读取键盘录入的最常见写法。BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));//OutputStream out = System.out;//OutputStreamWriter osw = new OutputStreamWriter(out);//BufferedWriter bufw = new BufferedWriter(osw);BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));String line = null;while((line=bufr.readLine())!=null){if("over".equals(line))break;bufw.write(line.toUpperCase());bufw.newLine();bufw.flush();//字符输出流用到了缓冲区,需要刷新}bufr.close(); bufw.close();}}
转换流可以指定编码表
流操作的规律
三种情形
1,源:键盘录入。
目的:控制台。
2,需求:想把键盘录入的数据存储到一个文件中。
源:键盘。
目的:文件。
3,需求:想要将一个文件的数据打印在控制台上。
源:文件。
目的:控制台。
流操作的基本规律(三个明确)
最痛苦的就是流对象有很多,不知道该用哪一个。通过三个明确来完成。
1,明确源和目的。
源:输入流。InputStream Reader
目的:输出流。OutputStream Writer。
2,操作的数据是否是纯文本。
是:字符流。
不是:字节流。
3,当体系明确后,在明确要使用哪个具体的对象。
通过设备来进行区分:
源设备:内存,硬盘。键盘
目的设备:内存,硬盘,控制台。
需求:将一个文本文件中数据存储到另一个文件中。复制文件。
1,源:因为是源,所以使用读取流。InputStream Reader
2,是不是操作文本文件。
是!这时就可以选择Reader
这样体系就明确了。
3,接下来明确要使用该体系中的哪个对象。
明确设备:硬盘。一个文件。
Reader体系中可以操作文件的对象是 FileReader
是否需要提高效率:是!加入Reader体系中缓冲区 BufferedReader.
目的:OutputStream Writer
是否是纯文本。
是!Writer。
设备:硬盘,一个文件。
Writer体系中可以操作文件的对象FileWriter。
是否需要提高效率:是!。加入Writer体系中缓冲区 BufferedWriter
BufferedWriter bufw = new BufferedWriter(fw);
---------------------------------------
需求:将键盘录入的数据保存到一个文件中。
这个需求中有源和目的都存在。
那么分别分析
源:InputStream Reader
是不是纯文本?是!Reader
设备:键盘。对应的对象是System.in.
不是选择Reader吗?System.in对应的不是字节流吗?
为了操作键盘的文本数据方便。转成字符流按照字符串操作是最方便的。
所以既然明确了Reader,那么就将System.in转换成Reader。
用了Reader体系中转换流,InputStreamReader
InputStreamReader isr = new InputStreamReader(System.in);
需要提高效率吗?需要!BufferedReader
BufferedReader bufr = new BufferedReader(isr);
目的:OutputStream Writer
是否是存文本?是!Writer。
设备:硬盘。一个文件。使用 FileWriter。
FileWriter fw = new FileWriter("c.txt");
需要提高效率吗?需要。
BufferedWriter bufw = new BufferedWriter(fw);
**************
扩展一下,想要把录入的数据按照指定的编码表(utf-8),将数据存到文件中。
目的:OutputStream Writer
是否是存文本?是!Writer。
设备:硬盘。一个文件。使用 FileWriter。
但是FileWriter是使用的默认编码表。GBK. 同样FileReader把码表写死了,当想要用指定的码表读数据时,就要用到FileReader的父类转换流InputStreamReader
但是存储时,需要加入指定编码表utf-8。而指定的编码表只有转换流可以指定。
所以要使用的对象是OutputStreamWriter。
而该转换流对象要接收一个字节输出流。而且还可以操作的文件的字节输出流。FileOutputStream
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("d.txt"),"UTF-8");
需要高效吗?需要。
BufferedWriter bufw = new BufferedWriter(osw);
所以,记住。转换流什么使用。字符和字节之间的桥梁,通常,涉及到字符编码转换时,
需要用到转换流。
改变标准输入输出设备
IO流的应用
异常的日志信息
import java.io.*;import java.util.*;import java.text.*;class ExceptionInfo{public static void main(String[] args)throws IOException {try{int[] arr = new int[2];System.out.println(arr[3]);}catch (Exception e){try{Date d = new Date();SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");String s = sdf.format(d);PrintStream ps = new PrintStream("exeception.log");ps.println(s);System.setOut(ps);}catch (IOException ex){throw new RuntimeException("日志文件创建失败");}e.printStackTrace(System.out);}}}//log4j 网上提供的专门针对建立异常信息的java包
系统信息
import java.util.*;import java.io.*;class SystemInfo{public static void main(String[] args) throws IOException{Properties prop = System.getProperties();//System.out.println(prop);prop.list(new PrintStream("sysinfo.txt"));}}
File类
File对象的创建
//将a.txt封装成file对象。可以将已有的和未出现的文件或者文件夹封装成对象。File f1 = new File("a.txt"); //此例传入相对路径//将父目录和文件名分别传入File f2 = new File("c:\\abc","b.txt");//将父目录封装成对象传入File d = new File("c:\\abc");File f3 = new File(d,"c.txt");sop("f1:"+f1);//打印出相对路径sop("f2:"+f2);//打印出绝对路径sop("f3:"+f3);//通过File类的seperator字段获取系统的目录分隔符,实现跨平台操作File f4 = new File("c:"+File.separator+"abc"+File.separator+"zzz"+File.separator+"a.txt");
File对象功能
创建。
boolean createNewFile():在指定位置创建文件,如果该文件已经存在,则不创建,返回false。会抛出IO异常和输出流不一样,输出流对象一建立创建文件。而且文件已经存在,会覆盖。所以该方式更合理
boolean mkdir():创建文件夹/目录。不能包括不存在的父目录
//创建单个文件夹File dir = new File("abc\\kkk\\a");//目录abc\\kkk已经存在sop("mkdir:"+dir.mkdir());boolean mkdirs():创建多级文件夹。在File对象封装时进行设定:new File("abc\\kk\\dd");
//创建多级文件夹File dir = new File("abc\\kkk\\a\\a\\dd\\ee\\qq\\aaa");sop("mkdir:"+dir.mkdirs());
删除。
boolean delete():删除失败返回false。如果文件正在被使用,则删除不了返回false。void deleteOnExit();在程序退出时删除指定文件。即使发生异常,也会删除
判断。
boolean exists() :文件是否存在. 在用流操作文件时可以先进行判断,这就是把文件封装成对象的好处
isFile():
isDirectory();
isHidden();
isAbsolute();不论文件存不存在,都可以判断该路径是否为绝对路径
获取信息。
getName():getPath():
getParent(): 获取父目录。只要没有明确指定上一层目录,返回的是null,录入abc\\file.txt和c:\\abc\\file.txt都可以
String getAbsolutePath(): 不论文件是否存在,返回此抽象路径名的绝对路径名字符串
long lastModified() ;返回此抽象路径名表示的文件最后一次被修改的时间
long length() ;返回文件的大小
文件列表
File dir = new File("d:\\java1223\\day18");String[] arr = dir.list(new FilenameFilter(){public boolean accept(File dir,String name){return name.endsWith(".bmp");}});System.out.println("len:"+arr.length);for(String name : arr){System.out.println(name);}
列出目录下所有内容
递归
- 限定条件
- 注意递归的次数,避免内存溢出
层级目录
//该方法返回该级文件或文件夹前面所插入的字符串public static String getLevel(int level){StringBuilder sb = new StringBuilder();sb.append("|--");for(int i=0;i<level;i++){sb.insert(0, " ");//从StringBuilder前面插入}return sb.toString();}private static void listAll(File file, int level) {System.out.println(getLevel(level)+file.getName());level++;File[] files = file.listFiles();for(File f: files){if(f.isDirectory()){listAll(f,level);}elseSystem.out.println(getLevel(level)+f.getName());}}
删除带内容的目录
public static void removeDir(File dir){File[] files = dir.listFiles();for(int x=0; x<files.length; x++){if(files[x].isDirectory())removeDir(files[x]);elseSystem.out.println(files[x].toString()+":-file-:"+files[x].delete());}System.out.println(dir+"::dir::"+dir.delete());}
创建java文件列表
建立一个java文件列表文件。
1,对指定的目录进行递归。
2,获取递归过程所有的java文件的路径。
3,将这些路径存储到集合中。
4,将集合中的数据写入到一个文件中。
import java.io.*;import java.util.*;class JavaFileList{public static void main(String[] args) throws IOException{File dir = new File("d:\\java1223");List<File> list = new ArrayList<File>();fileToList(dir,list);//System.out.println(list.size());File file = new File(dir,"javalist.txt");writeToFile(list,file.toString());}public static void fileToList(File dir,List<File> list){File[] files = dir.listFiles();for(File file : files){if(file.isDirectory())fileToList(file,list);else{if(file.getName().endsWith(".java"))list.add(file);}}}public static void writeToFile(List<File> list,String javaListFile)throws IOException{BufferedWriter bufw = null;try{bufw = new BufferedWriter(new FileWriter(javaListFile));for(File f : list){String path = f.getAbsolutePath();bufw.write(path);bufw.newLine();bufw.flush();}}catch (IOException e){throw e;}finally{try{if(bufw!=null)bufw.close();}catch (IOException e){throw e;}}}}
Properties
设置和获取元素
Properties prop = new Properties();//创建对象prop.setProperty("zhangsan","30");String value = prop.getProperty("lisi");//根据键名获取值prop.setProperty("zhangsan",89+"");//修改Set<String> names = prop.stringPropertyNames();//返回简明的set集合,该方法1.6才有效for(String s : names){System.out.println(s+":"+prop.getProperty(s));}
存取配置文件
将流中的数据存储到集合中
//想要将info.txt中键值数据存到集合中进行操作。/*1,用一个流和info.txt文件关联。2,读取一行数据,将该行数据用"="进行切割。3,等号左边作为键,右边作为值。存入到Properties集合中即可。*/public static void method_1()throws IOException{BufferedReader bufr = new BufferedReader(new FileReader("info.txt"));String line = null; //用line记录读取到的一行字符串数据Properties prop = new Properties();while((line=bufr.readLine())!=null){String[] arr = line.split("=");//用等号分割,返回一个字符串数组prop.setProperty(arr[0],arr[1]);}bufr.close();//注意关闭流System.out.println(prop);}
load方法
store方法
list方法
Properties prop = new Properties();FileInputStream fis = new FileInputStream("info.txt");//将流中的数据加载进集合。prop.load(fis);prop.setProperty("wangwu","39");FileOutputStream fos = new FileOutputStream("info.txt"); //将集合中的数据写入输出流prop.store(fos,"haha");prop.list(System.out);fos.close();fis.close();练习:限制程序运行次数。当运行次数到达5次时,给出,请您注册的提示。并不再让该程序执行。
/*用于记录应用程序运行次数。如果使用次数已到,那么给出注册提示。很容易想到的是:计数器。可是该计数器定义在程序中,随着程序的运行而在内存中存在,并进行自增。可是随着该应用程序的退出,该计数器也在内存中消失了。下一次在启动该程序,又重新开始从0计数。这样不是我们想要的。程序即使结束,该计数器的值也存在。下次程序启动会先加载该计数器的值并加1后在重新存储起来。所以要建立一个配置文件。用于记录该软件的使用次数。该配置文件使用键值对的形式。这样便于阅读数据,并操作数据。键值对数据是map集合。数据是以文件形式存储,使用io技术。那么map+io -->properties.配置文件可以实现应用程序数据的共享。*/import java.io.*;import java.util.*;class RunCount{public static void main(String[] args) throws IOException{Properties prop = new Properties(); //用File来对象化文件,那么文件不存在则创建File file = new File("count.ini"); //此处采用相对路径,因为绝对路径会因为盘符不存在而出现IO异常if(!file.exists())file.createNewFile();FileInputStream fis = new FileInputStream(file);prop.load(fis);int count = 0;String value = prop.getProperty("time");if(value!=null){count = Integer.parseInt(value);if(count>=5){System.out.println("您好,使用次数已到,拿钱!");return ; //用该语句直接终止程序的运行}}count++;//当value不为空但又小于5时,外层if代码块执行完之后就会执行此语句保证count的续增prop.setProperty("time",count+"");FileOutputStream fos = new FileOutputStream(file);//输出流对象不能在前面创建,否则会出现次数无法递增的问题!prop.store(fos,"");fos.close();fis.close();}}
配置文件有.properties和.xml,properties文件存储键值对结构简单,xml格式可以存储复杂结构的数据
age=20
xml格式:
<persons><person id="001"><name>zhagnsan</name><age>30</age><address>bj</address></person><person><name</person></persons>
打印流
字节打印流 PrintStream
构造函数可以接收的参数类型:1,file对象。File
2,字符串路径。String
3,字节输出流。OutputStream
字符打印流 PrintWriter
1,file对象。File,还可以传入String csn创建具有指定文件和字符集的新PrinterWriter
2,字符串路径。String
3,字节输出流。OutputStream,还可以传入boolean autoFlush可以创建带自动行刷新的的新PrintWriter
4,字符输出流,Writer。可带自动刷新
//读取键盘录入的标准语句BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));//PrintWriter out = new PrintWriter(System.out,true);//打印到控制台//PrintWriter out = new PrintWriter("a.txt");//直接存入文件,后面不能写刷新,因为刷新只能针对流而言PrintWriter out = new PrintWriter(new FileWriter("a.txt"),true);//在文件里面自动刷新,就将文件封装进流中String line = null;while((line=bufr.readLine())!=null){if("over".equals(line))break;out.println(line.toUpperCase());}//out.flush();}out.close();bufr.close();
序列流
将多个读取流合并成一个流
构造方法
多个流合并
SequenceInputStream(Enumeration<? extends InputStream> e) 通过记住参数来初始化新创建的 SequenceInputStream,该参数必须是生成运行时类型为 InputStream 对象的 Enumeration 型参数(可通过Vector获得)。
两个流合并
SequenceInputStream(InputStream s1, InputStream s2) 通过记住这两个参数来初始化新创建的 SequenceInputStream(将按顺序读取这两个参数,先读取 s1,然后读取 s2),以提供从此 SequenceInputStream 读取的字节。实例:把三个文件的数据变成一个文件
Vector<FileInputStream> v = new Vector<FileInputStream>();v.add(new FileInputStream("c:\\1.txt"));v.add(new FileInputStream("c:\\2.txt"));v.add(new FileInputStream("c:\\3.txt"));Enumeration<FileInputStream> en = v.elements();//vector的elements方法返回此向量的组件的枚举SequenceInputStream sis = new SequenceInputStream(en);FileOutputStream fos = new FileOutputStream("c:\\4.txt");byte[] buf = new byte[1024];int len =0;while((len=sis.read(buf))!=-1) //read方法返回读取的字符的长度{fos.write(buf,0,len);}fos.close();sis.close();
切割合并文件
public static void merge()throws IOException{ //Vector执行效率低,此处用ArrayList ArrayList<FileInputStream> al = new ArrayList<FileInputStream>();for(int x=1; x<=3; x++){al.add(new FileInputStream("c:\\splitfiles\\"+x+".part"));} //因为是匿名内部类,所以要对访问的局部变量进行final修饰,规定的final Iterator<FileInputStream> it = al.iterator(); //使用ArrayList的迭代器覆盖Enumneration匿名内部类的两个方法创建一个Enumneration对象Enumeration<FileInputStream> en = new Enumeration<FileInputStream>(){public boolean hasMoreElements(){return it.hasNext();}public FileInputStream nextElement(){return it.next();}};SequenceInputStream sis = new SequenceInputStream(en);FileOutputStream fos = new FileOutputStream("c:\\splitfiles\\0.bmp");byte[] buf = new byte[1024];int len = 0;while((len=sis.read(buf))!=-1){fos.write(buf,0,len);}fos.close();sis.close();}public static void splitFile()throws IOException{FileInputStream fis = new FileInputStream("c:\\1.bmp");FileOutputStream fos = null;byte[] buf = new byte[1024*1024];//当操作较大数据时,为了防止内存溢出,可以用每一个流对象操作100M数据int len = 0;int count = 1;while((len=fis.read(buf))!=-1){fos = new FileOutputStream("c:\\splitfiles\\"+(count++)+".part");fos.write(buf,0,len);fos.close();}fis.close();}
对象的序列化
把堆内存中的对象以字节形式存储在硬盘上,也叫对象的序列化或持久化ObjectOutputStream
public static void writeObj()throws IOException{ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("obj.txt"));oos.writeObject(new Person("lisi0",399,"kr"));oos.close();}
ObjectInputStream
public static void readObj()throws Exception//这里会抛出ClassNotFoundException和类没有序列化异常,所以抛出Exception{ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.txt"));Person p = (Person)ois.readObject();System.out.println(p);ois.close();}
import java.io.*;class Person implements Serializable{public static final long serialVersionUID = 42L; //不让java自动生成uid,通过自定义uidprivate String name;transient int age; //对非静态的成员也不使其序列化,则加上transient关键字,即使它在堆内存中static String country = "cn"; //静态成员是不能被序列化的,因为静态成员在方法区里面,所以不论构造Person时传入何值,读取的时候都是cnPerson(String name,int age,String country){this.name = name;this.age = age;this.country = country;}public String toString(){return name+":"+age+":"+country;}}
管道流
import java.io.*;//读取线程class Read implements Runnable{private PipedInputStream in;Read(PipedInputStream in){this.in = in;}public void run(){try//此处不能抛异常,必须处理异常,因为覆盖了run方法{byte[] buf = new byte[1024];System.out.println("读取前。。没有数据阻塞");int len = in.read(buf);System.out.println("读到数据。。阻塞结束");String s= new String(buf,0,len);System.out.println(s);in.close();}catch (IOException e){throw new RuntimeException("管道读取流失败");}}}//输出线程class Write implements Runnable{private PipedOutputStream out;Write(PipedOutputStream out){this.out = out;}public void run(){try{System.out.println("开始写入数据,等待6秒后。");Thread.sleep(6000);out.write("piped lai la".getBytes());//write方法写入的一定是字节型数据out.close();}catch (Exception e){throw new RuntimeException("管道输出流失败");}}}class PipedStreamDemo{public static void main(String[] args) throws IOException{PipedInputStream in = new PipedInputStream();PipedOutputStream out = new PipedOutputStream();in.connect(out);//可以使用connect方法来连接两个流对象,也可以使用构造函数传递参数连接两个流对象Read r = new Read(in);Write w = new Write(out);new Thread(r).start();new Thread(w).start();}}
RandomAccessFile
随机访问文件,自身具备读写的方法,结尾处没有父类名
通过skipBytes(int x),seek(int x)来达到随机访问,前提是保证数据的存储是有规律的
该类不算是IO体系中子类。
而是直接继承自Object。
但是它是IO包中成员。因为它具备读和写功能。
内部封装了一个数组(以文件的数据的一个字节为单位),而且通过指针对数组的元素进行操作。
可以通过getFilePointer获取指针位置,
同时可以通过seek改变指针的位置。
其实完成读写的原理就是内部封装了字节输入流和输出流。
通过构造函数可以看出,该类只能操作文件。不能操作键盘录入输出等其他形式
而且操作文件还有模式:只读r,读写rw等。
如果模式为只读 r。不会创建文件。会去读取一个已存在文件,如果该文件不存在,则会出现异常。
如果模式rw。操作的文件不存在,会自动创建。如果存在则不会覆盖。
public static void readFile()throws IOException{RandomAccessFile raf = new RandomAccessFile("ran.txt","r");//调整对象中指针。至第8个字节。数组的前后都能指定//raf.seek(8*1);//跳过指定的字节数raf.skipBytes(8);//只能在数组中往下跳byte[] buf = new byte[4];raf.read(buf);String name = new String(buf);int age = raf.readInt();//readInt方法一次读4个字节System.out.println("name="+name);System.out.println("age="+age);raf.close();}public static void writeFile_2()throws IOException{RandomAccessFile raf = new RandomAccessFile("ran.txt","rw");raf.seek(8*0); //将指针定位在数组开头,会根据写入数据的大小从开头覆盖数据raf.write("周期".getBytes());raf.writeInt(103);raf.close();}public static void writeFile()throws IOException//该方法写入会覆盖原有文件数据{RandomAccessFile raf = new RandomAccessFile("ran.txt","rw");//rw代表“读写”模式 /*raf.write("2".getBytes()); raf.write(258);//此时会取258的最低8位写出 raf.writeInt(258);//此时会取258的4个字节写出 */raf.write("李四".getBytes());raf.writeInt(97);raf.write("王五".getBytes());raf.writeInt(99);raf.close();}
可以实现读取,写入,修改操作
该类可以实现多线程的下载文件。每个线程负责文件的一段数据的下载,保证下载后数据的完整性
为了保证数据的规律性,可以进行定义,比如,姓名16个字节,年龄4个字节,空的字节会拿空补位
操作基本数据类型的流对象
DataInputStream和DataOutputStream
可以用于操作基本数据类型的数据的流对象
import java.io.*;class DataStreamDemo {public static void main(String[] args) throws IOException{//writeData();//readData();//writeUTFDemo();//OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("gbk.txt"),"gbk"); //使用转换流指定编码表gbk////osw.write("你好");//osw.close();//readUTFDemo();}public static void readUTFDemo()throws IOException{DataInputStream dis = new DataInputStream(new FileInputStream("utf.txt"));String s = dis.readUTF();//读取时应使用相应的方法,否则读取错误System.out.println(s);dis.close();}public static void writeUTFDemo()throws IOException{DataOutputStream dos = new DataOutputStream(new FileOutputStream("utfdate.txt"));dos.writeUTF("你好");//该方法使用修改过的UTF表写入数据dos.close();} public static void readData()throws IOException//可以读取基本数据类型{DataInputStream dis = new DataInputStream(new FileInputStream("data.txt"));int num = dis.readInt();boolean b = dis.readBoolean();double d = dis.readDouble();System.out.println("num="+num);System.out.println("b="+b);System.out.println("d="+d);dis.close();}public static void writeData()throws IOException//可以写入基本数据类型{DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));//注意传入输出流对象dos.writeInt(234);dos.writeBoolean(true);dos.writeDouble(9887.543);dos.close();ObjectOutputStream oos = null;oos.writeObject(new O());}
内存为源和目的的操作
操作字节数组的流对象
用于操作字节数组的流对象。ByteArrayInputStream :在构造的时候,需要接收数据源,。而且数据源是一个字节数组。ByteArrayOutputStream: 在构造的时候,不用定义数据目的,因为该对象中已经内部封装了可变长度的字节数组。这就是数据目的地。因为这两个流对象都操作的数组,并没有使用系统资源。不用抛IO异常所以,不用进行close关闭。
在流操作规律讲解时:
源设备:键盘 System.in,硬盘 FileStream,内存 ArrayStream。
目的设备:控制台 System.out,硬盘FileStream,内存 ArrayStream。
其实就是用流的读写思想来操作数组。
public static void main(String[] args) {//数据源。ByteArrayInputStream bis = new ByteArrayInputStream("ABCDEFD".getBytes());//数据目的ByteArrayOutputStream bos = new ByteArrayOutputStream();//内部已封装了一个数组,不用定义目的int by = 0;while((by=bis.read())!=-1)//不用再判断数组长度,因为都是在内存中进行{bos.write(by);//将读到的字节数据写入字节数组中}System.out.println(bos.size());//获取当前缓冲区的大小System.out.println(bos.toString());//使用平台默认的字符集,通过解码字节将缓冲区的内容转换为字符串//bos.writeTo(new FileOutputStream("a.txt"));//一次性把数组中的数据写到一个输出流中,但此时需要抛IO异常或者捕获该异常}
操作字符数组
CharArrayReader和CharArrayWriter类似字节数组
操作字符串
StringReader和StringWriter
类似字节数组
字符编码
概述
可以加入编码表的对象还有:PrintStream和PrintWriter,但是只能输出打印,不能读取
编码表的由来:计算机只能识别二进制数据,早期只能识别电信号,为了方便应用计算机,让它可以识别各国的文字,就将各国的文字用数字来表示,并一一对应,形成一张表,这就是编码表
常见的编码表:
- ASCII:美国标准信息交换码,用一个字节的7位表示
- IOS8859-1:拉丁码表。欧洲码表。用一个字节的8位表示
- GB2312:中国的中文编码表(早期)
- GBK:中国的中文编码表升级,融合了更多的中文文字字符。打头的是两个高位为1的两个字节编码。为负数
- Unicode:国际标准码,融合了多种文字。所有国家的文字都用两个字节来表示,但是对于一个字节就能表示的文字,浪费。Java语言使用的就是unicode。
- UTF-8:Unicode Transform Format,最多用三个字节表示一个字符,根据字符所占内存空间不同,分别用一个、两个、三个字节来编码。并在每个字节的开头都加了标识头,用以区分什么是UTF-8的编码
private static void readText() throws IOException {InputStreamReader isr = new InputStreamReader(new FileInputStream("utf.txt"),"GBK");//字节流到字符流char[] buf = new char[10];int len = isr.read(buf);String str = new String(buf,0,len);System.out.println(str);isr.close();}private static void writeText() throws IOException {/*OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("gbk.txt"));//因为系统默认的是GBK码表,这里将字符流转换为字节 流,所以此句等价于FileWriter*/OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("utf.txt"),"UTF-8");//按照UTF-8码表写出数据osw.write("你好");//osw是字符流对象,所以不用将数据转换为字节数组osw.close();}查错表的情况分析:
编码与解码
String str = "你好";//byte[] arr = str.getBytes();//结果:[-60, -29, -70, -61]//byte[] arr = str.getBytes("GBK");//结果:[-60, -29, -70, -61]会抛出异常,因为可能传入的值不识别byte[] arr = str.getBytes("UTF-8");//结果:[-28, -67, -96, -27, -91, -67]System.out.println(Arrays.toString(arr));//将字节数组直接转换成字符串形式常用的码表就两张:GBK和UTF-8,他们都识别中文
字符集转换注意事项
GBK与IOS8859-1
GBK与UTF-8
String s = "你好";byte[] b = s.getBytes("GBK");System.out.println(Arrays.toString(b));String s1 = new String(b,"UTF-8");System.out.println(s1);//对s1进行再次编码byte[] b1 = s1.getBytes("UTF-8");System.out.println(Arrays.toString(b1));String s2 = new String(b1,"GBK");System.out.println(s2);运行结果:
“联通”的问题
综合练习
/*有五个学生,每个学生有3门课的成绩,从键盘输入以上数据(包括姓名,三门课成绩),输入的格式:如:zhagnsan,30,40,60计算出总成绩,并把学生的信息和计算出的总分数高低顺序存放在磁盘文件"stud.txt"中。1,描述学生对象。2,定义一个可操作学生对象的工具类。思想:1,通过获取键盘录入一行数据,并将该行中的信息取出封装成学生对象。2,因为学生有很多,那么就需要存储,使用到集合。因为要对学生的总分排序。所以可以使用TreeSet。3,将集合的信息写入到一个文件中。*/import java.io.*;import java.util.*;//定义Student类实现Comparable接口并指定泛型,这样在实现compareTo方法时就只能接收该泛型了class Student implements Comparable<Student>{private String name;private int ma,cn,en,sum;Student(String name, int ma, int cn, int en){this.name = name;this.ma = ma;this.cn = cn;this.en = en;sum = ma+cn+en;}public String getName(){return this.name;}public int getSum(){return this.sum;}public int compareTo(Student s){int num = new Integer(this.sum).compareTo(new Integer(s.sum));if(num==0)return this.name.compareTo(s.name);return num;}//一般自定义对象都要覆盖Object类的hashCode和equals方法,还有toString方法public int hashCode(){return name.hashCode()+sum*35;}public boolean equals(Object obj){if(!(obj instanceof Student))throw new ClassCastException("类型不匹配");Student s = (Student)obj;return this.name.equals(s.name)&&this.sum==s.sum;}public String toString(){return "Student:"+name+"["+ma+","+cn+","+en+"]";}}class StudentInfoTool{//按照默认排序方式,总成绩从小到大public static Set<Student> getStudents() throws IOException{return getStudents(null);//不需要比较器,传入null。}//按照指定比较器排序方式,总成绩从大到小public static Set<Student> getStudents(Comparator<Student> cmp) throws IOException{BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));String line = null;Set<Student> stus = null;if(cmp==null)stus = new TreeSet<Student>();//如果没有传入比较器,那么就创建一个不带比较器的集合elsestus = new TreeSet<Student>(cmp);//如果传入了比较器,就创建一个带比较器的集合while(true){line = bufr.readLine();if("over".equals(line))break;String[] arr = line.split(",");Student s = new Student(arr[0],Integer.parseInt(arr[1]),Integer.parseInt(arr[2]),Integer.parseInt(arr[3]));stus.add(s);}bufr.close();return stus;}//将存有学生信息的集合写入文件public static void write2File(Set<Student> stus) throws IOException{BufferedWriter bufw = new BufferedWriter(new FileWriter("stuInfo.txt"));for(Student s: stus){bufw.write(s.toString()+'\t');bufw.write(s.getSum()+"");//如果单独输出整数只输出最后一个八位,会出现乱码,所以要转为字符串bufw.newLine();bufw.flush();//写入文件需要刷新,因为用到了缓存}bufw.close();}}public class StudentInfoDemo {public static void main(String[] args) throws IOException {Comparator<Student> cmp = Collections.reverseOrder();//返回一个逆序自身比较性的比较器Set<Student> stus = StudentInfoTool.getStudents(cmp);StudentInfoTool.write2File(stus);}}
- Java基础_IO流
- 十.Java基础_IO流
- Java基础<十一>_IO流(上)
- Java基础<十二>_IO流(下)
- java基础学习笔记_IO流
- java基础_IO流学习总结
- Java基础_IO
- java基础_IO
- 黑马程序员—java基础_IO流—字符流
- 黑马程序员—java基础_IO流—字节流
- Java基础第十九天学习日记_IO流
- 黑马程序员_毕向东JAVA基础_IO流(一)
- Java基础回顾_IO流_File简单操作
- Java基础视频教程第19天_IO流二
- 黑马程序员_IO流基础
- JAVA _IO流(1)
- 黑马程序员-java_基础加强_IO流
- 黑马程序员_JAVA基础_IO流(一)
- 使用VC2008 Express编写Win32控制台应用程序及其安装(初学者适用)
- poj2000 Gold Coins(模拟题)
- Auto Layout 及 ios6/ios7适配相关
- Factory模式(工厂模式)
- 黑马程序员_Java基础03天日志
- Java基础_IO流
- 十进制转化二.四,八.十六进制(使用递归)!!!
- 25 字符串中找出连续最长的数字串,返回长度
- HDU 2256 Problem of Precision(矩阵快速幂)
- Proguard5.0自动将泛型转成Object类型解决方法
- MingW下载与安装
- java进阶(七):详解JAVA代理
- 九.一八 勿忘国耻,勿忘编码耻
- 26 左旋转操作:把字符串前面的若干个字符移动到尾部