IO
相对路径:在module同层,code文件夹下的文件。
绝对路径:从磁盘到最后的路径(完全体)
File对象的三种创建方法:
File file = new File("hello.txt");
File file1 = new File("D:\\Learning_Java\\Java_basics\\HighLevel\\6_IO\\code", "hello.txt");
File file2 = new File("D:\\Learning_Java\\Java_basics\\HighLevel\\6_IO\\code");
File file3 = new File(file2, "hello.txt");
}
File
File的常用功能1
- public String getAbsolutePath():获取绝对路径
- public String getPath() :获取路径
- public String getName() :获取名称
- public String getParent():获取上层文件目录路径。若无,返回null
- public long length() :获取文件长度(即:字节数)。不能获取目录的长度。
- public long lastModified() :获取最后一次的修改时间,毫秒值
- public String[] list() :获取指定目录下的所有文件或者文件目录的名称数组
- public File[] listFiles() :获取指定目录下的所有文件或者文件目录的File数组
- public boolean renameTo(File dest):把文件重命名为指定的文件路径,dest文件不能在硬盘中存在
@Test
public void test1(){
File file = new File("hello.txt");
System.out.println(file.getAbsolutePath()); //获取绝对路径
System.out.println(file.getPath());
System.out.println(file.getName());
System.out.println(file.getParent());
System.out.println(file.length()); //字节数
System.out.println(new Date(file.lastModified()));
System.out.println("********************************************************************************************");
File file1 = new File("D:\\Learning_Java\\Java_basics\\课件笔记源码资料\\新建文件夹\\1_课件\\第2部分:Java高级编程\\尚硅谷_宋红康_第13章_IO流");
String[] list = file1.list();
for(String s: list){
System.out.println(s);
}
System.out.println("********************************************************************************************");
File[] files = file1.listFiles();
for(File f: files){
System.out.println(f);
}
System.out.println("********************************************************************************************");
File file2 = new File("test.txt");
File fileTest = new File("fileTest.java");
file2.renameTo(fileTest); //更改成功,若银盘中存在fileTest则更改不成功
}
File类的常用功能2
- public boolean isDirectory():判断是否是文件目录
- public boolean isFile() :判断是否是文件
- public boolean exists() :判断是否存在
- public boolean canRead() :判断是否可读
- public boolean canWrite() :判断是否可写
- public boolean isHidden() :判断是否隐藏
创建方法
- public boolean createNewFile() :创建文件。若文件存在,则不创建,返回false
- public boolean mkdir() :创建文件目录。如果此文件目录存在,就不创建了。
如果此文件目录的上层目录不存在,也不创建。 - public boolean mkdirs() :创建文件目录。如果上层文件目录不存在,一并创建
注:File类只设计文件及文件目录的创建删除以及属性获取等方法,如果涉及文件具体内容的读取修改等就必须使用IO流。
IO流
字节流(8bit、1Byte、图片,视频)、字符流(16bit、1char、文本)
输入流、输出流
节点流:直接作用于数据的传输。处理流:作用于节点流
字节输入流、输出流: InputStream、OutputStream
字符输入流、输出流: Reader、Writer
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DHpec8yS-1672725825886)(Snipaste_2022-09-11_20-07-54.png)]
具体的流
FileReader
FileReader读入数据的小例子:
使用.read()方法
FileReader fr = null;
//必须使用try-catch-finally结构进行操作,否则可能导致fr.close()操作不关闭,导致资源浪费(严重)
try {
//先创建一个父类的File对象
File file = new File("hello.txt");
//通过File对象创建具体的FileReader对象
fr = new FileReader(file);
//调用.read()方法进行遍历文件
int data = fr.read(); //该方法会返回文件中的字符型数据,由int接收会接收为ASCII码,若文件已经读取到末尾,则会返回-1
while(data != -1){
System.out.print((char)data);
data = fr.read(); //再次调用会自动往后执行
}
} catch (IOException e) {
e.printStackTrace();
}
//流文件不能交给JVM垃圾回收机制进行回收,资源必须进行手动关闭
try {
if(fr != null)
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
使用.read(char[])方法读入数据
public void test1() {
FileReader hello = null;
try {
// 创建File对象
File file = new File("hello.txt");
//字符型对象的读取选用FileReader类
hello = new FileReader(file);
//使用.read(char[])方法,每次读入char[]长度个字符并轮巡读入,若文件到达末尾则返回-1
char[] cbuf = new char[5];
int len;
//.read(char[])方法每次读入了几个数据会作为返回值返回,这样也便于遍历了
while ((len = hello.read(cbuf)) != -1) {
for (int i = 0; i < len; i++) {
System.out.print(cbuf[i]);
}
}
} catch (IOException e) {
e.printStackTrace();
}
try{
if(hello != null){
hello.close();
}
}catch(IOException e){
e.printStackTrace();
}
}
注:
//String应该这样写,代表从0处开始取,每次取len个
String str = new String(cbuf, 0, len);
FileWriter
FileWriter允许文件不存在,其会直接创建一个新文件。
.write()方法,会对文件进行写入,默认直接覆盖源文件。
也可以在FileWriter对象的创建时调用不覆盖的构造器,使文件在写入时不覆盖源文件。
public void test2(){
//File对象的创建
FileWriter fw = null;
try {
File file = new File("hello1.txt");
//FileWriter的创建,true代表不覆盖,默认值为覆盖
fw = new FileWriter(file, true);
//写入操作
fw.write("I have a dream!\n");
fw.write("You have a dream too!\n");
} catch (IOException e) {
e.printStackTrace();
} finally {
if(fw != null)
fw.close();
}
}
}
FileReader与FileWriter联合进行复制操作
public void test3(){
FileReader fr = null;
FileWriter fw = null;
try {
File srcFile = new File("hello1.txt");
File destFile = new File("hello2.txt");
fr = new FileReader(srcFile);
fw = new FileWriter(destFile);
char[] cbuf = new char[5];
int len;
while((len = fr.read(cbuf)) != -1){
fw.write(cbuf, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(fr != null){
fr.close();
}
if(fw != null)
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
注:字符流Reader、Writer不可以操作图片
缓冲流 BufferXxxXxx
BufferFileReader、BufferFileWriter
BufferInputStream、BufferOutputStream
一个使用Buffered处理流进行复制非文本数据的小例子。
Buffered通过构造缓冲区,待缓冲区满再进行写入的方式减少写入的次数,以达到提高效率的目的。
public void copyWithBuffered(String srcPath, String destPath) {
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
//造文件
File srcFile = new File(srcPath);
File destFile = new File(destPath);
//造字节流,BufferXxx是只能作用于节点流的处理流
//先造节点流
FileInputStream fis = new FileInputStream(srcFile);
FileOutputStream fos = new FileOutputStream(destFile);
//造处理流
bis = new BufferedInputStream(fis);
bos = new BufferedOutputStream(fos);
//执行操作
byte[] buffer = new byte[1024];
int len;
while ((len = bis.read(buffer)) != -1) {
bos.write(buffer, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭流:
//由外层向内层关闭,先关闭外层,再关闭内层。
//BufferedXxx的关闭会帮助我们关闭内层的节点流
if (bos != null) {
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bis != null) {
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
BufferdReader、BufferedWriter操作小例子,复制一个文件
注意:BufferedXxxx都会在写入操作之后自动进行一个flush()操作,意为清空缓冲区。
@Test
public void test2(){
BufferedReader br = null;
BufferedWriter bw = null;
try {
br = new BufferedReader(new FileReader(new File("hello.txt")));
bw = new BufferedWriter(new FileWriter(new File("BufferHello.txt")));
// 常规方法
// char[] cbuf = new char[5];
// int len;
// while((len = br.read(cbuf)) != -1){
// bw.write(cbuf, 0, len);
// }
//String和ReadLine()方法
//ReadLine()方法会一次读取一行数据,其不会将换行符读入,所以生成的文件可能都在同一行。
String data;
while((data = br.readLine()) != null){
// bw.write(data); //不提供换行符
bw.write(data + "\n"); //手动添加
// bw.newLine(); //或这样加一个换行符
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if(br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(bw != null){
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
转换流InputStreamReader、OutputStreamWriter
InputStreamReader、OutputStreamWriter是处理流的一种,要作用于节点流。
InputStreamReader是解码:字节—>字符
OutputStreamWriter是编码:字符—>字节
两者的抽象类都是字符流,其可以使用Reader与Writer的方法。
/*
这是一个使用字节流以及装换流实现读取文本文件的程序
*/
@Test
public void test3() throws IOException {
FileInputStream fis = new FileInputStream("hello.txt"); //不创建File对象直接在这里传入路径,会调用重载的构造器在构造器内创建一个File对象
InputStreamReader isr = new InputStreamReader(fis, "UTF-8");
char[] cbuf = new char[20];
int len;
while((len = isr.read(cbuf))!= -1){
String str = new String(cbuf, 0, len);
System.out.print(str);
}
}
@Test
/*
使用转换流实现将以utf-8编码的文本文件重新编码为以gbk编码的文本文件的程序。
*/
public void test4() throws IOException {
File file1 = new File("hello.txt");
File file2 = new File("hello_gbk.txt");
FileInputStream fis = new FileInputStream(file1);
FileOutputStream fos = new FileOutputStream(file2);
InputStreamReader isr = new InputStreamReader(fis, "utf-8"); //使用UTF-8进行解码
OutputStreamWriter osw = new OutputStreamWriter(fos, "gbk"); //使用gbk进行编码
char[] cbuf = new char[20];
int len;
while((len = isr.read(cbuf)) != -1){
String str = new String(cbuf, 0, len);
osw.write(str);
}
isr.close();
osw.close();
}
标准输入输出流System.in,System.out
System.in 标准输入流,从键盘输入,其属于InputStream类
System.out 标准输出流,从控制台输出,其属于PrintStream类,该类继承于OutputStream
/*
使用标准输入输出流通过转换流实现从控制台输入小写字母,在控制台输出对应大写字母的功能
*/
public static void main(String[] args) throws IOException {
InputStreamReader isr = new InputStreamReader(System.in); //将字节流System.in转换为字符流Reader类型
BufferedReader br = new BufferedReader(isr); //将Reader字符流转换为速度更快的BufferedReader缓冲流
while(true){
String data = br.readLine();
if("e".equalsIgnoreCase(data) || "exit".equalsIgnoreCase(data)){
System.out.println("程序结束");
break;
}
String upper = data.toUpperCase();
System.out.println(upper);
}
br.close();
}
此外,还有打印流、数据流、对象流等处理流,他们都应该被套在对应的节点流上并有着自己独特的功能,与BufferedXxxx类似。
数据流可以将内存中的基本数据类型、字符串等变量写出到文件中。
对象流又可以将对象输出到文件。
对象流ObjectOutputStream、ObjectInputStream
定义
对象流可以实现将内存中的对象(或基本类型的数据)存储到文件中,以实现内存中对象的持久化(该对象所属的类必须是可序列化的)
序列化:将内存中对象保存在文件中(持久化)
**使用ObjectOutputStream
反序列化:将文件中的数据还原为对象(反持久化)
**使用ObjectInputStream
注:只有可序列化的类所实例化的对象才可以被系列化
序列化机制:序列化机制允许将内存中的Java对象持久化的保存到文件中或通过网络进行传输,并通过反序列化机制将接收到的持久化文件转换为Java对象。
关于对象可的序列化的必要条件
一、对象必须实现Serializable接口或Externalizable接口才可以进行序列化。
Serializable接口是一个标签接口,其没有任何需要实现的抽象方法。
但如果对象想要序列化,在实现Serializable接口的同时还要创建其独有的全局常量SerializableVersionUID–序列化版本号:public static final long SerializableVersionUID = 45674789461654L;(此为举例)
关于SerializableVersionUID,必须显式的在类中声明,若不声明,类也会自动创建,但类会在进行修改时自动重新生成不同的SerializeableVersionUID,故必须显式的对SerializeableVersionUID进行显式的定义(会报错)。
二、类中的所有属性也必须是可序列化的
否则序列化过程会失败,报SerializeException异常。
三、成员变量不能用static、transient修饰
被static和transient修饰的成员变量无法被序列化,其不会报错,但无法被序列化存储在文件中。
transient可以被简单理解为,不允许序列化的属性,可以使用transient关键字修饰,令其不可被序列化
举例实践
一、序列化的过程举例:
实现将String通过序列化机制持久化并保存到.dat文件中的举例:
@Test
public void testObjectOutputStream(){
//注:流所特有的必须进行的异常处理操作。
ObjectOutputStream oos = null;
try {
//创建序列化流的对象
oos = new ObjectOutputStream(new FileOutputStream("ObjectTest.dat"));
//调用对象流将String类型的对象进行持久化
oos.writeObject(new String("清河"));
oos.flush(); //显示的刷新一下
oos.writeObject(new Person("李华", 18)); //自定义类的序列化写入
oos.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(oos != null) {
//.close()操作所特有的try/catch操作
try {
oos.close(); //流所特有的关闭操作
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
二、反序列化机制的过程举例:
将刚刚通过序列化生成的.dat文件反序列化为相应的对象。
@Test
public void testObjectInputStream(){
ObjectInputStream ois = null;
try { //使用try/catch结构来避免IO出错的情况
//创建一个反序列化的输入对象流
ois = new ObjectInputStream(new FileInputStream("ObjectTest.dat"));
Object obj = ois.readObject(); //进行反序列化的读取
String str = (String)obj; //将反序列化后得到的Object对象转换为我们已知的String对象
Object obj1 = ois.readObject(); //进行自定义类的反序列化读取
Person p1 = (Person) obj1;
System.out.println(str); //输出反序列化生成的String对象
System.out.println(p1);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
if(ois != null){
ois.close(); //流同一需要进行关闭操作
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
三、自定义一个序列化的类
一个可序列化的类必须实现Serializable接口并定义序列化版本号serializableVersionUID来实现一个自定义的可序列化的类。
public class Person implements Serializable { //要实现序列化就必须实现Serializable接口
public static final long serializableVersionUID = 45674896465489L; //必须生成的序列化版本号
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
结果为:
清河
Person{name='李华', age=18}
随机存取流 RandomAccessFile
定义
特点
- RandomAccessFile流既可以作为输入流,又可以作为输出流。但如果想实现输入和输出也必须生成两个RandomAccessFile来分别进行输入与输出。
- RandomAccessFile对文件进行覆盖时,会按照顺序从前往后覆盖,而不是直接覆盖文件,也不是向后添加(见举例二)。
参数
第一个参数是所读取的文件/要写入的文件。第二个参数是文件的读取模式。
r:只读。rw:读写。rwd:读立刻写。
RandomAccessFile raf1 = new RandomAccessFile(new File("告白之夜.mp3"), "r");
常用方法
.seek()实现操作指针位置的改变(见例三)。
举例实践
一、使用RandomAccessFile进行复制操作
@Test
public void RandomAccessFileT1(){
RandomAccessFile raf1 = null; //定义raf1作为输入流
RandomAccessFile raf2 = null; //定义raf2作为输出流
try {
raf1 = new RandomAccessFile(new File("告白之夜.mp3"), "r");
raf2 = new RandomAccessFile(new File("告白之夜1.mp3"), "rw");
byte[] buffer = new byte[1024]; //将文件转换为数据并存储在byte中。
int len; //定义len以记录buffer的长度为输出做准备
/**
* .read()参数为将读取的同时将文件存入的地方
* .read()返回值为读取的流的长度。
* .read()若返回-1则代表文件为空。(第一个位置为0)
* .write()参数依次分别为:从buffer写入,从第0个位置开始写入,写入len的长度,根据.write()的构造器来判断要写入的数据类型
*/
while((len = raf1.read(buffer)) != -1){
raf2.write(buffer, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if(raf1 != null) {
try {
raf1.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(raf2 != null) {
try {
raf2.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
上述例子会将“告白之夜.mp3”文件复制一份,其名称为“告白之夜1.mp3”
二、对于RandomAccessFile进行文件覆盖的理解
//test.txt文件覆盖之前如下
abcdefg
@Test
public void RandomAccessFileT2(){
RandomAccessFile raf1 = null;
try {
raf1 = new RandomAccessFile("test1.txt", "rw"); //以读写的方式打开test1.txt
raf1.write("xyz".getBytes()); //将xyz写入test1.txt
} catch (IOException e) {
e.printStackTrace();
} finally {
if(raf1 != null) {
try {
raf1.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//test.txt在覆盖后文件内容为
xyzdefg
三、使用.seek()方法对文件操作索引进行更改
@Test
public void RandomAccessFileT3() throws IOException {
RandomAccessFile raf1 = new RandomAccessFile("test1.txt", "rw");
raf1.seek(3); //从三号索引开始进行操作
raf1.write("NNN".getBytes());
raf1.close();
}
//test1.txt的最新内容
xyzNNNg
四、如何对RandomAccessFile流对文件执行插入操作
@Test
public void RandomAccessFileT4() throws IOException {
RandomAccessFile raf1 = new RandomAccessFile("test1.txt", "rw");
raf1.seek(3);
StringBuilder bud = new StringBuilder((int)new File("test1.txt").length());
byte[] byt = new byte[30];
int len;
while((len = raf1.read(byt)) != -1){
bud.append(new String(byt, 0, len)); //byt是要存入String的数组,0是从byte数组索引的开始位置,len是存储的byte的长度。
}
//注意此时指针会到移到最后,还需要通过.seek()方法将指针移回索引3的位置
raf1.seek(3);
raf1.write("JJJ".getBytes());
raf1.write(bud.toString().getBytes());
raf1.close();
}
//Test1.txt的内容会变更为
xyzJJJNNNg
NIO、NIO.2
定义
NIO、NIO.2是java7基于File的缺点升级的新的读取文件系统的方式,其弥补了很多File方式读取文件路径的不足,比如对异常信息的详细返回等。
//使用File创建对象
import java.io.File;
File file = new File("index.html");
//使用NIO创建对象
import java.nio.file.Path;
import java.nio.file.Paths;
Path path = Paths.get("index.html");
注:Paths、Files是一些工具类,其中有创建Path对象的方法以及操作文件的方法,具体在框架中在进行详细说明
本文详细介绍了Java中的File类,包括创建File对象、获取文件属性和操作文件的方法。接着,讨论了IO流的基本概念,如字节流和字符流,以及FileReader和FileWriter的使用。还提到了缓冲流BufferedReader和BufferedWriter的效率提升作用,以及转换流InputStreamReader和OutputStreamWriter在不同编码间的转换。此外,文章讲解了标准输入输出流System.in和System.out,以及对象流的序列化和反序列化机制。最后,简要提及了RandomAccessFile的特性及其在文件操作中的应用,并介绍了JavaNIO和NIO.2作为File的增强版在处理文件系统时的优势。
603

被折叠的 条评论
为什么被折叠?



