Preface
Starting from this article, we will start learning the Java IO system, which is essentially reading and writing files. It sounds simple, but it is not easy. Java's IO system has been improving and improving, and has designed a large number of classes. Only by understanding the meaning of these types being designed and their respective application scenarios can we improve the understanding of file IO.
So, the first step is to solve the problem of how to represent a file. In the Java world, "everything is an object", and how to correspond to an actual disk file or directory to a Java object is our primary problem.
File is used in Java to abstract a file, whether it is a normal file or a directory, it can correspond to a File object. I think everyone must be accurate in positioning the File type: it simply abstractly represents a file or directory on disk, and it actually relies on a platform-independent local file system class, and File cannot perform any read and write operations on the file content it represents (that is what stream does).
Build a File instance
Before actually introducing the File instance constructor method, we need to take a look at its several important attribute members.
private static final FileSystem fs = DefaultFileSystem.getFileSystem();
This is the most core member of the File class, which is represented as the file system API of the current system. All operations issued to the disk are based on this property.
private final String path;
path represents the full path name of the current instance. If the current File instance represents a directory, then the value of path is the complete directory name. If it represents a pure file, then the value of this path is equal to the full path of the file + the file name.
public static final char separatorChar = fs.getSeparator(); public static final char pathSeparatorChar = fs.getPathSeparator();
separatorChar represents the separator between directories, and pathSeparatorChar represents the separator under different paths. These two values are different under different system platforms. For example, the values of these two under Windows are: "" and ";", where the ban is used to separate multiple different paths.
The File class provides four different constructors for instantiating a File object, but only three are more commonly used. We also focus on learning the first three constructors.
public File(String pathname)
This is the most common way to instantiate a File object. The pathname value can be a directory or a plain file name. For example:
File file = new File("C://Users//yanga//Desktop");File file1 = new File("C://Users//yanga//Desktop//a.txt");File file2 = new File("a.txt");Of course, you can also explicitly specify a parent path:
public File(String parent, String child)
Inside the constructor, the program will splice a complete file path for us, for example:
File file = new File("C://Users//yanga//Desktop","a.txt");File file1 = new File("C://Users//yanga//Desktop","java");The third constructor is essentially the same as the second one, except that it adds an encapsulation process of the parent File instance:
public File(File parent, String child)
Similar situations will not be explained. We have not delved into the internal specific implementation of these constructors here. It is not that it is simple. Instead, File relies too much on the local file system and the implementation of many methods cannot be directly seen. Therefore, for the learning of File, it is enough to be proficient in mastering it, and the specific implementation cannot be learned in depth for the time being.
Obtain information related to file name or path
The getName method can be used to get the file name:
public String getName() { int index = path.lastIndexOf(separatorChar); if (index < prefixLength) return path.substring(prefixLength); return path.substring(index + 1);}Remember what our separatorChar represents?
It is represented as a path separator, the symbol "" in Windows is stored in the path attribute, and the full path name of the current File instance, so all characters after the last occurrence must be our file name.
Of course you must have discovered that for pure files, this method can return the simple name of the file, while for a directory, the return value will be the most recent directory name. For example:
File file = new File("C://Users//yanga//Desktop//a.txt");System.out.println(file.getName());File file1 = new File("C://Users//yanga//Desktop");System.out.println(file1.getName());The output will not surprise you:
a.txtDesktop
The getParent method is used to return the parent directory of the current file. Whether you are a plain file or a directory, you will eventually have your parent directory (of course, temporary files generated by the virtual machine are not of course).
public String getParent() { int index = path.lastIndexOf(separatorChar); if (index < prefixLength) { if ((prefixLength > 0) && (path.length() > prefixLength)) return path.substring(0, prefixLength); return null; } return path.substring(0, index);}The implementation of the method is very simple, so I won't go into details.
The getPath method can return the full file name of the current File instance:
public String getPath() { return path;}The following are some related operations related to directories, which are relatively simple to implement. Here is a brief list:
Here we need to explain some explanation of getCanonicalPath, what is a standard path, and is there any difference between an absolute path?
Generally speaking, "../" means the previous directory of the directory where the source file is located, "../../" means the previous directory of the directory where the source file is located, and so on. The getAbsolutePath method does not perform such conversion operations, while the getCanonicalPath method recognizes these special characters and takes appropriate semantics.
For example:
File file = new File("..//a.txt");System.out.println(file.getAbsolutePath());System.out.println(file.getCanonicalPath());Output result:
C:/Users/yanga/Desktop/Java/workspace2017/TestFile/../a.txt
C:/Users/yanga/Desktop/Java/workspace2017/a.txt
The former will use "../a.txt" as part of the file path name, while the latter can recognize that "../a.txt" means that "a.txt" is located in the upper directory of the current directory. This is the biggest difference between the two, suitable for different situations.
Get the attribute information of the file
The operation of this part of the file is actually very simple, it is nothing more than some questions about file permissions, whether it is readable, whether it is writable, whether it is a hidden file, etc. Let's take a look at these methods in detail:
It should be noted that the length method can correctly return the total number of bytes of the file for a pure file, but for a directory, the return value will be an "unspecified" value, which is neither the total number of bytes of all files in the directory nor zero, but is just an unspecified value, which has no meaning.
File operation
The operation of files is nothing more than "addition, deletion, modification and search". Let's take a look together.
Of course, when dealing with the above two simple new creation and deletion operations, the File class also provides the so-called "query" operations, which we need to learn carefully. For example:
public String[] list() { SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkRead(path); } if (isInvalid()) { return null; } return fs.list(this);}This method will retrieve all the simple names of "pure files" and "directory" in the directory represented by the current instance. For example:
File file = new File("C://Users//yanga//Desktop");String[] list = file.list(); for (String str : list){ System.out.println(str);}The output result of the program will print the simple names of all the files in my computer desktop directory, so I won't show it to you.
One thing to note is that if our File instance does not correspond to a directory but a plain file, then list will return null.
Next, let's look at a method to retrieve directory files:
public String[] list(FilenameFilter filter) { String names[] = list(); if ((names == null) || (filter == null)) { return names; } List<String> v = new ArrayList<>(); for (int i = 0 ; i < names.length ; i++) { if (filter.accept(this, names[i])) { v.add(names[i]); } } return v.toArray(new String[v.size()]);}This method is actually an overloaded version of list, which allows you to pass in a filter to filter only the files and directories we need when searching directories.
But the definition of the FilenameFilter interface is so simple:
public interface FilenameFilter { boolean accept(File dir, String name);}You only need to override this accept method. Every time the list for loop obtains a file or directory, it will try to call this filtering method first. If you pass the filtering, the simple name of the current file will be added to the return collection.
Therefore, the rewriting of this accept method determines which files can pass the filtering and which cannot. Let's look at an example:
The files in the test folder on my desktop are as follows:
File file = new File("C://Users//yanga//Desktop//test"); String[] list = file.list( new FilenameFilter() { @Override public boolean accept(File dir, String name) { // The current File object represented by dir //name is the simple name of the file item currently traversed if (!name.endsWith(".txt")) return false; else return true; } } ); for (String str : list){ System.out.println(str); }Here, we use anonymous inner class to create a subclass instance of FilenameFilter, and then implement its accept method. The specific implementation is very simple, filtering out all directories and taking out the simple names of all plain files.
The final output result is as follows:
3.txt
4.txt
Of course, there are also two "mutated" list methods in the File class, such as:
They no longer return the simple names of "pure files" and "directories" in the target directory, but return the corresponding File object. In fact, it is nothing. The target directory + a simple name can build these File instances.
Therefore, essentially, the list method will not traverse all files in the target directory, that is, the files in the subdirectory of the target directory will not be accessed and traversed.
So you should think about how to traverse all files in the target directory, including deep files in the first-level subdirectories. The answer will be given at the end of the article.
The next two methods are related to the creation of folders:
Both are based on the current File instance to create folders. Regarding their differences, let's first look at a piece of code:
File file = new File("C://Users//yanga//Desktop//test2");System.out.println(file.mkdir());File file2 = new File("C://Users//yanga//Desktop//test3//hello");System.out.println(file2.mkdir());Among them, test2 and test3 do not exist until the program is executed.
The output result is as follows:
true
false
Why did the latter fail to create?
This is due to the fact that the mkdir method can only create one folder at a time, which will cause creation failure if the parent or higher directory of the given directory exists an uncreated directory.
The mkdirs method is used to solve this situation. It will create all uncreated directories on the target path, see the code:
File file3 = new File("C://Users//yanga//Desktop//test3//hello//231");System.out.println(file3.mkdirs());Even if our test3 folder does not exist, after the program runs, the three folders test3, hello, and 231 will be created.
In addition, File also has a method to create temporary files. The so-called temporary files are: they exist during the runtime and are destroyed when the virtual machine is shut down. You can study it yourself. It is relatively simple to use, so I won’t go into details here.
At this point, we have roughly learned about the file type File. I believe everyone will more or less feel that the design of representing pure files and directories using the same type seems a bit confusing and unreasonable. I know that jdk1.7 sun has launched Files and Path to separate files and directories, and we will learn more about it in the future articles.
All codes, images, and files in the article are stored in the cloud on my GitHub:
(https://github.com/SingleYam/overview_java)
You can also choose to download locally.
Summarize
The above is the entire content of this article. I hope that the content of this article has certain reference value for everyone's study or work. If you have any questions, you can leave a message to communicate. Thank you for your support to Wulin.com.