IO包当中第二种非常重要的类File类;
一、File类概述
流在操作的只有数据,而数据最明显的体现形式就是文件;
文件包含很多的属性和行为信息;文件是一类复杂的事物;
File类是用来将文件或者文件夹封装成对象的类,可以用来操作文件或者文件夹的属性信息;
File类弥补了流的不足,流对象只能操作数据,它不能操作封装数据的文件夹,它不能操作封装数据的文件的属性信息;
操作数据只能用流对象,而操作管理流的封装就用File类;
字段摘要中比较重要的一个就是:
static String separator 与系统有关的默认名称分隔符,为了方便,它被表示为一个字符串。
构造方法演示:
创建File对象;
new File(a.txt) 将文件封装为File对象,可以将已有的和未出现的文件或者文件夹封装成对象;
File(String parent, String child) 左边只的是父目录,右边指的是文件名,如果把目录和文件名合在一起就是成了上面一种情况,但是这种分开传入的方式中,可以有操作的目录是固定的,而操作文件名是变化的,即一个字符串;
File(File parent, String child) 左边是把父目录封装成一个对象;这种情况和第二种情况又是一样的,只是把父目录封装成了对象;而第二种是直接用字符串表示;
把File对象作为字符串打印的结果是他们的绝对路径或者相对路径;看你封装的是什么路径他就打印什么路径;
File.separator这个字段可以用来替代路径里面的分隔符,这样就可以实现跨平台,因为不同平台可能分隔符不一样;
注意的是,文件或者文件路径的对象被初始化时,并不表示其就会在硬盘上创建对应的实体,这里和流对象不一样,要注意理解区分;
因为IO流的处理对象是数据,但是数据需要有一个载体,所以如果没有载体的时候,底层系统就会先帮你建立一个载体,因为不形成也行,那就报错,但是既然是处理就可以直接帮你建立,比较智能化,报错之后你还是要建立的,所以这个动作是在底层构造方法中给封装了;因为在初始化IO流对象时,存在对应的文件是必备的属性,没这,将无法进行各种操作;故不能初始化,就需要在构造方法中建立文件;
而File类他处理的就是文件本身,文件路径和文件名是他的属性,而创建文件是他的功能,IO正是调用的它的功能,但是他自己就只有去实现这个功能,并提供别人访问的;
注意理解的是,File类的实例对象不是文件个体,而是一个包含有属性和功能的具体对象或者说概念也行,文件名和其路径只是该实例对象对应的具体属性而已;因此File类初始化时只需要有具体的文件名和路径等相关的信息即可,至于存不存在并不影响初始化过程;而创建和是否存在是该对象的具体功能;
二、File类方法;
个人发现,按照老师的讲课的顺序,后面学的类,很多都是我们写不出底层代码来的,因为他的底层很多都调用了操作系统的方法了,前面学的一些类,自己还可以写出一些底层的实现代码来;
而且这个类里面的方法绝对是基本都是调用底层操作系统的文件类的方法了的;
基本方法功能有:创建文件,删除文件,判断文件,获取属性信息;
1、创建文件:
boolean createNewFile();
在指定位置创建文件,如果该文件已经存在,则不创建返回false;
和输出流不一样,输出流对象一建立就创建文件,而且文件已经存在,会进行覆盖;
创建临时文件,程序关闭即删除或成为垃圾;如xxx.temp;
static File createTempFile(String prefix,String suffix) 不指定目录的临时文件;
static File createTempFile(String prefix,String suffix, File directory) 指定目录的;
创建文件夹(文件目录):这个创建动作是覆盖性质的;
boolean mkdir() 只能创建一级目录文件夹;
boolean mkdirs() 可以创建多级目录文件夹; 当然肯定要先new一个路径文件对象;
2、删除:
boolean delete() 正常删除; code();??没见过;
如果程序发生异常,文件是删不掉的;放在finally块中也是删不掉;因为正在占用;返回false;
void deleteOnExit() 程序退出时删掉;
这种方法可以强行在退出时删除某文件;因为退出了,就没有占用程序了;
上面两个方法,在执行完毕之后,底层还进行了一次判断动作,所以就返回真假值;
3、判断:
boolean canExecute() 文件是否可执行; 可以结合Runtime类当中的exec()使用;
boolean canRead()
boolean canWrite()
int compareTo(File pathname) 文件排序的原理,可以自定义比较器进行比较排序;
boolean exists() 文件是否存在;
可以用于在用流在操作时判断文件是否存在,避免文件不存在流操作时抛异常;
boolean isDirectory()
boolean isFile()
在判断文件对象是否是文件或者目录是,必须要判断该文件对象封装的内容是否存在,通过exists存储,否则如果不存在,判断文件还是目录都返回假;
boolean isHidden() 是否隐藏;
Java有些文件用流是访问不了的,如系统当中system开头的隐藏文件,会抛异常,所以可以判断一下,隐藏就不访问,因为Java所有的文件都是可以进行访问不管你隐藏不隐藏;
boolean isAbsolute() 是否是绝对路径;即使不存在该文件也可以判断;
4、获取:
String getName() 只获取文件名,不返回路径;
String getPath()
String getParent()
只获取对象中指定的父目录,如果对象中没有指定文件上一级目录,则返回null;
File getAbsoluteFile()
String getAbsolutePath()
long length() 获取文件的大小;
和InPutStream中的一个available方法一样,不过available方法有最大的范围限定,他是int返回值类型;这里没有范围限定;
long lastModified() 上一次修改时间
boolean renameTo(Filedest) 对文件进行重命名后,并进行剪切操作;
5、特殊方法:
static File[] listRoots() 列出可用的文件系统根。 就是指出机器的有效盘符;
因为他不需要操作具体的特有数据,所以不需要对象;可以用类调用;
可以用来通过遍历来获取系统的有效盘符
遍历后的值调用length()方法返回为0;即此方法不操作具体数据,只对根目录进行取出;
String[] list() 返回指定目录中的所有文件或者文件夹组成的一个数组,包括隐藏的;然后通过遍历取得每一个文件名;
如果访问的是文件非目录,返回的是null即没有内容;
如果访问的是目录,该目录必须要存在,返回一个没有元素的数组;否则则为null;
String[] list(FilenameFilter filter) 文件名过滤器;查找指定的文件;
结合String类中的startsWith方法和endsWith方法使用;
还注意,对接口的使用可以直接使用匿名内部类,如果里面方法很少的情况;
File[] listFiles()
比list方法功能更强,直接返回文件对象,然后可以用File对象的方法获取更多信息;因此会比list方法用得更常见;
6、递归:获取当前目录的子目录的文件;
(1)需求:列出指定目录下文件或者文件夹,包含子目录的内容,也就是列出指定目录下的所有内容;
因为目录中还有目录,只要使用同一个列出目录功能的函数完成即可。
在列出过程中出现的还是目录的话,还可以再次调用本功能,也就是函数自身调用自身,这种表现形式,或者编程手法,称为递归;
为什么打印的结果都是绝对路径呢?
其实递归就是一种循环;是循环的另外一种表示形式;
递归要注意的是:
1、限定条件;否则循环会不停止;
2、要注意递归的次数,尽量避免内存溢出;
主函数是在栈内存里面进行运行的;如果调用的方法放在栈内存里没有结束,而又太多就容易出现内存溢出;
(2)需求:如何让得到的目录结果带有层次;
使用StringBuilder里的方法;
(3)需求:删除一个带内容的目录;
删的时候打印一下,然后看是否都打印的是true,否则就出现问题了;
即如果还有文件删除就会false,如果文件被删除了,再删除也false;
这样效率就非常低下;
系统里有一些键值对目录;
要注意的是,底层操作系统的一些系统级文件是Java访问不到的,这些文件都是隐藏内容;
(4)建立一个Java文件的列表清单;
需求:将一个指定目录下的Java文件的绝对路径,存储到一个文本文件中;
a、对指定的目录进行递归;
b、获取递归过程所有的Java文件的路径;
c、将这些路径存储到集合中;(这里是用集合缓存一下,可以用来进行多个操作)
d、将集合中的数据写入到一个文件中;
玩递归就要先搞一个方法;
把数据存在硬盘里面叫做数据的持久化;存在内存里,一关机就没有了;
注意,集合的元素都是对象,要存为字符串,就要先变为字符串才行;这不是打印,所以必须手动变,没有自动调用toString方法了;不过可以调用valueOf的方法,他会自动调用toString方法;
写入数据进文件,就是操作数据,就要建立流对象;
IO基本对象都学完了,剩下的扩展对象之后再说,现在说下单独的对象,即Properties对象;
三:Properties类; 这个类较重要,一定要懂;
Properties是Hashtable的子类,也就是说其具有Map集合的特点,而且它里面存储的键值对都是字符串;不需要定义泛型;
Properties是集合中和IO技术相结合的集合容器。
该对象的特点,可以用于键值对形式的配置文件;因为每一个配置信息都有具体的指向和和参数;
在加载数据时,需要数据有固定格式,即:键 = 值;
任何程序启动后就会在内存中进行开辟空间,如果我们对软件进行的配置修改并没有保存在硬盘中,只要程序关闭,改动信息就会失效;即没有进行持久化存储配置信息;
Properties类不仅可以操作键值对,还可以运用IO流技术操作硬盘上的配置信息;
java.util.Properties;
演示:如何将流中的数据存储到集合中;
Object setProperty(String key, Stringvalue)
String getProperty(String key)
Set<String> stringPropertyNames() 取出键集;这个方法要记住;
需求:想要将info.txt中的键值对数据存储到集合中进行操作;
思路
1、用一个流和info.txt文件关联;
2、读取一行数据,将该行数据用“=”进行切割;
3、等号左边作为键,右边作为值,存入到Properties集合中即可;
Properties类中提供的加载指定文件的属性数据的方法;
void load(InputStream inStream)
void load(Reader reader) 从1.6版本才有的;
保存修改之后的属性信息到配置文件中
void store(OutputStream out, Stringcomments)
void store(Writer writer, Stringcomments)
后面的注释信息可以不加;
其中前面带#的都表示注释信息,如日期等,不会被Properties所加载;
一般的,只输入空值双引号,他会添加默认的注释信息;即时间和修改值等;
其实我觉得这个原理,底层就是重新写了一个文件,然后把之前的文件给覆盖了;不是真在文件中修改的;
练习:
需求:用于记录应用程序运行次数;如果使用次数已到,那么给出注册提示;
如实际中很多的共享软件都是这样;
很容易想到的是:计数器;一般的,计数器定义在程序中,随着程序的运行而在内存中存在,并进行自增,随着应用程序的退出,该计数器也在内存中消失了;
下一次在启动该程序时,又重新开始从0计数;
需要程序即使结束,该计数器的值也存在,下次程序启动后会先加载该计数器的值并加1后再重新存储起来,所以要建立一个配置文件,用于记录该软件的使用次数;
该配置文件使用键值对的形式,这样便于阅读数据,并操作数据;
键值对数据是map集合,数据是以文件形式存储,使用IO技术,那么map+io就是Properties类;
配置文件应该要实现应用程序的共享;即程序启动前先判断文件是否存在;存在时先读入;
一般配置文件可以为ini格式,在Java里常用的配置文件格式有xml,properties;
以后学框架,里面有很多这样的配置文件;
在操作文件的时候,要养成习惯,先把文件封装成File对象;一般IO建立读入流对象时都要做这个操作,建立写入流对象一般也需要这个操作;
封装之后,可以对文件进行判断,而流是做不了这个操作;
一般防止文件卸载后配置文件删掉就把配置文件放在system32目录下;
以后只要是键值对的配置信息,都是用Properties类操作的;
properties格式的配置文件装的内容不太复杂,也不能够多,否则其干不了;
所以一般用xml格式的配置信息做的比较好;
这是因为properties的键值对的特点决定的;
接口org.w3c.dom.Document;
工具包dom4j;用于调出xml的配置信息;因为上面这个接口中取出想xml的信息的方法比较复杂;
四、IO包中的其他类;
1、打印流:
PrintWriter与PrintStream 一个字符打印流,一个字节打印流;没有对应的写入流;
打印流的区别就在于能打印原始数据的原形,而Write方法只能写入字节或者字符;
凡是能直接操作文件对象的或者说与文件类相关的流对象都是比较重要的流对象;
PrintStream 字节流打印流:
构造函数可以接受的参数类型:File对象;字符串路径String;字节输出流,OutputStream;
PrintWriter 字符流打印流;这个流对象非常的常用;
开发中常用这个流对象把数据打印到客户端,让客户端对其解析执行;
构造函数可以接收的参数类型:
File对象;字符串路径String;字节输出流OutputStream;字符输出流WriterStream;
PrintStream(OutputStreamout, boolean autoFlush)
PrintWriter(Writer out, booleanautoFlush)
如果boolean参数传入true,那么这个就不需要用flush方法进行自动刷新了;而用write方法时就需要flush才行;println()自动刷新只在true下才行,要带有ln这样的标记才会刷新,不然无法操作;
2、第二个功能性的流对象:序列流SequenceInputStream
没有对应的OutputStream的对象;
功能:InPutStream读取流的合并;多个读取流对应一个输出流;
应用把多个文件变成一个文件,或者一次读取多个文件;
SequenceInputStream(Enumeration<?extends InputStream> e)
SequenceInputStream(InputStream s1,InputStream s2)
Vector<File> v = new Vector<File>();
enumeration en = v.elements();
枚举就是把集合中的所有的元素都放在枚举里面,与迭代器都是这个原理,就是把元素放在它里面来;
文件切割:一个读取流对应多个输出流;
winrar分卷压缩;
创建几个输出流,就是创建几个文件;
把数组存在ArrayList里去,如何用枚举进行取出;??
匿名的内部类,对访问的局部变量要进行final修饰;
Enumeration类和Iterator类他们是一种工具类,或者说看成是一种工具,工具类的主要内容就是提供一些功能,对某一些特定的功能进行封装;所以他们是没有具体的操作数据的;但是缺少数据的工具类对象是没有任何的意义的;因为像这类工具类一样,他们通常都需要定义在一个具有数据的类的里面即内部类的位置,这样才能够访问得到具体的数据;因为内部类是可以直接访问到外部类的数据的;所以在创建出他们的实例对象时都是初始化了具体数据的,这就是工具类的特点,一初始化的时候就有可具体访问的对象的,否则这样的实例是不可能的;
其实简单一点说,就是在初始化的时候工具类当中的方法一定要有能够操作的对象,有的化,就可以进行初始化,没有是初始化不出来的,像上面这两个类至少他们里面的方法是没有针对的对象的;所以只要他们里面的方法有具体的针对对象,初始化就能进行;
需要注意的时,其实并不是说他们把数据存放在他们那里了,这么说只是为了方便理解,
他们作为工具类是不可能含有具体数据,只能说是否能操作到具体数据;这才是工具类真实的意思;