Book: Explorando APIs e bibliotecas Java


© Casa do Código
Todos os direitos reservados e protegidos pela Lei nº9.610, de
10/02/1998.
Nenhuma parte deste livro poderá ser reproduzida, nem transmitida, sem
autorização prévia por escrito da editora, sejam quais forem os meios:
fotográficos, eletrônicos, mecânicos, gravação ou quaisquer outros.
Casa do Código
Livros para o programador
Rua Vergueiro, 3185 - 8º andar
04101-300 – Vila Mariana – São Paulo – SP – Brasil
Casa do Código
“Às famílias Bertoldo, Ferreira e Turini.”
– Rodrigo Turini
i
Casa do Código
Agradecimentos
Em primeiro lugar, gostaria muito de agradecer a você, o leitor. Foi para você que cuidadosamente escrevi esse livro, pensando sempre em como o conteúdo poderia ser aplicado em seu dia a dia. Espero atender e, quem sabe,
superar as suas expectativas.
Para evitar consequências imprevisíveis, não posso deixar de agradecer à
minha esposa Jordana. Sem seu valioso incentivo este livro não passaria de
um plano. E também para pequena Katherine, que nesse momento provavel-
mente está chutando a sua barriga.
Não posso deixar de mencionar a família Silveira, que, pra mim, são os
maiores incentivadores do mundo. Ao Paulo, pela presença, incentivo e men-
toring. Ao Guilherme, pelas ideias, pareamentos e todo conhecimento com-
partilhado. E finalmente ao Sr. Carlos, pela cultura e inspiração diária.
Por fim, mas nem um pouco menos importante, a toda equipe da Caelum
e Alura. Em especial ao Victor Harada, pois sem suas ideias, críticas e dis-
cussões, boa parte deste livro seria diferente.
iii
Casa do Código
Sumário
Sumário
1
Introdução
1
1.1
O projeto e as tecnologias . . . . . . . . . . . . . . . . . . . . .
1
1.2
Instalando e configurando o Eclipse . . . . . . . . . . . . . . .
2
1.3
Download dos arquivos pro projeto . . . . . . . . . . . . . . .
3
1.4
Acesse o código desse livro . . . . . . . . . . . . . . . . . . . .
4
1.5
Aproveitando ao máximo o conteúdo . . . . . . . . . . . . . .
5
1.6
Tirando suas dúvidas . . . . . . . . . . . . . . . . . . . . . . .
5
2
Java FX
7
2.1
Nossa primeira App em Java FX . . . . . . . . . . . . . . . . .
7
2.2
Configurando a livraria-base . . . . . . . . . . . . . . . . . . .
11
2.3
Preparando nosso cenário . . . . . . . . . . . . . . . . . . . .
13
2.4
Uma listagem de produtos . . . . . . . . . . . . . . . . . . . .
18
3
Java IO
29
3.1
Entrada e saída de dados . . . . . . . . . . . . . . . . . . . . .
29
3.2
Lendo um arquivo de texto . . . . . . . . . . . . . . . . . . . .
30
3.3
Lendo texto do teclado com System.in . . . . . . . . . . . . .
34
3.4
Tornando tudo mais simples com Scanner . . . . . . . . . . .
36
3.5
Saída de dados e o OutputStream . . . . . . . . . . . . . . . .
38
3.6
Escrita mais simples com PrintStream . . . . . . . . . . . . .
42
3.7
Gerando um CSV de produtos . . . . . . . . . . . . . . . . . .
43
3.8
Botão de exportar produtos
. . . . . . . . . . . . . . . . . . .
48
3.9
Adicionando ações com setOnAction . . . . . . . . . . . . . .
49
3.10 JavaFx e Java 8 . . . . . . . . . . . . . . . . . . . . . . . . . . .
52
v
Sumário
Casa do Código
4
Banco de Dados e JDBC
57
4.1
Iniciando com MySQL . . . . . . . . . . . . . . . . . . . . . .
57
4.2
Criando a tabela de produtos . . . . . . . . . . . . . . . . . . .
60
4.3
O pacote java.sql e o JDBC . . . . . . . . . . . . . . . . . . . .
61
4.4
Abrindo conexão com MySQL em Java . . . . . . . . . . . . .
63
4.5
Listando todos os produtos do banco . . . . . . . . . . . . . .
66
4.6
Importando produtos de um dump . . . . . . . . . . . . . . .
69
4.7
Para saber mais: Adicionando programaticamente . . . . . .
71
4.8
Qual a melhor forma de fechar a conexão? . . . . . . . . . . .
75
4.9
O padrão de projeto DAO . . . . . . . . . . . . . . . . . . . .
78
5
Threads e Paralelismo
85
5.1
Processamento demorado, e agora? . . . . . . . . . . . . . . .
86
5.2
Trabalhando com Threads em Java . . . . . . . . . . . . . . .
87
5.3
O contrato Runnable . . . . . . . . . . . . . . . . . . . . . . .
89
5.4
Threads com classes anônimas e lambdas
. . . . . . . . . . .
91
5.5
Exportando em uma thread separada . . . . . . . . . . . . . .
94
5.6
Um pouco mais sobre as Threads . . . . . . . . . . . . . . . .
99
5.7
Garbage Collector . . . . . . . . . . . . . . . . . . . . . . . . .
101
5.8
Java FX assíncrono . . . . . . . . . . . . . . . . . . . . . . . . .
103
5.9
Trabalhando com a classe Task . . . . . . . . . . . . . . . . . .
104
5.10 Código final com e sem lambdas . . . . . . . . . . . . . . . . .
112
6
CSS no Java FX
117
6.1
Seu primeiro CSS no Java FX . . . . . . . . . . . . . . . . . . .
118
6.2
Extraindo estilos pra um arquivo .css . . . . . . . . . . . . . .
120
7
JAR, bibliotecas e build
135
7.1
JAR
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
135
7.2
Gerando JAR executável pela IDE . . . . . . . . . . . . . . . .
136
7.3
Executando a livraria-fx.jar . . . . . . . . . . . . . . . . . . . .
138
7.4
Bibliotecas em Java
. . . . . . . . . . . . . . . . . . . . . . . .
140
7.5
Documentando seu projeto com Javadoc . . . . . . . . . . . .
141
vi
Casa do Código
Sumário
7.6
Automatizando build com Maven . . . . . . . . . . . . . . . .
146
7.7
Transformando nossa app em um projeto Maven . . . . . . .
147
7.8
Adicionando as dependências com Maven . . . . . . . . . . .
151
7.9
Executando algumas tasks do Maven . . . . . . . . . . . . . .
157
7.10
Adicionando plugin do Java FX . . . . . . . . . . . . . . . . .
159
7.11
Maven na linha de comando . . . . . . . . . . . . . . . . . . .
163
7.12
Como ficou nosso pom.xml . . . . . . . . . . . . . . . . . . .
165
8
Refatorações
169
8.1
Refatoração . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
170
8.2
Os tão populares Design Patterns . . . . . . . . . . . . . . . .
175
9
Próximos passos com Java
179
9.1
Entre em contato conosco
. . . . . . . . . . . . . . . . . . . .
180
vii
Capítulo 1
Introdução
1.1
O projeto e as tecnologias
Durante este livro, trabalharemos no projeto de uma livraria. A princípio,
vamos criar uma listagem de produtos com Java FX e ao decorrer dos capítu-
los incrementaremos suas funcionalidades, passando pelas APIs de IO, JDBC,
Threads e muito mais. Além disso, a todo o momento discutiremos sobre boas
práticas da orientação a objetos e design patterns.
Ao final, o projeto deve ficar parecido com:

1.2. Instalando e configurando o Eclipse
Casa do Código
Fig. 1.1: Aparência final do projeto deste livro
Essa listagem de livros é carregada de um banco de dados MySQL, uti-
lizando a API de JDBC. O build e gerenciamento de dependências é total-
mente automatizado, com uso do tão popular Maven. A ação de exportar os
livros é feita em uma Thread diferente, que roda em paralelo com a Thread
principal da aplicação.
Nosso código já usará a sintaxe e recursos do Java 8, como as famosas ex-
pressões lambdas. Mas não se preocupe, você não precisa conhecer Java 8 ou
nenhuma outra tecnologia além de Java puro para continuar, cada novidade
será muito bem detalhada. Pronto para começar?
1.2
Instalando e configurando o Eclipse
Para começar, você precisará do Eclipse ou qualquer outra IDE de sua prefer-
ência. Utilizarei o Eclipse em meus exemplos, devido ao ganho de produtivi-
2
Casa do Código
Capítulo 1. Introdução
dade (que será mencionado em alguns pontos do livro), além de sua popu-
laridade no mercado e instituições de ensino. Se quiser, você pode fazer o
download do Eclipse em:
https://www.eclipse.org/downloads/
Para utilizar o Java FX no Eclipse, você também precisará do e( fx)clipse,
que pode ser baixado pelo seguinte link, no qual você também encontrará um
passo a passo para instalá-lo.
http://www.eclipse.org/efxclipse/install.html
Algumas outras opções
Além do Eclipse, existem diversas outras IDEs que podem ser utilizadas
no desenvolvimento desse projeto. Netbeans e Intellij IDEA são algumas delas.
O Netbeans já vem com suporte ao Java FX, sendo assim você não precisará
instalar nada. Você pode ler mais sobre ele e fazer o download em:
https://netbeans.org/
Segundo as pesquisas mais recentes, logo depois do Eclipse, o Intellij IDEA
é a IDE mais utilizada pelos desenvolvedores Java. Mas há uma questão: ela
é paga. Você pode baixar uma versão Community Edition (gratuita), mas ela
deixa um pouco a desejar. Se quiser experimentar a versão completa, tem
como opção baixar um trial por 30 dias.
https://www.jetbrains.com/idea/
Apesar de utilizamos Eclipse nos exemplos desse livro, você pode usar a
IDE de sua preferência. Fique à vontade em sua escolha.
1.3
Download dos arquivos pro projeto
No link a seguir, você encontrará todos os arquivos necessários para acom-
panhar ativamente os códigos desse livro. Ele inclui a base do projeto, os jars necessários para você fazer os exercícios, um arquivo de dump com alguns
livros já cadastrados no banco, entre outros.
http://goo.gl/CfzTLB
3

1.4. Acesse o código desse livro
Casa do Código
Desbravando a Orientação a Objetos
Se você já leu meu livro Desbravando a Orientação a Objetos, perce-
berá que esta é uma evolução do projeto que desenvolvemos por lá. Você
pode continuar com seu próprio projeto, ou baixar o projeto já pronto se
preferir.
Fig. 1.2: Desbravando Java e Orientação a Objetos
http://www.casadocodigo.com.br/products/
livro-orientacao-objetos-java
1.4
Acesse o código desse livro
Todos os exemplos desse livro e também seu código final, em Java 7 e 8, podem ser encontrados no seguinte repositório:
• https://github.com/Turini/livro-java
Não deixe de escrever todos os códigos e exercitá-los durante os capítulos.
Assim você pode tirar um proveito maior do conteúdo. Vá além do que é aqui
sugerido, escreva novos testes, novos layouts e não deixe de compartilhar sua experiência conosco.
4

Casa do Código
Capítulo 1. Introdução
1.5
Aproveitando ao máximo o conteúdo
Além de colocar todos os exemplos em prática, não deixe de consultar tam-
bém a documentação de cada API e biblioteca que vamos utilizar. Eu sempre
disponibilizo alguns links que podem ser úteis quando um novo assunto é
introduzido.
Como nosso foco principal é Java, tenha sempre por perto a documen-
tação da linguagem:
http://docs.oracle.com/javase/8/docs/api/
Se estiver devidamente configurado, você pode ver a documentação das
classes e métodos pelo próprio Eclipse. Sempre que estou conhecendo uma
nova API, costumo usar o recurso de autocomplete (atalho
Control +
space) e navegar pelos seus métodos.
Veja que, quando paramos em um método específico, um box com sua
documentação é exibido:
Fig. 1.3: Explorando a API do Java pelo Eclipse
1.6
Tirando suas dúvidas
Ficou com alguma dúvida durante o livro? Mande um e-mail! Uma lista de
discussões foi criada exclusivamente para facilitar seu contato comigo e com
os demais leitores deste livro. Você pode e deve compartilhar suas dúvidas,
ideias, sugestões e criticas. Todas serão muito bem-vindas.
https://groups.google.com/group/livro-java
5
1.6. Tirando suas dúvidas
Casa do Código
Outro recurso que você pode utilizar para esclarecer suas dúvidas e par-
ticipar ativamente na comunidade Java é o fórum do GUJ. Lá você não só
pode perguntar, mas também responder, editar, comentar e assistir a diversas
discussões sobre o universo da programação.
http://www.guj.com.br/
6
Capítulo 2
Java FX
2.1
Nossa primeira App em Java FX
Neste capítulo, criaremos uma tela simples usando Java FX, que servirá como
base para integrarmos algumas das outras APIs que veremos no decorrer do
livro. Apesar de nosso foco não ser a interface gráfica em si, escolhemos o
Java FX por ser uma alternativa muito interessante e mais moderna do que o
swing, além de ser bastante elegante. O Java FX já vem incluso no JDK e JRE.
Criando o projeto no Eclipse
Agora que já configuramos nosso Eclipse para escrever aplicações em Java
FX, podemos criar um novo projeto. Para fazer isso, basta clicar em File > New > Other... e escolher a opção Java FX Project. Podemos chamar
o projeto de livraria-fx, depois clicar em finish para concluir.

2.1. Nossa primeira App em Java FX
Casa do Código
Se você estiver utilizando uma versão atual do Eclipse (o que é bastante
recomendado), já deverá aparecer uma estrutura padrão para sua aplicação,
como na seguinte imagem:
Fig. 2.1: Estrutura inicial do projeto no Eclipse
Note que dentro do src, temos uma pacote chamado application
com a classe Main.java e o arquivo application.css. Veremos mais
detalhes sobre esse arquivo .css mais à frente, por enquanto vamos focar
no código Java. A classe Main já deve ter sido criada com o seguinte código:
public class Main extends Application {
@Override
public void start(Stage primaryStage) {
try {
BorderPane root = new BorderPane();
Scene scene = new Scene(root,400,400);
scene.getStylesheets()
.add(getClass().getResource("application.css")
.toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
8
Casa do Código
Capítulo 2. Java FX
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
Não se preocupe caso em seu Eclipse, ou qualquer outra IDE de sua
preferência, essa classe não tenha sido criada. Você pode criá-la facilmente, mas aproveite e mantenha apenas o código que importa agora nesse começo.
Vamos apagar esse try catch e linhas que criam a classe Scene e aplicam
o estilo css. Por agora só precisamos do seguinte código:
public class Main extends Application {
@Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Sistema da livraria com Java FX");
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Note que já adicionei um titulo que não existia no arquivo Java, faça o
mesmo e execute essa classe para ver o primeiro resultado. Se tudo correu
bem, uma tela cinza com o título Sistema da livraria com Java FX deve apare-
cer:
9

2.1. Nossa primeira App em Java FX
Casa do Código
Fig. 2.2: Primeira tela em Java FX
Executando código no Eclipse
Caso não esteja familiarizado com o Eclipse, você pode executar seu
código de diversas maneiras. Uma delas é clicando com o botão direito na
classe, selecionando Run As... e depois Java Application. Outra
alternativa bem prática é utilizando o atalho Control + F11.
Excelente, já temos algum resultado. Mas antes de seguir precisamos en-
tender um pouco mais sobre a anatomia desse nosso primeiro código.
Podemos começar pela classe javafx.application.Application.
A regra é simples, a classe principal de nossas aplicações em Java FX pre-
cisa herdar de Application e consequentemente sobrescrever seu único
método abstrato, chamado start. Esse é o ponto de partida de toda apli-
cação que em Java FX.
Vale lembrar que como esse método é abstrato, somos obrigados a
sobrescrevê-lo. Portanto, caso você não lembre disso, o compilador certa-
mente lhe lembrará com um erro. Além disso, perceba que esse método re-
10

Casa do Código
Capítulo 2. Java FX
cebe um objeto do tipo Stage como parâmetro, que é o container principal
da aplicação.
Por fim, note também que temos um método main. Nele, invocamos um
outro método chamado launch, também presente na classe Application.
Ele é o responsável por iniciar a aplicação.
2.2
Configurando a livraria-base
Agora que já sabemos um pouco mais sobre esse código inicial, vamos
começar a implementar a nossa listagem de Produtos. Para isso, precisare-
mos da interface Produto e de suas implementações, presentes no arquivo
que você deve ter baixado no capítulo anterior. Não se preocupe se você ainda não baixou, você pode fazer isso agora pelo seguinte link:
http://goo.gl/CfzTLB
Após concluir o download, você precisará importar o projeto no Eclipse.
Primeiro, extraia o conteúdo do ZIP que você baixou para alguma pasta de
sua preferência. Agora, pelo Eclipse, clique no menu File, Import... e
depois em Existing Projects into Workspace.
Fig. 2.3: Importando projeto pelo Eclipse.
Selecione o diretório (em Browse...) onde você colocou o projeto
11

2.2. Configurando a livraria-base
Casa do Código
livraria-base, que estava dentro do
ZIP, e logo depois clique em
Finish.
Fig. 2.4: Concluindo import do projeto base.
Queremos agora vincular os dois projetos. Por enquanto, faremos isso
pelo próprio Eclipse, clicando com o botão direito no projeto livraria-fx,
opção Build Path e depois Configure Build Path....
12


Casa do Código
Capítulo 2. Java FX
Fig. 2.5: Build Path > Configure Build Path...
Vá à aba Projects, clique em Add, escolha o projeto livraria-base,
que acabamos de importar. Depois basta clicar em Ok.
Fig. 2.6: Adicionando projeto no build path.
2.3
Preparando nosso cenário
Antes de criar uma tabela, precisamos criar um cenário, onde ficará todo o
conteúdo dessa nossa tela. Uma tela do Java FX sempre é composta por um
container principal ( Stage) e também por um cenário, representado pela
classe Scene. Repare em sua estrutura:
Scene scene = new Scene(root, largura, altura)
13
2.3. Preparando nosso cenário
Casa do Código
Os parâmetros largura e altura representam bem seu significado,
não é? Definimos nele o tamanho do nosso cenário, da tela da nossa aplicação.
Já esse root nada mais é do que um auxiliar, que ajuda no processo de layout, como ao adicionar ou remover elementos.
O tipo do parâmetro root é javafx.scene.Parent, que é uma classe
abstrata. De acordo com nossa necessidade, escolhemos uma de suas im-
plementações. Como a tela será composta por 2 elementos (um label e uma
tabela), podemos usar um Group.
14

Casa do Código
Capítulo 2. Java FX
Outras implementações
Além do
Group, temos como opções diretamente as classes
Control, Region e WebView. Além disso, cada uma dessas tem di-
versas outras subclasses. São várias possibilidades! Uma opção bastante
usada é a GridPane, do pacote javafx.scene.layout. Ela é filha
de Pane, que é filha de Region que, finalmente, é filha de Parent.
Ufa!
Fig. 2.7: Hierarquia da classe javafx.scene.layout.GridPane
Você pode conhecer um pouco mais sobre essa e outras implemen-
tações em:
https://docs.oracle.com/javafx/2/api/javafx/scene/Parent.html
Veremos diversas delas durante o livro.
O código do nosso método start deve ficar parecido com:
public void start(Stage primaryStage) {
Group group = new Group();
Scene scene = new Scene(group, 690, 510);
primaryStage.setScene(scene);
15
2.3. Preparando nosso cenário
Casa do Código
primaryStage.setTitle("Sistema da livraria com Java FX");
primaryStage.show();
}
Perceba que estamos chamando o método setScene, vinculando o
cenário com o container principal. Quer ver o resultado? Não deixe de ex-
ecutar o código a cada evolução. Agora a tela estará maior e com um fundo
branco, padrão do cenário.
O próximo passo será colocar um texto na tela, indicando que essa será a
nossa listagem de produtos. Faremos isso com a auxilio da classe Label, do
pacote javafx.scene.control.
Label label = new Label("Listagem de Livros");
Podemos agora utilizar a classe Group, que está vinculada ao nosso
cenário, para adicionar esse novo elemento na página.
Para fazer isso,
basta chamar seu método getChildren e adicionar todos os elementos
necessários, que neste caso será apenas o label:
group.getChildren().addAll(label);
O código completo do método start deverá ficar assim:
public void start(Stage primaryStage) {
Group group = new Group();
Scene scene = new Scene(group, 690, 510);
Label label = new Label("Listagem de Livros");
group.getChildren().addAll(label);
primaryStage.setScene(scene);
primaryStage.setTitle("Sistema da livraria com Java FX");
primaryStage.show();
}
Ao rodar esse código, teremos a saída esperada, mas o texto está muito
pequeno e grudado nas margens da tela!
16

Casa do Código
Capítulo 2. Java FX
Fig. 2.8: Listagem com o label desalinhado.
Para melhorar um pouco a aparência desse label, podemos mudar sua
fonte e também adicionar um padding. Para isso, vamos utilizar os métodos
setFont e setPadding, como a seguir:
Label label = new Label("Listagem de Livros");
label.setFont(Font
.font("Lucida Grande", FontPosture.REGULAR, 30));
label.setPadding(new Insets(20, 0, 10, 10));
O método estático font, da classe javafx.scene.text.Font, nos
possibilita passar a família, tipo e tamanho da fonte que queremos aplicar
em nosso texto. Existem outras sobrecargas desse método para aplicar ape-
nas o tamanho, apenas família e assim por diante. Repare também que no
setPadding estamos utilizando uma classe auxiliar para passar as medidas,
a Insets.
Execute seu código mais uma vez para perceber o resultado:
17

2.4. Uma listagem de produtos
Casa do Código
Fig. 2.9: Listagem com label alinhada.
Um pouco melhor, não acha? Agora podemos partir para a tabela!
2.4
Uma listagem de produtos
Nosso próximo passo será criar uma tabela para exibir nossa lista de pro-
dutos. Antes de focar na tabela, vamos criar uma nova classe no pacote
repositorio, que será responsável por retornar a lista de produtos que de-
verá ser exibida. Podemos chamá-la de RepositorioDeProdutos:
package repositorio;
public class RepositorioDeProdutos {
public ObservableList<Produto> lista() {
Autor autor = new Autor();
autor.setNome("Rodrigo Turini");
autor.setEmail("[email protected]");
autor.setCpf("123.456.789.10");
Livro livro = new LivroFisico(autor);
livro.setNome("Java 8 Prático");
livro.setDescricao("Novos recursos da linguagem");
livro.setValor(59.90);
livro.setIsbn("978-85-66250-46-6");
18
Casa do Código
Capítulo 2. Java FX
Livro maisUmlivro = new LivroFisico(autor);
maisUmlivro.setNome("Desbravando a O.O.");
maisUmlivro.setDescricao("Livro de Java e O.O");
maisUmlivro.setValor(59.90);
maisUmlivro.setIsbn("321-54-67890-11-2");
Autor outroAutor = new Autor();
outroAutor.setNome("Paulo Silveira");
outroAutor.setEmail("[email protected]");
outroAutor.setCpf("123.456.789.10");
Livro outroLivro = new LivroFisico(outroAutor);
outroLivro.setNome("Lógica de Programação");
outroLivro.setDescricao("Crie seus primeiros programas");
outroLivro.setValor(59.90);
outroLivro.setIsbn("978-85-66250-22-0");
return FXCollections
.observableArrayList(livro, maisUmlivro, outroLivro);
}
}
Por enquanto, estamos criando os livros e autores manualmente,
mas muito em breve faremos isso de uma forma bem mais interes-
sante.
Você já deve ter notado que o tipo de retorno dessa lista é um
tanto diferente daquilo com que estamos acostumados, ela retorna uma
ObservableList<Produto>.
Esse é o tipo de lista que a tabela do
Java FX espera receber.
Criá-la é bem simples com o apoio da factory
FXCollections, do pacote javafx.collections:
ObservableList lista =
FXCollections.observableArrayList(...);
Agora que já temos uma lista, podemos voltar para a classe Main e criar
um novo elemento em nosso cenário ( Scene), uma tabela. Para fazer isso,
utilizaremos a classe TableView, como a seguir:
ObservableList<Produto> produtos =
new RepositorioDeProdutos().lista();
19
2.4. Uma listagem de produtos
Casa do Código
TableView tableView = new TableView<>(produtos);
Já estamos passando a lista de produtos do RepositorioDeProdutos
como argumento em seu construtor. Nesse momento o código de nosso
método start está assim:
public void start(Stage primaryStage) {
Group group = new Group();
Scene scene = new Scene(group, 690, 510);
ObservableList<Produto> produtos =
new RepositorioDeProdutos().lista();
TableView tableView = new TableView<>(produtos);
Label label = new Label("Listagem de Livros");
label.setFont(Font
.font("Lucida Grande", FontPosture.REGULAR, 30));
label.setPadding(new Insets(20, 0, 10, 10));
group.getChildren().addAll(label);
primaryStage.setScene(scene);
primaryStage.setTitle("Sistema da livraria com Java FX");
primaryStage.show();
}
Podemos agora criar as colunas de nossa tabela. Basta criar uma instância
do tipo TableColumn, recebendo o nome da coluna como parâmetro:
TableColumn nomeColumn = new TableColumn("Nome");
Além disso, para cada coluna precisamos definir sua fonte de dados, ou
seja, qual propriedade da classe Produto deve ser exibida nessa determi-
nada coluna. Para isso, chamamos seu método setCellValueFactory,
passando um novo PropertyValueFactory como parâmetro:
20
Casa do Código
Capítulo 2. Java FX
TableColumn nomeColumn = new TableColumn("Nome");
nomeColumn.setCellValueFactory(
new PropertyValueFactory("nome"));
Opcionalmente, você pode definir um tamanho mínimo e máximo para
essa coluna, utilizando seus métodos setMinWidth e setMaxWidth. Um
exemplo seria:
descColumn.setMinWidth(230);
Vamos criar uma coluna para cada um dos quatro atributos principais
de nossos Produtos, seu nome, descrição, valor e ISBN (que é seu código
único).
Ao final, seu código deve ficar assim:
public void start(Stage primaryStage) {
Group group = new Group();
Scene scene = new Scene(group, 690, 510);
ObservableList<Produto> produtos =
new RepositorioDeProdutos().lista();
TableView tableView = new TableView(produtos);
TableColumn nomeColumn = new TableColumn("Nome");
nomeColumn.setMinWidth(180);
nomeColumn.setCellValueFactory(
new PropertyValueFactory("nome"));
TableColumn descColumn = new TableColumn("Descrição");
descColumn.setMinWidth(230);
descColumn.setCellValueFactory(
new PropertyValueFactory("descricao"));
TableColumn valorColumn = new TableColumn("Valor");
valorColumn.setMinWidth(60);
valorColumn.setCellValueFactory(
21
2.4. Uma listagem de produtos
Casa do Código
new PropertyValueFactory("valor"));
TableColumn isbnColumn = new TableColumn("ISBN");
isbnColumn.setMinWidth(180);
isbnColumn.setCellValueFactory(
new PropertyValueFactory("isbn"));
Label label = new Label("Listagem de Livros");
label.setFont(Font
.font("Lucida Grande", FontPosture.REGULAR, 30));
label.setPadding(new Insets(20, 0, 10, 10));
group.getChildren().addAll(label);
primaryStage.setScene(scene);
primaryStage.setTitle("Sistema da livraria com Java FX");
primaryStage.show();
}
Por fim, vamos adicionar as colunas na tabela e depois adicioná-la ao
nosso cenário:
tableView.getColumns().addAll(nomeColumn,
descColumn, valorColumn, isbnColumn);
group.getChildren().addAll(label, tableView);
Ao executar o código, a tabela estará populada como esperamos, mas to-
talmente desalinhada e escondendo a nossa Label:
22

Casa do Código
Capítulo 2. Java FX
Fig. 2.10: TableView desalinhada, cobrindo o label.
Para resolver o problema, vamos envolver essa
tableView em um
VBox. Esse elemento vai nos ajudar a alinhar a tabela, já que podemos definir um padding, como a seguir:
VBox vbox = new VBox(tableView);
vbox.setPadding(new Insets(70, 0, 0 ,10));
Basta adicionar esse novo item em nosso cenário, assim como fizemos
com a tabela:
group.getChildren().addAll(label, vbox);
O código final da sua classe Main deve ficar assim:
package application;
import javafx.application.Application;
import javafx.collections.ObservableList;
23
2.4. Uma listagem de produtos
Casa do Código
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.scene.text.FontPosture;
import javafx.stage.Stage;
import repositorio.RepositorioDeProdutos;
import br.com.casadocodigo.livraria.produtos.Produto;
public class Main extends Application {
@Override
public void start(Stage primaryStage) {
Group group = new Group();
Scene scene = new Scene(group, 690, 510);
ObservableList<Produto> produtos =
new RepositorioDeProdutos().lista();
TableView<Produto> tableView = new TableView<>(produtos);
TableColumn nomeColumn = new TableColumn("Nome");
nomeColumn.setMinWidth(180);
nomeColumn.setCellValueFactory(
new PropertyValueFactory("nome"));
TableColumn descColumn = new TableColumn("Descrição");
descColumn.setMinWidth(230);
descColumn.setCellValueFactory(
new PropertyValueFactory("descricao"));
TableColumn valorColumn = new TableColumn("Valor");
valorColumn.setMinWidth(60);
24
Casa do Código
Capítulo 2. Java FX
valorColumn.setCellValueFactory(
new PropertyValueFactory("valor"));
TableColumn isbnColumn = new TableColumn("ISBN");
isbnColumn.setMinWidth(180);
isbnColumn.setCellValueFactory(
new PropertyValueFactory("isbn"));
tableView.getColumns().addAll(nomeColumn, descColumn,
valorColumn, isbnColumn);
final VBox vbox = new VBox(tableView);
vbox.setPadding(new Insets(70, 0, 0 ,10));
Label label = new Label("Listagem de Livros");
label.setFont(Font
.font("Lucida Grande", FontPosture.REGULAR, 30));
label.setPadding(new Insets(20, 0, 10, 10));
group.getChildren().addAll(label, vbox);
primaryStage.setTitle("Sistema da livraria com Java FX");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Ao executá-lo, teremos como resultado uma tela parecida com:
25

2.4. Uma listagem de produtos
Casa do Código
Fig. 2.11: Listagem de produtos com os elementos alinhados.
26
Casa do Código
Capítulo 2. Java FX
Usando @SuppressWarnings no Eclipse
Para evitar os diversos alertas (warnings) em amarelo no Eclipse, você
pode adicionar sob a sua classe a anotação @SuppressWarnings:
@SuppressWarnings({ "unchecked", "rawtypes" })
public class Main extends Application {
@Override
public void start(Stage primaryStage) {
Group group = new Group();
Scene scene = new Scene(group, 690, 510);
// restante do código omitido
}
Esses warnings acontecem por causa dos parâmetros genéricos, que
optamos não passar para simplificar e evitar tanta repetição em nosso
código. Não se preocupe com isso, voltaremos a falar disso no final do
livro. Por enquanto, você pode deixar os warnings (o código funcionará
normalmente), ou adicionar a anotação @SuppressWarnings.
Excelente, já temos uma interface visual para nosso projeto. Ao decorrer
do livro, trabalharemos com leitura e escrita em arquivos, acesso a banco de
dados e mais. A cada novo capítulo uma ou mais novas funcionalidades serão
adicionadas à nossa aplicação.
27
2.4. Uma listagem de produtos
Casa do Código
Mais sobre Java Fx
Se você gostou do Java FX e quiser estudar um pouco mais a fundo,
talvez queira dar uma olhada em sua página no site da Oracle:
http://www.oracle.com/technetwork/java/javase/overview/
javafx-overview-2158620.html?ssSourceSiteId=otnpt
Nela você encontra um overview sobre a tecnologia e links para doc-
umentação, exemplos e muito mais.
28
Capítulo 3
Java IO
3.1
Entrada e saída de dados
Nossa
listagem
ainda
trabalha
com
valores
fixos,
definidos
no
RepositorioDeProdutos.
Apesar disso, já queremos adicionar uma
função de exportar os dados dessa listagem para um arquivo de texto. Ao
clicar no botão de exportar, um arquivo deverá ser gerado com os dados da
listagem.
Trabalhar com entrada e saída de dados é uma necessidade completa-
mente natural nos mais diferentes tipos de projetos. Ler valores digitados pelo teclado, ler ou escrever em um arquivo de diferentes formatos etc., conhecer
a API de controle de entrada e saída, ou IO como é comumente chamada, é um passo fundamental. Esse será nosso objetivo durante esse capítulo.
3.2. Lendo um arquivo de texto
Casa do Código
3.2
Lendo um arquivo de texto
Já vamos começar de forma prática, queremos ler o conteúdo de um arquivo.
Para começar a testar, crie na raiz do seu projeto (pasta livraria-fx) um
arquivo chamado teste.txt.
Adicione nesse arquivo o seguinte conteúdo:
Esse é um teste de leitura de arquivo
Queremos ler o conteúdo de todas as linhas
Mas como ler arquivos em Java?
Uma forma de ler o arquivo seria utilizando um FileInputStream,
responsável por fazer a leitura de bytes de um arquivo. O construtor dessa
classe nos obriga passar o nome do arquivo que será lido, que neste caso será teste.txt:
try {
InputStream is = new FileInputStream("teste.txt");
} catch (FileNotFoundException e) {
System.out.println("Arquivo não encontrado "+ e);
}
Quando estamos trabalhando com o pacote java.io, grande parte de
seus métodos lançam IOException. Como essa é uma Checked Exception
(não evitável), somos obrigados a tratá-la ou declará-la com um throws. Em
meu exemplo, eu optei por tratar, imprimindo uma mensagem amigável no
console.
Outro detalhe importante é que declaramos o tipo da classe
FileInputStream como InputStream, que é sua superclasse.
Essa
é uma classe abstrata e tem diversas outras implementações, como
AudioInputStream e ByteArrayInputStream.
30

Casa do Código
Capítulo 3. Java IO
Fig. 3.1: InputStream e algumas de suas subclasses
A grande ideia do InputStream é que ela implementa o comporta-
mento em comum entre os diferentes tipos de entrada. Além de ler um
arquivo de texto, podemos precisar ler de um áudio, uma conexão remota
(com sockets) etc. Cada tipo de leitura terá suas particularidades, mas a classe InputStream define o comportamento padrão entre elas.
Qual a vantagem dessa abstração? É bem simples, podemos tirar proveito
do polimorfismo. Imagine um método que recebe um InputStream para
fazer uma leitura de bytes. De onde os dados virão? De um txt? De um
blob do banco de dados? Não importa. Independente de onde os dados vêm,
o processo de leitura será o mesmo. Quando precisarmos ler bytes, não im-
portando de onde, poderemos chamar um método que aceita qualquer im-
plementação de InputStream.
31

3.2. Lendo um arquivo de texto
Casa do Código
Fig. 3.2: Método recebendo um InputStream como parâmetro.
O padrão de projeto template method
Essa abstração utilizada na classe InputStream ilustra um padrão
de projeto, ou Design Pattern, bastante conhecido e interessante. Estamos
falando do Template Method.
Há um post no blog da Caelum que se aprofunda bem nesse exemplo:
http://blog.caelum.com.br/design-patterns-no-java-se-o-template-method/
O mesmo acontece com OutputStream, que logo veremos. Assim
como usaremos
InputStream para leitura de bytes, podemos usar o
OutputStream para escrita. O pacote de java.io usa e abusa da orien-
tação a objetos, com classes abstratas, interfaces e muito polimorfismo.
Para concluir a leitura do arquivo, precisamos transformar os bytes desse
arquivo em unicode, guardando um conjunto de chars. Em seguida, con-
catenar esse conjunto de chars para formar as Strings com o conteúdo de
cada uma de suas linhas.
Parece bem trabalhoso, não é? Mas a API é bem simples, você verá que
esse código é bem padrão. Logo também veremos algumas classes do pacote
java.util que simplificam todo esse processo, mas primeiro, é importante
entendê-lo.
32


Casa do Código
Capítulo 3. Java IO
De forma visual, o fluxo padrão de entrada é:
Fig. 3.3: Etapas do fluxo padrão de entrada
E as responsáveis por esse trabalho:
Fig. 3.4: InputStream, InputStreamReader e BufferedReader
Já conhecemos a InputStream. As classes InputStreamReader e
BufferedReader são as responsáveis por fazer o restante do trabalho, ler
chars e transformá-las em Strings:
try {
InputStream is = new FileInputStream("teste.txt");
InputStreamReader isr = new InputStreamReader(is);
BufferedReader reader = new BufferedReader(isr);
String linha = reader.readLine();
while (linha != null) {
System.out.println(linha);
linha = reader.readLine();
}
reader.close();
} catch (IOException e) {
System.out.println("Erro ao tentar ler o arquivo " + e);
}
A cada chamada, o método readLine lê uma linha e muda o cursor para
a próxima, até que o arquivo acabe. Neste caso, retornará null.
33
3.3. Lendo texto do teclado com System.in
Casa do Código
Escreva e execute esse código e você verá que todas as linhas do arquivo
serão impressas no console. Legal, não é?
3.3
Lendo texto do teclado com System.in
Quer uma prova de que a abstração utilizada no InputStream é interes-
sante? Veja como é simples mudar nosso código para que passe a ler do
teclado em vez de um arquivo:
try {
InputStream is = System.in;
InputStreamReader isr = new InputStreamReader(is);
BufferedReader reader = new BufferedReader(isr);
String linha = reader.readLine();
while (linha != null) {
System.out.println(linha);
linha = reader.readLine();
}
reader.close();
} catch (IOException e) {
System.out.println("Erro ao tentar ler o arquivo " + e);
}
Percebeu a diferença?
Ela está apenas na primeira linha, quando
declaramos o
InputStream. Tudo que precisamos fazer foi utilizar o
System.in, que é a implementação do InputStream responsável por esse
tipo de leitura, do teclado.
Execute o método para ver o resultado. O console ficará em branco, es-
perando que um texto seja digitado. Digite qualquer coisa e pressione Enter,
o valor será impresso de volta no console. Perceba que, diferente de quando
estamos lendo de um arquivo, neste caso a linha nunca será nula. Portanto,
o código ficará em looping, sempre esperando uma nova entrada.
34

Casa do Código
Capítulo 3. Java IO
Fig. 3.5: Exemplo de saída do System.in
35

3.4. Tornando tudo mais simples com Scanner
Casa do Código
Encerrando execução manualmente ou com EOF
Para parar a aplicação que está em looping, aguardando a próxima
entrada do teclado, você pode pelo Eclipse clicar no ícone de stop.
Fig. 3.6: Como interromper a execução pelo Eclipse
Outra alternativa seria mandar um sinal de fim de stream, o con-
hecido EOF (end of file). Em sistemas operacionais de base :Unix, como Linux e Mac OS, você faz isso com o comando Control + D. Pelo Win-dows o comando será Control + Z.
3.4
Tornando tudo mais simples com Scanner
É muito interessante conhecer essa anatomia do InputStream para entrada
de dados, mas a realidade é que dessa forma o processo é muito verboso, com
todas essas etapas. Há uma maneira mais atual e interessante.
Podemos fazer a mesma leitura do arquivo teste.txt utilizando a
classe Scanner, presente no pacote java.util. Repare como o código
é mais declarativo:
Scanner sc = new Scanner(new File("teste.txt"));
while (sc.hasNextLine()) {
36
Casa do Código
Capítulo 3. Java IO
System.out.println(sc.nextLine());
}
Execute o código e você perceberá que a saída será a mesma. Muito
mais simples, não acha? O java.util.Scanner surgiu no Java 5 e tem
uma série de métodos úteis para facilitar seu trabalho com leitura de um
InputStream. Métodos como nextDouble, que já lê os bytes e faz a con-
versão, podem ser bastante convenientes no trabalho do dia a dia.
Conhecendo a API
Durante a leitura, não deixe de se aprofundar ainda mais pelo
assunto consultando a documentação das classes mencionadas.
O
java.util.Scanner, por exemplo, está bem documentado em:
http://docs.oracle.com/javase/8/docs/api/java/util/Scanner.html
Veja que a própria documentação da classe, logo no início, já mostra
alguns trechos de código como exemplo.
Note como fica nosso código de entrada do teclado com essa classe:
Scanner sc = new Scanner(System.in);
System.out.println("Digite seu nome");
String nomeDigitado = sc.nextLine();
System.out.println("Digite sua idade");
int idadeDigitada = sc.nextInt();
System.out.println("Nome: " + nomeDigitado);
System.out.println("Idade: " + idadeDigitada);
Bastou mudar o InputStream passado no construtor do Scanner para
o System.in, que já vimos. Note também que estamos utilizando o método
nextInt para recuperar o valor e já converter para um inteiro, caso contrário seria necessário fazer essa conversão manualmente. Por exemplo:
String idadeDigitada = sc.nextLine();
int idade = Integer.parseInt(idadeDigitada);
Exercite esse código para ir se familiarizando com a API, escreva e execute
em seu projeto. Um exemplo de saída seria:
37

3.5. Saída de dados e o OutputStream
Casa do Código
Digite seu nome
> Rodrigo Turini
Digite sua idade
> 25
Nome: Rodrigo Turini
Idade: 25
3.5
Saída de dados e o OutputStream
O processo de escrita (saída de dados) é muito parecido, mas no lu-
gar de
InputStream,
InputStreamReader e
BufferedReader,
temos respectivamente o
OutputStream,
OutputStreamWriter e
BufferedWriter.
Fig. 3.7: OutputStream, OutputStreamWriter e BufferedWriter
Um exemplo de uso seria:
OutputStream os = new FileOutputStream("saida.txt");
OutputStreamWriter osw = new OutputStreamWriter(os);
BufferedWriter bw = new BufferedWriter(osw);
Note que, agora, no lugar de ler de um arquivo existente, estamos criando
um novo, neste caso chamado saida.txt. Também somos obrigados a pas-
sar essa informação no construtor da classe FileOutputStream.
Por sinal, note novamente o polimorfismo em ação.
Usamos um
FileOutputStream, mas dissemos que seu tipo é OutputStream. Há
uma implementação para cada tipo de escrita:
38

Casa do Código
Capítulo 3. Java IO
Fig. 3.8: OutputStream e algumas de suas subclasses
Podemos utilizar o método write para escrita do arquivo, mas ele não
faz uma quebra de linha. Uma alternativa para isso seria chamar o método
newLine, que carrega essa responsabilidade.
bw.write("Testando a escrita em arquivo");
bw.newLine();
bw.write("Conteúdo na próxima linha");
Ou simplesmente concatenar um /n na String:
bw.write("Testando a escrita em arquivo\n");
bw.write("Conteúdo na próxima linha");
39
3.5. Saída de dados e o OutputStream
Casa do Código
Fechando o arquivo com close
É fundamental sempre fecharmos o arquivo, chamando seu método
close. Ao chamar o método close de um BufferedWriter,
ele já fará o trabalho de fechar o
OutputStreamWriter
e
FileOutputStream para você. Em cascata.
Caso você se esqueça de fechar o arquivo nesse exemplo, que é um
programa minúsculo, ele provavelmente não vai fazer a escrita no ar-
quivo (os bytes ficam no buffer do escritor). Se for uma aplicação maior,
o próprio Java fechará o BufferedWriter para você em algum mo-
mento (por causa do garbage collector, que veremos mais à frente). Mas
nunca devemos deixar de fechar um arquivo esperando que isso acon-
teça, sempre devemos chamar o close quando terminarmos de fazer
uma escrita ou leitura (um io).
No próximo capítulo, veremos uma forma bem interessante de
fechar recursos, como um arquivo ou mesmo uma conexão com
banco de dados.
Vamos aprender que isso pode e deve ser feito
em um bloco finally, ou ainda com um recurso conhecido como
try-with-resources.
Pronto, agora que já sabemos isso podemos criar uma nova classe de teste
para ver esse código em ação. Crie a classe TesteSaida para fazer alguns
testes, ela pode se parecer com:
public class TesteSaida {
public static void main(String[] args)
throws IOException {
OutputStream os = new FileOutputStream("saida.txt");
OutputStreamWriter osw = new OutputStreamWriter(os);
BufferedWriter bw = new BufferedWriter(osw);
bw.write("Testando a escrita em arquivo");
bw.newLine();
40

Casa do Código
Capítulo 3. Java IO
bw.write("Conteúdo na próxima linha");
bw.close();
}
}
Execute o código para ver o resultado. Um ponto importante é que a IDE
normalmente não atualiza o seu projeto automaticamente se você criar um
arquivo por fora dela, portanto, não se esqueça de dar um F5 (refresh) no
projeto após executar a classe para que o arquivo saida.txt fique visível.
Fig. 3.9: Arquivo saida.txt no Eclipse
Só por curiosidade, se quiser você pode testar mudar a implementação de
OutputStream, assim como fizemos no exemplo de escrita. Experimente
usar o System.out:
public class TesteSaida {
public static void main(String[] args)
throws IOException {
OutputStream os = System.out;
OutputStreamWriter osw = new OutputStreamWriter(os);
BufferedWriter bw = new BufferedWriter(osw);
41
3.6. Escrita mais simples com PrintStream
Casa do Código
bw.write("Testando a escrita em arquivo");
bw.newLine();
bw.write("Conteúdo na próxima linha");
bw.close();
}
}
Simples, não é? A mudança foi mínima e já temos a capacidade de escr-
ever em outro local, além de um arquivo. A saída será:
Testando a escrita em arquivo
Conteúdo na próxima linha
Mas, claro, nesse caso específico um System.out.println seria muito
mais simples. Por falar em simplicidade, veremos agora como simplificar esse
processo de escrita.
3.6
Escrita mais simples com PrintStream
Assim como o java.util.Scanner facilita o processo de leitura de bytes,
desde o Java 5 temos uma forma mais interessante de fazer esse processo de escrita, com a classe java.io.PrintStream. Repare como fica nosso código
utilizando-a:
PrintStream out = new PrintStream("saida.txt");
out.println("Testando a escrita em arquivo");
out.println("Conteúdo na próxima linha");
out.close();
Muito mais simples, não acha? Achou essa API familiar? Na verdade,
você provavelmente já vem usando indiretamente o PrintStream desde o
seu primeiro hello world em Java, pois este é o tipo do atributo out da classe System. Repare:
PrintStream out = System.out;
out.println("agora a saída é no console");
out.println("Conteúdo na próxima linha");
42
Casa do Código
Capítulo 3. Java IO
Ambas as classes Scanner e PrintStream facilitam bastante o pro-
cesso de entrada e saída de dados. Quer ler mais sobre essas duas classes?
Não deixe de conferir sua documentação:
• http://docs.oracle.com/javase/8/docs/api/java/io/PrintStream.html
• http://docs.oracle.com/javase/8/docs/api/java/util/Scanner.html
3.7
Gerando um CSV de produtos
Agora que já conhecemos um pouco da API de io, vamos adicionar um novo comportamento em nossa listagem de produtos. Queremos exportar os produtos da listagem para um arquivo CSV. O termo CSV vem de comma sep-
arated value, ou seja, valores separados por vírgula. Esse formato é bastante comum e utilizado no dia a dia.
Queremos ao final ter um arquivo parecido com:
Nome, Descricao, Valor, ISBN
Java 8 Prático, Novos recursos da linguagem, 59.9,
978-85-66250-46-6
Vamos começar criando a classe Exportador, que será responsável por
esse trabalho. Ela terá um único método, chamado paraCSV, que receberá
um List de Produto como parâmetro:
public class Exportador {
public void paraCSV(List<Produto> produtos) {
}
}
Implementando esse método, nosso primeiro passo será criar um arquivo
produtos.csv e já colocar um cabeçalho com as informações que vamos
escrever. Podemos usar um PrintStream para fazer o trabalho, repare:
public void paraCSV(List<Produto> produtos)
throws IOException {
43
3.7. Gerando um CSV de produtos
Casa do Código
PrintStream ps = new PrintStream("produtos.csv");
ps.println("Nome, Descricao, Valor, ISBN");
ps.close();
}
Já execute o código para ver o resultado. O arquivo deve ser criado na raiz
do seu projeto e por enquanto apenas com o conteúdo:
Nome, Descricao, Valor, ISBN
Excelente. Agora tudo o que precisamos fazer é iterar nessa lista de pro-
dutos adicionando uma linha para cada elemento, como por exemplo:
public void paraCSV(List<Produto> produtos)
throws IOException {
PrintStream ps = new PrintStream("produtos.csv");
ps.println("Nome, Descricao, Valor, ISBN");
for (Produto produto : produtos) {
ps.println(produto.getNome() + ", "
+ produto.getDescricao() + ", "
+ produto.getValor() + ", "
+ produto.getIsbn());
}
ps.close();
}
Isso já trará o resultado que esperamos. Mas o código esté bem feio, não
acha? Essa concatenação de Strings pode prejudicar bastante a legibilidade
e dificultar a manutenção de nosso código.
44
Casa do Código
Capítulo 3. Java IO
Usando o projeto do outro livro?
Se, no lugar de baixar o projeto base, você estiver utilizando o projeto
do livro Desbravando Java e Orientação a Objetos, você precisará modi-
ficar sua interface Produto para que esse código compile.
Ela deve ficar assim:
public interface Produto
extends Comparable<Produto> {
double getValor();
String getNome();
String getDescricao();
String getIsbn();
}
Uma opção talvez mais interessante seria usar o String.format, como
a seguir:
public void paraCSV(List<Produto> produtos)
throws IOException {
PrintStream ps = new PrintStream("produtos.csv");
ps.println("Nome, Descricao, Valor, ISBN");
for (Produto produto : produtos) {
ps.println(String.format("%s, %s, %s, %s",
produto.getNome(),
produto.getDescricao(),
produto.getValor(),
produto.getIsbn()));
}
ps.close();
}
Ufa!
Um pouco melhor.
Claro,
poderíamos ter usado um
45
3.7. Gerando um CSV de produtos
Casa do Código
StringBuilder,
StringJoiner etc., mas essa opção é bem sim-
ples e já resolve o problema. Vamos ver se está funcionando? Crie um
método main dentro dessa mesma classe para testar, ele pode ficar assim:
public static void main(String[] args)
throws IOException {
Livro livro = new LivroFisico(new Autor());
livro.setNome("Java 8 Prático");
livro.setDescricao("Novos recursos da linguagem");
livro.setValor(59.90);
livro.setIsbn("978-85-66250-46-6");
Livro maisUmlivro = new LivroFisico(new Autor());
maisUmlivro.setNome("Desbravando a O.O.");
maisUmlivro.setDescricao("Livro de Java e O.O");
maisUmlivro.setValor(59.90);
maisUmlivro.setIsbn("321-54-67890-11-2");
new Exportador().paraCSV(Arrays.asList(livro, maisUmlivro));
}
O resultado será o arquivo produtos.csv, com o seguinte conteúdo:
Nome, Descricao, Valor, ISBN
Java 8 Prático, Novos recursos da linguagem, 59.9,
978-85-66250-46-6
Desbravando a OO, Livro de Java e O.O, 59.9, 321-54-67890-11-2
Não encontrou o arquivo? Lembre-se de dar um F5 (refresh) em seu
projeto para ele aparecer na IDE. Ou, se preferir, abra a pasta do seu projeto direto pelo seu sistema operacional.
46

Casa do Código
Capítulo 3. Java IO
Fig. 3.10: Arquivo produtos.csv visto pelo sistema operacional
Nossa classe Exportador ficou assim:
import java.io.IOException;
import java.io.PrintStream;
import java.util.List;
import br.com.casadocodigo.livraria.produtos.Produto;
public class Exportador {
public void paraCSV(List<Produto> produtos)
throws IOException {
PrintStream ps = new PrintStream("produtos.csv");
ps.println("Nome, Descricao, Valor, ISBN");
for (Produto produto : produtos) {
ps.println(String.format("%s, %s, %s, %s",
produto.getNome(),
produto.getDescricao(),
produto.getValor(),
produto.getIsbn()));
}
ps.close();
}
47

3.8. Botão de exportar produtos
Casa do Código
}
3.8
Botão de exportar produtos
Agora que já temos o código para exportar uma lista de produtos como um
CSV, podemos adicionar um botão em nossa listagem feita em Java FX para
disparar essa lógica. Isso é bem simples.
O
primeiro
passo
é
criar
um
Button,
do
pacote
javafx.scene.control.
Opcionalmente, em seu construtor você já
pode passar o texto que quer exibir. Podemos fazer:
Button button = new Button("Exportar CSV");
Da mesma forma que fizemos com a label e tableView, precisamos
adicionar esse botão no cenário da página. Basta adicioná-lo como argu-
mento do método addAll:
group.getChildren().addAll(label, vbox, button);
Execute o código e você perceberá que ele já está na página, mas está de-
salinhado, cobrindo o texto da nossa lable:
Fig. 3.11: Button desalinhado na tela
Bem, para alinhá-lo podemos envolver esse button em um VBox com
um padding definido, assim como fizemos com a tabela. Outra opção mais
simples é utilizar seus métodos setLayoutX e setLayoutY para definir
sua posição, algo como:
48

Casa do Código
Capítulo 3. Java IO
Button button = new Button("Exportar CSV");
button.setLayoutX(575);
button.setLayoutY(25);
Agora sim, o resultado será:
Fig. 3.12: Button alinhado com setLayoutX e setLayoutY
Mas note que ao clicar no botão nenhuma ação será executada. Faz sen-
tido, ainda não implementamos isso.
3.9
Adicionando ações com setOnAction
O método setOnAction é o responsável por definir as ações que serão ex-
ecutadas no momento do click de nosso Button. Podemos fazer:
button.setOnAction(???); // oq?
Esse método espera receber uma instância do tipo EventHandler (in-
terface) como parâmetro. Sim, poderíamos criar uma classe que implemen-
tasse essa interface e passá-la como parâmetro, mas comumente fazemos isso
com uma classe anônima:
button.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
// ação a ser executada
}
});
49
3.9. Adicionando ações com setOnAction
Casa do Código
Classe anônima
Se você não está acostumado ou nunca viu classes anônimas, não se pre-
ocupe. Apesar de um tanto feias, elas são bem simples de entender. No lugar
de usar uma classe anônima, poderíamos ter criado uma classe que imple-
mentasse EventHandler<ActionEvent>:
public class AcaoDoBotao
implements EventHandler<ActionEvent> {
@Override
public void handle(ActionEvent event) {
// ação a ser executada
}
}
Poderíamos usá-la assim:
button.setOnAction(new AcaoDoBotao());
O problema desse código é que cada botão pode ter uma ação que não vai
se repetir em nenhum outro momento do código, só será usada aqui. Além
disso, podemos ter vários botões; teríamos que criar uma classe nova para
fazer a ação de cada um deles?
Aqui que entra a classe anônima! No lugar de criar uma classe, digamos
que convencional, podemos instanciar diretamente a interface e já instanciar
seus métodos ali mesmo. Essa instância criada é fruto de uma classe anônima,
uma classe que nem tem um nome.
Agora, voltando ao botão. Queremos portanto adicionar a ação do click
dentro do método handle, que estamos implementando na classe anônima.
Vamos começar com um exemplo simples, apenas mostrando uma mensagem
no console:
button.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
System.out.println("Click!");
}
});
50

Casa do Código
Capítulo 3. Java IO
Para testar, execute a aplicação e pressione o botão Exportar CSV al-
gumas vezes, você verá que a cada click o texto será impresso.
O que queremos na verdade é exportar todos os nossos produtos como
um CSV, portanto, fazemos:
button.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
new Exportador().paraCSV(produtos);
}
});
Mas, espera! Esse código ainda não compila. Como o método paraCSV
lança uma IOException, precisamos tratá-la ou declará-la. Vamos tratar:
button.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
try {
new Exportador().paraCSV(produtos);
} catch (IOException e) {
System.out.println("Erro ao exportar: "+ e);
}
}
});
Pronto! Coloque esse código em ação, basta clicar no botão Exportar
CSV que o arquivo produtos.csv será criado na raiz de seu projeto.
Fig. 3.13: Projeto com arquivo produtos.csv criado
51
3.10. JavaFx e Java 8
Casa do Código
3.10
JavaFx e Java 8
Que tal dar um toque de Java 8 nesse código? Os novos recursos dessa ver-
são deixam o código do Java FX bem mais enxuto e interessante. Quer um
exemplo? Dê uma boa olhada novamente na classe anônima que criamos no
setOnAction do botão de exportar:
button.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
try {
new Exportador().paraCSV(produtos);
} catch (IOException e) {
System.out.println("Erro ao exportar: "+ e);
}
}
});
Que tal escrevê-lo como a seguir, utilizando uma expressão lambda?
button.setOnAction(event -> {
try {
new Exportador().paraCSV(produtos);
} catch (IOException e) {
System.out.println("Erro ao exportar: "+ e);
}
});
Cortamos o código quase pela metade! Mas nem sempre menos linhas de
código significa melhora, temos que considerar que isso pode diminuir um
pouco a legibilidade. Para deixar esse código um pouco mais limpo, vamos
extrair um método. Podemos chamá-lo de exportaEmCSV:
private void exportaEmCSV(ObservableList<Produto> produtos) {
try {
new Exportador().paraCSV(produtos);
} catch (IOException e) {
System.out.println("Erro ao exportar: "+ e);
}
}
52
Casa do Código
Capítulo 3. Java IO
A expressão lambda ficará assim:
button.setOnAction(event -> exportaEmCSV(produtos));
Temos agora uma linha de código! E ela é bastante declarativa> temos um
evento como parâmetro e queremos chamar o método exportaEmCSV,
passando a lista de produtos. Muito mais enxuto e legível, não acha?
Claro, o Java 8 vai muito além disso. Há diversas outras novidades. Está
curioso para conhecer algumas delas? Então com certeza você vai gostar do
livro que escrevi junto com o Paulo Silveira, o Java 8 Prático Lambdas, Streams e os novos recursos da linguagem.
http://www.casadocodigo.com.br/products/livro-java8
Apesar de estarmos usando algumas coisas do Java 8 nos exemplos desse
livro, esse não é nosso foco. Você que não tem o Java 8 instalado pode manter a sintaxe das classes anônimas. Eu sempre procuro mostrar das duas formas
para que você consiga acompanhar independente da sua versão do Java. Ok?
Nesse ponto, o código final da classe Main está assim:
@SuppressWarnings({ "unchecked", "rawtypes" })
public class Main extends Application {
@Override
public void start(Stage primaryStage) {
Group group = new Group();
Scene scene = new Scene(group, 690, 510);
ObservableList<Produto> produtos =
new RepositorioDeProdutos().lista();
TableView<Produto> tableView = new TableView<>(produtos);
TableColumn nomeColumn = new TableColumn("Nome");
nomeColumn.setMinWidth(180);
nomeColumn.setCellValueFactory(
new PropertyValueFactory("nome"));
TableColumn descColumn = new TableColumn("Descrição");
53
3.10. JavaFx e Java 8
Casa do Código
descColumn.setMinWidth(230);
descColumn.setCellValueFactory(
new PropertyValueFactory("descricao"));
TableColumn valorColumn = new TableColumn("Valor");
valorColumn.setMinWidth(60);
valorColumn.setCellValueFactory(
new PropertyValueFactory("valor"));
TableColumn isbnColumn = new TableColumn("ISBN");
isbnColumn.setMinWidth(180);
isbnColumn.setCellValueFactory(
new PropertyValueFactory("isbn"));
tableView.getColumns().addAll(nomeColumn, descColumn,
valorColumn, isbnColumn);
final VBox vbox = new VBox(tableView);
vbox.setPadding(new Insets(70, 0, 0 ,10));
Label label = new Label("Listagem de Livros");
label.setFont(Font
.font("Lucida Grande", FontPosture.REGULAR, 30));
label.setPadding(new Insets(20, 0, 10, 10));
Button button = new Button("Exportar CSV");
button.setLayoutX(575);
button.setLayoutY(25);
button.setOnAction(event -> exportaEmCSV(produtos));
group.getChildren().addAll(label, vbox, button);
primaryStage.setTitle("Sistema da livraria com Java FX");
primaryStage.setScene(scene);
primaryStage.show();
}
54
Casa do Código
Capítulo 3. Java IO
private void exportaEmCSV(ObservableList<Produto> produtos) {
try {
new Exportador().paraCSV(produtos);
} catch (IOException e) {
System.out.println("Erro ao exportar: "+ e);
}
}
public static void main(String[] args) {
launch(args);
}
}
A única diferença para você, que está usando Java 7, será no
setOnAction do button. Agora que extraímos o método, ele ficará assim
com classe anônima:
button.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
exportaEmCSV(produtos);
}
});
55
Capítulo 4
Banco de Dados e JDBC
A aplicação está ficando mais completa, mas ainda estamos trabalhando com
valores fixos na listagem de produtos que criamos manualmente na classe
RepositorioDeProdutos. Nosso objetivo agora será buscar essas infor-
mações de um banco de dados, como é natural nas aplicações do dia a dia.
4.1
Iniciando com MySQL
Usaremos o banco de dados MySQL no projeto de nossa livraria, já que é
um dos bancos de dados relacionais mais utilizados no mercado, é gratuito e
bastante simples de instalar.
Se você ainda não tem o MySQL instalado, pode fazer download pelo link:
http://dev.mysql.com/downloads/mysql/


4.1. Iniciando com MySQL
Casa do Código
Ao final da página, você encontrará um campo para escolher seu sistema
operacional. Selecione e depois clique em download, na opção que preferir:
No mesmo site, você encontra um tutorial de instalação de acordo com o
58

Casa do Código
Capítulo 4. Banco de Dados e JDBC
seu sistema operacional. Ou você pode acessar o tutorial diretamente em:
http://dev.mysql.com/doc/refman/5.7/en/installing.html
Após baixar e instalar o MySQL, vamos nos logar no MySQL e criar a base
de dados (database) de nosso projeto. O primeiro passo é bem simples, tudo
que você precisa fazer para se logar é abrir seu terminal e digitar:
mysql -u SEU_USUARIO -p SUA_SENHA
Como em meu caso o usuário é root e eu não tenho uma senha, só
preciso fazer:
mysql -u root
Fig. 4.3: Tela inicial do MySQL Command Line
MySQL Command Line ou Workbench?
Há quem prefira utilizar o MySQL Workbench, que é uma ferramenta
visual. Pela simplicidade, nos exemplos do livro vamos utilizar o MySQL
Command Line. Nele, nós executamos as instruções diretamente pela
linha de comando, no terminal.
Se tiver qualquer dificuldade para instalar ou executar os comandos,
não pense duas vezes antes de mandar um e-mail para lista desse livro.
59
4.2. Criando a tabela de produtos
Casa do Código
Agora que já estamos logados, podemos criar uma nova database
chamada livraria. Isso pode ser feito com o comando:
create database livraria;
Após executá-lo, a saída deve ser parecida com:
Query OK, 1 row affected (0.00 sec)
Perfeito, vamos usar agora o comando use livraria para acessar essa
base de dados que criamos.
use livraria;
A mensagem Database changed deve ser exibida.
4.2
Criando a tabela de produtos
Podemos agora criar uma tabela onde serão persistidas as informações de nossos produtos. Faremos isso com o comando create table a seguir:
CREATE TABLE produtos (
id BIGINT NOT NULL AUTO_INCREMENT,
nome VARCHAR(255),
descricao VARCHAR(255),
valor VARCHAR(255),
isbn VARCHAR(255),
PRIMARY KEY (id)
);
Se você não está acostumado com SQL, não se preocupe. Nosso foco aqui
será a integração do código Java com um banco de dados, e não o SQL em si.
Em poucas palavras, o comando criou uma tabela de produtos com as colunas
id, nome, descricao e isbn, que é o que precisamos por agora. Note
também que dissemos que o id do produto não pode ser nulo e deve ser
autoincrementado, assim o próprio banco de dados cuidará de incrementar o
valor do id a cada novo registro.
60
Casa do Código
Capítulo 4. Banco de Dados e JDBC
4.3
O pacote java.sql e o JDBC
A biblioteca padrão do Java para persistência em banco de dados é con-
hecida como JDBC, de Java Data Base Connective. O JDBC é, na verdade, um
conjunto de interfaces bem elaborado que deve ser implementado de forma
diferente para cada banco de dados. Dessa forma, evitamos que cada banco
tenha seu próprio conjunto de classes e métodos. Qual a vantagem disso?
Manutenibilidade é uma das muitas. Migrar de um banco para outro é um
processo fácil, já que todos implementam as mesmas interfaces, portanto pos-
suem métodos com a mesma assinatura.
Esse conjunto de classes é conhecido como driver; elas fazem a ponte
entre a API de JDBC e o banco de dados. O nome é análogo aos drivers uti-
lizados na instalação de impressoras. Da mesma forma que os drivers possi-
bilitam a comunicação entre uma determinada impressora com os diferentes
sistemas operacionais existentes, os drivers que implementam as interfaces do JDBC possibilitam a comunicação entre nosso código Java com os diferentes
bancos de dados existentes.
61

4.3. O pacote java.sql e o JDBC
Casa do Código
Fig. 4.4: Comunicação entre código Java e Banco de Dados
Boa prática: pacote java.sql
No momento de fazer os imports das classes, tome o cuidado de sem-
pre escolher o pacote java.sql. Nunca escolha opções de classes
equivalentes do pacote com.mysql, ou qualquer outro banco de dados
que você estiver utilizando. Dessa forma, você não se acopla com a API
do banco de dados que está utilizando.
62

Casa do Código
Capítulo 4. Banco de Dados e JDBC
4.4
Abrindo conexão com MySQL em Java
A classe DriverManager é a responsável por gerenciar o processo de comu-
nicação com os drivers dos bancos de dados. Para abrir uma conexão, tudo
que precisamos fazer é invocar seu método getConnection passando como
parâmetro uma String de conexão do JDBC, seu login e senha. Essa String
de conexão, para o MySQL, tem a seguinte estrutura:
jdbc:mysql://IP/DATABASE
O IP deve ser substituído pelo IP do servidor onde se encontra o banco
de dados. Em nosso caso, como será na mesma máquina, podemos deixar
como localhost. A DATABASE deve ser substituída pelo nome da base de
dados que criamos para o projeto, a livraria. Portanto:
jdbc:mysql://localhost/livraria
Para utilizar o DriverManager, precisaremos adicionar o driver do
MySQL em nosso projeto. Esse arquivo também está disponível no zip
que você baixou com os arquivos necessários para este livro. Basta copiar o
arquivo mysql-connector-java-5.1.34-bin.jar para dentro do seu
projeto no Eclipse. Com o botão direito do mouse, clique no driver e depois
em Build Path e Add to Build Path.
Fig. 4.5: Adicionando driver do MySQL no Build Path.
Se você ainda não está familiarizado com o termo JAR, não se preocupe.
Temos um capítulo que detalha bem esse tipo de arquivo e seu uso.
Agora que temos o driver, vamos criar uma nova classe, que chamaremos
de ConnectionFactory para colocar isso tudo em prática. Seu código de-
verá se parecer com:
63
4.4. Abrindo conexão com MySQL em Java
Casa do Código
public class ConnectionFactory {
public Connection getConnection() {
String url = "jdbc:mysql://localhost/livraria";
return DriverManager.getConnection(url, "root", "");
}
}
Design Patterns e o Factory Method
Não foi por acaso que chamamos a classe ConnectionFactory
dessa forma. Essa é uma prática de projeto muito comum e bastante re-
comendada. Muitas vezes queremos controlar o processo de criação de
um objeto que pode ser repetido diversas vezes em nosso código, como
o de abrir uma conexão. O que aconteceria se esse código estivesse es-
palhado por nossa aplicação e alguma informação mudasse? Uma troca
de login, por exemplo, faria você mudar diversas partes do seu código.
Note que, agora que temos a classe ConnectionFactory, não es-
tamos deixando as informações do banco de dados se replicarem em
toda nossa aplicação. O método getConnection não recebe nenhum
parâmetro e retorna uma conexão pronta para ser utilizada. Alguma in-
formação mudou? Não tem problema, só preciso mudar um único ponto
do meu código (a implementação do método getConnection), que
isso será replicado pra todos que o usam. Interessante, não acha?
Essa boa prática de projeto é conhecida como factory method e
compõe um dos diversos Design Patterns existentes.
Mas esse código ainda não compila, o método getConnection lança
uma SQLException. Precisamos tratar ou declarar essa exceção de alguma
forma. uma maneira bastante recomendada de tratar seria:
public class ConnectionFactory {
public Connection getConnection() {
String url = "jdbc:mysql://localhost/livraria";
64
Casa do Código
Capítulo 4. Banco de Dados e JDBC
try {
return DriverManager.getConnection(url, "root", "");
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
Qual a vantagem em fazer isso? Afinal só estamos “embrulhando” e re-
lançando a SQLException como uma RuntimeException. A grande
ideia dessa prática é que assim não deixamos essa exception da API de JDBC
se espalhar pelo nosso código, diminuindo acoplamento com essa API.
Já podemos testar nosso código para garantir que a conexão com o
banco de dados está acontecendo sem nenhum problema. Podemos criar
um método main, na própria classe ConnectionFactory, com o seguinte
código:
public static void main(String[] args) {
Connection conn = new ConnectionFactory().getConnection();
System.out.println("Conexao aberta, e agora?");
conn.close();
}
Execute para ver se tudo está ok. Alguns dos erros comuns são:
• Errar a digitação da String de conexões, como por exemplo esquecer
de uma barra ou algo desse tipo. Neste caso, uma exception parecida
com a seguinte deve acontecer:
Caused by: java.sql.SQLException: No suitable
driver found for jdbc:mysql:/localhost/livraria
• Referenciar uma database que não existe. Por exemplo, se eu não criar
a database livraria no MySQL, o seguinte erro vai acontecer:
Caused by: com.mysql.jdbc.exceptions.jdbc4
.MySQLSyntaxErrorException: Unknown database 'livraria'
65
4.5. Listando todos os produtos do banco
Casa do Código
Se tudo correu bem, a mensagem Conexao aberta, e agora? deve ser ex-
ibida. Podemos dar mais um passo, adicionar e listar elementos com a API
do JDBC.
4.5
Listando todos os produtos do banco
Vamos começar pelo nosso objetivo, que é listar todos os produtos do banco
de dados. Para fazer isso, limpe o conteúdo do método lista da classe
RepositorioDeProdutos, deixe apenas:
public class RepositorioDeProdutos {
public ObservableList<Produto> lista() {
return FXCollections.observableArrayList();
}
}
O primeiro passo será escrever a SQL que queremos executar no banco,
que será algo como:
String sql = "select * from produtos";
A cláusula select com * está indicando que queremos todas as colu-
nas de todos os produtos. Para executar esta, ou qualquer outra query, pre-
cisaremos antes abrir uma conexão com o banco e pra isso podemos utilizar
a ConnectionFactory que criamos. O código fica assim:
Connection conn = new ConnectionFactory().getConnection();
String sql = "select * from produtos";
Como enviar e executar essa query em um banco de dados? É bem sim-
ples, há uma interface chamada PreparedStatement para nos ajudar neste
trabalho. Basta chamar o método prepareStatement da Connection
que criamos, passando a SQL como argumento:
Connection conn = new ConnectionFactory().getConnection();
String sql = "select * from produtos";
PreparedStatement ps = conn.prepareStatement(sql);
ps.executeQuery();
66
Casa do Código
Capítulo 4. Banco de Dados e JDBC
Esse código lança uma
SQLException, vamos relançar essa
exceção como uma
Runtime, da mesma forma que fizemos na
ConnectionFactory para diminuir o acoplamento com a API de
JDBC. O código ficará assim:
Connection conn = new ConnectionFactory().getConnection();
PreparedStatement ps;
try {
ps = conn.prepareStatement("select * from produtos");
ps.executeQuery();
} catch (SQLException e) {
throw new RuntimeException(e);
}
O método
executeQuery fará seu trabalho e retornará um objeto
com todos os registros do resultado da consulta. Esse objeto tem o tipo
ResultSet. Cada chamada ao seu método next retornará true en-
quanto houver próximos registros, portanto, é natural iterar nessa estrutura
utilizando um while, como faremos a seguir:
while(resultSet.next()) {
// como pegar o nome, valor, etc?
}
Seus métodos get são os responsáveis por recuperar o valor de uma
determinada coluna da tabela. Se for uma coluna de texto, utilizamos o
getString, getInt para um inteiro e assim por diante. Em nosso caso,
precisaremos apenas dos métodos getString para nome, descricao, isbn e
do getDouble para o valor do produto. O código deve ficar assim:
while(resultSet.next()) {
String nome = resultSet.getString("nome");
String descricao = resultSet.getString("descricao");
double valor = resultSet.getDouble("valor");
String isbn = resultSet.getString("isbn");
}
Para concluir, nosso método precisa retornar a ObservableList de
Produtos que será consumida pela nossa listagem em Java FX. Por enquanto,
vamos representar os produtos apenas como LivroFisico:
67
4.5. Listando todos os produtos do banco
Casa do Código
while(resultSet.next()) {
LivroFisico livro = new LivroFisico(new Autor());
livro.setNome(resultSet.getString("nome"));
livro.setDescricao(resultSet.getString("descricao"));
livro.setValor(resultSet.getDouble("valor"));
livro.setIsbn(resultSet.getString("isbn"));
produtos.add(livro);
}
Ao final, nossa classe RepositorioDeProdutos deve ficar assim:
public class RepositorioDeProdutos {
public ObservableList<Produto> lista() {
ObservableList<Produto> produtos = observableArrayList();
Connection conn = new ConnectionFactory().getConnection();
PreparedStatement ps;
try {
ps = conn.prepareStatement("select * from produtos");
ResultSet resultSet = ps.executeQuery();
while(resultSet.next()) {
LivroFisico livro = new LivroFisico(new Autor());
livro.setNome(resultSet.getString("nome"));
livro.setDescricao(resultSet.getString("descricao"));
livro.setValor(resultSet.getDouble("valor"));
livro.setIsbn(resultSet.getString("isbn"));
produtos.add(livro);
}
resultSet.close();
ps.close();
conn.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
return produtos;
}
}
68
Casa do Código
Capítulo 4. Banco de Dados e JDBC
Mas, espera! Ao executar a classe Main agora, nossa listagem de produtos
está vazia! Faz sentido, não é? Afinal, não adicionamos nenhum produto ao
banco de dados, a tabela produtos está vazia. Esse será nosso próximo
passo.
Inserindo manualmente no MySQL
Se você quiser, pode inserir um livro manualmente na tabela de pro-
dutos para confirmar desde já que seu código está funcionando como
deveria. Você pode fazer isso executando os seguintes comandos em seu
terminal:
$ mysql -u root livraria
$ insert into produtos (nome, descricao, valor, isbn)
values ('Java 8 Prático', 'Novos recursos da linguagem',
'59.90', '978-85-66250-46-6');
4.6
Importando produtos de um dump
Para não precisar cadastrar manualmente, vamos importar o arquivo
dump.sql com alguns livros já cadastrados para nossa tabela de produtos.
Um arquivo de dumb é um arquivo de texto com instruções SQL, com os in-
serts de dados ou até mesmo com as instruções de criação de tabelas.
Criando seu próprio dump
Se quiser saber mais sobre esse arquivo de dump, ou mesmo criar o
seu próprio, você pode dar uma olhada no link:
http://dev.mysql.com/doc/refman/5.0/en/mysqldump-sql-format.
html
Isso pode ser bem útil quando queremos fazer backups de segurança
de nossas bases de dados.
69

4.6. Importando produtos de um dump
Casa do Código
Esse arquivo também está presente naquele zip com o conteúdo inicial
do projeto, em arquivos-do-livro/dump.sql. Tudo que precisamos
fazer para importar esse dump em nosso banco de dados é executar a seguinte
instrução no terminal:
mysql -uroot < CAMINHO_COMPLETO_PARA_O_DUMP
Para facilitar o processo, você pode copiar o arquivo dump.sql para a
pasta raiz de seu usuário, e executar apenas:
mysql -uroot < dump.sql
Ótimo! Depois que concluir, tente acessar novamente a listagem de pro-
dutos. Se tudo correu bem, diversos livros devem ser listados.
70
Casa do Código
Capítulo 4. Banco de Dados e JDBC
Frameworks ORM
Existem diversas ferramentas que facilitam e muito o uso do JDBC,
que são conhecidas como ferramentas ORM, de Object Relational Map-
ping. A ideia é que você trabalhe pensando exclusivamente em orientação
a objetos, enquanto essas bibliotecas (ou frameworks, como são comu-
mente chamadas) o ajudam a traduzir isso para o mundo relacional dos
bancos de dados. O Hibernate é um dos frameworks de ORM mais con-
hecidos e utilizados do mercado. Aconselho bastante que você pesquise
a respeito desses e outros após conhecer a API de JDBC do Java.
Se quiser saber mais sobre o hibernate você pode querer dar uma ol-
hada no site:
http://hibernate.org/orm/
4.7
Para saber mais: Adicionando programati-
camente
Para conhecer um pouquinho mais sobre a API do JDBC, vamos criar mais
um método na classe RepositorioDeProdutos, que exemplificará como
é feito um INSERT no banco de dados programaticamente. Vamos chamá-lo
de adiciona, afinal esse será seu propósito.
public class RepositorioDeProdutos {
public ObservableList<Produto> lista() {
// código omitido
}
public void adiciona(Produto produto) {
// código de adicionar com JDBC
}
}
Note que esse método já está recebendo o Produto que deverá ser adi-
cionado no banco.
71
4.7. Para saber mais: Adicionando programaticamente
Casa do Código
Da mesma forma como fizemos com o método lista, utilizaremos a
interface PreparedStatement para executar as instruções SQL no banco
de dados.
public void adiciona(Produto produto) {
Connection conn = new ConnectionFactory().getConnection();
PreparedStatement ps = conn.prepareStatement("???");
// código de executar o PreparedStatement
ps.close();
conn.close();
}
Vale lembrar que esse código não compilará se não tratarmos a
SQLException que o método prepareStatement lança.
public void adiciona(Produto produto) {
Connection conn = new ConnectionFactory().getConnection();
PreparedStatement ps;
try {
ps = conn.prepareStatement("???");
// código de executar o PreparedStatement
ps.close();
conn.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
Por enquanto, o código está muito parecido com o de listagem, mas a
grande diferença começa aqui. Uma forma de escrever a cláusula INSERT
que será executada no banco de dados seria:
ps = conn.prepareStatement("insert into produtos (nome,
descricao, valor, isbn) values ('Livro de PHP',
'Um livro sobre PHP', '59.90', '123-45-67890-12-3')");
72
Casa do Código
Capítulo 4. Banco de Dados e JDBC
Mas, claro, não podemos deixar esses valores fixos em nosso código. Uma
alternativa é concatenar os atributos do Produto, como faremos a seguir:
ps = conn.prepareStatement("insert into produtos (nome, "
+ "descricao, valor, isbn) values ('"+produto.getNome()+"', "
+ "'"+produto.getDescricao()+"', '"+produto.getValor()+"', "
+ "'"+produto.getIsbn()+")");
Que tal? Nem um pouco legível, não acha? Imagine se no lugar de 4
campos existissem cerca de 20, com o que esse código se pareceria?
A prova de que é muito difícil manter um código como esse é que ao final
da String eu deixei de fora uma ‘ (aspas simples). Você identificou isso ao
olhar pro código? Sem dúvida poderia passar despercebido.
Mas esse não é o único problema desse código cheio de concatenações, há
ainda o clássico problema de nomes que contêm aspas simples (como Joana
D’arc). O que aconteceria ao tentar concatenar essa String na SQL anterior?
A instrução SQL vai quebrar toda! Pior ainda, fazer concatenação dessa forma
possibilita ao usuário final modificar seu SQL para executar o que ele bem
entender, o clássico problema de SQL Injection.
73
4.7. Para saber mais: Adicionando programaticamente
Casa do Código
Para saber mais: SQL Injection
Para entender melhor o que é uma injeção de SQL, vamos considerar
a seguinte instrução que faz um SELECT verificando se o usuário existe.
Ela comumente seria executada por um formulário de login.
ps = conn.prepareStatement("select * from usuarios where "
+ "login = '"+login+"' and senha ='"+senha+"'"); O que acontece se o valor da variável login for um texto vazio e o
valor da variável senha for ’ or 1=1? Vamos substituir a String
com esses valores para verificar. O resultado será:
select * from usuarios where login ='' and senha ='' or 1=1
Esse comando trará todos os usuários de nosso banco de dados! Ou
seja, o usuário final da aplicação pode enviar qualquer informação, que
pode ser maliciosa, e resultar em um retorno inesperado como esse.
Nunca devemos concatenar variáveis em comandos SQL dessa forma.
Bem, já vimos que essa é uma péssima prática. No lugar de concatenar
dessa forma, a interface PreparedStatement nos oferece uma série de
métodos set para popularmos os valores variáveis da instrução SQL. Nosso
comando SQL ficará como a seguir, deixando pontos de interrogação no lugar
desses valores:
ps = conn.prepareStatement("insert into produtos (nome,"
+ " descricao, valor, isbn) values (?,?,?,?)");
Para popular as variáveis, podemos utilizar os métodos setString para
os valores de texto e setDouble para o campo valor, que é do tipo
double, sempre passando a posição (que curiosamente começa em 1, e não
0), seguida do valor que deve ser preenchido:
ps = conn.prepareStatement("insert into produtos (nome,"
+ " descricao, valor, isbn) values (?,?,?,?)");
74
Casa do Código
Capítulo 4. Banco de Dados e JDBC
ps.setString(1, produto.getNome());
ps.setString(2, produto.getDescricao());
ps.setDouble(3, produto.getValor());
ps.setString(4, produto.getIsbn());
4.8
Qual a melhor forma de fechar a conexão?
Um ponto fundamental que precisa ser cuidadosamente verificado sempre
que estamos trabalhando com banco de dados é a forma como fechamos nos-
sas conexões. O nosso código parece estar fazendo isso adequadamente, não
acha? Dê uma boa olhada:
public class RepositorioDeProdutos {
public ObservableList<Produto> lista() {
// código omitido
try {
// código omitido
conn.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
return produtos;
}
}
Tudo parece estar certo, afinal estamos fechando a conexão. Isso é feito
dentro do bloco try, logo após executar o código no banco de dados. Mas
o que acontecerá quando esse código lançar uma Exception? Bem, você já
deve ter percebido o problema. O código do bloco try será interrompido, o
catch será executado e a conexão não será fechada.
Uma forma de resolver o problema seria adicionando a chamada ao
método close também dentro do catch, como no exemplo:
public class RepositorioDeProdutos {
75
4.8. Qual a melhor forma de fechar a conexão?
Casa do Código
public ObservableList<Produto> lista() {
// código omitido
try {
// código omitido
conn.close();
} catch (SQLException e) {
conn.close();
throw new RuntimeException(e);
}
return produtos;
}
}
Isso resolve o problema, mas o que você acha da solução? Nunca é muito
interessante replicar código dessa forma; o que aconteceria se esse método
tivesse múltiplos blocos catch? Copiamos e colamos em cada um deles? E
se esquecermos de algum?
Por nossa sorte não precisamos fazer dessa forma, que não parece ser
ideal. Podemos utilizar o finally:
public class RepositorioDeProdutos {
public ObservableList<Produto> lista() {
// código omitido
try {
// código omitido
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
conn.close();
}
return produtos;
}
}
As instruções de dentro do bloco
finally sempre são executadas,
em caso de exception ou não. É sempre muito interessante fazer oper-
76
Casa do Código
Capítulo 4. Banco de Dados e JDBC
ações como esta, de fechar uma conexão, dentro desse bloco. Colocamos no
finally operações que devem ser executadas a qualquer custo.
Mas ainda há um problema. Se você fez essa mudança, já deve ter perce-
bido que o código parou de compilar. O problema é que o método close da
classe Connection lança uma checked exception, ou seja, uma exceção que
precisa ser tratada ou declarada. Adicionar um novo try catch dentro do
bloco finally teria um custo bem grande na legibilidade desse código. Re-
pare como ele ficaria:
public class RepositorioDeProdutos {
public ObservableList<Produto> lista() {
// código omitido
try {
// código omitido
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
try {
conn.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
return produtos;
}
}
O código ficou bem ruim, não acha? Por nossa sorte, o Java 7 trouxe
uma novidade muito interessante, conhecida como try-with-resources.
Você pode declarar e também inicializar a Connection dentro do try,
como a seguir:
public class RepositorioDeProdutos {
public ObservableList<Produto> lista() {
77

4.9. O padrão de projeto DAO
Casa do Código
// código omitido
try (Connection conn = new ConnectionFactory()
.getConnection()) {
// código omitido
} catch (SQLException e) {
throw new RuntimeException(e);
}
return produtos;
}
}
Pronto, a conexão será fechada para você, independente de se o código
funcionou ou lançou uma exception. Mas em que momento chamamos
o método close? A resposta é: em nenhum. O segredo desse recurso
está na interface AutoCloseable, que interfaces como a Connection
herdam. Você só pode usar o try-with-resources em objetos do tipo
AutoCloseable, assim o Java sabe exatamente qual o método que deve
ser chamado após a execução do seu try catch. Ele funciona como um
finally implícito.
4.9
O padrão de projeto DAO
Uma
última
alteração
que
podemos
fazer
nessa
classe,
a
RepositorioDeProdutos, é em seu nome.
O que estamos fazendo
é uma prática bem comum: isolar a lógica de acesso ao banco de dados em
uma classe especialista neste trabalho. O fluxo atual está assim:
Fig. 4.7: Fluxo atual de acesso a dados.
O benefício de encapsular essa regra de negócios (acesso ao banco)
em uma classe já é facilmente notado quando pensamos em evoluções
futuras.
Por exemplo, se no lugar de persistirmos as informações em
78

Casa do Código
Capítulo 4. Banco de Dados e JDBC
um banco de dados resolvermos utilizar uma planilha de Excel, quantas
classes precisarão ser alteradas em nosso projeto?
Apenas uma, nosso
RepositorioDeProdutos.
Fig. 4.8: Acesso a dados do banco ou planilha.
Como o comportamento está isolado, encapsulado, fica fácil evoluir sem
causar efeitos colaterais indesejados. Para a classe Main, pouco importa
se quando ela chama repositorio.lista() a listagem é carregada de
uma planilha ou base de dados do MySQL, PostgreSQL, Oracle ou qualquer
outro. Pouco importa se estamos usando JDBC ou alguma ferramenta que
simplifique o trabalho, como o hibernate. O que importa para quem chama o
método lista é que ele retorne a listagem de produtos, e ponto.
Essa boa pratica é tão conhecida e utilizada no dia a dia que acabou vi-
rando um dos mais importantes Design Patterns; ele é usado em todos os pro-
jetos que conheço! Talvez você já tenha ouvido falar do termo DAO, eis seu significado: é um acrônimo para Data Access Object.
Mesmo sem saber, já estamos usando o design pattern DAO, mas é muito
comum e recomendado seguirmos o padrão de nome desse tipo de classe.
Como ela encapsula acesso a dados da classe Produto, por padrão normal-
mente a chamamos de ProdutoDAO.
Note que é muito similar ao que fizemos com a ConnectionFactory,
que é outro design pattern que conhecemos. Como ela é uma fábrica de
Connection, recebe como prefixo o nome da classe e como sufixo a palavra
Factory.
Agora que sabemos a convenção, vamos fazer a alteração em nosso
código. Para renomear uma classe pelo Eclipse, basta selecionar essa classe
79

4.9. O padrão de projeto DAO
Casa do Código
pelo package explorer (view lateral) e pressionar F2.
Uma janela chamada Rename Compilation Unit deve ser exibida. Para
concluir, mudamos o nome para ProdutoDAO e clicamos em Finish.
80

Casa do Código
Capítulo 4. Banco de Dados e JDBC
Nosso DAO completo deve ficar assim:
public class ProdutoDAO {
public ObservableList<Produto> lista() {
ObservableList<Produto> produtos =
observableArrayList();
PreparedStatement ps;
try (Connection conn =
new ConnectionFactory().getConnection()) {
ps = conn.prepareStatement(
"select * from produtos");
ResultSet resultSet = ps.executeQuery();
while(resultSet.next()) {
LivroFisico livro =
new LivroFisico(new Autor());
livro.setNome(resultSet.getString("nome"));
81
4.9. O padrão de projeto DAO
Casa do Código
livro.setDescricao(resultSet.getString(
"descricao"));
livro.setValor(resultSet.getDouble("valor"));
livro.setIsbn(resultSet.getString("isbn"));
produtos.add(livro);
}
resultSet.close();
ps.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
return produtos;
}
public void adiciona(Produto produto) {
PreparedStatement ps;
try (Connection conn =
new ConnectionFactory().getConnection()) {
ps = conn.prepareStatement("insert into produtos
(nome," + " descricao, valor, isbn)
values (?,?,?,?)");
ps.setString(1, produto.getNome());
ps.setString(2, produto.getDescricao());
ps.setDouble(3, produto.getValor());
ps.setString(4, produto.getIsbn());
ps.execute();
ps.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
O único impacto em nossa classe Main será na linha em que usávamos
o RepositorioDeProdutos:
ObservableList<Produto> produtos =
82


Casa do Código
Capítulo 4. Banco de Dados e JDBC
new RepositorioDeProdutos().lista();
Agora que ele chama ProdutoDAO, a linha ficará assim:
ObservableList<Produto> produtos = new ProdutoDAO().lista();
Mas não se preocupe em alterar manualmente, o Eclipse já fez isso para
você.
O fluxo agora é:
Fig. 4.11: Acesso a dados encapsulado em um DAO.
Outras alternativas para renomear classes
A forma que vimos é uma das muitas de se renomear uma classe pela
IDE. Outra opção bastante comum pelo Eclipse é utilizando o atalho
Control + Shift + R (rename). A diferença é que o utilizamos di-
retamente na classe, com o cursor sob seu nome:
Basta mudar seu nome e depois pressionar Enter. Todos que fazem
referência para essa classe também serão atualizados automaticamente.
83

4.9. O padrão de projeto DAO
Casa do Código
Também podemos renomear o pacote do nosso ProdutoDAO, que atual-
mente se chama repositorio. Vamos alterar para dao. O processo será o
mesmo, clicamos no pacote, pressionamos F2, escolhemos um novo nome e
depois Finish. Ele ficará assim:
A vantagem de utilizar essas convenções de nomes é que é muito mais
fácil para quem cair de paraquedas no projeto saiba se contextualizar. Se você entrar hoje em um projeto e precisar alterar a forma que um Usuario é
persistido, qual classe você vai abrir? O UsuarioDAO. Não tem erro, você
não precisa saber nada sobre o projeto para saber disso.
84
Capítulo 5
Threads e Paralelismo
A consulta ao banco de dados que alimenta nossa listagem de produtos é su-
ficientemente rápida, afinal estamos trabalhando com poucos valores. Mas já
parou para pensar o que aconteceria se ela demorasse para responder? Outro
ponto que poderia causar um problema é a exportação dessa lista para o ar-
quivo CSV. Quanto mais produtos, mais ela deve demorar. Como nossa apli-
cação reage enquanto uma tarefa está demorando para ser executada?
Neste capítulo, veremos esse problema em prática, assim como uma al-
ternativa para resolvê-lo. Vamos conhecer um pouco da forma como o Java
faz para executar ações em paralelo, com uso de Threads, classes anônimas
e muitos lambdas do Java 8. Se você ainda não está acostumado com esse
recurso, com certeza vai aproveitar bastante os exemplos deste capítulo para
praticar.
5.1. Processamento demorado, e agora?
Casa do Código
5.1
Processamento demorado, e agora?
Uma forma simples de experienciar isso em prática seria forçando nossa apli-
cação a dormir por alguns instantes, no momento em que geramos o arquivo
CSV. Assim conseguimos simular que a função demora cerca de 20 segundos
para ser executada.
button.setOnAction(event -> {
Thread.sleep(20000);
exportaEmCSV(produtos);
});
Veja que quem cuidou desse trabalho foi o método estático sleep, pre-
sente na classe Thread (que muito em breve será desmistificada). Essa classe
está presente no pacote java.lang, portanto não precisa ser importada. Ele
recebe como parâmetro o tempo em milissegundos em que deve dormir (tem-
porariamente pausar) a linha de execução atual, e o força a tratar ou declarar uma InterruptedException.
button.setOnAction(event -> {
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
System.out.println("Ops, ocorreu um erro: "+ e);
}
exportaEmCSV(produtos);
});
Depois dessa alteração, a ação de exportar o arquivo produtos.csv vai
levar pelo menos 20 segundos para ser executada. Execute a aplicação para
ver isso acontecendo.
O que acontece se você tentar redimencionar a janela, clicar nas linhas da
tabela, ou fazer qualquer coisa similar enquanto a ação do button está sendo
executada?
86

Casa do Código
Capítulo 5. Threads e Paralelismo
Fig. 5.1: Aplicação não respondendo, totalmente travada.
A aplicação fica totalmente travada!
A necessidade de executar ações em paralelo
Podem ser muitas as situações em que precisamos executar ações em par-
alelo. Em um sistema operacional, por exemplo, fazemos isso o tempo todo!
Podemos fazer um download e ao mesmo tempo navegar na internet. Já pen-
sou como seria ter que esperar o download terminar para só depois fazer qual-
quer outra coisa? O próprio sistema operacional já gerencia todos os progra-
mas executados em vários processos paralelos, não precisamos nos preocupar
com isso.
Em nossa aplicação Java também podemos executar ações em paralelo,
como exportar os dados da listagem sem ter que travar as demais funções e,
enquanto isso, mostrar o avanço da função de exportar para dar um retorno
(feedback) ao usuário.
5.2
Trabalhando com Threads em Java
Em Java podemos criar linhas de execuções paralelas utilizando a classe
Thread, do pacote java.lang, a mesma que utilizamos para fazer a apli-
87
5.2. Trabalhando com Threads em Java
Casa do Código
cação dormir por 20 segundos.
Para criar e iniciar a execução de uma thread, só precisamos instanciar
um objeto desse tipo e chamar seu método start:
Thread thread = new Thread();
thread.start();
Mas há uma questão, o que essa thread faz? Criamos uma nova linha
de execução, mas em nenhum momento dissemos o que ela deve fazer. Ela
morrerá assim que for criada.
A classe Thread tem um construtor que recebe como argumento um ob-
jeto com o código que queremos executar, tudo que precisamos fazer é criar
essa classe. Vamos chamá-la de ExportadorDeCSV, já que é isso o que quer-
emos fazer em paralelo.
package threads;
public class ExportadorDeCSV {
public void exporta() {
// código que deve ser executado em paralelo
}
}
Vamos, agora, no momento de criar uma Thread, passar o exportador
como argumento:
ExportadorDeCSV exportador = new ExportadorDeCSV();
Thread thread = new Thread(exportador);
thread.start();
Tudo parece certo, mas esse código não vai compilar. O erro será:
constructor java.lang.Thread.Thread(java.lang.Runnable) is
not applicable. (argument mismatch; threads.ExportadorDeCSV
cannot be converted to java.lang.Runnable)
Faz sentido, afinal, como a Thread saberá qual o método que deve ser
executado? E se a classe ExportadorDeCSV tivesse dez métodos, como ela
escolheria um?
88
Casa do Código
Capítulo 5. Threads e Paralelismo
5.3
O contrato Runnable
Para ensinar à classe Thread qual método deve ser executado e dar uma
garantia ao compilador de que esse método está declarado em nossa classe,
usamos um contrato, uma interface Java. Não precisamos criar uma nova interface; se você olhar com atenção na mensagem gerada pelo erro de compi-
lação, você perceberá que esse contrato já existe e é esperado. Estamos falando da interface java.lang.Runnable.
Uma classe que assina o contrato Runnable assume a responsabilidade
de ser um executável. Essa interface tem um único método, chamado run.
Por esse motivo, a Thread sabe exatamente como executá-la.
Vamos modificar nosso ExportadorDeCSV para implementar essa in-
terface. Agora que sabemos como o método deve se chamar, trocaremos o
nome do método de exporta para run:
package threads;
public class ExportadorDeCSV implements Runnable {
@Override
public void run() {
// código que deve ser executado em paralelo
}
}
Ótimo, nosso código passou a compilar.
Para testar, coloque um System.out.println no método run. Algo
simples, como:
package threads;
public class ExportadorDeCSV implements Runnable {
@Override
public void run() {
System.out.println("Rodando em paralelo!");
}
}
89
5.3. O contrato Runnable
Casa do Código
Agora, crie a classe TestandoThread, com o seguinte método main:
package threads;
public class TestandoThread {
public static void main (String... args) {
ExportadorDeCSV exportador = new ExportadorDeCSV();
Thread thread = new Thread(exportador);
thread.start();
System.out.println("Terminei de rodar o main");
}
}
Execute o código para ver o resultado. Deverá ser algo como:
Rodando em paralelo!
Terminei de rodar o main
A ordem pode variar, já que são duas linhas de execuções em paralelo.
Mas não se preocupe com isso agora. O importante é as duas mensagens
terem sido impressas no console. Note que em nenhum momento chamamos
o método run do nosso ExportadorDeCSV, mas ao chamar o método
start da classe Thread ela fez isso por nós.
90
Casa do Código
Capítulo 5. Threads e Paralelismo
Para saber mais: escalonador de Threads
Como executamos ações em paralelo quando existe um único pro-
cessador na máquina? O segredo está no scheduler de threads. Ele é
um escalonador que pega todas as threads que precisam ser executadas
e faz seu processador alternar entre elas. Isso acontece tão rápido que
parece que as duas (ou mais) threads estão rodando ao mesmo tempo.
Essa alternação que ele faz é conhecida como context switch, ou troca
de contexto.
Quando existem dois ou mais processadores, a máquina virtual do
Java e grande parte (se não todos) os sistemas operacionais conseguem
tirar proveito disso. Nesse caso, verdadeiramente teremos execuções par-
alelas, ainda escalonadas pelo scheduler do Java. Isso porque podemos
ter 20 threads sendo executadas em dois processadores, por exemplo. A
diferença é que a troca de contexto acontecerá entre eles e não em um
único processador.
5.4
Threads com classes anônimas e lambdas
No lugar de criarmos a classe
ExportadorDeCSV implementando um
Runnable, poderíamos fazer isso diretamente utilizando uma classe anôn-
ima. Isso é extremamente comum quando estamos trabalhando com
Threads. O código ficaria assim:
Runnable exportador = new Runnable() {
@Override
public void run() {
System.out.println("Rodando em paralelo!");
}
};
Thread thread = new Thread(exportador);
thread.start();
No lugar de extrair as variáveis intermediárias, é natural fazer isso direta-
mente:
91
5.4. Threads com classes anônimas e lambdas
Casa do Código
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Rodando em paralelo!");
}
}).start();
O código completo da classe TestandoThread fica:
package threads;
public class TestandoThread {
public static void main (String... args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Rodando em paralelo!");
}
}).start();
System.out.println("Terminei de rodar o main");
}
}
Vamos executar o código para ver o resultado? A mesma coisa. As duas
mensagens serão impressas, podendo variar um pouco a ordem, já que são
executadas em paralelo.
O problema desse código está tanto na verbosidade quanto na sintaxe, que
é bem poluída. Chegam a chamar esse tipo de código de “problema vertical
, por causa da quantidade de linhas.
Essa foi uma das motivações para introdução das expressões lambdas, do
Java 8. Assim como já estamos usando esse recurso com a ação do button,
podemos usar com o Runnable. O segredo está no conceito de interface
funcional, que é como uma interface de um único método abstrato é chamada no Java 8.
Em outras palavras, como um Runnable só tem um método abstrato,
podemos usar a sintaxe do lambda, já que o Java saberá exatamente qual
92
Casa do Código
Capítulo 5. Threads e Paralelismo
método está sendo implementado. O código ficará assim:
package threads;
public class TestandoThread {
public static void main (String... args) {
new Thread(() -> {
System.out.println("Rodando em paralelo!");
}).start();
System.out.println("Terminei de rodar o main");
}
}
O que acha, mais simples? A sintaxe pode parecer estranha no começo,
mas sem dúvida o código está mais enxuto. Como existe apenas uma única
instrução dentro da expressão lambda, podemos tirar as chaves, ponto e vír-
gula e colocar tudo em um único statement:
package threads;
public class TestandoThread {
public static void main (String... args) {
new Thread(() -> System.out
.println("Rodando em paralelo!")).start();
System.out.println("Terminei de rodar o main");
}
}
O foco do livro não é Java 8, mas se você ainda não está acostumado com
as expressões lambdas e outros recursos, não deixe de praticar. Ficou com
alguma dúvida? Lembre-se de mandar um e-mail na lista do grupo.
93
5.5. Exportando em uma thread separada
Casa do Código
5.5
Exportando em uma thread separada
Pronto, agora que conhecemos um pouco mais sobre Threads em Java,
podemos atacar o problema da lógica de exportar em CSV. Para não travar
a aplicação, podemos rodar toda a lógica de exportar a lista de produtos em
paralelo!
Vamos aplicar essa mudança à classe Main. A action do button ficará
assim:
Button button = new Button("Exportar CSV");
button.setOnAction(event -> {
new Thread(() -> {
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
System.out.println("Ops, ocorreu um erro: "+ e);
}
exportaEmCSV(produtos);
}).start();
});
94
Casa do Código
Capítulo 5. Threads e Paralelismo
Se você está com Java 7...
Em Java 7, com uso das classes anônimas, esse código ficaria assim:
Button button = new Button("Exportar CSV");
button.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
System.out.println("Ops,
ocorreu um erro: "+ e);
}
exportaEmCSV(produtos);
}
}).start();
}
});
Vale lembrar que você não precisa ter o Java 8 instalado para rodar
esses exemplos, no final do capítulo vou mostrar o código completo com
Java 7 também. Aproveite para fazer uma comparação entre as duas sin-
taxes.
Para melhorar um pouco a legibilidade, vamos extrair o código do
Thread.sleep, que faz a aplicação dormir por 20 segundos, para dentro
de um método:
private void dormePorVinteSegundos() {
try {
Thread.sleep(20000);
95
5.5. Exportando em uma thread separada
Casa do Código
} catch (InterruptedException e) {
System.out.println("Ops, ocorreu um erro: "+ e);
}
}
Agora o código da action de exportar em CSV fica assim:
button.setOnAction(event -> {
new Thread(() -> {
dormePorVinteSegundos();
exportaEmCSV(produtos);
}).start();
});
Com essas alterações, o código completo da classe Main deve ficar da
seguinte forma:
package application;
// imports omitidos
@SuppressWarnings({ "unchecked", "rawtypes" })
public class Main extends Application {
@Override
public void start(Stage primaryStage) {
Group group = new Group();
Scene scene = new Scene(group, 690, 510);
ObservableList<Produto> produtos =
new ProdutoDAO().lista();
TableView<Produto> tableView =
new TableView<>(produtos);
TableColumn nomeColumn = new TableColumn("Nome");
nomeColumn.setMinWidth(180);
nomeColumn.setCellValueFactory(
new PropertyValueFactory("nome"));
96
Casa do Código
Capítulo 5. Threads e Paralelismo
TableColumn descColumn = new TableColumn("Descrição");
descColumn.setMinWidth(230);
descColumn.setCellValueFactory(
new PropertyValueFactory("descricao"));
TableColumn valorColumn = new TableColumn("Valor");
valorColumn.setMinWidth(60);
valorColumn.setCellValueFactory(
new PropertyValueFactory("valor"));
TableColumn isbnColumn = new TableColumn("ISBN");
isbnColumn.setMinWidth(180);
isbnColumn.setCellValueFactory(
new PropertyValueFactory("isbn"));
tableView.getColumns().addAll(nomeColumn, descColumn,
valorColumn, isbnColumn);
final VBox vbox = new VBox(tableView);
vbox.setPadding(new Insets(70, 0, 0 ,10));
Label label = new Label("Listagem de Livros");
label.setFont(Font
.font("Lucida Grande", FontPosture.REGULAR, 30));
label.setPadding(new Insets(20, 0, 10, 10));
Button button = new Button("Exportar CSV");
button.setLayoutX(575);
button.setLayoutY(25);
button.setOnAction(event -> {
new Thread(() -> {
dormePorVinteSegundos();
exportaEmCSV(produtos);
}).start();
});
97
5.5. Exportando em uma thread separada
Casa do Código
group.getChildren().addAll(label, vbox, button);
primaryStage.setTitle(
"Sistema da livraria com Java FX");
primaryStage.setScene(scene);
primaryStage.show();
}
private void exportaEmCSV(ObservableList<Produto> produtos){
try {
new Exportador().paraCSV(produtos);
} catch (IOException e) {
System.out.println("Erro ao exportar: "+ e);
}
}
private void dormePorVinteSegundos() {
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
System.out.println("Ops, ocorreu um erro: "+ e);
}
}
public static void main(String[] args) {
launch(args);
}
}
Pronto para testar mais uma vez? Execute a aplicação e mande exportar a
lista de produtos como CSV. Você vai perceber que, mesmo que esse código
demore 20 segundos para ser executado, o restante da aplicação continua fun-
cionando sem travar. Experimente redimencionar a janela, clicar nas linhas
da tabela etc. Tudo continua funcionando.
98
Casa do Código
Capítulo 5. Threads e Paralelismo
5.6
Um pouco mais sobre as Threads
Trabalhar com Threads nem sempre é um sonho. Quando estamos trabal-
hando com linhas de execuções diferentes, a complexidade de nosso código
aumenta. Há ainda uma preocupação constante, que são os temíveis proble-
mas de concorrência. O que poderia dar errado?
Dê uma boa olhada no seguinte código:
public class LivroFisico extends Livro
implements Promocional {
private boolean jaTeveDesconto = false;
// parte do código omitido
public boolean aplicaDescontoDe(double porcentagem) {
if (jaTeveDesconto) {
return false;
}
jaTeveDesconto = true;
// restante do código do método
}
}
Grande parte do código está omitido, mas só precisamos focar nessa
parte. Essa classe possui um método chamado aplicaDescontoDe, que
da forma que está só aplica o desconto uma única vez por instância. Temos
um atributo boolean para nos ajudar, o jaTeveDesconto. O código é bem
simples e tudo parece certo. De certa maneira está, o problema só vai aparecer quando duas ou mais Thread concorrerem pelo acesso desse método.
Imagine que queremos fazer um processamento em massa, em todos os
nossos livros, chamando o método aplicaDescontoDe. Esse processa-
mento será executado em paralelo, com diversas Threads. O que acontece
se duas Threads passarem “ao mesmo tempo” pelo if que valida se ele já
teve desconto?
Isso pode resultar em um desastre. Como a primeira Thread pode
ainda não ter chegado à linha que atribui
true ao valor do atributo
jaTeveDesconto, as duas vão passar pelo if. Em outras palavras, vamos
aplicar o desconto duas vezes para o mesmo objeto.
99
5.6. Um pouco mais sobre as Threads
Casa do Código
Claro, esse é um exemplo bem simples e o problema não aconteceria sem-
pre. Esse código poderia ser executado por um mês sem nunca acontecer uma
acesso concorrente, até que um belo dia o scheduler de threads passasse duas
para o mesmo objeto.
Como impedir isso? O Java nos oferece um recurso interessante, um meio
de garantir que apenas uma Thread acessará determinado bloco ou método.
Se outra Thread chegar, ela vai precisar esperar a primeira terminar todo o
trabalho para depois começar o seu. O acesso é sincronizado.
public class LivroFisico extends Livro
implements Promocional {
private boolean jaTeveDesconto = false;
// parte do código omitido
public synchronized boolean
aplicaDescontoDe(double porcentagem) {
if (jaTeveDesconto) {
return false;
}
jaTeveDesconto = true;
// restante do código do método
}
}
Note no uso da palavra chave synchronized. É ela quem dá essa carac-
terística ao método aplicaDescontoDe. Com isso, já estamos prevenindo
acesso concorrente a essa lógica, garantimos que não existirá concorrência
nela. Normalmente chamamos de thread safe uma classe que está protegida contra acesso concorrente.
100
Casa do Código
Capítulo 5. Threads e Paralelismo
Bloco sincronizado
Utilizando o modificador synchronized em um método, estamos
dizendo que todo o código daquele método é sincronizado. Faz sentido.
Mas como dizer que apenas um bloco de código deve ser sincronizado?
Há uma outra forma de utilizar o synchronized, definindo um
bloco de código que recebe qual é o objeto que será bloqueado:
public boolean aplicaDescontoDe(double porcentagem) {
synchronized (this) {
if (jaTeveDesconto) {
return false;
}
}
jaTeveDesconto = true;
// restante do código do método
}
Agora apenas o if está no bloco do synchronized, no lugar de
todo o método.
5.7
Garbage Collector
Não podemos falar de threads sem comentar sobre uma Thread bastante
conhecida em Java, responsável por jogar fora os objetos que não estão mais
sendo referenciados em nosso código. Estamos falando do famoso Garbage
Collector (coletor de lixo).
Já não é uma novidade que quando criamos um novo objeto, suas infor-
mações serão mantidas em memória. Atribuímos esse objeto do tipo Autor,
por exemplo, a uma variável para futuramente termos uma forma de nos ref-
erenciarmos a ele:
Autor autor = new Autor();
101


5.7. Garbage Collector
Casa do Código
Fig. 5.2: Variável autor referenciando o objeto em memória.
Mas o que acontece com o objeto em memória quando deixamos de nos
referenciar a ele? Por exemplo, ao atribuir um novo autor para essa variável: Autor autor = new Autor();
Autor outro = new Autor();
autor = outro;
Fig. 5.3: Duas variáveis referenciando o mesmo objeto.
A partir desse momento, ninguém mais está referenciando esse objeto, di-
reta ou indiretamente. Isso significa que ele não existe mais em memória? Não necessariamente. Significa com toda a certeza que ele não está mais acessível, mas não que ele foi removido da memória.
Vale lembrar que quem cuida desse trabalho é o Garbage Collector
(GC), que é uma Thread. Mas quando ela é executada? A todo mo-
mento? Provavelmente não, isso teria um custo bem grande e totalmente
102
Casa do Código
Capítulo 5. Threads e Paralelismo
desnecessário. Ela é executada de forma estratégica, quando for preciso e conveniente para a JVM, podendo depender de uma implementação para outra.
Não há uma forma de obrigar a thread do Garbage Collector a ser execu-
tada. A classe System até possui um método estático chamado gc, mas não
se engane, chamá-lo não significa que o GC será executado. Isso apenas sug-ere para a JVM que rode o GC, ela pode ou não aceitar. O uso desse método
normalmente é desencorajado, são pouquíssimas as situações em que usá-lo
fará alguma diferença em sua aplicação, o ideal é não depender disso.
5.8
Java FX assíncrono
Agora que já conhecemos um pouco mais sobre a API de Threads do Java,
podemos voltar ao nosso código. Resolvemos o problema de a ação demorada
travar a aplicação, mas não estamos dando uma resposta muito adequada para
o cliente que pediu a lista como um CSV. Ele clica não botão, olha no diretório e o arquivo produtos.csv ainda não está lá. Só depois de alguns segundos
o arquivo é gerado.
Para melhorar isso, podemos mostrar uma mensagem indicando que o
arquivo está sendo exportado e logo em seguida atualizá-la para indicar que
o trabalho foi concluído.
Vamos começar criando uma
Label chamada
progresso, para
mostrar este texto.
Label progresso = new Label();
Note que não atribuímos um texto inicial para ela, seu texto será adi-
cionado e atualizado dinamicamente pela ação de exportar produtos.
Lembre-se de que, para deixá-la visível, precisamos adicioná-la ao group
de elementos dessa tela:
group.getChildren().addAll(
label, vbox, button, progresso);
Ótimo, agora podemos tentar adicionar e atualizar seu texto dentro da
Thread que acontece no setOnAction do botão de exportar. Uma possi-
bilidade seria:
103
5.9. Trabalhando com a classe Task
Casa do Código
button.setOnAction(event -> {
new Thread(() -> {
progresso.setText("Exportando...");
dormePorVinteSegundos();
exportaEmCSV(produtos);
progresso.setText("Concluído!");
}).start();
});
Com isso, queremos que, ao iniciar, nossa Thread adicione o texto
Exportando... no Label, que chamamos de progresso. Quando con-
cluir, mude seu texto para Concluído!. Parece fazer sentido, não acha? Mas
ao executar:
Exception in thread "Thread-5"
java.lang.IllegalStateException:
Not on FX application thread;
Isso aconteceu porque não podemos adicionar ou atualizar o texto de um
elemento do Java FX em uma Thread que não seja do Java FX, ou sua própria
Thread principal.
5.9
Trabalhando com a classe Task
Para nos ajudar nesse trabalho, o Java FX oferece uma classe chamada Task.
Essa classe é bem útil quando estamos trabalhando de forma assíncrona,
como já veremos em prática. Ela tem um único método abstrato, chamado
call.
Comumente instanciamos uma Task utilizando classes anônimas:
Task<Void> task = new Task<Void>() {
@Override
protected Void call() throws Exception {
// algum trabalho
return null;
}
};
104
Casa do Código
Capítulo 5. Threads e Paralelismo
Pergunta: por que não um lambda?
Talvez você se pergunte por que criamos a Task usando uma classe
anônima e não com uma expressão lambda? Porque funciona com o
Runnable, mas não com a Task. Não vou falar a resposta agora, tente
descobrir. Ok? Não se preocupe, em breve eu lhe conto.
A Task pode ter um retorno, mas note que, como não queremos retornar
nada, utilizamos o tipo Void (classe wrapper do já tão conhecido void).
Vamos modificar nosso código para que, no lugar de executar o método
de dormir por 20 segundos e exportar dentro de um Runnable, ela faça isso
dentro da Task.
No lugar de fazer:
button.setOnAction(event -> {
new Thread(() -> {
dormePorVinteSegundos();
exportaEmCSV(produtos);
}).start();
});
Vamos quebrar esse trabalho em duas partes, primeiro criando uma in-
stância de Task cujo método call fará esse trabalho. Ela deve ficar dentro
do setOnAction, no botão de exportar:
button.setOnAction(event -> {
Task<Void> task = new Task<Void>() {
@Override
protected Void call() throws Exception {
dormePorVinteSegundos();
exportaEmCSV(produtos);
return null;
}
};
});
105
5.9. Trabalhando com a classe Task
Casa do Código
Agora, ainda dentro do setOnAction, vamos criar e iniciar uma nova
Thread passando essa Task como argumento.
button.setOnAction(event -> {
Task<Void> task = new Task<Void>() {
@Override
protected Void call() throws Exception {
dormePorVinteSegundos();
exportaEmCSV(produtos);
return null;
}
};
new Thread(task).start();
});
O trecho de código do botão de exportar ficará assim:
Button button = new Button("Exportar CSV");
Label progresso = new Label();
button.setOnAction(event -> {
Task<Void> task = new Task<Void>() {
@Override
protected Void call() throws Exception {
dormePorVinteSegundos();
exportaEmCSV(produtos);
return null;
}
};
new Thread(task).start();
});
Podemos executar nossa aplicação para confirmar que tudo está funcio-
nando.
106
Casa do Código
Capítulo 5. Threads e Paralelismo
Resposta: por que não um lambda?
Você provavelmente já descobriu o motivo, mas não podemos imple-
mentar a Task como um lambda porque ela não é uma interface fun-
cional. Ela não é nem mesmo uma interface: se você olhar seu código perceberá que se trata de uma classe anônima. Nós só podemos usar a
sintaxe do lambda com interfaces funcionais, sem exceções.
Mas e quanto ao texto da Label?
Mudamos para Task e tudo continua funcionando, mas e quanto ao texto
da Label de progresso? O problema ainda não foi resolvido.
Agora que estamos usando a API do Java FX para executar código assín-
crono, podemos tirar bastante proveito de alguns recursos. Por exemplo, há
um método chamado setOnRunning em Task que nos permite executar
um evento quando a task está prestes a ser executada.
task.setOnRunning(new EventHandler<WorkerStateEvent>() {
@Override
public void handle(WorkerStateEvent e) {
// alguma ação aqui
}
});
O EventHandler sim é uma interface funcional, portanto, podemos
tirar proveito do lambda. O código fica assim:
task.setOnRunning( e -> {
// alguma ação aqui
});
Nesse momento, podemos atualizar o texto da Label, dizendo que o
produto está sendo exportado:
task.setOnRunning(e -> {
progresso.setText("exportando...");
});
107
5.9. Trabalhando com a classe Task
Casa do Código
Como é uma única expressão, não precisamos das chaves nem do ponto
e vírgula:
task.setOnRunning(e -> progresso.setText("exportando..."));
O código do evento de setOnAction ficará assim:
button.setOnAction(event -> {
Task<Void> task = new Task<Void>() {
@Override
protected Void call() throws Exception {
dormePorVinteSegundos();
exportaEmCSV(produtos);
return null;
}
};
task.setOnRunning(e -> progresso.setText("exportando..."));
new Thread(task).start();
});
Vamos testar. Após modificar o código, podemos executá-lo e ao clicar
em exportar o resultado será o esperado:
108

Casa do Código
Capítulo 5. Threads e Paralelismo
Fig. 5.4: Texto ‘exportando...’ sendo exibido na tela.
O texto está desalinhado, claro. Mas logo cuidaremos disso. Ainda pre-
cisamos fazer com que esse texto seja atualizado no momento em que a Task
seja concluída. Podemos fazer isso utilizando um outro método de callback,
o setOnSucceeded.
Ele também recebe um EventHandler como parâmetro, portanto, va-
mos de lambda:
task.setOnSucceeded(e -> {
progresso.setText("concluído!");
});
Agora nosso código está assim:
button.setOnAction(event -> {
Task<Void> task = new Task<Void>() {
@Override
protected Void call() throws Exception {
dormePorVinteSegundos();
exportaEmCSV(produtos);
return null;
}
};
109
5.9. Trabalhando com a classe Task
Casa do Código
task.setOnRunning(e -> progresso.setText("exportando..."));
task.setOnSucceeded(e -> progresso.setText("concluído!"));
new Thread(task).start();
});
Outros métodos interessantes
Além do setOnRunning e setOnSucceeded, que acabamos de
conhecer, a classe Task tem outros métodos de callback que podem ser
muito úteis em seu dia a dia. São eles:
• setOnCancelled
• setOnFailed
• setOnScheduled
Não deixe de conferir a documentação da classe Task para saber
mais detalhes desses e outros métodos. Além de pesquisar, tente testar
alguns deles em seu projeto.
https://docs.oracle.com/javafx/2/api/javafx/concurrent/Task.html
Pronto, vamos ver o resultado. Ao clicar em Exportar CSV a men-
sagem exportando... é exibida. Alguns segundos depois, o texto é atual-
izado para concluído!. Sucesso.
110


Casa do Código
Capítulo 5. Threads e Paralelismo
Fig. 5.5: Texto concluído sendo exibido na tela.
Por fim, podemos alinhar essas mensagens. Faremos isso da mesma
forma como foi feito no button:
Label progresso = new Label();
progresso.setLayoutX(485);
progresso.setLayoutY(30);
Ao executar a aplicação, clincando em Exportar CSV teremos:
111

5.10. Código final com e sem lambdas
Casa do Código
Após alguns segundos...
Perfeito. Vimos que trabalhar com Threads não é fácil, mas também não
é um bicho de sete cabeças. Temos que tomar alguns cuidados extras, claro.
Em Java FX, a classe Task ajuda bastante nesse trabalho. Ela oferece uma
forma flexível e desacoplada de encapsular e executar código paralelo.
5.10
Código final com e sem lambdas
Nosso código final, com Java 8, ficou assim:
@SuppressWarnings({ "unchecked", "rawtypes" })
public class Main extends Application {
@Override
public void start(Stage primaryStage) {
Group group = new Group();
Scene scene = new Scene(group, 690, 510);
ObservableList<Produto> produtos =
new ProdutoDAO().lista();
TableView<Produto> tableView =
112
Casa do Código
Capítulo 5. Threads e Paralelismo
new TableView<>(produtos);
TableColumn nomeColumn = new TableColumn("Nome");
nomeColumn.setMinWidth(180);
nomeColumn.setCellValueFactory(
new PropertyValueFactory("nome"));
TableColumn descColumn = new TableColumn("Descrição");
descColumn.setMinWidth(230);
descColumn.setCellValueFactory(
new PropertyValueFactory("descricao"));
TableColumn valorColumn = new TableColumn("Valor");
valorColumn.setMinWidth(60);
valorColumn.setCellValueFactory(
new PropertyValueFactory("valor"));
TableColumn isbnColumn = new TableColumn("ISBN");
isbnColumn.setMinWidth(180);
isbnColumn.setCellValueFactory(
new PropertyValueFactory("isbn"));
tableView.getColumns().addAll(nomeColumn, descColumn,
valorColumn, isbnColumn);
final VBox vbox = new VBox(tableView);
vbox.setPadding(new Insets(70, 0, 0 ,10));
Label label = new Label("Listagem de Livros");
label.setFont(Font
.font("Lucida Grande", FontPosture.REGULAR, 30));
label.setPadding(new Insets(20, 0, 10, 10));
Label progresso = new Label();
progresso.setLayoutX(485);
progresso.setLayoutY(30);
113
5.10. Código final com e sem lambdas
Casa do Código
Button button = new Button("Exportar CSV");
button.setLayoutX(575);
button.setLayoutY(25);
button.setOnAction(event -> {
Task<Void> task = new Task<Void>() {
@Override
protected Void call() throws Exception {
dormePorVinteSegundos();
exportaEmCSV(produtos);
return null;
}
};
task.setOnRunning(
e -> progresso.setText("exportando..."));
task.setOnSucceeded(
e -> progresso.setText("concluído!"));
new Thread(task).start();
});
group.getChildren().addAll(label, vbox, button,
progresso);
primaryStage.setTitle(
"Sistema da livraria com Java FX");
primaryStage.setScene(scene);
primaryStage.show();
}
private void exportaEmCSV(ObservableList<Produto> produtos){
try {
new Exportador().paraCSV(produtos);
} catch (IOException e) {
System.out.println("Erro ao exportar: "+ e);
}
114
Casa do Código
Capítulo 5. Threads e Paralelismo
}
private void dormePorVinteSegundos() {
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
System.out.println("Ops, ocorreu um erro: "+ e);
}
}
public static void main(String[] args) {
launch(args);
}
}
Conforme prometido, a parte que muda do setOnAction em Java 7:
button.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
Task<Void> task = new Task<Void>() {
@Override
protected Void call() throws Exception {
dormePorVinteSegundos();
exportaEmCSV(produtos);
return null;
}
};
task.setOnRunning(new EventHandler<WorkerStateEvent>() {
@Override
public void handle(WorkerStateEvent e) {
progresso.setText("exportando...");
}
});
task.setOnSucceeded(
new EventHandler<WorkerStateEvent>() {
@Override
115

5.10. Código final com e sem lambdas
Casa do Código
public void handle(WorkerStateEvent e) {
progresso.setText("concluído!");
}
});
new Thread(task).start();
}
});
Curiosamente, o Eclipse nos oferece uma forma bem simples de converter
classes anônimas para expressões lambda e vice-versa. Basta você selecionar
o código e utilizar o atalho Control + 1. Quando temos um lambda, a
opção Convert to anonymous classe creation estará disponível.
Fig. 5.8: Convertendo lambda para classe anônima no Eclipse.
O mesmo vale para o caminho contrário, quando temos uma classe anôn-
ima e queremos transformar em uma expressão lambda, mas nesse caso a
opção será Convert to lambda expression. Esses recursos da IDE nos ajudam
muito, migrar classes anônimas para lambdas nunca foi tão divertido!
116
Capítulo 6
CSS no Java FX
Nossa aplicação está cada vez mais completa, mas antes de continuar veremos
como melhorar ainda mais não só a aparência de nossa listagem de produtos,
mas também a qualidade e manutenibilidade de nosso código.
Se você perceber, o estilo padrão do Java FX já é bastante atraente. Sem
adicionar cores ou configurar nada, nossa aplicação já está com uma aparên-
cia bem aceitável. Repare em detalhes como a borda e a linha selecionada na
tabela de produtos. Ao passar o mouse sob o botão Exportar CSV, você
também pode perceber que sua cor ficará mais suave.
Mas ao utilizar estilos personalizados do Java FX vamos além da aparên-
cia. Você perceberá ao longo do capítulo que isso possibilita deixar nosso
código ainda mais limpo, enxuto e fácil de manter. Vamos começar?

6.1. Seu primeiro CSS no Java FX
Casa do Código
6.1
Seu primeiro CSS no Java FX
Antes de sair aplicando novos estilos em nosso código, vamos olhar mais de
perto a forma como fizemos até agora. Repare como fizemos para person-
alizar a fonte e adicionar um padding em nossa Label, por exemplo.
Label label = new Label("Listagem de Livros");
label.setFont(Font.font(
"Lucida Grande", FontPosture.REGULAR, 30));
label.setPadding(new Insets(20, 0, 10, 10));
Isso funciona muito bem, mas adiciona uma certa verbosidade a nosso
código. O que realmente importa aqui é a primeira linha, onde criamos a
Label. Queremos um código limpo e simples de entender, pois, quanto mais
próximo disso nosso código for, melhor será sua manutenibilidade. Repare
no que acontece se tirarmos o estilo desse código:
Label label = new Label("Listagem de Livros");
Ficou ótimo! Bem simples de entender. Mas o resultado visual...
O ponto é: precisamos atacar os dois pontos. O código precisa ser limpo,
mas ao mesmo tempo nossas views precisam ser estilizadas para que a apli-
cação fique mais atraente e tenha uma boa usabilidade.
Nossa solução inicial será extrair estes estilos e aplicá-los utilizando o
método setStyle, como a seguir:
118

Casa do Código
Capítulo 6. CSS no Java FX
Label label = new Label("Listagem de Livros");
label.setStyle(???);
Esse método espera receber como parâmetro as propriedades CSS que
devem ser aplicadas no elemento. Um exemplo seria:
Label label = new Label("Listagem de Livros");
label.setStyle("-fx-font-size:
30px; -fx-padding: 20 0 10 10;");
Se você já está habituado com CSS, deve ter percebido que as propriedades
aqui são muito parecidas com CSS que utilizamos na web, tendo o prefixo
-fx sempre presente como uma diferença. Neste caso, o código está fazendo
o mesmo que o anterior, onde declarávamos o tamanho da fonte e padding
programaticamente. Podemos executá-lo para verificar o resultado:
Excelente! Ele está com a aparência esperada, mas agora utilizando as
propriedades do CSS.
119

6.2. Extraindo estilos pra um arquivo .css
Casa do Código
Java FX CSS Reference Guide
Na documentação do método setStyle, você encontrará um link
direto para a página CSS Reference Guide, onde os possíveis estilos são
explicados detalhadamente. Essa página pode ajudar bastante no dia a
dia; consulte-a sempre que quiser conhecer mais detalhes sobre as pro-
priedades e possibilidades dos parâmetros CSS.
Fig. 6.3: Documentação do método setStyle pelo Eclipse
6.2
Extraindo estilos pra um arquivo .css
A mudança que fizemos já pode ser interessante, mas ainda não resolve
nosso problema. Ainda estamos misturando nosso código de estilos com
o restante do código!
No lugar de utilizar o
setStyle dessa forma,
podemos (e devemos, sempre que possível) isolar nossos estilos no arquivo
application.css.
Se você criou o projeto utilizando uma versão mais atual do Eclipse, ou
qualquer outra IDE moderna, o arquivo application.css deve ter sido
criado junto com ele. Não se preocupe se isso não aconteceu, esse é um ar-
quivo normal. Se necessário, você pode criá-lo clicando em New..., sub-
menu File e digitando o nome application.css ou qualquer outro
nome que preferir.
120

Casa do Código
Capítulo 6. CSS no Java FX
Dentro dele colocaremos o seguinte conteúdo:
.label {
-fx-font-size: 30px;
-fx-padding: 20 0 10 10;
}
Note que se parece muito com a estrutura do CSS padrão. Aplicamos o
efeito em .label, pois é a classe padrão (default) do elemento Label. A
maior parte dos elementos visuais do Java FX já tem uma classe default, que
normalmente é seu próprio nome.
Agora
tudo
que
precisamos
fazer
é
adicionar
o
arquivo
application.css como um estilo de nosso cenário. Em nossa classe
Main, faremos algo como:
scene.getStylesheets().add(getClass()
.getResource("application.css").toExternalForm());
Pronto! Ao rodar esse código, vemos que o resultado não foi bem o que
estávamos esperando:
O efeito foi aplicado, mas para todos os labels da view. Para nossa sorte
(ou o oposto disso) o componente TableView também utiliza labels em suas
colunas.
121

6.2. Extraindo estilos pra um arquivo .css
Casa do Código
Identificando elementos únicos
Precisamos de uma forma única de identificar o Label que contém nosso
texto da listagem, sem que os efeitos dele interfiram nos demais Labels que
existem, ou possam existir em algum momento futuro, na view. Podemos
fazer isso adicionando um identificador único ao elemento, como a seguir: Label label = new Label("Listagem de Livros");
label.setId("label-listagem");
Como você pode ver, o método setId nos ajuda com esse trabalho.
Agora que temos uma maneira única de nos referir ao Label, basta mod-
ificar o arquivo application.css para ficar assim:
#label-listagem {
-fx-font-size: 30px;
-fx-padding: 20 0 10 10;
}
O seletor de id tem a estrutura idêntica, mas recebe um # como prefixo,
no lugar do . utilizado para as classes dos elementos. Execute o código e
dessa vez o resultado será o esperado:
Extraindo mais estilos pro application.css
Da mesma forma como fizemos para tirar o padding na Label princi-
pal da nossa listagem, podemos tirar do VBox que utilizamos para alinhar a
tabela. Veja como ele está agora:
122

Casa do Código
Capítulo 6. CSS no Java FX
VBox vbox = new VBox(tableView);
vbox.setPadding(new Insets(70, 0, 0 ,10));
Poderíamos simplesmente remover a chamada ao método setPadding
e no arquivo application.css adicionar o estilo:
.vbox {
-fx-padding: 70 0 0 10;
}
Mas há um detalhe importante, nem todo elemento em Java FX tem uma
classe seletora já definida.
Em outras palavras, para aplicar um estilo no Label, fizemos:
.label {
# estilos aqui
}
Ou seja, por padrão o
Label já tem uma classe seletora, chamada
.label. Já o VBox não tem uma classe padrão, ou seja, o CSS que escreve-
mos com o seletor .vbox não vai funcionar. Ao executar a app dessa forma,
percebemos que o padding não foi aplicado:
Já sabemos como resolver esse problema, certo? Da mesma forma como
demos um identificador único para o Label (label-listagem), podemos dar um ID para o VBox, aplicando um estilo para ele:
123
6.2. Extraindo estilos pra um arquivo .css
Casa do Código
VBox vbox = new VBox(tableView);
vbox.setId("vbox");
Em nosso arquivo application.css, podemos fazer:
#vbox {
-fx-padding: 70 0 0 10;
}
Ótimo, ao executar o código vemos que o padding foi aplicado. Note o #
do seletor, para identificá-lo como um ID.
Podemos agora modificar um pouco o estilo do
Button de expor-
tar produtos como CSV. A primeira mudança pode ser extrair os métodos
setLayouts para um estilo CSS. O código agora está assim:
Button button = new Button("Exportar CSV");
button.setLayoutX(575);
button.setLayoutY(25);
No lugar disso, removemos as chamadas aos dois métodos e adi-
cionamos o mesmo efeito no
application.css, utilizando as pro-
priedades -fx-translate-x e -fx-translate-y:
.button {
-fx-translate-x: 575;
-fx-translate-y: 25;
}
Também podemos aplicar uma cor nesse botão,
utilizando o
-fx-background-color. Uma forma de fazer isso seria:
.button {
-fx-translate-x: 575;
-fx-translate-y: 25;
-fx-background-color: rgb(31, 149, 206);
-fx-text-fill: white;
}
Assim, o button ficará com o azul padrão do Java FX. Se preferir,
você pode aplicar qualquer outra cor. O mesmo para os outros elementos,
124

Casa do Código
Capítulo 6. CSS no Java FX
claro. Note que também utilizamos a propriedade -fx-text-fill para
deixar o texto branco. Além desses, existem diversas outras propriedades.
Aproveite para explorar o Java FX CSS Reference Guide durante seus testes
para conhecer mais algumas delas.
Outro detalhe bem interessante é que, da mesma forma que no CSS con-
vencional de navegadores, também podemos adicionar um :hover em nos-
sos seletores para definir o estilo para o momento em que o mouse estiver
sob o elemento. Para ver isso em prática, vamos dizer que queremos uma
transparência maior no button, quando estiver em hover:
.button:hover {
-fx-background-color: rgba(31, 149, 206, 0.71);
}
Atualize e execute seu código. Você perceberá a transparência ao passar
o cursor do mouse sob o botão.
Fig. 6.7: Button com e sem efeito de hover
O mesmo pode ser feito com o Label de progresso, que está assim:
Label progresso = new Label();
progresso.setLayoutX(485);
progresso.setLayoutY(30);
Removemos essas duas linhas e damos um ID para esse campo:
Label progresso = new Label();
progresso.setId("label-progresso");
Agora basta adicionar o estilo no arquivo de CSS:
125
6.2. Extraindo estilos pra um arquivo .css
Casa do Código
#label-progresso {
-fx-translate-x: 485;
-fx-translate-y: 30;
}
Explorando mais seletores e efeitos
Você pode utilizar diversas outras funções e efeitos, como um
linear-gradient na cor de seu button ou fundo do scene. E que
tal um efeito de sombra?
.button {
-fx-background-color:
linear-gradient(#61a2b1, #2A5058);
-fx-effect: dropshadow(three-pass-box,
rgba(0,0,0,0.6), 5, 0.0, 0, 1);
}
Não deixe de testar. Você pode ver esses e diversos outros efeitos no
tutorial de Java FX da Oracle:
https://docs.oracle.com/javase/8/javafx/get-started-tutorial
Quantidade de produtos e valor em estoque
Para deixar nossa listagem um pouco mais completa, vamos adicionar um
novo Label com o valor total que temos em estoque e também a quantidade
de produtos. Criar a label já sabemos. Podemos também adicionar um
identificador único, com o método setID, para aplicar um estilo no novo
elemento. O código ficará assim:
Label labelFooter = new Label("Texto vai aqui");
labelFooter.setId("label-footer");
No CSS, podemos alinhar a label-footer e também modificar o
tamanho de sua fonte, para que tenha um destaque maior.
#label-footer {
-fx-font-size: 15px;
126
Casa do Código
Capítulo 6. CSS no Java FX
-fx-translate-x: 210;
-fx-translate-y: 480;
}
Precisamos agora adicionar o texto e valores que devem aparecer nesse
Label. Vamos utilizar o método format da classe String mais uma vez,
para deixar o resultado formatado como a seguir:
Label labelFooter = new Label(
String.format("Você tem R$%.2f em estoque, " +
"com um total de %d produtos.", 100.00, 10));
labelFooter.setId("label-footer");
Dessa forma, o resultado será parecido com:
Você tem R$100.00 em estoque, com um total de 10 produtos.
Para saber mais: String#format
Note que utilizamos
%.2f para formatar um número de ponto flu-
tuante em duas casas decimais, além do
%d para exibir o número in-
teiro. Existem diversas outras opções, como
%s para Strings,
%c
para chars e
%t para datas. Se você não está familiarizado com as
convenções do Formatter e quiser conhecer um pouco mais, pode dar
uma olhada em:
https://docs.oracle.com/javase/7/docs/api/java/util/Formatter.html
Não queremos trabalhar com um valor fixo, como acabamos de fazer. No
lugar disso, nos interessa recuperar o valor total de todos os produtos de nossa lista. Podemos iterar na lista acumulando todos os valores em uma variável
temporária:
double valorTotal = 0.0;
for (Produto produto: produtos) {
valorTotal += produto.getValor();
}
127
6.2. Extraindo estilos pra um arquivo .css
Casa do Código
Não tem muito segredo nesse código, usamos o += para incrementar o
valor de cada produto.
Código mais declarativo com Java 8
No lugar de escrever esse código imperativo, onde declaramos uma
variável intermediária ( valorTotal) e incrementamos seu valor em
um enhanced-for, que tal fazer esse mesmo trabalho de uma forma
mais declarativa, com um toque de programação funcional do Java 8?
Utilizando a nova API de Stream, o código ficaria assim:
double valorTotal = produtos.stream()
.mapToDouble(Produto::getValor).sum();
Se você está utilizando Java 8, atualize seu código para testar. O resul-
tado será o mesmo.
Para concluir, podemos recuperar a quantidade de produtos em estoque
utilizando o método size da lista de produtos:
Label labelFooter = new Label(
String.format("Você tem R$%.2f em estoque, " +
"com um total de %d produtos.",
valorTotal, produtos.size()));
labelFooter.setId("label-footer");
Só precisamos adicionar o labelFooter na view, e pronto:
group.getChildren().addAll(label,
vbox, button, progresso, labelFooter);
Perfeito, execute o código para ver o resultado!
128

Casa do Código
Capítulo 6. CSS no Java FX
O código completo da classe Main ficou assim:
package application;
import io.Exportador;
import java.io.IOException;
import javafx.application.Application;
import javafx.collections.ObservableList;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.VBox;
129
6.2. Extraindo estilos pra um arquivo .css
Casa do Código
import javafx.scene.text.*;
import javafx.stage.Stage;
import br.com.casadocodigo.livraria.produtos.Produto;
import dao.ProdutoDAO;
@SuppressWarnings({ "unchecked", "rawtypes" })
public class Main extends Application {
@Override
public void start(Stage primaryStage) {
Group group = new Group();
Scene scene = new Scene(group, 690, 510);
scene.getStylesheets().add(getClass()
.getResource("application.css").toExternalForm());
ObservableList<Produto> produtos =
new ProdutoDAO().lista();
TableView<Produto> tableView =
new TableView<>(produtos);
TableColumn nomeColumn = new TableColumn("Nome");
nomeColumn.setMinWidth(180);
nomeColumn.setCellValueFactory(
new PropertyValueFactory("nome"));
TableColumn descColumn = new TableColumn("Descrição");
descColumn.setMinWidth(230);
descColumn.setCellValueFactory(
new PropertyValueFactory("descricao"));
TableColumn valorColumn = new TableColumn("Valor");
valorColumn.setMinWidth(60);
valorColumn.setCellValueFactory(
new PropertyValueFactory("valor"));
TableColumn isbnColumn = new TableColumn("ISBN");
130
Casa do Código
Capítulo 6. CSS no Java FX
isbnColumn.setMinWidth(180);
isbnColumn.setCellValueFactory(
new PropertyValueFactory("isbn"));
tableView.getColumns().addAll(nomeColumn, descColumn,
valorColumn, isbnColumn);
final VBox vbox = new VBox(tableView);
vbox.setId("vbox");
Label label = new Label("Listagem de Livros");
label.setId("label-listagem");
Label progresso = new Label();
progresso.setId("label-progresso");
Button button = new Button("Exportar CSV");
button.setOnAction(event -> {
Task<Void> task = new Task<Void>() {
@Override
protected Void call() throws Exception {
dormePorVinteSegundos();
exportaEmCSV(produtos);
return null;
}
};
task.setOnRunning(e -> progresso.setText(
"exportando..."));
task.setOnSucceeded(e -> progresso.setText(
"concluído!"));
new Thread(task).start();
});
double valorTotal = produtos.stream()
131
6.2. Extraindo estilos pra um arquivo .css
Casa do Código
.mapToDouble(Produto::getValor).sum();
Label labelFooter = new Label(
String.format("Você tem R$%.2f em estoque, " +
"com um total de %d produtos.",
valorTotal, produtos.size()));
labelFooter.setId("label-footer");
group.getChildren().addAll(label,
vbox, button, progresso, labelFooter);
primaryStage.setTitle(
"Sistema da livraria com Java FX");
primaryStage.setScene(scene);
primaryStage.show();
}
private void exportaEmCSV(ObservableList<Produto> produtos){
try {
new Exportador().paraCSV(produtos);
} catch (IOException e) {
System.out.println("Erro ao exportar: "+ e);
}
}
private void dormePorVinteSegundos() {
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
System.out.println("Ops, ocorreu um erro: "+ e);
}
}
public static void main(String[] args) {
launch(args);
}
}
132
Casa do Código
Capítulo 6. CSS no Java FX
E nosso arquivo application.css:
#label-listagem {
-fx-font-size: 30px;
-fx-padding: 20 0 10 10;
}
#label-progresso {
-fx-translate-x: 485;
-fx-translate-y: 30;
}
.table-view {
-fx-min-width: 665;
-fx-max-width: 665;
}
.table-column {
-fx-padding: 0 0 0 10;
}
#vbox {
-fx-padding: 70 0 0 10;
}
.button {
-fx-translate-x: 575;
-fx-translate-y: 25;
-fx-background-color: rgb(31, 149, 206);
-fx-text-fill: white;
}
.button:hover {
-fx-background-color: rgba(31, 149, 206, 0.71);
}
#label-footer {
-fx-font-size: 15px;
-fx-translate-x: 210;
133
6.2. Extraindo estilos pra um arquivo .css
Casa do Código
-fx-translate-y: 480;
}
Antes de seguir para os próximos capítulos, você pode personalizar ainda
mais a sua listagem. Aplique novos estilos, cores, fontes, efeitos. Use e abuse da documentação para absorver esse conteúdo de forma prática. Se quiser,
você pode compartilhar comigo e com os demais leitores o resultado final do
projeto com as suas melhorias.
134
Capítulo 7
JAR, bibliotecas e build
Queremos o quanto antes lançar nossa aplicação, distribuir nas diversas
livrarias e stands de venda. Por onde podemos começar?
Neste capítulo, veremos não só como compactar e distribuir nossa apli-
cação final, mas também como gerar e utilizar bibliotecas para linguagem
Java. Além disso, veremos uma forma interessante de fazer o build e gerenci-
amento de dependências de nosso projeto, utilizando Maven, que é uma das
ferramentas mais essenciais do mercado de hoje em dia.
7.1
JAR
Não queremos mandar todas as nossas classes e arquivos de configurações
soltos para o cliente final, isso seria uma bagunça. Perder um dos arquivos
implicaria no não funcionamento da aplicação, por exemplo. Uma forma bem
7.2. Gerando JAR executável pela IDE
Casa do Código
mais interessante seria compactar tudo em um único arquivo, um ZIP com
tudo que o usuário precisa para executar nosso código.
Esse tipo de ZIP recebe na verdade a extensão JAR, de Java ARchive (arquivo Java).
Nele estarão presentes nosso código-fonte compilado (
bytecode), arquivos de configurações e dependências.
Como criar um JAR executável? Isso é bem simples, você inclusive pode
fazer com qualquer compactador ZIP atual que conheça. Veremos uma
forma bem prática de fazer pela própria IDE, em nosso caso o Eclipse.
JAR na linha de comando
Você também pode gerar um JAR diretamente pala linha de co-
mando, utilizando o programa jar presente no JDK. Apesar de ser um
pouco mais trabalhoso, você consegue gerar o seu executável com o co-
mando:
jar -cvf livraria.jar
src/application/*.java
src/db/*.java
src/io/*.java
src/repositorio/*.java;
7.2
Gerando JAR executável pela IDE
Fazer o processo pelo Eclipse, ou qualquer outra IDE moderna, é verdadeira-
mente mais simples. Para gerar o JAR executável de nossa aplicação Java FX,
tudo o que precisamos fazer é clicar com o botão direito no projeto, selecionar Export e em seguida a opção Runnable JAR File.
136

Casa do Código
Capítulo 7. JAR, bibliotecas e build
Após selecioná-la, podemos escolher em Launch configuration qual a
classe que deverá ser executada ao rodar esse
JAR. Vamos escolher a
classe Main. Além disso, no campo Export destination, precisamos
preencher o local em que o arquivo executável Java deve ser criado, assim
como seu nome e extensão (que será .jar):
137

7.3. Executando a livraria-fx.jar
Casa do Código
Em meu caso, o caminho ficou /Users/turini/Desktop/livraria-fx.jar,
em outras palavras, o arquivo se chamará livraria-fx.jar e será criado
em minha área de trabalho.
Tudo pronto, basta clicar em Finish. Simples, não acha? Aproveite para
gerar o JAR executável em sua casa, siga esses passos e confira se o arquivo
livraria-fx.jar foi criado no caminho que você definiu.
7.3
Executando a livraria-fx.jar
Agora que temos o arquivo criado, podemos tentar executá-lo para conferir se
tudo correu bem. Vamos fazer isso pela linha de comando, utilizando java
138

Casa do Código
Capítulo 7. JAR, bibliotecas e build
-jar SEU_EXECUTAVEL. Um exemplo seria:
java -jar Desktop/livraria-fx.jar
Excelente, a app está funcionando conforme esperado!
Note que, ao clicar em Export CSV, o arquivo produtos.csv com a
relação de todos os livros será criado na área de trabalho, mesmo local onde
se encontra o JAR.
139

7.4. Bibliotecas em Java
Casa do Código
Para saber mais: JAR Launcher e outros
Você pode utilizar programas como o JAR Launcher para rodar os
seus executáveis Java com apenas um click, sem a necessidade de digi-
tar o comando java -jar no terminal. No Mac OS, por exemplo, esse
programa já vem instalado. Há diversas outras opções para os mais difer-
entes sistemas operacionais.
7.4
Bibliotecas em Java
Um JAR não precisa necessariamente ser um executável. É muito comum
criarmos um JAR com um conjunto de classes que podem ser reutilizados
em outros projetos. Em outras palavras, criar uma biblioteca Java.
Por sinal, o grande número de bibliotecas gratuitas em Java é um dos fa-
tores que fazem a linguagem ser tão bem aceita no mercado. Precisa gerar
140
Casa do Código
Capítulo 7. JAR, bibliotecas e build
relatórios, manipular XMLs, conectar ao banco de dados, gerar códigos de
barras etc.? Você já tem tudo isso pronto! O ecossistema do Java é enorme.
E, o melhor, grande parte (talvez a maior parte) dos projetos (bibliotecas) em Java é gratuita e tem seu código aberto, os conhecidos open sources.
A Caelum, empresa onde trabalho, possui diversos projetos open source.
VRaptor, Mamute e Stella são alguns deles. Contribuir com projetos de
código aberto tem diversos benefícios, desde o conhecimento, que você com
certeza vai adquirir e compartilhar, até o reconhecimento e divulgação de seu trabalho. Se você quiser, pode conhecer alguns deles no repositório público
da Caelum:
http://github.com/caelum/
7.5
Documentando seu projeto com Javadoc
É sempre muito interessante documentar as suas classes quando você cria
um novo JAR. Afinal, como a pessoa que vai usá-lo saberá quais classes e
métodos estão disponíveis? Outras informações como exceções que o código
pode lançar e detalhes importantes de seu uso também são diferenciais sig-
nificantes.
O próprio código do Java é todo documentado, você pode ver a docu-
mentação completa de sua API pelo site da Oracle:
http://download.java.net/jdk8/docs/api/index.html
141

7.5. Documentando seu projeto com Javadoc
Casa do Código
Você pode (e deve) usar o javadoc para criar uma documentação equiv-
alente para os seus projetos. Esse programa também já vem na JDK e pode
ser usado diretamente pela linha de comando, ou com ajuda da IDE. Se ainda
não conhece o javadoc, pode gostar de ler mais a respeito em:
http://www.oracle.com/technetwork/articles/java/index-137868.html
Um exemplo prático de documentação em Java
É muito simples documentar seus projetos em Java, o javadoc cuida de
boa parte do trabalho pesado. Mas, claro, é sempre interessante oferecer algumas informações para enriquecer a documentação de nosso projeto. Fazemos
isso no próprio código:
142
Casa do Código
Capítulo 7. JAR, bibliotecas e build
/**
* Cuida do acesso aos dados da classe {@link Produto}.
*
* @author Rodrigo Turini <[email protected]>
* @since 1.0
*/
public class ProdutoDAO {
/**
* Lista todos os produtos do banco de dados
*
* @return {@link ObservableList} com todos os produtos
* @throws RuntimeException em caso de erros
*/
public ObservableList<Produto> lista() {
// código omitido
}
/**
* @param produto que deverá ser adicionado no banco.
* @throws RuntimeException em caso de erros
*/
public void adiciona(Produto produto) {
// código omitido
}
}
Observe que isso é feito com um tipo de bloco de comentário com signifi-
cado especial. Ele sempre começa com /** e termina com */, nas outras
linhas, colocamos apenas um *.
Você pode adicionar uma descrição para as classes e métodos, mas prefira
nada muito extenso e que conte detalhes de implementação. Por exemplo,
imagine que no javadoc eu diga que estamos persistindo em um banco de
dados MySQL. O que acontecerá se eu migrar de banco? A documentação
ficará desatualizada ou eu precisarei lembrar (o que provavelmente não vai
acontecer).
Também podemos definir outras informações na documentação, como
fizemos com o autor, desde quando a classe existe (since), parâmetros, retorno, 143

7.5. Documentando seu projeto com Javadoc
Casa do Código
exceptions etc. Tudo isso é feito de forma simples com as próprias anotações
de marcação do javadoc.
Depois de devidamente documentado, pelo Eclipse, podemos ir em
Project e depois escolher a opção Generate Javadoc.
Fig. 7.6: Gerando javadoc pela interface do Eclipse.
Uma nova janela aparecerá. Tudo o que precisamos fazer é selecionar o
projeto que queremos documentar e o diretório onde a documentação deverá
ser gerada. Vamos colocar dentro de uma pasta chamada doc dentro do
próprio projeto.
144


Casa do Código
Capítulo 7. JAR, bibliotecas e build
Fig. 7.7: Configurando o projeto e destino do javadoc.
Agora só precisamos clicar em Finish e pronto, confira que a pasta doc
foi criada:
Fig. 7.8: Arquivos criados pelo javadoc na pasta doc.
145
7.6. Automatizando build com Maven
Casa do Código
Abra o arquivo index.html em seu navegador para ver o resultado,
você vai gostar.
7.6
Automatizando build com Maven
O Eclipse facilitou bastante o processo de gerar o JAR executável, mas con-
forme a aplicação vai evoluindo o processo de build pode se tornar mais com-
plexo. É necessário compilar o projeto, copiar suas dependências, configu-
rações etc. Esse processo tem que ser repetido sempre.
Fazer isso manualmente, ainda que com ajuda da IDE, pode ser tanto tra-
balhoso quanto problemático. A cada execução, o humano que está fazendo
a tarefa pode se esquecer de algum dos passos, cometer algum erro etc.
Qual a solução? Automatizar todo esse processo. Existem diversas ferra-
mentas de build que nos auxiliam nesse trabalho, o Maven é uma das mais
populares. Com ele e seus diversos plugins, o processo de build e gerencia-
mento de dependências de nossas aplicações ficam bem simples e sofisticados.
146
Casa do Código
Capítulo 7. JAR, bibliotecas e build
Download e instalação do Maven
Para saber se você tem o Maven instalado em seu sistema operacional,
basta abrir o terminal e digitar o comando mvn -version. A saída
deverá ser parecida com:
Rodrigos-MacBook-Pro:~ turini$ mvn -version
Apache Maven 3.2.3 (...; 2014-08-11T17:58:10-03:00)
Maven home: /Applications/apache-maven-3.2.3
Java version: 1.8.0_25, vendor: Oracle Corporation
Java home: /Library/Java/.../jdk1.8.0_25.jdk/Contents/Home/jre
Default locale: en_US, platform encoding: UTF-8
OS name: "mac os x", version: "10.9.4", arch: "x86_64"
Se você ainda não tem o Maven instalado, pode fazer seu download
direto pelo seu site:
http://maven.apache.org/download.cgi
Lá você também encontra instruções de instalação para cada SO. Se
encontrar qualquer dificuldade para fazer isso, não deixe de postar no
GUJ ou nos mandar um e-mail.
7.7
Transformando nossa app em um projeto
Maven
Agora que já temos tudo baixado e configurado, podemos converter nossa
app para um projeto Maven. O Eclipse também simplifica muito esse tra-
balho, tudo o que precisamos fazer é clicar com o botão direito no projeto e
selecionar:
• Configure
• Convert to Maven Project
147

7.7. Transformando nossa app em um projeto Maven
Casa do Código
A janela Create new POM deve aparecer. Nela, vamos preencher o campo
Group Id com br.com.casadocodigo, que é o pacote padrão do projeto,
o Artifact Id com o nome livraria-fx e manter as outras opções com
o valor padrão.
148

Casa do Código
Capítulo 7. JAR, bibliotecas e build
Clicamos em Finish e pronto, um arquivo chamado pom.xml foi cri-
ado em nosso projeto.
149

7.7. Transformando nossa app em um projeto Maven
Casa do Código
Esse arquivo possui o seguinte conteúdo:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>br.com.casadocodigo</groupId>
<artifactId>livraria-fx</artifactId>
<version>0.0.1-SNAPSHOT</version>
<build>
<sourceDirectory>src</sourceDirectory>
<resources>
<resource>
<directory>src</directory>
<excludes>
<exclude>**/*.java</exclude>
150

Casa do Código
Capítulo 7. JAR, bibliotecas e build
</excludes>
</resource>
</resources>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
Ao abrir o arquivo pela primeira vez, o Eclipse provavelmente mostrará
apenas a página de Overview. Para ver o conteúdo deste XML, você pode clicar
na aba pom.xml que fica visível no rodapé do editor:
Não se preocupe em entender cada detalhe deste XML de configuração
agora, mas note que ele já tem algumas informações essenciais de nosso build.
Nossa app agora já é um projeto Maven. Bem simples, não acha?
7.8
Adicionando as dependências com Maven
Agora que já estamos trabalhando com Maven, podemos deixar o controle
das bibliotecas que temos em nosso projeto com ele. Ele será o responsável
pelo gerenciamento de nossas dependências.
151

7.8. Adicionando as dependências com Maven
Casa do Código
Atualmente nosso projeto tem apenas duas: o driver de conexão com
MySQL e a base do projeto da livraria, onde se encontra a interface Produto
e suas implementações.
Para começar, vamos remover esses dois elementos de nosso classpath.
Faremos isso clicando com o botão direito no projeto, opção Build Path e
Configure Build Path.
Na aba Library, clicamos no mysql-connector-java.jar e em
Remove.
Em seguida, faremos o mesmo com o projeto livraria-base, na aba
Project:
152

Casa do Código
Capítulo 7. JAR, bibliotecas e build
Ao clicar em Ok, o projeto já deve parar de compilar, afinal essas bibliote-
cas são necessárias. Vamos agora abrir o arquivo pom.xml e declarar essas
duas dependências lá:
<!-- inicio do arquivo omitido -->
<dependencies>
<dependency>
<groupId>br.com.caelum.livraria</groupId>
<artifactId>livraria-base</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.34</version>
</dependency>
</dependencies>
153

7.8. Adicionando as dependências com Maven
Casa do Código
</project>
Note que para fazer isso só precisamos adicionar uma tag chamada
dependencies, e para cada dependência uma nova tag dependency com
a seguinte estrutura:
<dependency>
<groupId> VALOR_AQUI </groupId>
<artifactId> VALOR_AQUI </artifactId>
<version> VALOR_AQUI </version>
</dependency>
Adicionando pela interface do Eclipse
Se preferir, você também pode adicionar novas dependências em seu
projeto diretamente pela interface do Eclipse. Basta clicar em Maven e
Add Dependency.
Dessa forma, você não precisa escrever nenhuma linha de XML, ele
fará esse trabalho para você.
154

Casa do Código
Capítulo 7. JAR, bibliotecas e build
Vamos ver se tudo funcionou? Basta clicar com o botão direto no projeto,
Maven e escolher Update Project... (ou pressionar o atalho Alt + F5).
Feito isso, a ferramenta fará seu trabalho e o projeto deve voltar a com-
pilar. Note que as dependências ficam visíveis em Maven Dependencies,
direto pelo package explorer:
155

7.8. Adicionando as dependências com Maven
Casa do Código
Buscando por dependências no Maven
Assim como o mysql-connector-java, você pode encontrar qual-
quer outra dependência necessária pra seus projetos no site:
http://search.maven.org/
156


Casa do Código
Capítulo 7. JAR, bibliotecas e build
Basta clicar na versão que quer utilizar e copiar a tag da dependência para
dentro do arquivo pom.xml de seu projeto.
7.9
Executando algumas tasks do Maven
Vamos agora ver o build do Maven em prática? Na opção Run As..., cli-
cando com o botão direito no projeto, escolha Maven Build.
157


7.9. Executando algumas tasks do Maven
Casa do Código
Adicione no campo Goals as opções:
• compile package
Essas são algumas das tasks padrões do Maven. Como o próprio nome já
diz, estamos dizendo que queremos compilar e empacotar, gerar um JAR do
projeto. Se você quiser, pode ler mais sobre esses Goals em:
158
Casa do Código
Capítulo 7. JAR, bibliotecas e build
http://maven.apache.org/guides/introduction/
introduction-to-the-lifecycle.html
Ao executar, a saída será parecida com:
[INFO] Scanning for projects...
[INFO]
...
[INFO] -----------------------------------------------
[INFO] Building livraria-fx 0.0.1-SNAPSHOT
[INFO] -----------------------------------------------
[INFO]
...
[INFO] Building jar: /Users/turini/Documents/workspace
/livraria-fx/target/livraria-fx-0.0.1-SNAPSHOT.jar
[INFO] -----------------------------------------------
[INFO] BUILD SUCCESS
[INFO] -----------------------------------------------
[INFO] Total time: 7.129 s
[INFO] Finished at: 2014-11-22T20:14:47-03:00
[INFO] Final Memory: 8M/94M
[INFO] -----------------------------------------------
Sucesso! O build foi concluído sem nenhuma falha. Note que o JAR foi
criado dentro de uma pasta chamada target, esse é o destino padrão da
ferramenta.
Mas esse JAR não é um executável! Precisamos deixar claro ao Maven
que esse é um projeto Java FX. Como fazer isso?
7.10
Adicionando plugin do Java FX
Como um extra, ensinaremos ao Maven como gerar um JAR executável do
Java FX. Só de pensar parece trabalhoso, não é? Mas na verdade isso tudo já
está pronto, existem diversos plugins que nos auxiliam nesse trabalho. Uma
opção bastante utilizada é o javafx-maven-plugin.
Da mesma forma como fizemos para adicionar uma dependência, pode-
mos abrir o arquivo pom.xml e adicionar as seguintes linhas do plugin, logo
após o maven-compiler-plugin que já está declarado:
159
7.10. Adicionando plugin do Java FX
Casa do Código
<plugins>
<!-- maven-compiler-plugin omitido -->
<plugin>
<groupId>com.zenjava</groupId>
<artifactId>javafx-maven-plugin</artifactId>
<version>8.1.2</version>
<configuration>
<mainClass>application.Main</mainClass>
</configuration>
</plugin>
</plugins>
Observe que estamos definindo que a classe application.Main é a
classe principal de nossa aplicação. Além disso, precisamos adicionar a tag
organization como a seguir:
<organization>
<name>Casa do Código</name>
</organization>
Tudo pronto! Já podemos usar uma de suas tasks. Para gerar um JAR
executável, por exemplo, você pode clicar em Run As..., Maven Build
e em Goals adicionar o valor jfx:jar.
160

Casa do Código
Capítulo 7. JAR, bibliotecas e build
Ao clicar em Run, o resultado será parecido com:
[INFO] Scanning for projects...
...
[INFO] Building Java FX JAR for application
[INFO] Adding 'deploy' directory to Mojo
classpath: /Users/turini/Documents
/workspace/livraria-fx/src/main/deploy
[INFO] ---------------------------------------
[INFO] BUILD SUCCESS
[INFO] ---------------------------------------
[INFO] Total time: 6.793 s
[INFO] Finished at: 2014-11-22T20:36:10-03:00
[INFO] Final Memory: 9M/93M
[INFO] ---------------------------------------
Note que o arquivo JAR foi gerado dentro da pasta target, no subdi-
retório jfx/app:
161

7.10. Adicionando plugin do Java FX
Casa do Código
Se quiser, teste para verificar que ele foi criado corretamente. Para isso,
basta digitar o comando:
java -jar target/jfx/app/livraria-fx-0.0.1-SNAPSHOT-jfx.jar
Perfeito, tudo está funcionando. Mas isso foi um pouco trabalhoso, não
acha? Para nossa sorte, o plugin de Java FX já tem uma task que cria e executa o JAR de nossa aplicação automaticamente! Para utilizá-la, clique novamente
em Run As..., Maven Build.... No campo Goals adicione jfx:run:
162

Casa do Código
Capítulo 7. JAR, bibliotecas e build
Agora basta clicar em Run e a app será executada a partir do JAR criado
pelo plugin.
Conhecendo mais alguns plugins
Além do plugin de Java FX que utilizamos, existem mais centenas de
plugins que facilitam bastante o nosso trabalho. Você pode ver uma lista
com alguns dos plugins existentes em:
http://maven.apache.org/plugins/index.html
7.11
Maven na linha de comando
Vimos que é possível executar as metas do Maven diretamente pelo Eclipse,
mas comumente esse trabalho é feito diretamente pela linha de comando. Se
você quiser testar, basta abrir a pasta do seu projeto pelo terminal e executar a instrução mvn com as metas que você quiser. Por exemplo:
mvn jfx:run
163
7.11. Maven na linha de comando
Casa do Código
Note que o resultado será o mesmo, um JAR executável de nossa app será
criado e executado. Você pode executar uma série de comandos de uma só
vez, basta separá-los por um espaço, como a seguir:
mvn clean jfx:run
A primeira meta executada, o clean, é utilizada para limpar os arquivos
e diretórios gerados pelo Maven na pasta target. Esse comando, portanto,
limpa a pasta target e logo em seguida executa o jfx:run responsável
por gerar e executar o JAR do projeto.
Algumas das metas mais comuns do Maven são:
• compile compila o código-fonte do projeto
• test compila e executa seus testes de unidade existentes no projeto
• package empacota (gera o JAR, por exemplo) do código compilado
de seu projeto
• validate valida se o projeto tem todas as informações necessárias
para seu funcionamento
• install instala localmente seu projeto
Se você estiver interessado, pode conhecer um pouco mais sobre as pos-
sibilidades do Maven em:
http://maven.apache.org/general.html
164
Casa do Código
Capítulo 7. JAR, bibliotecas e build
Outra opção: ANT e Ivy
Outra opção bastante conhecida e utilizada no mercado é o Ant, tam-
bém da Apache. Ele cuida apenas do processo de build. Para o gerencia-
mento de dependências você pode contar com outro framework bastante
conhecido, o Ivy. Esses dois trabalham bem juntos e têm suas vantagens
e desvantagens, assim como qualquer outra ferramenta. Há quem prefira
usá-los no lugar do Maven. Interessado em saber mais sobre Ant e Ivy?
A documentação deles pode ajudar bastante, elas estão disponíveis nos
links:
• http://ant.apache.org/manual/index.html
• http://ant.apache.org/ivy/
7.12
Como ficou nosso pom.xml
Após as alterações, nosso arquivo pom.xml ficou assim:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>livraria-fx</groupId>
<artifactId>livraria-fx</artifactId>
<version>0.0.1-SNAPSHOT</version>
<organization>
<name>Casa do Código</name>
</organization>
<build>
<sourceDirectory>src</sourceDirectory>
165
7.12. Como ficou nosso pom.xml
Casa do Código
<resources>
<resource>
<directory>src</directory>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</resource>
</resources>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>com.zenjava</groupId>
<artifactId>javafx-maven-plugin</artifactId>
<version>8.1.2</version>
<configuration>
<mainClass>application.Main</mainClass>
</configuration>
</plugin>
</plugins>
</build>
<reporting>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-plugin-plugin</artifactId>
<version>3.2</version>
</plugin>
</plugins>
</reporting>
<dependencies>
<dependency>
<groupId>br.com.caelum.livraria</groupId>
166
Casa do Código
Capítulo 7. JAR, bibliotecas e build
<artifactId>livraria-base</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.34</version>
</dependency>
</dependencies>
</project>
É natural não reagir tão bem ao ver um arquivo xml com tantas in-
formações, mas após usar a ferramenta por alguns instantes você logo deve
perceber que não é nada de tão complicado. Atualizar suas dependências
nunca foi tão fácil, além de que podemos fazer o build completo de nossa
aplicação com apenas um comando.
167
Capítulo 8
Refatorações
“Refatoração é uma técnica controlada para reestruturar um trecho de código
existente, alterando sua estrutura interna sem modificar seu comportamento
externo. Consiste em uma série de pequenas transformações que preservam o
comportamento inicial. Cada transformação (chamada de refatoração) reflete
em uma pequena mudança, mas uma sequência de transformações pode
produzir uma significante reestruturação. Como cada refatoração é pequena, é
menos provável que se introduza um erro. Além disso, o sistema continua em
pleno funcionamento depois de cada pequena refatoração, reduzindo as
chances de o sistema ser seriamente danificado durante a reestruturação.
– Martin Fowler
Essa ideia de utilizar pequenas transformações para melhorar a qualidade
do nosso código recebe o nome de baby steps (passos de bebê). Essa é uma
prática fundamental, utilizada para melhorar a legibilidade, clareza e design

8.1. Refatoração
Casa do Código
de nosso código. Sempre que for fazer uma refatoração, evite mudar tudo
de uma só vez. Caso algum erro apareça, será muito mais difícil identificar
qual das mudanças foi a responsável pelo erro (ou bug, como normalmente
chamamos).
Neste capítulo, vamos fazer pequenas refatorações em nosso código, bus-
cando melhorar a legibilidade, evitar repetições e, com isso, aumentar a qualidade e manutenibilidade de nosso projeto. Além disso, relembraremos algu-
mas das boas práticas e design patterns que vimos ao decorrer do livro. Pronto para começar?
8.1
Refatoração
Logo que começamos a trabalhar com a tabela de Produtos, você deve ter
percebido que o Eclipse mostrou vários warnings. Inclusive, usamos a ano-
tação @SuppressWarnings para silenciar esses alertas, mas por que eles
estão acontecendo?
Olhando com cuidado para a mensagem, temos a informação:
TableColumn is a raw type. References to generic
type TableColumn<S,T> should be parameterized
Em outras palavras, não estamos informando os parâmetros genéricos da
classe TableColumn. Idealmente, no lugar de fazer:
TableColumn nomeColumn = new TableColumn("Nome");
nomeColumn.setMinWidth(180);
nomeColumn.setCellValueFactory(
new PropertyValueFactory("nome"));
170
Casa do Código
Capítulo 8. Refatorações
Seria necessário fazer assim:
TableColumn<Produto, String> nomeColumn =
new TableColumn<Produto, String>("Nome");
nomeColumn.setMinWidth(180);
nomeColumn.setCellValueFactory(
new PropertyValueFactory<Produto, String>("nome"));
Veja que agora estamos definindo a classe TableColumn passando o
Produto, que é o tipo do objeto que está sendo manipulado, mais a String,
que é o tipo do conteúdo no campo da tabela. Mas o código ficou muito
mais verboso, não acha? Por isso, desde o início optamos por não passar o
parâmetro genérico, apesar das centenas de warnings.
Outro problema nessa parte do código está na quantidade de repetições.
Observe que grande parte do código está idêntico, para cada coluna:
TableView<Produto> tableView = new TableView<>(produtos);
TableColumn nomeColumn = new TableColumn("Nome");
nomeColumn.setMinWidth(180);
nomeColumn.setCellValueFactory(
new PropertyValueFactory("nome"));
TableColumn descColumn = new TableColumn("Descrição");
descColumn.setMinWidth(230);
descColumn.setCellValueFactory(
new PropertyValueFactory("descricao"));
TableColumn valorColumn = new TableColumn("Valor");
valorColumn.setMinWidth(60);
valorColumn.setCellValueFactory(
new PropertyValueFactory("valor"));
TableColumn isbnColumn = new TableColumn("ISBN");
isbnColumn.setMinWidth(180);
isbnColumn.setCellValueFactory(
new PropertyValueFactory("isbn"));
171
8.1. Refatoração
Casa do Código
tableView.getColumns().addAll(nomeColumn, descColumn,
valorColumn, isbnColumn);
Seguindo as boas práticas da orientação a objetos, devemos isolar com-
portamentos repetidos em métodos. O ganho vai desde a reutilização, já que
ele poderá ser chamado várias vezes em nosso código, até em legibilidade, já
que podemos lhe dar um nome bastante significativo.
Vamos fazer essa pequena refatoração em nosso código.
O método
poderá ficar assim:
private TableColumn criaColuna(String titulo,
int largura, String atributo) {
TableColumn column = new TableColumn(titulo);
column.setMinWidth(largura);
column.setCellValueFactory(
new PropertyValueFactory(atributo));
return column;
}
172

Casa do Código
Capítulo 8. Refatorações
Usando Extract Method do Eclipse
Experimente selecionar o trecho de código que você quer extrair e
pressionar as teclas de atalho Control + Shift + M.
Basta digitar o nome do método que você quer criar e pronto!
Apesar de simples, essa pequena mudança já dá uma cara bem melhor
para nosso código. Ele ficou assim:
TableView<Produto> tableView = new TableView<>(produtos);
TableColumn nomeColumn = criaColuna("Nome", 180, "nome"); TableColumn descColumn =
criaColuna("Descrição", 230, "descricao");
TableColumn valorColumn = criaColuna("Valor", 60, "valor"); TableColumn isbnColumn = criaColuna("ISBN", 180, "isbn"); 173
8.1. Refatoração
Casa do Código
tableView.getColumns().addAll(nomeColumn, descColumn,
valorColumn, isbnColumn);
Além disso, agora que ele está isolado, podemos até adicionar os parâmet-
ros genéricos sem sentir tanto o custo de legibilidade. O método ficará assim: private TableColumn<Produto, String> criaColuna(String titulo,
int largura, String atributo) {
TableColumn<Produto, String> column =
new TableColumn<Produto, String>(titulo);
column.setMinWidth(largura);
column.setCellValueFactory(
new PropertyValueFactory<Produto, String>(atributo));
return column;
}
Outras formas de refatoração
A extração de métodos é apenas um dos muitos tipos de refatorações.
Renomear variáveis, classes e métodos também faz bastante diferença em
nosso código, quanto mais claro for o nome, mais fácil será entender o al-
goritmo. Extrair variáveis é um outro tipo bastante comum e recomendado.
O Eclipse ajuda bastante nesse trabalho, há uma diversidade bem grande
de atalhos para refatoração. A princípio, você não precisa decorar cada um
desses atalhos, basta se lembrar do Alt + Shift + T. Ele mostra as possíveis
refatorações para um determinado trecho de código. Por exemplo, ao usá-lo
em uma classe:
174

Casa do Código
Capítulo 8. Refatorações
Observe que algumas das sugestões são: renomear a classe, mover para
outro pacote, extrair uma interface, entre diversas outras.
Desafio: mais e mais refatorações
As mudanças que fizemos aqui são bastante simples, meu objetivo foi
mostrar o quanto isso pode significar em nosso código. Daqui para frente é
com você. Que outras transformações poderiam melhorar esse projeto? Passe
um tempo pesquisando a respeito, com toda certeza vai valer a pena. Vale
lembrar que existem refatorações bem mais complexas, que afetam diversas
partes de nossa aplicação e até mesmo a sua arquitetura.
8.2
Os tão populares Design Patterns
Não é à toa que eles são populares, vale muito a pena entender e aplicar esses conceitos sempre que for possível em nossos projetos. E isso não muda de-pendendo da linguagem ou tecnologia que você estiver utilizando, o que torna a ideia ainda mais interessante.
Design Patterns é um assunto muito extenso, precisaríamos de pelo menos
175
8.2. Os tão populares Design Patterns
Casa do Código
mais umas 200 páginas pra nos aprofundar nisso. Mas mesmo assim eu tentei
mostrar aqui alguns dos mais fundamentais para nosso dia a dia desenvol-
vendo em Java. Relembrando alguns deles:
• Factory Method: você não quer deixar o código de criação de um
objeto complicado espalhado por aí. Podemos estar falando de uma
Connection do JDBC, como vimos no exemplo passado, ou qualquer
outro. Ele não se limita a uma tecnologia específica. Nosso código ficou
assim:
public class ConnectionFactory {
public Connection getConnection() {
String url = "jdbc:mysql://localhost/livraria";
try {
return DriverManager.getConnection(url, "root", "");
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
Quais as vantagens? Estamos encapsulando esse processo compli-
cado que é criar uma conexão. Além disso, concentramos nossas in-
formações de login, senha e nome do banco de dados em um único
objeto. Se alguma dessas informações mudar, eu só preciso atualizar
essa classe, um único ponto da minha aplicação e não os zilhares de
lugares que acessam nosso banco.
• DAO: a ideia desse pattern é criar uma camada especialista em acessar dados de nossos objetos. Não importa se ele está usando JDBC, Hibernate ou qualquer outra tecnologia. As regras do MySQL, Oracle ou
qualquer outro banco de dados ficam encapsuladas. Note que encapsu-
lamento é um dos pilares de quase todos os design patterns. No lugar
de sair acessando o banco de dados diretamente pelas nossas classes,
passamos sempre pela camada do DAO:
176


Casa do Código
Capítulo 8. Refatorações
Fig. 8.4: Acesso a dados encapsulado em um DAO.
• Template Method: apesar de não implementarmos esse pattern dire-
tamente em nosso código, vimos ele em prática na API de IO do Java.
InputStream ilustra esse exemplo, no fluxo de entrada. O fluxo é o
mesmo, independente de onde vamos ler os bytes:
Fig. 8.5: Método recebendo um InputStream como parâmetro.
Esses são apenas alguns dos muitos que você provavelmente vai usar em
seu dia a dia. Mesmo que não saiba, muitas vezes isso acontece.
177
Capítulo 9
Próximos passos com Java
É isso, espero que tenha aproveitado bastante o conteúdo. Foi uma longa jor-
nada, que para você talvez tenha começado no livro Desbravando Java e Ori-
entação a Objetos. Nos dois livros tentei apresentar todos os conceitos com
exemplos mão na massa e com um foco mais prático.
Não pare por aqui, continue praticando! Tente sempre ir além com novos
testes e cenários além dos aqui propostos. Para mim, a prática é a forma mais efetiva de aprender e fixar qualquer coisa.
A partir daqui, você pode descobrir as muitas outras funcionalidades da
linguagem, assim como suas milhares de bibliotecas, explorar novas APIs etc.
Há muito mais o que explorar no ecossistema da plataforma Java.
9.1. Entre em contato conosco
Casa do Código
9.1
Entre em contato conosco
Não se esqueça de que você pode encaminhar suas dúvidas na lista que foi
criada especialmente para este livro. Não só dúvidas! As sugestões, críticas, ideias e melhorias também são essenciais para nós:
https://groups.google.com/group/livro-java
180