一、File类:操作文件
1.构造方法
File类封装的并不是真正的文件,仅仅是一个路径名
-
绝对路径
是一个完整的路径,从盘符开始。“D:\notes\Typora\JavaSE”
-
相对路径
是一个简化的路径,从当前项目根目录开始。“模块名\a.txt”
方法名 | 说明 |
---|---|
File(String pathname) | 通过路径名字符串创建File的对象 |
File(String parent, String child) | 通过父路径名字符串 + 子路径名字符串创建File的对象 |
File(File parent, String child) | 通过父路径名字符串的包装对象 + 子路径名字符串创建File的对象 |
public class FileDemo1 {
public static void main(String[] args) {
File file = new File("D:\\test");
System.out.println(file);
File file1 = new File("D:\\test", "\\demo1");
System.out.println(file1);
File file2 = new File(file1, "\\java.txt");
System.out.println(file2);
}
}
2.创建和删除功能
这些方法是真正的操作硬盘上的文件
创建功能 | 说明 |
---|---|
public boolean createNewFile() | 创建文件,已存在则返回false |
public boolean mkdir() (舍) | 创建目录,已存在则返回false |
public boolean mkdirs() (常用) | 创建多级目录,已存在则返回false |
删除功能 | 说明 |
public boolean delete() (不走回收站) | 只能删除文件或空文件夹 |
public class FileDemo2 {
public static void main(String[] args) throws IOException {
File file = new File("D:\\test");
System.out.println(file);
File file1 = new File("D:\\test", "\\demo1");
System.out.println(file1);
File file2 = new File(file1, "\\java.txt");
System.out.println(file2);
System.out.println(file.mkdir());
System.out.println(file1.mkdirs());
System.out.println(file2.createNewFile());
// 如果要删除的目录其内部还有子目录或者文件,则删除失败。换句话说,只能从内到外一级一级删除
System.out.println(file2.delete());
System.out.println(file1.delete());
System.out.println(file.delete());
}
}
3.判断和获取功能
判断功能 | 说明 |
---|---|
public boolean isDirectory() | 判断路径名表示的File是否为目录 |
public boolean isFile() | 判断路径名表示的File是否为文件 |
public boolean exists() | 判断路径名表示的File是否存在 |
获取功能 | 说明 |
public String getAbsolutePath() | 返回此抽象路径名的绝对路径名字符串 |
public String getPath() | 获取文件或目录的路径名字符串 |
public String getName() | 获取文件或目录的名称 |
public File[] listFiles() | 将该目录下所有文件和目录组成 File类型的数组返回 数组中的每一个元素都是一个完成的路径 |
public class MethodsTest {
public static void main(String[] args) throws IOException {
File file = new File("D:\\test");
System.out.println(file);
File file1 = new File("D:\\test", "\\demo1");
System.out.println(file1);
File file2 = new File(file1, "\\java.txt");
System.out.println(file2);
System.out.println(file.mkdir());
System.out.println(file1.mkdirs());
System.out.println(file2.createNewFile());
------------------------------------------ 以上为准备工作 ----------------------------------------------------
System.out.println("file2.isDirectory() = " + file2.isDirectory());
System.out.println("file2.isFile() = " + file2.isFile());
System.out.println("file2.exists() = " + file2.exists());
System.out.println("file2.getAbsolutePath() = " + file2.getAbsolutePath());
System.out.println("file2.getPath() = " + file2.getPath());
System.out.println("file2.getName() = " + file2.getName());
File file4 = new File(file1, "JavaSE.txt");
file4.createNewFile();
File file5 = new File(file1, "JavaSE");
file5.mkdir();
// 将该目录下所有文件和目录组成 File类型的数组返回
File[] files = file1.listFiles();
for (File file3 : files) {
System.out.println("file3.getName() = " + file3.getName());
}
}
}
------------------------------------------------------------------------------------------------------------
结果:
file2.isDirectory() = false
file2.isFile() = true
file2.exists() = true
file2.getAbsolutePath() = D:\test\demo1\java.txt
file2.getPath() = D:\test\demo1\java.txt
file2.getName() = java.txt
for循环:
file3.getName() = java.txt
file3.getName() = JavaSE
file3.getName() = JavaSE.txt
二、IO流:操作文件中的具体内容
1.字节流
1.1写数据
- 注意事项:
public class OutPutDemo {
public static void main(String[] args) throws IOException {
// 1.创建<字节输出流>对象:1.构造方法中会根据传来的参数生成一个File类
// 2.告诉虚拟机要往这个新生成的文件中写入数据
// 3.若文件不存在,会自动创建。若存在,则会把文件清空
FileOutputStream fos = new FileOutputStream("D:\\test\\a.txt");
// 2.写数据:实际写入的内容是该整数在码表中对应的字符
fos.write(98);
// 3.关闭资源:否则该文件会一直处于<在用>状态,没法对它进行任何操作
fos.close();
}
}
- 三种方法:
方法名 | 说明 |
---|---|
void write(int b) | 一次写一个字节 |
void write(byte[] b) | 一次写一个字节数组 |
void write(byte[] b, int off, int len) | (字节数组,从哪开始,写入几个) |
换行方法 | |
void write( “\r\n”.getBytes() ) | windows: \r\n linux: \n mac: \r |
public class OutPutMethods {
public static void main(String[] args) throws IOException {
// 1.一次写一个字节
FileOutputStream fos1 = new FileOutputStream("D:\\test\\1.txt");
fos1.write(97); // a
fos1.close();
// 2.一次写一个字节数组
FileOutputStream fos2 = new FileOutputStream("D:\\test\\2.txt");
byte[] bytes1 = {97, 98, 99};
fos2.write(bytes1); // abc
fos2.close();
// 3.(字节数组,从哪开始,写入几个)
FileOutputStream fos3 = new FileOutputStream("D:\\test\\3.txt");
byte[] bytes2 = {97, 98, 99, 100, 101, 102, 103};
fos3.write(bytes2, 2, 4); // cdef
fos3.close();
}
}
- 异常处理:
若字节流在读写过程中发生异常导致程序终止,而资源没有释放则会导致浪费。
将释放资源的代码放入finally方法体中则一定会执行。除非JVM停止。
public class FinallyDemo {
public static void main(String[] args) throws IOException {
FileOutputStream fos = null;
try {
fos = new FileOutputStream("D:\\test\\1.txt");
fos.write(97);
} catch (IOException e) {
e.printStackTrace();
// finally语句里的代码一定会执行
} finally {
// 如果字节流对象为null,说明没有占用资源,自然不需要再去释放
if(fos != null){
// 而在释放资源的时候也可能会报错,所以再次捕获一下
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
1.2读数据
方法名 | 说明 |
---|---|
int read() | 返回的字符在码表中对应的实际整数 -1说明文件读取完毕 |
int read(byte[] b) | 返回的是实际读取的字节个数 -1说明文件读取完毕 |
- 一次读取一个字节的数据
public class IntPutDemo {
public static void main(String[] args) throws IOException {
// 创建字节输入流对象:读
FileInputStream fis = new FileInputStream("16-io\\src\\com\\skh\\output\\a.txt");
// 通过while循环一个一个字节读取
int read;
// -1说明文件已经读取完毕
while((read = fis.read()) != -1){
System.out.println(read);
}
// 释放资源
fis.close();
}
}
- 复制文件
public class CopyFileDemo {
public static void main(String[] args) throws IOException {
// 先读
FileInputStream fis = new FileInputStream("16-io\\src\\com\\skh\\output\\a.txt");
// 再写
FileOutputStream fos = new FileOutputStream("16-io\\src\\com\\skh\\intput\\a.txt");
int by;
while ((by = fis.read()) != -1) {
// 每个字节的数据读出来后立马写入目标文件
fos.write(by);
}
// 释放资源
fis.close();
fos.close();
}
}
- 复制文件(一次读写多个字节的数据)
public class CopyFileDemo2 {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("16-io\\src\\com\\skh\\output\\a.txt");
FileOutputStream fos = new FileOutputStream("16-io\\src\\com\\skh\\intput\\a.txt");
byte[] bytes = new byte[1024];
int length;
// read(byte[] b)返回的是实际读取的字节个数
while ((length = fis.read(bytes)) != -1) {
// 一次读取并写入一个字节数组
// 因为bytes数组的长度为1024,其中有很多初始化值,如果写入时不加长度限制则会写入很多乱码(空格、问号、null等)
fos.write(bytes,0,length);
}
// 释放资源
fis.close();
fos.close();
}
}
1.3字节缓冲流(重点)
- 结合缓冲流,一次读取一个数组,复制文件
public class BufferedStreamDemo {
public static void main(String[] args) throws IOException {
// 创建字节缓冲输入流对象:在底层创建了一个默认长度为8192的字节数组:new byte[8192]
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("16-io\\a.avi"));
// 创建字节缓冲输出流对象:底层同上
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("16-io\\copy.avi"));
byte[] bytes = new byte[1024];
int length;
while ((by = bis.read()) != -1){
bos.write(bytes,0,length);
}
// 释放字节缓冲流时,底层会把相应的字节流也关闭
bis.close();
bos.close();
}
}
2.字符流
处理中文乱码的问题
2.1编码表(字符集)
-
ASCII字符集(American Standard Code for Information Interchange):包括了数字,大小写字符和一些常见的标点符号。
注意:ASCII码表中是没有中文的。
-
GBK:window系统默认的码表。兼容ASCII码表,也包含了21003个汉字,并支持繁体汉字以及部分日韩文字。
注意:GBK是中国的码表,一个中文以两个字节的形式存储。但不包含世界上所有国家的文字。
-
Unicode码表:由国际组织ISO制定,是统一的万国码,计算机科学领域里的一项业界标准,容纳世界上大多数国家的所有常见文字和符号。idea和以后工作默认使用Unicode的UTF-8编解码格式。
注意: Unicode是万国码,以UTF-8编码后一个中文以三个字节的形式存储。UTF-8是一中常用的编码方式,并不是编码表。
2.2编码与解码
编码 | 说明 |
---|---|
byte[] getBytes() | 根据平台默认的字符集将String字符串编码为一系列字节存入数组中 |
byte[] getBytes(String charsetName) | 根据指定的字符集将String字符串编码为一系列字节存入数组中 |
解码 | 说明 |
String(byte[] bytes) | 根据平台默认的字符集解码字节数组来创建字符串 |
String(byte[] bytes, String charsetName) | 根据指定的字符集解码字节数组来创建字符串 |
public class CharListDemo {
public static void main(String[] args) throws UnsupportedEncodingException {
String str = "中国";
// 1.根据默认的字符集将String字符串编码为一系列字节并存入数组中
byte[] bytes1 = str.getBytes();
// 将字节数组toString一下,否则输出的是地址值
String s1 = Arrays.toString(bytes1);
// [-28, -72, -83, -27, -101, -67] 默认字符集为UTF-8,一个中文占三个字节
System.out.println(s1);
// 2.根据指定的字符集编码
byte[] bytes2 = str.getBytes("GBK");
String s2 = Arrays.toString(bytes2);
// [-42, -48, -71, -6] GBK中一个中文占两个字节
System.out.println(s2);
System.out.println("--------------------------------------------------");
byte[] bytes3 = {-28, -72, -83, -27, -101, -67};
byte[] bytes4 = {-42, -48, -71, -6};
// 3.根据平台默认的字符集解码字节数组来创建字符串
String s3 = new String(bytes3);
System.out.println(s3);
// 4.根据指定的字符集解码
String s4 = new String(bytes4, "GBK");
System.out.println(s4);
}
}
2.3写数据
Writer: 用于写入字符流的抽象父类
FileWriter: 用于写入字符流的常用子类
常用构造方法 | 说明 |
---|---|
FileWriter(File file) | 根据给定的 File 对象构造一个 FileWriter 对象 |
FileWriter(String fileName) | 根据给定的文件名构造一个 FileWriter 对象 |
主要成员方法 | 说明 |
void write(int c) | 写一个字符 |
void write(char[] cbuf) | 写入一个字符数组 |
void write(char[] cbuf, int off, int len) | 写入字符数组的一部分,可能会出现角标越界 |
void write(String str) | 写一个字符串 |
void write(String str, int off, int len) | 写一个字符串的一部分,可能会出现角标越界 |
刷新和关闭的方法 | 说明 |
---|---|
flush() | 刷新流,之后还可以继续写数据 |
close() | 关闭流,释放资源,之后不能再写数据。但是在关闭之前会先刷新流 |
public class FileWriterDemo {
public static void main(String[] args) throws IOException {
// 1.先写数据
FileWriter fw = new FileWriter("16-io\\src\\com\\skh\\charstream\\charStream.txt");
// 一次写入一个字符
fw.write(98);
fw.write("\r\n");
// 一次写入一个字符数组
char[] bytes = {99, 100, 101, 102, 103, 104};
fw.write(bytes);
fw.write("\r\n");
// 一次写入一个字符数组的一部分
fw.write(bytes, 2, 3);
fw.write("\r\n");
// 一次写入一个字符串
String s = "skhString";
fw.write(s);
fw.write("\r\n");
// 一次写入一个字符串的一部分
fw.write(s, 3, 5);
// 释放资源
fw.close();
}
}
------------------------------------------------------------------------------------------------------------
结果:
b
cdefgh
efg
skhString
eiMaS
2.4读数据
Reader: 用于读取字符流的抽象父类
FileReader: 用于读取字符流的常用子类
构造方法 | 说明 |
---|---|
FileReader(File file) | 在给定从中读取数据的 File 的情况下创建一个新 FileReader |
FileReader(String fileName) | 在给定从中读取数据的文件名的情况下创建一个新 FileReader |
方法名 | 说明 |
int read() | 一次读一个字符数据 |
int read(char[] cbuf) | 一次读一个字符数组数据 |
public class FileReaderDemo {
public static void main(String[] args) throws IOException {
// 2.再读数据
FileReader fr = new FileReader("16-io\\src\\com\\skh\\charstream\\charStream.txt");
// 一次读取一个字符
int ch;
while ((ch = fr.read()) != -1) {
System.out.print((char) ch);
}
System.out.println("-------------------------上面读取的数据,不能再次读取了-------------------------");
// 一次读取一个字符数组
char[] chars = new char[4];
int length;
while ((length = fr.read(chars)) != -1){
System.out.println(new String(chars,0,length));
}
fr.close();
}
}
------------------------------------------------------------------------------------------------------------
结果:
b
cdefgh
efg
skhString
eiMaS
2.5字符缓冲流
构造方法 | 说明 |
---|---|
BufferedWriter(Writer out) | 创建字符缓冲输出流对象 |
BufferedReader(Reader in) | 创建字符缓冲输入流对象 |
public class CharBufferedStreamDemo {
public static void main(String[] args) throws IOException {
// 字符缓冲输出流:写入的实际内容都是该整数在编码表中对应的字符
BufferedWriter bw = new BufferedWriter(new FileWriter("16-io\\a.txt"));
bw.write(97);
bw.write("\r\n");
char[] chars = {98,99,100,101,102};
bw.write(chars);
bw.write("\r\n");
bw.write(chars,1,3);
bw.write("\r\n");
bw.write("小白程序员");
bw.write("\r\n");
String str = "qwertyui";
bw.write(str,2,4);
bw.write("\r\n");
bw.flush();
bw.close();
// 字符缓冲输入流
BufferedReader br = new BufferedReader(new FileReader("16-io\\a.txt"));
char[] chars = new char[1024];
int length;
while ((length = br.read(chars)) != -1) {
System.out.println(new String(chars,0,length));
}
br.close();
}
}
特有方法 | 说明 |
---|---|
BufferedWriter void newLine() | 跨平台的回车换行 |
BufferedReader String readLine() | 一次性读一行文字,不会读取换行符。 读取完毕则为null 这里也再次证明已经读取过的数据不能再次读了! |
public class SpecificMethods {
public static void main(String[] args) throws IOException {
// 字符缓冲输出流
BufferedWriter bw = new BufferedWriter(new FileWriter("16-io\\a.txt"));
bw.write(97);
bw.newLine();
bw.close();
// 字符缓冲输入流
BufferedReader br = new BufferedReader(new FileReader("16-io\\a.txt"));
String str;
while ((str = br.readLine()) != null) {
System.out.println(str);
}
br.close();
}
}
2.6IO流小结
3.转换流
OutputStreamReader:转换输出流,是字节流到字符流的桥梁(FileInputStream → InputStreamReader → FileReader)
InputStreamWriter: 转换输入流,是字符流到字节流的桥梁 (FileOutputStream →OutStreamWriter → FileWriter)
构造方法 | 说明 |
---|---|
InputStreamReader(InputStream in) | 使用默认字符编码创建InputStreamReader对象 |
InputStreamReader(InputStream in,String chatset) | 使用指定的字符编码创建InputStreamReader对象 |
OutputStreamWriter(OutputStream out) | 使用默认字符编码创建OutputStreamWriter对象 |
OutputStreamWriter(OutputStream out,String charset) | 使用指定的字符编码创建OutputStreamWriter对象 |
public class TransformStreamDemo {
public static void main(String[] args) throws IOException {
// 转换输出流:指定字符集
// OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("16-io\\src\\com\\skh\\TransformStream\\a.txt"), "GBK");
// 转换输出流:默认字符集(UTF-8)
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("16-io\\src\\com\\skh\\TransformStream\\a.txt"));
osw.write("小白程序员");
osw.close();
// 转换输入流:默认字符集(UTF-8)
InputStreamReader isr = new InputStreamReader(new FileInputStream("16-io\\src\\com\\skh\\TransformStream\\a.txt"));
int ch;
while ((ch = isr.read()) != -1) {
System.out.println((char) ch);
}
isr.close();
}
}
4.对象操作流
4.1对象序列化:
构造方法 | 说明 |
---|---|
ObjectOutputStream(OutputStream out) | 创建一个写入指定的OutputStream的ObjectOutputStream |
写对象 | 说明 |
void writeObject(Object obj) | 将指定的对象写入ObjectOutputStream |
public class ObjectStreamDemo {
public static void main(String[] args) throws IOException {
// 对象操作输出流
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("16-io\\src\\com\\skh\\ObjectStream\\a.txt"));
Student stu = new Student("王导", 23);
oos.writeObject(stu);
oos.close();
}
}
------------------------------------------------------------------------------------------------------------
结果:
�� sr com.skh.ObjectStream.Student���峩� I ageL namet Ljava/lang/String;xp t 王导
注意:
- 一个对象要想被序列化,该对象所属的类必须必须实现Serializable 接口
- Serializable是一个标记接口,实现该接口,不需要重写任何方法
4.2对象反序列化:
构造方法 | 说明 |
---|---|
ObjectInputStream(InputStream in) | 创建从指定的InputStream读取的ObjectInputStream |
读对象 | 说明 |
Object readObject() | 从ObjectInputStream读取一个对象 |
public class ObjectStreamDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
// 对象操作输出流
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("16-io\\src\\com\\skh\\ObjectStream\\a.txt"));
Student stu = new Student("王导", 23);
oos.writeObject(stu);
oos.close();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("16-io\\src\\com\\skh\\ObjectStream\\a.txt"));
Object object = ois.readObject();
Student student = (Student)object;
System.out.println(student);
}
}
------------------------------------------------------------------------------------------------------------
结果:
Student{name='王导', age=23}
4.3 serialVersionUID
自定类时,系统会默认给创建一个serialVersionUID,改变该类中的任何信息都会改变serialVersionUID的数值
所以在序列化了一个对象后,改变类文件,再反序列化对象的时候就会报 InvalidClassException(无效的类异常)
所以要自己手动将serialVersionUID设置为静态的,设置的两个步骤如下👇:
4.4transient(很少用)
如果一个对象中的某个成员变量的值不想被序列化,可以用transient关键字修饰。
这样在进行对象序列化的时候该属性的值就不会写到文件中,反序列化读取的时候会显示对应类型的默认值。
5.Properties
-
它并不是IO体系的成员,而是集合体系的,是一个Map体系的集合类。
-
之所以将它放在IO体系中学习,是因为其中有跟IO相关的方法。
-
Properties类在创建对象时,其后是没有泛型的,意味着可以存任意类型的数据。但在实际使用中只存字符串。
5.1Properties拥有Map集合的所有方法
public class PropertiesDemo1 {
public static void main(String[] args) {
Properties prop = new Properties();
//增
prop.put("小龙女","尹志平");
prop.put("郭襄","杨过");
prop.put("黄蓉","欧阳克");
System.out.println(prop);
//删
prop.remove("郭襄");
System.out.println(prop);
//改
//put --- 如果键不存在,那么就添加,如果键存在,那么就覆盖.
prop.put("小龙女","杨过");
System.out.println(prop);
//查
Object value = prop.get("黄蓉");
System.out.println(value);
//遍历1
Set<Object> keys = prop.keySet();
for (Object key : keys) {
Object value = prop.get(key);
System.out.println(key + "=" + value);
}
//遍历2
Set<Map.Entry<Object, Object>> entries = prop.entrySet();
for (Map.Entry<Object, Object> entry : entries) {
Object key = entry.getKey();
Object value = entry.getValue();
System.out.println(key + "=" + value);
}
}
}
5.2Properties作为Map集合的特有方法
手写 <.properties>文件的规范:键=值,无空格,结尾无分号
方法名 | 说明 |
---|---|
Object setProperty(String key, String value) | 设置集合的键和值,都是String类型,底层调用 Hashtable 的 put 方法。 相当于Map方法中的 put 方法 |
String getProperty(String key) | 使用此属性列表中指定的键搜索属性。相当于Map方法中的 get 方法 |
Set stringPropertyNames() | 返回一个不可修改的键集,其中键及其对应的值是字符串 |
public class PropertiesDemo2 {
public static void main(String[] args) {
//1.Object setProperty(String key, String value)
Properties prop = new Properties();
prop.setProperty("江苏","南京");
prop.setProperty("安徽","南京");
prop.setProperty("山东","济南");
System.out.println(prop);
//2.String getProperty(String key)
String value = prop.getProperty("江苏");
System.out.println(value);
//3.Set<String> stringPropertyNames()
Set<String> keys = prop.stringPropertyNames();
for (String key : keys) {
String value = prop.getProperty(key);
System.out.println(key + "=" + value);
}
}
}
5.3Properties和IO流相结合的方法
方法名 | 说明 |
---|---|
void load(Reader reader) | 将本地文件中的键值对数据读取到集合中 |
void store(Writer writer, String comments) | 将集合中的数据以键值对的形式保存在本地,第二个参数为注释 |
public class LoadDemo {
public static void main(String[] args) throws IOException {
// 创建一个集合
Properties prop = new Properties();
// 创建一个字符输入流对象
FileReader fr = new FileReader("16-io\\src\\com\\skh\\Properties\\prop.properties");
// 调用集合的load方法:此时文件中数据就已经以键值对的形式存储到集合中了
prop.load(fr);
fr.close();
// 数据顺序是不对的?因为Map中的键是无须的,所以只需要查看内容完整即可
System.out.println(prop);
}
}
public class StoreDemo {
public static void main(String[] args) throws IOException {
Properties prop = new Properties();
prop.put("zhangsan","123");
prop.put("lisi","456");
prop.put("wangwu","789");
FileWriter fw = new FileWriter("16-io\\src\\com\\skh\\Properties\\prop.properties");
// 第二个参数为注释,会写在文件的第一行,直接传一个null即可
prop.store(fw,null);
fw.close();
}
}