IO
● File类是java.io包中很重要的一个类;
● File类的对象可以表示文件,还可以表示目录,在程序中一个File类对象可以代 表一个文件或目录;
● File对象可以对文件或目录的属性进行操作,如:文件名、最后修改日期、文件 大小等;
● File对象无法操作文件的具体数据,即不能直接对文件进行读/写操作。
-
创建文件或目录:createNewFile(),mkdir(),mkdirs()
-
删除文件或目录:delete(),deleteOnExit()
-
判断文件或目录是否存在:exists()
-
获取文件或目录名称:getName()
-
获取文件路径:getPath()
-
获取文件绝对路径:getAbsolutePath()
-
获取文件大小:length()
-
判断是否为文件或目录:isFile(),isDirectory()
-
列出目录下的文件或目录:list(),listFiles()
-
修改文件或目录名称:renameTo()
-
判断文件或目录是否可读、可写、可执行:canRead(),canWrite(),canExecute()
-
设置文件或目录的可读、可写、可执行权限:setReadable(),setWritable(),setExecutable()
流
● 输入输出(I/O) 把电脑硬盘上的数据读到程序中,称为输入,即input,进行数据 的read 操作从程序往外部设备写数据,称为输出,即output,进行数据的write 操作
字节流与字符流
●从数据流编码格式上划分为
● 字节流
读取时以字节为单位
可以读取任意文件
● 字符
读取时以字符为单位
只能读文本文件
输入流与输出流
● 流按着数据的传输方向分为:
● 输入流:往程序中读叫输入流。
● 输出流:从程序中往外写叫输出流。
● InputStream和OutputStream的子类都是字节流 可以读写二进制文 件,主要处理音频、图片、歌曲、字节流,处理单元 为1个字节。
● Reader和Writer的子类都是字符流 主要处理字符或字符串,字符流处 理单元为1个字符。 字节流将读取到的字节数据,去指定的编码表中获取 对应文字字符。
字节流与字符流
● 字节流中常用类
字节输入流 FileInputStream
字节输出流 FileOutputStream
● 字符流中常用类
字符输入流 FileReader
字符输出流 FileWrit
●InputStream的基本方法 //读取一个字节并以整数的形式返回(0~255),如果返回-1已到输入流的末尾。 int read() throws IOException //读取一系列字节并存储到一个数组buffer, 返回实际读取的字节数,如果读取前已到输入流的 末尾返回-1 int read(byte[] buffer) throws IOException //关闭流释放内存资源 void close() throws IOException ● OutputStream的基本方法 //向输出流中写入一个字节数据,该字节数据为参数b的低8位 void write(int b) throws IOException //将一个字节类型的数组中的从指定位置(off)开始的 len个字节写入到输出流 void write(byte[] b, int off, int len) throws IOException //关闭流释放内存资源 void close() throws IOExceptio
节点流与处理流
● 根据封装类型不同流又分为
节点流 处理流
● 节点流:
如果流封装的是某种特定的数据源,如文件、字符串、字符串数组等,
直接封装数据(最基础去读数据的流)
则称为节点流。
● 处理流:
如果流封装的是其它流对象,称为处理流。 处理流提供了缓冲功能,
封装的是一个节点流对象,可以提供缓冲功能,称为处理流/包装流,缓冲字节输入流,默认缓冲区大小是8192个字节,可以自定义缓冲区大小;
提高读写效率,同时增加了一些新的方法;
总结:
-
一些常用的方法包括:
-
InputStream和OutputStream:
-
read():从输入流中读取数据。
-
write():向输出流中写入数据。
需要额外注意的地方:
-
使用InputStream和OutputStream时,需要在使用完后关闭流,以释放资源。
-
Reader和Writer:
-
read():从字符输入流中读取数据。
-
write():向字符输出流中写入数据。
需要额外注意的地方:
-
Reader和Writer是面向字符的输入输出流,适用于处理文本文件。
-
BufferedReader和BufferedWriter:
-
readLine():从缓冲字符输入流中读取一行数据。
-
write():向缓冲字符输出流中写入数据。
需要额外注意的地方:
-
使用BufferedReader和BufferedWriter可以提高读写效率,特别是对于大文件。
-
DataInputStream和DataOutputStream:
-
readInt():读取一个整数。
-
writeInt():写入一个整数。
需要额外注意的地方:
-
DataInputStream和DataOutputStream可以用于读写基本数据类型和字符串。
-
ObjectInputStream和ObjectOutputStream:
-
readObject():从对象输入流中读取对象。
-
writeObject():将对象写入对象输出流。
需要额外注意的地方:
-
注意对象的序列化和反序列化操作,需要实现Serializable接口。
-
FileReader和FileWriter:
-
read():从字符流中读取数据。
-
write():向字符流中写入数据。
需要额外注意的地方:
-
FileReader和FileWriter适用于处理文本文件,但不推荐用于处理二进制文件。
-
RandomAccessFile:
-
seek():设置文件指针位置。
-
read():从文件中读取数据。
-
write():向文件中写入数据。
需要额外注意的地方:
-
RandomAccessFile可以随机访问文件,但要注意指针位置的控制。
-
ByteArrayInputStream和ByteArrayOutputStream:
-
read():从字节数组输入流中读取数据。
-
write():向字节数组输出流中写入数据。
需要额外注意的地方:
-
ByteArrayInputStream和ByteArrayOutputStream适用于操作内存中的字节数组,不涉及文件操作。
-
这些类和方法是Java IO中常用的类和方法,用于处理文件和流的输入输出操作。
-
FileInputStream和FileOutputStream示例:
// 读取文件内容并输出到控制台 FileInputStream fis = new FileInputStream("input.txt"); int data; while ((data = fis.read()) != -1) { System.out.print((char) data); } fis.close(); // 将字符串写入文件 FileOutputStream fos = new FileOutputStream("output.txt"); String str = "Hello, World!"; fos.write(str.getBytes()); fos.close();
-
BufferedReader和BufferedWriter示例:
// 从文件中读取内容并输出到控制台 BufferedReader br = new BufferedReader(new FileReader("input.txt")); String line; while ((line = br.readLine()) != null) { System.out.println(line); } br.close(); // 将字符串写入文件 BufferedWriter bw = new BufferedWriter(new FileWriter("output.txt")); String str = "Hello, World!"; bw.write(str); bw.close();
序列化,反序列化
在Java中,序列化是指将对象转换为字节流的过程,这样可以方便地将对象保存到文件中、在网络上传输等操作。而反序列化则是将字节流转换回对象的过程。
要实现序列化,需要在类的定义中实现Serializable接口,这是一个空的接口,只是用来标记类可以被序列化。接着使用ObjectOutputStream类将对象写入字节流中,可以使用 FileOutputStream 将字节流写入文件中。反序列化的过程则是使用ObjectInputStream类从字节流中读取对象,可以使用FileInputStream从文件中读取字节流。
下面是一个简单的序列化与反序列化示例:
import java.io.*; class Student implements Serializable { private String name; private int age; public Student(String name, int age) { this.name = name; this.age = age; } } public class SerializationExample { public static void main(String[] args) { Student student = new Student("Alice", 20); try { FileOutputStream fileOut = new FileOutputStream("student.ser"); ObjectOutputStream out = new ObjectOutputStream(fileOut); out.writeObject(student); out.close(); fileOut.close(); System.out.println("Serialized data is saved in student.ser"); } catch (IOException e) { e.printStackTrace(); } Student deserializedStudent = null; try { FileInputStream fileIn = new FileInputStream("student.ser"); ObjectInputStream in = new ObjectInputStream(fileIn); deserializedStudent = (Student) in.readObject(); in.close(); fileIn.close(); } catch (IOException e) { e.printStackTrace(); return; } catch (ClassNotFoundException e) { System.out.println("Student class not found"); e.printStackTrace(); return; } System.out.println("Deserialized Student..."); System.out.println("Name: " + deserializedStudent.name); System.out.println("Age: " + deserializedStudent.age); } }
在上面的示例中,我们定义了一个Student类实现了Serializable接口,我们对Student对象进行了序列化,并将其保存在student.ser文件中。然后我们从文件中读取Student对象,并进行反序列化操作,输出反序列化后的Student对象的属性。
在JAVA中,如果一个类需要被序列化到文件中,
那么这个类需要实现Serializable接口。实现Serializable接口后,该类会自动为该类生成一个序列化编号,即serialVersionUID。这个serialVersionUID是用于在反序列化时验证类的版本一致性的,确保序列化和反序列化的类版本一致。
需要注意的地方有:
-
序列化编号一旦生成,就不能再被修改。如果修改了类的结构,如添加、删除或修改字段等,会导致旧的序列化数据无法正确反序列化。
-
如果不显式指定serialVersionUID,则JAVA会根据类的结构自动生成serialVersionUID。建议显式指定serialVersionUID,以确保序列化和反序列化的兼容性。
-
当类的serialVersionUID和序列化数据中存储的serialVersionUID不一致时,会抛出InvalidClassException异常,导致反序列化失败。
-
如果希望在类的结构发生变化时不影响反序列化的能力,可以使用Externalizable接口代替Serializable接口,并在writeExternal和readExternal方法中手动控制序列化和反序列化的过程。
在Java中进行序列化和反序列化时,有一些需要额外注意的地方,以确保顺利地实现对象的序列化和反序列化:
-
版本控制:在进行序列化时,需要注意给类添加serialVersionUID,这是一个版本号,用于实现类的版本控制。当类结构发生变化时,通过修改serialVersionUID可以避免反序列化失败。
-
transient关键字:如果类的某个字段不需要被序列化,可以使用transient关键字修饰该字段,序列化时不会将该字段写入输出流。
-
对象引用:对象引用是指类中的字段引用其他类的对象,在序列化时,如果有对象引用存在,需要确保所引用的对象也是可序列化的,否则会导致序列化失败。
-
单例类序列化:如果需要对单例类进行序列化,可以通过实现readResolve()方法来确保反序列化时返回同一实例。
-
父类序列化:如果类有父类,父类也需要实现Serializable接口,以确保父类的字段也能被序列化。
-
序列化性能:在进行序列化操作时,可以考虑使用更高效的序列化库,如Kryo、Protobuf等,以提升序列化的性能。
-
安全性:在进行反序列化操作时,需要注意安全性,避免恶意对象的反序列化攻击,可以对反序列化的输入进行校验或使用安全的序列化库。
通过注意以上这些方面,可以确保在Java中进行序列化和反序列化操作时能够安全、高效地实现对象的持久化和传输。