Java教程 第79页 Java IO教程 – Java文件树 FileVisitor API可以递归地处理文件树中的所有文件和目录。 当我们要对文件树中的所有或某些文件或目录执行某些操作时,FileVisitor API非常有用。 SimpleFileVisitor类是FileVisitor接口的基本实现。 当访问文件/目录时,SimpleFileVisitor类不执行任何操作。我们可以从SimpleFileVisitor类继承我们的文件访问类,并且只覆盖适合我们需要的方法。 FileVisitor接口的方法: ID 含义 1 FileVisitResult preVisitDirectory(T dir,BasicFileAttributes attrs) 在访问目录中的条目之前调用一次。 2 FileVisitResult postVisitDirectory(T dir,IOException exc) 已访问目录中的后调用项。如果在目录的迭代期间抛出了任何异常,则将异常对象作为第二个参数传递给此方法。如果此方法的第二个参数为null,则在目录迭代期间没有异常。 3 FileVisitResult visitFile(T文件,BasicFileAttributes attrs) 当访问目录中的文件时调用。 4 FileVisitResult visitFileFailed(T文件,IOException exc) 当由于任何原因而无法访问文件或目录时调用。 下表列出了FileVisitResult的枚举常量及其说明 枚举常量 描述 CONTINUE 继续处理 SKIP_SIBLINGS 继续处理而不访问文件或目录的兄弟节点。 SKIP_SUBTREE 继续处理,而不访问目录中的条目。 TERMINATE 终止文件访问过程。 我们不需要在我们的文件访问类的所有四个方法中编写逻辑。要复制目录,请使用preVisitDirectory()方法来创建一个新目录和visitFile()方法来复制文件。 以下代码显示如何打印目录的子目录和文件的名称。 import static java.nio.file.FileVisitResult.CONTINUE; import java.io.IOException; import java.nio.file.FileVisitResult; import java.nio.file.FileVisitor; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; public class Main { public static void main(String[] args) { Path startDir = Paths.get(""); FileVisitor<Path> visitor = getFileVisitor(); try { Files.walkFileTree(startDir, visitor); } catch (IOException e) { e.printStackTrace(); } } public static FileVisitor<Path> getFileVisitor() { class DirVisitor<Path> extends SimpleFileVisitor<Path> { @Override public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) { System.out.format("%s [Directory]%n", dir); return CONTINUE;...
2024-04-01
Java IO教程 – Java文件所有者权限 有三种方法可以管理文件所有者: 使用Files.getOwner()和Files.setOwner()方法。 使用“owner”作为属性名称的Files.getAttribute()和Files.setAttribute()方法。 使用FileOwnerAttributeView。 我们需要使用UserPrincipal和GroupPrincipal接口来管理文件的所有者。 文件的所有者可以是用户或组。 UserPrincipal表示用户。GroupPrincipal表示组。 当我们读取文件的所有者时,我们得到一个UserPrincipal的实例。在UserPrincipal对象上调用getName()方法以获取用户的名称。 要设置文件的所有者,请从用户名获取UserPrincipal的对象。 要从文件系统获取UserPrincipal,请使用UserPrincipalLookupService类的实例,我们可以使用FileSystem类的getUserPrincipalLookupService()方法获取该实例。 以下代码为用户ID为myName的用户获取一个UserPrincipal对象: FileSystem fs = FileSystems.getDefault(); UserPrincipalLookupService upls = fs.getUserPrincipalLookupService(); UserPrincipal user = upls.lookupPrincipalByName("myName"); System.out.format("User principal name is %s%n", user.getName()); 以下代码显示如何使用FileOwnerAttributeView更改文件的所有者。 import java.io.IOException; import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.attribute.FileOwnerAttributeView; import java.nio.file.attribute.UserPrincipal; import java.nio.file.attribute.UserPrincipalLookupService; public class Main { public static void main(String[] args) throws Exception { Path path = Paths.get("C:\\Java_Dev\\test1.txt"); FileOwnerAttributeView foav = Files.getFileAttributeView(path, FileOwnerAttributeView.class); UserPrincipal owner = foav.getOwner(); System.out.format("Original owner of %s is %s%n", path, owner.getName()); FileSystem fs = FileSystems.getDefault(); UserPrincipalLookupService upls = fs.getUserPrincipalLookupService(); UserPrincipal newOwner = upls.lookupPrincipalByName("brice"); foav.setOwner(newOwner); UserPrincipal changedOwner = foav.getOwner(); System.out.format("New owner of %s is %s%n", path, changedOwner.getName()); } } 以下代码使用Files.setOwner()方法更新在Windows上使用路径C:\Java_Dev\test1.txt标识的文件的所有者: UserPrincipal owner = get the owner; Path...
2024-04-01
Java IO教程 – Java目录事件 当文件系统中的对象被修改时,我们可以监听watch服务以获取警报。 java.nio.file包中的以下类和接口提供watch服务。 Watchable接口 WatchService接口 WatchKey接口 WatchEvent接口 WatchEvent.Kind接口 StandardWatchEventKinds类 可监视对象表示可以被监视的文件系统对象。可观看对象可以向手表服务注册。 Path对象是一个Watchable对象。 WatchService表示观察服务。当一个对象使用WatchService注册时,WatchService返回一个WatchKey作为注册的令牌。 WatchEvent表示注册到监视服务的对象上的事件。它的kind()方法返回发生的事件的类型。 它的context()方法返回一个Path对象,它表示事件发生的条目。 count()方法返回特定通知的事件发生次数。 如果它返回大于1的值,那么它是一个重复的事件。 WatchEvent.Kind <T>表示发生的事件的类型。 StandardWatchEventKinds类定义了用于表示事件种类的常量,如下所示。 ENTRY_CREATE ENTRY_DELETE ENTRY_MODIFY OVERFLOW OVERFLOW表示丢失或丢弃的事件。 创建观察服务以观察目录以进行更改。 WatchService ws = FileSystems.getDefault().newWatchService(); 要使用Watch服务注册目录,使用register()方法,该方法将返回一个WatchKey对象作为注册令牌。 // Get a Path object for C:\myName directory to watch Path dirToWatch = Paths.get("C:\\myName"); WatchKey token = dirToWatch.register(ws, ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE); 要取消注册,请使用WatchKey的cancel()方法。 当注册目录时,其WatchKey处于就绪状态。 我们可以通过手表服务注册多个目录。 要从监视服务队列中检索WatchKey,使用WatchService对象的take()或poll()方法来检索和删除发出信号并排队的WatchKey。 take()方法等待,直到WatchKey可用。poll()方法允许我们为等待指定超时。 以下代码使用无限循环来检索发出信号的WatchKey。 while(true) { WatchKey key = ws.take(); } 处理事件 WatchKey的pollEvents()方法检索并删除所有挂起的事件。它返回一个WatchEvent的列表。 List的每个元素代表WatchKey上的一个事件。 以下代码显示了处理事件的典型逻辑: while(true) { WatchKey key = ws.take(); // Process all events of the WatchKey for(WatchEvent<?> event : key.pollEvents()) { // Process each event here } } 处理事件后重置WatchKey 我们需要重置WatchKey对象,通过调用其reset()方法来再次接收事件通知。 reset()方法将WatchKey置于就绪状态。如果WatchKey仍然有效,reset()方法返回true。 否则,它返回false。 如果WatchKey被取消或其监视服务关闭,它可能会失效。 // Reset the WatchKey boolean isKeyValid = key.reset(); if (!isKeyValid) { System.out.println("No longer watching " + dirToWatch); } WatchService是可自动关闭的。我们可以在try-with-resources中创建一个WatchService的对象块,当程序退出块时它将自动关闭。...
2024-04-01
Java IO教程 – Java异步I/O 在同步文件I/O中,对I/O操作的请求将等待,直到I/O操作完成。 在异步文件I/O中,I/O操作的请求由系统异步执行。 当系统完成文件I/O时,它通知应用程序其请求的完成。 java.nio.channels.AsynchronousFileChannel类表示异步文件通道。 AsynchronousFileChannel类的静态open()方法获取AsynchronousFileChannel类的实例。 以下代码显示了如何获取WRITE的异步文件通道。 Path path = Paths.get("C:\\Java_Dev\\rainbow.txt"); AsynchronousFileChannel afc = AsynchronousFileChannel.open(path, WRITE, CREATE); AsynchronousFileChannel提供了两种方法来处理异步文件I/O操作的结果。 Using a java.util.concurrent.Future object. Using a java.nio.channels.CompletionHandler object. 支持异步文件I/O操作的AsynchronousFileChannel类的每个方法有两个版本。 一个版本返回一个Future对象,我们可以使用它来处理所请求的异步操作的结果。 Future对象的get()方法返回写入文件通道的字节数。 以下代码使用返回Future对象的write()方法的版本: ByteBuffer dataBuffer = a buffer; long startPosition = 0; Future<Integer> result = afc.write(dataBuffer, startPosition); 一旦我们得到一个Future对象,我们可以使用轮询方法或阻塞等待方法来处理异步文件I/O的结果。 下面的代码显示了轮询方法,它将继续调用Future对象的isDone()方法,以检查I/O操作是否完成: while (!result.isDone()) { } int writtenNumberOfBytes = result.get(); AsynchronousFileChannel类的另一个版本的方法获得一个CompletionHandler对象,当请求的异步I/O操作完成或失败时,该对象的方法被调用。 CompletionHandler接口有两个方法:completed()和failed()。 当所请求的I/O操作成功完成时,将调用completed()方法。 当请求的I/O操作时失败,则调用failed()方法。 以下代码使用Attachment类的对象作为完成处理程序的附件: class Attachment { public Path path; public ByteBuffer buffer; public AsynchronousFileChannel asyncChannel; } class MyHandler implements CompletionHandler<Integer, Attachment> { @Override public void completed(Integer result, Attachment attach) { // Handle completion of the I/O operation } @Override public void failed(Throwable e, Attachment attach) { // Handle failure of the I/O operation } } 以下代码使用MyHandler实例作为异步写操作的完成处理程序。 MyHandler handler = new MyHandler();...
2024-04-01
Java IO教程 – Java文件系统 Java 7引入了新的输入/输出2(NIO.2)API并提供了一个新的 I/O API。 它向Java类库添加了三个包:java.nio.file,java.nio.file.attribute和java.nio.file.spi。 文件系统 FileSystem类的对象表示Java程序中的文件系统。 FileSystem对象用于执行两个任务: Java程序和文件系统之间的接口。 一个工厂用于创建许多类型的文件系统相关对象和服务。 FileSystem对象与平台相关。 创建文件系统 要获取默认的FileSystem对象,我们需要使用FileSystems类的getDefault()静态方法,如下所示: FileSystem fs = FileSystems.getDefault(); FileSystem由一个或多个FileStore组成。FileSystem的getFileStores()方法返回FileStore对象的Iterator。 FileSystem的getRootDirectories()方法返回Path对象的迭代器,它表示到所有顶级目录的路径。 FileSystem的isReadOnly()方法告诉我们是否获得对文件存储的只读访问权限。 例子 以下代码显示如何使用FileSystem对象。 import java.nio.file.FileStore; import java.nio.file.FileSystem; import java.nio.file.FileSystems; import java.nio.file.Path; import java.io.IOException; public class Main { public static void main(String[] args) { FileSystem fs = FileSystems.getDefault(); System.out.println("Read-only file system: " + fs.isReadOnly()); System.out.println("File name separator: " + fs.getSeparator()); for (FileStore store : fs.getFileStores()) { printDetails(store); } for (Path root : fs.getRootDirectories()) { System.out.println(root); } } public static void printDetails(FileStore store) { try { String desc = store.toString(); String type = store.type(); long totalSpace = store.getTotalSpace(); long unallocatedSpace = store.getUnallocatedSpace(); long availableSpace = store.getUsableSpace(); System.out.println(desc + ", Total: " + totalSpace + ", Unallocated: " + unallocatedSpace...
2024-04-01
Java IO教程 – Java文件锁 NIO支持文件锁定以同步对文件的访问。我们可以锁定文件或整个文件的区域。 文件锁定机制由操作系统处理。 有两种文件锁定:排他和共享。 只有一个程序可以保存文件区域上的排他锁。 多个程序可以在文件的同一区域上保存共享锁。 我们不能在文件的同一区域混合排他锁和共享锁。 java.nio.channels.FileLock类表示文件锁。 我们通过使用FileChannel对象的lock()或tryLock()方法获取对文件的锁。 lock()方法阻止所请求区域上的锁是否不可用。 tryLock()方法不阻塞; 如果获取锁,它立即返回FileLock类的对象; 否则返回null。 lock()和tryLock()方法有两个版本:一个没有参数,另一个有参数。 不带参数的版本锁定整个文件。 带有参数的版本接受要锁定的区域的起始位置,要锁定的字节数以及用于指示锁是否共享的布尔标志。 如果锁是共享的,FileLock对象的isShared()方法返回true; 否则,返回false。 以下代码显示了获取文件锁的不同方法。 import java.io.RandomAccessFile; import java.nio.channels.FileChannel; import java.nio.channels.FileLock; public class Main { public static void main(String[] args) throws Exception { RandomAccessFile raf = new RandomAccessFile("test.txt", "rw"); FileChannel fileChannel = raf.getChannel(); FileLock lock = fileChannel.lock(); } } 例子 获得前10个字节的独占锁 import java.io.RandomAccessFile; import java.nio.channels.FileChannel; import java.nio.channels.FileLock; public class Main { public static void main(String[] args) throws Exception { RandomAccessFile raf = new RandomAccessFile("test.txt", "rw"); FileChannel fileChannel = raf.getChannel(); // Get an exclusive lock on first 10 bytes FileLock lock = fileChannel.lock(0, 10, false); } } 尝试获取整个文件的独占锁 import java.io.RandomAccessFile; import java.nio.channels.FileChannel; import java.nio.channels.FileLock; public class Main { public static void main(String[] args) throws...
2024-04-01
Java IO教程 – Java对象序列化 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...
2024-04-01
Java IO教程 – Java数据输出流 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(); } } } 上面的代码生成以下结果。
2024-04-01
Java IO教程 – 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
2024-04-01
Java IO教程 – Java文件输入流 在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()方法从文件中读取数据。我们可以一次读取一个字节或多个字节。 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...
2024-04-01