File类的对象是文件或目录的路径名的抽象表示。
我们可以从中创建一个File
对象
我们可以使用File类的以下构造函数之一创建一个文件:
File(String pathname)
File(File parent, String child)
File(String parent, String child)
File(URI uri)
如果我们有一个文件路径名字符串test.txt,我们可以创建一个抽象路径名作为下面的代码。
File dummyFile = new File("test.txt");
名为test.txt的文件不必存在,以使用此语句创建File对象。
dummyFile对象表示抽象路径名,它可能指向也可能不指向文件系统中的真实文件。
File类有几个方法来处理文件和目录。
使用File对象,我们可以创建新文件,删除现有文件,重命名文件,更改文件的权限等。
File类中的isFile()和isDirectory()告诉File对象是否表示文件或目录。
JVM的当前工作目录是根据我们如何运行java命令来设置的。
我们可以通过读取user.dir系统属性来获取JVM的当前工作目录,如下所示:
String workingDir = System.getProperty("user.dir");
使用System.setProperty()方法更改当前工作目录。
System.setProperty("user.dir", "C:\\myDir");
要在Windows上指定C:\ test作为user.dir系统属性值,我们运行如下所示的程序:
java -Duser.dir=C:\test your-java-class
我们可以使用File类的exists()方法检查File对象的抽象路径名是否存在。
boolean fileExists = dummyFile.exists();
完整源代码
import java.io.File;
public class Main {
public static void main(String[] argv) {
// Create a File object
File dummyFile = new File("dummy.txt");
// Check for the file"s existence
boolean fileExists = dummyFile.exists();
if (fileExists) {
System.out.println("The dummy.txt file exists.");
} else {
System.out.println("The dummy.txt file does not exist.");
}
}
}
上面的代码生成以下结果。
绝对路径在文件系统上唯一标识文件。规范路径是唯一标识文件系统上文件的最简单路径。
我们可以使用getAbsolutePath()和getCanonicalPath()方法来分别获得由File对象表示的绝对路径和规范路径。
import java.io.File;
import java.io.IOException;
public class Main {
public static void main(String[] args) {
printFilePath("dummy.txt");
printFilePath(".." + File.separator + "notes.txt");
}
public static void printFilePath(String pathname) {
File f = new File(pathname);
System.out.println("File Name: " + f.getName());
System.out.println("File exists: " + f.exists());
System.out.println("Absolute Path: " + f.getAbsolutePath());
try {
System.out.println("Canonical Path: " + f.getCanonicalPath());
}
catch (IOException e) {
e.printStackTrace();
}
}
}
上面的代码生成以下结果。
不同的操作系统使用不同的字符来分隔路径名中的两个部分。
例如,Windows在路径名中使用反斜杠(\)作为名称分隔符,而UNIX使用正斜杠(/)。
File类定义了一个名为分隔符Char的常量,它是系统相关的名称分隔符。
我们可以使用File.separator Char常量来获取名称分隔符作为字符。
File.separator
常量将我们的名称分隔符作为String。
在程序中使用名称分隔符将使您的Java代码在不同的平台上工作。
我们可以使用File类的createNewFile()方法创建一个新文件:
File dummyFile = new File("test.txt");
boolean fileCreated = dummyFile.createNewFile();
该createNewFile()方法创建一个新的空文件,如果有指定名称的文件不存在。
如果文件已成功创建,则返回true;否则,返回false。如果发生I/O错误,该方法将抛出IOException。
我们还可以在默认的临时文件目录或目录中创建一个临时文件。要在默认临时目录中创建临时文件,请使用File类的createTempFile()静态方法,该方法接受前缀和后缀以生成临时文件名。
File tempFile = File.createTempFile("abc", ".txt");
我们可以使用mkdir()或mkdirs()方法创建一个新目录。
仅当路径名中指定的父目录已存在时,mkdir()方法才创建目录。
File newDir = new File("C:\\users\\home");
只有当C:\users目录已经存在时,newDir.mkdir()方法才会创建主目录。
newDir.mkdirs()方法将创建users目录(如果它不存在于C:驱动器中),它将在C:\users目录下创建主目录。
我们需要使用File类的delete()方法来删除文件/目录。
目录必须为空,我们才能删除它。
如果文件/目录被删除,该方法返回true; 否则,返回false。
我们还可以延迟删除文件,直到JVM通过使用deleteOnExit()方法终止。
如果我们在程序中创建临时文件,当程序退出时要删除,这将非常有用。
File dummyFile = new File("dummy.txt");
dummyFile.delete();
File dummyFile = new File("dummy.txt");
dummyFile.deleteOnExit();
要重命名文件,我们可以使用renameTo()方法,它使用一个File对象来表示新文件:
boolean fileRenamed = oldFile.renameTo(newFile);
完整的源代码
import java.io.File;
public class Main {
public static void main(String[] argv) {
// Rename old-dummy.txt to new_dummy.txt
File oldFile = new File("old_dummy.txt");
File newFile = new File("new_dummy.txt");
boolean fileRenamed = oldFile.renameTo(newFile);
if (fileRenamed) {
System.out.println(oldFile + " renamed to " + newFile);
} else {
System.out.println("Renaming " + oldFile + " to " + newFile
+ " failed.");
}
}
}
如果文件的重命名成功,renameTo()方法返回true;否则,返回false。
我们需要检查返回值,以确保重命名成功。
上面的代码生成以下结果。
File对象是不可变的。创建后,它始终表示相同的路径名。当我们重命名文件时,旧的File对象仍然代表原始的路径名。
File对象表示路径名,而不是文件系统中的实际文件。
以下代码显示了如何进行文件创建,删除和重命名。
import java.io.File;
public class Main {
public static void main(String[] args) throws Exception {
File newFile = new File("my_new_file.txt");
printFileDetails(newFile);
// Create a new file
boolean fileCreated = newFile.createNewFile();
if (!fileCreated) {
System.out.println(newFile + " could not be created.");
}
printFileDetails(newFile);
// Delete the new file
newFile.delete();
System.out.println("After deleting the new file:");
printFileDetails(newFile);
// recreate the file
newFile.createNewFile();
printFileDetails(newFile);
// Let"s tell the JVM to delete this file on exit
newFile.deleteOnExit();
System.out.println("After using deleteOnExit() method:");
printFileDetails(newFile);
// Create a new file and rename it
File firstFile = new File("my_first_file.txt");
File secondFile = new File("my_second_file.txt");
fileCreated = firstFile.createNewFile();
if (fileCreated || firstFile.exists()) {
printFileDetails(firstFile);
printFileDetails(secondFile);
boolean renamedFlag = firstFile.renameTo(secondFile);
if (!renamedFlag) {
System.out.println("Could not rename " + firstFile);
}
printFileDetails(firstFile);
printFileDetails(secondFile);
}
}
public static void printFileDetails(File f) {
System.out.println("Absolute Path: " + f.getAbsoluteFile());
System.out.println("File exists: " + f.exists());
}
}
上面的代码生成以下结果。
File类包含让我们获取/设置文件和目录的属性的方法。
我们可以设置分别使用setReadOnly(),setReadable(),setWritable()和setExecutable()方法将文件设置为只读,可读,可写和可执行。
我们可以使用lastModified()和setLastModified()方法来获取和设置文件的最后修改日期和时间。
我们可以使用isHidden()方法检查文件是否被隐藏。
我们可以使用File类的length()方法获取文件的大小(以字节为单位)。
File myFile = new File("myfile.txt");
long fileLength = myFile.length();
如果File对象表示不存在的文件,则length()方法返回零。
length()方法的返回类型是long,而不是int。
我们可以使用File类的listRoots()静态方法获取文件系统中可用根目录的列表。 它返回一个File对象数组。
File[] roots = File.listRoots();
以下代码显示如何列出所有可用的根目录。
import java.io.File;
public class Main {
public static void main(String[] args) {
File[] roots = File.listRoots();
System.out.println("List of root directories:");
for (File f : roots) {
System.out.println(f.getPath());
}
}
}
我们可以使用File类的list()或listFiles()方法列出目录中的所有文件和目录。
list()方法返回一个String数组,而listFiles()方法返回一个File数组。
我们还可以使用这些方法的文件过滤器从返回的结果中排除一些文件和目录。
以下代码显示如何列出目录中的所有文件和目录。
import java.io.File;
public class Main {
public static void main(String[] args) {
// Change the dirPath value to list files from your directory
String dirPath = "C:\\";
File dir = new File(dirPath);
File[] list = dir.listFiles();
for (File f : list) {
if (f.isFile()) {
System.out.println(f.getPath() + " (File)");
} else if (f.isDirectory()) {
System.out.println(f.getPath() + " (Directory)");
}
}
}
}
上面的代码生成以下结果。
要从列表中排除扩展名为.SYS的所有文件,我们可以使用由功能接口FileFilter的实例表示的文件过滤器来实现。
它包含一个accept()方法,它将File作为参数列出,如果应该列出文件,则返回true。返回false不会列出文件。
以下代码创建一个文件过滤器,将过滤扩展名为.SYS的文件。
FileFilter filter = file -> {
if (file.isFile()) {
String fileName = file.getName().toLowerCase();
if (fileName.endsWith(".sys")) {
return false;
}
}
return true;
};
以下代码创建两个文件过滤器 - 一个仅过滤文件,另一个仅过滤目录:
// Filters only files
FileFilter fileOnlyFilter = File::isFile;
// Filters only directories
FileFilter dirOnlyFilter = File::isDirectory;
以下代码显示如何使用FileFilter过滤文件。
import java.io.File;
import java.io.FileFilter;
public class Main {
public static void main(String[] args) {
String dirPath = "C:\\";
File dir = new File(dirPath);
// Create a file filter to exclude any .SYS file
FileFilter filter = file -> {
if (file.isFile()) {
String fileName = file.getName().toLowerCase();
if (fileName.endsWith(".sys")) {
return false;
}
}
return true;
};
File[] list = dir.listFiles(filter);
for (File f : list) {
if (f.isFile()) {
System.out.println(f.getPath() + " (File)");
} else if (f.isDirectory()) {
System.out.println(f.getPath() + " (Directory)");
}
}
}
}
上面的代码生成以下结果。
Java输入流
抽象基本组件是InputStream类。
InputStream
|
+--FileInputStream
|
+--ByteArrayInputStream
|
+--PipedInputStream
|
+--FilterInputStream
|
+--BufferedInputStream
|
+--PushbackInputStream
|
+--DataInputStream
|
+--ObjectInputStream
我们有FileInputStream,ByteArrayInputStream和PipedInputStream,FilterInputStream的具体类。
超类InputStream包含从输入流读取数据的基本方法,所有具体类都支持这些方法。
对输入流的基本操作是从其读取数据。 InputStream类中定义的一些重要方法在下表中列出。
ID | 方法/说明 |
---|---|
1 | read() 读取一个字节并将读取的字节作为int返回。 当到达输入流的结尾时,它返回-1。 |
2 | read(byte[] buffer) 读取最大值直到指定缓冲区的长度。 它返回在缓冲区中读取的字节数。 如果到达输入流的结尾,则返回-1。 |
3 | read(byte [] buffer,int offset,int length) 读取最大值到指定长度字节。 数据从偏移索引开始写入缓冲区。 它返回读取的字节数或-1,如果到达输入流的结束。 |
3 | close() 关闭输入流 |
4 | available() 返回可以从此输入流读取但不阻塞的估计字节数。 |
在Java I/O中,流意味着数据流。流中的数据可以是字节,字符,对象等。
要从文件读取,我们需要创建一个FileInputStream类的对象,它将表示输入流。
String srcFile = "test.txt";
FileInputStream fin = new FileInputStream(srcFile);
如果文件不存在,FileInputStream类的构造函数将抛出FileNotFoundException异常。要处理这个异常,我们需要将你的代码放在try-catch块中,如下所示:
try {
FileInputStream fin = new FileInputStream(srcFile);
}catch (FileNotFoundException e){
// The error handling code goes here
}
FileInputStream类有一个重载的read()方法从文件中读取数据。我们可以一次读取一个字节或多个字节。
字节:byte
- 一字节等于八位,即8位二进制数。即 1byte = 8 bit
- 一个ASCII码就是一个字节。
- UTF-8编码:一个英文字符等于一个字节,一个中文(含繁体)等于三个字节。中文标点占三个字节,英文标点占一个字节
- Unicode编码:一个英文等于两个字节,一个中文(含繁体)等于两个字节。中文标点占两个字节,英文标点占两个字节
read()方法的返回类型是int,虽然它返回一个字节值。如果到达文件的结尾,则返回-1。
我们需要将返回的int值转换为一个字节,以便从文件中读取字节。通常,我们在循环中一次读取一个字节。
最后,我们需要使用close()方法关闭输入流。
// Close the input stream
fin.close();
close()方法可能抛出一个IOException,因此,我们需要在try-catch块中包含这个调用。
try {
fin.close();
}catch (IOException e) {
e.printStackTrace();
}
通常,我们在try块中构造一个输入流,并在finally块中关闭它,以确保它在我们完成后总是关闭。
所有输入/输出流都可自动关闭。我们可以使用try-with-resources来创建它们的实例,所以无论是否抛出异常,它们都会自动关闭,避免需要显式地调用它们的close()方法。
以下代码显示使用try-with-resources创建文件输入流:
String srcFile = "test.txt";
try (FileInputStream fin = new FileInputStream(srcFile)) {
// Use fin to read data from the file here
}
catch (FileNotFoundException e) {
// Handle the exception here
}
以下代码显示了如何从文件输入流一次读取一个字节。
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class Main {
public static void main(String[] args) {
String dataSourceFile = "asdf.txt";
try (FileInputStream fin = new FileInputStream(dataSourceFile)) {
byte byteData;
while ((byteData = (byte) fin.read()) != -1) {
System.out.print((char) byteData);
}
} catch (FileNotFoundException e) {
;
} catch (IOException e) {
e.printStackTrace();
}
}
}
BufferedInputStream通过缓冲数据向输入流添加功能。
它维护一个内部缓冲区
以存储从底层输入流读取的字节。
我们创建缓冲区输入流如下:
String srcFile =“test.txt";
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile));
以下代码显示如何使用BufferedInputStream从文件读取。
import java.io.BufferedInputStream;
import java.io.FileInputStream;
public class Main {
public static void main(String[] args) {
String srcFile = "test.txt";
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(srcFile))) {
// Read one byte at a time and display it
byte byteData;
while ((byteData = (byte) bis.read()) != -1) {
System.out.print((char) byteData);
}
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
上面的代码生成以下结果。
PushbackInputStream向输入流添加功能,允许我们使用其unread()方法推回读取的字节。
有三个版本的unread()方法。一个让我们推回一个字节,另外两个让我们推回多个字节。
import java.io.FileInputStream;
import java.io.PushbackInputStream;
public class Main {
public static void main(String[] args) {
String srcFile = "test.txt";
try (PushbackInputStream pis = new PushbackInputStream(new FileInputStream(
srcFile))) {
byte byteData;
while ((byteData = (byte) pis.read()) != -1) {
System.out.print((char) byteData);
pis.unread(byteData);
// Reread the byte we unread
byteData = (byte) pis.read();
System.out.print((char) byteData);
}
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
上面的代码生成以下结果。
DataInputStream可以从输入流中读取指定Java基本数据类型值。
DataInputStream类包含读取数据类型值的读取方法。
例如,要读取int值,它包含一个readInt()方法;
读取char值,它有一个readChar()方法等。
它还支持使用readUTF()方法读取字符串。
以下代码显示了如何从文件读取原始值和字符串。
import java.io.DataInputStream;
import java.io.FileInputStream;
public class Main {
public static void main(String[] args) {
String srcFile = "primitives.dat";
try (DataInputStream dis = new DataInputStream(new FileInputStream(srcFile))) {
// Read the data in the same order they were written
int intValue = dis.readInt();
double doubleValue = dis.readDouble();
boolean booleanValue = dis.readBoolean();
String msg = dis.readUTF();
System.out.println(intValue);
System.out.println(doubleValue);
System.out.println(booleanValue);
System.out.println(msg);
} catch (Exception e) {
e.printStackTrace();
}
}
}
上面的代码生成以下结果:
java.io.FileNotFoundException: primitives.dat (No such file or directory)
at java.io.FileInputStream.open(Native Method)
at java.io.FileInputStream.<init>(FileInputStream.java:146)
at java.io.FileInputStream.<init>(FileInputStream.java:101)
at file.main(file.java:8)
Java 输出流
在抽象超类OutputStream中定义了三个重要的方法:write(),flush()和close()。
write()方法将字节写入输出流。
它有三个版本,允许我们一次写一个字节或多个字节。
flush()方法用于将任何缓冲的字节刷新到数据宿。
close()方法关闭输出流。
要使用BufferedOutputStream装饰器以更好的速度写入文件,请使用以下语句:
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("your output file path"));
要将数据写入ByteArrayOutputStream,请使用
ByteArrayOutputStream baos = new ByteArrayOutputStream();
baos.write(buffer); // buffer is a byte array
要写入文件,我们需要创建一个FileOutputStream类的对象,它将表示输出流。
// Create a file output stream
String destFile = "test.txt";
FileOutputStream fos = new FileOutputStream(destFile);
当写入文件时,如果文件不存在,Java会尝试创建文件。我们必须准备好处理这个异常,将代码放在try-catch块中,如下所示:
try {
FileOutputStream fos = new FileOutputStream(srcFile);
}catch (FileNotFoundException e){
// Error handling code goes here
}
如果文件包含数据,数据将被擦除。为了保留现有数据并将新数据附加到文件,我们需要使用FileOutputStream类的另一个构造函数,它接受一个布尔标志,用于将新数据附加到文件。
要将数据追加方式加到文件,请在第二个参数中传递true,使用以下代码。
FileOutputStream fos = new FileOutputStream(destFile, true);
FileOutputStream类有一个重载的write()方法将数据写入文件。我们可以使用不同版本的方法一次写入一个字节或多个字节。
通常,我们使用FileOutputStream写入二进制数据。
要向输出流中写入诸如“Hello"的字符串,请将字符串转换为字节。
String类有一个getBytes()方法,该方法返回表示字符串的字节数组。我们给FileOutputStream写一个字符串如下:
String text = "Hello";
byte[] textBytes = text.getBytes();
fos.write(textBytes);
要插入一个新行,使用line.separator系统变量如下。
String lineSeparator = System.getProperty("line.separator");
fos.write(lineSeparator.getBytes());
我们需要使用flush()方法刷新输出流。
fos.flush();
刷新输出流指示如果任何写入的字节被缓冲,则它们可以被写入数据宿。
关闭输出流类似于关闭输入流。我们需要使用close()方法关闭输出流。
// Close the output stream
fos.close();
close()方法可能抛出一个IOException异常。如果我们希望自动关闭tit,请使用try-with-resources创建输出流。
以下代码显示如何将字节写入文件输出流。
import java.io.File;
import java.io.FileOutputStream;
public class Main {
public static void main(String[] args) {
String destFile = "luci2.txt";
// Get the line separator for the current platform
String lineSeparator = System.getProperty("line.separator");
String line1 = "test";
String line2 = "test1";
String line3 = "test2";
String line4 = "test3";
try (FileOutputStream fos = new FileOutputStream(destFile)) {
fos.write(line1.getBytes());
fos.write(lineSeparator.getBytes());
fos.write(line2.getBytes());
fos.write(lineSeparator.getBytes());
fos.write(line3.getBytes());
fos.write(lineSeparator.getBytes());
fos.write(line4.getBytes());
// Flush the written bytes to the file
fos.flush();
System.out.println("Text has been written to "
+ (new File(destFile)).getAbsolutePath());
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
上面的代码生成以下结果。
PrintStream类是输出流的具体装饰器。
PrintStream可以以合适的格式打印任何数据类型值,基本或对象。
PrintStream可以将数据写入输出流不抛出IOException。
如果一个方法抛出一个IOException,PrintStream设置一个内部标志,而不是抛出异常给调用者。可以使用其checkError()方法检查该标志,如果在方法执行期间发生IOException,则返回true。
PrintStream具有自动刷新功能。我们可以在其构造函数中指定它应该自动刷新写入它的内容。
如果我们将auto-flush标志设置为true,当写入一个字节数组时,PrintStream将刷新它的内容,它的一个重载的println()方法用于写入数据,一个换行符被写入,或一个字节(‘\n’)。
import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintStream;
public class Main {
public static void main(String[] args) {
String destFile = "luci3.txt";
try (PrintStream ps = new PrintStream(destFile)) {
ps.println("test");
ps.println("test1");
ps.println("test2");
ps.print("test3");
// flush the print stream
ps.flush();
System.out.println("Text has been written to "
+ (new File(destFile).getAbsolutePath()));
} catch (FileNotFoundException e1) {
e1.printStackTrace();
}
}
}
上面的代码生成以下结果。
DataOutputStream可以将Java基本数据类型值写入输出流。
DataOutputStream类包含一个写入数据类型的写入方法。它支持使用writeUTF(String text)方法将字符串写入输出流。
要将Java原始数据类型值写入名为primitives.dat的文件,我们将按如下所示构造DataOutputStream的对象:
DataOutputStream dos = new DataOutputStream(new FileOutputStream("primitives.dat"));
以下代码将一个int值,一个double值,一个布尔值和一个字符串写入名为primitives.dat的文件。
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
public class Main {
public static void main(String[] args) {
String destFile = "primitives.dat";
try (DataOutputStream dos = new DataOutputStream(new FileOutputStream(
destFile))) {
dos.writeInt(765);
dos.writeDouble(6789.50);
dos.writeBoolean(true);
dos.writeUTF("Java Input/Output is cool!");
dos.flush();
System.out.println("Data has been written to "
+ (new File(destFile)).getAbsolutePath());
} catch (Exception e) {
e.printStackTrace();
}
}
}
上面的代码生成以下结果。
管道连接输入流和输出流。
管道 I/O基于生产者 - 消费者模式,其中生产者产生数据并且消费者消费数据。
在管道 I/O中,我们创建两个流代表管道的两端。 PipedOutputStream对象表示一端,PipedInputStream对象表示另一端。我们使用两个对象上的connect()方法连接两端。
我们还可以通过在创建另一个对象时将一个对象传递给构造函数来连接它们。
以下代码显示了创建和连接管道两端的两种方法:
第一种方法创建管道输入和输出流并连接它们。它使用connect方法连接两个流。
PipedInputStream pis = new PipedInputStream();
PipedOutputStream pos = new PipedOutputStream();
pis.connect(pos); /* Connect the two ends */
第二种方法创建管道输入和输出流并连接它们。它通过将输入管道流传递到输出流构造器来连接两个流。
构造时连接: new 尾 (头)
PipedInputStream pis = new PipedInputStream();
PipedOutputStream pos = new PipedOutputStream(pis);
我们可以在连接管道的两端后生成和使用数据。
我们通过使用PipedOutputStream对象的一个write()方法产生数据。无论我们对管道输出流写入什么,自动变得可用于管道输入流对象进行读取。
我们使用PipedInputStream的read()方法从管道读取数据。如果数据在尝试从管道读取时不可用,则管道输入流被阻止。
管道流具有固定容量的缓冲器,以在写入管道和从管道读取之间存储数据。
我们可以设置管道容量,当我们创建它。如果管道的缓冲区已满,则尝试在管道上写入将被阻止。
以下代码创建缓冲区容量为2048字节的管道输入和输出流。
PipedOutputStream pos = new PipedOutputStream();
PipedInputStream pis = new PipedInputStream(pos, 2048);
管道用于将数据从一个线程传输到另一个线程。两个线程之间的同步由阻塞读和写来处理。
以下代码演示了如何使用管道 I/O。
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
public class Main {
public static void main(String[] args) throws Exception {
PipedInputStream pis = new PipedInputStream();
PipedOutputStream pos = new PipedOutputStream();
pos.connect(pis);
Runnable producer = () -> produceData(pos);
Runnable consumer = () -> consumeData(pis);
new Thread(producer).start();
new Thread(consumer).start();
}
public static void produceData(PipedOutputStream pos) {
try {
for (int i = 1; i <= 50; i++) {
pos.write((byte) i);
pos.flush();
System.out.println("Writing: " + i);
Thread.sleep(500);
}
pos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void consumeData(PipedInputStream pis) {
try {
int num = -1;
while ((num = pis.read()) != -1) {
System.out.println("Reading: " + num);
}
pis.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
上面的代码生成以下结果。
ObjectOutputStream类的一个对象用于序列化一个对象。
ObjectInputStream类的一个对象用于反序列化一个对象。
ObjectOutputStream继承自OutputStream。 ObjectInputStream继承自InputStream。
类必须实现Serializable或Externalizable接口以便序列化或反序列化。
Serializable接口是一个标记接口。
如果我们想要一个Person类的对象被序列化,我们需要声明Person类如下:
public class Person implements Serializable {
}
Java负责处理从/向流读取/写入Serializable对象的细节。我们只需要将对象写入/读取流到流类中的一个方法。
实现Externalizable接口使我们能够更好地控制从流中读取和写入对象。
它继承Serializable接口。它声明如下:
public interface Externalizable extends Serializable {
void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;
void writeExternal(ObjectOutput out) throws IOException;
}
当我们从流中读取一个对象时,Java调用readExternal()方法。当我们向一个流写一个对象时,它调用writeExternal()方法。
我们必须编写逻辑来分别读取和写入readExternal()和writeExternal()方法中的对象的字段。
实现Externalizable接口的类如下所示:
public class Person implements Externalizable {
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
// Write the logic to read the Person object fields from the stream
}
public void writeExternal(ObjectOutput out) throws IOException {
// Write the logic to write Person object fields to the stream
}
}
以下代码创建ObjectOutputStream类的对象,并将对象保存到person.ser文件。
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.ser"));
要将对象保存到ByteArrayOutputStream,我们构造一个对象输出流如下:
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// Creates an object output stream to write objects to the byte array output stream
ObjectOutputStream oos = new ObjectOutputStream(baos);
使用ObjectOutputStream类的writeObject()方法通过将对象引用作为参数传递来序列化对象,如下所示:
oos.writeObject(p1);
最后,当我们完成将所有对象写入时,使用close()方法关闭对象输出流:
oos.close();
以下代码显示如何序列化实现可序列化接口的Person类。
import java.io.Serializable;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
class Person implements Serializable {
private String name = "Unknown";
private String gender = "Unknown";
private double height = Double.NaN;
public Person(String name, String gender, double height) {
this.name = name;
this.gender = gender;
this.height = height;
}
@Override
public String toString() {
return "Name: " + this.name + ", Gender: " + this.gender + ", Height: "
+ this.height;
}
}
public class Main {
public static void main(String[] args) {
Person p1 = new Person("John", "Male", 1.7);
Person p2 = new Person("Wally", "Male", 1.7);
Person p3 = new Person("Katrina", "Female", 1.4);
File fileObject = new File("person.ser");
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(
fileObject))) {
oos.writeObject(p1);
oos.writeObject(p2);
oos.writeObject(p3);
// Display the serialized objects on the standard output
System.out.println(p1);
System.out.println(p2);
System.out.println(p3);
} catch (IOException e) {
e.printStackTrace();
}
}
}
上面的代码生成以下结果。
以下代码显示如何创建ObjectInputStream类的对象,并从person.ser文件读取对象。
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.ser"));
要从ByteArrayInputStream读取对象,请按如下所示创建对象输出流:
ObjectInputStream ois = new ObjectInputStream(Byte-Array-Input-Stream-Reference);
使用ObjectInputStream类的readObject()方法来反序列化对象。
Object obj = oos.readObject();
最后,关闭对象输入流如下:
ois.close();
以下代码显示如何从文件读取对象。
import java.io.File;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
public class Main {
public static void main(String[] args) {
File fileObject = new File("person.ser");
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
fileObject))) {
Person p1 = (Person) ois.readObject();
Person p2 = (Person) ois.readObject();
Person p3 = (Person) ois.readObject();
System.out.println(p1);
System.out.println(p2);
System.out.println(p3);
} catch (Exception e) {
e.printStackTrace();
}
}
}
要序列化和反序列化可外部化对象,请实现Externalizable接口。
import java.io.Externalizable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
class PersonExt implements Externalizable {
private String name = "Unknown";
private String gender = "Unknown";
private double height = Double.NaN;
public PersonExt() {
}
public PersonExt(String name, String gender, double height) {
this.name = name;
this.gender = gender;
this.height = height;
}
public String toString() {
return "Name: " + this.name + ", Gender: " + this.gender + ", Height: "
+ this.height;
}
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
this.name = in.readUTF();
this.gender = in.readUTF();
}
public void writeExternal(ObjectOutput out) throws IOException {
out.writeUTF(this.name);
out.writeUTF(this.gender);
}
}
public class Main {
public static void main(String[] args) {
PersonExt p1 = new PersonExt("John", "Male", 6.7);
PersonExt p2 = new PersonExt("Wally", "Male", 5.7);
PersonExt p3 = new PersonExt("Katrina", "Female", 5.4);
File fileObject = new File("personext.ser");
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(
fileObject))) {
oos.writeObject(p1);
oos.writeObject(p2);
oos.writeObject(p3);
System.out.println(p1);
System.out.println(p2);
System.out.println(p3);
} catch (IOException e1) {
e1.printStackTrace();
}
fileObject = new File("personext.ser");
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
fileObject))) {
p1 = (PersonExt) ois.readObject();
p2 = (PersonExt) ois.readObject();
p3 = (PersonExt) ois.readObject();
// Let"s display the objects that are read
System.out.println(p1);
System.out.println(p2);
System.out.println(p3);
// Print the input path
System.out.println("Objects were read from "
+ fileObject.getAbsolutePath());
} catch (Exception e) {
e.printStackTrace();
}
}
}
上面的代码生成以下结果。