02_输入与输出I/O(基础篇)

来源:互联网 发布:淘宝上nike正品店 编辑:程序博客网 时间:2024/05/19 18:17

一、编码格式总结

1、ASC码表:美国信息标准交换码(这是最初的一个表,其中的字母用各种各样的数字进行代替)。
2、GB2312:中国汉字的编码表(里面收录了几千个中国常用汉字和字符)。
3、GBK:GB2312的扩容版(里面收录了两万多的中国汉字,因为中国汉字博大精深,一般中国使用的都是该编码格式)。
4、(18030)表:相当于GBK的扩容版(里面收录了各个民族的字,后期会讲到)。
5、Unicode表:将世界上所有的语言中的文字进行的整合,所产生的一个表(里面无论什么字符都用两个字节进行表示,也就是16个二进制位)
6、UTF-8表:Unicode表进行优化之后的一个表(该使用一个字节的用一个字节,该用两个或者三个的用两个或者三个)。

二、“数据流”概念
学习“数据流”之前,一定要明白的是“流”就是用来操作数据的!不管什么“流”!

三、字符流的“读取”和“写入”
1、字符流的概念:
“字符流”为了让“汉字”的编码不出现错误,而出现的一个“流”形式,在这个“流”中,用户或者编程人员可以自己指定编码的格式,让java知道,这个“流”就可以正确识别该使用怎样的编码格式,从属关系:“字符流”基于“字节流”。(没有“字节流”就没有“字符流”!)。

2、字符文件的“读取”FileReader&“写入”FileWriter:
FileRead类创建的对象就是为了“读取”某个文件中的“字符“内容的,注意,是”字符“内容!
FileWriter类创建的对象就是为了“写入”到某个文件中的”字符“内容的,注意,是”字符“内容!

3、文件的copy操作(一般有两种方法):
(1)、逐个字符进行copy,使用的是read方法,这样的效率不是太高,是很不高!
(2)、常用的是使用一个定义了一定长度的字符数组(char类型),然后按照指定长度逐个长度取出字符内容,也是使用的read方法,参数不一样,这样的效率有点高!

下面是“逐个字符”写入的操作:

public static void main(String[] args) throws IOException { String pass01="d:\\你好.java"; String pass02="d:\\你坏.txt"; FileReader fr = null; FileWriter fw = null; try{  fr=new FileReader(pass01);  fw=new FileWriter(pass02);  int num=0; //进行一下初始化  while((num=fr.read())!=-1){   fw.write(num); //虽然写入的是int类型的数据,但实际写入的是字符串类型的数据!  } } catch(Exception e){  System.out.println("error is happened"); } finally{  fr.close();  //全部关流操作,这样才可以写入数据的。关流操作中带有刷新的操作。情况同下。  fw.close(); } }



下面是“一段字符串一段字符串”写入的操作:

public static void main(String[] args) throws IOException {String pass01="d:\\你好.java";String pass02="d:\\你坏.txt";FileReader fr = null;FileWriter fw = null;try{fr=new FileReader(pass01);fw=new FileWriter(pass02);char[] len=new char[1024];int length=0;while((length=fr.read(len))!=-1){fw.write(len,0,length);//虽然写入的是int类型的数据,但实际写入的是字符串类型的数据,一定要记住这个内容!}}catch(Exception e){System.out.println("error is happened");}finally{fr.close();//全部关流操作,这样才可以写入数据的。关流操作中带有刷新的操作!fw.close();}}



注意:开的“读写流”越多,需要关闭的“读写流”也越多,需要的try...catch也就越多!由于在创建“文件”的时候,可能会遇到创建不成功的情况,为了解决这种情况,需要使用try...catch异常语句,或者直接抛出异常也可以。

 

4、缓冲区
什么是缓冲区?
答:可以这样理解,缓冲区就是一个“水池”,有大有小,当然大了比较好而已。既然修了“水池”,也就必须有“水流”才可以,这里可以以“字符流”为例子,所以“缓冲区”的前提是必须有“流”才可以,缓冲区的意思就是“提高效率”的意思。

注意:
一个“流”使用了“缓冲区”之后,效率是提高了,但是如果想要得到最后的显示效果,比方说向一个文件中写入数据,想让这些数据显示出来,以前,自己知道使用的是方法flush或者是close方法来刷新流或者关闭流对象。但是,使用了“缓冲区”之后,该关闭哪个呢?是关闭“缓冲区”还是“流”?实际上,只需要关闭“缓冲区”就可以啦!关闭“缓冲区”的时候,就是在关闭“缓冲区”中的“流对象”,从而使需要写入的内容显示出来。

“缓冲区”常用方法:
nextLine()方法,就是换行的意思,相当于调用windows中的“\r\n”,相当于调用linux中的“\n”,一个方法在两个操作系统中都可以使用的。毕竟是java嘛,跨平台!优势所在。

5、字符缓冲区“BufferedReader”:
API概述:
见名知意,这个缓冲区就是为了给“字符流”提供高效的读取操作的。从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的“高效读取”。 可以指定缓冲区的大小,或者可使用默认的大小。大多数情况下,默认值就足够大了。 通常,Reader 所作的每个读取请求都会导致对底层字符或字节流进行相应的读取请求。因此,建议用 BufferedReader 包装所有其 read() 操作可能开销很高的 Reader(如 FileReader 和 InputStreamReader)。例如, BufferedReader in = new BufferedReader(new FileReader("foo.in"));将缓冲指定文件的输入。如果没有缓冲,则每次调用 read() 或 readLine() 都会导致从文件中读取字节,并将其转换为字符后返回,而这是极其低效的。通过用合适的 BufferedReader 替代每个 DataInputStream,可以对将 DataInputStream 用于文字输入的程序进行本地化。

常用方法:
这个类有一个比较牛的方法,就是readLine()方法,意思就是“一行一行”的对文本文件进行读取,这个方法比较牛,因为自己知道,文本文件中的内容都是一行一行的显示的,所以读取的时候,也是一行一行读取的。可以使用一个while循环,对整个文件进行读取,但是用readLine()方法返回的不是数值,是string类型的字符串,所以判定,如果返回的string类型是null的话,就会停止遍历。(说白了就是提高了读取效率!)

BufferedReader创建方法:
BufferedReader bufr=new BufferedReader(字符流对象);然后可以使用BufferedReader类中封装的方法readLine()方法一行一行读取文本内容。

 

子类LineNumberReader使用方法:
BufferedReader的子类LineNumberReader可以获取指定文件的每一行的行数,具体的使用方法如下所示:

public static void readFromFile(String filename) { LineNumberReader lineNumberReader = null; try {  lineNumberReader = new LineNumberReader(new FileReader(filename));  //里面一个文件读取流  String line = null;  while ((line = lineNumberReader.readLine()) != null) { //因为是BufferedReader的子类,所以可以使用readLine方法   System.out.println("Line " + lineNumberReader.getLineNumber()+ ": " + line); //象征意义的输出一下  } } catch (FileNotFoundException ex) {  ex.printStackTrace(); } catch (IOException ex) {  ex.printStackTrace(); } finally {  try {   if (lineNumberReader != null) {    lineNumberReader.close(); //判断关流操作   }  } catch (IOException ex) {   ex.printStackTrace();  } }}


 

 

 

自定义readLine方法
概述:
自己知道,在“字符缓冲区BufferedReader”中,可以使用readLine方法一行一行的读取文本文件中的内容,那么它的工作原理到底是什么呢?自己可以自定义一个 readLine方法来验证一下,下面是具体的验证代码:

class MyBufferedReader{ //将自己定义的readLine方法封装到MyReadLine类中,成为构造函数。 private FileReader r;  //定义一个是为了传到后面的方法中 MyBufferedReader(FileReader r){  //构造函数  this.r=r; } public String MyReadLine() throws IOException{ //构造方法String类型  StringBuilder sb=new StringBuilder();  int ch=0;  while((ch=r.read())!=-1){ //底层使用的是read方法   if(ch=='\r')    continue; //返回去,重新读一遍   if(ch=='\n')    return sb.toString(); //如果读完了“回车符”,那么就可以“返回”前面遍历的字符内容了,读取了一行内容   else    sb.append((char)ch);  }  return null; } public void myClose() throws IOException{//调用上面定义的r,然后调用close()方法  r.close(); }}public class MyReadLineDemo { public static void main(String[] args) throws IOException {  FileReader fr=new FileReader("d:\\我是被拷贝的java文件.java");  MyBufferedReader myBuf=new MyBufferedReader(fr);  String line=null;  while((line=myBuf.MyReadLine())!=null){  //因为调用的是MyReadLine方法,所以反推应该是自定义一个包含MyReadLine方法的类,   System.out.println(line);  }  fr.close(); }}


 

 

6、字符缓冲区“BufferedWriter”:
API概述:
将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。 可以指定缓冲区的大小,或者接受默认的大小。在大多数情况下,默认值就足够大了。 该类提供了 newLine() 方法,它使用平台自己的行分隔符概念,此概念由系统属性 line.separator 定义。并非所有平台都使用新行符 ('\n') 来终止各行。因此调用此方法来终止每个输出行要优于直接写入新行符。 通常 Writer 将其输出立即发送到底层字符或字节流。除非要求提示输出,否则建议用 BufferedWriter 包装所有其 write() 操作可能开销很高的 Writer(如 FileWriters 和 OutputStreamWriters)。例如,  PrintWriter out= new PrintWriter(new BufferedWriter(new FileWriter("foo.out")));将缓冲 PrintWriter 对文件的输出。如果没有缓冲,则每次调用 print() 方法会导致将字符转换为字节,然后立即写入到文件,而这是极其低效的。

BufferedWriter创建方法:
BufferedWriter bufw=new BufferedWriter(字符流流对象);然后就是使用BufferedWriter类中封装的write()方法一行一行读取文本内容。

四、字节流的“读取”和 "写入"
1、字节文件的“读取操作”:
创建“读取流”对象方法:FileInputStream fis=new FileInputStream("源文件的名字和路径");“字节流”就是一个字节一个字节进行遍历的,字节流“FileInputStream”中有一个方法,available()方法,返回的是一个int类型的数值,也就是将整个文件中的字节数返回,所以可以将这个数值传入到字节数组中,用字节数组进行遍历,然后使用read方法将这个字节流中的内容打印出来。

2、下面是一个超级简单的“字节读取流”程序示例:

FileInputStream fis=new FileInputStream("源文件的名字和路径");byte[] byte=new byte[fis.available]; //定义一个fis.available数值长度的字节数组fis.read(byte);System.out.println(new String(byte));fis.close();



注意:上面的这个小代码,虽然不用while循环进行文件的循环读取,但是如果文件足够大的话,自己电脑内存又不足,电脑直接挂掉,所以还是使用一个数组进行while循环的是最“中肯”的方法,上面的这个方法谨慎使用!

3、字节文件的“写入操作”:
创建“写入流”对象方法:FileOutputStream fos=new FileOutputStream(“目标文件的名字和路径”);可以将字符一个一个写入目标文件中。

4、字节缓冲区“BufferedInputStream”
API概述:
BufferedInputStream 为另一个输入流添加一些功能,即缓冲输入以及支持 mark 和 reset 方法的能力。在创建 BufferedInputStream 时,会创建一个内部缓冲区数组。在读取或跳过流中的字节时,可根据需要从包含的输入流再次填充该内部缓冲区,一次填充多个字节。mark 操作记录输入流中的某个点,reset 操作使得在从包含的输入流中获取新字节之前,再次读取自最后一次 mark 操作后读取的所有字节。

BufferedInputStream创建方法:
BufferedInputStream bis=new BufferedInputStream(字节流对象);然后就可以使用该类中的read(byte[] b, int off, int len) 方法一次读取一个字节数组,就可以高效率的进行读取操作。

5、字节缓冲区“BufferedOutputStream”
API概述:
该类实现缓冲的输出流。通过设置这种输出流,应用程序就可以将各个字节写入底层输出流中,而不必针对每次字节写入调用底层系统。

BufferedInputStream创建方法:
BufferedInputStream bis=new BufferedInputStream(字节流对象);可以使用该类中的write(byte[] b, int off, int len)方法一次写入一个字节数组长度的内容。

五、“字节流”和“字符流”的相互转换
1、“字节流”转“字符流”InputStreamReader

为什么转换:
“字节流”读取文件效率比较低,需要改进,所以可以将“字节流”改成“字符流”,因为“字符流”中的“字符缓冲区BufferedReader”有一个方法readLine()比较厉害,可以一次读取一行的内容,所以为了用上这个功能,可以将“字节流”转换成“字符流”,从而提高效率。使用类“InputStreamReader”创建的转换对象,可以将先需要转换的“字节流”创建的对象传进来。

创建方法:
InputStreamReader isr=new InputStreamReader(System.in);意思就是将“系统输入”的”字节流“转换成”字符流“。

2、“字符流”转“字节流”OutputStreamWriter
创建方法:
OutputStreamWriter osw=new OutputStream(System.out);意思就是将“字符流”转换成“系统输出”的字节流。

六、”字节流“和”字符流“的区别
1、如果复制一个文本文件的话,推介使用“字符流”,因为可以让其中的汉字不出错。
2、如果复制一个图片或者电影文件,因为其中都是字节,所以使用的是“字节流”,不会出错,并且,如果使用的是一个字节一个字节的读取的话,更不会出错的。

七、”流对象“的自定义编码
1、概述:
“字节流”转“字符流”:InputStreamReader
“字符流”转“字节流”:OutputStreamWriter
这两个类在转换的时候,会牵涉到字符的编码格式,系统使用的是默认的编码格式,在中国,应该是GBK,或者是Unicode,但是在使用上面的转换流,可以实现手动自定义指定编码格式。

2、”字节流“转”字符流“设定编码格式:
InputStreamReader isr=new InputStreamReader(new FileInputStream("d.txt"),"UTF-8");意思就是读取文件的时候将d.txt文件的编码格式设置成utf-8格式。
”字符流“转”字节流“设定编码格式:
OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("s.txt"),"UTF-8");意思就是指定了写入文本内容的时候用的是utf-8的编码格式。

3、总结:
这个方法相当的吊咋天!

八、”字节字符“转换常用语句
1、概述:
总结了”字节流“和”字符流“之间的转换经常用到的语句,包括缓冲区的使用,操作文本文件使用的是字符文件,一般文件使用的是字节文件。

2、句型:
“字节流”转“字符流”的使用缓冲区转换语句是(必须记住):
BufferedReader bufr=new BufferedReader(new InputStreamReader(System.in)); //这里是:源,这个示例是将“字节流”转换成了“字符流”。

“字符流”转“字节流”的转换语句是(必须记住):
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();



往后只要用到“字节”或者是“字符”的输入与输出,就用上面的示例代码,相当干练!

九、“流操作”的基本规律
1、明确“源”和“目的”
源:输入流。InputStream(常用FileInputStream)、 Reader(常用FileReader)
目的:输出流:OutputStream(常用FileOutputStream)、Writer(常用FileWriter)

2、操作的数据是否是纯文本。
是:字符流
不是:字节流

3、当体系明确后,在明确要使用哪个具体的对象。
通过设备来进行区分:
源设备:内存,硬盘,键盘
目的设备:内存,硬盘,控制台

4、实战总结
将一个文本文件中数据存储到另一个文件中,复制文件。
源:因为是源,所以使用读取流,InputStream(常用FileInputStream)、Reader(常用FileReader)
是不是操作文本文件?
是!这时就可以选择Reader(常用FileReader)
这样体系就明确了。
接下来明确要使用该体系中的哪个对象。
明确设备:硬盘上一个文件。
Reader体系中可以操作文件的对象是FileReader
是否需要提高效率:是!,加入的Reader体系中缓冲区BufferedReader
FileReader fr=new FileReader("a.txt");
BufferedReader bufr=new BufferedReader(fr);
目的:OutputStream Writer
是否是纯文本。
是!Writer(常用FileWriter)
设备:硬盘上的一个文件。
Writer体系中可以操作文件的对象FileWriter。
是否需要提高效率:是!。加入Writer体系中缓冲区BufferedWriter
FileWriter fw=new FileWriter("b.txt");
BufferedWriter bufw=new BufferedWriter(fw);

这样,一个像样的文件拷贝小程式就完成了!

十、装饰设计模式
1、概述:
什么叫“装饰设计模式”?见名知意,就是将一个“设计好的类装饰了一下而已,增强其功能”。当想要对已有的对象进行功能的增强的时候,可以定义一个装饰类,将已有的类创建的对象传入,基于已有的功能,并且提供加强功能。那么这个自定义的类就是“装饰类”,这个设计模式就叫做“装饰设计模式”。

2、格式:
“装饰设计模式”通常会使用“装饰类”的“构造方法”接受被装饰的类创建的对象,并基于被装饰的对象的功能,提供更强的功能。

 

3、例子:
比方说我定义了一个Dog类,里面创建的一个“Cry”方法就是输出一句话“汪汪”但是我觉得这个功能太少了,因为Dog类还有很多的功能,比方说可以“睡觉”“尿尿”等,所以我可以定义一个SuperDog装饰类,这个类用来加强Dog类中没有的功能,可以使用SuperDog类中的构造方法将前面定义的Dog类创建的对象传入,然后再定义一个SuperCry方法,在里面定义一些其他的功能,然后将Dog类中cry方法进行调用即可,这样相当的不错,然后再主函数中只要创建两个类的对象,然后将Dog类创建的对象传入到SuperDog后面作为参数中即可。

class Dog{ public void cry(){  System.out.println("汪汪"); }}class SuperDog{ private Dog d; SuperDog(Dog d){  this.d=d; } public void superCry(){  d.cry();  System.out.println("drink");  System.out.println("pee");  System.out.println("shit"); }}class Introduce{ Introduce(){  System.out.println("this is a introduce below:"); }}public class ZhuangShiSheJiMoShi { public static void main(String[] args) {  Dog d=new Dog();  new Introduce();  new SuperDog(d).superCry(); //所有功能都能实现! }}


 

 

4、“装饰类”和“继承类”的区别
两者的确很相似,都可以实现相同的功能。但是,“装饰设计模式”比“继承”要灵活,避免了“继承体系”的臃肿,降低了类与类之间的从属关系,装饰类因为增强了对象,具备的功能和已有的是相同的,只不过提供了更强的功能。所以“装饰类”和“被装饰类”通常都属于一个体系中。
可以想象到,如果使用的是继承的方法的话,那么整个体系将会比较臃肿,如果使用的是“装饰模式”的话,如果自己写的一个超级类中出现了问题需要被删掉的话,将会随时可以将自己写的扩展类“撤掉”,这是相当的方便的。

十一、“合并流”SequenceInputStream
1、概述:
首先明白什么是“流的合并”?就是将几个“读取流”合并成一个流,然后对这个合成的流进行操作。比方说有2个文件的“读取流”,先不管是用“字符流”还是“字节流”,先进行读取操作,自己可以使用SequenceInputStream类创建的“流对象”对这3个流进行“合并”,也可以说成是进行了封装操作。然后将这个“合并流”写入到第3个文件中去,相当的叼咋天吧!?

2、注意:
没有SequenceOutputStream这个类,因为几个“流对象”进行“合并”之后,成为了一个新的“流对象”,这个流对象就是SequenceInputStream类创建的对象,只不过是用来读取的,里面只有简单的几个方法,然后用这些方法进行操作就可以啦。如果有SequenceOutputStream这个类的话,意思将会是:将一个“流对象”进行拆分,但是没有这样的类!

 

3、举例说明问题:
程式要求:将两个文本中的内容写入到第三个文本中。

public class SequenceInputStreamDemo { public static void f() throws IOException{  //创建合并流  SequenceInputStream sis=null;  //创建输出流  FileOutputStream out=null;  //创建输入流  FileInputStream f1=null;  FileInputStream f2=null;  try{   f1=new FileInputStream("d:\\file1.txt");   f2=new FileInputStream("d:\\file2.txt");    sis=new SequenceInputStream(f1,f2);   out=new FileOutputStream("d:\\file3.txt");   byte[] buf=new byte[1024];   int len=0;   while((len=sis.read(buf))!=-1){    out.write(buf,0,len);    out.flush();   }  }  catch(FileNotFoundException e1){   e1.printStackTrace();  }  finally{   try{    if(sis !=null)     sis.close();   }   catch(IOException e){    e.printStackTrace();   }   try{    if(out!=null)     out.close();   }   catch(IOException e){    e.printStackTrace();   }  } } public static void main(String[] args) throws IOException{    f(); }}


 十二、文件的切割

前面学过了文件的合并,下面这个是文件的切割,文件的切割没有什么技术含量,用已经学过的知识点就可以实现这个功能,也就是将“字符”或者是“字节”一段一段的进行读取然后另存就可以了,下面是小代码示例:

FileInputStream fis=new FileInputStream("d:\\Total.txt");//关联Total.txt文件FileOutputStream fos=null;//输出文件不能确定,初始化为空byte[] buf=new byte[4];//一次分割4个字节int length=0;int counter=1;while((length=fis.read(buf))!=-1){fos=new FileOutputStream("d:\\"+(counter++)+".txt");//创建了“流对象”,但是没有进行操作呢,下面就是写入操作,将上面定义的流对象进行写入操作。fos.write(buf, 0, length);fos.close();}try{if(fis!=null)fis.close();}catch(IOException e){throw e;}


 

十二、File文件类
1、概述:
前面学过了很多的“流”,但是“流”归根到底是对“数据”的一种操作而已,你能对文件进行一定的操作,但是不能设置他的路径什么的。所以引入“File”这个类实现对“文件”和“文件夹”的详细操作。

2、转义字符:
自己知道java中有转义字符,所以在windows平台中,如果使用的是一个目录的话,比方说使用是:“c:\\dd\\asd\\谷歌”,使用的是“双斜杠”,但是到了linux平台中,就可能出现错误,那么怎样解决这样的问题的呢?在File类中有一个方法是separator(),可以使用File类创建的对象直接使用这个方法产生一个分隔符,从而实现跨平台的使用。

3、File的创建和删除:
(1)、File创建:创建就基本不用说了,File类创建对象的时候,后面的String类型的字符串就是文件的路径和文件名,然后调用createNewFile()方法进行文件的创建了。创建文件夹的时候,使用mkdir()方法,可以创建简单的目录结构。使用mkdirs()方法,可以创建多级目录结构。
(2)、FIle删除:删除就是使用的是delete()方法,实际上“删除”有两种方法,一种是delete(),一种是deleteOnExite()方法,这两种删除方法有什么不同呢?delete()方法直接删除文件,返回的是boolean。deleteOnExite()方法退出虚拟机的时候删除文件,有点像“临时文件的删除一样”。
下面是一个删除文件和文件夹的小程式

public class FilesOrFolderDeleteDemo { /**  * 本例子用来演示怎样删除多级目录,使用的是“递归调用”的方法。  */ public static void delete(File dir){  File[] files=dir.listFiles(); //返回的是一个数组  for(int i=0;i<files.length;i++){   if(files[i].isDirectory()){ //判断是否是目录    delete(files[i]); //调用自身的编程技巧   }   else{    System.out.println(files[i].toString()+":  删除成功!");//删除连带输出文件内容    files[i].delete(); //这个是删除的文件夹中的文件   }  }  System.out.println(dir+"::dir::"+dir.delete());//这个是将整个文件夹删除 } public static void main(String[] args) {  // TODO Auto-generated method stub  File f=new File("E:\\test");  delete(f); }}


4、File获取文件信息:
所谓获取,就是将文件的属性获取,然后进行相应的操作。获取file文件类创建的对象的方法如下:
getName();  //获取文件名称
getPath();   //获取抽象路径名的字符串形式
getParent();  //获取文件的父目录
getAbsolutePath();  //获取文件的绝对路径
long lastModified();  //获取文件最后一次
long length();  //获取文件的大小长度

5、File判断文件内容:
boolean exists():文件是否存在。
boolean isFile():判断是否是文件。
boolean isDirectory():判断是否是一个目录。
boolean isHidden():判断文件是否是隐藏的。
boolean isAbsolutePath():判断文件的绝对路径。

十二、配置文件
1、小知识:
Properties是一个子类,是hashtable集合的子类,也就是说,它具备map集合的特点,而且它里面存储的键值对都是“字符串”!是“集合框架”中和io技术相结合的集合容器。该对象的特点是:可以用于“键值对”形势的配置文件。那么在加载数据的时候,需要对数据有固定的格式:键=值。

2、配置文件:
(1)、java常用的配置文件有xml和property,学过xml文件知道里面都是一些标记,property里面都是些键值对,就是“什么等于什么”。
(2)、从文件结构来说:xml文件会更加强大一些,因为里面可以存放比较复杂的内容,property里面只能存放的是一个一个的键值对,存放的内容是相当的有限的!
(3)、自己先要知道的是:现在先学会property配置文件的协作方式,也就是.ini后缀的配置文件,里面放满了键值对。
(4)、因为Properties类是Map类的子类,所以也是一个集合,所以往后,只要用到向“配置文件”中写入数据的程序,不用考虑,就用Properties类创建集合对象!!!!因为Properties类里面有很多比较“牛掰”的方法 ,可以参考使用!!

3、著名配置文件dom4j:
dom4j是一个java的XML API文档,类似于jdom,用来读取XML文档的。dom4j是一个非常优秀的java Xml  API文档,具有性能优异,功能强大,极端易用性的特点,同时是一款开放源代码的软件,可以在SourceForge上找到他。dom4j是相当的出色的软件,你可以看到现今越来越多的java软件都在使用dom4j来读写XML配置文件,特别值得一提的是连Sun公司的JAXM也在使用dom4j。所以dom4j成为了一款越来越受欢迎的jar包,也是必不可少的一个配置XML配置文件的jar包,Hibernate用它来读取配置文件。

十三、对象序列化
1、概述:
刚开始的流对象中“流动”的都是一般的数据流,“对象的序列化”是将对象添加到“流”中,然后进行相应的操作。

2、格式:
ObjectInputSteam(对象的读入流)
ObjectOutputStream(对象的输出流)
上面这两个类创建的对象可以将“流”作为参数传进来,方法中有readObject()、writeObject()

3、注意的几件事情:
(1)、序列中一个比较重要的概念是UID(序列号),UID(序列号)不是随机产生的,而是根据程序自身的内容进行判定。
(2)、抛出异常的时候如果出现错误,记得将异常的范围变大,这样出现异常的情况就会减少(实际上是废话)。
(3)、UID序列号,如果关联的几个程序之间编译时间不相同的话,或者是出现使用private修饰的话,可以自己手动指定一个UID序列号,具体格式可以参考API文档,下面列举一个格式自己参考:public static final long serialVersionUID=42L;(可以将前面的public省去)
(4)、静态是不能被序列化的!因为静态是在方法区中,但是非静态的是在堆内存中!
(5)、最重要的一点!自己定义的需要添加到“流”中的对象,需要进行序列化,这个时候就应该使用implemens执行Serializable“序列化”接口!这是必须做的一步!

 

4、具体小程式:

class People implements Serializable{ //执行Serializable接口 String name; int age; People(String name,int age){  this.name=name;  this.age=age; } public String toString(){ //转换成String类型  return name+":"+age; }}public class ObjectInputStreamDemo { public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {  writeObj();  readObj(); } public static void readObj() throws FileNotFoundException, IOException, ClassNotFoundException{  ObjectInputStream ois=new ObjectInputStream(new FileInputStream("d:\\我好.txt"));  People p1=(People)ois.readObject();  People p2=(People)ois.readObject();  System.out.println(p1);  System.out.println(p2); } public static void writeObj() throws FileNotFoundException, IOException{  ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("d:\\我好.txt"));  oos.writeObject(new People("妈妈",22)); //添加多少对象,输出就会多少对象  oos.writeObject(new People("东东",22));  oos.close(); }}


 

 

5、小总结:
说白了,这两个类创建的对象也就是“流对象”,但是就是因为里面有两个可以操作“对象”的方法writeObject()和readObject()方法让这两个类看着不是太寻常,因为这两个类中的方法可以让“对象作为参数”。

十四、管道流
1、概述:
“管道流”包含“管道字节流”和“管道字符流”,“管道字节流”中包含两个作用类“PipedInputStream”和“PipedOutputStream”,“管道字符流”中包含两个作用类“PipedReader”和“PipedWriter”。配对的“管道流”中的两个类,分别管理“读取”和“写入”。这两个“流”进行关联的时候,需要使用一个方法,connect()方法。比方说PipedInputStream创建的对象in想要和PipedOutputStream创建的对象out进行关联的话,需要使用的是:in.connect(out);进行关联。也就是说“管道流”将“读取”和“写入”的操作封装到了一个过程中,考虑到读取写入的同步性,而使用线程,“读取”和“写入”需要相互进行阻塞等待,如果没有“写入操作”就没有“读取操作”,就是说没有原因,就没有结果。相互进行等待。话说这两个类具有相当高的能力了,可以直接将两个不太相干的流对象进行关联,使之产生关系!

 

2、管道字节流示例:
作用类是PipedInputStream和PipedOutputStream(作用字节流)

class Read implements Runnable{ //如果是”线程类“,那么常用的是执行Runnable接口复写run方法。 private PipedInputStream in; Read(PipedInputStream in){  this.in=in; } public void run() {  try{   byte[] buf=new byte[1024];   int len=in.read(buf);   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{   out.write("你好,很高兴认识你".getBytes()); //向管道中写入一字符串,然后转换成了byte字节类型  }catch(IOException e){   throw new RuntimeException("管道输出流失败");  } }}public class PipedStreamDemo { public static void main(String[] args) throws IOException {  PipedInputStream in=new PipedInputStream(); //创建管道读取和写入流  PipedOutputStream out=new PipedOutputStream();  out.connect(in);  //管道流的读取和写入进行了关联connect  Read read=new Read(in);  Write write=new Write(out);  Thread thread01=new Thread(read); //将方法加载到线程中  Thread thread02=new Thread(write);  thread01.start(); //线程启动  thread02.start(); }}


 

 

 

3、管道字符流示例:
作用类是:PipedReader和PipedWriter(作用字符流)

class Writer implements Runnable{ private PipedWriter out; Writer(PipedWriter out){  this.out=out; } public void run() {  try{   out.write("你好,很高兴认识你");  }  catch(IOException e){   throw new RuntimeException("管道输出流失败");  } }}class Reader implements Runnable{ private PipedReader in; Reader(PipedReader in){  this.in=in; } public void run() {  try{   char[] length=new char[1024];   int len=in.read(length);   String s=new String(length,0,len);   System.out.println(s);   in.close();  }  catch(IOException e){   throw new RuntimeException("管道读取流失败");  } } }public class PipedStreamDemo1 { public static void main(String[] args) throws IOException {  PipedReader in=new PipedReader();  PipedWriter out=new PipedWriter();  out.connect(in);  Writer w=new Writer(out);  Reader r=new Reader(in);  Thread thread1=new Thread(w);  Thread thread2=new Thread(r);  thread1.start();  thread2.start(); }}


 

 

4、”管道字节流“和”管道字符流“的区别:
可以看到,上面的两个小程式基本上一样,只不过在读取的时候”管道字节流“使用的是”固定长度的字节数组“。”管道字符流“使用的时”固定长度的字符数组“!!!剩下都是一样的。

十五、随机读取流文件RandomAccessFile
1、概述:
RandomAccessFile“随机读取流文件”。根据写作格式可以看出:该类不能算是IO体系中的子类,因为它直接继承的是Object类,所以“比较特殊”。但它又是IO包中的成员,因为它同时具备“读”和“写”的操作功能(这一点相当的重要!)内部封装了一个”数组“,而且可以通过指针对数组的元素进行操作。可以通过getFilePointer获取指针的位置。同时可以通过seek改变指针的位置。其实完成读写的原理就是内部封装了字节输入流和输出流。通过构造函数可以看出,该类只能操作文件(不足之处)而且操作文件还有模式:只读:r     读写:rw。
(1)、如果模式为只读 r,RandomAccessFile类创建对象的时候,不会创建“文件”。会去读取一个已经存的文件,如果该文件不存在,会出现异常。
(2)、如果模式为rw,RandomFile类创建对象的时候,如果操作的文件不存在,那么就会创建文件,并且文件如果存在的话,也不会覆盖。

2、RandomAccessFile常用方法:
seek()方法,可以调整对象中指针的位置。
skipBytes(int m)方法,跳过指定大小的字节数。

3、作用:
RandomAccessFile类创建的流对象十分的常用!自己知道“迅雷下载”的时候,就是用的多线程下载文件的,这个类可以实现多线程的流对象,所以十分的常用。可以模拟“迅雷下载”这个过程。

 

4、小示例:

public class RandomAccessFileDemo { //这个方法是一个写操作,向文件”我是好人.txt“文件中写入一些String类型的name,和int类型的数字,转换成文本的时候会被翻译成字母或者各种符号。 public static void writeFile() throws IOException{  RandomAccessFile raf=new RandomAccessFile("d:\\我是好人.txt","rw");  raf.write("马济亨".getBytes()); //想要写入String类型内容,转换成byte类型  raf.writeInt(97); //将int类型转换成字符类型  raf.write("马济东".getBytes());  raf.writeInt(67);  raf.close(); } //这个方法是一个读操作,读取文件”我是好人.txt“中的属性为String的name和以int类型的形式读取age,然后再控制台输出。 public static void readFile() throws IOException{  RandomAccessFile raf=new RandomAccessFile("d:\\我是好人.txt","r");  byte[] buf=new byte[4];  raf.read(buf);  long n=raf.getFilePointer();  String name=new String(buf);  int age=raf.readInt();  System.out.println("name"+name);  System.out.println("age"+age);  System.out.println(n);  raf.close(); } public static void main(String[] args) throws IOException {  readFile(); }}


 

 

十六、基本数据类型流DataStream
1、概述:
上面的DataStream是自己编造出来的,实际上是没有的,意在用来概括其中的两个基本数据类型流对象DataInputStream和DataOutputStream。自己知道,Java中有很多的基本数据类型,比方说int、double、long、boolean、float之类的基本数据类型(所以这个类的使用频率还是挺高的)java定义了一个操作这些基本数据类型的“流对象”类创建的对象可以进行操作。

2、创建方法:
DataInputStream in=new DataInputStream (new FileInputStream("nihao.txt"));
DataOutputStream out=new DataOutputStream(new FileOutputStream("nihao.txt"));

 

3、小程式演示:

public class DataStreamDemo { public static void main(String[] args){  DataInputStream in = null;  try {   in = new DataInputStream(new FileInputStream("d:\\学生信息.txt")); //使用“文件字节流”读取文件  } catch (FileNotFoundException e) {   e.printStackTrace();  }  DataOutputStream out = null;  try {   out = new DataOutputStream(new FileOutputStream("d:\\学生信息2.txt")); //使用“文件字节流”写入文件中  } catch (FileNotFoundException e) {   e.printStackTrace();  }   int len=0;  try {   while((len=in.read())!=-1){    out.write(len);   }  } catch (IOException e) {   e.printStackTrace();  } }}


 

 

4、总结:
DataInputStream和DataOutputStream类中还封装了很多的方法,自己参看api文档可以详细了解其中的乐趣,自己一定要学会查看api文档,因为这是一个从“java编码员”迈向“java程序员”的重要的一步!!!

十七、字节数组流ByteArrayInputStream&ByteArrayOutputStream
1、概述:
见名知意,这是一个操作字节数组的类。自己知道,先开始学习的各种流对象里面都是封装了字节数组的,为什么要单独进行讲解呢?这个类有点儿特殊,类创建的对象操作的内容不是文件,不是键盘,而是内存中的“字节数组”!

2、注意:
因为这两个流对象都操作的是字节数组,因为没有使用”系统资源“,所以不用进行close关闭操作的。(只有使用了系统资源才必须要关闭资源的!)

3、”流“操作的规律:
(1)、源设备:键盘System.in  硬盘FileStream  内存ArrayStream
(2)、目的设备:控制台System.out  硬盘FileStream  内存ArrayStream

 

4、小程式演示:

public class ByteArrayStreamDemo { public static void f(){  String s="我是一个好孩子";   ByteArrayInputStream bis=new ByteArrayInputStream(s.getBytes());  ByteArrayOutputStream bos=new ByteArrayOutputStream();  int len=0;  while((len=bis.read())!=-1){   bos.write(len); //这里的write操作是将len内容写入到了系统内存当中   String message=bos.toString();   System.out.println(message);  }  int size=bos.size(); //返回字节数组的size大小  System.out.println(size); } public static void main(String[] args) {  f(); }}


 

 

5、小扩展:
(1)、既然有字节数组,那么相应的就有“字符数组”了 ,CharArrayReader    CharArrayWriter类分别创建的是字符数组的“读取"和"写入流"。
(2)、既然有字符数组,那么相应的就有”字符串“数组了,StringReader   StringWriter类分别负责创建的是字符串的”读取“和”写入流“。
上面两个类的使用方法和ByteArrayInputStream和ByteArrayOutputStream的使用方法基本一致!!!后面就不一一介绍。

6、注意:
其中的ByteArrayOutputStream操作中的write是将内容写入到系统内存当中,不是一个硬盘上的什么文件当中!

 

1 0
原创粉丝点击