W3docs

Classe Properties do Java

Carregue e armazene pares chave-valor string no Java com a classe Properties, incluindo arquivos .properties.

Properties é o contêiner do JDK para configuração string-para-string — configurações de aplicação, substituições de ambiente, mensagens localizadas, parâmetros de conexão JDBC. Ela estende Hashtable<Object, Object> (uma decisão histórica que hoje lamentamos, mas com a qual convivemos) e adiciona três coisas: um formato de arquivo .properties com leitor e escritor, um leitor e escritor XML, e o conceito de um objeto default de propriedades consultado quando uma chave não é encontrada localmente.

System.getProperties() retorna um Properties. Toda chamada a System.getProperty("user.home") passa por ele. Uma vez que você conhece a classe, a encontra em todo lugar.

O contrato: strings nos dois lados

Embora a classe herde um método put(Object, Object) de Hashtable, a única API segura é o par tipado como string:

Properties config = new Properties();
config.setProperty("server.port", "8080");
config.setProperty("server.host", "localhost");
String port = config.getProperty("server.port");          // "8080"
String log  = config.getProperty("log.level", "INFO");    // default fallback

Se você ignorar setProperty e chamar put("server.port", 8080) com um Integer, a entrada ainda será inserida na tabela, mas você terá plantado uma bomba-relógio: stringPropertyNames() a filtra silenciosamente, e os métodos de escrita em arquivo (store, storeToXML) lançam ClassCastException no momento em que tentam converter esse Integer para String. Trate Properties como Properties<String, String> mesmo que os genéricos não digam isso.

O formato de arquivo .properties

Texto simples, orientado a linhas, key=value. Espaços em branco ao redor de = são permitidos. Linhas começando com # ou ! são comentários. Barra invertida no final continua o valor na próxima linha. Escapes Unicode (\uXXXX) são suportados, mas desde o Java 9 a sobrecarga load(Reader) lê UTF-8 nativamente, então raramente são necessários.

# server.properties — last edited 2026-05-12
server.host = localhost
server.port = 8080
server.path = /api/v1
greeting    = Welcome, \
              user!

load e store lidam com esse formato. loadFromXML e storeToXML lidam com o formato XML equivalente definido por properties.dtd — existe, às vezes é útil, mas quase nunca é preferido em relação à forma textual.

Carregando e armazenando

Properties config = new Properties();
try (var in = Files.newBufferedReader(Path.of("server.properties"))) {
  config.load(in);              // UTF-8 text
}

config.setProperty("server.port", "9090");

try (var out = Files.newBufferedWriter(Path.of("server.properties"))) {
  config.store(out, "edited by setup script");   // comment becomes the first line
}

O método store escreve um comentário de timestamp após o comentário do usuário, não ordena nada (as entradas ficam na ordem de iteração do Hashtable), e escapa caracteres especiais (=, :, #, espaços em branco no início) para permitir round-tripping. A saída é portável entre JVMs.

Para recursos empacotados com sua aplicação, carregue a partir do classpath em vez do sistema de arquivos:

try (var in = MyApp.class.getResourceAsStream("/app.properties")) {
  config.load(in);              // load(InputStream) defaults to ISO-8859-1
}

load(InputStream) é a sobrecarga histórica e usa ISO-8859-1 (Latin-1) com escapes \u. load(Reader) usa o charset com o qual o reader foi aberto. Prefira a forma com reader quando você controla a codificação.

Propriedades padrão: configuração em camadas

O getProperty(key, default) com dois argumentos retorna um valor substituto quando a chave está ausente. O construtor Properties(Properties defaults) faz a mesma coisa, mas no nível do objeto — o segundo Properties é consultado quando o primeiro não contém a chave:

Properties base = new Properties();
base.setProperty("server.port", "8080");
base.setProperty("log.level",   "INFO");

Properties override = new Properties(base);   // base is the defaults
override.setProperty("log.level", "DEBUG");   // override wins

override.getProperty("server.port");          // "8080"  (from base)
override.getProperty("log.level");            // "DEBUG" (from override)

Esse é o padrão padrão para "defaults fornecidos com o app, o usuário pode sobrescrever por ambiente." Duas camadas é o caso comum; você pode encadear mais.

Propriedades do System e flags -D

A JVM tem uma instância global de Properties acessível via System.getProperties() e System.getProperty(key). Chaves padrão incluem java.version, user.home, user.dir, os.name, file.separator e line.separator. A flag -Dkey=value na linha de comando da JVM adiciona a ela antes que main execute:

java -Dserver.port=9090 -Dlog.level=DEBUG -jar app.jar
String port = System.getProperty("server.port", "8080");

Essa é a "configuração por linha de comando" mais simples que você pode dar a um programa Java. Para configurações maiores, a convenção é um arquivo .properties fornecido com o app, mesclado na inicialização com as propriedades do sistema (que atuam como substituições).

O que Properties não é

  • Não é um mapa de tipos arbitrários. Apenas strings. Analise você mesmo com Integer.parseInt(config.getProperty("port")).
  • Não é hierárquico. Chaves como db.primary.host são apenas strings; os pontos são convencionais, não estruturais. Se você precisar de hierarquia real, use uma biblioteca de configuração YAML/JSON.
  • Não é thread-safe para operações compostas. Todo método é sincronizado (herdado de Hashtable), mas verificar e agir ainda tem condição de corrida. Mesmo aviso da classe pai.
  • Não é substituto de ResourceBundle para i18n. PropertyResourceBundle é um ResourceBundle baseado em um arquivo .properties e adiciona busca por locale; essa é a ferramenta correta para strings traduzidas.

Um exemplo completo: carregar defaults, sobrescrever por ambiente, salvar de volta

O programa abaixo constrói uma configuração em camadas (defaults dentro do JAR, arquivo de ambiente fora), lê uma propriedade do sistema para agir como uma substituição -D, achata o resultado para que possa ser salvo de volta em um buffer .properties para inspeção, e demonstra a armadilha do store com valor não string.

Uma sutileza que o exemplo trata deliberadamente: store escreve apenas as entradas próprias de um objeto Properties — nunca percorre a cadeia de defaults herdada. Portanto, para obter um arquivo completo e round-trippável, copiamos cada chave resolvida em um Properties plano antes de armazenar, em vez de chamar store em um objeto que depende de seu pai defaults.

java— editable, runs on the server

O que extrair da execução:

  • A configuração em três camadas (defaults → arquivo de ambiente → substituições -D) resolve corretamente. Os defaults preenchem o que ninguém substituiu; o arquivo de ambiente muda log.level e feature.beta; a flag -D vence para server.port.
  • store produziu um texto .properties portável com um comentário e um timestamp no topo, contendo todas as quatro chaves resolvidas. Você poderia passar esse arquivo diretamente para load e obter o mesmo mapa — porque achatamos as camadas primeiro.
  • setProperty("age", 30) não compilaria (requer uma String). put("age", 30) compila, a entrada fica na tabela, e stringPropertyNames a filtra — mas store não a ignora silenciosamente: ele lança ClassCastException assim que tenta converter o Integer para String. A lição: nunca use put com valor não string em um Properties — sempre use setProperty.

O que vem a seguir

Properties foi o último capítulo de "estrutura de dados" desta parte. Os capítulos restantes são sobre operações em coleções: percorrê-las (Iterators e ListIterator), comparar elementos (Comparable e Comparator), e os utilitários estáticos para ordenar, pesquisar e encapsular (a classe Collections). O próximo capítulo começa com a fundação — a interface Iterator que todo loop for-each usa secretamente.

Prática

Prática
Você escreve `props.put('port', 8080)` (um `Integer`) em um objeto `Properties` e depois chama `props.store(out, null)`. O que acontece?
Você escreve `props.put('port', 8080)` (um `Integer`) em um objeto `Properties` e depois chama `props.store(out, null)`. O que acontece?
Was this page helpful?