Fundamentos Java
Sobre nossa empresa A Jet Software é uma empresa jovem, dinâmica, que atua no mercado de TI, em consultoria e treinamento. Nossa empresa é especializada em Java, .NET, SOA, EAI e metodologias ágeis. Nossos profissionais são experientes, certificados e possuem vários anos de experiência de mercado, agregando assim alto valor nas soluções apresentadas a nossos clientes, quer sejam elas sobre consultoria ou treinamentos.
Sobre esta apostila Esta apostila tem a finalidade de levar ao aluno os fundamentos da linguagem Java. Ou seja, nosso objetivo é mostrar os conceitos que regem a linguagem, seu funcionamento, comportamento, configuração, setup de ambiente e toda informação básica que darão ao aluno o conhecimento técnico suficiente para a preparação para a certificação e cobrir os pré-requisitos para os próximos módulos de desenvolvimento Java para web, componentes distribuídos ( EJB ) e demais módulos.
Sobre este curso Nosso foco é trazer a nossos alunos um curso dinâmico, abordando o conteúdo da linguagem e da plataforma, e também as melhores práticas e bibliotecas Utilizadas pelo mercado. E foi pensando nisso que a Jet Software elaborou sua grade de cursos, afim de que ao término de cada módulo o aluno esteja dominando a tecnologia / plataforma, e também conhecendo tudo aquilo que o mercado utiliza, estando assim melhor preparado para as futuras oportunidades.
O que o aluno deve aprender sobre Java ? Esta é uma pergunta que muitas pessoas fazem e provavelmente continuarão a fazer. Existe um certo mito de que para ser um bom programador Java o indivíduo deve saber somente “tudo” sobre a linguagem. Fique calmo, pois isto não é verdade! A linguagem e a plataforma Java possui inúmeras features e APIs, e seria impossível descarregar todo este conteúdo sobre o aluno em um só curso ou em um só livro, o que existe sim é um conjunto de regras e conceitos básicos que o aluno necessita saber para iniciar sua vida como programador Java, e após a absorção destes conceitos é muito fácil adicionar mais conhecimento a sua base.
O que o aluno deve aprender sobre Java ? Todos os próximos módulos usarão os conceitos, APIs e demais conhecimentos adquiridos neste curso de fundamentos. Recomendamos aqueles alunos que desejam tirar a certificação que ao término de nosso curso invistam entre 2 ou 4 semanas se preparando para a prova, pois não basta somente absorver o conteúdo em sala de aula como será passado, mas também treinar o que foi aprendido, e ainda preparar-se para as famosas “pegadinhas” da prova. Recomendamos o seguinte livro para a preparação, que contém uma gama imensa de exercícios e dicas :
Preparação para a certificação SCJP Sun Certified Programmer for Java 6 Exam 310-065 [ILLUSTRATED] (Hardcover) by Katherine Sierra (Author), Bert Bates (Author)
Agenda do Curso Introdução ao Java Conceitos Básicos Introdução a Orientação a Objetos Declaração de variáveis Construção de classes Tipos Herança e Polimorfismo Interfaces Enumerações ( Enum ) Controle de Fluxo Tratamento de Exceções O Framework de coleções ( Collection framework ) Generics Threads JDBC
Introdução ao Java Capítulo 1
Simples, orientada a objetos e familiar A linguagem de programação Java foi pensada para “atacar” as dificuldades do desenvolvimento de aplicações, no contexto de heterogeneidade, ambientes de rede distribuídos. E além de todos estes desafios, a execução segura de aplicações que consomem o mínimo de recursos de um sistema, pode ser executada em qualquer hardware e plataforma de software, e além de tudo pode ser estendida dinamicamente.
Simples, orientada a objetos e familiar A linguagem de programação Java foi originada como parte de um projeto de pesquisa para desenvolver uma espécie de software avançado para uma grande variedade de dispositivos de rede ( móveis ). O objetivo era desenvolver uma plataforma operacional que fosse pequena, confiável, portável distribuída, tempo-real.
Simples, orientada a objetos e familiar Quando o projeto iniciou-se o C++ foi a linguagem escolhida. Mas conforme o tempo foi passando as dificuldades encontradas com o C++ cresceram ao ponto de que os problemas poderiam ser melhor resolvidos criando-se uma nova plataforma de linguagem. Decisões de design e arquitetura vieram de uma grande variedade de linguagens, tais como Eiffel, SmallTalk, Objective C e Cedar/Mesa. O resultado foi uma nova linguagem/plataforma que provou ser ideal para o desenvolvimento de aplicações seguras, distribuídas, network-based, aplicações de usuário final em diversos ambientes que vão desde sistemas embarcados ( embedded ) até a Web e desktops.
Robusta e segura A linguagem de programação Java foi arquitetada para ser um software altamente confiável. Ela provê uma extensiva checagem em tempo de compilação, seguida por um segundo nível de checagem em tempo de execução. As features da linguagem guiam os programadores no caminho dos hábitos confiáveis de programação.
Robusta e segura O modelo de gerenciamento de memória é extremamente simples, pois os objetos são criados com o operador new . Não existe nenhum tipo de ponteiro definido pelo programador, não existe aritimética de ponteiros , e temos ainda um garbage collector automático. Este modelo simples de gerenciamento elimina uma gama incrível de erros de programação que programadores C e C++ tem que lidar no seu dia-a-dia. Podemos desenvolver código Java com a confiança que o sistema/plataforma irá encontrar muitos erros rapidamente e que os maiores problemas não ficarão escondidos até nosso software estar em produção.
Robusta e segura A tecnologia Java foi desenvolvida para operar em ambientes distribuídos, o que significa que a segurança é um item de extrema importância. Com as propriedades de segurança que foram desenvolvidas dentro da linguagem e no sistema de run-time, a tecnologia Java nos permite construir aplicações que não podem ser invadidas por meios externos. Em um ambiente de rede, as aplicações escritas na linguagem Java estão seguras de qualquer intrusão por código não autorizado, ou ainda por vírus ou arquivos inválidos.
Arquitetura neutra e portável A tecnologia Java é também responsável por suportar aplicações que serão instaladas ( deployed ) em ambientes de redes heterogêneos. Nestes ambientes as aplicações precisam ser capazes de executar em uma grande variedade de arquitetura de hardware. Dentro desta variedade de plataformas de hardware, as aplicações devem executar em cima de uma variedade de sistemas operacionais, e inter-operar com Múltiplas interfaces de linguagem de programação. Para acomodar esta diversidade de sistemas operacionais, o compilador Java produz o bytecode , que é um formato arquitetural intermediário e neutro, que é designado a trasportar o código eficientemente para múltiplas plataformas de hardware e software. A natureza interpretada da tecnologia Java resolve ambos problemas de distribuição binária e versionamento; pois o mesmo byte code será executado em qualquer plataforma.
Arquitetura neutra e portável A arquitetura neutra e portável da plataforma da linguagem Java é conhecida como Java Virtual Machine . Ela é uma especificação de uma abstração de uma máquina, na qual os compiladores da linguagem Java podem gerar código. Implementações específicas Da Java Virtual Machine para plataformas específicas de hardware e software criam a concreta materialização da virtual machine. A Java Virtual Machine é baseada primeiramente na especificação de interface POSIX, um padrão da industria para a definição de interfaces de sistemas portáveis. Implementar a Java Virtual Machine em novas arquitetura é uma tarefa relativamente simples, necessitando apenas que a plataforma de destino cumpra com os requisitos básicos, tal como o suporte a multi-threading.
Alta Performance Performance é sempre alvo de grandes considerações. A plataforma Java possui uma performance superior adotando um esquema onde o interpretador pode ser executado em velocidade máxima sem a necessidade do ambiente de checagem de run-time. O garbage collector automático é executado como uma thread de baixa prioridade em background, proporcionando assim uma alta probabilidade que a memória estará disponível quando requisitada, causando assim uma melhor performance.
Alta Performance Aplicações que necessitam de uma grande quantidade de poder de processamento podem ser designadas tal como seções de computação intensivas podem ser re-escritas em código nativo de máquina como necessitar e interfacear com a plataforma Java. Em geral, usuários percebem que aplicações interativas respondem rápido, mesmo que elas sejam interpretadas.
Interpretada, threaded, dinâmica O interpretador Java pode executar bytecodes Java diretamente em qualquer máquina na qual o sistema de interpretador e run-time tenha sido portada. Em uma plataforma interpretada como o Java, a fase de link de um programa é simples, incremental e leve. Os benefícios são grandes, tais como ciclos de desenvolvimentos mais rápidos, prototipação, experimentação, e o desenvolvimento rápido de casos normais, ao contrário do tradicional compilador “peso-pesado” : compilar, linkar e ciclos de testes.
Interpretada, threaded, dinâmica Aplicações modernas baseadas em rede, tal como o HotJava Browser para a web, tipicamente necessida de muitas coisas ao mesmo tempo. Um usuário trabalhando com o HotJava browser pode executar muitas animações concorrentemente enquanto faz o download de uma imagem e usa o scroll de uma página. A capacidade de multi-thread da tecnologia Java provê os meios para a construção de aplicações com atividades concorrentes de threads. Multithreading resulta em um alto nível de interatividade para o usuário final.
Interpretada, threaded, dinâmica A plataforma Java suporta multi-threading no nível de linguagem com a sofisticação adicional de sincronização de primitivos: a biblioteca de linguagem provê a classe Thread , e o sistema de run-time provê o monitor e as condições de lock primitivas. No nível de biblioteca, novamente, as bibliotecas de sistema de alto nível, tem sido escritas para serem thread safe : a funcionalidade provida pelas bibliotecas está disponível sem o conflito para a execução de múltiplas threads.
Interpretada, threaded, dinâmica Enquanto o compilador Java é restrito no seu “compile-time static checking”, o sistema de run-time da linguagem é dinâmico nos seus estágios de linking. As classes são “linkadas” somente quando necessário. Novos módulos de código podem ser linkados sob demanda de uma grande variedade de fontes, mesmo de fontes através da rede. No caso do HotJava Browser e aplicações similares, código executável interativo pode ser carregado de qualquer lugar, o que possibilita o update transparente de aplicações. O resultado é um serviço constante on-line que sempre é incrementado; ele pode permaneter inovador e recente, ser oferecido para mais clientes, e suportar o crescimento do comércio eletrônico na internet.
Multi-plataforma
A plataforma Java, um novo jeito de fazer computação distribuída Se pegarmos individualmente, estas características discutidas acima podem ser encontradas em uma grande variedade de plataformas de software. Mas o que é completamente novo é a maneira que a tecnologia Java e o seu ambiente de runtime tem combinado eles para produzir um poderoso e flexível sistema de programação.
A plataforma Java, um novo jeito de fazer computação distribuída Desenvolver nossas aplicações usando a linguagem de programação Java resulta em um software que é portável através de múltiplas arquiteturas de máquinas, sistemas operacionais, e interfaces de usuário gráficas, seguro e de alta performance. Com a tecnologia Java, nosso trabalho como desenvolvedores é muito fácil, pois o foco de nossa atenção é somente no objetivo final de entregarmos um produto inovador no prazo, baseado nos sólidos fundamentos da plataforma Java. A melhor forma de desenvolver software é aqui, agora, trazido pela plataforma Java.
A plataforma Java http://java.sun.com/javase/technologies/index.jsp
Resumo Simples Orientada a Objetos Distribuída Multithreaded Dinâmica Arquiteturalmente Neutra Portável Alta Performance Robusta Segura
Instalando o ambiente de desenvolvimento do Java
O ambiente de desenvolvimento Para podermos desenvolver programas em Java precisaremos : Java Development Kit ( JDK ), que por sua vez é um conjunto de programas e APIs que nos possibilita escrevermos e compilar programas em Java. Notepad ++ Netbeans, que é uma das mais famosas IDEs para desenvolvimento Java.
Passos para a instalação do ambiente Download the Notepad++ http://notepad-plus.sourceforge.net/uk/site.htm Execute o instalador do Notepad++ Download the JDK : http://java.sun.com/javase/downloads Ou vá para a página principal : www.java.sun.com e navegue no link JavaSE, afim de baixar a versão correta ( 6.x ) Execute o instalador do JDK Crie a variável JAVA_HOME no seu painel de controle, apontando para o diretório de instalação de seu JDK, ex : C:\Arquivos de programas\Java\jdk1.6.0_05 Configure o seu path no painel de controle para conter o caminho do JDK, exemplo : %JAVA_HOME%;%JAVA_HOME\bin%;%JAVA_HOME\lib%;
Passos para a instalação do ambiente
Verificando a instalação Abra uma janela de comandos do windows e digite o seguinte comando : java –version Você deverá ver o seguinte resultado ( ou a versão que você instalou ): java version 1.6.0_05  Java(TM) SE Runtime Environment (build 1.6.0_05-b13) Java HotSpot(TM) Client VM (build 10.0-b19, mixed mode, sharing)
Estrutura de diretório
Estrutura de diretório Bin  Compilador e command line tools Db  Banco de dados Demo  Demos Include  Arquivos para métodos native Jre  Arquivos Java Runtime Env. Lib  Arquivos de bibliotecas Src  Código fonte das bibliotecas
Conceitos Básicos Capítulo 2
Tópicos Compilando um programa em Java • String • Blocos e expressões • Operadores • Controle de execução • Arrays
Compilando um programa Java Crie um diretório chamado c:\projetos\Welcome Abra o Notepad++ e digite o seguinte código : public class Welcome { public static void main(String[] args) { System.out.println(“Bem vindo ao mundo do Java”); } }
Compilando um programa Java Salve este arquivo como Welcome.java dentro do diretório Welcome. Abra uma janela do DOS e vamos compilar e rodar nosso novo programa : javac Welcome.java java Welcome Resultado : Bem vindo ao mundo do Java
Programando em Java Neste momento já possuímos um bom conhecimento sobre a linguagem e a plataforma Java, e já temos o JDK instalado, compilando e executando programas Java. Nesta parte veremos mais a fundo os conceitos básicos de programação, tal como tipos de dados, fluxo e loops.
Programando em Java Um programa Java simples. É o que exibimos no trecho código abaixo, e podemos até dizer que este programa tem o mínimo de código necessário para que o mesmo seja executado como um “executável”, como veremos mais a frente na anatomia de um programa Java : public class PrimeiroExemplo { public static void main(String[] args) { System.out.println("Nosso primeiro exemplo !!!"); } }
Anatomia de um programa Java Traçaremos a antomia deste programa Java para tornarmos mais confortáveis com o framework provido pelo JDK. A primeira propriedade que devemos notar é que o Java é “case sensitive”, ou seja, o Java pode interpretar maiúsculo/minúsculo como erro se digitarmos MAIN ao invés de main, e o programa não poderá ser executado gerando um erro : C:\projetos\Welcome>javac PrimeiroExemplo.java C:\projetos\Welcome>java PrimeiroExemplo Exception in thread "main" java.lang.NoSuchMethodError: main
Anatomia de um programa Java Este erro é ocasionado por que a JVM espera que a classe possua um método chamado main com a seguinte assinatura : public static void main (String[] args) { … } Ao invés de : public static void MAIN (String[] args) { … }
Anatomia de um programa Java public  é um modificador de acesso, que rege como a classe pode ser acessada. Veremos modificadores a fundo mais a frente. class  é um identificador, um marcador que diz que o bloco inteiro é uma classe, e vale a pena apontar mais uma vez que tudo no Java é um tipo de classe específica. Em linhas muito gerais uma classe é um container de código que define o comportamento de uma aplicação, um bloco de código no qual todo programa é montado sobre, e tudo em um programa Java deve estar dentro de uma class.
Anatomia de um programa Java O padrão de nomeclatura de classes Java é o famoso “camel case”, onde usamos uma letra maiúscula sempre que passamos para uma nova parte de identificação da classe, para ficar mais claro podemos dar como exemplo duas classes, uma para registrar pagamentos e outra para listar balanço : public class RegistrarPagamento { .. } Public class ListarBalanco { .. }
Anatomia de um programa Java Outra coisa a ser notada são os colchetes { }, que delimitam o início e fim de um determinado bloco, e se abrimos um novo bloco, o mesmo deve ser fechado em algum momento abaixo. Caso não façamos isso receberemos um erro do compilador. public class NomeDaClasse { public static void main(String[] args) { //invocação de métodos, apis, e etc… } }
Anatomia de um programa Java Não precisamos nos preocupar com a palavra chave “static” neste momento, pois veremos estes e outros conceitos mais de perto nos próximos capítulos. O ponto a ser lembrado é que toda classe Java que desejamos que seja “executável” por meio de linha de comando deve ter a sintaxe acima declarada.
Comentários // /* e */ /** e */
Comentários /** * Este é nosso primeiro JavaDoc * @version 1.00 08/02/2009 * @author Márcio Alves Marinho */ public class SegundoExemplo { /* Este é nosso primeiro comentário longo Blah blah blah blah blah blah blah blah Blah blah blah blah blah blah blah blah Blah blah blah blah blah blah blah blah Blah blah blah blah blah blah blah blah Blah blah blah blah blah blah blah blah */ public static void main(String[] args) { //Um comentário simples de uma linha só System.out.println("Nosso segundo exemplo !"); } }
Tipos de Dados Tipo Bits Bytes Mínimo Máximo byte 8 1 -2 7 2 7 -1 short 16 2 -2 15 2 15 -1 int 32 4 -2 31 2 31 -1 long 64 8 -2 63 2 63 -1 float 32 4 n/a n/a double 64 8 n/a n/a boolean Verdadeiro / falso True / false - - char caractere 1 String caracteres n/a n/a n/a
String • O tipo String é um dos mais utilizados em qualquer linguagem de programação • Em Java, o tipo String é uma classe e não um tipo primitivo • No entanto, assim como os tipos primitivos, as strings podem ser criadas a partir de constantes, que, no caso, são seqüências de caracteres delimitados por aspas duplas. Ex: String s = “abc”; • Embora seja um objeto, não há a necessidade de utilização do operador new , como acontece com TODAS as outras classes.
String • As instâncias de String são objetos imutáveis, ou seja, uma vez criada o seu valor não pode ser nunca alterado. • Isto acontece porque cada instância de String é criada apenas uma vez, e essa lista de strings é mantida pelo sistema. Se uma nova String for criada e o seu valor já estiver nesta lista, não há a necessidade de criar uma nova instância. Ex.: String s1, s2, s3; abc s1 = “abc”; s2 = “abc”; s3 = s1.toUpperCase();
Variáveis Em Java toda variável necessita ter um tipo, isto porque o Java é uma linguagem fortemente tipada, como vimos anteriormente : double salary; int vacationDays; long earthPopulation; boolean done; int i, j; // ambas inteiras Lembrando sempre que devemos inicializar uma variável antes de podermos utilizar a mesma para qualquer função : int vacationDays; // ERRO !--variable not initialized System.out.println(vacationDays);
Variáveis Constantes são variáveis “imutáveis”, pois uma vez atribuido um valor a mesma, nunca mais poderemos mudá-lo : public class Constants { public static void main(String[] args) { final double CM_PER_INCH = 2.54; double paperWidth = 8.5; double paperHeight = 11; System.out.println("Paper size in centimeters: " + paperWidth * CM_PER_INCH + " by " + paperHeight * CM_PER_INCH); } } A palavra chave final é quem torna a variável imutável ( constante ) !
Operadores e atribuição
Operadores e atribuição Operadores são utilizados para operações aritiméticas, ou seja, adição, subtração, multiplicação e divisão. Atribuição é a ação de determinar um valor a uma variável. O trabalho de atribuir um valor a uma variável parece ser algo muito simples, pois a única coisa que devemos fazer é colocar um valor no lado direito do sinal de igual “=” e a variável a esquerda do mesmo. Veremos capítulo de Orientação a Objetos que isto não é tão fácil quanto parece, mas neste momentos nos ateremos aos exemplos mais simples :
Atribuição int x = 4; int a = 12; long l = 44444444444444; double d = 355.44;
Operadores Como exmeplo de operações podemos utilizar : x += 4; Que é o equivalente a : x = x + 4; Geralmente quando queremos fazer este tipo de operação direta utilizamos o símbolo antes do sinal de =, como *= ou %=). Exemplos de Incrementanto e decremento : int myVar = 15 ; myVar++ ; O valor de myVar será de 16, pois adicionamos mais um número ao mesmo. Outros exemplos : int m = 7; int n = 7; int a = 2 * ++m; // vale 16, e “m” vale 8 int b = 2 * n++; // vale 14, e “n” vale 8
Operadores relacionais Operadores relacionais são aqueles que estão ligados a comparações, e o seu resultado é sempre true ou false, a caracterização dos mesmos é pelo sinal “==”, um duplo sinal de igual, como veremos a seguir : 3 == 7 ( false ) 3 != 7 ( true ) Nota : == ( igual ) != ou <> ( diferente ) Temos também os outros operadores, < menor que, > maior que, <= menor ou igual que, e >= maior ou igual que. O Java segue o C++ e usa os operadores && e || que são avaliados em “short circuit” . Ou seja, o segundo argumento não é avaliado se o primeiro já determinar o valor. A sintaxe é muito simples :
Operadores relacionais expressão1 && expressão2 int x = 5; x != 0 && 1 / x > x + y; A segunda parte de nossa expressão nunca será avaliada, pois a primeira parte dela já satisfaz a condição que queremos. E da mesma forma poderemos utilizar o ou lógico ||. expressão1 || expressão2 Outro operador lógico utilizado pelo Java é o operador ternário, que tem sua sintaxe : condição1 ? expressão1 : expressão2
Operadores relacionais Este operador avalia a condição1, e se a mesma for verdadeira (true), então o Java atribui a primeira expressão como resultado, senão a segunda : int x = 7; int y = 9; String resultado = x > y ? “X é Maior que Y” : “Não, X não é maior que Y” System.out.println(resultado); Operadores de bitwise Este tipo de operador funciona com qualquer tipo inteiro, e eles operam diretamente com os bits que compõem os inteiros. Isto significa que podemos utilizar um tipo de máscara para pegar os bits individuais de um número. Estes operadores são : & (“and”) | (“or”) ^ (“xor”) ~ (“not”)
Operadores relacionais Como exemplo temos : int fourthBitFromRight = (n & 8) / 8; Estes operadores tem uma particularidade, pois eles não são executados em short circuit, ou seja, a segunda expressão será avaliada de qualquer forma após a primeira. Por último temos os operadores de shift, que servem para mover os bits para a esquerda ou direita, eles são >> , << e >>> . int n = 6; int quartoBitDaDireita = (n & (1 << 3)) >> 3;
Conversão entre tipos numéricos
Conversão entre tipos numéricos Quando convertemos nossos valores para uma variável de capacidade superior, não temos problema algum com o conteúdo, mas quando tentamos fazer o inverso, devemos utilizar um cast , e lidar com a perda de dados : int n = 123456789; float f = n; System.out.println(&quot;n == &quot; + n); System.out.println(&quot;f == &quot; + f); n == 123456789 f == 1.23456792E8
Conversão entre tipos numéricos O exemplo acima nos mostra um cast automático entre um int e um float, nesse caso, a magnitude do número será mantida, mas haverá uma conversão automática para o tipo float e perderemos alguma precisão. Podemos ver na figura anterior que as setas cheias indicam uma conversão sem perda de precisão, e as setas pontilhadas conversão com possível perda de precisão. Quando dois valores usam um operador binário ( por exemplo n + f onde um operando é um integer e o outro um float), ambos operandos são convertidos para um tipo comum antes da operação ser executada.
Conversão entre tipos numéricos Se um dos operandos é do tipo double , o outro será convertido pra double . Se um dos operandos é do tipo float , o outro será convertido para float . Se um dos operandos é long , então o outro será convertido pra long . Em qualquer outro caso serão convertidos para int
Conversão entre tipos numéricos ( cast ) Nos exemplos anteriores vimos que as conversões são automáticas, pois estamos tentando colocar ó conteúdo de uma variável de menor grandeza em uma outra de maior grandeza. Esta conversão automática não acontece quando tentamos fazer o contrário, colocarmos o conteúdo de uma variável double dentro de um int , por exemplo : double x = 9.997; int nx = (int) x;
Operadores e hierarquia de parênteses Devemos prestar atenção na precedência de operadores no momento em que montamos uma expressão, pois dependendo da disposição das expressões ou dos operadores podemos ter uma precedência diferente da esperada. Se nenhum parêntese for usado, então a precedência natural será. Como exemplo, o operador && tem uma precedência mais altar q || :
Operadores e hierarquia de parênteses a && b || c é o mesmo que : (a && b) || c Por que o sinal += associa da direita para a esquerda, a expressão a += b += c significa a += (b += c) Ou seja, o valor de b += c (que é o valor de b depois da adição) é adicionado a.
Operadores e hierarquia de parênteses Operadores Associatividade [] . () (invocação de método) Esquerda para a direita ! ~ ++ -- + (unary) – (unary) () (cast) new Direita para a esquerda * / % Esquerda para a direita + - Esquerda para a direita << >> >>> Esquerda para a direita < <= > >= instanceof Esquerda para a direita == != Esquerda para a direita & Esquerda para a direita ^ Esquerda para a direita | Esquerda para a direita && Esquerda para a direita || Esquerda para a direita ?: Esquerda para a direita = += -= *= /= %= &= |= ^= <<= >>= >>>= Esquerda para a direita
Enumerations As vezes precisamos representar um conjunto distintos de valores para um cálculo, ou precisamos de um conjunto restritivo. O Java, apartir de sua versão 5 disponibiliza um valioso recurso chamado enumeration ( enum ), que é um tipo especial para armazenar um conjunto de dados : enum Size { SMALL, MEDIUM, LARGE, EXTRA_LARGE }; Size s = Size.MEDIUM;
Strings O tipo String é um dos mais utilizados em qualquer linguagem de programação. Em Java, o tipo String é uma classe e não um tipo primitivo, no entanto, assim como os tipos primitivos, as strings podem ser criadas a partir de constantes, que, no caso, são seqüências de caracteres delimitados por aspas duplas : String s = “abc”;
Strings Embora seja um objeto, não há a necessidade de utilização do operador new, como acontece com TODAS as outras classes. As instâncias de String são objetos imutáveis, ou seja, uma vez criada o seu valor não pode ser nunca alterado. Isto acontece porque cada instância de String é criada apenas uma vez, e essa lista de strings é mantida pelo sistema. Se uma nova String for criada e o seu valor já estiver nesta lista, não há a necessidade de criar uma nova instância.
Strings String s1, s2, s3; s1 = “abc”; s2 = “abc”; s3 = s1.toUpperCase();
String ( alguns métodos ) Assinatura Descrição Exemplo char charAt(int posicao) Retorna o caractere em determinada posição na String String s = “Teste”; char c = s.charAt( //Resultado: ‘s’ int indexOf(String s2) Retorna a posição da primeira ocorrência de s2 na String String s = “Teste”; int pos = s.indexOf(“st”); //Resultado: 2 int lastIndexOf(String s2) Retorna a posição da última ocorrência de s2 na String String s = “Teste”; int pos = s.lastIndexOf(“e”); //Resultado: 4 int length() Retorna o tamanho da String String s = “Teste”; int tam = s.length(); //Resultado: 5 String substring(int pos1, int pos2) Retorna um pedaço da string que vai de pos1 a pos2 String s = “Teste”; String s2 = s.substring(1, 4); //Resultado: “est” String toUpperCase() Converte para maiúsculo String s = “Teste”; String s2 = s.toUpperCase(); //Resultado: ‘TESTE’ String toLowerCase() Converte para minúsculo String s = “Teste”; String s2 = s.toLowerCase(); //Resultado: ‘teste’ String trim() Limpa os espaços no início e no final da String String s = “ Teste da a ”; String s2 = s.trim(); //Resultado: “Teste da a”
String ( comparações ) Comparação de Strings : Ao comparar duas Strings utilize sempre o método equals() em vez do operador ==. Isto é necessário porque o == compara as referências, enquanto que o equals() compara os valores das referências: String s1 = “a”; String s2 = “a”; if(s1 == s2){ //De vez em quando funciona  } if(s1.equals(s2)){ //Forma correta de comparar } “ Hello&quot;.equalsIgnoreCase(&quot;hello&quot;)
String ( Métodos ) String greeting = &quot;Hello&quot;; int n = greeting.length(); // 5. char first = greeting.charAt(0); // first is 'H' char last = greeting.charAt(4); // last is 'o' int cp = greeting.codePointAt(index); Documentação completa on-line de String : http://java.sun.com/javase/6/docs/api/java/lang/String.html
Orientação a Objetos Capítulo 3
Orientação a Objetos O paradigma de orientação a objetos ( OO ) é o assunto mais quente na forma de desenvolver software na “ atualidade”. O mesmo substituiu a famosa forma estruturada de desenvolver sistemas, e podemos citar por exemplo a linguagem C ou Clipper. Java é uma linguagem totalmente orientada a objetos. Um programa orientado a objetos é composto de objetos, e cada objeto tem uma funcionalidade específica que é exposta para os usuários, ou ainda temos outras funcionalidades que são comportamentos escondidos.
Classes Uma classe é um template, ou ainda podemos considerá-la uma forma na qual objetos são “cozinhados como bolos”, ou seja todo objeto terá o formato da forma que foi produzido. Como vimos anteriormente, qualquer código escrito em Java é SEMPRE escrito dentro de uma classe. As bibliotecas standard do Java possuem uma gama imensa de classes para tratamento de interface de usuário, datas, programação em rede e muito mais.
Classes
Classes Quando construímos um objeto apartir de uma classe, podemos dizer que o objeto é uma instância daquela classe. import java.util.Date; public class Contrato { Date dataRegistro; Date dataVencimento; double valor; void adicionarVencimento (int valor) { //implementação aqui } double calcularValor (int valor) { //implementação aqui return 0; } }
Classes ( propriedades ) Uma classe possui algumas propriedades básicas, que podemos classificar da seguinte forma : Encapsulamento – Também conhecido como “esconder a informação”, que nada mais é do que a combinação de estado e comportamento, empacotados para esconder os detalhes da implementação do usuário. Campos de instância – Os dados vivem aqui dentro, que são variáveis declaradas dentro das classes. Métodos – São os procedimentos de acesso aos dados contidos nos campos de instância. Para termos estas propriedades acima NUNCA devemos invocar um campo de instância diretamente, pelo contrário, devemos sempre invocar um método que realizará o trabalho necessário para retornar-nos o valor desejado.
Objetos Objetos são a materialização de uma classe, ou seja, como dissemos anteriormente uma classe é como uma forma para criarmos novos objetos que farão parte do nosso programa. Objetos possuem as seguintes propriedades: Comportamento – Simplesmente aquilo que o objeto é capaz de realizar. O estado do objeto – Como o objeto reage quando invocamos seus métodos. Identidade do objeto – Como o objeto de distingue dos outros objetos que podem ter o mesmo estado e comportamento;
Identificando classes Em programas procedurais nós iniciamos o programa com algum processo no topo, tipo “principal”, mas em programação OO não existe este tipo de mecanismo. Na programação OO precisamos criar classes e colocar métodos neles. Uma regra para encontrarmos classes é olhando para os substantivos de nossos use-cases ou estórias, e os métodos serão os verbos.
Identificando classes Ordem de Pagamento Item Endereço de envio Pagamento Pedido Conta
Relacionamento entre classes Dependência ( Usa uma classe ) Agregação ( Tem uma classe ) Herança ( É um tipo de classe )
Relacionamento entre classes (125)
Criando e usando objetos Para podermos usar objetos precisamos antes criá-los apartir de sua classe : java.util.Date data = new java.util.Date(); System.out.println(data); String s1 = new java.util.Date().toString(); System.out.println(s1);
Atributos Atributos são as variáveis de instância que existem dentro das classes para compor seu estado, ou seja, são as propriedades que uma classe carrega para compor sua informação.
Atributos public class Pessoa { long id; String nome; Date dataNascimento; }
Métodos Como vimos rapidamente anteriormente, os métodos são funções utilizadas para acessar um determinado comportamento da classe.
Métodos import java.util.Date; /** * @author Marcio */ public class Pessoa { private long id; private String nome; private Date dataNascimento; public long getId() { return id; } public void setId(long id) { this.id = id; }
Métodos public String getNome() { return nome; } public void setNome(String nome) { this.nome = nome; } public Date getDataNascimento() { return dataNascimento; } public void setDataNascimento(Date dataNascimento) { this.dataNascimento = dataNascimento; } }
Construtores Construtores são métodos especiais para criar novas instâncias de objetos. Estes métodos possuem o mesmo nome da classe, mas sem retorno. Podemos ainda usar os construtores para atribuirmos um estado inicial ao objeto, se assim o desejarmos.
Construtores import java.util.Date; public class Pessoa { private long id; private String nome; private Date dataNascimento; //Construtor defaul para a classe pessoa public Pessoa() { } public Pessoa(long id, String nome, Date dataNascimento) { this.id = id; this.nome = nome; this.dataNascimento = dataNascimento; }
Exemplo public class Pessoa { ... public static void main(String[] args) { Pessoa p1 = new Pessoa(); Pessoa p2 = new Pessoa(1, &quot;Marcio Alves Marinho&quot;, new Date(&quot;14/12/1974&quot;)); System.out.println(p1.getId() + &quot; # &quot; + p1.getNome() + &quot; # &quot; + p1.getDataNascimento()); System.out.println(p2.getId() + &quot; # &quot; + p2.getNome() + &quot; # &quot; + p2.getDataNascimento()); } ... } Resultado : 0 # null # null 1 # Marcio Alves Marinho # Sat Dec 14 00:00:00 BRT 1974
Pacotes Pacotes são uma forma de organizar nosso código, no fundo pacotes nada mais são do que uma estrutura hierárquica de diretórios que o Java usa para uma melhor organização do código fonte. Podemos então organizar nosso código por camadas, nível de importância, ou qualquer outra forma que for mais conveniente.
Pacotes
Pacotes
Pacotes Um pacote genérico seguindo a convenção proposta pela Sun, diz que o prefixo de um pacote único deve estar escrito em minúsculo, primeiramente com o domínio, e os componentes subsequentes sendo o nome da organização, divisão, departamento, projeto, máquina ou login name. com.sun.eng com.apple.quicktime.v2 edu.cmu.cs.bovik.cheese br.com.jetsoftware.domain
Especificando e Importando Pacotes O Comando Package serve para especificar a que pacote uma determinada classe ou inteface pertence. O Comando Import torna disponível para o pacote corrente, as classes dos pacotes mencionados após a cláusula import.
Resolvendo Nomes e Colisões Quando dois ou mais pacotes são importados para um determinado programa e eles contém classes de mesmo nome isso não representa problemas de colisão caso tais classes não sejam utilizadas No caso de ser necessário fazer referência a uma das classes citadas acima, isso deve ser feito de modo explícito para que não haja problemas de colisão. Caso isso não seja feito, o compilador enviará uma mensagem de erro, forçando que essa chamada seja explicitada. java.util.Date / java.sql.Date
Modificadores de acesso class MinhaClass { } Apesar desta declaração funcionar a mesma não é ideal, pois como já vimos necessitamos organizar o nosso código na forma de pacotes, e precisamos ainda organizar o acesso as nossas classes. Ou seja, precisamos definir quem poderá acessar nossas classes e de onde.
Modificadores De acesso : public, protected, private. Não relacionados a acesso : strictfp, final, e abstract.
Modificadores ( em miúdos ) Podemos exemplificar o acesso a classes, se dissermos por exemplo que a class A tem acesso a outra classe B, e isto significa que a classe A pode fazer uma destas 3 coisas : Criar uma instância de B Extender B ( ser uma sub-classe de B ) Acessar certos métodos e variáveis dentro de B, dependendo do controle de acesso destes métodos e variáveis.
Modificadores ( em miúdos ) Na verdade acesso significa visibilidade . Se a classe A não puder ver a classe B, então o nível de acesso dos métodos e variáveis dentro de B não farão a menor diferença, pois a classe A não terá como acessá-los de forma alguma.
Acesso Default Default – Uma classe com acesso default não possui declaração de modificadores, e este é o acesso default quando não especificamos nenhum. Default também é conhecido como “package level access”, por que as classes com acesso default podem somente ser vista por classes dentro do mesmo pacote.
Default, exemplo Arquivo Beverage.java package cert; class Beverage { } Arquivo Tea.java package exam.stuff; import cert.Beverage; class Tea extends Beverage { } Se tentarmos compilar.... Can't access class cert.Beverage. Class or interface must be public, in same package, or an accessible member class. import cert.Beverage;
Acesso Public Public – Uma classe que é declarada com a palavra chave public dá acesso a todas classes de todos pacotes. Todas as classes do universo ( e outros universos paralelos) tem acesso a classes “public” .
Public, exemplo package cert; public class Beverage { } Agora a compilação funcionará !
Private Como o próprio nome já diz, “private” é um modificador para acesso privado, ou seja, ninguém externamente a classe tem acesso ao que for definido com esse modificador de acesso.
Final Classes definidas como “final” não podem ter sub-classes ! Em outras palavras, outras classes não poderão extendê-las, e qualquer tentativa receberemos um erro do compilador.
Final, exemplo package cert; public final class Beverage { public void importantMethod() { } } package exam.stuff; import cert.Beverage; class Tea extends Beverage { } Se tentarmos compilar de novo... Can't subclass final classes: class cert.Beverage class Tea extends Beverage{ 1 error
Abstract Classes declaradas como “abstract” não podem ser instanciadas, isto acontece porque as mesmas são usadas como template para outras classes, ou seja, elas já nasceram para conterem algum comportamento base e serem extendidas e terem um refinamento nas sub-classes.
Abstract, exemplo abstract class Carro { private double price; private String model; private String year; public abstract void goFast(); public abstract void goUpHill(); public abstract void impressNeighbors(); // Mais métodos... } Se tentarmos a façanha de instanciá-la : Carro.java:7: class Car is an abstract class. It can't be instantiated. Car x = new Car(); 1 error
Abstract, exemplo package comida; public abstract class Fruta{ /* any code you want */ } import comida; class Banana extends Fruta{ /* any code you want */ }
Herança Extends é a palavra chave usada para podermos definir herança entre classes no Java. Toda sub-classe possui todas as características da classe base que foi herdada, isso funciona quase que exatamente como se fosse herança genética, onde o herdeiro tem as características de seus pais, cor dos olhos, cabelo, feições e etc. Simplesmente usamos a seguinte notação : ClasseB extends ClasseA
Herança Com herança podemos modificar todo o comportamento de uma cadeia de classes alterando somente o código da classe base. Por isso devemos ter muito cuidado quando fazemos o design da mesma, pois um bug ou código mal-feito se extenderá por toda hierarquia de classes.
Interfaces Interfaces são uma alternativa a herança, qualquer classe pode “assinar” ou seja, implementar os métodos definidos na mesma, sem dizer como o farão. Elas são também vistas como contratos, pois qualquer classe que implementar a interface serão obrigadas a implementar seus métodos. Interfaces podem ser implementadas por qualquer classe, de qualquer lugar ou hierarquia. Isso nos permite mudar radicalmente a característica de diferentes classes dando a elas a mesma característica.
Interfaces
Interfaces, exemplo package br.com.jetsoftware.domain; import java.util.Date; public interface IContratoFinanceiro { double calcularBalanco(); double calcularHistorico(Date dataInicial, Date dataFinal); double saldoMensal(int mes, int ano); }
Interfaces, exemplo package br.com.jetsoftware.domain; import java.util.Date; public class Caixa implements IContratoFinanceiro { public double calcularBalanco() { //seus cálculos return 2; } public double calcularHistorico(Date dataInicial, Date dataFinal) { //seus cálculos if (dataFinal.getTime() < dataInicial.getTime()) { return 0; } return 55.3; } public double saldoMensal(int mes, int ano) { //seus cálculos return 30; } }
Interfaces, exemplo package br.com.jetsoftware.domain; import java.util.Date; public class ContratoFinanceiro implements IContratoFinanceiro { public double calcularBalanco() { //seus cálculos return 7; } public double calcularHistorico(Date dataInicial, Date dataFinal) { //seus cálculos return 99.4; } public double saldoMensal(int mes, int ano) { //seus cálculos return 652.478; } }
Interfaces Interfaces podem ser vistas como classes 100% abstratas. Nós dissemos 100% abstratas pois enquanto classes abstratas podem definir métodos abstratos e não abstratos, interfaces por sua vez podem somente definir métodos abstratos. Outra diferença que existe entre interfaces e classes é que interfaces possuem pouca flexibilidade no tocante como métodos e variáveis são definidas. Todos os métodos de uma interface são implicitamente públicos e abstratos. Ou seja, não precisamos digitar as palavras chave public ou abstract, pois os mesmos serão sempre public e abstract. Todas as variáveis dentro de uma interface devem ser public, static, e final, ou seja, interfaces podem declarar somente constantes.
Interfaces Métodos de Interfaces não podem ser static. Já que métodos de interfaces são abstratos, eles não podem ser marcados como final, strictfp, or native. Uma interface pode estender ( extends ) uma ou mais interfaces. Uma interface não pode extender nada além de outra interface. Uma interface não pode implementar outra interface ou classe. Uma interface deve ser declarada com a palavra chave interface. Interfaces podem ser usadas de forma polimórfica.
Modificadores de acesso para atributos Métodos e variáveis permitem o acesso quase que da mesma forma. Existem 4 tipos de acesso distintos para atributos : Public Protected Default private
Modificadores de acesso para atributos Como vimos anteriormente, a proteção default é aquela que recebemos quando não digitamos nenhum modificador de acesso na declaração do membro. Os modificadores default e protected tem quase que o mesmo comportamento, exceto por uma diferença que será mencionada mais a frente.
Modificadores de acesso para atributos Public – Todos podem acessar. Private – Só pode ser acessado pela mesma classe na qual o o atributo foi definido. Default – Pode ser acessado se a classe que está tentando acessá-lo estiver dentro do mesmo pacote. Protected – Pode ser acessado por qualquer sub-classe mesmo que estejam em pacotes diferentes. Variáveis locais a um método não podem ter modificadores de acesso. Isto por que elas já são privadas ao método.
Encapsulamento Muito importante: conseguimos usar objetos da classe ContaCorrente sem saber nada sobre como a mesma foi implementada! • Isso se chama Ocultação de Informação e é muito importante na programação • É a forma básica de lidar com a complexidade dos programas • É comum usarmos &quot;private&quot; como especificador de controle de acesso para atributos de uma classe
Encapsulamento Também podemos dizer que a classe ContaCorrente encapsula dados e comportamento em cima desses dados Os dados são os atributos escondidos de nós (saldo, histórico de transações, dados do titular, etc) • O comportamento são os métodos que podemos chamar para manipular o objeto • Só podemos “alterar&quot; o estado do objeto através de seus métodos.
Overloading e Redefinição Em Java, o que identifica unicamente o método é o nome e a lista de argumentos. A técnica de definir métodos de mesmo nome, com listas de argumentos diferentes é chamado de &quot; method overloading &quot; . Quando uma classe B define um método usando o mesmo nome, tipo de retorno e argumentos de um método de uma classe ancestral A , este método redefine o método da classe ancestral.
Overloading e Redefinição public class ContaCorrente { public ContaCorrente ( String t, String c, int n ) { numero = n; titular = new Titular ( t, c ); } public ContaCorrente ( Titular t, int n ) { numero = n; titular = t; } }
Overloading e Redefinição public class Animal { public void comer() { System.out.println(&quot;Eu como de forma genérica !&quot;); } public void respirar() { System.out.println(&quot;Eu respiro de forma genérica !&quot;); } }
Overloading e Redefinição class Cao extends Animal { public void comer() { System.out.println(&quot;Eu com o foçinho enfiado na minha tijela.&quot;); } public void respirar() { System.out.println(&quot;Eu respiro pelos pulmoões e somente fora da agua.&quot;); } }
Overloading e Redefinição class SerHumano extends Animal { public void comer() { System.out.println(&quot;Eu como sentado na mesa, com garfo e faca.&quot;); } public void respirar() { System.out.println(&quot;Eu respiro pelos pulmoões e somente fora da agua.&quot;); } }
Visibilidade de membros Visibilidade Public Protected Default Private De dentro da mesma classe Sim Sim Sim Sim De qualquer classe dentro do mesmo pacote Sim Sim Sim Não De uma sub-classe dentro do mesmo pacote Sim Sim Sim Não De uma sub-classe fora do pacote Sim Sim, por herança Não Não De qualquer não sub-classe fora do pacote Sim Não Não Não
Polimorfismo A palavra &quot;polimorfismo&quot; significa &quot;Que apresenta várias formas“ Numa linguagem de programação, o polimorfismo permite tratar objetos de classes diferentes do mesmo jeito (com as mesmas chamadas a métodos), porque elas têm o mesmo comportamento As classes fazem a mesma operação (método), mas de forma diferente – &quot;O quê&quot; é igual – &quot;Como&quot; é diferente
Polimorfismo public static void main(String[] args) { Animal a1 = new Cao(); Animal b1 = new SerHumano(); a1.comer(); b1.comer(); } Eu com o foçinho enfiado na minha tijela. Eu como sentado na mesa, com garfo e faca.
Exercícios
Exercícios Pense sobre a implementação do seguinte programa, dado em pseudo-código Programa de Controle Bancácio 1. Abra uma conta de número 1 para João com CPF 309140605-06. Nesta conta, deposite R$1000,00. 2. Abra uma conta de número 2 para Ana com CPF 123456789-01. Transfira R$400,00 da conta de João para a conta de Ana. 3. Imprima o saldo da conta de João. 4. Imprima o saldo da conta de Ana.
Exercícios Quais as classes que podemos identificar neste exemplo? • Quais são os atributos relevantes para o nosso modelo do mundo real? • Quais os comportamentos devem ser modelados?
Controle de Fluxo Capítulo 4
Controle de fluxo É impossível imaginarmos algum programa que não precise executar nenhum código condicional ! O controle de fluxo é uma parte chave que qualquer linguagem tem, e o Java por sua vez oferece várias formas de fazer isto. Temos declarações de “if”, “for” para loops e muito mais.
Controle de fluxo As declarações “if” e “switch” são tipo de controles condição/decisão que permite aos nossos programas se comportarem diferentemente em uma “encruzilhada”, dependendo do resultado do teste lógico. O Java também provê três declarações de loop diferentes ( for, while e do ). As exceptions por sua vez não dão uma forma elegante de tratarmos error de run-time. Temos ainda as assertions, que foram adicionados na versão 1.4 do Java, que nos permite debugarmos e checarmos condições durante nossos testes.
IF A forma mais básica de “IF” é : if (booleanExpression) { System.out.println(&quot;Inside if statement&quot;); } Onde a expressão deve ser verdadeira para que o código entre as chaves seja executado.
IF / ELSE if (x > 3) { System.out.println(&quot;x é maior que 3&quot;); } else { System.out.println(&quot;x não é maior que 3&quot;); }
Else opcional if (x > 3) { y = 2; } z += 8; a = y + x;
Prática ruim if (x > 3) // horroroso ! y = 2; z += 8; a = y + x;
Aninhando IF/ELSE if (price < 300) { buyProduct(); } else { if (price < 400) { getApproval(); } else { dontBuyProduct(); } }
Aninhando IF/ELSE (re-escrevendo) if (price < 300) { buyProduct(); } else if (price < 400) { getApproval(); } else { dontBuyProduct(); }
Switch Um switch é uma forma de simular vários “ ifs” de uma só vez.
Switch, exemplo int x = 3; if(x == 1) { System.out.println(&quot;x equals 1&quot;); } else if(x == 2) { System.out.println(&quot;x equals 2&quot;); } else if(x == 3) { System.out.println(&quot;x equals 3&quot;); } else { System.out.println(&quot;No idea what x is&quot;); }
Switch, exemplo ( código mais limpo ) int x = 3; switch (x) { case 1: System.out.println(&quot;x is equal to 1&quot;); break; case 2: System.out.println(&quot;x is equal to 2&quot;); break; case 3: System.out.println(&quot;x is equal to 3&quot;); break; default: System.out.println(&quot;Still no idea what x is&quot;); }
Expressão legal para switch/case switch (expression) { case constant1: code block case constant2: code block default: code block }
Switch/case, break O break não é obrigatório na construção do switch, mas se não utilizarmos um break e a expressão for avaliada pare true, então o Java continuará avaliando as próximas expressões.
Switch/case, break int a = 10; switch (a) { case 10: System.out.println(&quot;O valor é 10 !&quot;); case 20: System.out.println(&quot;O valor é 20 !&quot;); case 30: System.out.println(&quot;O valor é 30 !&quot;); default: System.out.println(&quot;Nada feito, é default mesmo !&quot;); } Resultado trágico : ============== O valor é 10 ! O valor é 20 ! O valor é 30 ! Nada feito, é default mesmo !
Loops ( while ) O loop “while” é utilizado em cenários onde não sabemos quantas vezes a declaração será repetida, então continuaremos no loop enquanto a condição for verdadeira. A expressão utilizada deverá sempre ser uma expressão lógica, ou seja, retornar true ou false ! while (expression) { // do stuff } Ou ainda : int x = 2; while(x == 2) { System.out.println(x); ++x; }
Loops (do ... While) do { System.out.println(&quot;Inside loop&quot;); } A declaração do/while é diferente de while, pois a expressão interna será executada antes da checagem.
Loops ( for ) A declaração for nos permite declarar uma ou mais variáveis do mesmo tipo dentro dos parênteses depois da declaração for, se declarmos mais de uma variável, então necessitamos delimitá-las por vírgula : for (int x = 10, y = 3; y > 3; y++) { }
I/O Capítulo 5
Introdução I/O fornece comunicação com dispositivos – arquivos, console, networks, etc. • Onde o acesso aos dados podem ser: – sequenciais, aleatório, binário, caracteres, linhas, blocos, objetos, etc.
I/O Streams I/O Stream representam uma fonte de input e um destino de output. Uma stream pode representar diferentes tipos de fontes e destinos, incluindo arquivos no disco, dispositivos, outros programas e arrays em memória. Streams suportam diferentes tipos de dados, incluindo simples bytes, tipos primitivos, caracteres de localização, e objetos. Algumas streams simplesmente passam dados, outras manipulam e transformam os dados de maneira útil. Não importa muito como elas trabalham internamente, todas streams representam o mesmo modelo simples de programação : Uma stream é uma sequencia de dados. Um programa usa uma input stream para ler dados de uma origem para um destino, um item por vez.
I/O Streams Lendo a informação para um programa :
I/O Streams Um programa usa uma output stream para escrever dados para um destino, um item por vez :
I/O Streams I/O Streams
Byte Streams Os programas usam byte streams para input e output de bytes de 8 bits. Todas as classes de byte stream são descendentes de InputStream e OutputStream. Existem muitas classes byte stream, e para demonstrarmos como os byte stream trabalham, iremos utilizar o file I/O byte stream ; FileInputStream e FileOutputStream.
Usando um Byte Stream import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class CopyBytes { public static void main(String[] args) throws IOException { FileInputStream in = null; FileOutputStream out = null; try { in = new FileInputStream(&quot;xanadu.txt&quot;); out = new FileOutputStream(&quot;outagain.txt&quot;); int c; while ((c = in.read()) != -1) { out.write(c); } } finally { if (in != null) { in.close(); } if (out != null) { out.close(); } } } }
Usando um Byte Stream O programa CopyBytes lê o conteúdo do arquivo dentro de um loop e escreve para uma output stream, um byte de cada vez.
Usando um Byte Stream O método read retorna um int, isto para nos permitir usar o resultado -1 para sabermos que o programa atingiu o fim da stream. Sempre fecha streams, isto é muito importante, pois esta prática garante que não ficaremos com recursos presos sem necessidade. Byte Streams devem ser utilizadas somente para I/O primitivos.
Character Streams O Java armazena caracteres usando convenção Unicode. Stream de caracteres para I/O são traduzidas automáticamente de e para o conjunto de caracteres locais. Para a maioria das aplicações, I/O com caracteres é tão simples quanto com byte streams. O input e output é feito com classes de stream que fazem a tradução automética de conjuntos de caracteres. Um programa que usa streams de caracteres no lugar de byte streams adapta-se automáticamente aos caracteres locais e está pronta para internacionalização ( sem trabalho extra).
Usando Character Streams Todas as streams de caractere são descendentes de Reader e Writer. Como acontece com byte streams, existem character streams que são especializadas para fazer I/O: FileReader e FileWriter
Usando Character Streams import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; public class CopyCharacters { public static void main(String[] args) throws IOException { FileReader inputStream = null; FileWriter outputStream = null; try { inputStream = new FileReader(&quot;xanadu.txt&quot;); outputStream = new FileWriter(&quot;characteroutput.txt&quot;); int c; while ((c = inputStream.read()) != -1) { outputStream.write(c); } } finally { if (inputStream != null) { inputStream.close(); } if (outputStream != null) { outputStream.close(); } } } }
Usando Character Streams O programa CopyCharacters é muito semelhante ao CopyBytes. A grande diferença é que CopyCharacters usa FileReader e FileWriter para input e output no lugar de FileInputStream e FileOutputStream.
Streams de caracteres que usam Byte Streams Existe também a possibilidade de fazer “wrapper” para byte streams. A stream de caracteres usa uma byte stream para executar o I/O físico, enquanto a stream de caracteres trata a tradução entre os caracteres e bytes. FileReader por exemplo usa FileInputStream, enquanto FileWriter usa FileOutputStream.
I/O em linhas inteiras I/O com caracteres geralmente é utilizado com conjuntos maiores do que com caracteres únicos. Um conjunto comum é uma linha, que são vários caracteres agrupados com um terminador de linha no final. Um terminador de linha pode ser um carriage-return/line-feed (“\r\n”), carriage-return (“\r”), ou line-feed (“\n”).
I/O em linhas inteiras Modificaremos o nosso exemplo anterior, afim de torná-lo um I/O orientado a linhas. Usaremos também duas novas classes, BufferedReader e PrintWriter.
I/O em linhas inteiras import java.io.FileReader; import java.io.FileWriter; import java.io.BufferedReader; import java.io.PrintWriter; import java.io.IOException; public class CopyLines { public static void main(String[] args) throws IOException { BufferedReader inputStream = null; PrintWriter outputStream = null; try { inputStream = new BufferedReader(new FileReader(&quot;xanadu.txt&quot;)); outputStream = new PrintWriter(new FileWriter(&quot;characteroutput.txt&quot;));
I/O em linhas inteiras String l; while ((l = inputStream.readLine()) != null) { outputStream.println(l); } } finally { if (inputStream != null) { inputStream.close(); } if (outputStream != null) { outputStream.close(); } } } }
Buffered Streams Os exemplos anteriores que utilizamos são do tipo unbuffered . Ou seja, toda leitura ou escrita é tratada diretamente pelo sistema operacional. Isto pode fazer com que o programa seja menos eficiente, visto que toda requisição desencadeia acesso ao disco, rede ou qualquer outra ação que seja cara. Para reduzir este tipo de sobrecarga, a plataforma Java implementa o I/O buferizado. Input streams buferizadas lêem dados da memória de uma área conhecida como buffer, a API nativa é invocada somente quando o buffer estiver vazio. Da mesma forma output streams escrevem dados para um buffer, e a API nativa é invocada somente quando o buffer estiver cheio. Um programa pode converter uma stream não buferizada para uma buferizada, usando wrappers, onde passamos o objeto não buferizado para o construtor de uma stream buferizada.
Buffered Streams inputStream = new BufferedReader(new FileReader(&quot;xanadu.txt&quot;)); outputStream = new BufferedWriter(new FileWriter(&quot;characteroutput.txt&quot;)); Existem quatro classes de stream buferizadas para fazermos wrapper de streams não buferizadas : BufferedInputStreame BufferedOutputStream criam bute streams buferizados, enquanto BufferedReader e BufferedWriter criam streams de caracteres buferizados.
Descarregando Buffered Streams Devemos escrever os dados do buffer de tempos em tempos, pois é exatamente isso que queremos, escrever os dados em um meio persistente. Esta ação é conhecida como flushing o buffer. Algumas classes de output buferizadas suportam autoflush , especificado por um argumento opicional no construtor. Quando o autoflush está ligado, alguns eventos causam o descarregamento do buffer ( flush ). Por exemplo, um autoflush em objetos PrintWriter acontecem em cada invocação do método println ou format. Para realizar o flush na stream de forma manual, basta usar o método flush.
Escaneando e formatando Input e Output as vezes envolvem a tradução de formatos estranhos para formato humano. Para a realização desta tarefa o Java provê duas APIs. A API scanner quebra o input em tokens individuais associados com bits de dados. A API formatting monta os dados em formato bem formatado pronto para a leitura humana.
Escaneando e formatando import java.io.*; import java.util.Scanner; public class ScanXan { public static void main(String[] args) throws IOException { Scanner s = null; try { s = new Scanner(new BufferedReader(new FileReader(&quot;xanadu.txt&quot;))); while (s.hasNext()) { System.out.println(s.next()); } } finally { if (s != null) { s.close(); } } } }
Escaneando e formatando In Xanadu did Kubla Khan A stately pleasure-dome ... Usando um token diferente : s.useDelimiter(&quot;,\\s*&quot;)
Traduzindo tokens individuais A classe Scanner suporta todos os tipos primitivos do Java ( com exceção de char ), e também BigInteger e BigDecimal. Também valores numéricos podem usar diferentes separadores.
Traduzindo tokens individuais import java.io.FileReader; import java.io.BufferedReader; import java.io.IOException; import java.util.Scanner; import java.util.Locale; public class ScanSum { public static void main(String[] args) throws IOException { Scanner s = null; double sum = 0; try { s = new Scanner(new BufferedReader(new FileReader(&quot;usnumbers.txt&quot;))); s.useLocale(Locale.US); while (s.hasNext()) { if (s.hasNextDouble()) { sum += s.nextDouble(); } else { s.next(); } } } finally { s.close(); } System.out.println(sum); } }
Traduzindo tokens individuais Arquivo de exemplo : numeros.txt 8.2 32,767 3.34159 1,100,000.1
Formatação Stream de objetos que implementam formatação são instâncias de PrintWriter (stream de caracter), ou PrintStream (classe de byte stream). Como todos os objetos de streams de bytes ou caracteres, instâncias de PrintStream e PrintWriter implementam o conjunto padrão de métodos write para output de byte ou caractere. Em adição, ambos PrintStream e PrintWriter implementam o mesmo conjunto de métodos para conversão de dados interno para output formatado. Onde dois níveis de formatação são providos : print e println formatam valores individuais numa maneira padrão format formata quase qualquer valor numérico baseado em uma string de formatação, com várias opções de formatação.
Formatação public class Root { public static void main(String[] args) { int i = 2; double r = Math.sqrt(i); System.out.print(“A raiz quadrada de &quot;); System.out.print(i); System.out.print(&quot; é &quot;); System.out.print(r); System.out.println(&quot;.&quot;); i = 5; r = Math.sqrt(i); System.out.println(&quot; A raiz quadrada de &quot; + i + &quot; é &quot; + r + &quot;.&quot;); } } A raiz quadrada de 2 é 1.4142135623730951. A raiz quadrada de 5 é 2.23606797749979.
O método format O método format formata múltiplos argumentos baseado em uma string. public class Root2 { public static void main(String[] args) { int i = 2; double r = Math.sqrt(i); System.out.format(&quot;The square root of %d is %f.%n&quot;, i, r); } }
O método format Como usado no exemplo anterior, todos os formatadores começam com um % e terminam em 1 ou 2 caracteres que especificam o tipo de output formatado. d – formata um valor inteiro como decimal f - formata um valor ponto flutuante como decimal n – gera um delimidador que é específico de uma plataforma x – formata um inteiro como hexadecimal s - formata qualquer valor como string tB – formata um inteiro como um “local-specific” nome de mês
O método format public class Format { public static void main(String[] args) { System.out.format(&quot;%f, %1$+020.10f %n&quot;, Math.PI); } } Resultado : 3.141593, +00000003.1415926536
O método format Os elementos adicionais de nossa formatação são todos opcionais. Precision – para pontos flutuantes é a precisão do valor formatado ( pode ser truncado). Width – o tamanho mínimo do valor formatado. Flags – especifica formatação adicional, neste caso o número será sempre exibido com o sinal + Argument Index – nos permite explicitamente encontrarmos um determinado argumento.
I/O na linha de comandos Um programa também pode rodar na linha de comandos para interagir com o usuário final. O Java suporta este tipo de interação atráves do Console. Uma alternativa avançada as streams é o Console. Este objeto possui a maior parte das features disponíveis pelas stream standard, e outros objetos. O console é bem interessante para entradas de senha. Este objeto também provê input e output de streams que são streams de caracteres, através de seus métodos reader e writer. Antes de um programa poder usar o Console, o mesmo deve tentar recuperar este objeto invocando o método System.console(). Se o objeto Console estiver disponível este método o retornará, senão o mesmo retornará NULL, e então a interação com o console não será possível, pois o sistema operacional não suporta esta operação, ou o programa foi executado em um ambiente não interativo.
import java.io.Console; import java.util.Arrays; import java.io.IOException; public class PegaSenha { public static void main (String args[]) throws IOException { Console c = System.console(); if (c == null) { System.err.println(&quot;Sem console.&quot;); System.exit(1); } String login = c.readLine(&quot;Digite seu login: &quot;); char [] oldPassword = c.readPassword(&quot;Digite sua senha : &quot;);
if (verify(login, oldPassword)) { boolean noMatch; do { char [] newPassword1 = c.readPassword(&quot;Digite sua senha: &quot;); char [] newPassword2 = c.readPassword(&quot;Digite sua senha: &quot;); noMatch = ! Arrays.equals(newPassword1, newPassword2); if (noMatch) { c.format(&quot;A senha não confere. Tente de novo.%n&quot;); } else { change(login, newPassword1); c.format(&quot;Senha de %s modificada.%n&quot;, login); } Arrays.fill(newPassword1, ' '); Arrays.fill(newPassword2, ' '); } while (noMatch); }
Arrays.fill(oldPassword, ' '); } //Método fictício static boolean verify(String login, char[] password) { return true; } //Método fictício static void change(String login, char[] password) {} }
Data Streams Data streams suportam I/O binário de tipos primitivos (boolean, char, byte, short, int, long, float, and double) e também Strings. Todos os data stream implementam as interfaces DataInput ou DataOutput. Conheceremos também suas famosas implementações, DataInputStream e DataOutputStream.
Data Streams ( output ) import java.io.BufferedOutputStream; import java.io.DataOutputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; public class UseOutputDataStream { static final String dataFile = &quot;pedido&quot;; static final double[] prices = { 19.99, 9.99, 15.99, 3.99, 4.99 }; static final int[] units = { 12, 8, 13, 29, 50 }; static final String[] descs = { &quot;Camisa do Java&quot;, &quot;Chaveiro do Java&quot;, &quot;Disquete&quot;, &quot;Pen Drive&quot;, &quot;Boné&quot; };
Data Streams ( output ) public static void main(String[] args) throws FileNotFoundException, IOException { DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(dataFile))); //Escrevendo para a stream for (int i = 0; i < prices.length; i ++) { out.writeDouble(prices[i]); out.writeInt(units[i]); out.writeUTF(descs[i]); } } }
Data Streams ( input ) import java.io.BufferedInputStream; import java.io.DataInputStream; import java.io.EOFException; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; public class UseInputDataStream { static final String dataFile = &quot;pedido&quot;; public static void main(String[] args) throws FileNotFoundException, IOException { DataInputStream in = new DataInputStream(new BufferedInputStream(new FileInputStream(dataFile))); double price; int unit; String desc; double total = 0.0;
Data Streams ( output ) //Lendo do arquivo try { while (true) { price = in.readDouble(); unit = in.readInt(); desc = in.readUTF(); System.out.format(&quot;You ordered %d units of %s at $%.2f%n&quot;, unit, desc, price); total += unit * price; } } catch (EOFException e) { } System.out.println(&quot;Total = &quot; + total); } } Nota : DataStreams detectam o fim de arquivo capturando EOFException, ao invés de testarem um valor de retorno. E todas implementações de métodos de DataInput usam EOFException como retorno.
Object Streams Da mesma forma que streams suportam serialização de primitivos, elas também suportam I/O para objetos. Várias, mas nem todas as classes standard suportam serialização para seus objetos. Estas mesmas devem implementar a interface Serializable. As classes de object stream são ObjectInputStream e ObjectOutputStream. Essas classes implementam ObjectInput e ObjectOutput, que são sub-interfaces de DataInput e DataOutput. Isso significa que todos os métodos de I/O para dados primitivos cobertos em Data Streams são também implementados em streams de objetos. Se o método readObject() não retornar o tipo de objeto esperado, e ouver uma tentativa de cast para o tipo corredo, então a exception ClassNotFoundException poderá ser lançada.
Output e Input de objetos complexos Os métodos writeObject e readObject são muito simples de usar, mas eles contém uma lógica muito sofisticada de gerenciamento de objetos. Muitos objetos contém referências para outros objetos. Se readObject precisa reconstruir um objeto de uma stream, então o mesmo tem que reconstruir TODOS os objetos do grafo. Estes objetos adicionais podem ter suas próprias referências, que também deverão ser resolvidas e etc. Uma única invocação de writeObject pode causar que um grande número de objetos sejam escritos para a stream.
Grafo de objetos
Object Streams Se dois objetos dentro da mesma stream contiverem referência para um mesmo objeto, então eles irão referenciar um único objeto quando eles forem reconstruídos. Pois uma stream pode conter somente uma cópia de um objeto, apesar de poder conter um número infinito de referencias ao mesmo. Se escrevermos um objeto para uma stream duas vezes, então nós realmente estaremos escrevendo somente a referência duas vezes.
Object Streams Object ob = new Object(); out.writeObject(ob); out.writeObject(ob); Mas, se um único objeto for escrito para duas streams diferentes, então o mesmo será efetivamente duplicado.
Arquivos Mantivemos o foco de nossa discussão nas streams, que nos dão um modelo simples de leitura e escrita de dados. Streams trabalham com uma grande variedade de fontes de dados e destinos, incluindo arquivos no disco. Mas, streams não suportam todas as operações que são comuns com arquivos em disco, e é onde veremos dois conceitos não-stream : File Random Access File
File Objects A classe File nos torna muito fácil escrever código independente de plataforma que examina e manipula arquivos. O nome desta classe é um pouco confuso, pois instâncias de Files representam nomes de arquivos e não arquivos propriamente ditos, e o arquivo correspondente ao nome pode mesmo nem existir.
Capturando as propriedades de um arquivo import java.io.File; import java.io.IOException; import static java.lang.System.out; public class FileStuff { public static void main(String args[]) throws IOException { out.print(&quot;File system roots: &quot;); for (File root : File.listRoots()) { out.format(&quot;%s &quot;, root); } out.println(); for (String fileName : args) { out.format(&quot;%n------%nnew File(%s)%n&quot;, fileName); File f = new File(fileName); out.format(&quot;toString(): %s%n&quot;, f); out.format(&quot;exists(): %b%n&quot;, f.exists()); out.format(&quot;lastModified(): %tc%n&quot;, f.lastModified());
Capturando as propriedades de um arquivo out.format(&quot;isFile(): %b%n&quot;, f.isFile()); out.format(&quot;isDirectory(): %b%n&quot;, f.isDirectory()); out.format(&quot;isHidden(): %b%n&quot;, f.isHidden()); out.format(&quot;canRead(): %b%n&quot;, f.canRead()); out.format(&quot;canWrite(): %b%n&quot;, f.canWrite()); out.format(&quot;canExecute(): %b%n&quot;, f.canExecute()); out.format(&quot;isAbsolute(): %b%n&quot;, f.isAbsolute()); out.format(&quot;length(): %d%n&quot;, f.length()); out.format(&quot;getName(): %s%n&quot;, f.getName()); out.format(&quot;getPath(): %s%n&quot;, f.getPath()); out.format(&quot;getAbsolutePath(): %s%n&quot;, f.getAbsolutePath()); out.format(&quot;getCanonicalPath(): %s%n&quot;, f.getCanonicalPath()); out.format(&quot;getParent(): %s%n&quot;, f.getParent()); out.format(&quot;toURI: %s%n&quot;, f.toURI()); } } }
Random Access File Random acess files permitem o acesso aleatório ao conteúdo de um arquivo. new RandomAccessFile(&quot;xanadu.txt&quot;, &quot;r&quot;); new RandomAccessFile(&quot;xanadu.txt&quot;, &quot;rw&quot;); int skipBytes(int) — Move o ponteiro do arquivo para a frente com o número de bytes especificado. void seek(long) — Posiciona o pointeiro antes do byte especificado. long getFilePointer() — Retorna o a localização corrente do byte.
Exceções Capítulo 6
O que é uma exception ? O termo exception vem da frase “exceptional event”. Uma exception é um evento, que por sua vez ocorre durante a execução de um programa, que “bagunça” o fluxo normal do mesmo. Quando um erro ocorre dentro de um método, o método cria um objeto e o entrega ao sistema de runtime. O objeto é chamado de objeto de exceção, e contém informações sobre o erro, incluindo o seu tipo e estado do programa quando o erro aconteceu. A criação de objetos de exceção e a entrega deles ao sistema de runtime é chamado de “ throwing an exception ”, ou lançamento de exceções. Após um método lançar uma exceção, o sistema de runtime tenta encontrar alguém para tratar a mesma. O conjunto possível de candidatos a tratar a exceção é a lista ordenada de métodos que foram invocados até chegar ao método que lançou a exceção. Esta lista é denominada call stack .
Call Stack
Tratamento de exceção O sistema de runtime procura o call stack por um método que contém um bloco de código que possa tratar a exceção. Este bloco de código é denominado exception handler . A busca inicia-se com no método onde o erro ocorreu e segue através da call stack em ordem reversa que os métodos foram invocados. Quando um handler apropriado é encontrado, o sistema de runtime passa a exceção para o handler. Um exception handler é considerado apropriado se o tipo de objeto de exceção lançado é do mesmo tipo tratado pelo handler. O exception handler escolhido catch the exception , ou a caputra. Se o sistema de runtime procura em toda call stack e não consegue achar um handler, enão o sistema de runtime ( e o programa ) terminarão.
Tratamento de exceção
Capturando e tratando exceções Um código Java válido para tratar exceções deve : Usar uma declaração try para capturar a exceção, e o mesmo deve prover um tratador para esta exception. Ou O método deve especificar que o mesmo lança a exception, onde o método deve também declarar a cláusula throws .
Três tipos de exceção O primeiro tipo de exception as chamadas checked exeption . Estas exceptions são detectadas pelo compilador Java, e temos como exemplos : java.io.FileNotFoundException O segundo tipo de exception é o tipo error . Esta exception é ocasionada por algo externo a aplicação, e a aplicação geralmente não consegue antecipar-se a este tipo de exceção. Podemos usar como exemplo a abertura de um arquivo, pois podemos conseguir abrir o mesmo, mas na hora de gravarmos recebemos um erro por conta de uma falha de hardware que nos impediu de gravarmos o arquivo, exemplo : java.io.IOErro O terceiro tipo de exception são as runtime exceptions , que são internas a aplicação, mas a mesma não consegue se prevenir da mesma. Este tipo geralmente aponta para bugs, tais como error de lógica ou mal uso da API, exemplo : NullPointerException
Capturando e tratando exceções public class Numeros { public static void main(String []args) { int[] a = new int[5]; a[0] = 5; a[1] = 3; a[2] = 6; a[3] = 9; a[4] = 88; for (int i = 0; i < 6; i++) { int j = a[i]; } } } Exception in thread &quot;main&quot; java.lang.ArrayIndexOutOfBoundsException: 5 at Numeros.main(Numeros.java:13)
O bloco try A primeira coisa que devemos fazer para tratarmos um código de lança uma exceção é colocá-la em um bloco try : try { //codigo aqui } catch...
O bloco try / catch Nós criamos tratadores combinando blocos try e catch, onde cada catch será um tratador para uma excepção diferente : import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.PrintWriter; public class OpenFile { public static void main(String[] args) { File f = new File(&quot;MeuArquivo.txt&quot;); PrintWriter out = null; try { out = new PrintWriter(f); } catch (FileNotFoundException ex) { System.out.println(&quot;Não consegui abir o arquivo. &quot; + ex.getMessage()); } catch (IOException ex) { System.out.println(&quot;Peguei uma IOException. &quot; + ex.getMessage()); } } }
O bloco finally O bloco finally sempre é executado quando um try existe. Isto garante que o bloco finally será executado mesmo quando uma exceção inesperada aconteça. Mas o finally é muito útil além de tratar exceções. O finally permite ao programador ter qualquer código de limpeza sendo pulado acidentamente por algum return, continue ou break. Colocar código de limpeza dentro do finally é uma ótima prática. }finally{ if (out != null ) { System.out.println(&quot;Fechando o arquivo...&quot;); out.close(); }else{ System.out.println(&quot;O arquivo não estava aberto !&quot;); } }
Especificando exceptions lançadas por um método Vimos anteriormente que podemos tratar as exceptions dentro de nossos próprios métodos, mas existe ainda a opção de deixarmos este tratamento para todo código “cliente” que invocar nosso método. Para isso precisamos adicionar a cláusula throws na assinatura de nosso método, seguido das possíveis exceções que nosso código lançará :
Especificando exceptions lançadas por um método import java.io.File; import java.io.FileNotFoundException; import java.io.PrintWriter; class TrataArquivo { public void abreArquivo() throws FileNotFoundException { File f = new File(&quot;MeuArquivo.txt&quot;); PrintWriter out = null; out = new PrintWriter(f); out.println(&quot;ABCDEFGH&quot;); out.close(); } }
Especificando exceptions lançadas por um método public class OpenFile { public static void main(String[] args) { TrataArquivo ta = new TrataArquivo(); try { ta.abreArquivo(); } catch (FileNotFoundException ex) { System.out.println(&quot;Não consegui abrir o arquivo...&quot;); } } }
Lançando exceções É muito simples lançar exceções, tudo que necessitamos fazer é utilizarmos a cláusula throw dentro do método desejado : throw umObjeto Throwable ;
Lançando exceções public class LancandoExceptions { public double calculaPagamento(int mes) throws Exception { if (mes < 1 || mes > 12) { throw new Exception(&quot;Mês inválido !&quot;); } return 99.7; } }
A classe Throwable
Filhos de Throwable Error – Quando acontece um erro na virtual machine ela lança uma exceção deste tipo. E os programas geralmente não capturam ou lançam este tipo de exceção. Exception – Um objeto deste tipo indica que um problema ocorreu, mas isso não é um problema da VM e sim do programa, e geralmente os programas usam suas próprias exceptions para tratar condições de erro. Podemos usar como exemplos de exceptions : IllegalAccessException – Indica que um método não pode ser encontrado. NegativeArraySizeException – Indica que o programa tentou criar um array com tamanho negativo. A classe RuntimeException é reservada para exceptions que dizem respeito ao uso incorreto da API, e podemos citar como exemplo o NullPointerException, que é a tentativa de operação sobre um objeto nulo.
Exceções em cadeia Uma aplicação pode tratar uma exceção simplesmente lançando uma outra exceção. Esta técnica é muito útil, pois podemos capturar uma exceção genérica e transformá-la em outra mais específica : try { } catch (IOException e) { throw new MyException(“Outro Erro&quot;, e) ; }
StackTrace O objeto StackTrace é responsável por retornar um texto com a cadeia de métodos invocados até o lançamento da exceção : public static void main(String[] args) { Exception e = new Exception(&quot;Um erro foi gerado por mim !&quot;); e.printStackTrace(); } Resultado : java.lang.Exception: Um erro foi gerado por mim ! at LancandoExceptions.main(LancandoExceptions.java:13)
Criando uma exceção public class MinhaExcecao extends Exception { public MinhaExcecao() { super(); } public MinhaExcecao(String message) { super(message); } }
Nota sobre unchecked exceptions Muitos programadores sentem-se tentados a escrever código que herdam e lançam somente unchecked exceptions, pois as mesmas não são checadas pelo compilador. Isto é uma péssima prática ! Uma exception que pode ser lançada por um método é parte de sua interface pública, e todos que invocam esses métodos devem saber sobre as exceções que o método pode lançar, e então os mesmos podem decidir como tratá-las. Runtime Exceptions representam problemas que são resultado de programação, e o código cliente não é esperado que se recupera deste tipo de erro. Estes erros incluem exceções de aritimética, divisão por zero, null pointer exceptions, exception de índices dentre outros. Regra básica : Se o cliente pode se recuperar da exception, então a exception deve ser checked, caso contrário, ela deve ser unchecked.
Exceptions são Uma forma organizada de tratar um evento excepcional que aconteceu durante o fluxo do seu programa. As exceptions eliminam os códigos tipo espaguete ( bacalhau ) da forma tradicional de tratamento de erros.
Threads Capítulo 7
Concorrência Nós usuários de computadores acreditamos e confiamos que nossos sistemas podem fazer mais de uma coisa ao mesmo tempo. Assumimos que podemos continuar trabalhando no work enquanto fazemos download de músicas, imprimimos algo, copiamos arquivos e etc. Mesmo um simples aplicativo é esperado fazer mais de uma coisa de cada vez. A plataforma Java foi re-estruturada do zero para suportar programação concorrente, com suporte concorrente básico na linguagem java e em suas bibliotecas. Desde a versão 5.0 que a plataforma Java incluiu também a biblioteca de concorrência.
Processos e threads Em programação concorrente, existem duas unidades básicas de execução : processos e threads . Em Java a programação concorrente está mais focada em threads. Mas de qualquer forma os processos também são importantes. Um computador normalmente possui vários processos ativos e threads. E isto também é verdadeiro para sistemas que possuem uma única linha de execução, e possui somente uma thread sendo executada naquele momento. Tempo de processamento para um único core é compartilhado entre os processos e threads através de uma feature do SO chamada “time slicing”. Hoje em dia é muito comum que os computadores tenham vários procesadores com vários cores, e isto aumenta em muito a capacidade de execução concorrente de processos e threads. Mas a concorrência também é possível em sistemas simples, onde não existam vários processadores ou cores de execução.
Processos Um processo é um ambiente de execução auto-contido. E um processo geralmente tem um conjunto privado e completo de recursos de runtime, e em particular cada processo possui sus própria área de memória. Processos são geralmente vistos como sinônimos de programas ou aplicações, entretanto oque o usuário vê como uma simples aplicação pode ser na realidade um conjunto de processos cooperando. Para facilitar a comunicação entre processos, vários SOs suportam recursos de Inter Process Communication (IPC), tal como pipes ou sockets. IPC não é usado somente para a comunicação entre processos no mesmo sistema, mas processos em diferentes sistemas A grande maioria das implementações da JVM rodam em um único processo. Uma aplicação Java pode criar processos adicionais usando um objeto ProcessBuilder.
Processos
Threads Threads são também conhecidas como processos leves. Ambos processos e threads provêem um ambiente de execução, mas criar novas threads requer muito menos recursos do que criar novos processos. Threads existem dentro de processos, e cada processo possui pelo menos uma. As threads compartilham os recursos do processo, incluindo memória e arquivos. Isso aumenta em muito a eficiência, mas pode acarretar em uma comunicação problemática. Execução multi-thread é uma feature essencial da plataforma Java. Onde cada aplicação possui pelo menos uma thread, ou várias, se contarmos as threads do sistema que realizam trabalhos como gerenciamento de memória e tratamento de sinais. Mas da perspectiva do programador o programa é iniciado com uma única thread, chamada main thread .
Objetos Thread Cada thread é associada com uma instância da classe Thread . E existem duas formas de usarmos o objeto thread : Para controlar a thread diretamente, simplesmente instanciamos uma Thread cada vez q a aplicação precisar realizar alguma tarefa assíncrona. Para abstrair o gerenciamento da thread do resto da aplicação, passamos a tarefa a um executor.
Definir e iniciar uma Thread Criar um objeto Runnable. A interface Runnable define um método chamado run, que é esperado conter o código a ser executado em uma thread. O objeto runnable então é passado no construtor da thread para ser executado : public class HelloRunnable implements Runnable { public void run() { System.out.println(&quot;Hello from a thread!&quot;); } public static void main(String args[]) { (new Thread(new HelloRunnable())).start(); } }
Definir e iniciar uma Thread Criar uma sublasse de Thread. Podemos fazer isso pois a classe Thread também implementa runnable diretamente. Podemos então criar uma subclasse e prover o código para o método run : public class HelloThread extends Thread { public void run() { System.out.println(&quot;Hello from a thread!&quot;); } public static void main(String args[]) { (new HelloThread()).start(); } }
Dando uma pausa na execução com sleep O método Thread.sleep causa a suspensão da execução da thread corrente por um período específico de tempo. Isto é uma forma eficiente para termos tempo de processamento disponível para outras threads de uma aplicação, ou ainda para outras aplicações rodarem. O método sleep pode também ser usado para marcar o ritmo de execução, como por exemplo aguardar por outra thread. public static native void sleep(long millis) throws InterruptedException; public static void sleep(long millis, int nanos) throws InterruptedException;
Exemplo public class SleepMessages { public static void main(String args[]) throws InterruptedException { String importantInfo[] = { &quot;Mares eat oats&quot;, &quot;Does eat oats&quot;, &quot;Little lambs eat ivy&quot;, &quot;A kid will eat ivy too&quot; }; for (int i = 0; i < importantInfo.length; i++) { //Pause for 4 seconds Thread.sleep(4000); //Print a message System.out.println(importantInfo[i]); } } }
Nota No exemplo anterior vimos que o método main declara uma InterruptedException, que é a exception que o método sleep lança quando outra thread interrompe a thread corrente enquanto o sleep está ativo.
Interrupções Uma interrupção é um indicativo que a thread deve de fazer aquilo que estiver fazendo, e fazer alguma outra coisa. É de inteira responsabilidade do programador dizer como a thread responde a uma interrupção, mas é muito comum a thread ser terminada nesses casos. A thread envia uma interrupção invocando o método interrupt no objeto Thread para a thread que será interrompida. E para este mecanismo funcionar direito, a thread interrompida deve suportar sua própria operação.
Suportando interrupções A thread suporta interrupções através de vários métodos, podendo lançar a exceção InterruptedException, onde a execução será abortada. for (int i = 0; i < 10; i++) { //Pausa por 4 segundos try { Thread.sleep(4000); } catch (InterruptedException e) { //Exceção capturada, e nada mais a fazer. return; } //Print a message System.out.println(i); }
Suportando interrupções Existem vários métodos que lançam InterruptedException, e os mesmos foram configurados para cancelar sua execução corrente e retornar imediatamente qiuando uma interrupção for recebida. public static native void sleep(long millis) throws InterruptedException; public static void sleep(long millis, int nanos) throws InterruptedException {} public final synchronized void join(long millis) throws InterruptedException {} public final synchronized void join(long millis, int nanos) throws InterruptedException {} public final void join() throws InterruptedException {}
Sabendo se uma interrupção foi recebida As vezes as threads podem ficar um tempo muito longo sem invocar nenhum método que lança uma InterruptedException, então podemos invocar o método Thread.interrupted para sabermos se uma interrupção foi recebida. if (Thread.interrupted()) { //Interrupção recebida return; }
Joins O método join permite uma thread aguardar pelo término do trabalho de outra. minhaThread.join(); Causa a interrupção da thread corrente até que minhaThread termine seu trabalho.
Exemplo SimpleThreads public class SimpleThreads { }
Sincronização Threads comunicam-se primáriamente compartilhando o acesso a campos de objetos que elas referenciam. Esta forma de comunicação é muito eficiente, mas também é refém de dois erros : interferência de thread e erro de inconsistência de memória.
Interferência Interferências acontecem quando duas operações, rodando em diferentes threads, mas operando sobre o mesmo dado se intercalam. public class Contador { private int campo = 0; public void incrementa() { campo++; } public void subtrai() { campo--; } public int valorAtual() { return campo; } }
Exemplo de interferência Thread 1 pega o contador Thread 2 pega o contador Thread 1 recupera o valor e o incrementa (1) Thread 2 recupera o valor e o decrementa (-1) Thread 1 armazena o valor em c, que agora vale (1) Thread 2 armazena o valor em c, que agora vale (-1)
Métodos sincronizados O Java provê duas formas básicas de sincronização : métodos sincronizados e declarações sincronizadas. Para fazer um método se tornar sincronizado, somente precisamos adicionar a palavra chave synchronized em sua declaração.
Métodos sincronizados public class ContadorSincronizado { private int c = 0; public synchronized void increment() { c++; } public synchronized void decrement() { c--; } public synchronized int value() { return c; } }
Métodos sincronizados Agora que temos um contador sincronizado, temos o seguinte resultado : Não é mais possível termos mais de uma invocação em métodos sincronizados simultâneamente. Quando uma thread está executando um método sincronizado de um objeto, todas as outras thread que invocam este mesmo método estão suspensas, até que a primeira thread termine seu trabalho com o objeto. Quando um método sincronizado existe, o mesmo garante a consistência com qualquer invocação futura de um método sincronizado do mesmo objeto. Isto garante que mudanças no estado do objeto sejam visíveis a todas as threads. Obs : Construtores não podem receber a cláusula synchronized, pois somente uma thread consegue criar uma instância por vez, e isto também ocasionará em um erro de compilação.
Locks implícitos e sincronização A sincronização é feita atrvés de uma dispositivo interno chamado “monitor lock” . Locks implícitos comandam o acesso exclusivo ao estado do objeto. Cada objeto tem um lock implícito associado a ele. Uma thread que precisa acesso exclusivo e consistente a um campo de um objeto precisa pegar um lock implícito antes de acessá-lo, e então liberá-lo quando terminar seu trabalho. A thread é dona do lock entre o momento em quen o pega, até o momento da liberação. Enquanto uma thread tiver o lock sobre o objeto, nenhuma outra thread conseguirá pegar o mesmo lock, ou seja, as outras threads serão bloqueadas quando tentarem realizar o lock.
Locks em métodos sincronizados Quando uma thread invoca um método sincronizado, a mesma adquire automáticamente o lock para o método do objeto e o libera quando o método retornar.
Declarações sincronizadas Outra forma de criarmos código sincronizada é através das declarações sincronizadas . Ao contrário dos métodos sincronizados, as declarações devem especificar o objeto que provê o lock : public void addName(String name) { synchronized(this) { lastName = name; nameCount++; } nameList.add(name); }
DeadLock O deadlock é uma situação onde duas ou mais threads ficam bloqueadas para sempre, esperando uma pela outra.
Collections Capítulo 8
Introdução Uma coleção é um container, ou seja, um objeto que tem a capacidade de agrupar vários elementos em uma única unidade. Coleções são utilizadas para guardar, recuperar, manipular e comunicar dados agregados. Geralmente elas representam algum agrupamento natural, como por exemplo lista de funcionários, grupo de emails, e etc.
O framework de coleções É a arquitetura unificada para representar e maniuplar coleções, onde todos os frameworks possuem : Interfaces : Que são dados abstratos que representam as coleções. As interfaces permitem que as coleções sejam manipuladas independentemente dos detalhes de implementação. Implementações : São as implementações concretas das interfaces, ou seja, elas são as estruturas “de fato”. Algorítimos : São métodos que executam operações úteis, tal como pesquisa e ordenação em objetos que implementam interfaces de coleções. Os algorítimos são polimórficos, ou seja, o mesmo método pode ser usado em diferentes implementa~ções.
Benefícios do framework de coleções Reduz o trabalho com programação : Pois provê ótimas estruturas de dados e algorítimos, o framework de coleções nos libera para concentrarmos somente nas partes importantes do nosso programa, ao invés de ficarmos focados em problemas de “baixo-nível”. Aumenta a velocidade de programação e a qualidade : Pois provê implementações de alta performance e alta qualidade em suas estruturas e algorítimos. As várias implementações são intercambiáveis, então os programas podem mudar de implementação fácilmente. Reduz o tempo de aprendizado. Reduz o tempo de escrita de novas APIs Possibilita o reuso.
Interfaces As interfaces do core encapsulam diferentes tipos de coleções, que são mostradas na próxima figura. Estas interfaces permitem que as coleções sejam manipuladas independente de sua representação. As coleções do core são a pedra fundamental do Java Collections Framework.
Collection A raiz da hierarquia de coleções. Uma collection representa um grupo de objetos denominados elementos. A interface Collection é o denominador comum entre todas as coleções que a implementam e é usada para passar coleções a métodos e manipulá-las de forma genérica.
Set É uma coleção que não pode ter elementos duplicados. Esta interface modela a abstração matemática de “conjunto”.
List Uma coleção ordenada ( também conhecida como sequência). As listas podem conter elementos duplicados. O usuário geralmente tem o controle preciso de onde o elemento é inserido na mesma, e pode acessá-lo usando um índice de posição.
Queue Uma coleção usada para carregar múltiplos elementos antes de processá-los. Queues típicamente, mas não necessáriamente, ordenam elementos no modo FIFO (first-in, first-out). Entre as exceções estão priority queues, que ordenam os elementos de acordo com um comparator fornecido.
Map Um objeto que mapeia chaves e valores. Um mapa não pode ter chaves duplicadas, e uma chave só pode mapear um único valor.
Sorted SortedSet - Um conujunto que mantém seus elementos em ordem ascendente. SortedMap – Um mapa que mantém os seus mapeamentos em ordem ascendente de chave. Este mapa é análogo ao SortedSet.
A iterface Collection public interface Collection<E> extends Iterable<E> { // Basic operations int size(); boolean isEmpty(); boolean contains(Object element); boolean add(E element); //optional boolean remove(Object element); //optional Iterator<E> iterator(); // Bulk operations boolean containsAll(Collection<?> c); boolean addAll(Collection<? extends E> c); //optional boolean removeAll(Collection<?> c); //optional boolean retainAll(Collection<?> c); //optional void clear(); //optional // Array operations Object[] toArray(); <T> T[] toArray(T[] a); }
Atravessando coleções Declaração for-each for (Object o : collection) { System.out.println(o); } Iterators public interface Iterator<E> { boolean hasNext(); E next(); void remove(); //optional } static void filter(Collection<?> c) { for (Iterator<?> it = c.iterator(); it.hasNext(); ) if (!cond(it.next())) it.remove(); }
Operações em massa sobre Collections Operações em massa são executadas em cima de uma coleção inteira. containsAll – retorna true se a coleção destino possui todos os itens na coleção especificada. addAll – adiciona todos os elementos da coleção especificada na coleção destino. removeAll – remove da coleção destino todos os elementos que também estiverem contidos na coleção especificada. retainAll – remove da coleção destino todos os elementos que não estiverem contidos na coleção especificada. clear – remove todos os elementos da coleção. c.removeAll(Collections.singleton(e)); c.removeAll(Collections.singleton(null));
A interface Collection e Arrays O método toArray é provido como uma ponte de ligação entre coleções e APIs antigas que esperam arrays como entrada. As operações com arrays permitem que o conteúdo de uma coleção seja traduzido para um array. Object[] a = c.toArray();
A interface Set public interface Set<E> extends Collection<E> { // Basic operations int size(); boolean isEmpty(); boolean contains(Object element); boolean add(E element); //optional boolean remove(Object element); //optional Iterator<E> iterator(); // Bulk operations boolean containsAll(Collection<?> c); boolean addAll(Collection<? extends E> c); //optional boolean removeAll(Collection<?> c); //optional boolean retainAll(Collection<?> c); //optional void clear(); //optional // Array Operations Object[] toArray(); <T> T[] toArray(T[] a); }
Implementações de Set O Java possui 3 implementações para a interface Set. HashSet – Armazena os elementos em uma hash table, é a implementação de melhor performance, mas a mesma não garante order alguma no momento da iteração TreeSet – Armazena os elementos em formato red-black tree, ordena os elementos baseado nos seus valores, e é substancialmente mais lenta que HashSet. LinkedSet – Implementada como hash table com uma linked list, ordena os elementos baseada na order em que eles foram inseridos no conjunto. Collection<Type> noDups = new HashSet<Type>(c); Collection<Type> noDups = new LinkedHashSet<Type>(c);
Operações em massa com Sets Operações em massa são muito interessantes em Sets, pois elas são executadas de forma algébriga. s1.containsAll(s2) — retorna true se s2 é um subconjunto de s1. (s2 é um subconjunto de s1 se s1 contém todos os elementos que estão em s2.) s1.addAll(s2) — transforma s1 na união de s1 e s2. (A união de dois sets é o set contendo todos os elementos contidos em ambos sets.) s1.retainAll(s2) — transforma s1 na interseção de s1 e s2. (A interseção de dois sets é um set contendo somente os elementos comuns em ambos sets.) s1.removeAll(s2) — transforma s1 na diferença (assimétrica) de s1 e s2. (Por exemplo, a diferença entre s1 menos s2 é o set contendo todos os elementos encontrados em s1 mas não em s2.)
Operações em massa com Sets Para calcular a união, interseção ou a diferença entre sets de forma não destrutiva, então devemos copiar o set antes de invocarmos as operações em massa : Set<Type> union = new HashSet<Type>(s1); union.addAll(s2); Set<Type> intersection = new HashSet<Type>(s1); intersection.retainAll(s2); Set<Type> difference = new HashSet<Type>(s1); difference.removeAll(s2);
A interface List public interface List<E> extends Collection<E> { // Positional access E get(int index); E set(int index, E element); //optional boolean add(E element); //optional void add(int index, E element); //optional E remove(int index); //optional boolean addAll(int index, Collection<? extends E> c); //optional // Search int indexOf(Object o); int lastIndexOf(Object o); // Iteration ListIterator<E> listIterator(); ListIterator<E> listIterator(int index); // Range-view List<E> subList(int from, int to); }
Implementações de List O Java possui duas implementações para List. ArrayList – Geralmente é a implementação de melhor performance. LinkedList – Oferece melhor performance sob certas circunstâncias. Vector “intruso” – Foi refatorado para implementar List.
Operações sobre List list1.addAll(list2); //Forma não destrutiva List<Type> list3 = new ArrayList<Type>(list1); list3.addAll(list2); //acesso posicional e pesquisa public static <E> void swap(List<E> a, int i, int j) { E tmp = a.get(i); a.set(i, a.get(j)); a.set(j, tmp); }
Iterators É retornado pelas operações de iterators da lista. public interface ListIterator<E> extends Iterator<E> { boolean hasNext(); E next(); boolean hasPrevious(); E previous(); int nextIndex(); int previousIndex(); void remove(); //optional void set(E e); //optional void add(E e); //optional } for (ListIterator<Type> it = list.listIterator(list.size()); it.hasPrevious(); ) { Type t = it.previous(); ... }
Queue public interface Queue<E> extends Collection<E> { E element(); boolean offer(E e); E peek(); E poll(); E remove(); } static <E> List<E> heapSort(Collection<E> c) { Queue<E> queue = new PriorityQueue<E>(c); List<E> result = new ArrayList<E>(); while (!queue.isEmpty()) result.add(queue.remove()); return result; }
A interface Map public interface Map<K,V> { // Basic operations V put(K key, V value); V get(Object key); V remove(Object key); boolean containsKey(Object key); boolean containsValue(Object value); int size(); boolean isEmpty(); // Bulk operations void putAll(Map<? extends K, ? extends V> m); void clear();
A interface Map // Collection Views public Set<K> keySet(); public Collection<V> values(); public Set<Map.Entry<K,V>> entrySet(); // Interface for entrySet elements public interface Entry { K getKey(); V getValue(); V setValue(V value); } } Existem três implementações para a interface Map : HashMap, TreeMap e LinkedHashMap. Que por sua vez são análogas as implementações de Set.
Operações com Map import java.util.*; public class Freq { public static void main(String[] args) { HashMap<String, Integer> m = new HashMap<String, Integer>(); // Initialize frequency table from command line for (String a : args) { Integer freq = m.get(a); m.put(a, (freq == null) ? 1 : freq + 1); } System.out.println(m.size() + &quot; distinct words:&quot;); System.out.println(m); } } for (Map.Entry<KeyType, ValType> e : m.entrySet()) System.out.println(e.getKey() + &quot;: &quot; + e.getValue());
Generics Capítulo 9
Introdução Generics é a alteração mais significante da linguagem Java desde a sua versão 1.0. A adição das generics no java 5.0 foi o resultado de uma especificação ( JSR 14 ), que foi feita em 1999. Generics são muito úteis porque elas nos permitem escrever código que é seguro e fácil de ler, ao invés de código que trabalha com Objects e usa vários casts. As generics são muito úteis no trabalho com collections, tal como o ArrayList.
Introdução Generics são muito parecidas com as famosas templates do C++. Programação com generics significa escrever código que pode ser reutilizado por objetos de diferente tipos.
Usando coleções com cast List myIntList = new LinkedList(); // 1 myIntList.add(new Integer(0)); // 2 Integer x = (Integer) myIntList.iterator().next(); // 3 Famoso código “saco de gatos”
Com Generics Uma interface ou classe pode ser declarada para receber um ou mais parâmetros, que são escritos entre os sinais de menor e maior < e >. List <Integer> myIntList = new LinkedList<Integer>(); // 1' myIntList.add(new Integer(0)); // 2' Integer x = myIntList.iterator().next(); // 3'
Especificação de generics Definição da interface List : public interface List <E>{ void add(E x); Iterator<E> iterator(); } public interface Iterator<E>{ E next(); boolean hasNext(); }
Programando com generics É muito fácil utilizarmos uma classe genérica como ArrayList. E a maioria de nós programadores mortais iremos simplesmente digitar ArrayList<String>, pois esta declaração já faz parte da linguagem, e é tão natural quanto se estivessemos declarando um array de strings : String[]. Infelizmente não é tão fácil implementar uma classe genérica, pois os programadores que utilizarão este código podem querer plugar todos os tipos de classes como parâmetro de tipo. E os mesmos esperam que tudo funcione sem restrição ou mensagem de erro.
Wrappers Todo tipo em Java é um tipo referência ou primitivo. Um tipo referência é qualquer classe, instância ou array. Todos os tipos referência são subclasses de Object. Existem 8 tipos de primitivos, e cada um deles possui uma classe ( Wrapper ) correspondente.
Wrappers Primitivo Referência byte Byte short Short int Integer long Long float Float double Double boolean Boolean char Character
Boxing e Unboxing A conversão de primitivo para refência é chamado de boxing, e a conversão de referência para primitivo é chamado de unboxing. O Java com generics insere mecanismos de boxing e unboxing quando necessário, deixando o programador livre de criar um wrapper ou convertê-lo para um primitivo. List<Integer> list = new ArrayList<Integer>(); list.add(1); //Boxing int n = list.get(0); //Unboxing //É o mesmo que : List<Integer> list = new ArrayList<Integer>(); list.add(new Integer(1)); int n = list.get(0);
Definição de classe genérica public class Pair<T> { public Pair() { first = null; second = null; } public Pair(T first, T second) { this.first = first; this.second = second; } public T getFirst() { return first; } public T getSecond() { return second; } public void setFirst(T newValue) { first = newValue; } public void setSecond(T newValue) { second = newValue; } private T first; private T second; }
Definição de classe genérica Temos em nossa classe “Pair” uma variável de tipo T, que está entre < > depois do nome da classe. Vale a pena lembrar que uma classe genérica pode ter mais de um tipo de variável. Poderíamos ter definido a classe Pair usando dois parâmetros : public class Pair<T, U> { . . . }
Padrões Os “type variables” são utilizados dentro da definição da classe para especificar o retorno do método, tipos de campos e variáveis locais : private T first; É uma prática comum usar letras em caixa alta para tipos, e mantê-los curtos. As bibliotecas do Java usam a variável E para tipo de elemento de coleção, K e V para chave e valor ( key/value ) de um mapa/tabela, e T ( e também U e S, se necessário), para qualquer outro tipo.
Instanciando tipos genéricos Para tal tarefa necessitamos somente substituir as variáveis de tipo pelo tipo desejado : Pair<String>
Método genérico e varargs Public static <T> void addAll(list<T>, T... arr) { For (T tipo : arr ) { list.add(tipo); } }
Testando a classe genérica Pair public class PairTeste { public static void main(String[] args) { Pair<String> p = new Pair<String>(); p.setFirst(&quot;a&quot;); p.setSecond(&quot;b&quot;); } }
foreach Existe uma forma mais rápida e inteligente de iterar em arrays e coleções : double[] ar = {1.2, 3.0, 0.8}; int sum = 0; for (double d : ar) { sum += d; }
foreach List<String> names = new ArrayList<String>(); names.add(&quot;a&quot;); names.add(&quot;b&quot;); names.add(&quot;c&quot;); for (String name: names) { System.out.println(name.charAt(0)); }

Apostila de Fundamentos Java

  • 1.
  • 2.
    Sobre nossa empresaA Jet Software é uma empresa jovem, dinâmica, que atua no mercado de TI, em consultoria e treinamento. Nossa empresa é especializada em Java, .NET, SOA, EAI e metodologias ágeis. Nossos profissionais são experientes, certificados e possuem vários anos de experiência de mercado, agregando assim alto valor nas soluções apresentadas a nossos clientes, quer sejam elas sobre consultoria ou treinamentos.
  • 3.
    Sobre esta apostilaEsta apostila tem a finalidade de levar ao aluno os fundamentos da linguagem Java. Ou seja, nosso objetivo é mostrar os conceitos que regem a linguagem, seu funcionamento, comportamento, configuração, setup de ambiente e toda informação básica que darão ao aluno o conhecimento técnico suficiente para a preparação para a certificação e cobrir os pré-requisitos para os próximos módulos de desenvolvimento Java para web, componentes distribuídos ( EJB ) e demais módulos.
  • 4.
    Sobre este cursoNosso foco é trazer a nossos alunos um curso dinâmico, abordando o conteúdo da linguagem e da plataforma, e também as melhores práticas e bibliotecas Utilizadas pelo mercado. E foi pensando nisso que a Jet Software elaborou sua grade de cursos, afim de que ao término de cada módulo o aluno esteja dominando a tecnologia / plataforma, e também conhecendo tudo aquilo que o mercado utiliza, estando assim melhor preparado para as futuras oportunidades.
  • 5.
    O que oaluno deve aprender sobre Java ? Esta é uma pergunta que muitas pessoas fazem e provavelmente continuarão a fazer. Existe um certo mito de que para ser um bom programador Java o indivíduo deve saber somente “tudo” sobre a linguagem. Fique calmo, pois isto não é verdade! A linguagem e a plataforma Java possui inúmeras features e APIs, e seria impossível descarregar todo este conteúdo sobre o aluno em um só curso ou em um só livro, o que existe sim é um conjunto de regras e conceitos básicos que o aluno necessita saber para iniciar sua vida como programador Java, e após a absorção destes conceitos é muito fácil adicionar mais conhecimento a sua base.
  • 6.
    O que oaluno deve aprender sobre Java ? Todos os próximos módulos usarão os conceitos, APIs e demais conhecimentos adquiridos neste curso de fundamentos. Recomendamos aqueles alunos que desejam tirar a certificação que ao término de nosso curso invistam entre 2 ou 4 semanas se preparando para a prova, pois não basta somente absorver o conteúdo em sala de aula como será passado, mas também treinar o que foi aprendido, e ainda preparar-se para as famosas “pegadinhas” da prova. Recomendamos o seguinte livro para a preparação, que contém uma gama imensa de exercícios e dicas :
  • 7.
    Preparação para acertificação SCJP Sun Certified Programmer for Java 6 Exam 310-065 [ILLUSTRATED] (Hardcover) by Katherine Sierra (Author), Bert Bates (Author)
  • 8.
    Agenda do CursoIntrodução ao Java Conceitos Básicos Introdução a Orientação a Objetos Declaração de variáveis Construção de classes Tipos Herança e Polimorfismo Interfaces Enumerações ( Enum ) Controle de Fluxo Tratamento de Exceções O Framework de coleções ( Collection framework ) Generics Threads JDBC
  • 9.
  • 10.
    Simples, orientada aobjetos e familiar A linguagem de programação Java foi pensada para “atacar” as dificuldades do desenvolvimento de aplicações, no contexto de heterogeneidade, ambientes de rede distribuídos. E além de todos estes desafios, a execução segura de aplicações que consomem o mínimo de recursos de um sistema, pode ser executada em qualquer hardware e plataforma de software, e além de tudo pode ser estendida dinamicamente.
  • 11.
    Simples, orientada aobjetos e familiar A linguagem de programação Java foi originada como parte de um projeto de pesquisa para desenvolver uma espécie de software avançado para uma grande variedade de dispositivos de rede ( móveis ). O objetivo era desenvolver uma plataforma operacional que fosse pequena, confiável, portável distribuída, tempo-real.
  • 12.
    Simples, orientada aobjetos e familiar Quando o projeto iniciou-se o C++ foi a linguagem escolhida. Mas conforme o tempo foi passando as dificuldades encontradas com o C++ cresceram ao ponto de que os problemas poderiam ser melhor resolvidos criando-se uma nova plataforma de linguagem. Decisões de design e arquitetura vieram de uma grande variedade de linguagens, tais como Eiffel, SmallTalk, Objective C e Cedar/Mesa. O resultado foi uma nova linguagem/plataforma que provou ser ideal para o desenvolvimento de aplicações seguras, distribuídas, network-based, aplicações de usuário final em diversos ambientes que vão desde sistemas embarcados ( embedded ) até a Web e desktops.
  • 13.
    Robusta e seguraA linguagem de programação Java foi arquitetada para ser um software altamente confiável. Ela provê uma extensiva checagem em tempo de compilação, seguida por um segundo nível de checagem em tempo de execução. As features da linguagem guiam os programadores no caminho dos hábitos confiáveis de programação.
  • 14.
    Robusta e seguraO modelo de gerenciamento de memória é extremamente simples, pois os objetos são criados com o operador new . Não existe nenhum tipo de ponteiro definido pelo programador, não existe aritimética de ponteiros , e temos ainda um garbage collector automático. Este modelo simples de gerenciamento elimina uma gama incrível de erros de programação que programadores C e C++ tem que lidar no seu dia-a-dia. Podemos desenvolver código Java com a confiança que o sistema/plataforma irá encontrar muitos erros rapidamente e que os maiores problemas não ficarão escondidos até nosso software estar em produção.
  • 15.
    Robusta e seguraA tecnologia Java foi desenvolvida para operar em ambientes distribuídos, o que significa que a segurança é um item de extrema importância. Com as propriedades de segurança que foram desenvolvidas dentro da linguagem e no sistema de run-time, a tecnologia Java nos permite construir aplicações que não podem ser invadidas por meios externos. Em um ambiente de rede, as aplicações escritas na linguagem Java estão seguras de qualquer intrusão por código não autorizado, ou ainda por vírus ou arquivos inválidos.
  • 16.
    Arquitetura neutra eportável A tecnologia Java é também responsável por suportar aplicações que serão instaladas ( deployed ) em ambientes de redes heterogêneos. Nestes ambientes as aplicações precisam ser capazes de executar em uma grande variedade de arquitetura de hardware. Dentro desta variedade de plataformas de hardware, as aplicações devem executar em cima de uma variedade de sistemas operacionais, e inter-operar com Múltiplas interfaces de linguagem de programação. Para acomodar esta diversidade de sistemas operacionais, o compilador Java produz o bytecode , que é um formato arquitetural intermediário e neutro, que é designado a trasportar o código eficientemente para múltiplas plataformas de hardware e software. A natureza interpretada da tecnologia Java resolve ambos problemas de distribuição binária e versionamento; pois o mesmo byte code será executado em qualquer plataforma.
  • 17.
    Arquitetura neutra eportável A arquitetura neutra e portável da plataforma da linguagem Java é conhecida como Java Virtual Machine . Ela é uma especificação de uma abstração de uma máquina, na qual os compiladores da linguagem Java podem gerar código. Implementações específicas Da Java Virtual Machine para plataformas específicas de hardware e software criam a concreta materialização da virtual machine. A Java Virtual Machine é baseada primeiramente na especificação de interface POSIX, um padrão da industria para a definição de interfaces de sistemas portáveis. Implementar a Java Virtual Machine em novas arquitetura é uma tarefa relativamente simples, necessitando apenas que a plataforma de destino cumpra com os requisitos básicos, tal como o suporte a multi-threading.
  • 18.
    Alta Performance Performanceé sempre alvo de grandes considerações. A plataforma Java possui uma performance superior adotando um esquema onde o interpretador pode ser executado em velocidade máxima sem a necessidade do ambiente de checagem de run-time. O garbage collector automático é executado como uma thread de baixa prioridade em background, proporcionando assim uma alta probabilidade que a memória estará disponível quando requisitada, causando assim uma melhor performance.
  • 19.
    Alta Performance Aplicaçõesque necessitam de uma grande quantidade de poder de processamento podem ser designadas tal como seções de computação intensivas podem ser re-escritas em código nativo de máquina como necessitar e interfacear com a plataforma Java. Em geral, usuários percebem que aplicações interativas respondem rápido, mesmo que elas sejam interpretadas.
  • 20.
    Interpretada, threaded, dinâmicaO interpretador Java pode executar bytecodes Java diretamente em qualquer máquina na qual o sistema de interpretador e run-time tenha sido portada. Em uma plataforma interpretada como o Java, a fase de link de um programa é simples, incremental e leve. Os benefícios são grandes, tais como ciclos de desenvolvimentos mais rápidos, prototipação, experimentação, e o desenvolvimento rápido de casos normais, ao contrário do tradicional compilador “peso-pesado” : compilar, linkar e ciclos de testes.
  • 21.
    Interpretada, threaded, dinâmicaAplicações modernas baseadas em rede, tal como o HotJava Browser para a web, tipicamente necessida de muitas coisas ao mesmo tempo. Um usuário trabalhando com o HotJava browser pode executar muitas animações concorrentemente enquanto faz o download de uma imagem e usa o scroll de uma página. A capacidade de multi-thread da tecnologia Java provê os meios para a construção de aplicações com atividades concorrentes de threads. Multithreading resulta em um alto nível de interatividade para o usuário final.
  • 22.
    Interpretada, threaded, dinâmicaA plataforma Java suporta multi-threading no nível de linguagem com a sofisticação adicional de sincronização de primitivos: a biblioteca de linguagem provê a classe Thread , e o sistema de run-time provê o monitor e as condições de lock primitivas. No nível de biblioteca, novamente, as bibliotecas de sistema de alto nível, tem sido escritas para serem thread safe : a funcionalidade provida pelas bibliotecas está disponível sem o conflito para a execução de múltiplas threads.
  • 23.
    Interpretada, threaded, dinâmicaEnquanto o compilador Java é restrito no seu “compile-time static checking”, o sistema de run-time da linguagem é dinâmico nos seus estágios de linking. As classes são “linkadas” somente quando necessário. Novos módulos de código podem ser linkados sob demanda de uma grande variedade de fontes, mesmo de fontes através da rede. No caso do HotJava Browser e aplicações similares, código executável interativo pode ser carregado de qualquer lugar, o que possibilita o update transparente de aplicações. O resultado é um serviço constante on-line que sempre é incrementado; ele pode permaneter inovador e recente, ser oferecido para mais clientes, e suportar o crescimento do comércio eletrônico na internet.
  • 24.
  • 25.
    A plataforma Java,um novo jeito de fazer computação distribuída Se pegarmos individualmente, estas características discutidas acima podem ser encontradas em uma grande variedade de plataformas de software. Mas o que é completamente novo é a maneira que a tecnologia Java e o seu ambiente de runtime tem combinado eles para produzir um poderoso e flexível sistema de programação.
  • 26.
    A plataforma Java,um novo jeito de fazer computação distribuída Desenvolver nossas aplicações usando a linguagem de programação Java resulta em um software que é portável através de múltiplas arquiteturas de máquinas, sistemas operacionais, e interfaces de usuário gráficas, seguro e de alta performance. Com a tecnologia Java, nosso trabalho como desenvolvedores é muito fácil, pois o foco de nossa atenção é somente no objetivo final de entregarmos um produto inovador no prazo, baseado nos sólidos fundamentos da plataforma Java. A melhor forma de desenvolver software é aqui, agora, trazido pela plataforma Java.
  • 27.
    A plataforma Javahttp://java.sun.com/javase/technologies/index.jsp
  • 28.
    Resumo Simples Orientadaa Objetos Distribuída Multithreaded Dinâmica Arquiteturalmente Neutra Portável Alta Performance Robusta Segura
  • 29.
    Instalando o ambientede desenvolvimento do Java
  • 30.
    O ambiente dedesenvolvimento Para podermos desenvolver programas em Java precisaremos : Java Development Kit ( JDK ), que por sua vez é um conjunto de programas e APIs que nos possibilita escrevermos e compilar programas em Java. Notepad ++ Netbeans, que é uma das mais famosas IDEs para desenvolvimento Java.
  • 31.
    Passos para ainstalação do ambiente Download the Notepad++ http://notepad-plus.sourceforge.net/uk/site.htm Execute o instalador do Notepad++ Download the JDK : http://java.sun.com/javase/downloads Ou vá para a página principal : www.java.sun.com e navegue no link JavaSE, afim de baixar a versão correta ( 6.x ) Execute o instalador do JDK Crie a variável JAVA_HOME no seu painel de controle, apontando para o diretório de instalação de seu JDK, ex : C:\Arquivos de programas\Java\jdk1.6.0_05 Configure o seu path no painel de controle para conter o caminho do JDK, exemplo : %JAVA_HOME%;%JAVA_HOME\bin%;%JAVA_HOME\lib%;
  • 32.
    Passos para ainstalação do ambiente
  • 33.
    Verificando a instalação Abra uma janela de comandos do windows e digite o seguinte comando : java –version Você deverá ver o seguinte resultado ( ou a versão que você instalou ): java version 1.6.0_05  Java(TM) SE Runtime Environment (build 1.6.0_05-b13) Java HotSpot(TM) Client VM (build 10.0-b19, mixed mode, sharing)
  • 34.
  • 35.
    Estrutura de diretórioBin  Compilador e command line tools Db  Banco de dados Demo  Demos Include  Arquivos para métodos native Jre  Arquivos Java Runtime Env. Lib  Arquivos de bibliotecas Src  Código fonte das bibliotecas
  • 36.
  • 37.
    Tópicos Compilando umprograma em Java • String • Blocos e expressões • Operadores • Controle de execução • Arrays
  • 38.
    Compilando um programaJava Crie um diretório chamado c:\projetos\Welcome Abra o Notepad++ e digite o seguinte código : public class Welcome { public static void main(String[] args) { System.out.println(“Bem vindo ao mundo do Java”); } }
  • 39.
    Compilando um programaJava Salve este arquivo como Welcome.java dentro do diretório Welcome. Abra uma janela do DOS e vamos compilar e rodar nosso novo programa : javac Welcome.java java Welcome Resultado : Bem vindo ao mundo do Java
  • 40.
    Programando em Java Neste momento já possuímos um bom conhecimento sobre a linguagem e a plataforma Java, e já temos o JDK instalado, compilando e executando programas Java. Nesta parte veremos mais a fundo os conceitos básicos de programação, tal como tipos de dados, fluxo e loops.
  • 41.
    Programando em JavaUm programa Java simples. É o que exibimos no trecho código abaixo, e podemos até dizer que este programa tem o mínimo de código necessário para que o mesmo seja executado como um “executável”, como veremos mais a frente na anatomia de um programa Java : public class PrimeiroExemplo { public static void main(String[] args) { System.out.println(&quot;Nosso primeiro exemplo !!!&quot;); } }
  • 42.
    Anatomia de umprograma Java Traçaremos a antomia deste programa Java para tornarmos mais confortáveis com o framework provido pelo JDK. A primeira propriedade que devemos notar é que o Java é “case sensitive”, ou seja, o Java pode interpretar maiúsculo/minúsculo como erro se digitarmos MAIN ao invés de main, e o programa não poderá ser executado gerando um erro : C:\projetos\Welcome>javac PrimeiroExemplo.java C:\projetos\Welcome>java PrimeiroExemplo Exception in thread &quot;main&quot; java.lang.NoSuchMethodError: main
  • 43.
    Anatomia de umprograma Java Este erro é ocasionado por que a JVM espera que a classe possua um método chamado main com a seguinte assinatura : public static void main (String[] args) { … } Ao invés de : public static void MAIN (String[] args) { … }
  • 44.
    Anatomia de umprograma Java public  é um modificador de acesso, que rege como a classe pode ser acessada. Veremos modificadores a fundo mais a frente. class  é um identificador, um marcador que diz que o bloco inteiro é uma classe, e vale a pena apontar mais uma vez que tudo no Java é um tipo de classe específica. Em linhas muito gerais uma classe é um container de código que define o comportamento de uma aplicação, um bloco de código no qual todo programa é montado sobre, e tudo em um programa Java deve estar dentro de uma class.
  • 45.
    Anatomia de umprograma Java O padrão de nomeclatura de classes Java é o famoso “camel case”, onde usamos uma letra maiúscula sempre que passamos para uma nova parte de identificação da classe, para ficar mais claro podemos dar como exemplo duas classes, uma para registrar pagamentos e outra para listar balanço : public class RegistrarPagamento { .. } Public class ListarBalanco { .. }
  • 46.
    Anatomia de umprograma Java Outra coisa a ser notada são os colchetes { }, que delimitam o início e fim de um determinado bloco, e se abrimos um novo bloco, o mesmo deve ser fechado em algum momento abaixo. Caso não façamos isso receberemos um erro do compilador. public class NomeDaClasse { public static void main(String[] args) { //invocação de métodos, apis, e etc… } }
  • 47.
    Anatomia de umprograma Java Não precisamos nos preocupar com a palavra chave “static” neste momento, pois veremos estes e outros conceitos mais de perto nos próximos capítulos. O ponto a ser lembrado é que toda classe Java que desejamos que seja “executável” por meio de linha de comando deve ter a sintaxe acima declarada.
  • 48.
    Comentários // /*e */ /** e */
  • 49.
    Comentários /** *Este é nosso primeiro JavaDoc * @version 1.00 08/02/2009 * @author Márcio Alves Marinho */ public class SegundoExemplo { /* Este é nosso primeiro comentário longo Blah blah blah blah blah blah blah blah Blah blah blah blah blah blah blah blah Blah blah blah blah blah blah blah blah Blah blah blah blah blah blah blah blah Blah blah blah blah blah blah blah blah */ public static void main(String[] args) { //Um comentário simples de uma linha só System.out.println(&quot;Nosso segundo exemplo !&quot;); } }
  • 50.
    Tipos de DadosTipo Bits Bytes Mínimo Máximo byte 8 1 -2 7 2 7 -1 short 16 2 -2 15 2 15 -1 int 32 4 -2 31 2 31 -1 long 64 8 -2 63 2 63 -1 float 32 4 n/a n/a double 64 8 n/a n/a boolean Verdadeiro / falso True / false - - char caractere 1 String caracteres n/a n/a n/a
  • 51.
    String • O tipo String é um dos mais utilizados em qualquer linguagem de programação • Em Java, o tipo String é uma classe e não um tipo primitivo • No entanto, assim como os tipos primitivos, as strings podem ser criadas a partir de constantes, que, no caso, são seqüências de caracteres delimitados por aspas duplas. Ex: String s = “abc”; • Embora seja um objeto, não há a necessidade de utilização do operador new , como acontece com TODAS as outras classes.
  • 52.
    String • As instâncias de String são objetos imutáveis, ou seja, uma vez criada o seu valor não pode ser nunca alterado. • Isto acontece porque cada instância de String é criada apenas uma vez, e essa lista de strings é mantida pelo sistema. Se uma nova String for criada e o seu valor já estiver nesta lista, não há a necessidade de criar uma nova instância. Ex.: String s1, s2, s3; abc s1 = “abc”; s2 = “abc”; s3 = s1.toUpperCase();
  • 53.
    Variáveis Em Javatoda variável necessita ter um tipo, isto porque o Java é uma linguagem fortemente tipada, como vimos anteriormente : double salary; int vacationDays; long earthPopulation; boolean done; int i, j; // ambas inteiras Lembrando sempre que devemos inicializar uma variável antes de podermos utilizar a mesma para qualquer função : int vacationDays; // ERRO !--variable not initialized System.out.println(vacationDays);
  • 54.
    Variáveis Constantes sãovariáveis “imutáveis”, pois uma vez atribuido um valor a mesma, nunca mais poderemos mudá-lo : public class Constants { public static void main(String[] args) { final double CM_PER_INCH = 2.54; double paperWidth = 8.5; double paperHeight = 11; System.out.println(&quot;Paper size in centimeters: &quot; + paperWidth * CM_PER_INCH + &quot; by &quot; + paperHeight * CM_PER_INCH); } } A palavra chave final é quem torna a variável imutável ( constante ) !
  • 55.
  • 56.
    Operadores e atribuiçãoOperadores são utilizados para operações aritiméticas, ou seja, adição, subtração, multiplicação e divisão. Atribuição é a ação de determinar um valor a uma variável. O trabalho de atribuir um valor a uma variável parece ser algo muito simples, pois a única coisa que devemos fazer é colocar um valor no lado direito do sinal de igual “=” e a variável a esquerda do mesmo. Veremos capítulo de Orientação a Objetos que isto não é tão fácil quanto parece, mas neste momentos nos ateremos aos exemplos mais simples :
  • 57.
    Atribuição int x= 4; int a = 12; long l = 44444444444444; double d = 355.44;
  • 58.
    Operadores Como exmeplode operações podemos utilizar : x += 4; Que é o equivalente a : x = x + 4; Geralmente quando queremos fazer este tipo de operação direta utilizamos o símbolo antes do sinal de =, como *= ou %=). Exemplos de Incrementanto e decremento : int myVar = 15 ; myVar++ ; O valor de myVar será de 16, pois adicionamos mais um número ao mesmo. Outros exemplos : int m = 7; int n = 7; int a = 2 * ++m; // vale 16, e “m” vale 8 int b = 2 * n++; // vale 14, e “n” vale 8
  • 59.
    Operadores relacionais Operadoresrelacionais são aqueles que estão ligados a comparações, e o seu resultado é sempre true ou false, a caracterização dos mesmos é pelo sinal “==”, um duplo sinal de igual, como veremos a seguir : 3 == 7 ( false ) 3 != 7 ( true ) Nota : == ( igual ) != ou <> ( diferente ) Temos também os outros operadores, < menor que, > maior que, <= menor ou igual que, e >= maior ou igual que. O Java segue o C++ e usa os operadores && e || que são avaliados em “short circuit” . Ou seja, o segundo argumento não é avaliado se o primeiro já determinar o valor. A sintaxe é muito simples :
  • 60.
    Operadores relacionais expressão1&& expressão2 int x = 5; x != 0 && 1 / x > x + y; A segunda parte de nossa expressão nunca será avaliada, pois a primeira parte dela já satisfaz a condição que queremos. E da mesma forma poderemos utilizar o ou lógico ||. expressão1 || expressão2 Outro operador lógico utilizado pelo Java é o operador ternário, que tem sua sintaxe : condição1 ? expressão1 : expressão2
  • 61.
    Operadores relacionais Esteoperador avalia a condição1, e se a mesma for verdadeira (true), então o Java atribui a primeira expressão como resultado, senão a segunda : int x = 7; int y = 9; String resultado = x > y ? “X é Maior que Y” : “Não, X não é maior que Y” System.out.println(resultado); Operadores de bitwise Este tipo de operador funciona com qualquer tipo inteiro, e eles operam diretamente com os bits que compõem os inteiros. Isto significa que podemos utilizar um tipo de máscara para pegar os bits individuais de um número. Estes operadores são : & (“and”) | (“or”) ^ (“xor”) ~ (“not”)
  • 62.
    Operadores relacionais Comoexemplo temos : int fourthBitFromRight = (n & 8) / 8; Estes operadores tem uma particularidade, pois eles não são executados em short circuit, ou seja, a segunda expressão será avaliada de qualquer forma após a primeira. Por último temos os operadores de shift, que servem para mover os bits para a esquerda ou direita, eles são >> , << e >>> . int n = 6; int quartoBitDaDireita = (n & (1 << 3)) >> 3;
  • 63.
  • 64.
    Conversão entre tiposnuméricos Quando convertemos nossos valores para uma variável de capacidade superior, não temos problema algum com o conteúdo, mas quando tentamos fazer o inverso, devemos utilizar um cast , e lidar com a perda de dados : int n = 123456789; float f = n; System.out.println(&quot;n == &quot; + n); System.out.println(&quot;f == &quot; + f); n == 123456789 f == 1.23456792E8
  • 65.
    Conversão entre tiposnuméricos O exemplo acima nos mostra um cast automático entre um int e um float, nesse caso, a magnitude do número será mantida, mas haverá uma conversão automática para o tipo float e perderemos alguma precisão. Podemos ver na figura anterior que as setas cheias indicam uma conversão sem perda de precisão, e as setas pontilhadas conversão com possível perda de precisão. Quando dois valores usam um operador binário ( por exemplo n + f onde um operando é um integer e o outro um float), ambos operandos são convertidos para um tipo comum antes da operação ser executada.
  • 66.
    Conversão entre tiposnuméricos Se um dos operandos é do tipo double , o outro será convertido pra double . Se um dos operandos é do tipo float , o outro será convertido para float . Se um dos operandos é long , então o outro será convertido pra long . Em qualquer outro caso serão convertidos para int
  • 67.
    Conversão entre tiposnuméricos ( cast ) Nos exemplos anteriores vimos que as conversões são automáticas, pois estamos tentando colocar ó conteúdo de uma variável de menor grandeza em uma outra de maior grandeza. Esta conversão automática não acontece quando tentamos fazer o contrário, colocarmos o conteúdo de uma variável double dentro de um int , por exemplo : double x = 9.997; int nx = (int) x;
  • 68.
    Operadores e hierarquiade parênteses Devemos prestar atenção na precedência de operadores no momento em que montamos uma expressão, pois dependendo da disposição das expressões ou dos operadores podemos ter uma precedência diferente da esperada. Se nenhum parêntese for usado, então a precedência natural será. Como exemplo, o operador && tem uma precedência mais altar q || :
  • 69.
    Operadores e hierarquiade parênteses a && b || c é o mesmo que : (a && b) || c Por que o sinal += associa da direita para a esquerda, a expressão a += b += c significa a += (b += c) Ou seja, o valor de b += c (que é o valor de b depois da adição) é adicionado a.
  • 70.
    Operadores e hierarquiade parênteses Operadores Associatividade [] . () (invocação de método) Esquerda para a direita ! ~ ++ -- + (unary) – (unary) () (cast) new Direita para a esquerda * / % Esquerda para a direita + - Esquerda para a direita << >> >>> Esquerda para a direita < <= > >= instanceof Esquerda para a direita == != Esquerda para a direita & Esquerda para a direita ^ Esquerda para a direita | Esquerda para a direita && Esquerda para a direita || Esquerda para a direita ?: Esquerda para a direita = += -= *= /= %= &= |= ^= <<= >>= >>>= Esquerda para a direita
  • 71.
    Enumerations As vezesprecisamos representar um conjunto distintos de valores para um cálculo, ou precisamos de um conjunto restritivo. O Java, apartir de sua versão 5 disponibiliza um valioso recurso chamado enumeration ( enum ), que é um tipo especial para armazenar um conjunto de dados : enum Size { SMALL, MEDIUM, LARGE, EXTRA_LARGE }; Size s = Size.MEDIUM;
  • 72.
    Strings O tipoString é um dos mais utilizados em qualquer linguagem de programação. Em Java, o tipo String é uma classe e não um tipo primitivo, no entanto, assim como os tipos primitivos, as strings podem ser criadas a partir de constantes, que, no caso, são seqüências de caracteres delimitados por aspas duplas : String s = “abc”;
  • 73.
    Strings Embora sejaum objeto, não há a necessidade de utilização do operador new, como acontece com TODAS as outras classes. As instâncias de String são objetos imutáveis, ou seja, uma vez criada o seu valor não pode ser nunca alterado. Isto acontece porque cada instância de String é criada apenas uma vez, e essa lista de strings é mantida pelo sistema. Se uma nova String for criada e o seu valor já estiver nesta lista, não há a necessidade de criar uma nova instância.
  • 74.
    Strings String s1,s2, s3; s1 = “abc”; s2 = “abc”; s3 = s1.toUpperCase();
  • 75.
    String ( algunsmétodos ) Assinatura Descrição Exemplo char charAt(int posicao) Retorna o caractere em determinada posição na String String s = “Teste”; char c = s.charAt( //Resultado: ‘s’ int indexOf(String s2) Retorna a posição da primeira ocorrência de s2 na String String s = “Teste”; int pos = s.indexOf(“st”); //Resultado: 2 int lastIndexOf(String s2) Retorna a posição da última ocorrência de s2 na String String s = “Teste”; int pos = s.lastIndexOf(“e”); //Resultado: 4 int length() Retorna o tamanho da String String s = “Teste”; int tam = s.length(); //Resultado: 5 String substring(int pos1, int pos2) Retorna um pedaço da string que vai de pos1 a pos2 String s = “Teste”; String s2 = s.substring(1, 4); //Resultado: “est” String toUpperCase() Converte para maiúsculo String s = “Teste”; String s2 = s.toUpperCase(); //Resultado: ‘TESTE’ String toLowerCase() Converte para minúsculo String s = “Teste”; String s2 = s.toLowerCase(); //Resultado: ‘teste’ String trim() Limpa os espaços no início e no final da String String s = “ Teste da a ”; String s2 = s.trim(); //Resultado: “Teste da a”
  • 76.
    String ( comparações) Comparação de Strings : Ao comparar duas Strings utilize sempre o método equals() em vez do operador ==. Isto é necessário porque o == compara as referências, enquanto que o equals() compara os valores das referências: String s1 = “a”; String s2 = “a”; if(s1 == s2){ //De vez em quando funciona  } if(s1.equals(s2)){ //Forma correta de comparar } “ Hello&quot;.equalsIgnoreCase(&quot;hello&quot;)
  • 77.
    String ( Métodos) String greeting = &quot;Hello&quot;; int n = greeting.length(); // 5. char first = greeting.charAt(0); // first is 'H' char last = greeting.charAt(4); // last is 'o' int cp = greeting.codePointAt(index); Documentação completa on-line de String : http://java.sun.com/javase/6/docs/api/java/lang/String.html
  • 78.
  • 79.
    Orientação a ObjetosO paradigma de orientação a objetos ( OO ) é o assunto mais quente na forma de desenvolver software na “ atualidade”. O mesmo substituiu a famosa forma estruturada de desenvolver sistemas, e podemos citar por exemplo a linguagem C ou Clipper. Java é uma linguagem totalmente orientada a objetos. Um programa orientado a objetos é composto de objetos, e cada objeto tem uma funcionalidade específica que é exposta para os usuários, ou ainda temos outras funcionalidades que são comportamentos escondidos.
  • 80.
    Classes Uma classeé um template, ou ainda podemos considerá-la uma forma na qual objetos são “cozinhados como bolos”, ou seja todo objeto terá o formato da forma que foi produzido. Como vimos anteriormente, qualquer código escrito em Java é SEMPRE escrito dentro de uma classe. As bibliotecas standard do Java possuem uma gama imensa de classes para tratamento de interface de usuário, datas, programação em rede e muito mais.
  • 81.
  • 82.
    Classes Quando construímosum objeto apartir de uma classe, podemos dizer que o objeto é uma instância daquela classe. import java.util.Date; public class Contrato { Date dataRegistro; Date dataVencimento; double valor; void adicionarVencimento (int valor) { //implementação aqui } double calcularValor (int valor) { //implementação aqui return 0; } }
  • 83.
    Classes ( propriedades) Uma classe possui algumas propriedades básicas, que podemos classificar da seguinte forma : Encapsulamento – Também conhecido como “esconder a informação”, que nada mais é do que a combinação de estado e comportamento, empacotados para esconder os detalhes da implementação do usuário. Campos de instância – Os dados vivem aqui dentro, que são variáveis declaradas dentro das classes. Métodos – São os procedimentos de acesso aos dados contidos nos campos de instância. Para termos estas propriedades acima NUNCA devemos invocar um campo de instância diretamente, pelo contrário, devemos sempre invocar um método que realizará o trabalho necessário para retornar-nos o valor desejado.
  • 84.
    Objetos Objetos sãoa materialização de uma classe, ou seja, como dissemos anteriormente uma classe é como uma forma para criarmos novos objetos que farão parte do nosso programa. Objetos possuem as seguintes propriedades: Comportamento – Simplesmente aquilo que o objeto é capaz de realizar. O estado do objeto – Como o objeto reage quando invocamos seus métodos. Identidade do objeto – Como o objeto de distingue dos outros objetos que podem ter o mesmo estado e comportamento;
  • 85.
    Identificando classes Emprogramas procedurais nós iniciamos o programa com algum processo no topo, tipo “principal”, mas em programação OO não existe este tipo de mecanismo. Na programação OO precisamos criar classes e colocar métodos neles. Uma regra para encontrarmos classes é olhando para os substantivos de nossos use-cases ou estórias, e os métodos serão os verbos.
  • 86.
    Identificando classes Ordemde Pagamento Item Endereço de envio Pagamento Pedido Conta
  • 87.
    Relacionamento entre classesDependência ( Usa uma classe ) Agregação ( Tem uma classe ) Herança ( É um tipo de classe )
  • 88.
  • 89.
    Criando e usandoobjetos Para podermos usar objetos precisamos antes criá-los apartir de sua classe : java.util.Date data = new java.util.Date(); System.out.println(data); String s1 = new java.util.Date().toString(); System.out.println(s1);
  • 90.
    Atributos Atributos sãoas variáveis de instância que existem dentro das classes para compor seu estado, ou seja, são as propriedades que uma classe carrega para compor sua informação.
  • 91.
    Atributos public classPessoa { long id; String nome; Date dataNascimento; }
  • 92.
    Métodos Como vimosrapidamente anteriormente, os métodos são funções utilizadas para acessar um determinado comportamento da classe.
  • 93.
    Métodos import java.util.Date;/** * @author Marcio */ public class Pessoa { private long id; private String nome; private Date dataNascimento; public long getId() { return id; } public void setId(long id) { this.id = id; }
  • 94.
    Métodos public StringgetNome() { return nome; } public void setNome(String nome) { this.nome = nome; } public Date getDataNascimento() { return dataNascimento; } public void setDataNascimento(Date dataNascimento) { this.dataNascimento = dataNascimento; } }
  • 95.
    Construtores Construtores sãométodos especiais para criar novas instâncias de objetos. Estes métodos possuem o mesmo nome da classe, mas sem retorno. Podemos ainda usar os construtores para atribuirmos um estado inicial ao objeto, se assim o desejarmos.
  • 96.
    Construtores import java.util.Date;public class Pessoa { private long id; private String nome; private Date dataNascimento; //Construtor defaul para a classe pessoa public Pessoa() { } public Pessoa(long id, String nome, Date dataNascimento) { this.id = id; this.nome = nome; this.dataNascimento = dataNascimento; }
  • 97.
    Exemplo public classPessoa { ... public static void main(String[] args) { Pessoa p1 = new Pessoa(); Pessoa p2 = new Pessoa(1, &quot;Marcio Alves Marinho&quot;, new Date(&quot;14/12/1974&quot;)); System.out.println(p1.getId() + &quot; # &quot; + p1.getNome() + &quot; # &quot; + p1.getDataNascimento()); System.out.println(p2.getId() + &quot; # &quot; + p2.getNome() + &quot; # &quot; + p2.getDataNascimento()); } ... } Resultado : 0 # null # null 1 # Marcio Alves Marinho # Sat Dec 14 00:00:00 BRT 1974
  • 98.
    Pacotes Pacotes sãouma forma de organizar nosso código, no fundo pacotes nada mais são do que uma estrutura hierárquica de diretórios que o Java usa para uma melhor organização do código fonte. Podemos então organizar nosso código por camadas, nível de importância, ou qualquer outra forma que for mais conveniente.
  • 99.
  • 100.
  • 101.
    Pacotes Um pacotegenérico seguindo a convenção proposta pela Sun, diz que o prefixo de um pacote único deve estar escrito em minúsculo, primeiramente com o domínio, e os componentes subsequentes sendo o nome da organização, divisão, departamento, projeto, máquina ou login name. com.sun.eng com.apple.quicktime.v2 edu.cmu.cs.bovik.cheese br.com.jetsoftware.domain
  • 102.
    Especificando e ImportandoPacotes O Comando Package serve para especificar a que pacote uma determinada classe ou inteface pertence. O Comando Import torna disponível para o pacote corrente, as classes dos pacotes mencionados após a cláusula import.
  • 103.
    Resolvendo Nomes eColisões Quando dois ou mais pacotes são importados para um determinado programa e eles contém classes de mesmo nome isso não representa problemas de colisão caso tais classes não sejam utilizadas No caso de ser necessário fazer referência a uma das classes citadas acima, isso deve ser feito de modo explícito para que não haja problemas de colisão. Caso isso não seja feito, o compilador enviará uma mensagem de erro, forçando que essa chamada seja explicitada. java.util.Date / java.sql.Date
  • 104.
    Modificadores de acessoclass MinhaClass { } Apesar desta declaração funcionar a mesma não é ideal, pois como já vimos necessitamos organizar o nosso código na forma de pacotes, e precisamos ainda organizar o acesso as nossas classes. Ou seja, precisamos definir quem poderá acessar nossas classes e de onde.
  • 105.
    Modificadores De acesso: public, protected, private. Não relacionados a acesso : strictfp, final, e abstract.
  • 106.
    Modificadores ( emmiúdos ) Podemos exemplificar o acesso a classes, se dissermos por exemplo que a class A tem acesso a outra classe B, e isto significa que a classe A pode fazer uma destas 3 coisas : Criar uma instância de B Extender B ( ser uma sub-classe de B ) Acessar certos métodos e variáveis dentro de B, dependendo do controle de acesso destes métodos e variáveis.
  • 107.
    Modificadores ( emmiúdos ) Na verdade acesso significa visibilidade . Se a classe A não puder ver a classe B, então o nível de acesso dos métodos e variáveis dentro de B não farão a menor diferença, pois a classe A não terá como acessá-los de forma alguma.
  • 108.
    Acesso Default Default– Uma classe com acesso default não possui declaração de modificadores, e este é o acesso default quando não especificamos nenhum. Default também é conhecido como “package level access”, por que as classes com acesso default podem somente ser vista por classes dentro do mesmo pacote.
  • 109.
    Default, exemplo ArquivoBeverage.java package cert; class Beverage { } Arquivo Tea.java package exam.stuff; import cert.Beverage; class Tea extends Beverage { } Se tentarmos compilar.... Can't access class cert.Beverage. Class or interface must be public, in same package, or an accessible member class. import cert.Beverage;
  • 110.
    Acesso Public Public– Uma classe que é declarada com a palavra chave public dá acesso a todas classes de todos pacotes. Todas as classes do universo ( e outros universos paralelos) tem acesso a classes “public” .
  • 111.
    Public, exemplo packagecert; public class Beverage { } Agora a compilação funcionará !
  • 112.
    Private Como opróprio nome já diz, “private” é um modificador para acesso privado, ou seja, ninguém externamente a classe tem acesso ao que for definido com esse modificador de acesso.
  • 113.
    Final Classes definidascomo “final” não podem ter sub-classes ! Em outras palavras, outras classes não poderão extendê-las, e qualquer tentativa receberemos um erro do compilador.
  • 114.
    Final, exemplo packagecert; public final class Beverage { public void importantMethod() { } } package exam.stuff; import cert.Beverage; class Tea extends Beverage { } Se tentarmos compilar de novo... Can't subclass final classes: class cert.Beverage class Tea extends Beverage{ 1 error
  • 115.
    Abstract Classes declaradascomo “abstract” não podem ser instanciadas, isto acontece porque as mesmas são usadas como template para outras classes, ou seja, elas já nasceram para conterem algum comportamento base e serem extendidas e terem um refinamento nas sub-classes.
  • 116.
    Abstract, exemplo abstractclass Carro { private double price; private String model; private String year; public abstract void goFast(); public abstract void goUpHill(); public abstract void impressNeighbors(); // Mais métodos... } Se tentarmos a façanha de instanciá-la : Carro.java:7: class Car is an abstract class. It can't be instantiated. Car x = new Car(); 1 error
  • 117.
    Abstract, exemplo packagecomida; public abstract class Fruta{ /* any code you want */ } import comida; class Banana extends Fruta{ /* any code you want */ }
  • 118.
    Herança Extends éa palavra chave usada para podermos definir herança entre classes no Java. Toda sub-classe possui todas as características da classe base que foi herdada, isso funciona quase que exatamente como se fosse herança genética, onde o herdeiro tem as características de seus pais, cor dos olhos, cabelo, feições e etc. Simplesmente usamos a seguinte notação : ClasseB extends ClasseA
  • 119.
    Herança Com herançapodemos modificar todo o comportamento de uma cadeia de classes alterando somente o código da classe base. Por isso devemos ter muito cuidado quando fazemos o design da mesma, pois um bug ou código mal-feito se extenderá por toda hierarquia de classes.
  • 120.
    Interfaces Interfaces sãouma alternativa a herança, qualquer classe pode “assinar” ou seja, implementar os métodos definidos na mesma, sem dizer como o farão. Elas são também vistas como contratos, pois qualquer classe que implementar a interface serão obrigadas a implementar seus métodos. Interfaces podem ser implementadas por qualquer classe, de qualquer lugar ou hierarquia. Isso nos permite mudar radicalmente a característica de diferentes classes dando a elas a mesma característica.
  • 121.
  • 122.
    Interfaces, exemplo packagebr.com.jetsoftware.domain; import java.util.Date; public interface IContratoFinanceiro { double calcularBalanco(); double calcularHistorico(Date dataInicial, Date dataFinal); double saldoMensal(int mes, int ano); }
  • 123.
    Interfaces, exemplo packagebr.com.jetsoftware.domain; import java.util.Date; public class Caixa implements IContratoFinanceiro { public double calcularBalanco() { //seus cálculos return 2; } public double calcularHistorico(Date dataInicial, Date dataFinal) { //seus cálculos if (dataFinal.getTime() < dataInicial.getTime()) { return 0; } return 55.3; } public double saldoMensal(int mes, int ano) { //seus cálculos return 30; } }
  • 124.
    Interfaces, exemplo packagebr.com.jetsoftware.domain; import java.util.Date; public class ContratoFinanceiro implements IContratoFinanceiro { public double calcularBalanco() { //seus cálculos return 7; } public double calcularHistorico(Date dataInicial, Date dataFinal) { //seus cálculos return 99.4; } public double saldoMensal(int mes, int ano) { //seus cálculos return 652.478; } }
  • 125.
    Interfaces Interfaces podemser vistas como classes 100% abstratas. Nós dissemos 100% abstratas pois enquanto classes abstratas podem definir métodos abstratos e não abstratos, interfaces por sua vez podem somente definir métodos abstratos. Outra diferença que existe entre interfaces e classes é que interfaces possuem pouca flexibilidade no tocante como métodos e variáveis são definidas. Todos os métodos de uma interface são implicitamente públicos e abstratos. Ou seja, não precisamos digitar as palavras chave public ou abstract, pois os mesmos serão sempre public e abstract. Todas as variáveis dentro de uma interface devem ser public, static, e final, ou seja, interfaces podem declarar somente constantes.
  • 126.
    Interfaces Métodos deInterfaces não podem ser static. Já que métodos de interfaces são abstratos, eles não podem ser marcados como final, strictfp, or native. Uma interface pode estender ( extends ) uma ou mais interfaces. Uma interface não pode extender nada além de outra interface. Uma interface não pode implementar outra interface ou classe. Uma interface deve ser declarada com a palavra chave interface. Interfaces podem ser usadas de forma polimórfica.
  • 127.
    Modificadores de acessopara atributos Métodos e variáveis permitem o acesso quase que da mesma forma. Existem 4 tipos de acesso distintos para atributos : Public Protected Default private
  • 128.
    Modificadores de acessopara atributos Como vimos anteriormente, a proteção default é aquela que recebemos quando não digitamos nenhum modificador de acesso na declaração do membro. Os modificadores default e protected tem quase que o mesmo comportamento, exceto por uma diferença que será mencionada mais a frente.
  • 129.
    Modificadores de acessopara atributos Public – Todos podem acessar. Private – Só pode ser acessado pela mesma classe na qual o o atributo foi definido. Default – Pode ser acessado se a classe que está tentando acessá-lo estiver dentro do mesmo pacote. Protected – Pode ser acessado por qualquer sub-classe mesmo que estejam em pacotes diferentes. Variáveis locais a um método não podem ter modificadores de acesso. Isto por que elas já são privadas ao método.
  • 130.
    Encapsulamento Muito importante: conseguimos usar objetos da classe ContaCorrente sem saber nada sobre como a mesma foi implementada! • Isso se chama Ocultação de Informação e é muito importante na programação • É a forma básica de lidar com a complexidade dos programas • É comum usarmos &quot;private&quot; como especificador de controle de acesso para atributos de uma classe
  • 131.
    Encapsulamento Também podemosdizer que a classe ContaCorrente encapsula dados e comportamento em cima desses dados Os dados são os atributos escondidos de nós (saldo, histórico de transações, dados do titular, etc) • O comportamento são os métodos que podemos chamar para manipular o objeto • Só podemos “alterar&quot; o estado do objeto através de seus métodos.
  • 132.
    Overloading e RedefiniçãoEm Java, o que identifica unicamente o método é o nome e a lista de argumentos. A técnica de definir métodos de mesmo nome, com listas de argumentos diferentes é chamado de &quot; method overloading &quot; . Quando uma classe B define um método usando o mesmo nome, tipo de retorno e argumentos de um método de uma classe ancestral A , este método redefine o método da classe ancestral.
  • 133.
    Overloading e Redefiniçãopublic class ContaCorrente { public ContaCorrente ( String t, String c, int n ) { numero = n; titular = new Titular ( t, c ); } public ContaCorrente ( Titular t, int n ) { numero = n; titular = t; } }
  • 134.
    Overloading e Redefiniçãopublic class Animal { public void comer() { System.out.println(&quot;Eu como de forma genérica !&quot;); } public void respirar() { System.out.println(&quot;Eu respiro de forma genérica !&quot;); } }
  • 135.
    Overloading e Redefiniçãoclass Cao extends Animal { public void comer() { System.out.println(&quot;Eu com o foçinho enfiado na minha tijela.&quot;); } public void respirar() { System.out.println(&quot;Eu respiro pelos pulmoões e somente fora da agua.&quot;); } }
  • 136.
    Overloading e Redefiniçãoclass SerHumano extends Animal { public void comer() { System.out.println(&quot;Eu como sentado na mesa, com garfo e faca.&quot;); } public void respirar() { System.out.println(&quot;Eu respiro pelos pulmoões e somente fora da agua.&quot;); } }
  • 137.
    Visibilidade de membrosVisibilidade Public Protected Default Private De dentro da mesma classe Sim Sim Sim Sim De qualquer classe dentro do mesmo pacote Sim Sim Sim Não De uma sub-classe dentro do mesmo pacote Sim Sim Sim Não De uma sub-classe fora do pacote Sim Sim, por herança Não Não De qualquer não sub-classe fora do pacote Sim Não Não Não
  • 138.
    Polimorfismo A palavra&quot;polimorfismo&quot; significa &quot;Que apresenta várias formas“ Numa linguagem de programação, o polimorfismo permite tratar objetos de classes diferentes do mesmo jeito (com as mesmas chamadas a métodos), porque elas têm o mesmo comportamento As classes fazem a mesma operação (método), mas de forma diferente – &quot;O quê&quot; é igual – &quot;Como&quot; é diferente
  • 139.
    Polimorfismo public staticvoid main(String[] args) { Animal a1 = new Cao(); Animal b1 = new SerHumano(); a1.comer(); b1.comer(); } Eu com o foçinho enfiado na minha tijela. Eu como sentado na mesa, com garfo e faca.
  • 140.
  • 141.
    Exercícios Pense sobrea implementação do seguinte programa, dado em pseudo-código Programa de Controle Bancácio 1. Abra uma conta de número 1 para João com CPF 309140605-06. Nesta conta, deposite R$1000,00. 2. Abra uma conta de número 2 para Ana com CPF 123456789-01. Transfira R$400,00 da conta de João para a conta de Ana. 3. Imprima o saldo da conta de João. 4. Imprima o saldo da conta de Ana.
  • 142.
    Exercícios Quais asclasses que podemos identificar neste exemplo? • Quais são os atributos relevantes para o nosso modelo do mundo real? • Quais os comportamentos devem ser modelados?
  • 143.
    Controle de FluxoCapítulo 4
  • 144.
    Controle de fluxoÉ impossível imaginarmos algum programa que não precise executar nenhum código condicional ! O controle de fluxo é uma parte chave que qualquer linguagem tem, e o Java por sua vez oferece várias formas de fazer isto. Temos declarações de “if”, “for” para loops e muito mais.
  • 145.
    Controle de fluxoAs declarações “if” e “switch” são tipo de controles condição/decisão que permite aos nossos programas se comportarem diferentemente em uma “encruzilhada”, dependendo do resultado do teste lógico. O Java também provê três declarações de loop diferentes ( for, while e do ). As exceptions por sua vez não dão uma forma elegante de tratarmos error de run-time. Temos ainda as assertions, que foram adicionados na versão 1.4 do Java, que nos permite debugarmos e checarmos condições durante nossos testes.
  • 146.
    IF A formamais básica de “IF” é : if (booleanExpression) { System.out.println(&quot;Inside if statement&quot;); } Onde a expressão deve ser verdadeira para que o código entre as chaves seja executado.
  • 147.
    IF / ELSEif (x > 3) { System.out.println(&quot;x é maior que 3&quot;); } else { System.out.println(&quot;x não é maior que 3&quot;); }
  • 148.
    Else opcional if(x > 3) { y = 2; } z += 8; a = y + x;
  • 149.
    Prática ruim if(x > 3) // horroroso ! y = 2; z += 8; a = y + x;
  • 150.
    Aninhando IF/ELSE if(price < 300) { buyProduct(); } else { if (price < 400) { getApproval(); } else { dontBuyProduct(); } }
  • 151.
    Aninhando IF/ELSE (re-escrevendo) if (price < 300) { buyProduct(); } else if (price < 400) { getApproval(); } else { dontBuyProduct(); }
  • 152.
    Switch Um switché uma forma de simular vários “ ifs” de uma só vez.
  • 153.
    Switch, exemplo intx = 3; if(x == 1) { System.out.println(&quot;x equals 1&quot;); } else if(x == 2) { System.out.println(&quot;x equals 2&quot;); } else if(x == 3) { System.out.println(&quot;x equals 3&quot;); } else { System.out.println(&quot;No idea what x is&quot;); }
  • 154.
    Switch, exemplo ( código mais limpo ) int x = 3; switch (x) { case 1: System.out.println(&quot;x is equal to 1&quot;); break; case 2: System.out.println(&quot;x is equal to 2&quot;); break; case 3: System.out.println(&quot;x is equal to 3&quot;); break; default: System.out.println(&quot;Still no idea what x is&quot;); }
  • 155.
    Expressão legal paraswitch/case switch (expression) { case constant1: code block case constant2: code block default: code block }
  • 156.
    Switch/case, break Obreak não é obrigatório na construção do switch, mas se não utilizarmos um break e a expressão for avaliada pare true, então o Java continuará avaliando as próximas expressões.
  • 157.
    Switch/case, break inta = 10; switch (a) { case 10: System.out.println(&quot;O valor é 10 !&quot;); case 20: System.out.println(&quot;O valor é 20 !&quot;); case 30: System.out.println(&quot;O valor é 30 !&quot;); default: System.out.println(&quot;Nada feito, é default mesmo !&quot;); } Resultado trágico : ============== O valor é 10 ! O valor é 20 ! O valor é 30 ! Nada feito, é default mesmo !
  • 158.
    Loops ( while) O loop “while” é utilizado em cenários onde não sabemos quantas vezes a declaração será repetida, então continuaremos no loop enquanto a condição for verdadeira. A expressão utilizada deverá sempre ser uma expressão lógica, ou seja, retornar true ou false ! while (expression) { // do stuff } Ou ainda : int x = 2; while(x == 2) { System.out.println(x); ++x; }
  • 159.
    Loops (do ...While) do { System.out.println(&quot;Inside loop&quot;); } A declaração do/while é diferente de while, pois a expressão interna será executada antes da checagem.
  • 160.
    Loops ( for) A declaração for nos permite declarar uma ou mais variáveis do mesmo tipo dentro dos parênteses depois da declaração for, se declarmos mais de uma variável, então necessitamos delimitá-las por vírgula : for (int x = 10, y = 3; y > 3; y++) { }
  • 161.
  • 162.
    Introdução I/O fornececomunicação com dispositivos – arquivos, console, networks, etc. • Onde o acesso aos dados podem ser: – sequenciais, aleatório, binário, caracteres, linhas, blocos, objetos, etc.
  • 163.
    I/O Streams I/O Stream representam uma fonte de input e um destino de output. Uma stream pode representar diferentes tipos de fontes e destinos, incluindo arquivos no disco, dispositivos, outros programas e arrays em memória. Streams suportam diferentes tipos de dados, incluindo simples bytes, tipos primitivos, caracteres de localização, e objetos. Algumas streams simplesmente passam dados, outras manipulam e transformam os dados de maneira útil. Não importa muito como elas trabalham internamente, todas streams representam o mesmo modelo simples de programação : Uma stream é uma sequencia de dados. Um programa usa uma input stream para ler dados de uma origem para um destino, um item por vez.
  • 164.
    I/O Streams Lendo a informação para um programa :
  • 165.
    I/O Streams Umprograma usa uma output stream para escrever dados para um destino, um item por vez :
  • 166.
  • 167.
    Byte Streams Os programas usam byte streams para input e output de bytes de 8 bits. Todas as classes de byte stream são descendentes de InputStream e OutputStream. Existem muitas classes byte stream, e para demonstrarmos como os byte stream trabalham, iremos utilizar o file I/O byte stream ; FileInputStream e FileOutputStream.
  • 168.
    Usando um ByteStream import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class CopyBytes { public static void main(String[] args) throws IOException { FileInputStream in = null; FileOutputStream out = null; try { in = new FileInputStream(&quot;xanadu.txt&quot;); out = new FileOutputStream(&quot;outagain.txt&quot;); int c; while ((c = in.read()) != -1) { out.write(c); } } finally { if (in != null) { in.close(); } if (out != null) { out.close(); } } } }
  • 169.
    Usando um ByteStream O programa CopyBytes lê o conteúdo do arquivo dentro de um loop e escreve para uma output stream, um byte de cada vez.
  • 170.
    Usando um ByteStream O método read retorna um int, isto para nos permitir usar o resultado -1 para sabermos que o programa atingiu o fim da stream. Sempre fecha streams, isto é muito importante, pois esta prática garante que não ficaremos com recursos presos sem necessidade. Byte Streams devem ser utilizadas somente para I/O primitivos.
  • 171.
    Character Streams O Java armazena caracteres usando convenção Unicode. Stream de caracteres para I/O são traduzidas automáticamente de e para o conjunto de caracteres locais. Para a maioria das aplicações, I/O com caracteres é tão simples quanto com byte streams. O input e output é feito com classes de stream que fazem a tradução automética de conjuntos de caracteres. Um programa que usa streams de caracteres no lugar de byte streams adapta-se automáticamente aos caracteres locais e está pronta para internacionalização ( sem trabalho extra).
  • 172.
    Usando Character StreamsTodas as streams de caractere são descendentes de Reader e Writer. Como acontece com byte streams, existem character streams que são especializadas para fazer I/O: FileReader e FileWriter
  • 173.
    Usando Character Streamsimport java.io.FileReader; import java.io.FileWriter; import java.io.IOException; public class CopyCharacters { public static void main(String[] args) throws IOException { FileReader inputStream = null; FileWriter outputStream = null; try { inputStream = new FileReader(&quot;xanadu.txt&quot;); outputStream = new FileWriter(&quot;characteroutput.txt&quot;); int c; while ((c = inputStream.read()) != -1) { outputStream.write(c); } } finally { if (inputStream != null) { inputStream.close(); } if (outputStream != null) { outputStream.close(); } } } }
  • 174.
    Usando Character StreamsO programa CopyCharacters é muito semelhante ao CopyBytes. A grande diferença é que CopyCharacters usa FileReader e FileWriter para input e output no lugar de FileInputStream e FileOutputStream.
  • 175.
    Streams de caracteresque usam Byte Streams Existe também a possibilidade de fazer “wrapper” para byte streams. A stream de caracteres usa uma byte stream para executar o I/O físico, enquanto a stream de caracteres trata a tradução entre os caracteres e bytes. FileReader por exemplo usa FileInputStream, enquanto FileWriter usa FileOutputStream.
  • 176.
    I/O em linhasinteiras I/O com caracteres geralmente é utilizado com conjuntos maiores do que com caracteres únicos. Um conjunto comum é uma linha, que são vários caracteres agrupados com um terminador de linha no final. Um terminador de linha pode ser um carriage-return/line-feed (“\r\n”), carriage-return (“\r”), ou line-feed (“\n”).
  • 177.
    I/O em linhasinteiras Modificaremos o nosso exemplo anterior, afim de torná-lo um I/O orientado a linhas. Usaremos também duas novas classes, BufferedReader e PrintWriter.
  • 178.
    I/O em linhasinteiras import java.io.FileReader; import java.io.FileWriter; import java.io.BufferedReader; import java.io.PrintWriter; import java.io.IOException; public class CopyLines { public static void main(String[] args) throws IOException { BufferedReader inputStream = null; PrintWriter outputStream = null; try { inputStream = new BufferedReader(new FileReader(&quot;xanadu.txt&quot;)); outputStream = new PrintWriter(new FileWriter(&quot;characteroutput.txt&quot;));
  • 179.
    I/O em linhasinteiras String l; while ((l = inputStream.readLine()) != null) { outputStream.println(l); } } finally { if (inputStream != null) { inputStream.close(); } if (outputStream != null) { outputStream.close(); } } } }
  • 180.
    Buffered Streams Os exemplos anteriores que utilizamos são do tipo unbuffered . Ou seja, toda leitura ou escrita é tratada diretamente pelo sistema operacional. Isto pode fazer com que o programa seja menos eficiente, visto que toda requisição desencadeia acesso ao disco, rede ou qualquer outra ação que seja cara. Para reduzir este tipo de sobrecarga, a plataforma Java implementa o I/O buferizado. Input streams buferizadas lêem dados da memória de uma área conhecida como buffer, a API nativa é invocada somente quando o buffer estiver vazio. Da mesma forma output streams escrevem dados para um buffer, e a API nativa é invocada somente quando o buffer estiver cheio. Um programa pode converter uma stream não buferizada para uma buferizada, usando wrappers, onde passamos o objeto não buferizado para o construtor de uma stream buferizada.
  • 181.
    Buffered Streams inputStream= new BufferedReader(new FileReader(&quot;xanadu.txt&quot;)); outputStream = new BufferedWriter(new FileWriter(&quot;characteroutput.txt&quot;)); Existem quatro classes de stream buferizadas para fazermos wrapper de streams não buferizadas : BufferedInputStreame BufferedOutputStream criam bute streams buferizados, enquanto BufferedReader e BufferedWriter criam streams de caracteres buferizados.
  • 182.
    Descarregando Buffered StreamsDevemos escrever os dados do buffer de tempos em tempos, pois é exatamente isso que queremos, escrever os dados em um meio persistente. Esta ação é conhecida como flushing o buffer. Algumas classes de output buferizadas suportam autoflush , especificado por um argumento opicional no construtor. Quando o autoflush está ligado, alguns eventos causam o descarregamento do buffer ( flush ). Por exemplo, um autoflush em objetos PrintWriter acontecem em cada invocação do método println ou format. Para realizar o flush na stream de forma manual, basta usar o método flush.
  • 183.
    Escaneando e formatandoInput e Output as vezes envolvem a tradução de formatos estranhos para formato humano. Para a realização desta tarefa o Java provê duas APIs. A API scanner quebra o input em tokens individuais associados com bits de dados. A API formatting monta os dados em formato bem formatado pronto para a leitura humana.
  • 184.
    Escaneando e formatandoimport java.io.*; import java.util.Scanner; public class ScanXan { public static void main(String[] args) throws IOException { Scanner s = null; try { s = new Scanner(new BufferedReader(new FileReader(&quot;xanadu.txt&quot;))); while (s.hasNext()) { System.out.println(s.next()); } } finally { if (s != null) { s.close(); } } } }
  • 185.
    Escaneando e formatandoIn Xanadu did Kubla Khan A stately pleasure-dome ... Usando um token diferente : s.useDelimiter(&quot;,\\s*&quot;)
  • 186.
    Traduzindo tokens individuaisA classe Scanner suporta todos os tipos primitivos do Java ( com exceção de char ), e também BigInteger e BigDecimal. Também valores numéricos podem usar diferentes separadores.
  • 187.
    Traduzindo tokens individuaisimport java.io.FileReader; import java.io.BufferedReader; import java.io.IOException; import java.util.Scanner; import java.util.Locale; public class ScanSum { public static void main(String[] args) throws IOException { Scanner s = null; double sum = 0; try { s = new Scanner(new BufferedReader(new FileReader(&quot;usnumbers.txt&quot;))); s.useLocale(Locale.US); while (s.hasNext()) { if (s.hasNextDouble()) { sum += s.nextDouble(); } else { s.next(); } } } finally { s.close(); } System.out.println(sum); } }
  • 188.
    Traduzindo tokens individuaisArquivo de exemplo : numeros.txt 8.2 32,767 3.34159 1,100,000.1
  • 189.
    Formatação Stream deobjetos que implementam formatação são instâncias de PrintWriter (stream de caracter), ou PrintStream (classe de byte stream). Como todos os objetos de streams de bytes ou caracteres, instâncias de PrintStream e PrintWriter implementam o conjunto padrão de métodos write para output de byte ou caractere. Em adição, ambos PrintStream e PrintWriter implementam o mesmo conjunto de métodos para conversão de dados interno para output formatado. Onde dois níveis de formatação são providos : print e println formatam valores individuais numa maneira padrão format formata quase qualquer valor numérico baseado em uma string de formatação, com várias opções de formatação.
  • 190.
    Formatação public classRoot { public static void main(String[] args) { int i = 2; double r = Math.sqrt(i); System.out.print(“A raiz quadrada de &quot;); System.out.print(i); System.out.print(&quot; é &quot;); System.out.print(r); System.out.println(&quot;.&quot;); i = 5; r = Math.sqrt(i); System.out.println(&quot; A raiz quadrada de &quot; + i + &quot; é &quot; + r + &quot;.&quot;); } } A raiz quadrada de 2 é 1.4142135623730951. A raiz quadrada de 5 é 2.23606797749979.
  • 191.
    O método formatO método format formata múltiplos argumentos baseado em uma string. public class Root2 { public static void main(String[] args) { int i = 2; double r = Math.sqrt(i); System.out.format(&quot;The square root of %d is %f.%n&quot;, i, r); } }
  • 192.
    O método formatComo usado no exemplo anterior, todos os formatadores começam com um % e terminam em 1 ou 2 caracteres que especificam o tipo de output formatado. d – formata um valor inteiro como decimal f - formata um valor ponto flutuante como decimal n – gera um delimidador que é específico de uma plataforma x – formata um inteiro como hexadecimal s - formata qualquer valor como string tB – formata um inteiro como um “local-specific” nome de mês
  • 193.
    O método formatpublic class Format { public static void main(String[] args) { System.out.format(&quot;%f, %1$+020.10f %n&quot;, Math.PI); } } Resultado : 3.141593, +00000003.1415926536
  • 194.
    O método formatOs elementos adicionais de nossa formatação são todos opcionais. Precision – para pontos flutuantes é a precisão do valor formatado ( pode ser truncado). Width – o tamanho mínimo do valor formatado. Flags – especifica formatação adicional, neste caso o número será sempre exibido com o sinal + Argument Index – nos permite explicitamente encontrarmos um determinado argumento.
  • 195.
    I/O na linhade comandos Um programa também pode rodar na linha de comandos para interagir com o usuário final. O Java suporta este tipo de interação atráves do Console. Uma alternativa avançada as streams é o Console. Este objeto possui a maior parte das features disponíveis pelas stream standard, e outros objetos. O console é bem interessante para entradas de senha. Este objeto também provê input e output de streams que são streams de caracteres, através de seus métodos reader e writer. Antes de um programa poder usar o Console, o mesmo deve tentar recuperar este objeto invocando o método System.console(). Se o objeto Console estiver disponível este método o retornará, senão o mesmo retornará NULL, e então a interação com o console não será possível, pois o sistema operacional não suporta esta operação, ou o programa foi executado em um ambiente não interativo.
  • 196.
    import java.io.Console; importjava.util.Arrays; import java.io.IOException; public class PegaSenha { public static void main (String args[]) throws IOException { Console c = System.console(); if (c == null) { System.err.println(&quot;Sem console.&quot;); System.exit(1); } String login = c.readLine(&quot;Digite seu login: &quot;); char [] oldPassword = c.readPassword(&quot;Digite sua senha : &quot;);
  • 197.
    if (verify(login, oldPassword)){ boolean noMatch; do { char [] newPassword1 = c.readPassword(&quot;Digite sua senha: &quot;); char [] newPassword2 = c.readPassword(&quot;Digite sua senha: &quot;); noMatch = ! Arrays.equals(newPassword1, newPassword2); if (noMatch) { c.format(&quot;A senha não confere. Tente de novo.%n&quot;); } else { change(login, newPassword1); c.format(&quot;Senha de %s modificada.%n&quot;, login); } Arrays.fill(newPassword1, ' '); Arrays.fill(newPassword2, ' '); } while (noMatch); }
  • 198.
    Arrays.fill(oldPassword, ' ');} //Método fictício static boolean verify(String login, char[] password) { return true; } //Método fictício static void change(String login, char[] password) {} }
  • 199.
    Data Streams Data streams suportam I/O binário de tipos primitivos (boolean, char, byte, short, int, long, float, and double) e também Strings. Todos os data stream implementam as interfaces DataInput ou DataOutput. Conheceremos também suas famosas implementações, DataInputStream e DataOutputStream.
  • 200.
    Data Streams (output ) import java.io.BufferedOutputStream; import java.io.DataOutputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; public class UseOutputDataStream { static final String dataFile = &quot;pedido&quot;; static final double[] prices = { 19.99, 9.99, 15.99, 3.99, 4.99 }; static final int[] units = { 12, 8, 13, 29, 50 }; static final String[] descs = { &quot;Camisa do Java&quot;, &quot;Chaveiro do Java&quot;, &quot;Disquete&quot;, &quot;Pen Drive&quot;, &quot;Boné&quot; };
  • 201.
    Data Streams (output ) public static void main(String[] args) throws FileNotFoundException, IOException { DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(dataFile))); //Escrevendo para a stream for (int i = 0; i < prices.length; i ++) { out.writeDouble(prices[i]); out.writeInt(units[i]); out.writeUTF(descs[i]); } } }
  • 202.
    Data Streams (input ) import java.io.BufferedInputStream; import java.io.DataInputStream; import java.io.EOFException; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; public class UseInputDataStream { static final String dataFile = &quot;pedido&quot;; public static void main(String[] args) throws FileNotFoundException, IOException { DataInputStream in = new DataInputStream(new BufferedInputStream(new FileInputStream(dataFile))); double price; int unit; String desc; double total = 0.0;
  • 203.
    Data Streams (output ) //Lendo do arquivo try { while (true) { price = in.readDouble(); unit = in.readInt(); desc = in.readUTF(); System.out.format(&quot;You ordered %d units of %s at $%.2f%n&quot;, unit, desc, price); total += unit * price; } } catch (EOFException e) { } System.out.println(&quot;Total = &quot; + total); } } Nota : DataStreams detectam o fim de arquivo capturando EOFException, ao invés de testarem um valor de retorno. E todas implementações de métodos de DataInput usam EOFException como retorno.
  • 204.
    Object Streams Da mesma forma que streams suportam serialização de primitivos, elas também suportam I/O para objetos. Várias, mas nem todas as classes standard suportam serialização para seus objetos. Estas mesmas devem implementar a interface Serializable. As classes de object stream são ObjectInputStream e ObjectOutputStream. Essas classes implementam ObjectInput e ObjectOutput, que são sub-interfaces de DataInput e DataOutput. Isso significa que todos os métodos de I/O para dados primitivos cobertos em Data Streams são também implementados em streams de objetos. Se o método readObject() não retornar o tipo de objeto esperado, e ouver uma tentativa de cast para o tipo corredo, então a exception ClassNotFoundException poderá ser lançada.
  • 205.
    Output e Inputde objetos complexos Os métodos writeObject e readObject são muito simples de usar, mas eles contém uma lógica muito sofisticada de gerenciamento de objetos. Muitos objetos contém referências para outros objetos. Se readObject precisa reconstruir um objeto de uma stream, então o mesmo tem que reconstruir TODOS os objetos do grafo. Estes objetos adicionais podem ter suas próprias referências, que também deverão ser resolvidas e etc. Uma única invocação de writeObject pode causar que um grande número de objetos sejam escritos para a stream.
  • 206.
  • 207.
    Object Streams Sedois objetos dentro da mesma stream contiverem referência para um mesmo objeto, então eles irão referenciar um único objeto quando eles forem reconstruídos. Pois uma stream pode conter somente uma cópia de um objeto, apesar de poder conter um número infinito de referencias ao mesmo. Se escrevermos um objeto para uma stream duas vezes, então nós realmente estaremos escrevendo somente a referência duas vezes.
  • 208.
    Object Streams Objectob = new Object(); out.writeObject(ob); out.writeObject(ob); Mas, se um único objeto for escrito para duas streams diferentes, então o mesmo será efetivamente duplicado.
  • 209.
    Arquivos Mantivemos ofoco de nossa discussão nas streams, que nos dão um modelo simples de leitura e escrita de dados. Streams trabalham com uma grande variedade de fontes de dados e destinos, incluindo arquivos no disco. Mas, streams não suportam todas as operações que são comuns com arquivos em disco, e é onde veremos dois conceitos não-stream : File Random Access File
  • 210.
    File Objects A classe File nos torna muito fácil escrever código independente de plataforma que examina e manipula arquivos. O nome desta classe é um pouco confuso, pois instâncias de Files representam nomes de arquivos e não arquivos propriamente ditos, e o arquivo correspondente ao nome pode mesmo nem existir.
  • 211.
    Capturando as propriedadesde um arquivo import java.io.File; import java.io.IOException; import static java.lang.System.out; public class FileStuff { public static void main(String args[]) throws IOException { out.print(&quot;File system roots: &quot;); for (File root : File.listRoots()) { out.format(&quot;%s &quot;, root); } out.println(); for (String fileName : args) { out.format(&quot;%n------%nnew File(%s)%n&quot;, fileName); File f = new File(fileName); out.format(&quot;toString(): %s%n&quot;, f); out.format(&quot;exists(): %b%n&quot;, f.exists()); out.format(&quot;lastModified(): %tc%n&quot;, f.lastModified());
  • 212.
    Capturando as propriedadesde um arquivo out.format(&quot;isFile(): %b%n&quot;, f.isFile()); out.format(&quot;isDirectory(): %b%n&quot;, f.isDirectory()); out.format(&quot;isHidden(): %b%n&quot;, f.isHidden()); out.format(&quot;canRead(): %b%n&quot;, f.canRead()); out.format(&quot;canWrite(): %b%n&quot;, f.canWrite()); out.format(&quot;canExecute(): %b%n&quot;, f.canExecute()); out.format(&quot;isAbsolute(): %b%n&quot;, f.isAbsolute()); out.format(&quot;length(): %d%n&quot;, f.length()); out.format(&quot;getName(): %s%n&quot;, f.getName()); out.format(&quot;getPath(): %s%n&quot;, f.getPath()); out.format(&quot;getAbsolutePath(): %s%n&quot;, f.getAbsolutePath()); out.format(&quot;getCanonicalPath(): %s%n&quot;, f.getCanonicalPath()); out.format(&quot;getParent(): %s%n&quot;, f.getParent()); out.format(&quot;toURI: %s%n&quot;, f.toURI()); } } }
  • 213.
    Random Access FileRandom acess files permitem o acesso aleatório ao conteúdo de um arquivo. new RandomAccessFile(&quot;xanadu.txt&quot;, &quot;r&quot;); new RandomAccessFile(&quot;xanadu.txt&quot;, &quot;rw&quot;); int skipBytes(int) — Move o ponteiro do arquivo para a frente com o número de bytes especificado. void seek(long) — Posiciona o pointeiro antes do byte especificado. long getFilePointer() — Retorna o a localização corrente do byte.
  • 214.
  • 215.
    O que éuma exception ? O termo exception vem da frase “exceptional event”. Uma exception é um evento, que por sua vez ocorre durante a execução de um programa, que “bagunça” o fluxo normal do mesmo. Quando um erro ocorre dentro de um método, o método cria um objeto e o entrega ao sistema de runtime. O objeto é chamado de objeto de exceção, e contém informações sobre o erro, incluindo o seu tipo e estado do programa quando o erro aconteceu. A criação de objetos de exceção e a entrega deles ao sistema de runtime é chamado de “ throwing an exception ”, ou lançamento de exceções. Após um método lançar uma exceção, o sistema de runtime tenta encontrar alguém para tratar a mesma. O conjunto possível de candidatos a tratar a exceção é a lista ordenada de métodos que foram invocados até chegar ao método que lançou a exceção. Esta lista é denominada call stack .
  • 216.
  • 217.
    Tratamento de exceçãoO sistema de runtime procura o call stack por um método que contém um bloco de código que possa tratar a exceção. Este bloco de código é denominado exception handler . A busca inicia-se com no método onde o erro ocorreu e segue através da call stack em ordem reversa que os métodos foram invocados. Quando um handler apropriado é encontrado, o sistema de runtime passa a exceção para o handler. Um exception handler é considerado apropriado se o tipo de objeto de exceção lançado é do mesmo tipo tratado pelo handler. O exception handler escolhido catch the exception , ou a caputra. Se o sistema de runtime procura em toda call stack e não consegue achar um handler, enão o sistema de runtime ( e o programa ) terminarão.
  • 218.
  • 219.
    Capturando e tratandoexceções Um código Java válido para tratar exceções deve : Usar uma declaração try para capturar a exceção, e o mesmo deve prover um tratador para esta exception. Ou O método deve especificar que o mesmo lança a exception, onde o método deve também declarar a cláusula throws .
  • 220.
    Três tipos deexceção O primeiro tipo de exception as chamadas checked exeption . Estas exceptions são detectadas pelo compilador Java, e temos como exemplos : java.io.FileNotFoundException O segundo tipo de exception é o tipo error . Esta exception é ocasionada por algo externo a aplicação, e a aplicação geralmente não consegue antecipar-se a este tipo de exceção. Podemos usar como exemplo a abertura de um arquivo, pois podemos conseguir abrir o mesmo, mas na hora de gravarmos recebemos um erro por conta de uma falha de hardware que nos impediu de gravarmos o arquivo, exemplo : java.io.IOErro O terceiro tipo de exception são as runtime exceptions , que são internas a aplicação, mas a mesma não consegue se prevenir da mesma. Este tipo geralmente aponta para bugs, tais como error de lógica ou mal uso da API, exemplo : NullPointerException
  • 221.
    Capturando e tratandoexceções public class Numeros { public static void main(String []args) { int[] a = new int[5]; a[0] = 5; a[1] = 3; a[2] = 6; a[3] = 9; a[4] = 88; for (int i = 0; i < 6; i++) { int j = a[i]; } } } Exception in thread &quot;main&quot; java.lang.ArrayIndexOutOfBoundsException: 5 at Numeros.main(Numeros.java:13)
  • 222.
    O bloco tryA primeira coisa que devemos fazer para tratarmos um código de lança uma exceção é colocá-la em um bloco try : try { //codigo aqui } catch...
  • 223.
    O bloco try/ catch Nós criamos tratadores combinando blocos try e catch, onde cada catch será um tratador para uma excepção diferente : import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.PrintWriter; public class OpenFile { public static void main(String[] args) { File f = new File(&quot;MeuArquivo.txt&quot;); PrintWriter out = null; try { out = new PrintWriter(f); } catch (FileNotFoundException ex) { System.out.println(&quot;Não consegui abir o arquivo. &quot; + ex.getMessage()); } catch (IOException ex) { System.out.println(&quot;Peguei uma IOException. &quot; + ex.getMessage()); } } }
  • 224.
    O bloco finallyO bloco finally sempre é executado quando um try existe. Isto garante que o bloco finally será executado mesmo quando uma exceção inesperada aconteça. Mas o finally é muito útil além de tratar exceções. O finally permite ao programador ter qualquer código de limpeza sendo pulado acidentamente por algum return, continue ou break. Colocar código de limpeza dentro do finally é uma ótima prática. }finally{ if (out != null ) { System.out.println(&quot;Fechando o arquivo...&quot;); out.close(); }else{ System.out.println(&quot;O arquivo não estava aberto !&quot;); } }
  • 225.
    Especificando exceptions lançadaspor um método Vimos anteriormente que podemos tratar as exceptions dentro de nossos próprios métodos, mas existe ainda a opção de deixarmos este tratamento para todo código “cliente” que invocar nosso método. Para isso precisamos adicionar a cláusula throws na assinatura de nosso método, seguido das possíveis exceções que nosso código lançará :
  • 226.
    Especificando exceptions lançadaspor um método import java.io.File; import java.io.FileNotFoundException; import java.io.PrintWriter; class TrataArquivo { public void abreArquivo() throws FileNotFoundException { File f = new File(&quot;MeuArquivo.txt&quot;); PrintWriter out = null; out = new PrintWriter(f); out.println(&quot;ABCDEFGH&quot;); out.close(); } }
  • 227.
    Especificando exceptions lançadaspor um método public class OpenFile { public static void main(String[] args) { TrataArquivo ta = new TrataArquivo(); try { ta.abreArquivo(); } catch (FileNotFoundException ex) { System.out.println(&quot;Não consegui abrir o arquivo...&quot;); } } }
  • 228.
    Lançando exceções Émuito simples lançar exceções, tudo que necessitamos fazer é utilizarmos a cláusula throw dentro do método desejado : throw umObjeto Throwable ;
  • 229.
    Lançando exceções publicclass LancandoExceptions { public double calculaPagamento(int mes) throws Exception { if (mes < 1 || mes > 12) { throw new Exception(&quot;Mês inválido !&quot;); } return 99.7; } }
  • 230.
  • 231.
    Filhos de ThrowableError – Quando acontece um erro na virtual machine ela lança uma exceção deste tipo. E os programas geralmente não capturam ou lançam este tipo de exceção. Exception – Um objeto deste tipo indica que um problema ocorreu, mas isso não é um problema da VM e sim do programa, e geralmente os programas usam suas próprias exceptions para tratar condições de erro. Podemos usar como exemplos de exceptions : IllegalAccessException – Indica que um método não pode ser encontrado. NegativeArraySizeException – Indica que o programa tentou criar um array com tamanho negativo. A classe RuntimeException é reservada para exceptions que dizem respeito ao uso incorreto da API, e podemos citar como exemplo o NullPointerException, que é a tentativa de operação sobre um objeto nulo.
  • 232.
    Exceções em cadeiaUma aplicação pode tratar uma exceção simplesmente lançando uma outra exceção. Esta técnica é muito útil, pois podemos capturar uma exceção genérica e transformá-la em outra mais específica : try { } catch (IOException e) { throw new MyException(“Outro Erro&quot;, e) ; }
  • 233.
    StackTrace O objetoStackTrace é responsável por retornar um texto com a cadeia de métodos invocados até o lançamento da exceção : public static void main(String[] args) { Exception e = new Exception(&quot;Um erro foi gerado por mim !&quot;); e.printStackTrace(); } Resultado : java.lang.Exception: Um erro foi gerado por mim ! at LancandoExceptions.main(LancandoExceptions.java:13)
  • 234.
    Criando uma exceçãopublic class MinhaExcecao extends Exception { public MinhaExcecao() { super(); } public MinhaExcecao(String message) { super(message); } }
  • 235.
    Nota sobre uncheckedexceptions Muitos programadores sentem-se tentados a escrever código que herdam e lançam somente unchecked exceptions, pois as mesmas não são checadas pelo compilador. Isto é uma péssima prática ! Uma exception que pode ser lançada por um método é parte de sua interface pública, e todos que invocam esses métodos devem saber sobre as exceções que o método pode lançar, e então os mesmos podem decidir como tratá-las. Runtime Exceptions representam problemas que são resultado de programação, e o código cliente não é esperado que se recupera deste tipo de erro. Estes erros incluem exceções de aritimética, divisão por zero, null pointer exceptions, exception de índices dentre outros. Regra básica : Se o cliente pode se recuperar da exception, então a exception deve ser checked, caso contrário, ela deve ser unchecked.
  • 236.
    Exceptions são Umaforma organizada de tratar um evento excepcional que aconteceu durante o fluxo do seu programa. As exceptions eliminam os códigos tipo espaguete ( bacalhau ) da forma tradicional de tratamento de erros.
  • 237.
  • 238.
    Concorrência Nós usuáriosde computadores acreditamos e confiamos que nossos sistemas podem fazer mais de uma coisa ao mesmo tempo. Assumimos que podemos continuar trabalhando no work enquanto fazemos download de músicas, imprimimos algo, copiamos arquivos e etc. Mesmo um simples aplicativo é esperado fazer mais de uma coisa de cada vez. A plataforma Java foi re-estruturada do zero para suportar programação concorrente, com suporte concorrente básico na linguagem java e em suas bibliotecas. Desde a versão 5.0 que a plataforma Java incluiu também a biblioteca de concorrência.
  • 239.
    Processos e threadsEm programação concorrente, existem duas unidades básicas de execução : processos e threads . Em Java a programação concorrente está mais focada em threads. Mas de qualquer forma os processos também são importantes. Um computador normalmente possui vários processos ativos e threads. E isto também é verdadeiro para sistemas que possuem uma única linha de execução, e possui somente uma thread sendo executada naquele momento. Tempo de processamento para um único core é compartilhado entre os processos e threads através de uma feature do SO chamada “time slicing”. Hoje em dia é muito comum que os computadores tenham vários procesadores com vários cores, e isto aumenta em muito a capacidade de execução concorrente de processos e threads. Mas a concorrência também é possível em sistemas simples, onde não existam vários processadores ou cores de execução.
  • 240.
    Processos Um processoé um ambiente de execução auto-contido. E um processo geralmente tem um conjunto privado e completo de recursos de runtime, e em particular cada processo possui sus própria área de memória. Processos são geralmente vistos como sinônimos de programas ou aplicações, entretanto oque o usuário vê como uma simples aplicação pode ser na realidade um conjunto de processos cooperando. Para facilitar a comunicação entre processos, vários SOs suportam recursos de Inter Process Communication (IPC), tal como pipes ou sockets. IPC não é usado somente para a comunicação entre processos no mesmo sistema, mas processos em diferentes sistemas A grande maioria das implementações da JVM rodam em um único processo. Uma aplicação Java pode criar processos adicionais usando um objeto ProcessBuilder.
  • 241.
  • 242.
    Threads Threads sãotambém conhecidas como processos leves. Ambos processos e threads provêem um ambiente de execução, mas criar novas threads requer muito menos recursos do que criar novos processos. Threads existem dentro de processos, e cada processo possui pelo menos uma. As threads compartilham os recursos do processo, incluindo memória e arquivos. Isso aumenta em muito a eficiência, mas pode acarretar em uma comunicação problemática. Execução multi-thread é uma feature essencial da plataforma Java. Onde cada aplicação possui pelo menos uma thread, ou várias, se contarmos as threads do sistema que realizam trabalhos como gerenciamento de memória e tratamento de sinais. Mas da perspectiva do programador o programa é iniciado com uma única thread, chamada main thread .
  • 243.
    Objetos Thread Cadathread é associada com uma instância da classe Thread . E existem duas formas de usarmos o objeto thread : Para controlar a thread diretamente, simplesmente instanciamos uma Thread cada vez q a aplicação precisar realizar alguma tarefa assíncrona. Para abstrair o gerenciamento da thread do resto da aplicação, passamos a tarefa a um executor.
  • 244.
    Definir e iniciaruma Thread Criar um objeto Runnable. A interface Runnable define um método chamado run, que é esperado conter o código a ser executado em uma thread. O objeto runnable então é passado no construtor da thread para ser executado : public class HelloRunnable implements Runnable { public void run() { System.out.println(&quot;Hello from a thread!&quot;); } public static void main(String args[]) { (new Thread(new HelloRunnable())).start(); } }
  • 245.
    Definir e iniciaruma Thread Criar uma sublasse de Thread. Podemos fazer isso pois a classe Thread também implementa runnable diretamente. Podemos então criar uma subclasse e prover o código para o método run : public class HelloThread extends Thread { public void run() { System.out.println(&quot;Hello from a thread!&quot;); } public static void main(String args[]) { (new HelloThread()).start(); } }
  • 246.
    Dando uma pausana execução com sleep O método Thread.sleep causa a suspensão da execução da thread corrente por um período específico de tempo. Isto é uma forma eficiente para termos tempo de processamento disponível para outras threads de uma aplicação, ou ainda para outras aplicações rodarem. O método sleep pode também ser usado para marcar o ritmo de execução, como por exemplo aguardar por outra thread. public static native void sleep(long millis) throws InterruptedException; public static void sleep(long millis, int nanos) throws InterruptedException;
  • 247.
    Exemplo public classSleepMessages { public static void main(String args[]) throws InterruptedException { String importantInfo[] = { &quot;Mares eat oats&quot;, &quot;Does eat oats&quot;, &quot;Little lambs eat ivy&quot;, &quot;A kid will eat ivy too&quot; }; for (int i = 0; i < importantInfo.length; i++) { //Pause for 4 seconds Thread.sleep(4000); //Print a message System.out.println(importantInfo[i]); } } }
  • 248.
    Nota No exemploanterior vimos que o método main declara uma InterruptedException, que é a exception que o método sleep lança quando outra thread interrompe a thread corrente enquanto o sleep está ativo.
  • 249.
    Interrupções Uma interrupçãoé um indicativo que a thread deve de fazer aquilo que estiver fazendo, e fazer alguma outra coisa. É de inteira responsabilidade do programador dizer como a thread responde a uma interrupção, mas é muito comum a thread ser terminada nesses casos. A thread envia uma interrupção invocando o método interrupt no objeto Thread para a thread que será interrompida. E para este mecanismo funcionar direito, a thread interrompida deve suportar sua própria operação.
  • 250.
    Suportando interrupções Athread suporta interrupções através de vários métodos, podendo lançar a exceção InterruptedException, onde a execução será abortada. for (int i = 0; i < 10; i++) { //Pausa por 4 segundos try { Thread.sleep(4000); } catch (InterruptedException e) { //Exceção capturada, e nada mais a fazer. return; } //Print a message System.out.println(i); }
  • 251.
    Suportando interrupções Existemvários métodos que lançam InterruptedException, e os mesmos foram configurados para cancelar sua execução corrente e retornar imediatamente qiuando uma interrupção for recebida. public static native void sleep(long millis) throws InterruptedException; public static void sleep(long millis, int nanos) throws InterruptedException {} public final synchronized void join(long millis) throws InterruptedException {} public final synchronized void join(long millis, int nanos) throws InterruptedException {} public final void join() throws InterruptedException {}
  • 252.
    Sabendo se umainterrupção foi recebida As vezes as threads podem ficar um tempo muito longo sem invocar nenhum método que lança uma InterruptedException, então podemos invocar o método Thread.interrupted para sabermos se uma interrupção foi recebida. if (Thread.interrupted()) { //Interrupção recebida return; }
  • 253.
    Joins O métodojoin permite uma thread aguardar pelo término do trabalho de outra. minhaThread.join(); Causa a interrupção da thread corrente até que minhaThread termine seu trabalho.
  • 254.
    Exemplo SimpleThreads publicclass SimpleThreads { }
  • 255.
    Sincronização Threads comunicam-seprimáriamente compartilhando o acesso a campos de objetos que elas referenciam. Esta forma de comunicação é muito eficiente, mas também é refém de dois erros : interferência de thread e erro de inconsistência de memória.
  • 256.
    Interferência Interferências acontecemquando duas operações, rodando em diferentes threads, mas operando sobre o mesmo dado se intercalam. public class Contador { private int campo = 0; public void incrementa() { campo++; } public void subtrai() { campo--; } public int valorAtual() { return campo; } }
  • 257.
    Exemplo de interferênciaThread 1 pega o contador Thread 2 pega o contador Thread 1 recupera o valor e o incrementa (1) Thread 2 recupera o valor e o decrementa (-1) Thread 1 armazena o valor em c, que agora vale (1) Thread 2 armazena o valor em c, que agora vale (-1)
  • 258.
    Métodos sincronizados OJava provê duas formas básicas de sincronização : métodos sincronizados e declarações sincronizadas. Para fazer um método se tornar sincronizado, somente precisamos adicionar a palavra chave synchronized em sua declaração.
  • 259.
    Métodos sincronizados publicclass ContadorSincronizado { private int c = 0; public synchronized void increment() { c++; } public synchronized void decrement() { c--; } public synchronized int value() { return c; } }
  • 260.
    Métodos sincronizados Agoraque temos um contador sincronizado, temos o seguinte resultado : Não é mais possível termos mais de uma invocação em métodos sincronizados simultâneamente. Quando uma thread está executando um método sincronizado de um objeto, todas as outras thread que invocam este mesmo método estão suspensas, até que a primeira thread termine seu trabalho com o objeto. Quando um método sincronizado existe, o mesmo garante a consistência com qualquer invocação futura de um método sincronizado do mesmo objeto. Isto garante que mudanças no estado do objeto sejam visíveis a todas as threads. Obs : Construtores não podem receber a cláusula synchronized, pois somente uma thread consegue criar uma instância por vez, e isto também ocasionará em um erro de compilação.
  • 261.
    Locks implícitos esincronização A sincronização é feita atrvés de uma dispositivo interno chamado “monitor lock” . Locks implícitos comandam o acesso exclusivo ao estado do objeto. Cada objeto tem um lock implícito associado a ele. Uma thread que precisa acesso exclusivo e consistente a um campo de um objeto precisa pegar um lock implícito antes de acessá-lo, e então liberá-lo quando terminar seu trabalho. A thread é dona do lock entre o momento em quen o pega, até o momento da liberação. Enquanto uma thread tiver o lock sobre o objeto, nenhuma outra thread conseguirá pegar o mesmo lock, ou seja, as outras threads serão bloqueadas quando tentarem realizar o lock.
  • 262.
    Locks em métodossincronizados Quando uma thread invoca um método sincronizado, a mesma adquire automáticamente o lock para o método do objeto e o libera quando o método retornar.
  • 263.
    Declarações sincronizadas Outraforma de criarmos código sincronizada é através das declarações sincronizadas . Ao contrário dos métodos sincronizados, as declarações devem especificar o objeto que provê o lock : public void addName(String name) { synchronized(this) { lastName = name; nameCount++; } nameList.add(name); }
  • 264.
    DeadLock O deadlocké uma situação onde duas ou mais threads ficam bloqueadas para sempre, esperando uma pela outra.
  • 265.
  • 266.
    Introdução Uma coleçãoé um container, ou seja, um objeto que tem a capacidade de agrupar vários elementos em uma única unidade. Coleções são utilizadas para guardar, recuperar, manipular e comunicar dados agregados. Geralmente elas representam algum agrupamento natural, como por exemplo lista de funcionários, grupo de emails, e etc.
  • 267.
    O framework decoleções É a arquitetura unificada para representar e maniuplar coleções, onde todos os frameworks possuem : Interfaces : Que são dados abstratos que representam as coleções. As interfaces permitem que as coleções sejam manipuladas independentemente dos detalhes de implementação. Implementações : São as implementações concretas das interfaces, ou seja, elas são as estruturas “de fato”. Algorítimos : São métodos que executam operações úteis, tal como pesquisa e ordenação em objetos que implementam interfaces de coleções. Os algorítimos são polimórficos, ou seja, o mesmo método pode ser usado em diferentes implementa~ções.
  • 268.
    Benefícios do frameworkde coleções Reduz o trabalho com programação : Pois provê ótimas estruturas de dados e algorítimos, o framework de coleções nos libera para concentrarmos somente nas partes importantes do nosso programa, ao invés de ficarmos focados em problemas de “baixo-nível”. Aumenta a velocidade de programação e a qualidade : Pois provê implementações de alta performance e alta qualidade em suas estruturas e algorítimos. As várias implementações são intercambiáveis, então os programas podem mudar de implementação fácilmente. Reduz o tempo de aprendizado. Reduz o tempo de escrita de novas APIs Possibilita o reuso.
  • 269.
    Interfaces As interfacesdo core encapsulam diferentes tipos de coleções, que são mostradas na próxima figura. Estas interfaces permitem que as coleções sejam manipuladas independente de sua representação. As coleções do core são a pedra fundamental do Java Collections Framework.
  • 270.
    Collection A raizda hierarquia de coleções. Uma collection representa um grupo de objetos denominados elementos. A interface Collection é o denominador comum entre todas as coleções que a implementam e é usada para passar coleções a métodos e manipulá-las de forma genérica.
  • 271.
    Set É umacoleção que não pode ter elementos duplicados. Esta interface modela a abstração matemática de “conjunto”.
  • 272.
    List Uma coleçãoordenada ( também conhecida como sequência). As listas podem conter elementos duplicados. O usuário geralmente tem o controle preciso de onde o elemento é inserido na mesma, e pode acessá-lo usando um índice de posição.
  • 273.
    Queue Uma coleçãousada para carregar múltiplos elementos antes de processá-los. Queues típicamente, mas não necessáriamente, ordenam elementos no modo FIFO (first-in, first-out). Entre as exceções estão priority queues, que ordenam os elementos de acordo com um comparator fornecido.
  • 274.
    Map Um objetoque mapeia chaves e valores. Um mapa não pode ter chaves duplicadas, e uma chave só pode mapear um único valor.
  • 275.
    Sorted SortedSet -Um conujunto que mantém seus elementos em ordem ascendente. SortedMap – Um mapa que mantém os seus mapeamentos em ordem ascendente de chave. Este mapa é análogo ao SortedSet.
  • 276.
    A iterface Collectionpublic interface Collection<E> extends Iterable<E> { // Basic operations int size(); boolean isEmpty(); boolean contains(Object element); boolean add(E element); //optional boolean remove(Object element); //optional Iterator<E> iterator(); // Bulk operations boolean containsAll(Collection<?> c); boolean addAll(Collection<? extends E> c); //optional boolean removeAll(Collection<?> c); //optional boolean retainAll(Collection<?> c); //optional void clear(); //optional // Array operations Object[] toArray(); <T> T[] toArray(T[] a); }
  • 277.
    Atravessando coleções Declaraçãofor-each for (Object o : collection) { System.out.println(o); } Iterators public interface Iterator<E> { boolean hasNext(); E next(); void remove(); //optional } static void filter(Collection<?> c) { for (Iterator<?> it = c.iterator(); it.hasNext(); ) if (!cond(it.next())) it.remove(); }
  • 278.
    Operações em massasobre Collections Operações em massa são executadas em cima de uma coleção inteira. containsAll – retorna true se a coleção destino possui todos os itens na coleção especificada. addAll – adiciona todos os elementos da coleção especificada na coleção destino. removeAll – remove da coleção destino todos os elementos que também estiverem contidos na coleção especificada. retainAll – remove da coleção destino todos os elementos que não estiverem contidos na coleção especificada. clear – remove todos os elementos da coleção. c.removeAll(Collections.singleton(e)); c.removeAll(Collections.singleton(null));
  • 279.
    A interface Collectione Arrays O método toArray é provido como uma ponte de ligação entre coleções e APIs antigas que esperam arrays como entrada. As operações com arrays permitem que o conteúdo de uma coleção seja traduzido para um array. Object[] a = c.toArray();
  • 280.
    A interface Setpublic interface Set<E> extends Collection<E> { // Basic operations int size(); boolean isEmpty(); boolean contains(Object element); boolean add(E element); //optional boolean remove(Object element); //optional Iterator<E> iterator(); // Bulk operations boolean containsAll(Collection<?> c); boolean addAll(Collection<? extends E> c); //optional boolean removeAll(Collection<?> c); //optional boolean retainAll(Collection<?> c); //optional void clear(); //optional // Array Operations Object[] toArray(); <T> T[] toArray(T[] a); }
  • 281.
    Implementações de SetO Java possui 3 implementações para a interface Set. HashSet – Armazena os elementos em uma hash table, é a implementação de melhor performance, mas a mesma não garante order alguma no momento da iteração TreeSet – Armazena os elementos em formato red-black tree, ordena os elementos baseado nos seus valores, e é substancialmente mais lenta que HashSet. LinkedSet – Implementada como hash table com uma linked list, ordena os elementos baseada na order em que eles foram inseridos no conjunto. Collection<Type> noDups = new HashSet<Type>(c); Collection<Type> noDups = new LinkedHashSet<Type>(c);
  • 282.
    Operações em massacom Sets Operações em massa são muito interessantes em Sets, pois elas são executadas de forma algébriga. s1.containsAll(s2) — retorna true se s2 é um subconjunto de s1. (s2 é um subconjunto de s1 se s1 contém todos os elementos que estão em s2.) s1.addAll(s2) — transforma s1 na união de s1 e s2. (A união de dois sets é o set contendo todos os elementos contidos em ambos sets.) s1.retainAll(s2) — transforma s1 na interseção de s1 e s2. (A interseção de dois sets é um set contendo somente os elementos comuns em ambos sets.) s1.removeAll(s2) — transforma s1 na diferença (assimétrica) de s1 e s2. (Por exemplo, a diferença entre s1 menos s2 é o set contendo todos os elementos encontrados em s1 mas não em s2.)
  • 283.
    Operações em massacom Sets Para calcular a união, interseção ou a diferença entre sets de forma não destrutiva, então devemos copiar o set antes de invocarmos as operações em massa : Set<Type> union = new HashSet<Type>(s1); union.addAll(s2); Set<Type> intersection = new HashSet<Type>(s1); intersection.retainAll(s2); Set<Type> difference = new HashSet<Type>(s1); difference.removeAll(s2);
  • 284.
    A interface Listpublic interface List<E> extends Collection<E> { // Positional access E get(int index); E set(int index, E element); //optional boolean add(E element); //optional void add(int index, E element); //optional E remove(int index); //optional boolean addAll(int index, Collection<? extends E> c); //optional // Search int indexOf(Object o); int lastIndexOf(Object o); // Iteration ListIterator<E> listIterator(); ListIterator<E> listIterator(int index); // Range-view List<E> subList(int from, int to); }
  • 285.
    Implementações de ListO Java possui duas implementações para List. ArrayList – Geralmente é a implementação de melhor performance. LinkedList – Oferece melhor performance sob certas circunstâncias. Vector “intruso” – Foi refatorado para implementar List.
  • 286.
    Operações sobre Listlist1.addAll(list2); //Forma não destrutiva List<Type> list3 = new ArrayList<Type>(list1); list3.addAll(list2); //acesso posicional e pesquisa public static <E> void swap(List<E> a, int i, int j) { E tmp = a.get(i); a.set(i, a.get(j)); a.set(j, tmp); }
  • 287.
    Iterators É retornadopelas operações de iterators da lista. public interface ListIterator<E> extends Iterator<E> { boolean hasNext(); E next(); boolean hasPrevious(); E previous(); int nextIndex(); int previousIndex(); void remove(); //optional void set(E e); //optional void add(E e); //optional } for (ListIterator<Type> it = list.listIterator(list.size()); it.hasPrevious(); ) { Type t = it.previous(); ... }
  • 288.
    Queue public interfaceQueue<E> extends Collection<E> { E element(); boolean offer(E e); E peek(); E poll(); E remove(); } static <E> List<E> heapSort(Collection<E> c) { Queue<E> queue = new PriorityQueue<E>(c); List<E> result = new ArrayList<E>(); while (!queue.isEmpty()) result.add(queue.remove()); return result; }
  • 289.
    A interface Mappublic interface Map<K,V> { // Basic operations V put(K key, V value); V get(Object key); V remove(Object key); boolean containsKey(Object key); boolean containsValue(Object value); int size(); boolean isEmpty(); // Bulk operations void putAll(Map<? extends K, ? extends V> m); void clear();
  • 290.
    A interface Map// Collection Views public Set<K> keySet(); public Collection<V> values(); public Set<Map.Entry<K,V>> entrySet(); // Interface for entrySet elements public interface Entry { K getKey(); V getValue(); V setValue(V value); } } Existem três implementações para a interface Map : HashMap, TreeMap e LinkedHashMap. Que por sua vez são análogas as implementações de Set.
  • 291.
    Operações com Mapimport java.util.*; public class Freq { public static void main(String[] args) { HashMap<String, Integer> m = new HashMap<String, Integer>(); // Initialize frequency table from command line for (String a : args) { Integer freq = m.get(a); m.put(a, (freq == null) ? 1 : freq + 1); } System.out.println(m.size() + &quot; distinct words:&quot;); System.out.println(m); } } for (Map.Entry<KeyType, ValType> e : m.entrySet()) System.out.println(e.getKey() + &quot;: &quot; + e.getValue());
  • 292.
  • 293.
    Introdução Generics éa alteração mais significante da linguagem Java desde a sua versão 1.0. A adição das generics no java 5.0 foi o resultado de uma especificação ( JSR 14 ), que foi feita em 1999. Generics são muito úteis porque elas nos permitem escrever código que é seguro e fácil de ler, ao invés de código que trabalha com Objects e usa vários casts. As generics são muito úteis no trabalho com collections, tal como o ArrayList.
  • 294.
    Introdução Generics sãomuito parecidas com as famosas templates do C++. Programação com generics significa escrever código que pode ser reutilizado por objetos de diferente tipos.
  • 295.
    Usando coleções comcast List myIntList = new LinkedList(); // 1 myIntList.add(new Integer(0)); // 2 Integer x = (Integer) myIntList.iterator().next(); // 3 Famoso código “saco de gatos”
  • 296.
    Com Generics Umainterface ou classe pode ser declarada para receber um ou mais parâmetros, que são escritos entre os sinais de menor e maior < e >. List <Integer> myIntList = new LinkedList<Integer>(); // 1' myIntList.add(new Integer(0)); // 2' Integer x = myIntList.iterator().next(); // 3'
  • 297.
    Especificação de genericsDefinição da interface List : public interface List <E>{ void add(E x); Iterator<E> iterator(); } public interface Iterator<E>{ E next(); boolean hasNext(); }
  • 298.
    Programando com genericsÉ muito fácil utilizarmos uma classe genérica como ArrayList. E a maioria de nós programadores mortais iremos simplesmente digitar ArrayList<String>, pois esta declaração já faz parte da linguagem, e é tão natural quanto se estivessemos declarando um array de strings : String[]. Infelizmente não é tão fácil implementar uma classe genérica, pois os programadores que utilizarão este código podem querer plugar todos os tipos de classes como parâmetro de tipo. E os mesmos esperam que tudo funcione sem restrição ou mensagem de erro.
  • 299.
    Wrappers Todo tipoem Java é um tipo referência ou primitivo. Um tipo referência é qualquer classe, instância ou array. Todos os tipos referência são subclasses de Object. Existem 8 tipos de primitivos, e cada um deles possui uma classe ( Wrapper ) correspondente.
  • 300.
    Wrappers Primitivo Referênciabyte Byte short Short int Integer long Long float Float double Double boolean Boolean char Character
  • 301.
    Boxing e UnboxingA conversão de primitivo para refência é chamado de boxing, e a conversão de referência para primitivo é chamado de unboxing. O Java com generics insere mecanismos de boxing e unboxing quando necessário, deixando o programador livre de criar um wrapper ou convertê-lo para um primitivo. List<Integer> list = new ArrayList<Integer>(); list.add(1); //Boxing int n = list.get(0); //Unboxing //É o mesmo que : List<Integer> list = new ArrayList<Integer>(); list.add(new Integer(1)); int n = list.get(0);
  • 302.
    Definição de classegenérica public class Pair<T> { public Pair() { first = null; second = null; } public Pair(T first, T second) { this.first = first; this.second = second; } public T getFirst() { return first; } public T getSecond() { return second; } public void setFirst(T newValue) { first = newValue; } public void setSecond(T newValue) { second = newValue; } private T first; private T second; }
  • 303.
    Definição de classegenérica Temos em nossa classe “Pair” uma variável de tipo T, que está entre < > depois do nome da classe. Vale a pena lembrar que uma classe genérica pode ter mais de um tipo de variável. Poderíamos ter definido a classe Pair usando dois parâmetros : public class Pair<T, U> { . . . }
  • 304.
    Padrões Os “typevariables” são utilizados dentro da definição da classe para especificar o retorno do método, tipos de campos e variáveis locais : private T first; É uma prática comum usar letras em caixa alta para tipos, e mantê-los curtos. As bibliotecas do Java usam a variável E para tipo de elemento de coleção, K e V para chave e valor ( key/value ) de um mapa/tabela, e T ( e também U e S, se necessário), para qualquer outro tipo.
  • 305.
    Instanciando tipos genéricosPara tal tarefa necessitamos somente substituir as variáveis de tipo pelo tipo desejado : Pair<String>
  • 306.
    Método genérico evarargs Public static <T> void addAll(list<T>, T... arr) { For (T tipo : arr ) { list.add(tipo); } }
  • 307.
    Testando a classegenérica Pair public class PairTeste { public static void main(String[] args) { Pair<String> p = new Pair<String>(); p.setFirst(&quot;a&quot;); p.setSecond(&quot;b&quot;); } }
  • 308.
    foreach Existe umaforma mais rápida e inteligente de iterar em arrays e coleções : double[] ar = {1.2, 3.0, 0.8}; int sum = 0; for (double d : ar) { sum += d; }
  • 309.
    foreach List<String> names= new ArrayList<String>(); names.add(&quot;a&quot;); names.add(&quot;b&quot;); names.add(&quot;c&quot;); for (String name: names) { System.out.println(name.charAt(0)); }