------- android培训、java培训、期待与您交流! ----------
2-1 File类
将文件系统中的文件和文件夹封装成了对象。提供了更多的属性和行为可以对这些文件和文件夹进行操作。这些是流对象办不到的,因为流只操作数据
2-1-1 File类常见方法
1:创建。
boolean createNewFile():在指定目录下创建文件,如果该文件已存在,则不创建。而对操作文件的输出流而言,输出流对象已建立,就会创建文件,如果文件已存在,会覆盖。除非续写。
boolean mkdir():创建此抽象路径名指定的目录。
boolean mkdirs():创建多级目录。
2:删除。
boolean delete():删除此抽象路径名表示的文件或目录。
void deleteOnExit():在虚拟机退出时删除。
注意:在删除文件夹时,必须保证这个文件夹中没有任何内容,才可以将该文件夹用delete删除。
window的删除动作,是从里往外删。注意:java删除文件不走回收站。要慎用。
3:获取.
long length():获取文件大小。
String getName():返回由此抽象路径名表示的文件或目录的名称。
String getPath():将此抽象路径名转换为一个路径名字符串。
String getAbsolutePath():返回此抽象路径名的绝对路径名字符串。
String getParent():返回此抽象路径名父目录的抽象路径名,如果此路径名没有指定父目录,则返回 null。
long lastModified():返回此抽象路径名表示的文件最后一次被修改的时间。
File.pathSeparator:返回当前系统默认的路径分隔符,windows默认为 “;”。
File.Separator:返回当前系统默认的目录分隔符,windows默认为 “\”。
4:判断:
boolean exists():判断文件或者文件夹是否存在。
boolean isDirectory():测试此抽象路径名表示的文件是否是一个目录。
boolean isFile():测试此抽象路径名表示的文件是否是一个标准文件。
boolean isHidden():测试此抽象路径名指定的文件是否是一个隐藏文件。
boolean isAbsolute():测试此抽象路径名是否为绝对路径名。
5:重命名。
boolean renameTo(File dest):可以实现移动的效果。剪切+重命名。
String[] list():列出指定目录下的当前的文件和文件夹的名称。包含隐藏文件。
如果调用list方法的File 对象中封装的是一个文件,那么list方法返回数组为null。如果封装的对象不存在也会返回null。只有封装的对象存在并且是文件夹时,这个方法才有效。
示例:
class FileDemo
{
public static void main(String[] args)throws Exception
{
method4();
}
public static void method1()
{
try
{
File f = new File("c:\\shabi.txt");
f.createNewFile();
File file2 = new File("D:\\temp");// D;/temp 为一个目录
File tempFile1= file2.createTempFile("msg", ".tmp",file2);//指定目录创建
File tempFile2 = file2.createTempFile("msg", ".tmp");//系统默认目录创建
System.out.println(tempFile2.getAbsolutePath());
f.delete();
}
catch (IOException e)
{
throw new RuntimeException("nishishabi");
}
}
public static void method2()throws Exception
{
File dir = new File("nish\\b");
System.out.println(dir.mkdirs());//多级目录
System.out.println(dir.mkdir());//单个目录
}
public static void method3()throws Exception
{
File file = new File("d:\\workspace\\lianxi\\day20");
//记住在判断文件对象是否是文件或者目的时,必须要先判断该文件对象封装的内容是否存在。
//通过exists判断。
sop(file.exists());
sop(file.isDirectory());
}
public static void method4()throws Exception
{
File f = new File("d:\\workspace\\lianxi\\day20\\FileDemo.java");
sop(f.getPath());
sop(f.getAbsoluteFile());
sop(f.getAbsolutePath());
sop(f.getParent());
sop(f.getParentFile());
}
public static void sop(Object obj)
{
System.out.println(obj);
}
}
示例二:使用list和listFiles分别获取文件夹中的文件名称和文件对象
class FileDemo2
{
public static void main(String[] args) throws Exception
{
File dir = new File("d:\\workspace\\lianxi\\day20");
File[] files = dir.listFiles();
//这个方法是获取到了对象,list仅仅获取了名称
for(File f : files)
{
System.out.println(f.getName()+"::"+f.length());
}
}
public static void method1()throws Exception
{
/*File[] f = File.listRoots();
for (File f1 : f)
{
sop(f1);
}*/
File file = new File("d:\\workspace\\lianxi\\day20");
String[] str = file.list();
for (String s : str)
{
sop(s);
}
}
public static void method2()throws Exception
{
File file = new File("d:\\workspace\\lianxi\\day20");
//使用匿名内部类的方式构造过滤器
String[] str = file.list(new FilenameFilter()//很重要,要熟记。
{
public boolean accept(File dir,String name)
{
return name.endsWith(".java");
}
});
for (String s : str)
{
sop(s);
}
}
public static void sop(Object obj)
{
System.out.println(obj);
}
}
2-1-2 递归
递归:就是函数自身调用自身。
什么时候用递归呢?
当一个功能被重复使用,而每一次使用该功能时的参数不确定,都由上次的功能元素结果来确定。
简单说:功能内部又用到该功能,但是传递的参数值不确定。(每次功能参与运算的未知内容不确定)。
递归的注意事项:
1:一定要定义递归的条件。
2:递归的次数不要过多。容易出现 StackOverflowError 栈内存溢出错误。
其实递归就是在栈内存中不断的加载同一个函数。
示例一:使用递归获取一个目录中的所有文件名
class FileDemo3
{
public static void main(String[] args)
{
File dir = new File("d:\\workspace\\lianxi");
fileRecursion(dir,0);
}
public static String getLevel(int level)
{
StringBuilder sb = new StringBuilder();
for (int i=0; i<=level; i++)
{
sb.append("|--");
}
return level+" "+sb.toString();
}
public static void fileRecursion(File dir,int level)
{
sop(getLevel(level)+dir);
level++;
File[] f = dir.listFiles();
for (File f1 : f)
{
if(f1.isDirectory())
fileRecursion(f1,level);
if(f1.isFile())
sop(dir);
}
}
public static void sop(Object obj)
{
System.out.println(obj);
}
}
示例二:删除一个目录中的所有文件
class FileListDemo
{
public static void main(String[] args)throws IOException
{
File dir = new File("d:\\workspace\\lianxi");
List<File> li = new ArrayList<File>();
getFile(dir,li);
toText(li);
}
public static void getFile(File dir,List<File> li)
{
File[] f = dir.listFiles();
li.add(dir);
for (File f1 : f)
{
if(f1.isDirectory())
getFile(f1,li);
else
li.add(f1);
}
//删除时这里需要加一句delete,打印和添加到集合不需要
}
public static void toText(List<File> li)throws IOException
{
BufferedWriter bufw = new BufferedWriter(new FileWriter("sb.txt"));
for (File f : li)
{
String s = f.getAbsolutePath();
bufw.write(s);
bufw.newLine();
bufw.flush();
}
bufw.close();
}
}
2-2 Properties类
特点:1:可以持久化存储数据。2:键值都是字符串。3:一般用于配置文件。
|-- load():将流中的数据加载进集合。
原理:其实就是将读取流和指定文件相关联,并读取一行数据,因为数据是规则的key=value,所以获取一行后,通过 = 对该行数据进行切割,左边就是键,右边就是值,将键、值存储到properties集合中。
|-- store():写入各个项后,刷新输出流。
|-- list():将集合的键值数据列出到指定的目的地
Properties是hashtable的子类,也就是说它具备map集合的特点,而且它里面存储的键值对都是字符串,是集合中和IO技术相结合的集合容器。
该对象的特点:可以用于键值对形式的配置文件。
那么在加载数据时,需要数据有固定格式:键=值。
练习:限制程序运行次数。当运行次数到达5次时,给出,请您注册的提示。并不再让该程序执行。
思路:
如果使用次数已到,那么给出注册提示,很容易想到的是:计数器。
可是该计数器定义在程序中,随着程序的运行而在内存中存在,并进行自增。
可是随着该应用程序的退出,该计数器也在内存中消失了,下一次在启动该程序,又重新开始从0计数,这样不是我们想要的。
程序即使结束,该计数器的值也存在,下次程序启动在会先加载该计数器的值并加1后在重新存储起来。
所以要建立一个配置文件。用于记录该软件的使用次数,该配置文件使用键值对的形式。这样便于阅读数据,并操作数据。
键值对数据是map集合。
数据是以文件形式存储,使用io技术。那么map+io -->properties.
配置文件可以实现应用程序数据的共享。
class PropertiesTest
{
public static void main(String[] args) throws Exception
{
Properties p = new Properties();
File f = new File("count.ini");
if (! f.exists())
f.createNewFile();
// Properties p = new Properties();
//
// File f = new File("count.ini");
// if(!f.exists())
// f.createNewFile();
FileInputStream fis = new FileInputStream(f);
p.load(fis);
int num = 0;
String times = p.getProperty("time");
if (times != null)
{
num = Integer.parseInt(times);
}
if (num >=5)
{
System.out.println("使用次数已满,请充值");
return ;//返回值为void的函数中return,用来终止函数运行
}
num++;
p.setProperty("time",num+"");
FileOutputStream fos = new FileOutputStream(f);
p.store(fos,"ruanjianshiyongcishu");
fos.close();
fis.close();
}
}
2-3 IO流其他常用流
2-3-1 打印流
PrintStream可以操作目的:1:File对象。2:字符串路径。3:字节输出流。
前两个都JDK1.5版本才出现。而且在操作文本文件时,可指定字符编码了。
当目的是一个字节输出流时,如果使用的println方法,可以在printStream对象上加入一个true参数。这样对于println方法可以进行自动的刷新,而不是等待缓冲区满了再刷新。最终print方法都将具体的数据转成字符串,而且都对IO异常进行了内部处理。
既然操作的数据都转成了字符串,那么使用PrintWriter更好一些。因为PrintWrite是字符流的子类,可以直接操作字符数据,同时也可以指定具体的编码。
PrintWriter:具备了PrintStream的特点同时,还有自身特点:
该对象的目的地有四个:1:File对象。2:字符串路径。3:字节输出流。4:字符输出流。
开发时尽量使用PrintWriter。
方法中直接操作文件的第二参数是编码表。
直接操作输出流的,第二参数是自动刷新。
示例:class PrintStreamDemo
{
public static void main(String[] args) throws IOException
{
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter("abc.txt")),true);
String line = null;
while ((line = bufr.readLine()) != null)
{
if("over".equals(line))
break;
pw.println(line.toUpperCase());
}
bufr.close();
pw.close();
}
}
2-3-2 序列流
作用就是将多个读取流合并成一个读取流。实现数据合并。
表示其他输入流的逻辑串联。它从输入流的有序集合开始,并从第一个输入流开始读取,直到到达文件末尾,接着从第二个输入流读取,依次类推,直到到达包含的最后一个输入流的文件末尾为止。
这样做,可以更方便的操作多个读取流,其实这个序列流内部会有一个有序的集合容器,用于存储多个读取流对象。
该对象的构造函数参数是枚举,想要获取枚举,需要有Vector集合,但不高效。需用ArrayList,但ArrayList中没有枚举,只有自己去创建枚举对象。
但是方法怎么实现呢?因为枚举操作的是具体集合中的元素,所以无法具体实现,但是枚举和迭代器是功能一样的,所以,可以用迭代替代枚举。
合并原理:多个读取流对应一个输出流。
示例:
class SequenceDemo
{
public static void main(String[] args) throws IOException
{
Vector<FileInputStream> v = new Vector<FileInputStream>();
v.add(new FileInputStream("c:\\1.txt"));
v.add(new FileInputStream("c:\\2.txt"));
v.add(new FileInputStream("c:\\3.txt"));
SequenceInputStream sis = new SequenceInputStream(v.elements());
BufferedReader bufr = new BufferedReader(new InputStreamReader(sis));
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("c:\\4.txt")));
String line = null;
while ((line = bufr.readLine()) != null)
{
bufw.write(line);
bufw.flush();
}
bufw.close();
bufr.close();
}
}
切割原理:一个读取流对应多个输出流。
示例:
class SplitDemo
{
public static void main(String[] args)throws IOException
{
merge();
}
public static void merge()throws IOException
{
File f = new File("d:\\1.jpg");
List<FileInputStream> list = new ArrayList<FileInputStream>();
for (int i=1; i<=3; i++)
{
list.add(new FileInputStream(f+".part"+i));
}
final Iterator<FileInputStream> it = list.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("d:\\4.jpg");
byte[] buf = new byte[1024];
int len = 0;
while ((len = sis.read(buf)) != -1)
{
fos.write(buf,0,len);
}
sis.close();
fos.close();
}
public static void split()throws IOException
{
File f = new File("d:\\1.jpg");
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(f));
byte[] buf = new byte[1024*100];
int len = 0;
int count = 0;
while ((len = bis.read(buf)) != -1)
{
count++;
FileOutputStream fos = new FileOutputStream(f+".part"+count);
fos.write(buf,0,len);
fos.close();
}
}
}
2-3-3 RandomAccessFile
特点:
1:该对象即可读取,又可写入。
2:该对象中的定义了一个大型的byte数组,通过定义指针来操作这个数组。
3:可以通过该对象的getFilePointer()获取指针的位置,通过seek()方法设置指针的位置。
4:该对象操作的源和目的必须是文件。
5:其实该对象内部封装了字节读取流和字节写入流。
注意:实现随机访问,最好是数据有规律。
class RandomAccessFileDemo
{
public static void main(String[] args) throws IOException
{
writeFile();
readFile();
}
public static void writeFile()throws IOException
{
RandomAccessFile raf = new RandomAccessFile("shabi.txt","rw");
raf.seek(8);
raf.write("sb".getBytes());
raf.writeInt(27);
raf.close();
}
public static void readFile()throws IOException
{
RandomAccessFile raf = new RandomAccessFile("shabi.txt","rw");
// byte[] buf = new byte[4];
// raf.read(buf);
//
// String s = new String(buf);
//调整对象中指针。
//raf.seek(8*1);
//跳过指定的字节数
//raf.skipBytes(8);
raf.seek(4);
int age = raf.readInt();
byte[] buf = new byte[4];
raf.read(buf);
String s = new String(buf);
System.out.println(age+s);
raf.close();
}
}
2-3-4 管道流
道读取流和管道写入流可以像管道一样对接上,管道读取流就可以读取管道写入流写入的数据。
注意:需要加入多线程技术,因为单线程,先执行read,会发生死锁,因为read方法是阻塞式的,没有数据的read方法会让线程等待。
2-3-5 DataInputStream与DataOutputStream
可以用于操作基本数据类型的数据的流对象。class DataStreamDemo
{
public static void main(String[] args) throws IOException
{
//writeData();
//readData();
//writeUTFDemo();
// OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("gbk.txt"),"gbk");
//
// osw.write("你好");
// osw.close();
// readUTFDemo();
}
public static void readUTFDemo()throws IOException
{
DataInputStream dis = new DataInputStream(new FileInputStream("utf.txt"));
String s = dis.readUTF();
System.out.println(s);
dis.close();
}
public static void writeUTFDemo()throws IOException
{
DataOutputStream dos = new DataOutputStream(new FileOutputStream("utfdate.txt"));
dos.writeUTF("你好");
dos.close();
}
public static void readData()throws IOException
{
DataInputStream dis = new DataInputStream(new FileInputStream("data.txt"));
int num = dis.readInt();
boolean b = dis.readBoolean();
double d = dis.readDouble();
System.out.println("num="+num);
System.out.println("b="+b);
System.out.println("d="+d);
dis.close();
}
public static void writeData()throws IOException
{
DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));
dos.writeInt(234);
dos.writeBoolean(true);
dos.writeDouble(9887.543);
dos.close();
ObjectOutputStream oos = null;
oos.writeObject(new O());
}
}
2-3-6 ByteArrayInputStream与ByteArrayOutputStream
ByteArrayInputStream :在构造的时候,需要接收数据源,。而且数据源是一个字节数组。
ByteArrayOutputStream: 在构造的时候,不用定义数据目的,因为该对象中已经内部封装了可变长度的字节数组。
这就是数据目的地。
因为这两个流对象都操作的数组,并没有使用系统资源。
所以,不用进行close关闭。
示例:
class ByteArrayStream
{
public static void main(String[] args)
{
read();
}
public static void read()
{
ByteArrayInputStream bais = new ByteArrayInputStream("shabi".getBytes());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int buf = 0;
while((buf = bais.read()) != -1)
{
baos.write(buf);
}
System.out.println(baos.toString());
}
}