1. File介绍
File是“文件”和“目录路径名”的抽象表示形式。
File直接继承Object,实现Serializable和Comparable接口,表示File对象既支持序列化,也可以File对象之间比较大小,可以直接存储在有序集合中(TreeSet,TreeMap)。
File构造方法列表
public File(String pathname) //给定路径名字符串创建File实例 public File(String parent, String child) public File(File parent, String child) public File(URI uri)
File常用方法列表
boolean exists() //文件是否存在,返回boolean createNewFile() //文件不存在则创建文件 mkdir() //创建文件夹 mkdirs() //递归创建文件夹 getParent() //获取父文件的路径 getParentFile() //获取父文件 getAbsolutePath() //获取绝对路径 getName() //获取文件名 isDirectory() //判断是否是一个目录 isFile() //判断是否是一个文件 lastModified() //获取上一次修改时间,返回一个long的毫秒,从1970年到修改时间 listFiles() //获取当前文件夹下所有子文件 ,返回一个File数组;
2. File实例
public class FileTest01{ public static void main(String[] args) throws IOException{ File f1 = new File("C:\\Users\\THINK\\Desktop\\JavaTest\\Temp09.txt"); // System.out.println(f1.exists()); //false // if(!f1.exists()){ // f1.createNewFile(); //文件不存在则创建文件 // } File f2 = new File("C:\\Users\\THINK\\Desktop\\JavaTest\\file"); // if(!f2.exists()){ // f2.mkdir(); //文件夹不存在则创建文件夹 // } File f3 = new File("C:\\Users\\THINK\\Desktop\\JavaTest\\a\\b\\c"); // if(!f3.exists()){ // f3.mkdirs(); //文件夹不存在则递归创建文件夹 // } File f4 = new File("C:\\Users\\THINK\\Desktop\\JavaTest\\Temp09.txt"); String f4ParentPath = f4.getParent(); System.out.println("获取父路径:"+f4ParentPath); //C:\\Users\\THINK\\Desktop\\JavaTest File f4Parent = f4.getParentFile(); System.out.println("获取父文件绝对路径:"+f4Parent.getAbsolutePath()); //C:\\Users\\THINK\\Desktop\\JavaTest System.out.println("获取文件绝对路径:"+f4.getAbsolutePath()); //C:\\Users\\THINK\\Desktop\\JavaTest\\Temp09.txt File f5 = new File("C:\\Users\\THINK\\Desktop\\JavaTest\\chapter19.iml"); System.out.println(f5.getName()); //chapter19.iml System.out.println(f5.isDirectory()); //false System.out.println(f5.isFile()); //true long time = f5.lastModified(); Date date = new Date(time); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); String str = sdf.format(date); System.out.println(str); File f6 = new File("C:\\Users\\THINK\\Desktop\\JavaTest\\file"); File[] files = f6.listFiles(); for(File f : files){ System.out.println(f.getName()); System.out.println(f.getAbsolutePath()); } } }
3. 目录拷贝
public class CopyTest03{ public static void main(String[] args){ File srcfile = new File("D:\\pyproject"); File desfile = new File("C:\\Users\\THINK\\Desktop\\JavaTest\\file1"); copyfile(srcfile,desfile); } public static void copyfile(File srcfile,File desfile){ if(srcfile.isFile()){ FileInputStream fis = null; FileOutputStream fos = null; try{ fis = new FileInputStream(srcfile); String path = (desfile.getAbsolutePath().endsWith("\\")? desfile.getAbsolutePath():desfile.getAbsolutePath()+"\\") + srcfile.getAbsolutePath().substring(3); fos = new FileOutputStream(path); byte[] bytes = new byte[1024*1024]; int readData = 0; while((readData = fis.read(bytes)) != -1){ fos.write(bytes,0,readData); } fos.flush(); }catch(FileNotFoundException e){ e.printStackTrace(); }catch(IOException e){ e.printStackTrace(); }finally{ if(fos != null){ try{ fos.close(); }catch(IOException e){ e.printStackTrace(); } } if(fis != null){ try{ fis.close(); }catch(IOException e){ e.printStackTrace(); } } } return; } File[] files = srcfile.listFiles(); for(File fi:files){ if(fi.isDirectory()){ String srcDir = fi.getAbsolutePath(); String desDir = (desfile.getAbsolutePath().endsWith("\\")? desfile.getAbsolutePath():desfile.getAbsolutePath()+"\\")+srcDir.substring(3); File newfile = new File(desDir); if(!newfile.exists()){ newfile.mkdirs(); } } copyfile(fi,desfile); } } }
4. 序列化和反序列化ObjectInputStream和ObjectOutputStream
ObjectInputStream和ObjectOutputStream的作用是对基本数据和对象进行序列化操作。
序列化就是为了保存对象的状态,反序列化可以把保存的对象状态再读出来。
序列化和反序列化时Java提供的一种专门保存/恢复对象形态的机制。
ObjectOutputStream方法列表
public ObjectOutputStream(OutputStream out) throws IOException //构造方法,参数是一个输出字节变量 public final void writeObject(Object obj) //写入对象 public void flush() //刷新
ObjectInputStream方法列表
public ObjectInputStream(InputStream in) throws IOException //构造方法,参数是一个输入字节变量 public final Object readObject() //读取对象 public void close() //关闭字节流
ObjectOutputStream序列化实例
public class ObjectOutputStreamTest01{ public static void main(String[] args) throws Exception{ ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\Users\\THINK\\Desktop\\JavaTest\\Temp10")); oos.writeObject(new Student(18,"zhangsan")); oos.flush(); oos.close(); } } class Student implements Serializable{ private int age; private String name; public Student(){} public Student(int age, String name){ this.age = age; this.name = name; } public String toString(){ return "Student{ age = " + age + ", name = " + name + "}"; } }
ObjectInputStream反序列化实例
public class ObjectInputStreamTest01{ public static void main(String[] args) throws Exception{ ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\\Users\\THINK\\Desktop\\JavaTest\\Temp10")); Object obj = ois.readObject(); System.out.println(obj); //Student{ age = 18, name = zhangsan},反序列化对象是由JVM生成的,不通过构造方法生成 ois.close(); } }
程序序列化了Student对象到文件中,序列化对象必须实现Serializable接口。
Serializable接口中没有抽象方法,只是一个表示给JVM,JVM根据标识会生成序列号。
序列化版本号SerialVersionUID
随着项目的进行可能当之前实现序列化的类发生变化时,class文件也会发生变化,没有固定的版本号这时就不能直接反序列化出对象。
public class OtherStudent implements Serializable { private int age; private String name; private String address; //Student已经发生了变化,添加了新属性 public OtherStudent(int age, String name) { this.age = age; this.name = name; } public OtherStudent() { } @Override public String toString() { return "OtherStudent{" + "age=" + age + ", name='" + name + '\'' + '}'; } }
在IDEA中再次执行反序列化(不用执行序列化),就会抛出异常,在文件流中的class字节码文件已经和classpath中的class不在兼容了。
Exception in thread "main" java.io.InvalidClassException: lewang.javase.OtherStudent; local class incompatible:
stream classdesc serialVersionUID = -9037750848657185009, local class serialVersionUID = -5541357443907101828
如果我们真的有需求在序列化后修改字段和方法,就需要指定一个SeriaVerionID告诉JVM这是同一个文件,就可以再次反序列化了。
重新修改程序,我们固定化一个序列化版本号,然后执行序列化和反序列化,此时是可以反序列化出对象的。在我们固定化版本号的基础上,我们再次添加属性,这次也可以反序列化出对象了。
public class OtherStudent implements Serializable { private static final long serialVersionUID = 1226762205367173625L; //在类名上alt+enter,可以自动生成 private int age; private String name; private String address; //在固定化版本号基础上,再次添加属性 public OtherStudent(int age, String name) { this.age = age; this.name = name; } public OtherStudent() { } @Override public String toString() { return "OtherStudent{" + "age=" + age + ", name='" + name + '\'' + '}'; } }
5. 序列化和反序列化多个对象
public class OtherUser implements Serializable { private int age; private transient String name; public OtherUser() { } public OtherUser(int age, String name) { this.age = age; this.name = name; } @Override public String toString() { return "OtherUser{" + "age=" + age + ", name='" + name + '\'' + '}'; } }
public class ObjectOutputStreamTest02 { public static void main(String[] args) throws Exception{ List<OtherUser> list = new ArrayList<>(); list.add(0,new OtherUser(11,"zhangsan")); list.add(1,new OtherUser(12,"lisi")); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("practice/src/lewang/javase/Temp02")); oos.writeObject(list); oos.flush(); oos.close(); } }
public class ObjectInputStreamTest02 { public static void main(String[] args) throws Exception{ ObjectInputStream ois = new ObjectInputStream(new FileInputStream("practice/src/lewang/javase/Temp02")); List<OtherUser> lists = (List<OtherUser>)ois.readObject(); for(OtherUser o : lists){ System.out.println(o); //OtherUser{age=11, name='null'} OtherUser{age=12, name='null'} } } }
(1)使用transient修饰属性表示该属性不参与序列化,所以在反序列化时输出为默认值;
(2)当多个对象参与序列化时可以使用List,对象需要实现Serialiable接口,ArrayList已经实现了Serizliable接口
6. IO和Properties联合使用
可以直接读取属性配置文件,一般以.properties结尾,设计经常改动的部分可以放在属性配置文件中,使用程序动态读取,不需要修改代码,也不需要重新编译,不需要重启,就可以获取改动的数据。
程序实例
public class IOProperties{ public static void main(String[] args) throws Exception{ FileReader fr = new FileReader("C:\\Users\\THINK\\Desktop\\JavaTest\\userinfo.properties"); Properties p = new Properties(); p.load(fr); String username = p.getProperty("username"); System.out.println(username); String password = p.getProperty("password"); System.out.println(password); String user = p.getProperty("user"); //null System.out.println(user); } }
#userinfo.properties文件
#key和value之间不要有空格 username=abc password=123 # properties配置文件使用#注释 # 有重复的key时,value会覆盖 #password=234 #也可以使用:分割,但是不推荐使用 #user:eee
当修改属性配置文件中password=13时,不需要重新编译,直接执行就可以获取到修改后的password值。