------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
一、File类:
1、File类概述
将文件系统中的文件和文件夹封装成了对象。提供了更多的属性和行为可以对这些文件和文件夹进行操作。这些是流对象办不到的,因为流只操作数据。
2、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。只有封装的对象存在并且是文件夹时,这个方法才有效。
代码示例:
import java.io.*;
class FileDemo{
public static voidmain(String[] args)throws IOException{
//method_1();
//method_2();
//method_3();
method_4();
}
//重命名
public static void method_4(){
File f1=newFile("C:\\Users\\Sun\\Desktop\\Test\\zz.txt");
File f2=newFile("C:\\Users\\Sun\\Desktop\\Test\\11111.txt");
sop("rename:"+f1.renameTo(f2));//将f1文件重命名为f2文件
}
//获取
public static void method_3(){
File ff=newFile("file.txt");
//File ff=newFile("anv\\file.txt");
sop("path:"+ff.getPath());//所获取的文件是可存在可不存在的
sop("Abspath:"+ff.getAbsolutePath());
sop("parent:"+ff.getParent());
//该方法返回的是绝对路径中的父目录,如果获取的是相对路径,返回null
//若相对路径中有上层目录,那么该目录就是返回结果。
}
//判断
public static voidmethod_2()throws IOException{
File dir = newFile("file.txt");
dir.mkdirs();//创建文件夹,注意:file.txt可以是文件夹名称,不要主观臆断
dir.createNewFile();//创建文件
//在判断文件对象是否是文件或者目录时,必须要先判断该文件对象封装的内容是否存在。
//通过exists判断。
if(dir.exists()){
sop("判断是否是目录 dir:"+dir.isDirectory());
sop("判断是否是文件 File:"+dir.isFile());
}
sop("判断是否为绝对路径 isAbsolute:"+dir.isAbsolute());
}
//创建、删除、判断
public static voidmethod_1()throws IOException{
//创建对象,设置在当前目录下的目录,但此时并未成功生成文件夹
File dir=newFile("aaa\\kkk\\ccc");
//dir.mkdir();//创建生成一级文件夹目录
dir.mkdirs();//创建生成多级文件夹目录
//创建文件对象,指定文件名,此时文件并未创建生成成功
File ff=newFile("file.txt");
ff.createNewFile();//创建文件
//ff.deleteOnExit();//退出时,删除创建的文件
sop("判断文件是否可执行 canExecute:"+ff.canExecute());
sop("判断文件是否存在 exists:"+ff.exists());
}
//创建File对象,可以将已有的或未出现的文件或者文件夹封装成对象。
public static voidconsMethod(){
//四种方法作用相同。重点掌握第四种。
//1、将a.txt封装成对象
File f1=newFile("F:\\abc\\a.txt");
//2、左边指父目录,右边指子文件.与f1作用一样。它们的区别是,右边的子文件是可以变化的,可以传参进去一个变量。
File f2=newFile("F:\\abc","a.txt");
//3、与上面2的作用一样。将文件的父目录封装成一个对象,然后作为参数传递
File d=newFile("F:\\abc");
File f3=newFile(d,"a.txt");
//4、实现跨平台:由于系统分隔符是分平台的,为了体现java的跨平台性,使用File.separator作为系统的目录分隔符
File f4=newFile("F:"+File.separator+"abc"+File.separator+"a.txt");
}
//输出语句方法
public static void sop(Objecto){
System.out.println(o);
}
}
3、递归:就是函数自身调用自身。
什么时候用递归呢?
当一个功能被重复使用,而每一次使用该功能时的参数不确定,都由上次的功能元素结果来确定。
简单说:功能内部又用到该功能,但是传递的参数值不确定。(每次功能参与运算的未知内容不确定)。
递归的注意事项:
1)一定要定义递归的条件。
2)递归的次数不要过多。容易出现 StackOverflowError 栈内存溢出错误。
其实递归就是在栈内存中不断的加载同一个函数。
递归示例:
import java.io.*;
//recursive 递归
class Recursive1{
public static voidmain(String[] args) {
File dir=newFile("F:\\other");
showDir(dir);//利用递归输出指定文件夹下的所有文件
//toBin(6);//调用二进制运算,演示递归
//System.out.println(getSum(100));//求和方法,演示递归
}
//求和运算
public static int getSum(intn){
if(n==1){
return n;
}else{
returnn+getSum(n-1);
}
}
//递归运算,以二进制方法为例
public static void toBin(intnum){
if(num>0){
toBin(num/2);
System.out.print(num%2);
}
}
//输出指定路径下的所有文件和文件夹
public static voidshowDir(File dir){
System.out.println(dir);//打印目录
File[]files=dir.listFiles();
for(inti=0;i<files.length;i++){
if(files[i].isDirectory()){//判断是否是目录(文件夹)
showDir(files[i]);//递归
}else{
System.out.println(files[i]);//打印指定路径下的所有文件名称
}
}
}
}
二、Properties类
1、概述
1)Properties一个可以将键值进行持久化存储的对象。是Hashtable的子类,它具备Map集合的特点。而且它里面还有存储的键值对,都是字符串,无泛型定义。是集合中和IO技术想结合的集合容器。
2)特点:
a)可用于键值对形式的配置文件
b)在加载时,需要数据有固定的格式,常用的是:键=值
c)键和值都是字符串
2、特有方法
1)设置
Object setProperty(Stringkey,String value); //设置键和值,调用Hashtable的方法put
2)获取
String getProperty(String key); //指定key搜索value
Set<String>stringPropertyName();//返回属性列表的键集,存入Set集合
3)加载流和存入流
void load(InputStream ism); //从输入字节流中读取属性列表(键和元素对)。又称将流中的数据加载进集合。
void load(Readerreader); //从输入字符流中读取属性列表(键和元素对)。又称将流中的数据加载进集合。
voidlist(PrintStreamout);//将属性列表输出到指定的输出流
voidstore(OutputStreamout,String comments); //对应load(InputStream)将属性列表(键值对)写入输出流。comments属性列表的描述。
void store(Writerwriter,String comments); //对应load(Reader)将属性列表(键值对)写入输出流。comments属性列表的描述。
Properties应用
/*
用于记录应用程序运行次数。如果使用次数已到,那么给出注册提示。
思路:1、用读取流关联文本信息文件。如果存在则读取,如果不存在,则创建
2、每次运行,将文件数据存入集合中,读取值,判断次数,如果小于等于5次,则次数增加1次,如果大于则输出提示信息。
3、将值小于等于5次的信息数据存入文件中
*/
import java.util.*;
import java.io.*;
class RunCount{
public static voidmain(String[] args)throws IOException {
int count=runCount();
if(count>5) {//如果程序被使用了超过5次,则终止使用,并提示
System.out.println("次数到了,交钱!!!!!");
return ;
}else
System.out.println("程序第"+count+"次Run!");
}
//获取程序运行的次数
public static intrunCount()throws IOException{
Properties ps=newProperties();//创建集合对象
File file=newFile("info.ini");//将文件进行封装
if(!file.exists())//判断是否存在
file.createNewFile();
FileReader fr=newFileReader(file);//将文件于读取流进行关联
ps.load(fr);//加载流中的文件数据到集合中
int count=0;//定义计数器
Stringvalue=ps.getProperty("time");//获取次数值
if(value!=null) {//如过值不等于null,则将其赋值给count
count=Integer.parseInt(value);
}
count++;//每启动一次自增
ps.setProperty("time",count+"");//将次数记录住集合
FileWriter fw=newFileWriter(file);
ps.store(fw,"");//将集合中的数据存入硬盘文件中
fr.close();//关流
fw.close();
return count;//返回程序启动的次数
}
}
三、打印流
打印流其实是装饰设计模式的一个体现,包含PrintStream和PrintWriter,提供了打印方法,可将各种类型的数据都原样打印。
1、打印流特点
1)提供了更多的功能,比如打印方法。可以直接打印任意类型的数据。
2)它有一个自动刷新机制,创建该对象,指定参数,对于指定方法可以自动刷新。
3)它使用的本机默认的字符编码.
4)该流的print方法不抛出IOException。
2、字节打印流PrintStream
1)PrintStream的构造函数
PrintStream(File file) :创建具有指定文件且不带自动行刷新的新打印流。
PrintStream(File file, String csn) :创建具有指定文件名称和字符集且不带自动行刷新的新打印流。
PrintStream(OutputStream out) :创建新的打印流。
PrintStream(OutputStream out, boolean autoFlush) :创建新的打印流。
PrintStream(OutputStream out, boolean autoFlush, String encoding) :创建新的打印流。
PrintStream(String fileName) :创建具有指定文件名称且不带自动行刷新的新打印流。
PrintStream(String fileName, String csn)
2)PrintStream可以操作目的:a)File对象。b)字符串路径。c)字节输出流。
当目的是一个字节输出流时,如果使用的println方法,可以在printStream对象上加入一个true参数。这样对于println方法可以进行自动的刷新,而不是等待缓冲区满了再刷新。最终print方法都将具体的数据转成字符串,而且都对IO异常进行了内部处理。
3、字符打印流 PrintWriter
具备了PrintStream的特点同时,还有自身特点:
1)该对象的目的地有四个:1:File对象。2:字符串路径。3:字节输出流。4:字符输出流。开发时尽量使用PrintWriter。
2)方法中直接操作文件的第二参数是编码表。直接操作输出流的,第二参数是自动刷新。
打印流示例:
import java.io.*;
class PrintStreamDemo{
public static voidmain(String[] args) throws IOException{
//键盘录入
BufferedReader bufr =
newBufferedReader(new InputStreamReader(System.in));
//打印流关联文件,自动刷新
PrintWriter out = newPrintWriter(new FileWriter("a.txt"),true);
String line = null;
while((line=bufr.readLine())!=null){
if("over".equals(line))//结束字符
break;
out.println(line.toUpperCase());
//out.flush();
}
//关流
out.close();
bufr.close();
}
}
四、SequenceInputStream:合并流
作用就是将多个读取流合并成一个读取流。实现数据合并。
表示其他输入流的逻辑串联。它从输入流的有序集合开始,并从第一个输入流开始读取,直到到达文件末尾,接着从第二个输入流读取,依次类推,直到到达包含的最后一个输入流的文件末尾为止。
这样做,可以更方便的操作多个读取流,其实这个序列流内部会有一个有序的集合容器,用于存储多个读取流对象。
该对象的构造函数参数是枚举,想要获取枚举,需要有Vector集合,但不高效。需用ArrayList,但ArrayList中没有枚举,只有自己去创建枚举对象。
合并原理:多个读取流对应一个输出流。
切割原理:一个读取流对应多个输出流。
代码示例:
1、合并流
import java.util.*;
import java.io.*;
class SequenceDemo {
//一定要注意处理异常问题,此处演示只做抛异常处理
public static voidmain(String[] args) throws IOException{
Vector<FileInputStream>v = new Vector<FileInputStream>();//利用枚举,创建一个集合
//添加(流)元素:文件
v.add(newFileInputStream("C:\\Users\\Sun\\Desktop\\Test\\1.txt"));
v.add(newFileInputStream("C:\\Users\\Sun\\Desktop\\Test\\2.txt"));
v.add(newFileInputStream("C:\\Users\\Sun\\Desktop\\Test\\3.txt"));
Enumeration<FileInputStream>en=v.elements();//可以理解为列举元素
SequenceInputStreamsis=new SequenceInputStream(en);//创建序列流,对多个流进行合并,读取
//创建输出流,关联写入文件(若不存在则创建)
FileOutputStreamfos=new FileOutputStream("C:\\Users\\Sun\\Desktop\\Test\\4.txt");
byte[] buf=newbyte[1024*5];
int len=0;
while((len=sis.read(buf))!=-1){
fos.write(buf,0,len);
//注意此处没有做流与流之间的换行问题处理。如果文件内容结尾都有换行就不存在此问题了。
//或者用fos.write("\r\n".getBytes());做换行处理。这里是指文件与文件之间内容的换行
}
fos.close();
sis.close();//关闭所有流
}
}
2、切割文件
import java.io.*;
class SpiltDemo{
public static voidmain(String[] args)throws IOException{
partPic();
}
public static voidpartPic()throws IOException{
//创建输入流,关联一个图片文件
FileInputStreamfis=new FileInputStream("C:\\Users\\Sun\\Desktop\\Test\\2.jpg");
FileOutputStream fos=null; //创建输出流,设为空,以便在循环中创建多个文件使用
byte[] b=newbyte[1024*500];
int len;
int count=1;
while((len=fis.read(b))!=-1){
fos=newFileOutputStream("C:\\Users\\Sun\\Desktop\\Test\\"+(count++)+"part.part");
//创建存入的文件,此文件随意设置,因为是没有实际作用的。
//切割成多个文件,文件名是变化的,因此用count记录变化。
fos.write(b,0,len);
fos.close();//需在循环中关闭输出流
}
fis.close();
}
}
五、RandomAccessFile类
1、概述
RandomAccessFile是直接继承Object类的一个类,此类的实例支持对随机访问文件的读取和写入。
构造函数:RandomAccessFile(Filefile,String mode),可已从它的构造函数中看出,该类只能操作文件(可以传入文件名字符串),而且操作文件还有模式:只读“r”、读写“rw”等四种模式。
注:如果模式为只读r,则不会创建文件,会去读一个已存在的文件,若文件不存在,则会出现异常。如果模式为读写rw,且该对象的构造函数要操作的文件不存在,会自动创建;如果存在,则不会覆盖。
2、特点:
1)该对象即可读取,又可写入。
2)该对象中的定义了一个大型的byte数组,通过定义指针来操作这个数组。
3)可以通过该对象的getFilePointer()获取指针的位置,通过seek()方法设置指针的位置。
4)该对象操作的源和目的必须是文件。
5)其实该对象内部封装了字节读取流和字节写入流。
代码示例:
//使用RandomAccessFileDemo进行读写操作
import java.io.*;
class RandomAccessFileDemo {
public static voidmain(String[] args)throws IOException {
//指定文件
File file =newFile("ran.txt");
//写数据
writeFile(file);
//读数据
readFile(file);
}
//读取指定文件中的数据
public static voidreadFile(File file)throws IOException {
//创建对象
RandomAccessFileraf=new RandomAccessFile(file,"r");
//设置指针位置
raf.seek(8);
//设置跳过的字节数
//raf.skipBytes(8);
//读取四个字节存入
byte[] by=new byte[4];
//读数据
raf.read(by);
//将存入数据的字节数组转换为字符串
String name=newString(by);
//根据写入规律,读取年龄,这里用到了读一个int方法
int age=raf.readInt();
System.out.println("name="+name+"age="+age);
raf.close();//关流
}
//将数据写入指定文件中
public static voidwriteFile(File file)throws IOException {
//创建对象
RandomAccessFileraf=new RandomAccessFile(file,"rw");
//写入姓名
raf.write("张三".getBytes());
//写入年龄,这里调用了写一个int方法
raf.writeInt(23);
raf.write("李四".getBytes());
raf.writeInt(100);
raf.seek(8*0);//改变指针
raf.write("小三".getBytes());
raf.writeInt(3);
raf.skipBytes(8*2);//改变指针
raf.write("王五".getBytes());
raf.writeInt(5);
raf.close();//关流
}
}
六、管道流
管道读取流和管道写入流可以像管道一样对接上,管道读取流就可以读取管道写入流写入的数据。
注意:需要加入多线程技术,因为单线程,先执行read,会发生死锁,因为read方法是阻塞式的,没有数据的read方法会让线程等待。
代码示例
public static void main(String[] args) throws IOException{
<span style="white-space:pre"> </span>PipedInputStream pipin = new PipedInputStream();
<span style="white-space:pre"> </span>PipedOutputStream pipout = new PipedOutputStream();
<span style="white-space:pre"> </span>pipin.connect(pipout);
<span style="white-space:pre"> </span>new Thread(new Input(pipin)).start();
<span style="white-space:pre"> </span>new Thread(new Output(pipout)).start();
}
七、对象的序列化
1、概述
将堆内存中的对象存入硬盘,保留对象中的数据,称之为对象的持久化(或序列化)。使用到的两个类:ObjectInputStream和ObjectOutputStream
目的:将一个具体的对象进行持久化,写入到硬盘上。
注意:静态数据不能被序列化,因为静态数据不在堆内存中,是存储在静态方法区中。
如何将非静态的数据不进行序列化?用transient 关键字修饰此变量即可。
2、如何序列化
Serializable:用于启动对象的序列化功能,可以强制让指定类具备序列化功能,该接口中没有成员,这是一个标记接口。
这个标记接口用于给序列化类提供UID。这个uid是依据类中的成员的数字签名进行运行获取的。如果不需要自动获取一个uid,可以在类中,手动指定一个名称为serialVersionUID id号。依据编译器的不同,或者对信息的高度敏感性。最好每一个序列化的类都进行手动显示的UID的指定。
代码示例:
//对象的序列化存储和读取
import java.io.*;
class ObjectStreamDemo {
public static voidmain(String[] args)throws Exception{
writeObj();//调用存储对象的方法
readObj();//读取文件,返回对象。为避免文件不存在对象,需抛异常。
}
//读取对象的方法
public static voidreadObj()throws Exception{
//创建输入流,关联读取文件
ObjectInputStreamois=new ObjectInputStream(new FileInputStream("obj.txt"));
Personp=(Person)ois.readObject();//读取文件,并返回一个对象,对象需要向下转型
System.out.println(p);
ois.close();
}
//对象的序列化存储
public static voidwriteObj()throws Exception{
//创建输出流,关联创建写入文件.
//注意存储的文件名一般不存成.txt文件,因为我们不需要看懂该文件。文件可自己定义:存储对象一般就用对象作后缀.object
ObjectOutputStream oos=
newObjectOutputStream(new FileOutputStream("obj.txt"));
oos.writeObject(newPerson("lisi",39,"kr"));//将对象存入流中
oos.close();
}
}
//对象的序列化持久存储;序列化必须实现接口Serializable
class Person implements Serializable{
//保证对象的ID号是唯一的,避免成员改变时报错,42L是可以自定义的数值。
//标识对象,序列化方便
public static final longserialVersionUID=42L;
private String name;
//若非静态成员也不想被序列化,在其前面加transient即可。
transient int age;
//注意静态是不能被序列化的。因此country的值是始终不变的。
static Stringcountry="cn";
Person(String name,intage,String country){
this.name=name;
this.age=age;
this.country=country;
}
public String toString(){
returnname+":"+age+":"+country;
}
//此代码只为演示实例化存储效果,以下省略了set、get方法。
}
八、操作字节数组的流
ByteArrayInputStream:源:内存
ByteArrayOutputStream:目的:内存。
这两个流对象不涉及底层资源调用,操作的都是内存中数组,所以不需要关闭。
直接操作字节数组就可以了,为什么还要把数组封装到流对象中呢?因为数组本身没有方法,只有一个length属性。为了便于数组的操作,将数组进行封装,对外提供方法操作数组中的元素。
对于数组元素操作无非两种操作:设置(写)和获取(读),而这两操作正好对应流的读写操作。这两个对象就是使用了流的读写思想来操作数组。
代码示例:
import java.io.*;
class ByteArrayStream {
public static voidmain(String[] args) {
//数据源。
ByteArrayInputStreambis = new ByteArrayInputStream("ABCDEFD".getBytes());
//数据目的
ByteArrayOutputStreambos = new ByteArrayOutputStream();
int by = 0;
while((by=bis.read())!=-1){
bos.write(by);
}
System.out.println(bos.size());
System.out.println(bos.toString());
// bos.writeTo(newFileOutputStream("a.txt"));需要抛异常或者处理异常,否则此句编译不通过。
}
}