java.io.File.<init>(File,String) JDK version dependent [duplicate]
Asked Answered
R

3

7

It's looks like java.io.File.(File, String) is JDK version dependent. Code example was run on Windows 10.

Code example:

public static void main(String... args) {
    String path = "C:\\Workspace\\project";
    File file = null;
    for (String part : path.split("\\\\")) {
        file = new File(file, part);
    }
    System.out.println(file);
    // prints "C:Workspace\project" for JDK 9+
    // prints "C:\Workspace\project" for JDK 8
}

Could you please address is there any known issue or solution for the case

Rust answered 3/9, 2018 at 13:54 Comment(2)
The new behavior seems to be the correct one. C: means the current directory on drive C and when you append Workspace it should become the relative C:Workspace. If you start with `C:\` then you should get the other result.Solander
I wonder if that's where the JEP 326: Raw String Literals (#File Paths Example) can really be helpful.Veneering
P
7

In Java 9, the WinNTFileSystem class (FileSystem implementation for windows) changed.
It probably solves a issue which the way the class considers what a absolute path is. I didn't find a bug that exactly specifies this one but some are close to.

File.isAbsolute() states :

On Microsoft Windows systems, a pathname is absolute if its prefix is a drive specifier followed by "\", or if its prefix is "\\".

So according to the specification, for the following:

File x = new File("C:", "Workspace"); 
System.out.println(x.isAbsolute()); // not absolute according to the spec

File xx = new File("C:\\", "Workspace");
System.out.println(xx.isAbsolute());

File xxx = new File("\\\\Workspace");
System.out.println(xxx.isAbsolute());

we expect :

false true true

But we get :

true true true

From Java 9 it produces the expected result.

The problem is that before Java 9 a path without \ is all the same considered as an absolute path :

File x = new File("C:", "Workspace");
System.out.println(x.isAbsolute()); // true

While that this should not be the case.

Concretely, the main changes concern the resolve(String parent, String child) method of the WinNTFileSystem class.
Before, resolve() resolved the abstract path by adding a slash between the parent and the child for any child path that doesn't start with a slash.
From Java 9, resolve() doesn't add a slash any longer between the parent and the child if the parent is a drive.

Here is the change :

boolean isDirectoryRelative =
    pn == 2 && isLetter(parent.charAt(0)) && parent.charAt(1) == ':';

if (child.charAt(childStart) == slash || isDirectoryRelative) {
    theChars = new char[strlen];             ^-------- this one was added from Java 9
    parent.getChars(0, parentEnd, theChars, 0);
    child.getChars(childStart, cn, theChars, parentEnd);
} else {
    theChars = new char[strlen + 1];
    parent.getChars(0, parentEnd, theChars, 0);
    theChars[parentEnd] = slash;
    child.getChars(childStart, cn, theChars, parentEnd + 1);
}

Consequences

About your question :

Could you please address is there any known issue or solution for the case

The difference between the two JDK versions matters. It concerns both the value of the abstract pathname's normalized pathname (File.getPath()) and the value of the absolute path File.getAbsolutePath() as now new File("C:", "Workspace") produces a relative path.

If your application relies on File.getPath() to do some parsing on that, it could have a distinct behavior and create some issues.
To have a portable code between the JDK versions, any parsing on the resolved path should consider as optional the \ just after the Windows drive.
In this way C:\ and C: would be handled in the same way.

If your application relies on File.getAbsolutePath(), it could also have some surprises from Java 9 as now the path that is relative will be resolved against the filesystem (before as an absolute path this just returned itself).
So instead, you should so probably use File.getPath() to not resolve the path against the file system.

Pachisi answered 3/9, 2018 at 15:16 Comment(0)
S
4

Addition to @david answer,

It was a bug#8153250, which was address and fixed version is available JDK 9 b153.

Separator answered 3/9, 2018 at 15:26 Comment(2)
Should this be the duplicate of SO question linked in the Bug you've shared? Seems like the proper implementation is what the fix implies in JDK-10 or above.Veneering
@nullpointer Even I think, it is duplicate of same question. There are similar issue which is related to same bug (which actually mentioned in same bug) are fixed in JDK10.Separator
P
2

As @davidxxx pointed out getAbsoluteFile or getAbsolutePath on the initial File instance is the same for Java8 and Java9. If you however decide to use this initial File to create a new File you'd probably be surprised about the differences - at least I was.

public static void main(String[] args) throws Exception {

    File file = new File((File)null, "C:");
    System.out.println(file);
    System.out.println(file.getAbsoluteFile());

    System.out.println("----");

    file = new File(file, "Windows");
    System.out.println(file);
    System.out.println(file.getAbsoluteFile());
}

If you execute the above program with Java 8 and 9 file will point to completely different folders after the second assignment. As the initial file is the same for Java8 and 9 I'd actually consider this to be a bug in Java 8 (and before probably).

Output Java 8:

C:
C:\ws\PLAYGROUND\test
----
C:\Windows
C:\Windows

Output Java 9:

C:
C:\ws\PLAYGROUND\test
----
C:Windows
C:\ws\PLAYGROUND\test\Windows

Pirnot answered 3/9, 2018 at 16:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.