Este artigo analisa as diferenças entre JDK1.4, JDK1.5 e JDK1.6 na programação Java em combinação com exemplos. Compartilhe -o para sua referência, como segue:
Simplificando: existem duas maiores diferenças entre 1,4 e 1,5. Uma é que 1.5 possui genéricos e o outro 1.5 pode encapsular automaticamente tipos de dados encapsulados de oito tipos de dados básicos. Ou seja, o número inteiro A = 4 não é permitido. Não há muita diferença entre 1,5 e 1,6. 1.6 Acho que a maioria das mudanças são a GUI, que fornece muito gerenciamento e extensão de layout convenientes.
Durante esse período, entrei para uma empresa de governo eletrônico e usei o WebLogic8. Então vamos usar o JDK1.4. O Eclipse mudou a versão JDK. No entanto, os projetos anteriores basicamente se tornaram populares.
★ Novos recursos do JDK1.5:
1. Genéricos
2 Embalagem/Unboxing automático
3 para cada cada
4 Importação estática
5 parâmetros de comprimento variável
1. Genéricos (evite erros de execução que possam ser causados por fundição de tipo)
Por exemplo:
ArrayList List = new ArrayList (); list.add (novo número inteiro (3)); list.add (novo número inteiro (4)); int i = ((número inteiro) (list.get (0))). parseint ();
Muito problemático
ArrayList <Teger> list = new ArrayList <Teger> (); list.add (novo número inteiro (3)); list.add (novo número inteiro (4)); int i = list.get (0) .parseint ();
2 Embalagem/Unboxing automático
A última frase do exemplo acima pode ser alterada para:
A cópia do código é a seguinte: int i = list.get (0);
Porque o tipo original e a classe Wrapper correspondente não precisam ser explicitamente convertidos
3 para cada cada
Aprimoramento de loops
int a [] = {......}; // inicialize para (int i: a) {......}Não use o i = 0 anterior; i <a.Length; i ++
4 Importação estática
Java.math anteriormente sintonizado
A cópia do código é a seguinte: math.sqrt ();
Agora importação estática java.lang.math.sqrt;
sqrt ();
É equivalente a ter esse método em sua própria classe
5 parâmetros de comprimento variável
int sum (int ... intlist) {int sum; soma = 0; for (int i = 0; i <intlist.length; i ++) {sum+= intlist [i]; } retornar a soma;}Existe algum parâmetro, trate -o como uma matriz
★ Novos recursos do JDK6.0
Aprimorada para a declaração de loop, enumeração de anotação de parâmetros variáticos "ocultos" do método estático (vararg)
Cardards e devolução de covariância aprimorados para declarações de loop
Para iterar sobre conjuntos e matrizes, o aprimorado para loop fornece uma sintaxe simples e compatível. Há dois pontos que vale a pena mencionar:
1. Em um loop, a expressão de inicialização é calculada apenas uma vez. Int expressão
Não aprimorado para:
int sum = 0; Inteiro [] números = Computenumbers (); for (int i = 0; i <números.length; i ++) soma+= números [i];
Aprimorado para:
int sum = 0; para (INT número: computenumbers ()) soma += número;
limitação
Iterador ou subscrito não pode ser acessado durante o aprimoramento para iteração de loop
Por favor, veja o seguinte exemplo:
for (int i = 0; i <números.length; i ++) {if (i! = 0) System.out.print (","); System.out.print (números [i]);}Aqui está outro exemplo:
for (iterator <Teger> it = n.iterator (); it.hasnext ();) if (it.next () <0) it.remove ();
Comentários
O processamento de comentários é um grande tópico. Como este artigo se concentra apenas nos recursos do idioma principal, não pretendemos cobrir todas as suas formas e armadilhas possíveis. Discutiremos anotações internas (supressoras, depreciadas e substituídas) e as limitações do processamento geral da anotação.
Suprimir avisos
Este comentário desliga os avisos do compilador no nível da classe ou método. Às vezes, você sabe mais claramente do que o compilador que o código deve usar um método rejeitado ou executar alguma ação que não possa determinar estaticamente se o tipo de tipo é seguro de tipo e use:
@Suppresswarnings ("deprecação") public static void autodestruct () {thread.currentThread (). Stop ();}Esta é provavelmente a coisa mais útil sobre anotações internas. Infelizmente, o JAVAC para 1.5.0_04 não o suporta. Mas 1.6 suporta, e a Sun está trabalhando para transitá -lo para trás em 1.5.
Esta anotação é suportada no Eclipse 3.1 e outros IDEs também podem apoiá -lo. Isso permite que você libere completamente o código do aviso. Se houver um aviso no horário de compilação, você pode ter certeza de que acabou de adicioná -lo - para ajudar a visualizar o código que pode ser inseguro. Com a adição de genéricos, será mais útil de usar.
Descontinuado
Infelizmente, depreciado não é tão útil. Originalmente, foi destinado a substituir a tag Javadoc @Deprecated, mas como não contém campos, não há como sugerir o que os usuários de classes ou métodos depreciados devem usar como substituição. maioria
Ambos o uso requer a tag javadoc e esta anotação.
Substituir
Subster diz que o método que anota deve substituir os métodos pela mesma assinatura na superclasse:
@OverridePublic int hashCode () {...}Olhando para o exemplo acima, se "C" não estiver capitalizado no HashCode, não haverá erros no momento da compilação, mas o método não será chamado como esperado em tempo de execução. Ao adicionar a tag de substituição, o compilador solicitará se ele realmente executou a reescrita.
Isso também é útil em situações em que as superclasses mudam. Se um novo parâmetro for adicionado ao método e o próprio método for renomeado, a subclasse de repente não será compilada porque não reescreve mais nada da superclasse.
Outras notas
Os comentários são muito úteis em outros cenários. Quando não é para modificar o comportamento diretamente, mas aprimorar o comportamento, especialmente ao adicionar código de boilerplate, a anotação funciona muito bem nas estruturas como EJB e serviços da Web.
Os comentários não podem ser usados como pré -processadores. O design da Sun impede especificamente a modificação do bytecode da classe inteiramente devido a comentários. Isso permite que você compreenda corretamente os resultados do idioma, e ferramentas como o IDE também podem executar análises e refatoramento de código aprofundadas.
O comentário não é uma bala de prata. Quando o encontrei, as pessoas tentaram experimentar várias técnicas. Consulte as seguintes sugestões obtidas de outros:
classe pública Foo {@PropertyPrivate int bar;}A idéia é criar automaticamente métodos Getter e Setter para a barra de campo privada. Infelizmente, há duas falhas nessa idéia: 1) não funciona e 2) dificulta a leitura e o processo do código. É impossível implementar, porque, como mencionado anteriormente, a Sun evita especificamente a modificação das classes com comentários.
Mesmo se possível, não é uma boa ideia, porque torna o código mal legível. Na primeira vez que você vê esse código, não saberá que o comentário cria o método. Além disso, se você precisar executar algumas operações dentro desses métodos no futuro, os comentários serão inúteis. Em suma, não tente fazer coisas que o código regular pode fazer com comentários.
enumerar
Enum é muito parecido com uma declaração pública de estática estática, que tem sido usada como valor de enumeração por muitos anos. A maior e mais óbvia melhoria do INT é a segurança do tipo - você não pode substituir erroneamente outro tipo por um tipo de enumeração, que é diferente do INT, e todas as INTs são as mesmas para o compilador. Com muito poucas exceções, todas as estruturas INT em estilo enum geralmente devem ser substituídas por instâncias de enum.
A enumeração fornece alguns recursos adicionais. As duas classes práticas enummap e enumset são implementações de conjunto padrão especialmente otimizadas para enumeração. Se você souber que uma coleção contém apenas tipos de enum, você deve usar essas coleções especiais em vez de hashmap ou hashset.
Na maioria dos casos, você pode usar o Enum para substituir todas as INTs finais públicas estáticas no código. Eles são comparáveis e podem ser importados estaticamente; portanto, as referências a eles parecem equivalentes, mesmo para classes internas (ou tipos de enum interno). Observe que, ao comparar os tipos de enum, as instruções que os declaram indicam seus valores seqüenciais.
Método estático "Hidden"
Dois métodos estáticos aparecem em todas as declarações do tipo enum. Por serem métodos estáticos nas subclasses da enumeração, não nos métodos de enum, eles não aparecem no javadoc de java.lang.enum.
O primeiro é valores (), que retorna uma matriz de todos os valores possíveis do tipo de enumeração.
O segundo é ValueOf (), que retorna um tipo de enumeração para a string fornecida, que deve corresponder exatamente à declaração do código -fonte.
método
Um dos nossos aspectos favoritos sobre os tipos de enum é que ele pode ter métodos. No passado, talvez você precise escrever algum código para converter o Public Static Final Int e convertê -lo do tipo de banco de dados em um URL JDBC. Agora, o tipo de enumeração em si pode ser feito com um completo
Métodos para manipular o código. Aqui está um exemplo, incluindo o método abstrato do Tipo de enum do banco de dados e a implementação fornecida em cada instância de enum:
public Enum DatabaseType {Oracle {public String getjdbcurl () {...}}, mysql {public string getjdbcurl () {...}}; public abstrate string getjdbcurl ();}Agora, o tipo enum pode fornecer diretamente seus métodos práticos. Por exemplo:
DatabaseType DBType = ...; String jdbcurl = dbtype.getjdbcurl ();
Para obter o URL, você deve saber com antecedência onde está o método de utilidade.
Vararg
O uso de parâmetros mutáveis corretamente limpa algum código lixo. Um exemplo típico é um método de log com um número variável de parâmetros de string:
Log.log.log (string code) log.log (código da string, string arg) log.log (código da string, string arg1, string arg2) log.log (string code, string [] args)
Ao discutir parâmetros variáveis, é interessante que, se você substituir os quatro primeiros exemplos por novos parâmetros variáveis, será compatível:
Copie o código da seguinte forma: log.log (código da string, string ... args)
Todos os parâmetros mutáveis são compatíveis com a fonte - ou seja, se todos os programas de chamada do método log () forem recompilados, todos os quatro métodos poderão ser substituídos diretamente. No entanto, se for necessária uma compatibilidade binária para trás, os três primeiros métodos precisam ser abandonados. Somente o último método com um parâmetro de matriz de string é equivalente à versão variádica, para que possa ser substituída pela versão variádica.
Tipo de elenco
Se você deseja que o chamador saiba que tipo de parâmetros deve ser usado, evite a fundição do tipo com parâmetros mutáveis. Olhando para o exemplo a seguir, a primeira esperança é String, e a segunda esperança é a exceção:
Log.log (objeto ... objetos) {string message = (string) objetos [0]; if (objetos.Length> 1) {Exceção e = (exceção) objetos [1]; // Faça algo com a exceção}} A assinatura do método deve ser a seguinte, e os parâmetros mutáveis correspondentes são declarados usando string e exceção, respectivamente:
A cópia do código é a seguinte: log.log (mensagem de string, Exceção e, objeto ... objetos) {...}
Não use parâmetros variáveis para destruir o sistema de tipos. Ele só pode ser usado quando for fortemente digitado. Para esta regra, PrintStream.printf () é uma exceção interessante: fornece informações de tipo como seu primeiro argumento para que esses tipos possam ser aceitos posteriormente.
Retorno de covariância
O uso básico do retorno covariante é evitar a fundição do tipo quando se sabe que o tipo de retorno de uma implementação é mais específico que a API. No exemplo a seguir, há uma interface de zoológico que retorna um objeto animal. Nossa implementação retorna um objeto AnimalImpl, mas antes do JDK 1.5, ele deve ser declarado devolver um objeto animal. :
interface pública zoo {public animal getanimal ();} classe pública zooimpl implementa o zoológico {public animal getanimal () {return New AnimalImpl ();}}O uso de retornos covariantes substitui três anti-padrões:
Acesso ao campo direto. Para contornar as limitações da API, algumas implementações expõem subclasses diretamente nos campos:
Copie o código da seguinte forma: Zooimpl._animal
Outra forma é realizar uma conversão de baixa no programa de chamadas, sabendo que a implementação é realmente uma subclasse específica:
A cópia do código é a seguinte: (((AnimalImpl) zooimpl.getAnimal ()). ImplMethod ();
A última forma que vi é um método concreto usado para evitar problemas causados por uma assinatura completamente diferente:
A cópia do código é a seguinte: zooimpl._getAnimal ();
Esses três modos têm seus problemas e limitações. Ou não é limpo o suficiente ou expõe detalhes desnecessários da implementação.
Covariância
O modo de retorno covariante é mais limpo, mais seguro e fácil de manter, e não requer tipo de elenco ou métodos ou campos específicos:
public animalImpl getAnimal () {retorna new animalImpl (); } Use resultados:
A cópia do código é a seguinte: zooimpl.getAnimal (). ImplMethod ();
Usando genéricos
Aprenderemos sobre genéricos de duas perspectivas: usando genéricos e construindo genéricos. Não discutimos o uso óbvio de lista, conjunto e mapa. É suficiente saber que as coleções genéricas são poderosas e devem ser usadas com frequência.
Discutiremos o uso de métodos genéricos e o método do compilador de inferir tipos. Geralmente, nada disso dá errado, mas quando algo der errado, a mensagem de erro pode ser muito confusa, para que você precise saber como corrigir esses problemas.
Métodos genéricos
Além dos tipos genéricos, o Java 5 também apresenta métodos genéricos. Neste exemplo do java.util.Collections, uma única lista de elementos é construída. O tipo de elemento da nova lista é inferido com base no tipo de objeto aprovado no método:
Copie o código da seguinte
Exemplo de uso:
Lista pública <Integer> getListofone () {return collectionS.singletonList (1);}No exemplo de uso, passamos em um int. Portanto, o tipo de retorno do método é a lista <Integer>. O compilador infere t para o número inteiro. Isso é diferente dos tipos genéricos, porque você geralmente não precisa especificar parâmetros do tipo explicitamente.
Isso também mostra a interação entre autoboxing e genéricos. O parâmetro de tipo deve ser um tipo de referência: é por isso que estamos obtendo uma lista <TEGER> em vez de list <int>.
Métodos genéricos sem parâmetros
O método do espartylist () é introduzido com os genéricos como uma permutação segura do tipo do campo email_list em java.util.Collections:
Copie o código da seguinte
Exemplo de uso:
Lista pública <Integer> getNointeGers () {return collectionS.Emptylist ();}Ao contrário do exemplo anterior, esse método não possui parâmetros, então como o compilador infere o tipo de t? Basicamente, ele tentará usar o parâmetro uma vez. Se não funcionar, ele tenta usar o tipo de retorno ou atribuição novamente. Neste exemplo, o retorno é a lista <Integer>, então T é inferido como número inteiro.
O que acontece se um método genérico for chamado fora da declaração de retorno ou declaração de atribuição? Em seguida, o compilador não poderá executar a segunda transferência de inferência de tipo. No exemplo a seguir, o emppylist () é chamado de dentro do operador condicional:
Lista pública <Integer> getNointeGers () {return x? Coleções.Emptylist (): NULL;} Como o compilador não pode ver o contexto de retorno e não pode inferir, ele abandona e toma objeto. Você verá uma mensagem de erro como: "Não é possível converter a Lista <ject> para listar <Integer>".
Para corrigir esse erro, os parâmetros de tipo devem ser passados explicitamente para a chamada do método. Dessa forma, o compilador não tentará inferir parâmetros do tipo e pode obter o resultado correto:
A cópia do código é a seguinte: retornar x? Coleções. <Integer> emppylist (): null;
Outro lugar onde isso acontece geralmente está em chamadas de método. Se um método pegar um parâmetro de lista <String> e precisar chamar o esvazio aprovado () para esse parâmetro, essa sintaxe também será necessária.
Fora da coleção
Aqui estão três exemplos de tipos genéricos, que não são coleções, mas usam genéricos de uma maneira nova. Todos os três exemplos vêm das bibliotecas Java padrão:
Copie o código da seguinte forma: Classe <T>
A classe é parametrizada no tipo de classe. Isso possibilita construir uma recém -nastance sem fundição de tipo.
Copie o código da seguinte forma: Comparável <T>
Comparável é parametrizado pelo tipo de comparação real. Isso fornece uma digitação mais forte quando comparado a chamadas (). Por exemplo, a string implementa comparável <string>. Chamar compareto () em qualquer coisa que não seja a string falhará no momento da compilação.
Copie o código da seguinte forma: enum <e estende enum <e>>
Enum é parametrizado pelo tipo enum. Um tipo de enum chamado Color estenderá enum <Cor>. O método getDecLaringClass () retorna um objeto de classe do tipo enumeração, neste exemplo, um objeto de cor. É diferente de getClass (), que pode retornar uma classe sem nome.
Curinga
A parte mais complexa dos genéricos é o entendimento dos caracteres curinga. Discutiremos três tipos de curingas e seus usos.
Primeiro, vamos entender como as matrizes funcionam. Você pode atribuir um valor de um número inteiro [] a um número []. Se você tentar escrever um flutuador para o número [], ele poderá ser compilado, mas falhará em tempo de execução e uma ArrayStoreException aparece:
Inteiro [] ia = novo número inteiro [5]; número [] Na = IA; Na [0] = 0,5; // compila, mas falha em tempo de execução
Se você tentar converter o exemplo diretamente em um genérico, ele falhará no horário de compilação porque a atribuição não é permitida:
List <Integer> ilist = new ArrayList <Teger> (); list <número> nlist = ilist; // não permitidonlist.add (0,5);
Se você usar genéricos, não encontrará um ClassCastException de tempo de execução, desde que o código não apareça ao compilar.
Limite superior curinga
O que queremos é uma lista do tipo exato de elemento desconhecido, que é diferente das matrizes.
Lista <Mum> é uma lista cujo tipo de elemento é o número de tipo específico.
Lista <? O número estende> é uma lista do tipo exato de elemento desconhecido. É número ou seu subtipo.
Limite superior
Se atualizarmos o exemplo inicial e atribuímos o valor para listar <?? estende o número>, então a tarefa será bem -sucedida agora:
Lista <Teger> ilist = new ArrayList <Teger> (); lista <?? estende o número> nlist = ilist; número n = nlist.get (0); nlist.add (0,5); // não é permitido
Podemos obter o número da lista, porque podemos atribuí -la ao número, independentemente do tipo exato de elemento da lista (float, número inteiro ou número).
Ainda não podemos inserir tipos de pontos flutuantes na lista. Isso falhará no momento da compilação, porque não podemos provar que isso é seguro. Se quisermos adicionar tipos de ponto flutuante à lista, ela quebrará o tipo de segurança inicial do ILIST - ele armazena apenas o número inteiro.
Os curingas nos dão mais poder expressivo do que matrizes.
Por que usar curingas
No exemplo a seguir, os curingas são usados para ocultar informações de tipo dos usuários da API. Internamente, o conjunto é armazenado como CustomerImpl. Os usuários da API sabem apenas que estão recebendo um conjunto do qual podem ler o cliente.
Os curingas são necessários aqui, porque é impossível atribuir valores para definir <lustery> do set <ctoreImpl>:
public class CustomerFactory {Set Private <CustomerImpl> _customers; public set <? estende o cliente> getCustomers () {return _customers;}}Wildcard e Return
Outro uso comum de caracteres curinga é usá -lo com retorno covariante. As mesmas regras que a atribuição podem ser aplicadas a retornos covariantes. Se você deseja retornar um tipo genérico mais específico em um método reescrito, o método declarado deve usar um curinga:
interface pública NumberGenerator {list public <? estende o número> generate ();} classe pública fibonaccigerator estende o númeroGenerator {public list <Teger> generate () {...}}Se você deseja usar uma matriz, a interface pode retornar o número [], enquanto a implementação pode retornar o número inteiro [].
Limite inferior
O que estamos falando é principalmente sobre o limite superior selvagem. Há também um curinga de limite inferior. Lista <? Super Number> é uma lista do "tipo de elemento" exato desconhecido, mas pode ser mnumber ou o supertipo de número. Portanto, pode ser uma lista <Mumber> ou uma lista <Becut>.
Os curingas com menor limite são muito menos comuns que os curingas com limites superiores, mas são necessários quando necessários.
Limite inferior e superior
Lista <? estende o número> readlist = new ArrayList <Teger> (); número n = readlist.get (0); lista <?? super número> writelist = new ArrayList <ject> (); writelist.add (novo inteiro (5));
O primeiro é uma lista de números que podem ser lidos.
O segundo é uma lista de números para os quais você pode escrever.
Curinga ilimitado
Por fim, o conteúdo de uma lista <?> Pode ser do tipo e é quase o mesmo que a lista <? estende o objeto>. Os objetos podem ser lidos a qualquer momento, mas o conteúdo não pode ser gravado na lista.
Curingas em APIs públicas
Em suma, como mencionado anteriormente, os curingas são muito importantes para ocultar detalhes da implementação do chamador, mas mesmo que os curingas limitados mais baixos pareçam fornecer acesso somente leitura, eles não são o caso devido a métodos não genéricos como remover (int position). Se você deseja uma coleção verdadeiramente inalterada, pode usar o método em java.util.Collection como não modificada ().
Lembre -se de curinga ao escrever APIs. Geralmente, ao passar os tipos genéricos, você deve tentar usar curingas. Permite que mais chamadores acessem a API.
Ao receber lista <? estende o número> em vez de list <número>, o método a seguir pode ser chamado por muitos tipos diferentes de listas:
A cópia do código é a seguinte: Removenegativos void (lista <? Extende o número> Lista);
Construa tipos genéricos
Agora discutiremos a construção de nossos próprios tipos genéricos. Mostraremos alguns exemplos em que a segurança do tipo pode ser aprimorada usando genéricos e também discutiremos alguns problemas comuns ao implementar tipos genéricos.
Funções semelhantes a coleções
O primeiro exemplo de uma classe genérica é um exemplo de estilo de coleção. O par possui dois parâmetros de tipo e o campo é uma instância do tipo:
Public Final Class Par <A, b> {public final a primeiro; public final B Second; Public par (um primeiro, B segundo) {this.first = primeiro; this.Second = Second;}}Isso possibilita retornar dois itens de um método sem escrever uma classe dedicada para cada combinação de dois tipos. Outra maneira é retornar o objeto [], que é o tipo não é seguro ou desarrumado.
No uso a seguir, retornamos um arquivo e um booleano do método. O cliente do método pode usar campos diretamente sem tipo de fundição:
public par <arquivo, boolean> getFileAndWriteStatus (String Path) {// Crie arquivo e statusEngurn novo par <arquivo, boolean> (arquivo, status);} par <arquivo, boolean> resultado = getFileandWriteStatus ("..."); arquivo f = resultado.first; BOOLEANS Writeable = Result.Second;Fora da coleção
No exemplo a seguir, os genéricos são usados para segurança adicional de tempo de compilação. Ao parametrizar a classe DBFactory para o tipo de pares criado, você está realmente forçando a subclasse de fábrica a retornar um subtipo específico de um par:
Classe abstrata public dbFactory <t estende dbpeer> {abstrato protegido t createEmptyPeer (); public list <t> get (string restrins) {list <t> pares = new ArrayList <T> (); // Database Magicreturn Peers;}}Ao implementar o DBFactory <Cliente>, o cliente Factory deve devolver um cliente do CreateEmptyPeer ():
public class CustomerFactory estende dbfactory <lusteral> {public Customer CreateEmptyPeer () {return new Customer ();}}Métodos genéricos
Se você deseja impor restrições aos tipos genéricos entre parâmetros e entre parâmetros e tipos de retorno, você pode usar métodos genéricos:
Por exemplo, se a função de inversão escrita for invertida posicionalmente, um método genérico poderá não ser necessário. No entanto, se você deseja que a inversão retorne uma nova lista, convém que o tipo de elemento da nova lista seja o mesmo que o tipo da lista de entrada. Nesse caso, é necessário um método genérico:
Copie o código da seguinte
Concreto
Ao implementar uma classe genérica, convém construir uma matriz t []. Como os genéricos são implementados por apagamento, isso não é permitido.
Você pode tentar lançar o objeto [] para t []. Mas isso não é seguro.
Soluções concretas
De acordo com a convenção de tutoriais genéricos, a solução usa um "token do tipo". Ao adicionar um parâmetro de classe <t> ao construtor, você pode forçar o cliente a fornecer o objeto de classe correto para os parâmetros de tipo da classe:
classe pública ArrayExample <t> {classe privada <t> clazz; public ArrayExample (classe <t> clazz) {this.clazz = clazz;} public t [] getArray (int size) {return (t []) Array.NewInstance (clazz, tamanho);}}Para construir um ArrayExample <string>, o cliente deve passar a String.class para o construtor porque o tipo de string.class é classe <string>.
Ter um objeto de classe torna possível construir um grupo de um tipo de elemento correto
Espero que este artigo seja útil para a programação Java de todos.