Qual é a diferença entre um recurso, URI, URL, caminho e arquivo em Java?

95

Estou vendo um pedaço de código Java agora, e ele pega um caminho como uma String e obtém seu URL usando URL resource = ClassLoader.getSystemClassLoader().getResource(pathAsString);, em seguida, chama String path = resource.getPath()e, finalmente, executa new File(path);.

Ah, e também há chamadas para URL url = resource.toURI();e String file = resource.getFile().

Estou totalmente confuso agora - principalmente por causa da terminologia, eu acho. Alguém pode me explicar as diferenças ou fornecer alguns links para materiais à prova de fictícios? Especialmente URI para URL e Recurso para Arquivo ? Para mim, parece que deveriam ser a mesma coisa, respectivamente ...

A diferença entre getFile()e getPath()é explicada aqui: Qual é a diferença entre url.getFile () e getpath ()? (Curiosamente, os dois parecem retornar Strings, o que provavelmente acrescenta muito ao meu estado de espírito ...)

Agora, se eu tiver um localizador que faz referência a uma classe ou pacote em um arquivo jar, esses dois (isto é, caminho e strings de arquivo) serão diferentes?

resource.toString()lhe daria jar:file:/C:/path/to/my.jar!/com/example/, afinal (observe o ponto de exclamação).

A diferença entre URI e URL em Java é que o primeiro não codifica espaços? Cf. Arquivos, URIs e URLs em conflito em Java (esta resposta explica a diferença conceitual geral entre os dois termos muito bem: URIs identificam e URLs localizam; )

Por último - e mais importante - por que preciso do Fileobjeto; por que um Resource ( URL) não é suficiente? (E existe um objeto Resource?)

Desculpe se esta pergunta é um pouco desorganizada; apenas reflete a confusão que tenho ... :)

cristão
fonte
5
E você nem começou a olhar para Pathe FileSystem de NIO :)
eckes
2
@eckes Uma dor de cabeça de cada vez, por favor. ;)
Cristão de
1
Bem, no contexto da sua pergunta, Arquivo / URL + URI não estão relacionados. Um é um meio para nomear e operar em arquivos, o outro é um método para nomear e ler recursos (que podem ser arquivos). Os métodos getFile e getPath lidam com os componentes de uma URL que são (confusamente) nomeados como objetos de arquivo. Os recursos do carregador de classe não são representados como arquivos, pois podem ter origens diferentes (ou estar aninhados em arquivos JAR).
eckes
1
Eu observaria que é improvável que esse código funcione conforme o esperado. A URLé opaco - como você mostra jar:file:, ou seja, um recurso em um .jararquivo. Colocar isso em um Filedificilmente resultará em algo útil.
Boris the Spider
1
O cerne do seu problema é que as palavras recurso e caminho podem ter significados diferentes, dependendo do contexto.
Raedwald

Respostas:

43

ATUALIZAÇÃO 12-04-2017 Verifique a resposta do JvR, pois contém uma explicação mais exaustiva e exata!


Note que não me considero 100% competente para responder, mas mesmo assim aqui ficam alguns comentários:

  • File representa um arquivo ou diretório acessível através do sistema de arquivos
  • recurso é um termo genérico para um objeto de dados que pode ser carregado pelo aplicativo
    • geralmente os recursos são arquivos distribuídos com o aplicativo / biblioteca e carregados através do mecanismo de carregamento de classe (quando eles residem no caminho da classe)
  • URL#getPathé getter na parte do caminho do URL ( protocol://host/path?query)
  • URL#getFile conforme JavaDoc retorna path+query

Em Java, URIé apenas uma estrutura de dados para manipular o próprio identificador genérico.

URLpor outro lado, é realmente um localizador de recursos e oferece recursos para realmente ler o recurso por meio de URLStreamHandlers registrados .

URLs podem levar a recursos do sistema de arquivos e você pode construir URL para cada recurso do sistema de arquivos usando file://protocolo (portanto, relação File<-> URL).

Também esteja ciente de que isso URL#getFilenão está relacionado com java.io.File.


Por que preciso do objeto File; por que um Recurso (URL) não é suficiente?

É o suficiente. Somente se você deseja passar o recurso para algum componente que pode trabalhar apenas com arquivos, você precisa obtê File-lo. No entanto, nem todos os URLs de recursos podem ser convertidos em Files.

E existe um objeto Resource?

Do ponto de vista do JRE, é apenas um termo. Alguns frameworks fornecem essa classe (por exemplo, Spring's Resource ).

Pavel Horal
fonte
5
Há também o java.nio.file.Pathque é basicamente um substituto do (Java 7+) java.io.File, já que a última API era aparentemente mal planejada nos primeiros dias do Java.
ntoskrnl
1
Geralmente, você deve minimizar o uso de URL, a menos que seja absolutamente necessário. O motivo é que os métodos equals e hashCode da URL são implementados de uma maneira surpreendente: eles estão bloqueando chamadas de método.
kibibyte de
3
@kibibyte: Eu esperaria que a chamada estivesse bloqueando, tivesse uma implementação assíncrona de hashcode e igual a agora, o que seria muito perturbador. Acho que você quis dizer que as chamadas tentarão resolver o host para descobrir se são equivalentes e, portanto, podem bloquear chamadas de rede.
Newtopian
50

Estou totalmente confuso agora - principalmente por causa da terminologia, eu acho. Alguém pode me explicar as diferenças ou fornecer alguns links para materiais à prova de fictícios? Especialmente URI para URL e Recurso para Arquivo? Para mim, parece que deveriam ser a mesma coisa, respectivamente ...

A terminologia é confusa e às vezes atordoante, e principalmente nasceu da evolução do Java como uma API e como uma plataforma ao longo do tempo. Para entender como esses termos passaram a significar o que significam, é importante reconhecer duas coisas que influenciam o design do Java:

  • Compatibilidade com versões anteriores. Os aplicativos antigos devem ser executados em instalações mais recentes, de preferência sem modificações. Isso significa que uma API antiga (com seus nomes e terminologia) precisa ser mantida em todas as versões mais recentes.
  • Plataforma cruzada. A API deve fornecer uma abstração utilizável de sua plataforma subjacente, seja um sistema operacional ou um navegador.

Vou examinar os conceitos e como eles surgiram. Vou responder suas outras perguntas específicas depois disso, porque posso ter que me referir a algo na primeira parte.

O que é um "Recurso"?

Um dado genérico e abstrato que pode ser localizado e lido. Em poucas palavras, Java usa isso para se referir a um "arquivo" que pode não ser um arquivo, mas representa uma parte nomeada de dados. Ele não tem uma classe direta ou representação de interface em Java , mas por causa de suas propriedades (localizável, legível), geralmente é representado por uma URL.

Como um dos primeiros objetivos de design do Java era ser executado dentro de um navegador, como um aplicativo em sandbox (applets!) Com direitos / privilégios / autorização de segurança muito limitados, o Java faz uma diferença (teórica) clara entre um arquivo (algo no local sistema de arquivos) e um recurso (algo que ele precisa ler). É por isso que a leitura de algo relativo ao aplicativo (ícones, arquivos de classe e assim por diante) é feita por meio ClassLoader.getResourcee não por meio da classe File.

Infelizmente, como "recurso" também é um termo genérico útil fora dessa interpretação, ele também é usado para nomear coisas muito específicas (por exemplo, classe ResourceBundle , UIResource , Resource ) que não são, neste sentido, um recurso.

As principais classes que representam (um caminho para) um recurso são java.nio.file.Path , java.io.File , java.net.URI e java.net.URL .

Arquivo (java.io, 1.0)

Uma representação abstrata de nomes de caminho de arquivo e diretório.

A classe File representa um recurso que pode ser acessado por meio do sistema de arquivos nativo da plataforma . Ele contém apenas o nome do arquivo, então é realmente mais um caminho (veja mais tarde) que a plataforma host interpreta de acordo com suas próprias configurações, regras e sintaxe.

Observe que Arquivo não precisa apontar para algo local , apenas algo que a plataforma host entenda no contexto de acesso ao arquivo, por exemplo, um caminho UNC no Windows. Se você montar um arquivo ZIP como um sistema de arquivos em seu sistema operacional, o Arquivo lerá suas entradas contidas perfeitamente.

URL (java.net, 1.0)

URL de classe representa um Localizador Uniforme de Recursos, um ponteiro para um "recurso" na World Wide Web. Um recurso pode ser algo tão simples como um arquivo ou diretório, ou pode ser uma referência a um objeto mais complicado, como uma consulta a um banco de dados ou a um mecanismo de pesquisa.

Paralelamente ao conceito de recurso, a URL representa esse recurso da mesma forma que a classe File representa um arquivo na plataforma host: como uma string estruturada que aponta para um recurso. A URL contém adicionalmente um esquema que sugere como acessar o recurso (com "arquivo:" sendo "pergunte à plataforma do host") e, portanto, permite apontar para recursos por meio de HTTP, FTP, dentro de um JAR e outros enfeites.

Infelizmente, os URLs vêm com sua própria sintaxe e terminologia, incluindo o uso de "arquivo" e "caminho". Caso a URL seja uma URL de arquivo, URL.getFile retornará uma string idêntica à string do caminho do arquivo referenciado.

Class.getResource retorna um URL: é mais flexível do que retornar File e atendeu às necessidades do sistema imaginadas no início dos anos 90.

URI (java.net, 1.4)

Representa uma referência de URI (Uniform Resource Identifier).

URI é uma (leve) abstração sobre URL. A diferença entre URI e URL é conceitual e principalmente acadêmica, mas URI é melhor definido em um sentido formal e cobre uma gama mais ampla de casos de uso. Como URL e URI são / não são a mesma coisa, uma nova classe foi introduzida para representá-los, com os métodos URI.toURL e URL.toURI para mover entre um e outro.

Em Java, a principal diferença entre URL e URI é que uma URL carrega a expectativa de ser resolvida , algo a partir do qual o aplicativo pode querer um InputStream; um URI é tratado mais como um thingamajig abstrato que pode apontar para algo resolvível (e geralmente faz), mas o que significa e como alcançá-lo são mais abertos ao contexto e interpretação.

Caminho (java.nio.file, 1.7)

Um objeto que pode ser usado para localizar um arquivo em um sistema de arquivos. Normalmente representa um caminho de arquivo dependente do sistema.

A nova API de arquivo, iconificada na interface Path, permite uma flexibilidade muito maior do que a classe File poderia oferecer. A interface Path é uma abstração da classe File e faz parte da New IO File API . Onde File necessariamente aponta para um "arquivo" conforme entendido pela plataforma host, Path é mais genérico: ele representa um arquivo (recurso) em um sistema de arquivos arbitrário .

O caminho tira a confiança no conceito de arquivo da plataforma host. Pode ser uma entrada em um arquivo ZIP, um arquivo acessível por FTP ou SSH-FS, uma representação com várias raízes do classpath do aplicativo ou realmente qualquer coisa que possa ser representada de forma significativa por meio da interface FileSystem e seu driver, FileSystemProvider. Ele traz o poder de "montar" sistemas de arquivos no contexto de um aplicativo Java.

A plataforma host é representada por meio do "sistema de arquivos padrão"; quando você chama File.toPath, você obtém um caminho no sistema de arquivos padrão.


Agora, se eu tiver um localizador que faz referência a uma classe ou pacote em um arquivo jar, esses dois (isto é, caminho e strings de arquivo) serão diferentes?

Improvável. Se o arquivo jar é no sistema de arquivos local, você não deve ter um componente de consulta, de modo URL.getPathe URL.getFiledeve retornar o mesmo resultado. No entanto, escolha o que você precisa: arquivos-URLs podem não ter componentes de consulta, mas eu poderia adicionar um de qualquer maneira.

Por último - e mais importante - por que preciso do objeto File; por que um Recurso (URL) não é suficiente?

URL pode não ser suficiente porque Arquivo fornece acesso a dados de manutenção, como permissões (legível, gravável, executável), tipo de arquivo (sou um diretório?) E a capacidade de pesquisar e manipular o sistema de arquivos local. Se esses forem os recursos de que você precisa, Arquivo ou Caminho os fornecerá.

Você não precisa do Arquivo se tiver acesso ao Caminho. No entanto, algumas APIs mais antigas podem exigir o arquivo.

(E existe um objeto Resource?)

Não, não há. Há muitas coisas com nomes semelhantes, mas não são um recurso no sentido de ClassLoader.getResource.

JvR
fonte
Uau, muito completo. Só estou passando por isso, mas já tenho a primeira pergunta de acompanhamento: Quando você diz que um arquivo "contém apenas o nome do arquivo", não contradiga sua afirmação inicial de que é "Uma representação abstrata de nomes de caminho de arquivo e diretório" - iemore?
Cristão de
1
@Christian eu quis dizer "apenas o nome" como em: não modela de forma alguma o conteúdo do arquivo; é apenas um invólucro fino em volta de um barbante. A parte "representação abstrata" é citada nos documentos da API. ;)
JvR de
Esta resposta merece ter muito mais votos positivos ... atualizarei minha resposta aceita para indicar esta aos leitores.
Pavel Horal
12

A resposta de Pavel Horal é boa.

Como ele diz, a palavra "arquivo" tem significados totalmente diferentes (praticamente não relacionados) em URL#getFilevs java.io.File- pode ser que isso seja parte da confusão.

Apenas para adicionar:

  • Um recurso em Java é um conceito abstrato, uma fonte de dados que pode ser lida. A localização (ou endereço) de um recurso é representada em Java por um URLobjeto.

  • Um recurso pode corresponder a um arquivo regular no sistema de arquivos local (especificamente, quando URLcomeça com file://). Mas um recurso é mais geral (pode ser também algum arquivo armazenado em um jar, ou alguns dados a serem lidos da rede, ou da memória, ou ...). E também é mais limitado, porque um File(além de ser outras coisas que um arquivo normal: um diretório, um link) também pode ser criado e gravado.

  • Lembre-se de que em Java um Fileobjeto não representa realmente "um arquivo", mas a localização (o nome completo, com o caminho) de um arquivo. Assim, um Fileobjeto permite localizar (e abrir) um arquivo, assim como URLpermite acessar (e abrir) um recurso. (Não existe nenhuma Resourceclasse em Java para representar um recurso, mas também não existe uma para representar um arquivo! Mais uma vez: Filenão é um arquivo, é o caminho de um arquivo).

leonbloy
fonte
3

Pelo que entendi, você poderia categorizá-los da seguinte forma:

Baseado na Web: URIs e URLs.

  • URLs: um URL é um local definido no interno (apenas um endereço da web normal como - stackoverflow.com)
  • URIs: cada URL é um URI. Mas URIs também podem conter coisas como "mailto:", então eles também são, bem, um tipo de "script", eu diria.

E local: Recurso, Caminho e Arquivos

  • Recurso: os recursos são arquivos dentro do seu jar. Eles são usados ​​para carregar arquivos de jars / contêineres.
  • Caminho: um caminho é basicamente uma string. Mas ele vem com algumas funções úteis para concatenar várias strings ou adicionar arquivos a uma string. Isso garante que o caminho que você está construindo é válido.
  • Arquivo: é uma referência a um diretório ou arquivo. É usado para modificar arquivos, abri-los etc.

Seria mais fácil se eles fossem mesclados em uma classe - eles são realmente confusos: D

Espero que isso ajude você :)

(Acabei de dar uma olhada na documentação - veja docs.oracle.com)

Cyfrags
fonte
0

Um arquivo é uma representação abstrata de uma entidade no sistema de arquivos local.

Um caminho geralmente é uma string que indica a localização de um arquivo em um sistema de arquivos. Geralmente não inclui o nome do arquivo. Portanto, c: \ documents \ mystuff \ stuff.txt teria um caminho com o valor de "C: \ documents \ mystuff" Obviamente, o formato dos nomes de arquivos e caminhos absolutos variariam enormemente de sistema de arquivo para sistema de arquivo.

URL é um subconjunto de URI com URL geralmente representando recursos acessíveis por http. Não acho que haja qualquer tipo de regra rígida sobre quando algo deve ser um URI versus um URL. URIs são strings na forma de "protocol: // resource-identifier", como bitcoin: // params, http://something.com?param=value . Classes como URL geralmente envolvem a string e fornecem métodos utilitários que String não teria razão para fornecer.

Recurso não existe, pelo menos não no sentido de que você está falando. Só porque um método é denominado getResource não significa que ele retorne um objeto do tipo Resource.

Em última análise, a melhor maneira de descobrir o que os métodos de uma classe fazem é criar uma instância dela em seu código, chamar os métodos e, em seguida, avançar no modo de depuração ou enviar os resultados para System.out.

Jim W
fonte
Sua definição de "caminho" NÃO corresponde ao conceito de "caminho" no contexto do OP
leonbloy