Como o Java resolve um caminho relativo no novo File ()?

Eu estou tentando entender a maneira como Java resolve caminho relativo ao criar um object File .

SO usado: Windows

Para o trecho abaixo, estou recebendo uma IOException pois não pode encontrar o caminho:

 @Test public void testPathConversion() { File f = new File("test/test.txt"); try { f.createNewFile(); System.out.println(f.getPath()); System.out.println(f.getAbsolutePath()); System.out.println(f.getCanonicalPath()); } catch (Exception e) { e.printStackTrace(); } } 

Meu entendimento aqui é que o Java trata o caminho fornecido como absoluto e retorna um erro quando o caminho não existe. Então faz sentido.

Quando atualizo o código acima para usar o caminho relativo:

 @Test public void testPathConversion() { File f = new File("test/../test.txt"); try { f.createNewFile(); System.out.println(f.getPath()); System.out.println(f.getAbsolutePath()); System.out.println(f.getCanonicalPath()); } catch (Exception e) { e.printStackTrace(); } } 

Cria um novo arquivo e fornece a saída abaixo:

 test\..\test.txt C:\JavaForTesters\test\..\test.txt C:\JavaForTesters\test.txt 

Nesse caso, minha suposição é que, mesmo que o caminho fornecido não exista, porque o caminho contém “/../”, o java trata isso como um caminho relativo e cria o arquivo no user.dir . Então isso também faz sentido.

Mas se eu atualizar o caminho relativo como abaixo:

  @Test public void testPathConversion() { File f = new File("test/../../test.txt"); try { f.createNewFile(); System.out.println(f.getPath()); System.out.println(f.getAbsolutePath()); System.out.println(f.getCanonicalPath()); } catch (Exception e) { e.printStackTrace(); } } 

Então eu recebo IOException: access negado.

Minhas perguntas são:

  1. por que "test/../test.txt" é tratado como um caminho relativo e cria o arquivo em "user.dir" mas "test/../../test.txt" retorna um erro? Onde ele tenta criar o arquivo para o caminho "test/../../test.txt" ?
  2. Quando o caminho relativo especificado não é encontrado, o arquivo parece ser criado no user.dir . Então, parece-me que os dois cenários abaixo fazem a mesma coisa:

     //scenario 1 File f = new File("test/../test.txt"); f.createNewFile(); //scenario 2 File f = new File("test.txt"); f.createNewFile(); 

Então, há um caso do mundo real em que alguém usaria o cenário 1 em vez do cenário 2?

Suponho que esteja faltando algo óbvio aqui ou que tenha percalhado caminhos relativos fundamentalmente mal compreendidos. Eu passei pela documentação do Java para o File e não consigo encontrar uma explicação para isso. Existem algumas perguntas postadas no Stack Overflow a respeito de caminhos relativos, mas os que eu pesquisei foram para cenários específicos e não exatamente sobre como os caminhos relativos são resolvidos.

Será ótimo se alguém puder me explicar como isso funciona ou apontar para alguns links relacionados?

Existe um conceito de um working directory .
Este diretório é representado por um . (ponto).
Em caminhos relativos, todo o resto é relativo a isso.

Basta colocar o . (o diretório de trabalho) é onde você executa seu programa.
Em alguns casos, o diretório de trabalho pode ser alterado, mas, em geral, isso é
o que o ponto representa. Eu acho que isso é C:\JavaForTesters\ no seu caso.

Então test\..\test.txt significa: o test subdiretório
no meu diretório de trabalho, em seguida, um nível acima, então o
arquivo test.txt . Isso é basicamente o mesmo que apenas test.txt .

Para mais detalhes, confira aqui.

http://docs.oracle.com/javase/7/docs/api/java/io/File.html

http://docs.oracle.com/javase/tutorial/essential/io/pathOps.html

Quando seu caminho começa com um diretório raiz, isto é, C:\ no Windows ou no Unix ou no caminho de resources do java, ele é considerado um caminho absoluto. Tudo o resto é relativo, então

 new File("test.txt") is the same as new File("./test.txt") new File("test/../test.txt") is the same as new File("./test/../test.txt") 

A principal diferença entre getAbsolutePath e getCanonicalPath é que o primeiro concatena um pai e um caminho filho, então ele pode conter pontos: .. ou . . getCanonicalPath sempre retornará o mesmo caminho para um arquivo específico.

Nota: File.equals usa uma forma abstrata de um caminho ( getAbsolutePath ) para comparar arquivos, então isso significa que dois objects File para o mesmo podem não ser iguais e File s não são seguros para usar em collections como Map ou Set .

O diretório de trabalho é um conceito comum em praticamente todos os sistemas operacionais e linguagens de programa, etc. É o diretório em que seu programa está sendo executado. Isso geralmente (mas nem sempre, existem maneiras de alterá-lo) no diretório em que o aplicativo está.

Caminhos relativos são aqueles que iniciam sem um especificador de unidade. Então no linux eles não começam com um / , no windows eles não iniciam com um C:\ , etc. Eles sempre começam no seu diretório de trabalho.

Os caminhos absolutos são aqueles que começam com um especificador de unidade (ou máquina para caminhos de rede). Eles sempre vão desde o começo daquele passeio.

No Windows e no Netbeans, você pode definir o caminho relativo como:

  new FileReader("src\\PACKAGE_NAME\\FILENAME"); 

No Linux e Netbeans, você pode definir o caminho relativo como:

  new FileReader("src/PACKAGE_NAME/FILENAME"); 

Se você tem seu código dentro de Source Packages código- Source Packages , não sei se é o mesmo para eclipse ou outro IDE

Apenas ligeiramente relacionado com a questão, mas tente envolver a sua cabeça em torno desta. Tão pouco intuitivo:

 import java.nio.file.*; class Main { public static void main(String[] args) { Path p1 = Paths.get("/personal/./photos/./readme.txt"); Path p2 = Paths.get("/personal/index.html"); Path p3 = p1.relativize(p2); System.out.println(p3); //prints ../../../../index.html !! } } 

Caminhos relativos podem ser melhor entendidos se você souber como o Java executa o programa.

Existe um conceito de diretório de trabalho ao executar programas em Java. Supondo que você tenha uma class, digamos, FileHelper que faz o IO em /User/home/Desktop/projectRoot/src/topLevelPackage/ .

Dependendo do caso em que você invocar java para executar o programa, você terá um diretório de trabalho diferente. Se você executar seu programa de dentro e do IDE, provavelmente será projectRoot .

  • Neste caso $ projectRoot/src : java topLevelPackage.FileHelper será src .

  • Neste caso $ projectRoot : java -cp src topLevelPackage.FileHelper será projectRoot .

  • Nesse caso, $ /User/home/Desktop : java -cp ./projectRoot/src topLevelPackage.FileHelper será Desktop .

(Assuming $ is your command prompt with standard Unix-like FileSystem. Similar correspondence/parallels with Windows system)

Portanto, a raiz do caminho relativo (.) resolvida para o diretório de trabalho. Assim, para ter mais certeza de onde escrever os arquivos, é dito considerar a abordagem abaixo.

 package topLevelPackage import java.io.File; import java.nio.file.Path; import java.nio.file.Paths; public class FileHelper { // Not full implementation, just barebone stub for path public void createLocalFile() { // Explicitly get hold of working directory String workingDir = System.getProperty("user.dir"); Path filePath = Paths.get(workingDir+File.separator+"sampleFile.txt"); // In case we need specific path, traverse that path, rather using . or .. Path pathToProjectRoot = Paths.get(System.getProperty("user.home"), "Desktop", "projectRoot"); System.out.println(filePath); System.out.println(pathToProjectRoot); } } 

Espero que isto ajude.