黑马程序员—10、Java基础&IO流
--------------------- android培训、java培训、期待与您交流!
IO流 :Input(输入)Output(输出)。简称输入输出流。IO流就是处理设备之间的数据传输。java对数据的操作是通过数据流的方法来完成的。
流按流向分为:输入流,输出流。
流按字节分为:
字节流:
字符流:为了处理文字数据方便而出现的对象。其实这些对象内部使用的还是字节流(因为文字最终页是字节数据)。只不过,通过字节流读取了相对应的字节数,没有对这些字节直接操作。而是去查了指定的(本机默认的)编码表。获取到了对应的文字。简单说:字符流就是:字节流+编码表。
IO流中的最基本的最长用的基类:IO流的四大体系
1、字节流的抽象基类:InputStream 从外围设备到内存的流 OutputStream 从内存到外围设备的流
2、字符流的抽象基类:Reader 读者(也是从外围设备到内存) Writer 作者(也是从内存到外围设备)
由这四个基本流派生出来的子类都有一个共同的特点:那就是子类的名称都是以其父类名作为子类名的后缀。
比如:字节流中的输入流InputStream的子类名为FileInputStream
字符流中的输入流Reader的子类名为FileReader
各种流都会提供操作文件的流。文件就是硬盘存储数据的体现。
IO流类都存放在java.IO中。既然通过api查阅到了可以用来操作字符数据写入动作的流对象,那么就通过该类完成对象的创建。
要想创建一个对象,给外围设备上存储数据,此时不能直接用Writer,因为他是抽象类,需要用它的子类来创建对象。查阅API发现可以使用的类有:OutputStreamWriter和FileWriter。而FileWriter是用来写入字符文件的便捷类。那么就可以使用FileWriter来创建对象了。
FileWriter fw = new Filewriter(); 这个时候一定要注意目的地可用,在创建对象运行时,就会在指定的位置创建一个指定的文件。如果该文件已经存在,会发生覆盖。
写动作的特点:写动作的对象,都必须在创建对象构造时,明确数据要存储的目的地。
FileWriter fw = new FileWriter("demo.txt"); IO流在处理的时候都会抛出异常。不管是读还是写,都要有先决条件,比如:写的时候,当我们传递参数,很可能盘符不存在,或者磁盘已满,等问题。这个时候都会抛出异常。
调用字符写入流对象的写入方法,将字符串数据写入到指定目的地中。注意:会先写到该流对象的缓冲区中。对数据进行临时存储。流的缓冲区其实就在内存中有一个数组临时存储数据。
fw.write("abcdefg");
当想把数据需要存放的目的去的时候必须将缓冲区中的数据刷新到目的地中,流还可以继续使用。
fw.flush();刷新该流的缓冲。如果该流已保存缓冲区中各种 write() 方法的所有字符,则立即将它们写入预期目标。当数据处理完后需要关闭流对象。
fw.close();关闭此流,但要先刷新它。在关闭该流之后,再调用 write() 或 flush() 将导致抛出 IOException。关闭以前关闭的流无效。所以使用完了系统资源后,一定要释放,所以close动作一定要有。
FileWriter(File file, boolean append) 根据给定的 File 对象构造一个 FileWriter 对象。如果第二个参数为 true,则将字节写入文件末尾处,而不是写入文件开始处。即就是在已有文件末尾继续写数据。
FileWriter fw = null;//在try外面创建流对象的引用,是可以让可以该引用作用范围变大。finally中可用。 try { fw = new FileWriter("demo.txt",true);//在try中对流对象进行初始化。以方便捕获异常。 fw.write("xixii"+ System.getProperty("line.separator") +"heihei"); System.getProperty("line.separator")获取系统换行符 } catch (IOException e) { System.out.println(e.toString()+"......."); }finally{ if(fw!=null)//如果流对象创建失败,关闭是无效,所以加入了健壮性判断。 try { fw.close();//关闭资源一定要定义finally中,因为资源关闭动作一定要执行。 } catch (IOException e) { e.printStackTrace(); } } |
IO的读动作:查阅api,找到字符读取流Reader中用于读取字符文件的对象。同样不能直接用Reader来创建对象,Reader仍然是抽象类。找它的子类创建对象InputStream 和 FileReader,这时候使用FileReader类,使用该对象以创建对象,创建对象必须在构造时,指定要被读取的数据源文件。其实创建FileReade对象,就是创建流对象并和指定的源相关联。
FileReader fr = new FileReader("demo.txt");
使用Reader对象中的read方法,一次读取一个字符,当读到文件结尾时会返回-1.
int ch1 =fr.read(); 读的时候其实就是从硬盘上读取一段二进制代码。所以返回值应该是int类型。
System.out.println("ch1="+ch1);
fr.close;
一般文件里的数据都比较多,这个时候需要用循环来完成数据读取。
FileReader fr = new FileReader("d:\\abc1.txt");
int ch = 0;
while ((ch = fr.read()) != -1) { 文件读完后会返回-1
System.out.print((char) ch + " ");
}
fr.close(); 读完之后一定要关闭流。
同时也可以使用另外一种读取数据的方法:reader(数组);
char[] buf = new char[1024];
int len1 = fr.read(buf);//将读取到字符存储到了指定的数组中,并返回读取到字符个数。
System.out.println(len1+"....."+new String(buf,0,len1));
通常是用循环来完成数据读取:
char[] buf = new char[1024];
int len = 0;
while((len=fr.read(buf))!=-1){
System.out.println(new String(buf,0,len));
此时发现通过数组来取数据,明显提高了读取效率。这个数组就是一个缓冲区。这个数组的长度一般都是1024的整数倍。因为在计算机中给文件提供的最小存储单位是1K,当文件超过1K,就按2K算。
练习:文件复制,将c盘的文件复制到d盘中,其实就是一个最简单的读写过程,从C盘源读取数据,写入到目的D盘中。
FileReader fr = new FileReader("d:\\abc1.txt");
FileWriter fw = new FileWriter("d:\\复制.txt");
int ch = 0;
while ((ch = fr.read()) != -1) {
fw.write(ch);
}
fr.close();
fw.close();
这种复制太低效,读取一个字符,写一个字符,磁头会在读文件和写文件之间频繁切换。
建议使用缓冲区来完成:
创建一个字符读取流和读写流于文件进行关联
FileReader fr = new FileReader("d:\\abc1.txt");
FileWriter fw = new FileWriter("d:\\复制.txt");
char[] buff = new char[1024]; 定义一个临时存储读到的数据。
int len =0 ; 记录每次读取到字符的个数
while ((len = fr.read(buff)) != -1) {
fw.write(buff,0,len);
}
fr.close();
fw.close();
完成的读写案例:主要是异常处理
FileReader fr = null;
FileWriter fw = null;
try {
fr = new FileReader("其他API文档.txt");
fw = new FileWriter("copy_demo2.txt");
char[] buf = new char[1024];
int len = 0;
while ((len = fr.read(buf)) != -1) {
fw.write(buf, 0, len);
}
} catch (Exception e) {
} finally {
if (fw != null) 关闭文件需要单独处理
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
if (fr != null)
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
真正的读写过程,其实就是需要一个中转站,两个流之间不能直接建立关系。而这个中转站将读取流连接起来,
字符流的缓冲区:本质就是将数组进行了封装,给我们提供了缓冲区对象。
BufferedWriter 将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的写入操作
BufferedReader 从字符输入流中读取文本。缓冲各个字符,从而实现字符、数组和行的高效读取。
缓冲区本身不能操作windows底层资源,仅仅是提高流的操作效率。最后还是需要底层的流对象来操作windows底层资源。
缓冲区是提高效率的,提高谁的效率呢?提高流的操作数据的效率。所以创建缓冲区之前必须先有流对象。
缓冲区的基本思想:其实就是定义容器将数据进行临时存储。对于缓冲区对象,其实就是将这个容器进行了封装,并提供了更多高效的操作方法。
举例:创建字符输出流对象,并创建存储数据的目的地
FileWriter fw = new FileWriter("buf.txt");
为了提高写入效率,定义字符输出流缓冲区,并和指定的字符输出流对象关联
BufferedWriter bufw = new BufferedWriter(fw);
这时我们可以使用输出流的方法来进行数据写操作,但是有了缓冲区对输出流进行效率的提高。这时可以使用缓冲区的高效方法,来操作数据。其实缓冲区的底层使用还是fw的方法,只不过对其高效了。
bufw.writer("hahahhahaha"); 数据被写入到了缓冲区中,相当于水已经到了杯子中。
final String linSeparator = System.getProperty("lin.separator"); 获取系统的换行符
bufw.writer(linSeparator+"hfsdhfshflkhjfls");
bufw.flush(); 刷新缓冲区。记住:只要用到缓冲区,记得一定要刷新。
bufw.close(); 先刷新缓冲区的数据到目的地,然后关闭缓冲区中的流资源。缓冲关系后,fw也会随之关闭了。我们不用在关闭输出流。
读取缓冲区
1、创建基础读取流对象,关联一个数据源,这个数据源目前是一个文本文件
FileReader fr = new FileReader("buf.txt");
2、提高了效率,有了字符读取流缓冲区
BufferedReader bufr = new BufferedReader(fr);
使用缓冲区的读取方式,来操作数据。
int ch= 0 ;
while((ch=bufr.read)!=-1) 一次给缓冲区中读取一个字符
{
System.out.println(ch);
}
String line = null;
while((line = bufr.readLine())!=null) 一次读缓冲区中读取一行,由于一次读取一行,返回的是一个字符串,当读到没有数据的时候,会返回一个空。
{
System.out.println(line);
}
bufr.close();
readLine() 返回一行文本,根据回车符来判断是否是一行文本。就是从缓冲区获取数据,并进行临时存储,直到读取到了换行符,将临时存储的数据转成字符串返回。
readLine对于操作文本是比较方便,可以完成一行一行的读取文本。
将d盘的abc1.txt文件复制到d盘下名为复制.txt文件中。
FileReader fr = new FileReader("d:\\abc1.txt");
FileWriter fw = new FileWriter("d:\\复制.txt");
BufferedReader bufr = new BufferedReader(fr);
BufferedWriter bufw = new BufferedWriter(fw);
int ch = 0 ;
while((ch=bufr.read())!=-1){ 将读缓冲区的数据存放到ch中
bufw.write(ch); 将ch中的数据写到了写的缓冲区中。
bufw.flush(); 刷新写的缓冲区,数据刷到指定的目的地中
}
bufr.close();
bufw.close();
当我们通过readLine的理解后发现,缓冲区的出现提高的是流的操作效率。文件的来源很多,有了缓冲区之后,提高了流对数据的操作效率。缓冲区可以提高流的操作效率。其实是使用了一种设计思想完成的。这个设计模式就是装饰(包装)设计模式。装饰设计模式:其实就是对已有的功能进行增强。
Writer 一种写的功能。
|--TextWriter 文本写的功能
|--MediaWriter 媒体写的功能
现在对该体系中的对象进行功能的增强。增强的最长见手段就是缓冲区。先将数据写到缓冲区中,在将缓冲区中的数据写到目的地。
按照之前学习过的基本思想那就是对对象中的写方法进行覆盖。产生已有的对象子类,覆写writer方法,不往目的地写,而是往缓冲区中写,所以这个体系变成这样。
Writer
|--TextWriter 往目的地写
|--BufferedTextWriter 往缓冲区写
|--MediaWriter
|--BufferedMediaWriter
想要写一些其他数据,就会出现子类,DataWriter,为了提高其效率,还要创建该类的子类,BufferedDataWriter
Writer
|--TextWriter 往目的地写
|--BufferedTextWriter 往缓冲区写
|--MediaWriter
|--BufferedMediaWriter
|--DataWriter
|--BufferedDataWriter
发现这个体系相当的麻烦,每产生一个子类都要有一个高效的子类,而且这些高效的子类使用的功能原理都一样,都是缓冲区原理,无论数据是什么,都是通过缓冲区临时存储提高效率的。那么对于这个体系就可以进行优化,因为没有必要让每一个对象都具备相同的功能的子类。哪个对象想要进行效率的提高,只要让缓冲区对其操作即可。也就是说,单独将缓冲区进行封装变成对象。它的出现为了提高对象的效率,所以必须在创建它的时候先有需要被提高效率的对象。
class BufferedWriter
{
[];需要一个数组充当缓冲区中临时存放数据的容器
BufferedWriter(Writer w)
{
}
}
BufferedWriter 的出现增强了Writer中writer方法,但是增强后,BufferedWriter对外提供的还是writer方法,只不过是高效的。所以写的实质没有变,那么BufferedWriter也是Writer中的一员。所以体系会变成这样
Writer
|--TextWriter
|--MediaWriter
|--BufferedWriter
|--DataWriter
BufferedWriter 出现了避免了继承体系关系的臃肿。比继承更为灵活,如果是为了增强功能,这种方法解决起来更为方便。所以将这种优化总结出来,起个名字:装饰设计模式。装饰类和被装饰类肯定所属同一个体系。
既然明确了BufferedReader由来,我们可以独立完成缓冲区的建立:
原理:
1、使用流的read方法从源中读取一批数据存储到缓冲区的数组中
2、通过计数器记录住存储的元素个数
3、通过数组的角标来获取数组中的元素(从缓冲区中取数据)
4、指针会不断的自增,当增到数组长度,会归0,计数器会自减,自减到零的时候,就从源拿一批数据进缓冲区。
public class MyBufferedReader extends Reader{ private Reader r; private char[] buf = new char[1024]; private int pos = 0,count = 0; public MyBufferedReader(Reader r) { this.r = r; } /** * 一次从缓冲区中取一个。 * @return 一个字符 * @throws IOException */ public int myRead() throws IOException{//其实就是覆盖了Reader类中read方法。 if(count==0){//如果计数器为0时说明缓冲区中已被取完,需要从源再获取数据。 //通过流对象从源获取数据到缓冲区中。并用count记录住个数。 count = r.read(buf); pos = 0; } if(count<0) return -1; char ch = buf[pos]; pos++; count--; return ch; } /** * 按照文本特点,提供了一个特有的操作文本的方法。 * 一次读取一行文本,只要是到行结束符之前的文本。 * @return 读取到一行文本。 * * 原理:就是从缓冲区中取出数据,并存储到一个临时容器中。 * 如果取到了回车符,就将临时容器中的数据转成字符串返回。 * @throws IOException */ public String myReadLine() throws IOException{ //定义一个临时容器。 StringBuilder sb = new StringBuilder(); int ch = 0; while((ch = myRead())!=-1){ if(ch=='\r') continue; if(ch=='\n') return sb.toString(); sb.append((char)ch); } if(sb.length()!=0) return sb.toString(); return null; } |
读取数据的时候可以指定行号, 可以使用LineNumberReader类来完成。LineNunberReader是bufferedReader的子类,其中父类的所有方法都可以使用,是基于缓冲区的一个功能。
public static void main(String[] args) throws Exception { FileReader fr = new FileReader("d:\\abc1.txt"); 创建一个读取流对象,并关联一个文件 FileWriter fw = new FileWriter("d:\\复制.txt");创建一个写的流对象,并关联一个文件 BufferedWriter bufw = new BufferedWriter(fw);创建写的缓冲区对象 LineNumberReader lnr = new LineNumberReader(fr); 创建行号缓冲区对象,它本身集成了bufferedReader类。 String str = null; 定义一个字符串 while ((str = lnr.readLine()) != null) { 从缓冲区中读取一行字符串(其实在这里面还包含了从文件给缓冲区读的过程) System.out.println(lnr.getLineNumber()+":"+str);将读到的数据打印到控制台上,并且将它的行号也打印出来。行号默认从零开始。 bufw.write(str); 将读到的数据写到写的缓冲区中 bufw.flush(); 将写的缓冲区的数据刷新到指定文件中 } lnr.close(); 关闭行号对象,同时关闭了文件读取流 bufw.close();关闭写缓冲区对象。同时关闭文件输出流。 } |
通过缓冲区对文本文件进行复制
public static void main(String[] args) { BufferedReader bufr = null; 定义一个读取缓冲区 BufferedWriter bufw = null; 顶一个一个写缓冲区 try { bufr = new BufferedReader(new FileReader("IO流文档_2.txt")); bufw = new BufferedWriter(new FileWriter("copy_2.txt")); String line = null; while((line=bufr.readLine())!=null){ bufw.write(line); bufw.newLine(); 创建新的一行 bufw.flush(); 将写缓冲区的内容刷新到文件中 } } catch (Exception e) { throw new RuntimeException("读写失败"); }finally{ if(bufw!=null) try { bufw.close(); } catch (IOException e) { e.printStackTrace(); } if(bufr!=null) try { bufr.close(); } catch (IOException e) { e.printStackTrace(); } } } |
字符流:只能操作字符数据,文本文件。字符流的四个对象
FileReader BufferedReader 读
FileWriter BufferedWriter 写
字节流:操作的最小单位是字节。字节流不需要做任何的缓冲,直接将数据写到目的地
InputStream 此类是表示字节输入流的所有类的超类,是个抽象类。
OutputStream 此类是表示输出字节流的所有类的超类,是个抽象类。操作的最小单位都是字节。
FileOutputStream 是OutputStream的子类。用于写入诸如图像数据之类的原始字节的流
FileInputStream 是InputStream的子类 。用于读取诸如图像数据之类的原始字节流
写入字节流:
FileOutputStream fos = new FileOutputStream("d:\\abc2.txt");
fos.write("hahahahah".getBytes());
字节流不涉及任何的编码转换,而字符流对写入的数据通常会进行临时缓存一下,缓存后要进行查表。而字节流不会那么去做,而是将字节变成最基本的字节数据,并将数据直接写到目的。
fos.close();虽然不用刷新,但是必须要关闭资源。
读取字节流:
FileInputStream fis = new FileInputStream("d:\\abc2.txt");
int ch = 0;
while ((ch=fis.read())!=-1){
System.out.println((char)ch);
}
fis.close(); 必须关闭资源。
发现上面的程序读取速度比较慢,需要一个缓冲区来提高效率:
FileInputStream fis = new FileInputStream("d:\\abc2.txt");
byte[] buff = new byte[1024]; 定义一个字符数组来存放读取到的字符
int len = 0;
while((len=fis.read(buff))!=-1){
System.out.println(new String(buff,0,len));
}
fis.close();
在FileInputStream类中有一个方法int available();获取所关联文件的字节大小。
通过字节输入输出流复制一个word文档。 public static void main(String[] args) throws IOException { long start = System.currentTimeMillis(); 开始时间 copy(); long end = System.currentTimeMillis(); 结束时间 System.out.println((end-start)+"毫秒"); } public static void copy() throws IOException { FileInputStream fis = new FileInputStream("F:\\java0218学习\\前五天总结.doc"); FileOutputStream fos = new FileOutputStream("d:\\前五天总结.doc"); byte [] buf = new byte[1024]; int len = 0; while( (len=fis.read(buf))!=-1 ){ fos.write(buf,0,len); } fos.close(); fos.close(); } |
BufferedOutputStream 字节输出缓冲区
BufferedInputStream 字节输入缓冲区
使用字节流缓冲区复制文件 public static void main(String[] args) throws IOException { long start = System.currentTimeMillis(); copy(); long end = System.currentTimeMillis(); System.out.println((end-start)+"毫秒"); } public static void copy() throws IOException { FileInputStream fis = new FileInputStream("F:\\java0218学习\\前五天总结.doc"); FileOutputStream fos = new FileOutputStream("d:\\前五天总结.doc"); BufferedInputStream bufis = new BufferedInputStream(fis); 定义输入字节流缓冲区 BufferedOutputStream bufos = new BufferedOutputStream(fos);定义输出字节流缓冲区 int len = 0; while( (len=bufis.read())!=-1 ){ bufos.write(len); } bufos.close(); bufos.close(); } |
不能使用字符流来完成多媒体文件等文件的原因:字符流不能复这些文件,原因是字符流是有字节流+编码表完成的,每当拿到一个字节都会去查对应的码表,如果表中没有这样的字符的话,就会拿一个相似的字符来替代。然后组合成一个新的字符,这样会导致错误的内容出现,因为不能使用字符流来复制多媒体等文件。
字符流和字节流之间的转换
获取用户的键盘输入,就要使用系统默认的输入设备,可以使用 System.in “标准”输入流。此流已打开并准备提供输入数据。通常,此流对应于键盘输入或者由主机环境或用户指定的另一个输入源。
//获取系统的标准输入设备键盘。类型是InputStream
InputStream in = System.in;
int ch1 = in.read(); 执行一次,从键盘上输入一个字符。
System.out.println("ch1:"+ch1);
读取键盘录入,将一行录入的数据转成大写显示在控制台上,
如果用户录入的是over,那么转成程序结束。
思路:
1,通过System.in获取键盘录入。
2,因为每次获取的都是字节,而用于判断的都是字符串。需要将录入的字节转成字符串进行操作。
3,需要定义临时容器,将录入的字节都存储起来,当到了换行符时,将存储的数据变成字符串。并判断或者转换大写。
public static void readKeyLine() throws IOException{
StringBuilder sb = new StringBuilder();//定义临时容器。
InputStream in = System.in;
int ch = 0;
while((ch=in.read())!=-1){
if(ch=='\r')
continue;
if(ch=='\n'){
String s = sb.toString();
if("over".equals(s)){
break;
}else{
System.out.println(s.toUpperCase());将字符转换成大写,输出这行字符
sb.delete(0, sb.length()); 清空容器中的所有数据,为下次输入做好准备
}
}
else
sb.append((char)ch); 当不是换行的时候存入到缓冲区中
}
}代码完成后,发现该代码其实就是在读取一行数据。而这个动作本身IO对象就有。BufferedReader readLine.
创建字符流缓冲区,因为该缓冲区中有readLine方法。
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;
System.out.println(line.toUpperCase());
bufw.write(line.toUpperCase());
bufw.newLine();
bufw.flush();
}
osw.close();
bufr.close();
通过上面的程序发现,当用户输入的是字节流,而输出用的字符流,那么怎么把字节流转成字符流呢,这个时候就要使用字符流体系中的转换流来完成。
InputStreamReader 字节流通往字符流的桥梁 可以指定具体的编码表
OutputStreamWriter 字符流通往字节流的桥梁 可以指定具体的编码表
InputStream in = System.in;
int ch1 = in.read();
System.out.println("ch1:"+ch1);
InputStreamReader isr = new InputStreamReader(in);
System.out.println("ch4:"+(char)ch4);
转换流的出现非常重要,在遇到中文字符的时候不管是字节流还是字符流都是一个一个字节的读取,不同的是使用字符流,它会将读到的字节数据直接应用。而对于字符流而言,它拿到字节之后不会直接应用,而是拿读到的字节去查表,然后将查完后的那个字符返回。
字节流一次读一个字节,尤其对各个国家特殊的文件来讲,字节流就只能读一个字节,而字符流它能够读对个字节。并将它转成本地编码中的一个文字。
第二十二天总结
IO流的操作规律总结:
BufferedReader bufr = new BufferedReader(new InputStreamReader(new FileInputStream("a.txt"))); 以字节的形式从a.txt中读取字节数据,而BufferedReader中存放的是字符,通过InputStreamReader将字节转换成字符存放在输入缓冲区中 BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("b.txt"))); 字符输出缓冲区中存放的是字符,而指定文本中存放的是字符,这个时候需要通过InputStreamReader将字符转换成字节存放到b.txt文本中。 |
IO流的操作规律总结:
1,明确体系:
数据源:InputStream ,Reader
数据汇:OutputStream,Writer
2,明确数据:因为数据分两种:字节,字符。
数据源:是否是纯文本数据呢?
是:Reader
否:InputStream
数据汇:
是:Writer
否:OutputStream
到这里就可以明确具体要使用哪一个体系了。剩下的就是要明确使用这个体系中的哪个对象。
3,明确设备:
数据源:
键盘:System.in
硬盘:FileXXX
内存:数组。
网络:socket
数据汇:
控制台:System.out
硬盘:FileXXX
内存:数组
网络:socket
4,明确额外功能:
1,需要转换?是,使用转换流。InputStreamReader OutputStreamWriter
2,需要高效?是,使用缓冲区。Buffered
3,需要其他?
1,复制一个文本文件。
1,明确体系:
源:InputStream ,Reader
目的:OutputStream ,Writer
2,明确数据:
源:是纯文本吗?是 Reader
目的;是纯文本吗?是 Writer
3,明确设备:
源:硬盘上的一个文件。 FileReader
目的:硬盘上的一个文件。FileWriter
FileReader fr = new FileReader("a.txt");
FileWriter fw = new FileWriter("b.txt");
4,需要额外功能吗?
需要,高效,使用buffer
BufferedReader bufr = new BufferedReader(new FileReader("a.txt"));
BufferedWriter bufw = new BufferedWriter(new FileWriter("b.txt"));
2,读取键盘录入,将数据存储到一个文件中。
1,明确体系:
源:InputStream ,Reader
目的:OutputStream ,Writer
2,明确数据:
源:是纯文本吗?是 Reader
目的;是纯文本吗?是 Writer
3,明确设备:
源:键盘,System.in
目的:硬盘,FileWriter
InputStream in = System.in;
FileWriter fw = new FileWriter("a.txt");
4,需要额外功能吗?
需要,因为源明确的体系时Reader。可是源的设备是System.in。
所以为了方便于操作文本数据,将源转成字符流。需要转换流。InputStreamReader
InputStreamReader isr = new InputStreamReader(System.in);
FileWriter fw = new FileWriter("a.txt");
需要高效不?需要。Buffer
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufw = new BufferedWriter(new FileWriter("a.txt"));
3,读取一个文本文件,将数据展现在控制台上。
1,明确体系:
源:InputStream ,Reader
目的:OutputStream ,Writer
2,明确数据:
源:是纯文本吗?是 Reader
目的;是纯文本吗?是 Writer
3,明确设备:
源:硬盘文件,FileReader。
目的:控制台:System.out。
FileReader fr = new FileReader("a.txt");
OutputStream out = System.out;
4,需要额外功能?
因为源是文本数据,确定是Writer体系。所以为了方便操作字符数据,需要使用字符流,但是目的又是一个字节输出流。需要一个转换流,OutputStreamWriter
FileReader fr = new FileReader("a.txt");
OutputStreamWriter osw = new OutputStreamWriter(System.out);
需要高效吗?需要。
BufferedReader bufr = new BufferedReader(new FileReader("a.txt"));
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
4,读取键盘录入,将数据展现在控制台上。
1,明确体系:
源:InputStream ,Reader
目的:OutputStream ,Writer
2,明确数据:
源:是纯文本吗?是 Reader
目的;是纯文本吗?是 Writer
3,明确设备:
源:键盘:System.in
目的:控制台:System.out
InputStream in = System.in;
OutputStream out = System.out;
4,需要额外功能吗?
因为处理的数据是文本数据,同时确定是字符流体系。
为方便操作字符数据的可以将源和目的都转成字符流。使用转换流。
为了提高效率,使用Buffer
BufferedReader bufr =new BufferedReader(new InputStreamReader(Systme.in));
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(System.out));
5,读取一个文本文件,将文件按照指定的编码表UTF-8进行存储,保存到另一个文件中。
1,明确体系:
源:InputStream ,Reader
目的:OutputStream ,Writer
2,明确数据:
源:是纯文本吗?是 Reader
目的;是纯文本吗?是 Writer
3,明确设备:
源:硬盘:FileReader.
目的:硬盘:FileWriter
FileReader fr = new FileReader("a.txt");
FileWriter fw = new FileWriter("b.txt");
4,额外功能:
注意:目的中虽然是一个文件,但是需要指定编码表。
而直接操作文本文件的FileWriter本身内置的是本地默认码表。无法明确具体指定码表。
这时就需要转换功能。OutputStreamWriter,而这个转换流需要接受一个字节输出流,而且
对应的目的是一个文件。这时就使用字节输出流中的操作文件的流对象。FileOutputStream.
FileReader fr = new FileReader("a.txt");
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("b.txt"),"UTF-8");
需要高效吗?
BufferedReader bufr = new BufferedReader(new FileReader("a.txt"));
BufferedWriter bufw =
new BufferedWriter(new OutputStreamWriter(new FileOutputStream("b.txt"),"UTF-8"));
File 类
file类:不仅代表文件,而且还是可以代表文件夹。File类封装了文件自己的属性,比如文件的大小,文件存储的路径。流是操作文件中的具体数据。File是操作这个文件的。
记住:file创建的封装的文件或者文件夹,可以是存在的,也可以不存在的。
演示File构造方法
File f1 = new File("c:\\a.txt");
File f2 = new File("c:\\","a.txt"); 将目录与文件分离。
File f = new File("c:\\"); 将目录封装成对象
File f3 = new File(f,"a.txt"); 在目录对象的位置的文件
File f1 = new File("c:"+File.separator+"a.txt"); File.separator与系统有关的默认名称分隔符,为了方便,它被表示为一个字符串。
File 类常见的方法:
1、创建文件:
boolean createNewFile(); 如果该文件不存在,会创建,如果已存在,则不创建,不会像输出流一样覆盖。
File f = new File("file.txt");
boolean b = f.createNewFile();
2、删除文件
boolean delete(); java的删除不走回收站。使用需慎重,它是从最子层目录开始删除,如果当前文件中还有文件的话,是无法删除的。
File dir = new File("abc.txt");
dir.mkdir();
boolean b = dir.delete(); 删除成功返回true
File dir = new File("abc1.txt");
dir.deleteOnExit();JVM退出时必须删除的文件
java不能删除系统文件,还有某个流正在操作的文件也无法删除。
3、创建文件夹或者多级文件夹
boolean mkdir(); 创建一个文件夹
boolean mkdirs(); 创建多层目录文件夹
4、获取
getAbsolutePath:获取绝对路径。 路径不完整的就是相对路径,路径完整的就是绝对路径
getPath:获取相对路径。File中封装的是什么,就获取什么。
getParent:获取父目录。
getName:获取文件名称。
lastModified:获取最后一次被修改的时间
length 获取文件的大小
long time = file.lastModified();
Date date = new Date(time);
DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG); 将获取的时间变成指定的时间格式
String str_time = dateFormat.format(date);将这个时间转换成字符串
5、判断
boolean canRead();判断文件是否可读
boolean canWriter();判断文件是否可写
boolean exists(); 判断文件是否存在。
boolean isFile(); 判断File对象中封装的是否是个文件
boolean isDirectory(); 判断File对象中封装的是否是个路径
在判断File对象中封装不管是文件还是路径必须保证文件存在才可以判断,也就是说exists为true才可以进行File对象中封装的文件和路径的判断。
6、其他方法:
renameTo 重命名
File f1 = new File("aaa.txt");
File f2 = new File("c:\\qq.txt");
f1.renameTo(f2); 将f1文件的名字该为f2的名字,并删除原来的文件。也可以说是将f1文件存放到f2文件的目录位置,再将f1文件名该为f2的文件名。并删除f1文件。其实本质就是剪切功能。
listRoots() 获取本机的所有盘符。
File[] files = File.listRoots();
for(File f : files){
System.out.println(f);
System.out.println("getFreeSpace:"+f.getFreeSpace());获取本地磁盘剩余空间
System.out.println("getTotalSpace:"+f.getTotalSpace());获取本地磁盘总共空间
System.out.println("getUsableSpace:"+f.getUsableSpace());获取本地磁盘可用空间
}
List()
列出当前路径中的所有文件名和文件夹名字(包含隐藏文件)。不会给出具体路径,只能得到名称。
File dir = new File("c:\\");
String[] names = dir.list();
for(String name : names){
System.out.println(name);
}
listFiles() 取到当前路径下的对象,而对象中既包含着名称,又包含着目录和所咋路径。
File[] files = dir.listFiles();
for(File f : files){
System.out.println(f.toString());
}
String list(FilenameFilter filter) 文件过滤
实现FilenameFilter接口,通过构造函数指定对应的扩展名(后缀名)。然后覆写accept方法 public class FilterBySuffix implements FilenameFilter { private String suffix; public FilterBySuffix(String suffix) { super(); this.suffix = suffix; } public boolean accept(File dir, String name) { return name.endsWith(suffix); endsWith是String类中测试此字符串是否以指定的后缀结束的方法 } } |
private static void method_2() { File dir = new File("c:\\"); String[] names = dir.list(new FilterBySuffix(".java")); for(String name : names){ System.out.println(name); } } |
String listFiles(FilenameFilter filter) 过滤隐藏文件
自定义一个过滤器,实现FileFilter,覆写accept方法 public class FilterByHidden implements FileFilter { public boolean accept(File pathname) { return pathname.isHidden(); isHidden是隐藏文件 } } |
递归:函数自身直接或者间接调用自身就是递归。当一个功能被重复使用,而且使用过程中,参数运算的数据不断的发生着变化。这时可以使用递归这种手段来解决问题。
递归的前提
1、必须要定义条件;2、控制递归次数。这两前提:任何一个出问题都会导致栈内存溢出。
Properties 用于存储键值的map集合中HashMap的子类。
记录应用程序使用次数,
如果满足5次,要给出提示,软件使用次数已到,请注册!
思路:
1、其实需要一个计算器
2、这个计算器不能仅仅存储到内存中,需要持久化。
3、每次软件启动都要读取该文件中的已有次数,并取出对其进行自增,在重新存回去
4、为了保证文件中存储的数据有意义,需要给数据起个名字,出席那了键值对
5、该数据还在硬盘上,需要使用io技术,还需要用到map集合,最好的结果就是Properties。
public class FilePropertiesTest {
public static void main(String[] args) throws IOException{
//将配置文件封装成file对象
File confFile = new File("d:\\time.properties");
if(!confFile.exists()){
confFile.createNewFile();
}
FileInputStream fis = new FileInputStream(confFile);
//创建properties集合
Properties prop = new Properties();
//加载流中的数据
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++;
prop.setProperty("time",count+"");
FileOutputStream fos = new FileOutputStream(confFile);
prop.store(fos,"");
fos.close();
fis.close();
}
}
************** 打印流 ******************
打印流:该流提供了打印方法,可以将各种数据类型的数据都原样打印。
字节打印流 PrintStream
特点:
1.构造函数可以接收的参数类型
⑴.file对象
⑵.字符串路径
⑶.字节输出流
2.该对象具备特有的方法,打印方法 print println ,可以打印任何类型的数据。
3.特有的print方法可以保持任意类型数据表现形式的原样性,将数据输出到目的地。
对于OutputStream父类中的write,是将数据的最低字节写出去。
字符打印流 PrintWriter
特点:
1.当操作的数据是字符时,可以使用PrintWrite,比使用PrintStream要方便。
2.构造函数可以接收的参数类型
⑴.file对象
⑵.字符串路径
⑶.字节输出流
⑷.字符输出流
3.可以在构造函数中如果参数是输出流,那么可以通过指定另一个参数true完成自动刷新,
该true对println方法有效。
什么时候用?
当需要保证数据表现的原样性时,就可以使用打印流的打印方法来完成,这样更为方便。保
证表现形式原样性的原理:其实就是将数据变成字符串,再进行写入操作。
************* 序列流 *******************
序列流 SequenceInputStream
将多个流合并成一个流。
代码演示:
package IO;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.SequenceInputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
//首先在D盘目录下创建三个文件1.txt,2.txt,3.txt
public class SquenceInputStreamDemo {
public static void main(String[] args) throws IOException{
ArrayList<FileInputStream>
al = new ArrayList<FileInputStream>();
for(int x = 1;x<=3;x++){
//循环读取文件
al.add(new FileInputStream("d:\\"+x+".txt"));
}
//需要的枚举接口可以通过
// Collections.enumeration(Collection);获取
Enumeration<FileInputStream>
en = Collections.enumeration(al);
SequenceInputStream sis = new SequenceInputStream(en);
FileOutputStream fos = new FileOutputStream("d:\\4.txt");
byte[] buf = new byte[1024];
int len = 0;
while((len=sis.read(buf))!=-1){
fos.write(buf,0,len);
}
fos.close();
sis.close();
}
}
特点:
1.将多个字节读取流合并成一个读取流,将多个源合并成一个源,操作起来方便。
2.需要的枚举接口可以通过Collections.enumeration(Collection);获取。
对象的序列化
ObjectOutputStream
该对象必须实现 Serializable标记接口。
package cn.itcast.d.bean;
import java.io.Serializable;
public class Person implements Serializable {
private static final long serialVersionUID = 42L;
private /*static*/ String name;//静态数据不会被序列化。
private /*transient*/ int age;//可以让非静态的数据不被序列化。
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
对象的反序列化使用ObjectInputStream 。
/*
* 将对象进行持久化存储,对象的序列化。
*/
public class ObjectStreamDemo {
/**
* @param args
* @throws IOException
* @throws ClassNotFoundException
*/
public static void main(String[] args) throws IOException, ClassNotFoundException {
//writeObj();
readObj();
}
/*
* 对象的反序列化。使用ObjectInputStream
*/
private static void readObj() throws IOException, ClassNotFoundException {
FileInputStream fis = new FileInputStream("obj.txt");
ObjectInputStream ois = new ObjectInputStream(fis);
Person p = (Person)ois.readObject();
System.out.println(p.getAge()+":"+p.getName());
Person p1 = (Person)ois.readObject();
System.out.println(p1.getAge()+":"+p1.getName());
Person p2 = (Person)ois.readObject();
System.out.println(p2);
ois.close();
}
/*
* 对象的序列化。
*/
private static void writeObj() throws IOException {
/*
* 将内存中的数据写入到文件中。
*/
FileOutputStream fos = new FileOutputStream("obj.txt");
//数据有很多,被封装到Person对象中。
Person p = new Person("wangwu",34);
//要想实现使用流将数据的封装体对象进行写出,需要使用流体系中的功能对象。
ObjectOutputStream oos = new ObjectOutputStream(fos);
//使用ObjectOutputStream的writeObject方法将指定对象写入到目的地。
oos.writeObject(p);
oos.writeObject(new Person("lisi",25));
oos.close();
}
}
用于将对象的生命周期延长。
注意:静态数据不会被序列化。
关键字:transient 可以让非静态的数据不被序列化。
************** 随机访问文件*************
RandomAccessFile 类
此类对象
特点:
⑴既能读取也可以写入。
⑵内部维护了一个大型的byte数组,通过对数组的操作完成读取和写入
⑶通过getFilePointer方法获取指针的位置,还可以通过seek方法设置指针的位置。
⑷该对象的内容应该封装了字节输入流和字节输出流
⑸该对象只能操作文件。
创建RandomAccessFile对象时,如果文件不存在则创建,如果文件存在则不创建。
public static void main(String[] args) throws IOException {
//writeFile();
//randomRead();
randomWrite();
}
/*
* 随机写入。
*/
public static void randomWrite()throws IOException{
RandomAccessFile raf = new RandomAccessFile("random.txt", "rw");
int num = 0;
raf.seek(8*num);
/*
* 写入第四个人的信息。王五 102.
*/
raf.write("张三".getBytes());
raf.writeInt(65);
raf.close();
}
/*
* 随机读取。
*/
public static void randomRead() throws IOException{
//读取任意一个人的信息,比如李四。
RandomAccessFile raf = new RandomAccessFile("random.txt", "r");
int n = 1;
//只要改变指针的位置,通过seek方法。
raf.seek(8*n);
byte[] buf = new byte[4];
raf.read(buf);//一次读四个字节并存储。
String name = new String(buf);
int age = raf.readInt();
System.out.println(name+".."+age);
System.out.println("pos:"+raf.getFilePointer());
raf.close();
}
/*
* 读取数据。random.txt 使用RandomAccessFile.
*/
public static void readFile() throws IOException{
RandomAccessFile raf = new RandomAccessFile("random.txt","r");
byte[] buf = new byte[4];
raf.read(buf);//一次读四个字节并存储。
String name = new String(buf);
int age = raf.readInt();
System.out.println(name+".."+age);
raf.close();
}
/*
* 通过RandomAccessFile类创建文件并给文件中写入数据。
* 数据以:姓名+年龄为主的个人信息。
*/
public static void writeFile() throws IOException{
/*
* 创建RandomAccessFile对象时,如果文件不存在则创建,
* 如果文件已存在,则不创建。
*/
RandomAccessFile
raf = new RandomAccessFile("random.txt","rw");
raf.write("张三".getBytes());
raf.writeInt(97);
raf.write("李四".getBytes());
raf.writeInt(99);
raf.close();
}
}
通过seek方法操作指针,可以从这个数组中的任意位置上进行读和写。可以完成对数据
的修改,但是注意:数据必须有规律。
--------------------- android培训、java培训、期待与您交流!
----------------------详细请查看:http://edu.csdn.net/heima