------- android培训、java培训、期待与您交流! ----------
第一部分 File类
一.概述
1.File类是IO包中唯一代表磁盘文件本身信息的类,而不是文件中的内容。
2.File中定义了一些与平台无关的方法来操作文件,例如。创建、删除、重命名文件,判断文件的读写权限,文件是否存在,查看文件最近的修改时间等。
3.Java中的目录被当做一种特殊的文件使用,list方法可以返回目录中的所有目录和文件名。
4.在Linux下的路径分隔符是(/),而Windows下的路径分隔符是(\),Java可以正确处理它们的路径分隔符。
注4:在源文件的字符串中,由于(\)用作转义字符,所以想真正表示字符反斜杠要用(\\)的形式。
二.构造方法
File(String pathname);//完整的抽象路径名
File(String parent, String child);//parent为完整的抽象路径名的前半部分,child为后半部分。
File(File parent, String child);//parent为完整的抽象路径名的前半部分创建File 实例,child为后半部分的字符串。
抽象路径名指的是绝度路径或相对路径。在eclipse中相对路径指的是相对于工程根目录的路径。
三.字段摘要
static String pathSeparator;//与系统有关的路径分隔符的字符串形式。
static char pathSeparatorChar;//与系统有关的路径分隔符的字符形式。即系统属性 path.separator 值的第一个字符。在 UNIX 系统上,此字段为 ':';在 Microsoft Windows 系统上,它为 ';'。
static String separator;//与系统有关的默认名称分隔符的字符串形式。
static char separatorChar;//与系统有关的默认名称分隔符的字符形式。即系统属性 file.separator 值的第一个字符。在 UNIX 系统上,此字段的值为 '/';在 Microsoft Windows 系统上,它为 '\\'。
四.常用方法
1.获取
String getName();//返回由此抽象路径名表示的文件或目录的名称。
String getPath();//获取抽象路径名,即创建文件对象时传入的字符串名称。
String getAbsolutePath();//绝对路径名。
long length();//文件的长度。
long lastModified();//文件最后一次被修改的时间。
File getAbsoluteFile();//获取绝对路径名形式的文件对象。
String getParent();//本文件对象父目录的路径名字符串;如果采用相对路径且本文件位于工程根目录下,则返回 null。
File getParentFile();//本文件对象父目录的文件对象;如果采用相对路径且本文件位于工程根目录下,则返回 null。
2.创建与删除
boolean createNewFile();//如果文件不存在,则创建;否则不创建。这和输出流不同。
boolean delete();//删除本文件或目录。删除目录时,当目录中不为空时,删除失败。 当对象为多级目录时,删除的是最后一级目录。
void deleteOnExit();//在虚拟机终止时,请求删除此文件或目录。
boolean mkdir();//创建单级目录。
boolean mkdirs();//创建多级目录。
3.判断
boolean exists();//测试文件或目录是否存在。
boolean isDirectory();//测试文件对象是否是一个目录。
boolean isFile();//测试文件对象是否是一个标准文件。
4.重命名
boolean renameTo(File dest);//重新命文件。如果路径不同相当于移动文件位置。
5.列出系统根目录和容量获取
static File[] listRoots();//列出文件系统根目录(也叫分区或盘符)。
long getFreeSpace();//返回指定分区中未分配的字节数。
long getTotalSpace();//返回指定分区大小。
long getUsableSpace();//返回指定分区上可用于此虚拟机的字节数。
6.获取目录内容
String[] list();//获取指定目录的当前目录下的文件和目录名称,包含隐藏文件。
//调用本方法的File对象中封装的必须是目录,否则会抛出空指针异常;
//如果访问的是系统级目录也会抛出异常;
//如果目录存在但是没有内容,会返回一个数组,但长度为0.
File[] listFiles();// 获取当前目录下所有文件和目录的File对象。
String[] list(FilenameFilter filter);//根据FilenameFilter过滤器accept方法的获取符合指定过滤条件的文件和目录的名称。
File[] listFiles(FilenameFilter filter);//根据FilenameFilter过滤器accept方法的获取符合指定过滤条件的文件和目录的File文件。
File[] listFiles(FileFilter filter);//根据FileFilter过滤器accept方法的获取符合指定过滤条件的文件和目录的File文件。
五.应用案例
1.目录列表过滤器的应用案例
1>根据指定的后缀名过滤当前目录的文件或子目录
//定义的过滤器
class SuffixFilter implements FilenameFilter{
private String suffix;
public SuffixFilter(String Suffix) {
this.suffix=Suffix;
}
public boolean accept(File dir, String name) {
return name.endsWith(suffix);
}
}
//使用过滤器过滤以.java为结尾的文件
File[] files=dir.listFiles(new SuffixFilter(".java"));
2>获取所有隐藏文件
<span style="white-space:pre"> </span>File[] files=dir.listFiles(new FileFilter() {
public boolean accept(File pathname) {
return pathname.isHidden();
}
<span style="white-space:pre"> </span>});
2.深度遍历
需求:对指定目录进行所有内容的列出(包括子目录中的内容)
public class ListDemo {
public static void main(String[] args) {
File dir=new File("g:/gaoge");
deepTraversal(dir,0);
}
//显示目录的递归函数,参数deep记录递归的深度
public static void deepTraversal(File dir,int deep) {
System.out.println(getSpace(deep)+dir.getName());
File[] files=dir.listFiles();
for(int i=0;i<files.length;i++){
if(files[i].isDirectory()){
deepTraversal(files[i],deep+1);
}else if(files[i].isFile()){
System.out.println(getSpace(deep+1)+files[i].getName());
}
}
}
//获取指定格式的空格字符串
public static String getSpace(int deep) {
StringBuilder sb=new StringBuilder();
sb.append("|--");
for(int x=0;x<deep;x++){
sb.insert(0, "| ");
}
return sb.toString();
}
}
3.递归算法演示
函数直接或间接的调用了自身。
一个功能被重复使用,并且每次运算时参与运算的结果和上次调用有关。这是可以用递归来解决问题。
注意: 1>递归一定要明确条件,否则容易栈溢出。
2>注意递归的次数。
//递归无返回值演示:输出整数的二进制串
public static void toBin(int num){
if(num>0){
toBin(num/2);
System.out.print(num%2);
}else{
return;
}
}
//递归有返回值:求n的累加和
public static int getSum(int num) {
if(num==1)
return 1;
return num+getSum(num-1);
}
4.删除一个带内容的目录
原理:必须从里往外删,需要进行深度遍历。
public static void deleteFunc(File dir){
File[] files=dir.listFiles();
for (File file : files) {
if(file.isDirectory()){
deleteFunc(file);
}else{
file.delete();
}
}
dir.delete();
}<span style="background-color: rgb(255, 255, 255); font-family: Arial, Helvetica, sans-serif;"> </span>
第二部分 RandomAccessFile类
一.概述
1.RandomAccessFile类提供了众多的文件访问方法。
自身具备读写方法。
2.RandomAccessFile类支持随机访问方式。
该对象内部维护了一个byte数组,并通过指针可以操作数组中的元素。
通过skipBytes(int x),seek(int x)操作指针来达到随机访问。
3.可以通过getFilePointer方式获取指针的位置。
4.其实该对象就是将字节输入流和输出流进行了封装。
5.RandomAccessFile类在随机(相对于顺序而言)读写等长记录格式的文件时有很大的优势。
6.RandomAccessFile类仅限于操作文件,不能访问其他的IO设备,如,网络、内存影像等。
7.访问文件时如果文件不存在则创建,如果文件存在则不创建。
二.两种构造方法:
RandomAccessFile(File file, String mode);//file为读写的文件对象
RandomAccessFile(String name, String mode);//name是读写文件的字符串
mode 参数指定用以打开文件的访问模式,其取值可以为:
"r" 以只读方式打开文件。
"rw" 以读写方式打开文件。如果该文件尚不存在,则尝试创建该文件。
三.操作方法
1.完整读写八大基本数据类型的十六个方法readXxx(),writeXxx()。
外加,int readUnsignedByte();//从此文件读取一个无符号的8位数。
int readUnsignedShort();//从此文件读取一个无符号的16位数。
2.读写字节字节数组的方法:
int read();//读取一个字节
int read(byte[] b);//将最多b.length个字节从此文件读入 byte 数组。
int read(byte[] b, int off, int len);//将最多len个字节从此文件读入 byte 数组。
void write(int b);//将int型数据的低字节写入文件。
void write(byte[] b);//将b.length个字节从指定byte数组写入文件。
void write(byte[] b, int off, int len);//将len个字节从指定byte数组写入到此文件。
外加, void readFully(byte[] b);//将正好b.length个字节读入byte数组。
void readFully(byte[] b, int off, int len);//将正好len个字节读入byte数组。
3.读写字符串的方法:
String readLine();//读取一行字节,并把每个字节转换为字符,返回字符串。
String readUTF();//完整读取文件中的字符,返回字符串。
void writeBytes(String s);//把字符串中每个字符的低字节写入文件。
void writeChars(String s);//把字符串中每个字符的完整的写入文件。
void writeUTF(String str);//使用UTF-8编码以字符串的完整信息写入文件。
4.指针
long getFilePointer();//获取文件指针的位置。
void seek(long pos);//设置文件指针到指定位置。
int skipBytes(int n);//让指针跳过n个字节,到达指定位置。
5.文件长度
long length();//返回文件的长度。
void setLength(long newLength);//设置文件的长度。如果设置的长度小于原长度,文件将被截短;如果大于原长度,文件将被扩展。
6.其他
close();//关闭流
第三部分 节点流类
一.理解流的概念
1.流是字节序列的抽象集合
2.文件是数据的静态存储形式,而流是指数据传输时的形态。
3.流分为两个大类,节点流类和过滤流类(也叫流包装类,处理流类)
二.抽象基础流
1.InputStream
程序可以从中连续读取字节的对象叫输入流。在Java中,用InputStream类来描述所有输入流的抽象概念。
2.OutputStream
程序可以向其中连续写入的对象叫输出流。在Java中,用OutputStream类来描述所有输出流的抽象概念。
3.Reader和Writer
1>Reader和Writer是所有字符流类的抽象基类,用于简化对字符串的输入很输出编程,即用于读写文本数据。
2>二进制文件和文本文件的区别:在不考虑正负数的情况下,每个字节中的数据时0到255之间的任意值,它们在内存中都是二进制形式存放的,文件中的每个字节数据也都是二进制的,所以严格意义上来说文件系统中的文件都是二进制文件。各种文本字符是由一个或多个字节组成的,而每个字节只能是0到255之间的某些特定数字,还有一些其它的数字是在任何字符的字节中都不可能出现的。
如果文件中每个字节或每相邻的几个字节中的数据都可以表示成某种字符,我们称这个文件为文本文件。
可见文本文件只是二进制文件的某种特例。为了与文本文件相区别,人们就把文本文件以为的文件成为二进制文件。
三.文件流
1.FileInputStream和FileOutputStream
1>FileInputStream和FileOutputStream分别用来创建磁盘文件的输入流和输出流对象,通过它们的构造函数来指定文件路径和文件名。
2>创建FileInputStream实例对象时,指定的文件应当是存在和刻度的。创建FileOutputStream实例对象时如果指定的文件已经存在,这个文件中的原来的内容将被覆盖清除;如果不存在则创建一个新文件。
3>创建FileOutputStream实例对象时,可以指定不存在的文件名,不能指定一个已被其他程序打开的文件。
2.FileWriter
1>使用FileWriter写入字符流的步骤:
a.创建一个FileWriter对象,该对象一被初始化,就必须要明确被操作的文件。且该目录下如果已有同名文件,则同名文件将被覆盖。其实该步就是在明确数据要存放的目的地。
b.调用write(String s)方法,将字符串写入到流中。
c.调用flush()方法,刷新该流的缓冲,将数据刷新到目的地中。
d.调用close()方法,关闭流资源。但是关闭前会刷新一次内部的缓冲数据,并将数据刷新到目的地中。
2>close()和flush()区别:
flush()刷新后,流可以继续使用;
而close()刷新后,将会关闭流,不可再写入字符流。
flush()方法对应的图解:
注2:
1>其实java自身不能写入数据,而是调用系统内部方式完成数据的书写,使用系统资源后,一定要关闭资源。
2>文件的数据的续写是通过构造函数 FileWriter(Strings,boolean append),在创建对象时,传递一个true参数,代表不覆盖已有的文件。并在已有文件的末尾处进行数据续写。(windows系统中的文件内换行用\r\n两个转义字符表示,在linux系统中只用\n表示换行)
3>由于在创建对象时,需要指定创建文件位置,如果指定的位置不存在,就会发生IOException异常,所以在整个步骤中,需要对IO异常进行try处理。
3.FileReader
使用FileReader读取字符流步骤:
a.创建一个文件读取流对象,和指定名称的文件相关联。要保证该文件已经存在,若不存在,将会发生异常FileNotFoundException。
b.调用读取流对象的read()方法。read():一次读一个字符,且会继续往下读。
第一种方式:读取单个字符。第二种方式:通过字符数组进行读取。
c.读取后要调用close方法将流资源关闭。
4.演示案例:将c盘文件复制到d盘,
方式一:
FileReader fr=new FileReader("c:/copydemo.txt");
FileWriter fw=new FileWriter("d:/tofile.txt");
int ch=0;
while((ch=fr.read())!=-1){
fw.write(ch);
}
fr.close();
fw.close();
方式二:
FileReader fr=new FileReader("c:/copydemo.txt");
FileWriter fw=new FileWriter("d:/tofile.txt");
int ch=0;
while((ch=fr.read())!=-1){
fw.write(ch);
}
fr.close();
fw.close();
四.管道流
1.PipedInputStream与PipedOutputStream
这两个类用于在应用程序中创建管道通信。它们之间必须建立连接太可用。主要用于线程间通信。建立连接,可以在创建对象时通过构造方法完成,也可以通过调用connect方法来完成。
应用举例:分别像两个线程传入建立连接的PipedInputStream与PipedOutputStream的实例对象,然后在各自的线程中一个向管道中写入数据一个从管道中读取数据。
public class PipedStreamDemo {
public static void main(String[] args) throws Exception {
PipedOutputStream pos = new PipedOutputStream();
PipedInputStream pis = new PipedInputStream();
pos.connect(pis);//建立连接
new Thread(new Sender(pos)).start();
new Thread(new Receiver(pis)).start();
}
}
class Sender implements Runnable {
private PipedOutputStream pos;
public Sender(PipedOutputStream pos) {
this.pos = pos;
}
public void run() {
try {
while (true) {
Thread.sleep(2000);
String str = "hello,,moto";
pos.write(str.getBytes());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
class Receiver implements Runnable {
private PipedInputStream pis;
public Receiver(PipedInputStream pis) {
this.pis = pis;
}
public void run() {
try {
while(true){
byte[] buf = new byte[1024];
int len = pis.read(buf);
System.out.println(new String(buf, 0, len)+"-->reciever");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
2.PipedReader与PipedWriter
这两个类以字符的形式在应用程序中创建管道通信。使用和PipedInputStream、PipedOutputStream相同。
五.数组内存流
1.ByteArrayInputStream和ByteArrayOutputStream
1>ByteArrayInputStream和ByteArrayOutputStream用于以IO流的方式来完成对字 节数组内容的读写,来支持类似内存虚拟文件或者内存影像文件的功能。
2>我们知道对于字节数组的处理非常简单,为何还要以流的形式来处理呢?
在现实应用中有很多专门针对流处理的已经完成的功能,比如处理文件流、网络流。后来为了提高效率改为在内存中处理,但是有不想重新实现功能函数。此时可以用内存流来虚拟网络流或文件流,只要使用统一接口InputStream,OutputStream即可。
3>ByteArrayInputStream操作的源是创建对象实例时传入的数组。
4>ByteArrayOutputStream类实现了一个输出流,其中的数据被写入一个 byte 数组。缓冲区会随着数据的不断写入而自动增长。可使用 toByteArray() 和 toString() 获取数据。
5>关闭这两个流是无效的。
2.CharArrayReader和CharArrayWriter
这两个类是以字符的形式操作流,使用方法和ByteArrayInputStream、ByteArrayOutputStream相同。
3.StringReader和StringWriter
StringReader操作的源是创建对象实例时传入的字符串。
StringWriter内部操作的是StringBuffer。
其做作用和ByteArrayInputStream、ByteArrayOutputStream相同。
4.StringBufferInputStream
I/O类库提供StringBufferInputStream类的本意是把字符串转换为字节流,然后进行读操作,但是在这个类的实现中仅仅使用了字符编码的低8位,不能把字符串中的所有字符(比如中文字符)正确转换为字节,因此这个类已经被废弃,取而代之的是StringReader类
六.重视IO程序代码的复用
1.System.in连接到键盘,是InputStream类的实例对象。
System.out连接到显示器,是PrintStream类的实例对象。
2.不管各种底层物理设备用什么方式实现数据的终止点,InputStream的read方法总是返回-1来表示输入流的结束。
3.在Windows下,按下Ctrl+z组合键可以产生键盘输入流的结束标记,在Linux下则是Ctrl+D组合键来产生键盘的输入流的结束标记。
4.建议:要编写从键盘上连续读取一大段数据时,应尽量将读取数据的过程放在函数中完成,使用-1来作为键盘输入的结束点。在该函数中编写的程序代码不应直接使用System.in读取数据,而是用一个InputStream类型的形式参数对象来读取数据,然后将System.in作为实参传递给InputStream类型的形参来调用该函数。