一、File类

1.概述:文件和目录(文件夹)路径名的抽象表示

【注意】:文件和目录的路径名可以封装成File对象,但是封装的仅仅是一个路径名,而不是真正的文件和目录

2.构造方法:

File(String pathname)
将指定的路径名转换为抽象路径名来创建File对象。
File(String parent, String child)
父路径名字符串和子路径名字符串创建File对象。(封装多级目录)
File(File parent, String child)
父抽象路径名和子路径名字符串创建File对象。 (封装多级目录)

3.创建方法:

public boolean createNewFile()
创建(真正的)文件。当文件不存在时,创建成功,返回true;当文件或同名目录存在时,创建失败,返回false。
public boolean mkdir()
创建目录。当目录不存在时,创建成功,返回true;当文件或同名目录存在时,创建失败,返回false。
public boolean mkdirs()
创建多级目录。当目录不存在时,创建成功,返回true;当文件或同名目录存在时,创建失败,返回false。
【tips】:使用createNewFile()创建文件时,如果该文件的上级目录不存在,则不能创建文件。

4.判断和获取方法:

public boolean isDirectory()
判断File封装的内容是不是目录
public boolean isFile()
判断File封装的内容是不是文件
public boolean exists()
判断File封装的内容是否存在
public String getAbsolutePath()
获取File封装内容的绝对路径字符串
public String getPath()
获取File封装内容的抽象路径名字符串
public String getName()
获取File封装内容表示的文件或目录的名称
public String[] list()
获取一个字符串数组,该数组的元素是该抽象路径下所有的文件和目录的名称字符串
public File[] listFiles()
获取一个抽象路径名数组,该数组的元素是该抽象路径下所有的文件和目录的File对象
【tips】:绝对路径和相对路径:
①绝对路径:指的是完整的路径名,如:E:\IDEA_Project\JavaSE_Project;
②相对路径:必须使用取自其他路径名的信息才能定位到的目录,如:myFile\java.txt

5.删除方法

public boolean delete()
删除由此抽象路径表示的文件或目录
【注意】:如果要删除的目录下有其他内容(目录或文件),则应该先删除目录中内容,再删除该目录。

二、IO流的概述和分类

1.概述:

IO流是用来处理设备间数据传输问题的。IO是输入/输出(Input/Output)的缩写,流是对数据传输的总称,是一个抽象概念。
【tips】:输入:读,把文件向其他地方输入数据。
                输出:写,其他地方的数据输出到文件中。

2.分类:


【tips】:①一般来说,提到IO流指的是按数据类型分类的;
                ②如果数据通过记事本打开还可以读懂里面的内容,就用字符流;读不懂,就用字节流;如果不知道该用什么流,就用字节流。

三、字节流

1.字节流抽象超类:

(1)InputStream:这个抽象类是所有表示字节输入流的类的超类;
(2)OutputStream:这个抽象类是所有表示字节输出流的类的超类。
【tips】:二者所有的子类名都以InputStream/OutputStream为后缀:

2.字节流写数据——OutputStream

(1)使用OutputStream的子类FileOutputStream来向文件File中写入数据。
(2)使用字节输出流写数据的步骤:
//第一步:创建字节输出流对象
FileOutputStream fos = new FileOutputStream("E:\\IDEA_Project\\myIO\\OutputTest.txt");
//第二步:调用字节输出流对象的写数据方法write()
fos.write(97);//a
fos.write(48);//0
//第三步:释放资源
fos.close();
【tips】:①“创建字节输出流对象”这个语句做了三个动作:
                    a.调用系统功能创建文件OutputTest.txt;
                    b.创建字节输出流对象fos;
                    c.让字节输出流对象指向创建的文件。
                ②创建字节输出流对象时包含了创建文件的动作,不需要用File类里的createNewFile()来创建文件;
                ③这里调用的write(int b)写入的是ASCII码对应的字符,所以txt文件里看到的0不是数字0而是字符0;
                ④fos.close();//释放资源:关闭了该文件输出流,并释放了与该文件输出流相关联的系统资源
(3)字节输出流写数据的三种方式:
public abstract void write(int b)
将指定的字节写入此输出流,一次写入一个字节的数据。
public abstract void write(byte[] b)
将b.length的字节从指定的字节数组写入此输出流,一次写入一个字节数组的数据。
public abstract void write(byte[] b,int index,int length)
将指定字节数组的一部分写入此输出流,一次写入从index索引开始(含index)的length长度的字节。
【tips】String中有一个byte[]  getBytes()方法,调用该方法可以得到字符串对应的字节数组。所以想写什么字符串不用去想其对应的ASCII码是什么,直接这样做:
//想把"dsaasd"写入文件输出流fos中
//使用getBytes()获取"dsaasd"对应的字节数组
fos.write("dsaasd".getBytes());
(4)如何实现换行追加写入
    1)换行:写入换行符
【tips】:不同操作系统的换行符:Windows:\r\n
                                                        Linux:\n
                                                        Mac:\r
    2)追加写入:创建字节输出流对象时选用这个带参构造方法:
public FileOutputStream(String name,boolean append)
此时,如果第二个参数是true,代表可以追加写入,即每次执行程序,字节将写入文件的末尾而不是开头。
(5)异常处理
在使用字节输出流写数据时会抛出IOException异常例如要写入的文件不存在(如盘符都不存在),将会出现FileNotFoundException异常,这时需要用try...catch...处理异常。
    1)如果像下面这种写法,创建字节输出流对象FileOutputStream fos = new FileOutputStream("Z:\\IDEA_Project\\myIO\\OutputTest2.txt");会抛出异常并被catch捕获fos.close();不执行,就会出现未释放系统资源的情况。
try {
    FileOutputStream fos = new FileOutputStream("Z:\\IDEA_Project\\myIO\\OutputTest2.txt");//抛出异常,被catch捕获,不继续往下执行write和close
    fos.write(97);
    fos.close();//不执行,未释放资源
} catch (IOException ioe) {
    ioe.printStackTrace();
}
    2)finally子句:无论try中是否抛出异常,都会执行finally语句体,所以把fos.close();放在finallyn内,这样就能保证资源一定会被释放。
第一次改进
FileOutputStream fos = null;//★
try {
    fos = new FileOutputStream("Z:\\IDEA_Project\\myIO\\OutputTest2.txt");
    fos.write(97);
} catch (IOException e) {
    e.printStackTrace();
} finally {
    try {
        fos.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}
此时,如果文件不存在,会出现NullPointerException(空指针异常)。这是因为fos初始化为null,new了一个不存在的文件后,fos还是null,所以最后finally中是null.close(),所以会出现空指针异常。
第二次改进:
FileOutputStream fos = null;
try {
    fos = new FileOutputStream("Z:\\IDEA_Project\\myIO\\OutputTest2.txt");
    fos.write(97);
} catch (IOException ioe) {
    ioe.printStackTrace();
} finally {
    if (fos != null) {//判断fos是否为空,只有当fos不为空时,才需要释放资源。
        try {
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
第三次改进:采用JDK7后的改进方案,在try...catch...的基础上,添加try(定义流对象){},并去掉finally子句,实现自动释放资源
private static void copyFiles(File srcFloder, File desFloder) {
    try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFloder));
         BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(desFloder))) {

        byte[] bys = new byte[1024];
        int len;
        while ((len = bis.read(bys)) != -1) {
            bos.write(bys, 0, len);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

3.字节流读数据——InputStream

(1)使用InputStream的子类FileInputStream从文件File中读取数据到控制台输出。
(2)使用字节输入流读数据的步骤:
 //创建字节输入流对象
FileInputStream fis = new FileInputStream("E:\\IDEA_Project\\myIO\\OutputTest.txt");

//读取数据并输出到控制台(★)
int by;
while ((by = fis.read()) != -1) {//read()读取到-1时停止。
    System.out.print((char) by);//读到的by是int型的ASCII码值,所以要强制转换成char。
}

//释放资源
fis.close();
(3)字节输入流读数据的三种方式:
public int read()
从该输入流读取一个字节的数据。当读到末尾时,返回-1
public int read(byte[] b)
将从该输入流读取到的数据存入一个字节数组,返回实际读到的字节长度当读到末尾时,返回-1
public int read(byte[] b, int off, int len)

FileInputStream fis = new FileInputStream("E:\\IDEA_Project\\myIO\\将进酒.txt");

byte[] bys = new byte[1024];
int len;//len是实际读到的字节长度
while ((len = fis.read(bys)) != -1) {
    System.out.print(new String(bys,0,len));//★将bys字节数组中的从0开始,长度为len的内容转换成String
}

fis.close();

4.字节缓冲流:BufferedOutputStream与BufferedInputStream

(1)字节缓冲输出流:BufferedOutputStream:该类实现缓冲输出流。 通过设置这样的输出流,应用程序可以向底层输出流写入字节,而不必为写入的每个字节导致底层系统的调用。
    构造方法:public BufferedOutputStream(OutputStream out)
【tips】:OutputStream是抽象类,所以()要的是其子类对象,如FileOutputStream。
(2)字节缓冲输入流:BufferedInputStream:创建BufferedInputStream将创建一个内部缓冲区数组。 当从流中读取或跳过字节时,内部缓冲区将根据需要从所包含的输入流中重新填充,一次很多字节。
    构造方法:public BufferedInputStream(InputStream in)
【注意】:①字节缓冲流使用上述两个构造方法创建对象时需要的是字节流,而不是具体的文件或路径,这是因为字节缓冲流只提供缓冲区,真正的读写操作还是要靠基本的字节流对象来操作。
                  ②字节缓冲流用完也要释放资源!
(3)使用字节缓冲流读写数据:
写:
BufferedOutputStream bos=new BufferedOutputStream(new FileOutStream("抽象路径名"));
//字节缓冲流只提供缓冲区,真正的读写操作还是要靠基本的字节流对象(FileOut/InputStream)来操作
bos.write("abcde".getBytes());

//使用字节缓冲流用完也要释放资源!
bos.close();
读:
BufferedInputStream bis=new BufferedInputStream(new FileInputStream("抽象路径名"));

byte[] bys=new byte[1024];
int len;
while((len=bis.read(bys)) != -1){
    System.out.print(new String(bye,0,len));
}

bis.close();

四、字符流

1.字符串中的编码和解码

(1)编码:将我们日常生活中的文字等字符转换成计算机中的二进制数。
    byte[] getBytes():使用平台默认的字符集将该String编码为一系列字节,将结果存储到新的字节数组中;
    byte[] getBytes(String charsetName):使用指定的字符集(如GBK、UTF-8等将该String编码为一系列字节,将结果存储到新的字节数组中
(2)解码:编码的逆过程,即将计算机中的二进制数变成文字、英文等字符。采用String构造方法的方式:
    String(byte[] bys):通过使用平台默认的字符集解码指定的字节数组来构造新的String。
    String(byte[] bys,String charsetName):通过指定的字符集解码指定的字节数组来构造新的String。
【注意】:编码和解码使用的字符集要保持一致才不会出现乱码!

2.字符流抽象超类

(1)Reader:用于字符流的抽象类,是字符输入流的超类。

(2)Writer:用于写入字符流的抽象类,是字符输出流的超类。

(3)与解码和编码问题相关的两个类:
    1)InputStreamReader:
        字符流读数据的2种方法:
public int read()
同字节流
public int read(char[] cbuf)
同字节流
    2)OutputStreamWriter:
        字符流写数据的5种方法:
public void write(int c)
同字节流
public void write(char[] cbuf)
同字节流
public abstract void write(char[] cbuf, int off, int len)
同字节流
public void write(String str)
写入一个字符串
public void write(String str, int off, int len)
写入一个字符串的一部分
【tips】:flush与close的区别:
public abstract void flush()
刷新流
public abstract void close()
先刷新,再关闭流,一旦流关闭,再调用write()或flush()将导致抛出IOException。

3.不涉及编码转码问题的文件读写

(1)FileReader:用于读取文件的类,是InputStreamReaderd的子类。
(2)FileWriter:用于写入文件的类,是OutputStreamWriter的子类。
FileWriter(File file)
创建一个新的FileWriter,给出File写入。
FileWriter(String fileName)
创建一个新的FileWriter,给定要写入的文件的名称。
【tips】:二者在使用构造方法创建对象时,都可以用File或直接用文件的名称

4.字符缓冲流:BufferedReader与BufferedWriter

(1)构造方法:

BufferedReader(Reader in)
BufferedWriter(Writer out)
(2)字符缓冲流的特有功能:
    1)BufferedWriter:
public void newLine():写一个根据操作系统而定换行符
    2)BufferedReader:。
public String readLine():读一行字符串,但是不包括换行符或回车符。 如果读到结尾,则返回值为null 。
【tips】:遇到换行符或回车符算一行。
(3)字符流复制文件最终版(使用字符缓冲流特有功能):
BufferedReader br = new BufferedReader(new FileReader("D:\\IDEA_DEMO\\将进酒.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("E:\\IDEA_Project\\myIO\\将进酒.txt"));

String line;
while ((line = br.readLine()) != null) {
    bw.write(line);
    bw.newLine();
    bw.flush();
}

br.close();
bw.close();

五、打印流

1.分类

(1)字节打印流:PrintStream(extends  FilerOutputStream)
(2)字符打印流:PrintWriter(extends Writer)

2.特点

(1)打印流只能输出数据,不能读取数据;
(2)打印流可以有自己特有的方法。

3.字节打印流PrintStream

(1)构造方法:PrintStream(String fileName)
(2)特有方法:print()、println()
【tips】:字节打印流使用继承父类的write()时,查看输出数据时会转码;使用特有方法print()、println()时,输出啥就是啥。

4.字符打印流PrintWriter

(1)构造方法:
PrintWriter(String fileName)
使用指定的文件名创建一个新的PrintWriter,但输出字符时需要手动flush刷新
PrintWriter(Writer out, boolean autoFlush)
创建一个新的PrintWriter。
  • out- 字符输出流
  • autoFlush- 如果为true,则println(),printf(),或format()方法将自动刷新输出缓冲区
【注意】:第二种构造方法即使是true,调用print()还是需要手动刷新!(是printf()能自动刷新)
(2)特有方法:print()、printf()、println()

六、特殊操作流

1.对象序列化流和对象反序列化流

(1)什么是对象序列化反序列化
    对象序列化就是把对象保存到磁盘中,或在网络中传输此对象。这种机制就是用一个字节序列来表示一个对象,该字节序列包括对象的类型、对象的数据和对象存储的属性等信息。当字节序列写到文件之后,相当于该文件中持久保存了该对象的信息。
    对象反序列化就是把字节序列从文件中读取回来,重构对象,实现反序列化。
    要实现对象的序列化和反序列化就要用到对象序列化流(ObjectOutputStream)和对象反序列化流(ObjectInputStream)。
(2)对象序列化流ObjectOutputStream
    1)构造方法:ObjectOutputStream(OutputStream out)。
    2)序列化对象的方法:void writeObject(Object obj):将指定的对象写入对象序列化流
【注意】:①一个对象要想被序列化,该对象所在的类要实现Serializable接口,不然会抛出NotSerializableException异常
                  ②Serializable接口是一个标记接口,其实现类不需要重写任何方法。
(3)对象反序列化流ObjectInputStream
    1)构造方法:ObjectInputStream(InputStream in)。
    2)反序列化对象的方法:Object readObject():从对象反序列化流中读取一个对象(所以返回值是Object类型)。
(4)序列化和反序列化过程中要注意的问题:
    1)用ObjectOutputStream序列化了一个对象后,如果该对象所属的类被修改(属性或行为),读取文件时会抛出InvalidClassException异常
    InvalidClassException异常出现的原因:序列化运行时,与每个可序列化的类关联一个版本号(serialVersionUID),它在反序列化过程中使用,以验证序列化对象的发送者和接收者是否加载了与序列化兼容的对象的类。 如果接收者已经为具有与对应发件人类别不同的serialVersionUID的对象加载了一个类,则反序列化将导致一个InvalidClassException 。
    当序列化运行时检测到类中的以下问题之一时抛出InvalidClassException异常
  • 类的串行版本与从流中读取的类描述符的类型不匹配
  • 该类包含未知的数据类型
  • 该类没有可访问的无参构造方法
    2)如何解决InvalidClassException异常
    给序列化对象所属的类显式声明一个serialVersionUID:
    private static final long serialVersionUID = 42L(自己给);
【tips】:①该字段必须是static,final和long类型;
                ②如果可序列化类没有显式声明serialVersionUID,则序列化运行时会隐式地给出该类的默认serialVersionUID值。但是为了保证不同Java编译器之间实现一致的serialVersionUID值,所有可序列化的类都应该显式声明serialVersionUID值,因为默认的serialVersionUID可能因编译器而异,因此可能会在反序列化期间导致意外的InvalidClassException 。
    3)如果让某一成员变量不被序列化?
    将该成员变量用transient修饰,被transient修饰的成员变量将不会被序列化。

2.Properties

(1)概述:Properties 类表示了一个持久的属性集。Properties 可保存在流中或从流中加载。属性列表中每个键及其对应值都是一个字符串
    public class Properties extends Hashtable<Object,Object>:Properties继承了Hashtable ,是Map体系下的集合,但不建议使用Map集合的方法,因为Properties有自己特有的方法。
【注意】:Properties虽然继承了Hashtable<Object,Object>,但它没有使用泛型,因为属性列表中的每个键及其对应的值都是字符串类型
(2)特有方法:
Object setProperty(String key, String value)
Properties对象中添加一个都是String类型的键值对。(代替put方法)
String getProperty(String key) 获取指定键对应的值。
Set<String> stringPropertyNames() 获取一个存有所有键的Set集合。
(3)Properties与IO流结合的方法(load和store):
void load(InputStream inStream)
从字节输入流读取键值对,可用来通过字节输入流把文件中的键值对存入Properties集合。
void load(Reader reader)
从字符输入流读取键值对,可用来通过字符输入流把文件中的键值对存入Properties集合。
void store(OutputStream out, String comments)
将键值对写入字节输出流,可用来将Properties集合中的键值对通过字节输出流写入文件。
void store(Writer writer, String comments)
将键值对写入字符输出流,可用来将Properties集合中的键值对通过字符输出流写入文件