API do caminho Java NIO2

API do caminho Java NIO2

1. Visão geral

Neste artigo, aprenderemos como usar a nova API I / O (NIO2)Path em Java.

As APIsPath no NIO2 constituem uma das principais novas áreas funcionais que acompanham o Java 7 e, especificamente, um subconjunto da nova API do sistema de arquivos junto com as APIs de arquivo.

2. Configuração

O suporte NIO2 está incluído no pacotejava.nio.file. Portanto, configurar seu projeto para usar as APIsPath é apenas uma questão de importar tudo neste pacote:

import java.nio.file.*;

Como os exemplos de código neste artigo provavelmente serão executados em ambientes diferentes, vamos controlar o diretório inicial do usuário:

private static String HOME = System.getProperty("user.home");

Essa variável apontará para um local válido em qualquer ambiente.

A classePaths é o principal ponto de entrada para todas as operações que envolvem caminhos do sistema de arquivos. Ele nos permite criar e manipular caminhos para arquivos e diretórios.

Digno de nota é que as operações de caminho são principalmente de natureza sintática; eles não têm efeito no sistema de arquivos subjacente e o sistema de arquivos não tem nenhum efeito sobre o êxito ou a falha. Isso significa que a passagem de um caminho inexistente como parâmetro de uma operação de caminho não tem influência se é bem-sucedida ou falha.

3. Operações de Caminho

Nesta seção, apresentaremos a principal sintaxe usada nas operações de caminho. Como o próprio nome indica, a classePath é uma representação programática de um caminho no sistema de arquivos.

Um objetoPath contém o nome do arquivo e a lista de diretórios usados ​​para construir o caminho e é usado para examinar, localizar e manipular arquivos.

A classe auxiliar,java.nio.file.Paths (no plural) é a maneira formal de criar objetosPath. Ele tem dois métodos estáticos para criar umPath a partir de uma string de caminho:

Path path = Paths.get("path string");

Não importa se usamos uma barra para frente ou para trás no caminhoString,, a API resolve esse parâmetro de acordo com os requisitos do sistema de arquivos subjacente.

E de um objetojava.net.URI:

Path path = Paths.get(URI object);

Agora podemos ir em frente e vê-los em ação.

4. Criando um Caminho

Para o objetocreate a Path de uma string de caminho:

@Test
public void givenPathString_whenCreatesPathObject_thenCorrect() {
    Path p = Paths.get("/articles/example");

    assertEquals("\\articles\\example", p.toString());
}

A APIget pode aceitar um parâmetro de argumentos variáveis ​​das partes da string do caminho (neste caso,articleseexample), além da primeira parte (neste caso,articles )

Se fornecermos essas partes em vez de uma cadeia de caminho completa, elas serão usadas para construir o objeto Path, não precisamos incluir os separadores de nomes (barras) na parte dos argumentos da variável:

@Test
public void givenPathParts_whenCreatesPathObject_thenCorrect() {
    Path p = Paths.get("/articles", "example");

    assertEquals("\\articles\\example", p.toString());
}

5. Recuperando Informações do Caminho

Você pode pensar no objeto Path como elementos de nome como uma sequência. Um caminhoString, comoE:\examplerticles\java consiste em três elementos de nome, ou seja, example,articles ejava. O elemento mais alto na estrutura do diretório estaria localizado no índice 0, neste caso sendoexample.

O elemento mais baixo na estrutura do diretório estaria localizado no índice[n-1], onden é o número de elementos de nome no caminho. Este elemento inferior é chamado defile name, independentemente de ser um arquivo real ou não:

@Test
public void givenPath_whenRetrievesFileName_thenCorrect() {
    Path p = Paths.get("/articles/example/logs");

    Path fileName = p.getFileName();

    assertEquals("logs", fileName.toString());
}

Os métodos estão disponíveis para recuperar elementos individuais por índice:

@Test
public void givenPath_whenRetrievesNameByIndex_thenCorrect() {
    Path p = Paths.get("/articles/example/logs");
    Path name0 = getName(0);
    Path name1 = getName(1);
    Path name2 = getName(2);
    assertEquals("articles", name0.toString());
    assertEquals("example", name1.toString());
    assertEquals("logs", name2.toString());
}

ou uma sub-sequência do caminho usando esses intervalos de índice:

@Test
public void givenPath_whenCanRetrieveSubsequenceByIndex_thenCorrect() {
    Path p = Paths.get("/articles/example/logs");

    Path subPath1 = p.subpath(0,1);
    Path subPath2 = p.subpath(0,2);

    assertEquals("articles", subPath1.toString());
    assertEquals("articles\\example", subPath2.toString());
    assertEquals("articles\\example\\logs", p.subpath(0, 3).toString());
    assertEquals("example", p.subpath(1, 2).toString());
    assertEquals("example\\logs", p.subpath(1, 3).toString());
    assertEquals("logs", p.subpath(2, 3).toString());
}

Cada caminho está associado a um caminho pai ounull se o caminho não tiver pai. O pai de um objeto de caminho consiste no componente raiz do caminho, se houver, e em cada elemento no caminho, exceto para o nome do arquivo. Por exemplo, o caminho pai de/a/b/c é/a/be o de/a é nulo:

@Test
public void givenPath_whenRetrievesParent_thenCorrect() {
    Path p1 = Paths.get("/articles/example/logs");
    Path p2 = Paths.get("/articles/example");
    Path p3 = Paths.get("/articles");
    Path p4 = Paths.get("/");

    Path parent1 = p1.getParent();
    Path parent2 = p2.getParent();
    Path parent3 = p3.getParent();
    Path parent4 = p4.getParenth();

    assertEquals("\\articles\\example", parent1.toString());
    assertEquals("\\articles", parent2.toString());
    assertEquals("\\", parent3.toString());
    assertEquals(null, parent4);
}

Também podemos obter o elemento raiz de um caminho:

@Test
public void givenPath_whenRetrievesRoot_thenCorrect() {
    Path p1 = Paths.get("/articles/example/logs");
    Path p2 = Paths.get("c:/articles/example/logs");

    Path root1 = p1.getRoot();
    Path root2 = p2.getRoot();

    assertEquals("\\", root1.toString());
    assertEquals("c:\\", root2.toString());
}

6. Normalizando um Caminho

Muitos sistemas de arquivos usam a notação“.” para denotar o diretório atual e“..” para denotar o diretório pai. Você pode ter uma situação em que um caminho contém informações de diretório redundantes.

Por exemplo, considere as seguintes cadeias de caminho:

/example/./articles
/example/authors/../articles
/example/articles

Todos eles resolvem para o mesmo local/example/articles. Os dois primeiros têm redundâncias, enquanto o último não.

Normalizar um caminho envolve remover redundâncias nele. A operaçãoPath.normalize() é fornecida para esse propósito.

Este exemplo agora deve ser auto-explicativo:

@Test
public void givenPath_whenRemovesRedundancies_thenCorrect1() {
    Path p = Paths.get("/home/./example/articles");

    Path cleanPath = p.normalize();

    assertEquals("\\home\\example\\articles", cleanPath.toString());
}

Esse também:

@Test
public void givenPath_whenRemovesRedundancies_thenCorrect2() {
    Path p = Paths.get("/home/example/../articles");

    Path cleanPath = p.normalize();

    assertEquals("\\home\\articles", cleanPath.toString());
}

7. Conversão de Caminho

Existem operações para converter um caminho em um formato de apresentação escolhido. Para converter qualquer caminho em uma string que pode ser aberta no navegador, usamos o métodotoUri:

@Test
public void givenPath_whenConvertsToBrowseablePath_thenCorrect() {
    Path p = Paths.get("/home/example/articles.html");

    URI uri = p.toUri();
    assertEquals(
      "file:///E:/home/example/articles.html",
        uri.toString());
}

Também podemos converter um caminho para sua representação absoluta. O métodotoAbsolutePath resolve um caminho em um diretório padrão do sistema de arquivos:

@Test
public void givenPath_whenConvertsToAbsolutePath_thenCorrect() {
    Path p = Paths.get("/home/example/articles.html");

    Path absPath = p.toAbsolutePath();

    assertEquals(
      "E:\\home\\example\\articles.html",
        absPath.toString());
}

No entanto, quando o caminho a ser resolvido for detectado como já absoluto, o método o retornará como é:

@Test
public void givenAbsolutePath_whenRetainsAsAbsolute_thenCorrect() {
    Path p = Paths.get("E:\\home\\example\\articles.html");

    Path absPath = p.toAbsolutePath();

    assertEquals(
      "E:\\home\\example\\articles.html",
        absPath.toString());
}

Também podemos converter qualquer caminho em seu equivalente real chamando o métodotoRealPath. Este método tenta resolver o caminho mapeando seus elementos para diretórios e arquivos reais no sistema de arquivos.

É hora de usar a variável que criamos na seçãoSetup que aponta para a localização inicial do usuário conectado no sistema de arquivos:

@Test
public void givenExistingPath_whenGetsRealPathToFile_thenCorrect() {
    Path p = Paths.get(HOME);

    Path realPath = p.toRealPath();

    assertEquals(HOME, realPath.toString());
}

O teste acima não nos diz muito sobre o comportamento desta operação. O resultado mais óbvio é que, se o caminho não existir no sistema de arquivos, a operação lançará umIOException, continue lendo.

Para a falta de uma maneira melhor de levar esse ponto para casa, basta dar uma olhada no próximo teste, que tenta converter um caminho inexistente em um caminho real:

@Test(expected = NoSuchFileException.class)
public void givenInExistentPath_whenFailsToConvert_thenCorrect() {
    Path p = Paths.get("E:\\home\\example\\articles.html");

    p.toRealPath();
}

O teste é bem-sucedido quando pegamos umIOException. A subclasse real deIOException que esta operação lança éNoSuchFileException.

8. Caminhos de união

A união de quaisquer dois caminhos pode ser obtida usando o métodoresolve.

Simplificando, podemos chamar o métodoresolve em qualquerPathe passarpartial path como argumento. Esse caminho parcial é anexado ao caminho original:

@Test
public void givenTwoPaths_whenJoinsAndResolves_thenCorrect() {
    Path p = Paths.get("/example/articles");

    Path p2 = p.resolve("java");

    assertEquals("\\example\\articles\\java", p2.toString());
}

No entanto, quando a string de caminho passada para o métodoresolve não é umpartial path; mais notavelmente um caminho absoluto, o caminho passado é retornado:

@Test
public void givenAbsolutePath_whenResolutionRetainsIt_thenCorrect() {
    Path p = Paths.get("/example/articles");

    Path p2 = p.resolve("C:\\example\\articles\java");

    assertEquals("C:\\example\\articles\\java", p2.toString());
}

O mesmo acontece com qualquer caminho que tenha um elemento raiz. A string de caminho“java” não tem elemento raiz, enquanto a string de caminho“/java” tem um elemento de raiz. Portanto, quando você passa um caminho com um elemento raiz, ele é retornado como está:

@Test
public void givenPathWithRoot_whenResolutionRetainsIt_thenCorrect2() {
    Path p = Paths.get("/example/articles");

    Path p2 = p.resolve("/java");

    assertEquals("\\java", p2.toString());
}

9. Relativizing caminhos

O termorelativizing significa simplesmente criar um caminho direto entre dois caminhos conhecidos. Por exemplo, se temos um diretório/examplee dentro dele, temos dois outros diretórios, tais como/example/authorse/example/articles são caminhos válidos.

O caminho paraarticles em relação aauthors seria descrito como“move one level up in the directory hierarchy then into articles directory” ou..rticles:

@Test
public void givenSiblingPaths_whenCreatesPathToOther_thenCorrect() {
    Path p1 = Paths.get("articles");
    Path p2 = Paths.get("authors");

    Path p1_rel_p2 = p1.relativize(p2);
    Path p2_rel_p1 = p2.relativize(p1);

    assertEquals("..\\authors", p1_rel_p2.toString());
    assertEquals("..\\articles", p2_rel_p1.toString());
}

Supondo que movamos o diretórioarticles para a pastaauthors de forma que eles não sejam mais irmãos. As seguintes operações de relativização envolvem a criação de um caminho entreexampleearticles e vice-versa:

@Test
public void givenNonSiblingPaths_whenCreatesPathToOther_thenCorrect() {
    Path p1 = Paths.get("/example");
    Path p2 = Paths.get("/example/authors/articles");

    Path p1_rel_p2 = p1.relativize(p2);
    Path p2_rel_p1 = p2.relativize(p1);

    assertEquals("authors\\articles", p1_rel_p2.toString());
    assertEquals("..\\..", p2_rel_p1.toString());
}

10. Comparando Caminhos

A classePath tem uma implementação intuitiva do métodoequals que nos permite comparar dois caminhos para igualdade:

@Test
public void givenTwoPaths_whenTestsEquality_thenCorrect() {
    Path p1 = Paths.get("/example/articles");
    Path p2 = Paths.get("/example/articles");
    Path p3 = Paths.get("/example/authors");

    assertTrue(p1.equals(p2));
    assertFalse(p1.equals(p3));
}

Você também pode verificar se um caminho começa com uma determinada sequência:

@Test
public void givenPath_whenInspectsStart_thenCorrect() {
    Path p1 = Paths.get("/example/articles");

    assertTrue(p1.startsWith("/example"));
}

Ou termina com outra string:

@Test
public void givenPath_whenInspectsEnd_thenCorrect() {
    Path p1 = Paths.get("/example/articles");

    assertTrue(p1.endsWith("articles"));
}

11. Conclusão

Neste artigo, mostramos as operações do Path na nova API do sistema de arquivos (NIO2), que foi enviada como parte do Java 7 e que a maioria delas estava em ação.

Os exemplos de código usados ​​neste artigo podem ser encontrados emGithub project do artigo.