Java基础:输入输出流

来源:互联网 发布:下载中国移动crm软件 编辑:程序博客网 时间:2024/06/16 13:55

1.输入输出流的基本概念

(1).用来处理设备之间的数据传输:包括有内存的数据,硬盘上的文件,以及网络中的数据等;
(2).Java对数据的操作时通过流的方式;
(3).Java中用于操作流的对象都在java.io包中;
(4).流按照操作的数据分类:字节流:包括有图片,视频,音频等;字符流:包括的主要是文本文件;
(5).按照流的流向分类:输入流:读;输出流:写;

2.IO流体系

Java.io:
|--Reader:字符读取流
|--InputStreamReader:转换流
|--FileReader:文件字符流
|--BufferedReader:带缓冲的加强的字符读取流
|--LineNumberReader:带行标的字符读取流
|--PipedReader:管道字符读取流
|--Writer:字符写入流
|--OutputStreamWriter:转换流
|--FileWriter:文件字符流
|--BufferedWriter:带缓冲的加强的字符写入流
|--PrintWriter:打印流
|--PipedWriter:管道字符写入流
|--InputStream:字节输入流,读取流
|--FileInputStream:文件字节流
|--FilterInputStream
|--BufferedInputStream:带缓冲的加强的字节输入流
|--ObjectInputStream:操作对象的输入流
|--SequenceInputStream:合并流
|--PipedInputStream:管道字节输入流
|--OutputStream:字节输出流,写入流
|--FileOutputStream:文件字节流
|--FilterOutputStream
|--BufferedOutputStream:带缓冲的加强的字节输出流
|--PrintStream:打印字节流
|--ObjectOutputStream:操作对象的字节输出流
|--PipedOutputStream:管道字节输出流、
IO流中的基类就是InputStream,OutputStream,Reader,Writer,由这些基类派生出来的子类名称都是以其父类名作为子类名的后缀;
在IO流中,类的前缀往往与该流的功能有关;

字符流的底层其实也是以字节为单位来读写数据;


3.字符流

3.1 Reader

(1).Reader中的读取方式:

int read():读取单个字符,返回的是字符的Unicode,如果到达流的末尾则返回-1;阻塞式方法;throws IOException;

int read(char[ ] buf):读取字符到字符数组,返回的是读取的字符数,如果已经到达流的末尾返回-1;阻塞式方法;throws IOException;

abstract void close():抽象的关闭资源方法,具体的实现有子类去实现;

上述方法都有可能会出现IOException;

(2).用来读文件(主要是文本文件)的Reader:FileReader

对构造方法的说明:因为FileReader是用来操作文件的,所以在一初始化时应该关联要操作的文件;

FileReader(File file):参数为文件类型对象;

FileReader(String fileName):参数为文件名;

由于可能关联的文件不存在或者由于什么原因没有关联到,所以该构造方法会抛出:FileNotFoundException;

(3).使用FileReader按照两种方式将指定文件的内容在控制台输出;

/* * 使用FileReader读取指定文件 */package com.zr.day20;import java.io.*;class FileReaderDemo{public static void main(String[] args) throws IOException{myRead_1();myRead_2();}//其中的一些方法在调用时可能会发生异常,所以需要抛异常public static void myRead_1() throws IOException{//FileReader是用来操作文件的读取流,所以在其初始化时要绑定文件;//在构造方法中可以接受字符型的文件名也可以是封装好的文件类型对象FileReader fr = new FileReader("demo.txt");//读取方式一:read();每次读取一个字符;int ch;while((ch=fr.read())!=-1){System.out.print((char)ch);}//关闭资源fr.close();}//带异常处理public static void myRead_2(){FileReader fr = null;try {fr = new FileReader("demo.txt");//第二种方式,通过字符数组进行存储char[] buf = new char[1024];for(int len;(len = fr.read(buf))!=-1;){System.out.print(new String(buf,0,len));}}catch(IOException e){throw new RuntimeException("读取文件失败");}finally{//此处判断,避免空指针异常;if(fr!=null)try{fr.close();}catch(Exception e){throw new RuntimeException("关闭资源失败");}}}}


3.2 Writer

(1).Writer中的特有方法:

void write(char[ ] buf):写入字符数组;throws IOException ;

void write(char[ ] buf, int off, int len):写入字符的指定部分;throws IOException ;

void write(int c):写入单个字符;写入的字符包含在给定int值的低16位,高16位被忽略;throws IOException ;

void write(String s):写入字符串;throws IOException ;

abstract void flush():刷新该流的缓冲,刷新到目的文件中,这是Writer体系中的特有方法;

abstract void close():关闭资源操作,关闭之前会刷新流;

上述方法都有可能会出现IOException;

(2).用来写文件的Writer:FileWriter

对构造方法的说明:用来操作文件,所以流对象在初始化时就应该关联具体的文件,也就是明确数据要存放的目的地;

当文件不存在时,会在指定的目录下创建文件,如果文件已经存在可以根据需要设定是否覆盖或者是否在文件末尾继续写入;

IOException:指定文件存在可能是个目录,或者无法创建,或者无法打开;

FileWriter(File file)

FileWriter(File file, boolean append)

FileWriter(String fileName):默认是覆盖已有文件的内容;

FileWriter(String fileName, boolean append):当append字段为true时,会在文件末尾续写;

(3).将某些内容写入到硬盘上的目的文件;

/* * 文件的写操作*/package com.zr.day20;import java.io.*;class FileWriterDemo{public static void main(String[] args) throws IOException{String str = "在屋顶唱着你的歌";myWriter(str);}//文件的覆盖写操作public static void myWriter(String str){FileWriter fw = null;char[] chs = str.toCharArray();try{//如果文件不存在会创建文件,然后写入;//如果文件已经存在会覆盖其中的内容;fw = new FileWriter("demo.txt");fw.write(chs);//刷新fw.flush();}catch(IOException e){throw new RuntimeException("写入操作失败");}finally{if(fw!=null)try{fw.close();}catch(Exception e){throw new RuntimeException("关闭资源失败");}}}//文件的续写操作public static void myContinueWriter(String str) throws IOException{//关联某个目的文件,append字段设置为true,表示在文件末尾续写;FileWriter fw = new FileWriter("demo.txt",true);fw.write(str);//刷新操作fw.flush();//关闭资源,在关闭之前会刷新fw.close();}}


3.3 IO中的异常处理操作

通过复制文本文件来说明;
/* * 复制文本文件; * */package com.zr.day20;import java.io.*;class FileCopyDemo{public static void main(String[] args){//定义输入流,用来读取源文件;FileReader fr = null;//定义输出流,用来写入目的文件;FileWriter fw = null;try{fr = new FileReader("demo.txt");fw = new FileWriter("copydemo.txt");//也可以读一个写一个;/*for(int ch; (ch=fr.read())!=-1;){fw.write(ch);fw.flush();} *///定义字符数组;char[] buf = new char[1024];for(int len; (len=fr.read(buf))!=-1;){fw.write(buf,0,len);fw.flush();}}catch(IOException e){throw new RuntimeException("复制文件失败");}finally//对于资源的关闭要分别处理;{if(fr!=null)try{fr.close();}catch(Exception e){throw new RuntimeException("关闭资源失败");}if(fw!=null)try{fw.close();}catch(Exception e){throw new RuntimeException("关闭资源失败");}}}}




3.4 字符流缓冲区:BufferedReader

BufferedReader的出现是为了提高字符流的读取效率;

(1).构造方法:有参的构造方法,需要提高字符读取流的效率,在初始化的时候就必须要关联某个读取流Reader;

BufferedReader(Reader in):创建一个使用默认大小的缓冲区来缓冲字符输入流,关联某个读输入流;

(2).BufferedReader的缓冲技术原理是使用数组缓冲;

(3).特有的读取方法:String readLine(),读取一个文本行,不包含任何行终止符,如果已经到达流的末尾,返回null;

原理是遇到换行或者回车标记,不论是读取一行还是读取多个字符,其实都是一个字符一个字符的读取,底层使用的还是Reader中的read()方法;

由此可以看出装饰类的设计原理:利用原有的方法功能,提供更加强大的方法;

(4).使用字符缓冲区复制文件;

/* * 使用字符流缓冲区复制文件 * */package com.zr.day20;import java.io.*;class BufferedCopyDemo{public static void main(String[] args){//定义带字符缓冲区的输入流BufferedReader br = null;//定义带字符缓冲区的输出流BufferedWriter bw = null;try{//字符流缓冲区要关联一个输入流对象,输入流对象要关联源文件br = new BufferedReader(new FileReader("demo.txt"));//字符缓冲区关联一个输出流对象,输出流关联目的文件bw = new BufferedWriter(new FileWriter("bufferdemo.txt"));//BufferedReader中特有的读取方式:一次读取一行for(String line=null;(line = br.readLine())!=null;){bw.write(line);//BuffedWriter中特有的写方式,写一个行分隔符,相当于回车;bw.newLine();bw.flush();}}catch(IOException e){throw new RuntimeException("复制文件失败");}finally{if(br!=null)try{//关闭该流,它所关联的流也关闭;不需要在单独的去关闭流;br.close();}catch(Exception e){throw new RuntimeException("关闭资源失败");}if(bw!=null)try{bw.close();}catch(Exception e){throw new RuntimeException("关闭资源失败");}}}}


(5).根据readLine()的实现原理,自定义BufferedReader装饰类;

/* * 自定义BufferedReader装饰类 * */package com.zr.day20;import java.io.*;class MyBufferedDemo{public static void main(String[] args) throws IOException{MyBufferedReader mbr = new MyBufferedReader(new FileReader("demo.txt"));BufferedWriter bw = new BufferedWriter(new FileWriter("myCopy.txt"));for(String line = null; (line=mbr.myReadLine())!=null;){bw.write(line);bw.newLine();bw.flush();}mbr.close();bw.close();}}//自定义Reader的装饰类class MyBufferedReader extends Reader{//在初始化时关联输入流;private Reader in;MyBufferedReader(Reader in){this.in = in;}//实现读取一行的方法;public String myReadLine() throws IOException{//StringBuilder,也可以使用数组;StringBuilder sb = new StringBuilder();for(int ch=0; (ch=in.read())!=-1;){//window系统中的换行是有两个字符表示的;//读取到第一个字符,继续遍历该行;if(ch=='\r')continue;//读取到结束符,就返回该行;else if(ch=='\n')return sb.toString();//读取的是字符elsesb.append((char)ch);}//当读到最后一行时,可能没有换行的标记,最后一行就没有输出,所以在这里判断字符串缓冲区里面是否有内容;if(sb.length()!=0)return sb.toString();return null;}public int read(char[] cbuf, int off, int len) throws IOException {return in.read(cbuf, off, len);}public void close() throws IOException {in.close();}}


(6).带行号的装饰字符缓冲区:LineNumberReader

其继承自BufferedReader,所以构造方法同样要关联某个输入流;

其特有方法:

1).int  getLineNumber():获得当前行号;

2).void setLIneNumber(int number):设置当前行号;

可以在读取的时候带上行号读取,还可以设置行号读取的初始值;

自定义LineNumberReader:

package com.zr.day20;import java.io.*;class MyLineNumber{public static void main(String[] args) throws IOException {MyLineNumberReader mylr = new MyLineNumberReader(new FileReader("demo.txt"));mylr.setLinenumber(100);for(String line = null; (line = mylr.myReadLine())!=null;){System.out.println(mylr.getLinenumber()+"**"+line);}mylr.close();}}class MyLineNumberReader extends BufferedReader{private Reader in;//定义行标private int linenumber;MyLineNumberReader(Reader in) {//因为父类中没有空参的构造方法,所以必须要显示初始化调用父类的构造方法;super(in);this.in = in;}public String myReadLine() throws IOException{linenumber++;StringBuilder sb = new StringBuilder();for(int ch = 0;(ch=in.read())!=-1;){if(ch=='\r')continue;else if(ch=='\n')return sb.toString();elsesb.append((char)ch);}if(sb.length()!=0)return sb.toString();return null;}public int getLinenumber() {return linenumber;}public void setLinenumber(int linenumber) {this.linenumber = linenumber;}}


3.5 字符流缓冲区:BufferedWriter

BufferedWriter的出现时为了提高字符流的写入效率;

(1).构造方法:有参的构造方法,需要提高字符的写入效率,在初始化的时候就必须关联某个写入流Writer;

BufferedWriter(Writer out):关联某个写输出流;

(2).BufferedWriter中的特有方法:void newLine(),写入一个行分隔符,可以跨平台;

3.6 打印流:PrintWriter

(1).构造方法:

PrintWriter(File file):通过文件对象指定写入的目的文件

PrintWriter(String fileName):通过文件名指定写入的目的文件

PrintWriter(OutputStream out):使用现有的OutputStream创建该对象,不带自动刷新;

PrintWriter(OutputStream out, boolean autoFlush):带自动刷新;

PrintWriter(Writer out):使用现有的Writer创建该对象,不带自动刷新;

PrintWriter(Writer out, boolean autoFlush):带自动刷新;

(2).因为同属于Writer体系,所以具备Writer具有的写入方法;

(3).提供了相当丰富的打印输出方法:

print:打印;

println:打印换行;

printf:按照C中格式打印;

当需要用到PrintWriter中丰富的打印方法时,可以将输出流封装到PrintWriter对象中;

4.字节流

4.1 InputStream

(1).字节输入流中的方法

int available():返回可以不受阻塞的从此输入流读取的字节数;如果已经到达末尾返回0;

由此方法可以定义InputStream中特有的读取方式;

void close():关闭该流;

int read():返回下一个数据字节,范围在0 - 255;如果到达流的末尾则返回-1;

int read(byte[] b):将数据读入到指定的字节数组中;返回的是该缓冲区中的总字节数,如果已经到达流末尾则返回-1;

(2).用来操作字节流文件的输入流:FileInputStream

其构造方法必须在初始化的时候关联某个流文件:

FileInputStream(String fileName)

FIleInputStream(File file)

可能会抛出的异常:FileNotFoundException,SecurityException;

4.2 OutputStream

(1).字节输出流中的方法

该流中的方法同字节流中的方法一样,相较于Reader来说,其输出的过程不需要刷新;

OutputStream中的flush方法不执行任何操作,只是实现Flushable接口需要定义一个空的方法;

void write(int b):将指定的字节写入此输入流,写入的是int参数的低八位,高24位被忽略;

(2).用来操作字节流文件的输出流:FileOutputStream

其构造方法必须在初始化时关联某个目的文件,同时在构造时可以指定是否覆盖已存在的文件内容;

FileOutputStream(String fileName)

FileOutputStream(String fileName, boolean append)

FileOutputStream(File file)

FileOutputStream(File file, boolean append)

示例:使用字节流完成复制文件操作

/* * 复制图片文件; * */package com.zr.day20;import java.io.*;class FileCopyStream{public static void main(String[] args){FileInputStream is = null;FileOutputStream os = null;try{is = new FileInputStream("demo.jpg");os = new FileOutputStream("copy2.jpg");//方式一:读取一个字节写入一个字节for(int ch;(ch=is.read())!=-1;){//不需要刷新os.write(ch);}//方式二:读取后先存放在字节数组,然后在将字节数组写入//最优方式;byte[] buf = new byte[1024];for(int len;(len=is.read(buf))!=-1;){os.write(buf,0,len);}//方式三:由于在字节流中有提供一个获取大约字符数的方法,可以通过该方法设定大小刚刚好的数组;//不需要循环//这种方式在文件很大的时候不适用;byte[] bf = new byte[is.available()];int len = is.read(bf);os.write(bf);}catch(Exception e){throw new RuntimeException("复制失败");}finally{try{if(is!=null)is.close();}catch(Exception e){throw new RuntimeException("关闭资源失败");}try{if(os!=null)os.close();}catch(Exception e){throw new RuntimeException("关闭资源失败");}}}}


4.3 字节流缓冲区:BufferedInputStrem

构造方法:关联某个字节输入流:BufferedInputStream(InputStream in)

4.4 字节流缓冲区:BufferedOutputStream

(1).构造方法:关联某个字节输出流:BufferedOutputStream(OutputStream out)

(2).在该装饰类中的flush()具有实际效果,可以进行刷新;

示例:拷贝MP3操作

/* * 复制MP3 * 步骤: * 1.定义字节输入流和输出流,然后与文件相关联; * 2.使用装饰类对输入输出流进行装饰; * 3.进行读写操作; * 4.关闭资源; */package com.zr.day20;import java.io.*;class CopyMp3{public static void main(String[] args){BufferedInputStream bis = null;BufferedOutputStream bos = null;try{//输入流关联源文件,然后用字节缓冲区装饰bis = new BufferedInputStream(new FileInputStream("demo.mp3"));//输出流关联目的文件,然后用字节缓冲区装饰bos = new BufferedOutputStream(new FileOutputStream("copy.mp3"));byte[] buf = new byte[1024];for(int len;(len=bis.read(buf))!=-1;){bos.write(buf,0,len);//刷新操作可有可无bos.flush();}}catch(Exception e){throw new RuntimeException("复制文件失败");}finally{try{if(bis!= null)bis.close();}catch(Exception e){throw new RuntimeException("关闭资源失败");}try{if(bos!= null)bos.close();}catch(Exception e){throw new RuntimeException("关闭资源失败");}}}}


示例:自定义字节流缓冲区

/*自定义BufferedInputStream,实现其中的myRead()方法;思路:1.首先关联一个字节输入流,对其装饰;2.定义缓冲区数组,定义指针和计数器;3.操作数据;4.关闭资源;*/package com.zr.day20;import java.io.*;class MyBufferedInputStream{//关联某个输入流对象private InputStream in;MyBufferedInputStream(InputStream in){this.in = in;}//定义读取操作,返回的是字节,每次从缓冲区返回一个字节private int count = 0;//定义数组指针private int pos;//定义一个数组缓冲区;byte[] buf = new byte[4*1024];public int myRead() throws IOException{//每次调用该方法都要判断count;//数组中没有字节数时;if(count==0){//先调用in对象的read方法count = in.read(buf);//已经到达数据的末尾if(count==-1)return -1;//还有数据返回else{pos = 0;byte b = buf[pos++];count--;//直接返回byte类型会出现类型提升,提升为int型,字节数增加,且高位全部补1,原字节数据改变;//读取的byte可能刚好是1111-1111,提升为int型后返回的刚好是-1,导致循环结束;return b & 0xff;}}//字节数组中还有字节可以返回;else if(count>0){pos = 0;byte b = buf[pos++];count--;return b & 0xff;}return -1;}//关闭资源public void myClose() throws IOException {in.close();}}


示例:读取键盘录入

/* * 读取键盘录入; * 需求:要求从键盘录入数据信息,回车后将输入信息的大写数据在控制台中返回; * */package com.zr.day20;import java.io.*;class SystemInDemo{public static void main(String[] args)throws IOException{//键盘录入是System.in,它是InputStream类型,字节输入流//分析键盘录入操作发现需要使用到字符流缓冲区中的读取一行操作,所有实用转化流BufferedReader br = new BufferedReader(new InputStreamReader(System.in));BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));while(true){String line = br.readLine();if("over".equals(line))break;bw.write(line.toUpperCase());bw.newLine();bw.flush();}br.close();bw.close();}}


4.5 打印流:PrintStream

该流从属于字节输出流体系,可以对字符串和文件对象进行目的文件的封装;

同时还可以对字节输出流进行封装,然后使用该类中丰富的打印功能,其功能和PrintWirter类一致;

5.转换流

转换流除了用于对字符字节进行转换外,还主要在涉及编码的指定中频繁使用;

5.1 InputStreamReader

InputStreamReader是字节流通向字符流的桥梁;

InputStreamReader(InputStream in):转换字节流

InputStreamReader(InputStream in, String charsetName):指定编码集

5.2 OutputStreamWriter

OutputStreamWriter是字符流通向字节流的桥梁;

OutputStreamWriter(OutputStream out)

OutputStreamWriter(OutputStream out, String charsetName):指定编码集

6.流操作规律

(1).明确体系:
数据源:InputStream,Reader
目的:OutputStream,Writer
(2).明确数据:是否是纯文本
源:是:Reader;否:InputStream;
目的:是:Writer;否:OutputStream;

(3).明确设备

源:键盘,硬盘,内存,网络;

目的:控制台,硬盘,内存,网络;

(4).明确额外功能:

是否需要转换,或者指定字符集:InputStreamReader,OutputStreamWriter;

是否需要高效:Buffered

其他功能:比如打印的方法:PrintWriter,PrintStream

修改标准的输入输出:

System.setOut(OutputStream out);

System.setIn(InputStream in);

生成异常日志文件:可以将异常信息在硬盘上进行保存

catch(Exception e)

{

e.printStackTrace(new PrintStream("指定的日志文件"));

}

示例:

package com.zr.day20;import java.io.*;  import java.text.*;  import java.util.*;  class  ExceptionDemo  {      public static void main(String[] args)       {          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("exception.log");                  //修改输出流设备                 System.setOut(ps);                 //输出时间                ps.println(s);                                }              //文件在创建的过程中也有可能出现异常            catch (IOException ex)              {                  throw new RuntimeException("异常日志文件创建失败");              }              //将异常信息输出指定输出流              e.printStackTrace(System.out);          }      }  } 


打印系统属性:将系统属性信息输出到硬盘文件

Properties prop = System.getProperties();

prop.list(PrintWriter out)://关联一个输出流,可以指定到硬盘的文件;

示例:

/* * 将系统属性信息输出到文本文件中; * */package com.zr.day20;import java.io.*;import java.util.*;class SystemInfoDemo{public static void main(String[] args)throws IOException{Properties prop = System.getProperties();prop.list(new PrintStream("sysinfo.txt"));}}


7.File类

7.1 File类中常用的方法
(1).创建
boolean creatNewFile():创建成功返回true,如果指定文件或文件夹已经存在,则返回false;
boolean mkdir():创建此抽象路径名指定的目录,只能是一级;
boolean mkdirs():创建多级目录;
(2).删除
boolean delete():如果该路径名是目录则只有当目录为空时才能删除;
void deleteOnExit():在虚拟机退出时,删除指定的文件或目录;
(3).判断
boolean canExecute():是否是可执行;
boolean canRead():可读;
boolean canWrite():可写;
boolean exists():是否存在;
boolean isAbsolute():是否为绝对路径;
boolean isDirectory():是否是目录;
boolean isFile():是否是文件;
boolean isHidden():是否是隐藏文件;
int compareTo(File pathname):比较,按字母顺序比较两个抽象路径名;
(4).获取
File getAbsoluteFile()
String getAbsolutePath():总是返回绝对路径;
String getName()
String getPath():封装的什么路径就返回什么路径
String getParent():返回的是绝对路径中的文件父目录,如果获取的是相对路径,则返回null;如果相对路径中有上一层目录则返回该目录;
String[ ] list():用数组存储指定目录下的所有目录和文件
String[ ] list(FIlenameFilter filter) :过滤
Public interface FilenameFilter:文件名过滤器
在该接口中只定义了一个方法,boolean accept(File dir, String name),当需要使用时可以使用匿名内部类;
示例:
/* * 文件过滤:列出某个目录下后缀名为.java的文件; * FilenameFilter是一个文件名过滤器,使用匿名内部类传递一个过滤器对象; */package com.zr.day20;import java.io.*;class FileFilterDemo{public static void main(String[] args){File file = new File("D:\\workspace\\heima\\src\\com\\zr\\day20");String[] strArr = file.list(new FilenameFilter(){@Overridepublic boolean accept(File dir, String name) {return name.endsWith(".txt");}});for(String str : strArr){System.out.println(str);}}}


File[ ] listFiles()
File[ ] listFiles(FilenameFilter filter)
static File[ ] listRoots():返回有效盘符;
File getParentFile()
long length():获取文件的大小;
boolean renameTo(File f):可以实现剪切功能,同时rename;
(5).构造方法
File(File parent, String child):
File(String pathname)
File(String parent, String child)
7.2 File对象的应用:递归
(1).列出文件列表,可以打印,可以输出到硬盘文件;
/* * 需求:列出指定目录下文件或文件夹,包含子目录,带层次; * 分析:因为目录之中还有目录,所以想到一个方法会被自己调用,使用递归; * */package com.zr.day20;import java.io.*;class PrintFileList{public static void main(String[] args)throws IOException{//注意路径中的分隔符File file = new File("F:\\Favorite");if(!file.exists())throw new RuntimeException("文件不存在");if(!file.isDirectory())throw new RuntimeException("非法的目录");fileList(file,0);}//列出指定目录下的所有文件和目录,在控制台打印输出;public static void fileList(File file,int level){System.out.println(getLevel(level++)+file.getAbsolutePath());File[] files = file.listFiles();for(File f : files){if(f.isFile())System.out.println(getLevel(level)+f.getName());else if(f.isDirectory())fileList(f,level);}}public static String getLevel(int level){StringBuilder sb = new StringBuilder();for(int x=0; x<level; x++)sb.append("\t");return sb.toString();}}

(2).将文件列表输出到指定文件中;
package com.zr.day20;/* 练习: 将一个指定目录下的java文件的绝对路径,存储到一个文本文件中。建立一个java文件列表的文件。 思路:      1、对指定的目录进行递归。      2、获取递归过程所有的java文件的路径。      3、将这些路径存储到集合中。      4、将集合中的数据写入到一个文件中。 */  import java.util.*;  import java.io.*;    class  PrintFileDemo  {      public static void main(String[] args)       {          //指定目录          File dir=new File("D:\\workspace\\heima");                    //定义一个List集合,用于存储.java文件的File对象          List<File> list =new ArrayList<File>();                    //调用获取文件路径方法          fileToList(dir,list);                    //指定写入文件          File file=new File(dir,"list.txt");          //调用写入文件方法          writeToFile(list,file);            }      //获取指定文件夹内的所有java文件的绝对路径,并存入集合中      public static void fileToList(File dir,List<File> list)      {      //列出dir路径下的所以文件和目录,        File[] files=dir.listFiles();          //遍历          for (File file : files)          {              //如果是目录,则继续获取              if(file.isDirectory())              {                  list.add(file.getAbsoluteFile());//把父目录路径也存入                  fileToList(file,list);              }              //将是.java文件的绝对路径存入              else if(file.getName().endsWith(".java"))                  list.add(file);          }      }        //将集合中元素写入到一个文本文件中      public static void writeToFile(List<File> list,File file)      {                    BufferedWriter bw=null;                        try          {   //使用字符流缓冲区对象关联写入的文件              bw=new BufferedWriter(new FileWriter(file));              for (File f : list )              {                  bw.write(f.getAbsolutePath());//写入                  bw.newLine();//换行                  bw.flush();//刷新              }          }          catch (IOException e)          {              throw new RuntimeException("写入文件失败");          }          finally          {              try              {                  if(bw!=null)                      bw.close();//关流              }              catch (IOException e)              {                  throw new RuntimeException("流资源关闭失败");              }          }      }  } 


(3).删除带有内容的目录;
package com.zr.day20;/* 删除一个带内容的目录。 删除原理: 在windows中,删除目录从里面往外面删除的。 既然是从里往外删除。就需要用到递归。 */  import java.io.*;  class DeleteDirDemo   {      public static void main(String[] args)       {          //指定目录          File dir=new File("D:\\workspace\\heima");          //删除目录          removeDir(dir);        }        //删除传入目录      public static void removeDir(File dir)      {      //列出目录下的所以文件和文件夹          File[] files=dir.listFiles();        //遍历          for (File file : files )          {              //如果还是目录且非隐藏              if(!file.isHidden()&&file.isDirectory())                  removeDir(file);//继续删除目录里的内容              else                  System.out.println(file.toString()+":-file-:"+file.delete());//删除文件          }          System.out.println(dir+":::dir:::"+dir.delete());//删除目录      }  } 


8.Properties类

Properties是Hashtable的子类,具备Map集合的特点,而且它里面存储的键值对都是字符串;
Properties是集合和IO技术相结合的集合容器,可以用于键值对形式的配置文件,键=值;
System.getProperties()方法可以获取系统的配置文件信息;
常用方法:
String getProperty(String key):使用指定的键搜索;
Set<String> stringPropertyNames():返回此属性列表中的键集;
setProperty(String key, String value):设置键值对,调用的是Hashtable的put方法
输出属性列表:
void list(PrintStream out)
void list(PrintWriter out)
加载属性列表
void load(InputStream in)
void load(Reader in)
存储属性列表
void store(OutputStream out, String comments)
void store(Writer writer, String comments)
对字节流的处理是JDK6.0才出现的;

示例:

(1).根据load方法的原理,自定义load方法,将流中的数据添加到集合中

/* * 将外部配置文件中的键值对加载到Properties集合中; * */package com.zr.day20;import java.io.*;import java.util.*;class PropertiesDemo{public static void main(String[] args)throws IOException{BufferedReader br = new BufferedReader(new FileReader("prop.txt"));Properties Prop = new Properties();for(String line = null; (line=br.readLine())!=null;){//使用切割方法,切割字符串;String[] arr = line.split("=");Prop.put(arr[0], arr[1]);}System.out.println(Prop);br.close();}}


(2).实现软件免费使用次数的设计;

/* 练习:用于记录应用程序运行次数。如果使用次数已到,那么给出注册提示。      分析: 很容易想到的是:计数器。可是该计数器定义在程序中,随着该应用程序的退出,该计数器也在内存中消失了。 所以要建立一个配置文件,用于记录该软件的使用次数。该配置文件使用键值对的形式。键值对数据是map集合。数据是以文件形式存储。使用io技术。那么map+io——>Properties。  思路:1、用读取流关联文本信息文件。如果存在则读取,如果不存在,则创建       2、每次运行,将文件数据存入集合中,读取值,判断次数,如果小于等于5次,则次数增加1次,如果大于则输出提示信息。       3、将值小于等于5次的信息数据存入文件中 */  package com.zr.day20;import java.util.*;  import java.io.*;    class  UseSoftDemo  {      public static void main(String[] args)throws IOException       {          int count=runCount();          if(count>5)//如果程序被使用了超过5次,则终止使用,并提示          {              System.out.println("使用次数已到,请注册");              return ;          }          else              System.out.println("第"+count+"次运行");      }      //获取程序运行的次数      public static int runCount()throws IOException      {      //创建集合对象        Properties ps=new Properties();          //将文件进行封装         File file=new File("info.properties");         //判断是否存在        if(!file.exists())          //如果不存在该文件,就是第一次登录,需要创建新文件            file.createNewFile();        //将文件于读取流进行关联          FileReader fr=new FileReader(file);          //加载流中的文件数据到集合中 ,调用Properties中的加载方法,传入一个输入流;        ps.load(fr);         //定义计数器        int count=0;          //获取次数值        String value=ps.getProperty("time");          //如过值不等于null,则将其赋值给count        if(value!=null)          {              count=Integer.parseInt(value);          }          //每启动一次自增        count++;         //将次数记录住集合        ps.setProperty("time",count+"");            FileWriter fw=new FileWriter(file);        //将集合中的数据存入硬盘文件中        //将修改后的配置信息,重新存储到硬盘文件中,传递一个输出流,关联文件,可以添加说明信息        ps.store(fw,"");          //关闭流资源        fr.close();          fw.close();          //返回程序启动的次数          return count;    }  } 


9.合并和切割流

SequenceInputStream:表示其他输入流的逻辑串联,从输入流的有序集合开始,并从第一个输入流开始读取,直到文件的末尾然后接着第二个输入流读取,依次类推,直到最后一个输入流的末尾为止;
构造方法:
SequenceInputStream(Enumeration e )
该方法的参数必须是枚举类型参数,可以通过集合中Vector对象中的elements()方法获取,或者通过迭代器的Enumeration获取;
SequenceInputStream(InputStream in1, InputStream in2)
切割流的具体原理是定义大小固定的缓冲区,当缓冲区装满时就将该部分文件输出,定义成单独的文件;
示例:
(1).流的合并
/* * 合并多个输入流; * 思路: * 首先通过Vector集合获取Enumeration对象 * 然后将Enumeration对象作为SequenceInputStream构造方法的参数,创建流对象; * 然后进行读写操作 * 最后关闭资源 */package com.zr.day20;import java.io.*;import java.util.*;class StreamMergeDemo{public static void main(String[] args) throws IOException {//因为SequenceInputStream的构造方法可以接收的参数是Enumeration类型对象;//所以在这里定义Vector集合,获取枚举对象;Vector vec = new Vector();vec.add(new FileInputStream("1.txt"));vec.add(new FileInputStream("2.txt"));vec.add(new FileInputStream("3.txt"));Enumeration en = vec.elements();//建立合并流对象SequenceInputStream sis = new SequenceInputStream(en);//定义输出流,关联文件FileOutputStream fos = new FileOutputStream("merg.txt");for(int ch; (ch=sis.read())!=-1;){fos.write((char)ch);}//关闭流资源;sis.close();fos.close();}}


(2).流的分割
/* *切割流:对某个文件进行指定大小的切割; *思路: *1.定义输入流对象关联文件; *2.定义固定大小的数组,当数据存满时就输出生成文件,直至文件读取完毕; *3.关闭资源; * */package com.zr.day20;import java.io.*;class StreamSpitDemo{public static void main(String[] args)throws IOException{BufferedInputStream bis = new BufferedInputStream(new FileInputStream("demo.mp3"));byte[] buf = new byte[1024*1024];int count = 1;for(int len;(len=bis.read(buf))!=-1;){FileOutputStream fos = new FileOutputStream((count++)+".part");fos.write(buf,0,len);fos.close();}bis.close();}}


10.对象的序列化

Java.io中定义了专门用来操作对象的流;
被流操作的对象必须要实现标记接口,Java.io.Serializable;
ObjectOutputStream将Java对象的基本数据类型的和图形写入OutputStream,可以使用ObjectInputStream重构对象,通过在流中使用文件可以实现对象的持久存储;
ObjectOutputStream:完成对象的序列化;writeObject(Object obj)
ObjectInputstream:完成对象的反序列化;readObject()
说明:
UID是根据对象的成员计算出来的一个序列标识,对象能否给被序列化就是通过判断UID来实现的;
 public static final long serialVersionUID = 42L;
静态不能被序列化,堆中的数据可以被序列化,当堆中的成员被transient修饰后不能被序列化;
具体操作步骤:
(1).写入流对象:

创建对象写入流,与文件相关联;

通过写入writeObject(Object obj)方法,将对象作为参数传入,写入文件;

(2).读取流对象:

创建对象读取流,与文件相关联;

通过readObject()方法,读取文件中的对象,并返回这个对象;

示例:对象序列化

/* * 实现对象的序列化,写入文件,然后反序列化,从文件中读取对象; */package com.zr.day20;import java.io.*;class ObjectStream{public static void main(String[] args)throws IOException{File file = new File("obj.txt");Student stu = new Student("Sharon",19,"gsm","TW");//对象序列化writeObj(stu,file);//对象反序列化readObj(file);}private static void writeObj(Student stu,File file) {//定义操作对象的输出流ObjectOutputStream oos = null;try{//其构造方法要封装一个输出流oos = new ObjectOutputStream(new FileOutputStream(file));oos.writeObject(stu);}catch(Exception e){throw new RuntimeException("序列化失败");}finally{try{if(oos!=null)oos.close();}catch(Exception e){throw new RuntimeException("流资源关闭失败");}}}private static void readObj(File file) {ObjectInputStream ois = null;try{ois = new ObjectInputStream(new FileInputStream(file));Student stu = (Student)ois.readObject();System.out.println(stu);}catch(Exception e){throw new RuntimeException("序列化失败");}finally{try{if(ois!=null)ois.close();}catch(Exception e){throw new RuntimeException("流资源关闭失败");}}}}//实现接口Serializable,该接口中没有定义方法,标记接口,表示对象可以被序列化;class Student implements Serializable{//还可以自定义UID,如果没有自定义系统会分配这个IDpublic final static long serialVersionUID = 32L;private String name;private int age;//被transient修饰的成员不能被序列化transient String id;//静态成员不能被序列化;static String country ="CN";public Student(String name, int age, String id, String country){this.name = name;this.age = age;this.id = id;this.country = country;}public String toString(){return name+":"+age+":"+id+":"+country;}}


11.其他对象

管道流:
PipedInputStream
PipedOutputStream 
特点:
(1).输入输出可以直接进行连接,不要在借助数组或者集合容器进行临时存储;
(2).一般结合多线程使用;
操作:
(1).创建一个读和写的两个类,实现runnable接口,复写run方法,在内部处理异常;
(2).创建两个管道流,并用connect()方法将两个流连接;
(3).创建读写对象,传入两个线程,执行;
示例:
/* *通过管道流操作数据; * */package com.zr.day20;import java.io.*;class PipedDemo{public static void main(String[] args)throws Exception{PipedInputStream pin = new PipedInputStream();PipedOutputStream pout = new PipedOutputStream();//输入流和输出流相连接pin.connect(pout);new Thread(new PipedOut(pout)).start();new Thread(new PipedIn(pin)).start();}}//定义管道写入流,向管道中写入数据class PipedOut implements Runnable{private PipedOutputStream pout;PipedOut(PipedOutputStream pout){this.pout = pout;}public void run(){try{System.out.println("马上准备写入数据");Thread.sleep(5000);pout.write("sharon".getBytes());System.out.println("数据已经写完");}catch(Exception e){throw new RuntimeException("写入数据失败");}finally{try{if(pout!=null)pout.close();}catch(Exception e){throw new RuntimeException("关闭资源失败");}}}}//定义管道读取流,从管道获取数据class PipedIn implements Runnable{private PipedInputStream pin;PipedIn(PipedInputStream pin){this.pin = pin;}//异常要在方法内部处理;public void run(){try{//定义一个缓冲数组byte[] buf = new byte[1024];System.out.println("数据开始传输前,没有获取数据,阻塞等待");//开始读取管道中的数据;int len = pin.read(buf);System.out.println("数据读取完毕");//转换成字符串输出;String str = new String(buf,0,len);System.out.println(str);}catch(Exception e){throw new RuntimeException("读取数据失败");}finally{try{if(pin!=null)pin.close();}catch(Exception e){throw new RuntimeException("关闭资源失败");}}}}



操作基本数据类型
DataInputStream
DataOutputStream
操作字节数组
ByteArrayInputStream
ByteArrayOutputStream
操作字符数组
CharArrayReader
CharArrayWriter
操作字符串
StringReader
StringWriter
随机访问文件
RandomAccessFile:该类直接继承自Object,具备读和写功能,内部封装了一个数组,通过指针对数组元素进行操作;
可以通过getFilePointer()获取指针位置,通过seek()改变指针的位置;
其实完成读写的原理就是内部封装了字节输入输出流;
只能操作文件,同时还有不同的模式;’
模式:
如果模式为只读R,则不会创建文件,会去读一个已经存在的文件,若文件不存在会出现异常;
如果模式为读写rw,且该对象的构造方法要操作的文件不存在,会自动创建;如果存在,则不会覆盖;
特有方法:
seek(long pos):调整对象中的指针,来进行指定位置的数据读和写;
int skipBytes(int n):跳过指定的字节数,不能往前跳;

12.字符编码

12.1 概述
(1).字符流的出现是为了方便操作字符;
(2).IO体系中的特别重要的和编码有关的流,转换流;
(3).在对对象进行构造时,可以加入指定的字符集;
转换流:InputStreamReader和OutputStreamWriter
打印流:PrintStream和printWriter
(4).常见的编码表:
ASCII:美国标准信息交换码表,用一个字节的7位来表示
IOS8859-1:拉丁码表,欧洲,用一个字节的8位来表示
GB2312:中国使用的早期的码表
GBK:中国使用的编码表升级版,打头的是两个高位为1的两个字节编码;解析为数值就是负数;
Unicode:国际标准码,所有的字符都是用两个字节来表示,Java语言使用的是Unicode;
UTF-8:最多用三个字节来表示一个字符;
一个字节:0开头
两个字节:字节一:110 ;字节二:10;
三个字节:字节一:110;字节二:10;字节三:10;
12.2 转换流的应用

(1). 可以将字符以指定编码格式存储;

(2).可以对文本数据指定编码格式来解读;

(3).指定编码表的动作有构造方法完成;

12.3 编码和解码

(1).编码:字符串变成字节数组

默认字符集:string.getBytes()

指定字符集:string getBytes(charsetName)

(2).解码:字节数组变成字符串

默认字符集:new String(bytes[])

指定字符集:new String(bytes[], charsetName)

注意:

如果编码失败了,解码就没有意义了;

如果编码正确,解码出来是乱码,可以对乱码使用错误的解码表再次编码,然后用正确的码表解码;

如果使用GBK编码,用UTF-8解码,再通过编错误码表解正确码表的方法获得原来的数据就不能成功了,因为UTF-8也支持中文;



练习:

/* 有五个学生,每个学生有3门课的成绩,从键盘输入以上数据(包括姓名,三门课成绩), 输入的格式:如:张三,30,40,60 计算出总成绩,并把学生的信息和计算出的总分数高低顺序存放在磁盘文件“stuinfo.txt”中。  1、描述学生类 2、定义一个可操作学生对象的工具类。  思想: 1、通过获取键盘录入一行数据,并将该行中的信息取出封装成学生对象。 2、因为学生有很多,那么就需要存储,使用到集合。因为要对学生的总分排序。所以可以使用TreeSet。 3,将集合的信息写入到一个文件中。 */ package com.zr.day20;import java.io.*;import java.util.*;//定义学生类class RankStudent{public static void main(String[] args)throws IOException{//读取键盘录入BufferedReader br = new BufferedReader(new InputStreamReader(System.in));//定义TreeSet集合,可以对元素进行排序;TreeSet<Stu> ts = new TreeSet<Stu>(Collections.reverseOrder( //使用工具类中的方法反转比较器的比较顺序//自定义比较器,按照总分由低到高,总分相同时,按照name的字典顺序new Comparator<Stu>(){public int compare(Stu s1,Stu s2){int num = Integer.valueOf(s1.getSum()).compareTo(Integer.valueOf(s2.getSum()));if(num==0)return s1.getName().compareTo(s2.getName());return num;}}));//读取键盘录入,读取一行就向集合中存储一个Stu元素;for(String line;(line=br.readLine())!=null;){if("over".equals(line))break;//按照逗号分隔String[] buf = line.split(",");ts.add(new Stu(buf[0],Integer.parseInt(buf[1]),Integer.parseInt(buf[2]),Integer.parseInt(buf[3])));}//关闭流br.close();//定义输出流,将获取的信息按照指定的方式输出到文件中;BufferedWriter bw = new BufferedWriter(new FileWriter("stu.txt"));//迭代集合中的元素;for(Iterator<Stu> it = ts.iterator();it.hasNext();){//每迭代一个元素就向文件中写入一行;Stu student = it.next();bw.write(student.toString());bw.newLine();bw.flush();}//关闭流bw.close();}}//定义学生类,让其具备自然顺序;class Stu implements Comparable<Stu>{private String name;private int chinese;private int math;private int english;private int sum;//构造方法Stu(String name,int chinese,int math,int english){this.name = name;this.chinese = chinese;this.english = english;this.math = math;this.sum = chinese + math + english;}public int getSum(){return sum;}public String getName(){return name;}@Override//自然顺序比较器public int compareTo(Stu student){int num = this.name.compareTo(student.name);if(num==0)return Integer.valueOf(this.sum).compareTo(Integer.valueOf(student.sum));return num;}@Override//hashCode()复写public int hashCode(){return this.name.hashCode()+this.sum*38;}@Override//比较equals()复写public boolean equals(Object obj){if(!(obj instanceof Stu))throw new RuntimeException("类型不匹配");Stu student = (Stu)obj;return this.name.equals(student.name);}@Overridepublic String toString(){return this.name+":"+this.chinese+"+"+this.math+"+"+this.english+"="+this.sum;}}


0 0
原创粉丝点击