Blog-Archiv

Sonntag, 24. Februar 2019

Java 11 Windows Drive File Construction

If you have old Java applications that use File constructors for Windows drives, avoiding File.listRoots(), you may want to read this Blog, because that functionality changed a little bit in Java 11. Unix systems are not affected.

Why not use File.listRoots()? This method didn't exist in Java 1.1. Moreover, in times of floppy-drives, this call caused periodical dialogs appearing in case no floppy was inserted. Java 1.2 listRoots() was not really usable for listing Windows drives, a still persisting problem are network drives that answer very slowly. You will want to let run each of those drive-explorations in a background thread, integrating them just when they answer within a configured timeout.

Windows versus Unix File-System

The difference between Windows and Unix file systems is that Unix has exactly one file-system root ("/"), while Windows has as many roots as disks and drives of any kind are available ("A:", "B:", "C:", .... "Z:"). The Windows path-separator is "\" (backslash), while Unix has "/" (slash).

Constructor Test Results

In the following you find different calls to File constructors, and what properties they yield. I was running these tests with Java 1.8 and 11 on Windows 8.1. For comparability I also included the results from Ubuntu 18.04 Linux. Click on the expand-controls below to see how constructors behave.

  • Windows 8.1
    • Java 1.8.0_25
      • File drive = new File("C:")
        • exists()getName()getPath()getAbsolutePath()toString()
          trueC:C:\Users\fred\eclipse-workspace\friwintestC:
          for (String name: drive.list())
              new File(drive, name);
          exists()getName()getPath()getAbsolutePath()toString()
          falsebinC:\binC:\binC:\bin
          falsesrcC:\srcC:\srcC:\src
      • File drive = new File("C:\")
        • exists()getName()getPath()getAbsolutePath()toString()
          trueC:\C:\C:\
          for (String name: drive.list())
              new File(drive, name);
          exists()getName()getPath()getAbsolutePath()toString()
          trueProgram FilesC:\Program FilesC:\Program FilesC:\Program Files
          trueUsersC:\UsersC:\UsersC:\Users
          trueWindowsC:\WindowsC:\WindowsC:\Windows
      • File drive = new File("\")
        • exists()getName()getPath()getAbsolutePath()toString()
          true\C:\\
          for (String name: drive.list())
              new File(drive, name);
          exists()getName()getPath()getAbsolutePath()toString()
          trueProgram Files\Program FilesC:\Program Files\Program Files
          trueUsers\UsersC:\Users\Users
          trueWindows\WindowsC:\Windows\Windows
    • Java 11.0.2
      • File drive = new File("C:")
        • exists()getName()getPath()getAbsolutePath()toString()
          trueC:C:\Users\fred\eclipse-workspace\friwintestC:
          for (String name: drive.list())
              new File(drive, name);
          exists()getName()getPath()getAbsolutePath()toString()
          truebinC:binC:\Users\fred\eclipse-workspace\friwintest\binC:bin
          truesrcC:srcC:\Users\fred\eclipse-workspace\friwintest\srcC:src
      • File drive = new File("C:\")
        • exists()getName()getPath()getAbsolutePath()toString()
          trueC:\C:\C:\
          for (String name: drive.list())
              new File(drive, name);
          exists()getName()getPath()getAbsolutePath()toString()
          trueProgram FilesC:\Program FilesC:\Program FilesC:\Program Files
          trueUsersC:\UsersC:\UsersC:\Users
          trueWindowsC:\WindowsC:\WindowsC:\Windows
      • File drive = new File("\")
        • exists()getName()getPath()getAbsolutePath()toString()
          true\C:\\
          for (String name: drive.list())
              new File(drive, name);
          exists()getName()getPath()getAbsolutePath()toString()
          trueProgram Files\Program FilesC:\Program Files\Program Files
          trueUsers\UsersC:\Users\Users
          trueWindows\WindowsC:\Windows\Windows
  • Linux
    • Java 1.8.0_25
      • File drive = new File("/")
        • exists()getName()getPath()getAbsolutePath()toString()
          true///
          for (String name: drive.list())
              new File(drive, name);
          exists()getName()getPath()getAbsolutePath()toString()
          truebin/bin/bin/bin
          trueusr/usr/usr/usr
    • Java 11.0.2
      • File drive = new File("/")
        • exists()getName()getPath()getAbsolutePath()toString()
          true///
          for (String name: drive.list())
              new File(drive, name);
          exists()getName()getPath()getAbsolutePath()toString()
          truebin/bin/bin/bin
          trueusr/usr/usr/usr

new File("C:")

When expanding Windows -> Java 1.8 -> new File("C:"), you will see that the absolute path doesn't match expectations. Instead of "C:\" it is the current working directory where my test-app was running. Below Java 11 you need to use new File("C:\\"), see next example.

Nevertheless, when you do new File("C:").list() and construct children with these names, and the drive as parent, the constructed files will not exist, because the list() children were taken from current working directory!

Similar problems exist in Java 11, although the list-children actually exist in this version. But look at the getPath() result: "C:bin" isn't a valid path!

new File("C:\\")

With "C:\\" everything works fine, and it's the same for Java 1.8 and Java 11. It's just the ugly file-separator in the name of the drive.

new File("\\")

This would be something similar to UNIX "/". It works, but you don't get the drive name "C:" from getPath().

By the way, toString() still delegates to getPath(), they did not change that in Java 11.

Test Source Code

You could do such a test with the following example, compiling it with Java 1.8 and then running it with both 1.8 and 11 (JRE 1.8 will not run classes compiled with 11):

public class FileConstructorTest
{
    public static void main(String [] args) {
        test("C:");
        test("C:"+File.separator);
        test(File.separator);
    }

    private static void test(String driveName) {
        final File drive = new File(driveName);
        output(drive);
        
        for (final String name : drive.list())
            output(new File(drive, name));
    }

    private static void output(File file)   {
        System.err.println(
                "exists="+file.exists()+
                ", name="+file.getName()+
                ", path="+file.getPath()+"</td>"+
                ", absolutePath="+file.getAbsolutePath()+
                ", toString="+file.toString());
    }
}



Keine Kommentare: