黑马程序员_IO(下)

来源:互联网 发布:淘宝50字通用好评 编辑:程序博客网 时间:2024/06/09 12:23

                     -----------android培训java培训java学习型技术博客、期待与您交流! ------------

 

七,File 类
该类的出现是对文件系统的中的文件以及文件夹进行对象的封装。

可以通过对象的思想来操作文件以及文件夹。
1,构造函数:
File(String filename):将一个字符串路径(相对或者绝对)封装成 File 对象,该路径是可
存在的,也可以是不存在。
File(String parent,String child);
File(File parent,String child);

2,特别的字段:separator:跨平台的目录分隔符。
例子:File file = new File("c:"+File.separator+"abc"+File.separator+"a.txt");

3,常见方法:
1,创建。
 boolean createNewFile():在指定位置创建文件,如果该文件已经存在,则不创建,返回false。
      和输出流不一样,输出流对象一建立创建文件。而且文件已经存在,会覆盖。

 boolean mkdir():创建文件夹。
 boolean mkdirs():创建多级文件夹。
2,删除。
 boolean delete():删除失败返回false。如果文件正在被使用,则删除不了返回falsel。
 注意:对于文件夹只能删除不带内容的空文件夹,
          对于带有内容的文件夹,不可以直接删除,必须要从里往外删除。

 void deleteOnExit();删除动作交给系统完成。无论是否反生异常,系统在退出
时执行删除动作。

3,判断。
 boolean exists() :文件是否存在.
 isFile():判断文件对象是否是文件
 isDirectory();判断文件对象是否是目录
 isHidden();判断文件对象是否是隐藏文件
 isAbsolute();判断文件的绝对路径是否为真

4,获取信息。

getName():获取文件或者文件夹的名称。
getPath():File 对象中封装的路径是什么,获取的就是什么。
getAbsolutePath():无论 File 对象中封装的路径是什么,获取的都是绝对路径。
getParent():  获取 File 对象封装文件或者文件夹的父目录。
注意:如果封装的是相对路径,那么返回的是 null.
long length():获取文件大小。
long lastModified():获取文件或者文件最后一次修改的时间。

static File[] listRoots():获取的是被系统中有效的盘符。
String[] list():获取指定目录下当前的文件以及文件夹名称。
String[] list(Filenamefilter):  可以根据指定的过滤器,过滤后的文件及文件夹名
称。
File[] listFiles():获取指定目录下的文件以及文件夹对象。


5,重命名:
renameTo(File):
File f1 = new File("c:\\a.txt");
File f2 = new File("c:\\b.txt");
f1.renameTo(f2);//将 c 盘下的 a.txt 文件改名为 b.txt 文件。

方法的代码体现:

1,

import java.io.*;public class FileDemo {public static void main(String[] args) throws IOException{method_5();}//更改文件的名字或者存放的位置public static void method_5(){File f1 = new File("c:\\Test.java");File f2 = new File("d:\\hahah.java");sop("rename:"+f2.renameTo(f1));}//获取文件的路径public static void method_4(){File f= new File("file.txt");//虽然文件不存在 但是可以获取文件的相对路径和绝对路径sop("path:"+f.getPath());sop("abspath:"+f.getAbsolutePath());sop("parent:"+f.getParent());//该方法返回的是绝对路径中的父目录。如果获取的是相对路径,返回null。//如果相对路径中有上一层目录那么该目录就是返回结果。}//判断文件对象是否是文件或目录 public static void method_3(){File f=new File("e:\\java\\java2013\\day20\\file2.txt");//记住在判断文件对象是否是文件或者目录时,必须要先判断该文件对象封装的内容是否存在。//通过exists判断。sop("dir:"+f.isDirectory());//判断是否是目录sop("file:"+f.isFile());//判断是否是文件//判断文件的绝对路径sop(f.isAbsolute());}//判断文件是否存在和创建文件夹public static void method_2(){File f=new File("file.txt");//判断文件是否存在//sop("exists:"+f.exists());sop("execute:"+f.canExecute());//创建文件夹File dir =new File("abc\\kkk\\a\\a\\dd\\ee\\qq\\aaa");sop("mkdir:"+dir.mkdirs());}//创建和删除文件public static void method_1() throws IOException{File f=new File("file.txt");//创建文件sop("create:"+f.createNewFile());//删除文件sop("delete:"+f.delete());}//创建File对象public static void consMethod(){//将a.txt封装成file对象。可以将已有的和为出现的文件或者文件夹封装成对象。File f1=new File("a.txt");//将文件的父目录 和 文件的路径名传入File封装成对象File f2=new File("c:\\abc","b.txt");File d= new File("c:\\abc");File f3=new File(d,"c.txt");//可以跨平台使用  separator 是与系统有关的默认名称分隔符File f4=new File("c:"+File.separator+"abc"+File.separator+"zzz"+File.separator+"a.txt");sop("f1:"+f1);//打印的都是相对路径名sop("f2:"+f2);sop("f3:"+f3);sop("f4:"+f4);}public static void sop(Object obj){System.out.println(obj);}}


2,

import java.io.*;public class FileDemo2 {public static void main(String[] args) {listDemo_2();}//list(FilenameFilter filter) //返回一个字符串数组,这些字符串指定此抽象路径名表示的目录中满足指定过滤器的文件和目录。public static void listDemo_2(){File dir =new File("e:\\java\\java2013\\day18");String[] arr=dir.list(new FilenameFilter()//通过匿名内部类{public boolean accept(File dir,String name){//获取bmp文件return name.endsWith(".bmp");}});//打印文件的长度System.out.println("len:"+arr.length);for(String name:arr){//打印文件的名字System.out.println(name);}}//列出路径名下的目录或者文件public static void listDemo(){File f=new File("e:/java");String[] names =f.list();//调用list方法的file对象必须是封装了一个目录。该目录还必须存在。for(String name :names){System.out.println(name);}}//列出可用的文件系统根listRoots()public static void listRootsDemo(){File[] files =File.listRoots();//使用一个高级for循环来遍历数组中的元素for(File f :files){System.out.println(f);}}}


 

4,递归
其实就是在使用一个功能过程中,又对该功能有需求。
就出现了函数自身调用自身。
注意
1,一定要限定条件,否则内存溢出。
2,使用递归时,调用次数不要过多,否则也会出现内存溢出。

1,需求:
列出指定目录下文件或者文件夹,包含子目录中的内容。
也就是列出指定目录下所有内容。

//因为目录中还有目录,只要使用同一个列出目录功能的函数完成即可。//在列出过程中出现的还是目录的话,还可以再次调用本功能。public class FileDemo3 {public static void main(String[] args) {File dir =new File("e:\\java\\java2013");showDir(dir,0);}//定义一个方法使目录带有层次public static String getLevel(int level){//定义一个容器StringBuilder sb =new StringBuilder();for(int x=0;x<level;x++){sb.append("|--");}return sb.toString();}public static void showDir(File dir,int level){//打印带有层次的目录名称System.out.println(getLevel(level)+dir.getName());level++;File[] files =dir.listFiles();for(File file : files){ //当目录下还有目录文件的时候  要进行判断 if(file.isDirectory())showDir(file,level);elseSystem.out.println(getLevel(level)+file);}}}


2,需求:删除一个带内容的目录。
原理:从里往外删除,所以需要使用递归完成。

public static void main(String[] args) {//创建一个文件对象  里面是要删除的文件目录File dir =new File("d:\\101719");removeDir(dir);//调用删除方法}//删除方法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]);else//打印出目录下的文件名字 和是否删除返回结果System.out.println(files[x]+":-file-"+files[x].delete());}//打印目录和是否删除返回结果System.out.println(dir+"::dir::"+dir.delete());}


 

, Properties
可以和流相关联的集合对象 Properties.

Map
    |--Hashtable
 |--Properties
Properties:该集合不需要泛型,因为该集合中的键值对都是 String 类型。

1,存入键值对:setProperty(key,value);
2,获取指定键对应的值:value getProperty(key);
3,获取集合中所有键元素:
Enumeration   propertyNames();
在 jdk1.6 版本给该类提供一个新的方法。
Set<String> stringPropertyNames();
4,列出该集合中的所有键值对,可以通过参数打印流指定列出到的目的地。
list(PrintStream);
list(PrintWriter);
例:list(System.out):将集合中的键值对打印到控制台。
        list(new PrintStream("prop.txt")):将集合中的键值对存储到 prop.txt 文件中。
5,可以将流中的规则数据加载进行集合,并称为键值对。
load(InputStream):
jdk1.6 版本。提供了新的方法。
load(Reader):
注意:流中的数据要是  "键=值" 的规则数据。
6,可以将集合中的数据进行指定目的的存储。
store(OutputStram,String comment)方法。
jdk1.6 版本。提供了新的方法。
store(Writer ,String comment):
使用该方法存储时,会带着当时存储的时间。

方法演示:

import java.util.*;import java.io.*;public class PropertiesDemo {public static void main(String[] args)throws IOException {loadDemo();}public static void loadDemo()throws IOException{//创建一个Properties集合Properties prop =new Properties();//用一个字节流关联文件FileInputStream fis =new FileInputStream("e://java//java2013/info.txt");//将流中的数据加载进集合。prop.load(fis);//void load(InputStream inStream) 从输入流中读取属性列表(键和元素对)。 //修改某个键对应的值prop.setProperty("lisi", "45");//这种修改的方法只是将修改后的内容存入了内存 而实际文本中的值没有改变//所以要将修改后的值输入到文本中 创建个输出流关联文件FileOutputStream fos=new FileOutputStream("e://java//java2013/info.txt");//store()方法是将Properties中的属性列表写入输出流prop.store(fos,"haha");//“haha”是属性列表的描述//System.out.println(prop);prop.list(System.out);//list方法是将Properties中的元素输出到指定的输出流fis.close();}//演示,如何将流中的数据存储到集合中。//想要将info.txt中键值数据存到集合中进行操作。/*1,用一个流和info.txt文件关联。2,读取一行数据,将该行数据用"="进行切割。3,等号左边作为键,右边作为值。存入到Properties集合中即可。*/public static void method_1()throws IOException{//1通过一个字符流来关联文件并存入缓冲区中BufferedReader bufr=new BufferedReader(new FileReader("E:/java\\java2013\\info.txt"));String line=null;//创建一个prop集合Properties prop =new Properties();//用=号将键值对分割 存入到Properties集合中while((line=bufr.readLine())!=null){String[] arr=line.split("=");prop.setProperty(arr[0],arr[1]);}//关闭流资源bufr.close();System.out.println(prop);}//设置和获取元素public static void setAndGet(){Properties prop =new Properties();//设置元素prop.setProperty("zhangsan","30");prop.setProperty("lisi","39");//System.out.println(prop);//根据键获取值String value =prop.getProperty("lisi");//System.out.println(value);//stringPropertyNames        //返回此属性列表中的键集,其中该键及其对应值是字符串Set<String> names =prop.stringPropertyNames();for(String s:names){System.out.println(s+":"+prop.getProperty(s));}}}


 

练习:限制程序运行次数。当运行次数到达5次时,给出,请您注册的提示。并不再让该程序执行。

用于记录应用程序运行次数。如果使用次数已到,那么给出注册提示。
很容易想到的是:计数器。
可是该计数器定义在程序中,随着程序的运行而在内存中存在,并进行自增。
可是随着该应用程序的退出,该计数器也在内存中消失了。
下一次在启动该程序,又重新开始从0计数。这样不是我们想要的。
程序即使结束,该计数器的值也存在。下次程序启动在会先加载该计数器的值并加1后在重新存储起来。
所以要建立一个配置文件。用于记录该软件的使用次数。该配置文件使用键值对的形式。
这样便于阅读数据,并操作数据。键值对数据是map集合。数据是以文件形式存储,使用io技术。
那么map+io -->properties.
配置文件可以实现应用程序数据的共享。
比如代码:

public class RunCount {public static void main(String[] args)throws IOException {//创建一个Properties集合Properties prop =new Properties();//当文件不存在时 要先创建这个文件来存储次数File file =new File("count.ini");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 ++;//将键值对存入prop集合prop.setProperty("time", count+"");//用一个输入流和文件关联FileOutputStream fos = new FileOutputStream(file);//将集合中元素写入输出流prop.store(fos,"");fos.close();fis.close();}}


 

八,IO包中的对象
 IO 包中的常见对象
字节流:
FileInputStream
FileOutputStream
BufferedInputStream
BufferedOutputStream

字符流:
FileReader
FileWriter
BufferedReader
BufferedWriter
转换流:
InputStreamReader
OutputStreamWriter

文件对象:
File

打印流:
PrintStream
PrintWriter

所有的带 File 的流对象都可以直接操作 File 对象。

IO 包中的其他对象:

1,打印流。
该流提供了打印方法,可以将各种数据类型的数据都原样打印。
PrintStream:
是一个字节打印流,System.out 对应的类型就是 PrintStream。
它的构造函数可以接收三种数据类型的值。
1,字符串路径。
2,File 对象。
3,OutputStream。

PrintWriter:
是一个字符打印流。构造函数可以接收四种类型的值。
1,字符串路径。
2,File 对象。
对于 1,2 类型的数据,还可以指定编码表。也就是字符集。

3,OutputStream
4,Writer
对于 3,4 类型的数据,可以指定自动刷新。
注意:该自动刷新值为 true 时,只有三个方法可以用:println,printf,format.

//如果想要既有自动刷新,又可执行编码。如何完成流对象的包装?PrintWrter pw =   new PrintWriter(new OutputSteamWriter(new FileOutputStream("a.txt"),"utf-8"),true); //如果想要提高效率。还要使用打印方法。PrintWrter pw =   new PrintWriter(new BufferdWriter(new OutputSteamWriter(new     FileOutputStream("a.txt"),"utf-8")),true); 


 

2,管道流。
PipedInputStream
PipedOutputStream
特点:
读取管道流和写入管道流可以进行连接。
连接方式:
1,通过两个流对象的构造函数。
2,通过两个对象的 connect 方法。

通常两个流在使用时,需要加入多线程技术,也就是让读写同时运行。
注意;对于 read 方法。该方法是阻塞式的,也就是没有数据的情况,该方法会等
待。

3,RandomAccessFile:
该对象并不是流体系中的一员。
该对象中封装了字节流,同时还封装了一个缓冲区(字节数组),通过内部的指针来操作
数组中的数据。

该对象特点:
1,该对象只能操作文件,所以构造函数接收两种类型的参数。
a,字符串路径。
b,File 对象。
2,该对象既可以对文件进行读取,也可以写入。
在进行对象实例化时,必须要指定的该对象的操作模式,r rw 等。

该对象中有可以直接操作基本数据类型的方法。

该对象最有特点的方法:
skipBytes():跳过指定的字节数。
seek():指定指针的位置。
getFilePointer():获取指针的位置。
通过这些方法,就可以完成对一个文件数据的随机的访问。

想读哪里就读哪里,想往哪里写就往哪里写。

该对象功能,可以读数据,可以写入数据,如果写入位置已有数据,会发生数据覆盖。
也就是可以对数据进行修改。

在使用该对象时,建议数据都是有规则的。或者是分段的。

注意;该对象在实例化时,如果要操作的文件不存在,会自动建立。
如果要操作的文件存在,则不会建立,如果存在的文件有数据。
那么在没有指定指针位置的情况下,写入数据,会将文件开头的数据覆盖。

 

可以用于多线程的下载,也就是通过多线程往一个文件中同时存储数据。

4,序列流。也称为合并流。


SequenceInputStream:
特点:可以将多个读取流合并成一个流。这样操作起来很方便。
原理:其实就是将每一个读取流对象存储到一个集合中。最后一个流对象结尾作为这个
流的结尾。
两个构造函数:
1,SequenceInputStream(InputStream in1,InputStream in2)
可以将两个读取流合并成一个流。
2,SequenceInputStream(Enumeration<? extends InputStream> en)
可以将枚举中的多个流合并成一个流。

作用:可以用于多个数据的合并。

注意:因为 Enumeration 是 Vector 中特有的取出方式。而 Vector 被 ArrayList 取代。
所以要使用 ArrayList 集合效率更高一些。那么如何获取 Enumeration 呢?

ArrayList<FileInputStream > al = new ArrayList<FileInputStream>(); for(int x=1; x<4; x++) al.add(new FileInputStream(x+".txt")); Iterator<FileInputStream> it = al.iterator(); 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("4.txt"); byte[] buf = new byte[1024*4]; int len = 0; while((len=sis.read(buf))!=-1) { fos.write(buf,0,len); } fos.close(); sis.close(); 


 

//如果要一个对文件数据切割。一个读取对应多了输出。FileInputStream fis = new FileInputStream("1.mp3"); FileOutputStream fos    = null; byte[] buf = new byte[1024*1024];//是一个 1MB 的缓冲区。int len = 0; int count = 1; while((len=fis.read(buf))!=-1) { fos = new FileOutputStream((count++)+".part); fos.write(buf,0,len); fos.close(); } fis.close(); //这样就是将 1.mp3 文件切割成多个碎片文件。


 

想要合并使用 SequenceInputStream 即可。
对于切割后,合并是需要的一些源文件的信息。
可以通过配置文件进行存储。该配置可以通过键=值的形式存在。
然后通过 Properties 对象进行数据的加载和获取。

5,对象的序列化。


ObjectInputStream
ObjectOutputStream

可以通过这两个流对象直接操作已有对象并将对象进行本地持久化存储。
存储后的对象可以进行网络传输。
两个对象的特有方法:
ObjectInputStream
Object readObject():该方法抛出异常:ClassNotFoundException。
ObjectOutputStream
void writeObject(Object):被写入的对象必须实现一个接口:Serializable
否则会抛出:NotSerializableException 

Serializable:该接口其实就是一个没有方法的标记接口。
用于给类指定一个 UID。该 UID 是通过类中的可序列化成员的数字签名运算出
来的一个 long 型的值。
只要是这些成员没有变化,那么该值每次运算都一样。

该值用于判断被序列化的对象和类文件是否兼容。

如果被序列化的对象需要被不同的类版本所兼容。可以在类中自定义 UID。
定义方式:static final long serialVersionUID = 42L;

注意:对应静态的成员变量,不会被序列化。
对应非静态也不想被序列化的成员而言,可以通过 transient 关键字修饰。

通常,这两个对象成对使用。

6,操作基本数据类型的流对象。
DataInputStream
DataInputStream(InputStream);
操作基本数据类型的方法:
int readInt():一次读取四个字节,并将其转成 int 值。
boolean readBoolean():一次读取一个字节。
short readShort();
long readLong();

剩下的数据类型一样。

String readUTF():按照 utf-8 修改版读取字符。注意,它只能读 writeUTF()写入的字符
数据。
DataOutputStream
DataOutputStream(OutputStream):
操作基本数据类型的方法:
writeInt(int):一次写入四个字节。
注意和 write(int)不同。write(int)只将该整数的最低一个 8 位写入。剩余三个 8
位丢弃。
writeBoolean(boolean);
writeShort(short);
writeLong(long);
剩下是数据类型也也一样。

writeUTF(String):按照 utf-8 修改版将字符数据进行存储。只能通过 readUTF 读取。

通常只要操作基本数据类型的数据。就需要通过 DataStream 进行包装。

通常成对使用。

7,操作数组的流对象。
1,操作字节数组
ByteArrayInputStream

ByteArrayOutputStream
toByteArray();
toString();
writeTo(OutputStream);

 

2,操作字符数组。
CharArrayReader
CharArrayWriter

对于这些流,源是内存。目的也是内存。

而且这些流并未调用系统资源。使用的就是内存中的数组。
所以这些在使用的时候不需要 close。

操作数组的读取流在构造时,必须要明确一个数据源。所以要传入相对应的数组。

对于操作数组的写入流,在构造函数可以使用空参数。因为它内置了一个可变长度数组
作为缓冲区。

这几个流的出现其实就是通过流的读写思想在操作数组。

类似的对象同理:
StringReader,
StringWriter。


九 编码转换:
在 io 中涉及到编码转换的流是转换流和打印流。
但是打印流只有输出。

在转换流中是可以指定编码表的。
默认情况下,都是本机默认的码表。GBK. 这个编码表怎么来的?
System.getProperty("file.encoding");

 

常见码表:
ASCII:美国标准信息交换码。使用的是 1 个字节的 7 位来表示该表中的字符。

ISO8859-1:拉丁码表。使用 1 个字节来表示。

GB2312:简体中文码表。
GBK:简体中文码表,比 GB2312 融入更多的中文文件和符号。

unicode:国际标准码表。都用两个字节表示一个字符。
UTF-8:对 unicode 进行优化,每一个字节都加入了标识头。

编码转换:
字符串  -->字节数组 :编码。通过 getBytes(charset);
字节数组-->字符串 : 解码。通过 String 类的构造函数完成。String(byte[],charset);

 

如果编错了,没救!
如果编对了,解错了,有可能还有救!

String s = "你好"; //编码。byte[] b = s.getBytes("GBK"); //解码。String s1 = new String(b,"iso8859-1"); System.out.println(s1);//???? //想要还原。/* 对 s1 先进行一次解码码表的编码。获取原字节数据。然后在对原字节数据进行指定编码表的解码。*/ byte[] b1 = s1.getBytes("iso8859-1"); String s2 = new String(b1,"gbk"); System.out.println(s2);//你好。


 

这种情况在 tomcat 服务器会出现。
因为 tomcat 服务器默认是 iso8859-1 的编码表。
所以客户端通过浏览器向服务端通过 get 提交方式提交中文数据时,
服务端获取到会使用 ISO8859-1 进行中文数据的解码。会出现乱码。
这时就必须要对获取的数据进行 iso8859-1 编码。然后在按照页面指定的编码表进行解
码即可

而对于 post 提交,这种方法也通用。但是 post 有更好的解决方式。
request.setCharacterEncoding("utf-8");即可。
所以建立客户端提交使用 post 提交方式。

 

 

          -----------android培训java培训java学习型技术博客、期待与您交流! ------------
原创粉丝点击