O conteúdo a seguir são perguntas e respostas responsáveis dadas após o conjunto de perguntas e respostas originais da entrevista em Java foram totalmente revisadas. Havia muitas perguntas duplicadas e perguntas valiosas nas perguntas originais, e muitas respostas de referência também estavam erradas. O conjunto de perguntas de entrevista Java modificado faz referência à versão mais recente do JDK, remove conteúdo inútil, como EJB 2.x, e complementa a estrutura de dados e perguntas relacionadas ao algoritmo, perguntas clássicas de programação de entrevistas, arquitetura técnica de grande site, sistema operacional, sistema de dados, testes de software, padrões de design, UML e outros conteúdos. Ao mesmo tempo, muitos pontos de conhecimento também foram analisados em profundidade, como o design do método HashCode, a pilha e a geração de coleta de lixo, a nova programação simultânea de Java, Nio.2, etc. Acredito que será benéfico para os programadores Java que estão preparando a se juntar.
Conjunto de perguntas da entrevista do programador Java (1-50)
1. Java Basics
1. Quais são os aspectos das características orientadas a objetos?
Resposta: Os principais aspectos dos recursos orientados a objetos são:
1) Resumo: Abstração é o processo de resumir as características comuns de um tipo de objeto em uma classe de objetos, incluindo abstração de dados e abstração de comportamento. A abstração se concentra apenas em quais atributos e comportamentos o objeto tem e não presta atenção nos detalhes desses comportamentos.
2) herança: herança é o processo de obtenção de informações de herança de classes existentes e criação de novas classes. A classe que fornece informações herdadas é chamada de classe pai (Superclass, classe base); A classe que obtém informações herdadas é chamada de subclasse (classe derivada). A herança fornece ao sistema de software em mudança um certo grau de continuidade, e a herança também é um meio importante de encapsular fatores variáveis no programa (se você não conseguir entender, leia a parte sobre o modo Bridge no "java e padrões" do Dr. Yan Hong "ou" Projeto padrões requintados ").
3) Encapsulamento: Geralmente, acredita -se que o encapsulamento seja vincular dados ao método de dados operacionais, e o acesso aos dados só pode ser alcançado através da interface definida. A essência da orientação a objetos é retratar o mundo real como uma série de objetos completamente autônomos e fechados. O método que escrevemos em uma classe é encapsular detalhes da implementação; Escrevemos uma classe é para encapsular operações de dados e dados. Pode -se dizer que a embalagem é ocultar tudo o que pode ser oculto e fornece apenas a interface de programação mais simples para o mundo exterior (você pode pensar na diferença entre uma máquina de lavar comuns e uma máquina de lavar totalmente automática. É óbvio que a máquina de lavar totalmente automática é embalada melhor e, portanto, mais fácil de operar; o smartphone que estamos usando agora também é bem empacotado porque os poucos botões.
4) Polimorfismo: O polimorfismo refere -se a permitir que objetos de diferentes subtipos respondam de maneira diferente à mesma mensagem. Simplificando, é chamar o mesmo método com a mesma referência de objeto, mas fazer coisas diferentes. Os polimorfismos são divididos em polimorfismos em tempo de compilação e polimorfismos em tempo de execução. Se o método do objeto for considerado um serviço fornecido pelo objeto para o mundo exterior, o polimorfismo de tempo de execução pode ser explicado como: quando o sistema A acessa os serviços fornecidos pelo sistema B, o sistema B tem várias maneiras de fornecer serviços, mas tudo é transparente para o sistema A (assim como um barbeador elétrico, o sistema de alimentação e o sistema de alimentação é o sistema B, pode ser o sistema B pode ser alimentado por um sistema de sistema que pode ser alimentado por um sistema de alimentação, que pode ser o sistema B, o sistema B pode ser alimentado. Objetos, mas não sabe qual é a implementação subjacente do sistema de fonte de alimentação e como ele obtém energia). Método Sobrecarregando os implementos O polimorfismo de tempo de compilação (também conhecido como pré-ligação), enquanto o método substitui o polimorfismo em tempo de execução (também conhecido como pós-regresso). O polimorfismo de tempo de execução é a coisa mais essencial sobre orientação de objetos. Para implementar o polimorfismo, duas coisas precisam ser feitas: 1. Reescrita do método (a subclasse herda a classe pai e reescreve métodos existentes ou abstratos na classe pai); 2. Modelagem de objetos (referindo -se ao objeto de tipo de criança com a referência do tipo pai, para que a mesma referência chama o mesmo método mostre comportamentos diferentes de acordo com os diferentes objetos de subclasse).
2. Qual é a diferença entre acessar modificadores públicos, privados, protegidos e não escrever (padrão)?
Resposta: As diferenças são as seguintes:
O escopo é o mesmo que a subclasse do pão.
público √ √ √ √ √ √
protegido √ √ √ ×
padrão √ √ × ×
Privado √ × × ×
O padrão é padrão quando os membros da classe não escrevem modificação de acesso. Por padrão, é equivalente ao público para outras classes no mesmo pacote e privado para outras classes que não estão no mesmo pacote. Protegido é equivalente ao público a subclasses e privado para classes que não estão no mesmo pacote que não têm relacionamento entre pais e filhos.
3. String é o tipo de dados mais básico?
Resposta: Não. Existem apenas 8 tipos de dados básicos em java: byte, curto, int, longa, flutuante, dupla, char e booleano; Exceto pelo tipo básico (tipo primitivo) e tipo de enumeração (tipo de enumeração), o restante são tipos de referência (tipo de referência).
4. Float f = 3,4; Está correto?
Resposta: Incorreto. 3.4 é um número de precisão dupla. A atribuição dupla ao tipo de ponto flutuante (float) causará perda de precisão (cobrança de baixo, também conhecida como estreitamento), então você precisa fundir o float f = (float) 3.4; ou escreva flutuação f = 3,4f;.
5. S1 curto = 1; s1 = s1 + 1; Há algo errado? s1 curto = 1; s1 += 1; Há algo errado?
Resposta: para curto S1 = 1; s1 = s1 + 1; Como 1 é um tipo int, o resultado da operação S1+1 também é um tipo int e um tipo de elenco é necessário para atribuir valor ao tipo curto. E s1 curto = 1; s1 += 1; pode ser compilado corretamente porque S1+= 1; é equivalente a S1 = (curto) (S1 + 1); Existem elencos implícitos.
6. Existe um Goto em Java?
Resposta: Goto é uma palavra reservada em Java e não é usada na versão atual do Java. (A list of Java keywords is given in the appendix of the book "The Java Programming Language" written by James Gosling (the father of Java), which includes goto and const, but these two are keywords that are currently unusable, so some places call it reserved words. In fact, the word reserved words should have a broader meaning, because programmers familiar with C language know that words or combinations of words that have special meanings used in the system library são consideradas palavras reservadas)
7. Qual é a diferença entre int e inteiro?
Resposta: Java é uma linguagem de programação quase pura orientada a objetos, mas, por uma questão de conveniência de programação, ainda apresenta tipos de dados básicos que não são objetos. No entanto, para operar esses tipos de dados básicos como objetos, o Java introduziu a classe Wrapper correspondente para cada tipo de dados básico. A classe de embalagem de int é inteira. Desde que o JDK 1.5, um mecanismo automático de embalagem/unboxing foi introduzido, para que os dois possam ser convertidos um ao outro.
Java fornece tipos de invólucros para cada tipo primitivo:
Tipos primitivos: boolean, char, byte, curto, int, longo, flutuante, duplo
Tipos de embalagem: booleano, caráter, byte, curto, inteiro, longo, flutuante, duplo
pacote com.lovo; // Por que perguntar sobre hovertree.compublic class AutounBoxingTest {public static void main (string [] args) {integer a = new Integer (3); Número inteiro b = 3; // Caixa automática 3 no tipo inteiro int c = 3; System.out.println (a == b); // false, as duas referências não fazem referência ao mesmo sistema de objeto.out.println (a == c); // TRUE A Automaticamente uns caixas no tipo int e depois se compara com C}}Adicionado: encontrei recentemente uma pergunta de entrevista, que também está relacionada à embalagem e unboxing automáticas, o código é o seguinte:
classe pública test03 {public static void main (string [] args) {integer f1 = 100, f2 = 100, f3 = 150, f4 = 150; System.out.println (f1 == f2); System.out.println (f3 == f4); }} // hovertree.comSe você não entende, é fácil pensar que ambas as saídas são verdadeiras ou falsas. Primeiro de tudo, é importante observar que as quatro variáveis F1, F2, F3 e F4 são todos objetos inteiros; portanto, a seguinte == operação compara não valores, mas referências. Qual é a essência da embalagem? Quando atribuímos um valor int a um objeto inteiro, chamaremos o valor do método estático da classe inteira. Se olharmos para o código -fonte do ValueOF, saberemos o que está acontecendo.
public static integer valueof (int i) {if (i> = Integercache.low && i <= Integercache.high) Retorne Integercache.cache [i + (-integercache.low)]; devolver novo número inteiro (i); } // hovertree.comO Integercache é uma classe interna de número inteiro, e seu código se parece com o seguinte:
/** * Cache para suportar a semântica de identidade do objeto da auto -boxação para valores entre * -128 e 127 (inclusive) conforme exigido pelo JLS. * * O cache é inicializado no primeiro uso. O tamanho do cache * pode ser controlado pela opção {@code -xx: autoboxcachemax = <tamanho>}. * Durante a inicialização da VM, java.lang.integer.integercache.high Propriedade * pode ser definida e salva nas propriedades do sistema privado na classe * Sun.misc.vm. * hovertree.com */ classe estática privada Integercache {estática final int low = -128; estático final int alto; Cache inteiro final estático []; estático {// Alto valor pode ser configurado pela propriedade int h = 127; String integegercacheHighPropValue = Sun.misc.vm.getSavedProperty ("java.lang.integer.integercache.high"); if (integercacheHighPropValue! = null) {try {int i = parseInt (integercacheHighPropValue); i = math.max (i, 127); // O tamanho máximo da matriz é inteiro.max_value h = math.min (i, integer.max_value -(-low) -1); } catch (númeroFormatexception nfe) {// Se a propriedade não puder ser analisada em um int, ignore -o. }} alta = h; cache = novo número inteiro [(alto - baixo) + 1]; int j = baixo; for (int k = 0; k <cache.length; k ++) cache [k] = novo número inteiro (j ++); // intervalo [-128, 127] deve ser internalizado (JLS7 5.1.7) assert integercache.high> = 127; } private integercache () {}}Simplificando, se o valor literal estiver entre -128 e 127, o novo objeto inteiro não será novo, mas o objeto inteiro no pool constante será referenciado diretamente. Portanto, o resultado de F1 == F2 na pergunta de entrevista acima é verdadeiro e o resultado de F3 == F4 é falso. Quanto mais simples as perguntas da entrevista, mais mistério existem e o entrevistador precisa ter habilidades consideráveis.
8. Qual é a diferença entre & &&?
Resposta: Existem dois usos do Operador: (1) bit e (2) lógicos e. O operador && é um curto -circuito e operação. A diferença entre lógica e curto circuitos é muito enorme, embora ambos exijam que os valores booleanos nas extremidades esquerda e direita do operador sejam fiéis ao valor de toda a expressão. && é chamado de operação de curto-circuito porque, se o valor da expressão à esquerda de && for false, a expressão à direita será diretamente circuitada e a operação não será executada. Muitas vezes podemos precisar usar && em vez de &. Por exemplo, ao verificar se o nome do usuário não é nulo e não é uma string vazia, ela deve ser escrita como: nome de usuário! = Null &&! UserName.equals (""). A ordem dos dois não pode ser trocada e o Operador não pode ser usado, porque se a primeira condição não for verdadeira, a comparação igual da string não poderá ser executada, caso contrário, uma NullPointerException será gerada. Nota: O mesmo vale para a diferença entre lógico ou operador (|) e curto-circuito ou operador (||).
Adicionado: se você estiver familiarizado com o JavaScript, poderá sentir o poder da computação de curto-circuito mais. Se você deseja se tornar um mestre do JavaScript, comece tocando computação de curto-circuito.
9. Explique o uso de áreas de pilha, pilha e armazenamento estático na memória.
Resposta: Geralmente, definimos uma variável do tipo de dados básico, uma referência de objeto e o armazenamento no local de chamadas de função usam o espaço da pilha na memória; e os objetos criados através da nova palavra -chave e construtor são colocados no espaço da pilha; Os literais do programa, como 100, "Hello e Constantes escritos diretamente são colocados na área de armazenamento estático. O espaço da pilha opera mais rápido, mas também é muito pequeno. Geralmente, um grande número de objetos é colocado no espaço da pilha, e toda a memória, incluindo a memória virtual no disco rígido, pode ser usada como espaço de heap.
String str = new String ("Hello");
Na declaração acima, o STR é colocado na pilha, o objeto String criado com o novo é colocado na pilha e o "hello" literal é colocado na área de armazenamento estático.
Suplemento: Uma versão mais recente do Java usa uma tecnologia chamada "Análise de Escape" que pode colocar alguns objetos locais na pilha para melhorar o desempenho operacional dos objetos.
10. O que é Math.Round (11.5) igual a? O que é Math.Round (-11,5) igual a?
RESPOSTA: O valor de retorno da Math.Round (11.5) é 12 e o valor de retorno da matemática (-11,5) é -11. O princípio do arredondamento é adicionar 0,5 ao parâmetro e depois arredondar -o.
11. Swtich pode agir em byte, agir longas na corda?
Resposta: No início do JDK, no Switch (EXPR), EXPR pode ser byte, curto, char e int. A partir da versão 1.5, o Java introduziu os tipos de enum (enums) e o EXPR também pode ser enums, a partir da versão 1.7 do JDK e também uma string (string). Tipo longo não é permitido.
12. Use o método mais eficiente para calcular 2 vezes 8?
Resposta: 2 << 3 (mover 3 bits à esquerda é equivalente a multiplicar por 2 até o poder de 3, e mover 3 bits à direita é equivalente a dividir por 2 ao poder de 3).
Suplemento: Quando reescrevemos o método HashCode para a aula que escrevemos, podemos ver o código mostrado abaixo. Na verdade, não entendemos bem por que usamos essa multiplicação para gerar um código de hash (código de hash) e por que esse número é um número primo e por que o número 31 é geralmente selecionado? Você pode Baidu sobre as respostas para as duas primeiras perguntas. Escolha 31 porque as operações de mudança e subtração podem ser usadas em vez de multiplicação, alcançando um melhor desempenho. Falando nisso, você pode ter pensado em: 31 * num <==> (num << 5) - NUM, a mudança dos 5 bits esquerda é equivalente a multiplicar por 2 para a 5ª potência (32) e a própria subtração é equivalente a multiplicar por 31. Todas as VMs podem concluir automaticamente essa otimização.
pacote com.loonstudio; classe pública PhoneNumber {private int AreaCode; prefixo de string privado; Linho de linha de cordas privado; @Override public int hashCode () {final int prime = 31; int resultado = 1; Result = Prime * Result + AreaCode; resultado = prime * resultado + ((linenumber == null)? 0: lineNumber.hashcode ()); resultado = prime * resultado + ((prefixo == null)? 0: prefix.hashcode ()); resultado de retorno; } @Override public boolean equals (object obj) {if (this == obj) retorna true; if (obj == null) retorna false; if (getClass ()! = obj.getclass ()) retornar false; PhoneNumber Other = (PhoneNumber) OBJ; if (AreaCode! = Other.areacode) retornar false; if (linenumber == null) {if (other.linenumber! = null) retorna false; } else if (! lineNumber.equals (outros.lineNumber)) retorna false; if (prefix == null) {if (other.prefix! = null) retorna false; } else if (! prefix.equals (outros.prefix)) retorna false; retornar true; }} // Por que perguntar sobre hovertree.com13. Existe um método de comprimento () para matrizes? Existe um método de comprimento () para string?
Resposta: A matriz não possui um método de comprimento (), mas possui um atributo de comprimento. String possui um método de comprimento (). No JavaScript, a obtenção do comprimento da string é obtida através do atributo de comprimento, que é facilmente confundido com Java.
14. Em Java, como sair dos atuais loops múltiplos aninhados?
Resposta: Adicione uma marca como A antes do loop mais externo e use o intervalo a; Vários loops podem ser quebrados. (Java suporta declarações de quebra e continuação marcadas, e suas funções são um pouco semelhantes às declarações GoTo em C e C ++, mas, assim como evitar o Goto, você deve evitar quebra marcada e continuar porque não tornará seu programa mais elegante e, muitas vezes, terá o efeito oposto; portanto, essa sintaxe é realmente melhor.
15. O construtor pode ser substituído?
Resposta: O construtor não pode ser herdado, por isso não pode ser reescrito, mas pode ser sobrecarregado.
16. Dois objetos têm o mesmo valor (x.equals (y) == true), mas podem ter códigos de hash diferentes. Está certo?
Resposta: Não, se os dois objetos x e y satisfazem x.equals (y) == true, seu código de hash deve ser o mesmo. Java estipula o método eqauls e o método HashCode da seguinte forma: (1) Se dois objetos forem iguais (o método igual a retorno verdadeiro), seus valores de código de hash deverão ser os mesmos; (2) Se os códigos de hash dos dois objetos forem iguais, eles não são necessariamente os mesmos. Obviamente, você não precisa fazer o que é necessário, mas se violar os princípios acima, descobrirá que, ao usar contêineres, o mesmo objeto pode aparecer na coleção de conjuntos, e a eficiência de adicionar novos elementos será bastante reduzida (para sistemas usando armazenamento de hash, conflitos frequentes nos códigos de hash causarão um declínio acentuado no desempenho do acesso).
Suplemento: Muitos programas Java conhecem os métodos iguais e hashcode, mas muitas pessoas simplesmente sabem disso. Na obra-prima de Joshua Bloch, "Java eficaz" (muitas empresas de software, "Effeitor Java", "Java Programming Thoughts" e "Refatoration: Melhorando a qualidade do código existente" são livros obrigatórios por Java Programmers (se você não o leu, apresse-o e compre uma Amazing. Verdadeiro), e a simetria (x.Equals (y) retorna true, y.equals (x) também deve ser que ele deve retornar verdadeiro), transitividade (x.equals (y) e y.quals (z) também deve retornar verdadeiro) e consistência (quando as informações do objeto referenciadas por x e y não são modificadas, múltiplas chamadas para X. (y) devem obter o retorno), o mesmo valor de retorno), e sim, não é modificado, que não é de retorno X. Os truques para implementar métodos de alta qualidade são iguais aos métodos: 1. Use o operador == para verificar "se o parâmetro é uma referência a esse objeto"; 2. Use a instância do operador para verificar "se o parâmetro é o tipo correto"; 3. Para atributos -chave na classe, verifique se os atributos passados para o objeto correspondem a ele; 4. Depois de escrever o método igual, pergunte -se se ele satisfaz simetria, transitividade e consistência; 5. Sempre reescreva o código de hash quando a reescrita é igual a; 6. Não substitua o objeto no objeto nos parâmetros do método igual por outros tipos e não se esqueça da anotação @Override ao reescrever.
17. A classe String pode ser herdada?
Resposta: A classe String é uma classe final e não pode ser herdada.
Suplemento: A herdadora de string é um comportamento errado em si. A melhor maneira de reutilizar os tipos de string é a associação (HAS-A) e não a herança (IS-A).
18. Quando um objeto é passado como um parâmetro para um método, esse método pode alterar as propriedades do objeto e retornar o resultado alterado. Então, é um passe de valor ou um passe de referência aqui?
Resposta: É transferência de valor. A linguagem de programação Java passa apenas parâmetros com valores. Quando uma instância de objeto é passada para um método como um parâmetro, o valor do parâmetro é uma referência ao objeto. As propriedades de um objeto podem ser alteradas durante o processo de chamada, mas a referência ao objeto nunca mudará. Em C ++ e C#, o valor dos parâmetros passados pode ser alterado passando referências ou transferindo parâmetros.
Suplemento: É realmente inconveniente não passar referências em Java, que não foi melhorado no Java 8. É exatamente assim que um grande número de classes de invólucro aparece no código escrito em Java (colocando referências que precisam ser modificadas através de chamadas de método em uma classe de invólucro e depois passando o objeto Wrapper em um método). Essa abordagem apenas tornará o código inchado, especialmente para desenvolvedores que se transformam de programadores C e C ++ para Java para intoleráveis.
19. Qual é a diferença entre String e StringBuilder e StringBuffer?
Resposta: A plataforma Java fornece dois tipos de strings: String e StringBuffer/StringBuilder, que podem armazenar e manipular strings. Onde a string é uma string somente leitura, o que significa que o conteúdo da string referenciado pela string não pode ser alterado. Os objetos da string representados pelas classes StringBuffer e StringBuilder podem ser modificados diretamente. StringBuilder foi introduzido no JDK 1.5. É exatamente o mesmo que o método do StringBuffer, a diferença é que ele é usado em um único ambiente rosqueado, porque todos os aspectos dele não são modificados por sincronizados, por isso é um pouco mais eficiente que o StringBuffer.
Suplemento 1: Existe uma pergunta de entrevista: Existe alguma situação em que o uso de + para fazer concatenação de string é melhor do que chamar o método Apênder do objeto StringBuffer/StringBuilder? Se a string obtida após a conexão já existir na área de armazenamento estático, o uso de + para concatenação da string será melhor que o método APEND do StringBuffer/StringBuilder.
Suplemento 2: a seguir também é uma pergunta de entrevista, pedindo à saída do programa para ver se você pode dar a resposta correta.
pacote com.lovo; // Por que perguntar sobre hovertree.compublic class stringequaltest {public static void main (string [] args) {string a = "programação"; String b = new String ("Programação"); String c = "Program" + "Ming"; System.out.println (a == b); System.out.println (a == c); System.out.println (A.Equals (b)); System.out.println (A.Equals (C)); System.out.println (a.intern () == b.intern ()); }}20. A diferença entre sobrecarga e substituição. Os métodos sobrecarregados podem ser distinguidos com base no tipo de retorno?
RESPOSTA: Tanto a sobrecarga quanto a reescrita de métodos são maneiras de implementar o polimorfismo. A diferença é que os implementos anteriores compilam o polimorfismo do tempo, enquanto o último implementa o polimorfismo em tempo de execução. Sobrecarga ocorre em uma classe. Se um método com o mesmo nome tiver uma lista de parâmetros diferentes (diferentes tipos de parâmetros, número diferente de parâmetros ou ambos), ele é considerado uma sobrecarga; A reescrita ocorre entre a subclasse e a classe pai. A reescrita exige que o método reescrito da subclasse e a classe pai tenham o mesmo tipo de retorno que o método reescrito da classe pai, que é melhor acesso do que o método reescrito da classe pai e não pode declarar mais exceções do que o método reescrito da classe pai (princípio de substituição de Rischer). A sobrecarga não possui requisitos especiais para tipos de devolução.
Suplemento: Huawei fez uma pergunta na pergunta da entrevista: por que você não pode distinguir a sobrecarga com base no tipo de retorno e informar sua resposta!
21. Descreva o princípio e o mecanismo dos arquivos da classe de carregamento da JVM?
Resposta: O carregamento de classes na JVM é implementado por um carregador de classe (classe de classe) e suas subclasses. O carregador de classe no Java é um importante componente do sistema de tempo de execução do Java, responsável por encontrar e carregar classes nos arquivos de classe em tempo de execução.
Reabastecer:
1. Devido à natureza cruzada do Java, o programa de origem Java compilado não é um programa executável, mas um ou mais arquivos de classe. Quando um programa Java precisa usar uma classe, a JVM garante que a classe tenha sido carregada, conectada (verificada, preparada e analisada) e inicializada. O carregamento da classe refere -se à leitura de dados do arquivo .Class. Class na memória. Geralmente, criando uma matriz de bytes para lê -la no arquivo .class e, em seguida, gerar um objeto de classe correspondente à classe carregada. Após a conclusão do carregamento, o objeto de classe ainda está incompleto, para que a classe não esteja disponível no momento. Quando a classe é carregada, ela entra no estágio de conexão. Este estágio inclui três etapas: verificação, preparação (alocando memória para variáveis estáticas e definir o valor inicial padrão) e a análise (substitua referências de símbolo por referências diretas). Finalmente, a JVM inicializa a classe, incluindo: 1. Se a classe tiver uma classe pai direta e a classe não foi inicializada, a classe pai será inicializada primeiro; 2. Se houver declarações de inicialização na classe, essas declarações de inicialização serão executadas por sua vez.
2. O carregamento da classe é feito pelo carregador de classe, que inclui: carregador de raiz (bootstrap), carregador de extensão (extensão), carregador de sistemas (sistema) e carregador de classe definido pelo usuário (subclasse de java.lang.classloader). A partir do JDK 1.2, o processo de carregamento da classe adota o mecanismo de delegação do pai (PDM). O PDM garante melhor a segurança da plataforma Java. Nesse mecanismo, o próprio bootstrap da JVM é o carregador raiz, e outros carregadores têm e possui apenas um carregador de classe pai. A classe carregando primeiro solicita que o carregador de classe pai seja carregado e o carregador de classe pai será carregado apenas pelo carregador de subclasse quando estiver impotente. A JVM não fornece referências ao Bootstrap aos programas Java. Aqui estão algumas instruções para vários carregadores de classe:
a) Bootstrap: Geralmente é implementado usando o código local e é responsável por carregar a biblioteca básica da classe Core da JVM (rt.jar);
b) Extensão: Carregue a biblioteca de classes do diretório especificado pela propriedade do sistema java.ext.dirs e seu carregador pai é o bootstrap;
c) Sistema: também conhecido como carregador de classe de aplicativo, sua classe pai é extensão. É o carregador de classe mais amplamente usado. Ele registra classes do diretório especificado pela variável de ambiente ou atributo do sistema java.class.path e é o carregador pai padrão do carregador definido pelo usuário.
22. Um caractere chinês pode ser armazenado em uma variável de tipo de char? Por que?
Resposta: O tipo de char pode armazenar um caractere chinês, porque a codificação usada em java é unicode (nenhuma codificação específica é selecionada e os caracteres são usados diretamente no número do conjunto de caracteres, que é o único método unificado). Um tipo de char ocupa 2 bytes (16 bits), portanto, não há problema em colocar um chinês.
Suplemento: Usar Unicode significa que os caracteres têm manifestações diferentes dentro e fora da JVM e são unicode dentro da JVM. Quando esse caractere é transferido da JVM para o exterior (por exemplo, armazenado no sistema de arquivos), é necessária uma conversão de codificação. Portanto, o Java possui fluxos de bytes e fluxos de caracteres, bem como fluxos de conversão que convertem entre fluxos de caracteres e fluxos de bytes, como o InputStreamReader e o OutputStreamReader. Essas duas classes são classes adaptadoras entre fluxos de bytes e fluxos de caracteres e realizam a tarefa de codificar a conversão; Para os programadores C, para concluir essa conversão de codificação, eles provavelmente confiam nas características da memória compartilhada da União (União/Comunidade).
23. Quais são as semelhanças e diferenças entre classe abstrata e interface?
Resposta: Tanto classes abstratas quanto interfaces não podem ser instanciadas, mas referências a classes abstratas e tipos de interface podem ser definidas. Se uma classe herda uma classe abstrata ou implementa uma interface, ela precisa implementar todos os métodos abstratos nela; caso contrário, a classe ainda precisará ser declarada como uma classe abstrata. As interfaces são mais abstratas do que as classes abstratas, porque os construtores podem ser definidos em classes abstratas, e pode haver métodos abstratos e métodos concretos, enquanto os construtores não podem ser definidos em interfaces, e todos os métodos são métodos abstratos. Os membros de classes abstratos podem ser privadas, inadimplentes, protegidas e públicas, enquanto os membros em interfaces são todos públicos. As variáveis de membros podem ser definidas em classes abstratas, enquanto as variáveis de membros definidas em interfaces são na verdade constantes. As classes com métodos abstratos devem ser declarados como classes abstratas, e classes abstratas não têm necessariamente métodos abstratos.
24. Qual é a diferença entre classes aninhadas estáticas e classes internas (classe interna)?
Resposta: A classe aninhada estática é uma classe interna declarada como estática, que pode ser instanciada sem depender de instâncias de classe externa. As classes internas usuais precisam ser instanciadas depois que a classe externa é instanciada e a sintaxe parece bastante estranha, como mostrado abaixo.
pacote com.lovo; / ** * Classe de poker (um baralho de poker) Por que perguntar sobre hovertree.com * */ public classe poker {private static string [] suites = {"spade", "rose", "grama flor", "cubo"}; private static int [] faces = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}; cartões de cartão privado []; / *** construtor**/ public poker () {cartões = novo cartão [52]; for (int i = 0; i <suites.length; i ++) {for (int j = 0; j <faces.length; j ++) {cartões [i * 13+j] = novo cartão (suítes [i], faces [j]); }}} / ** * shuffle (aleatório fora de ordem) * * / public void shuffle () {for (int i = 0, len = card.length; i <len; i ++) {int index = (int) (math.random () * len); Cartão temp = cartões [índice]; cartões [index] = cartões [i]; cartões [i] = temp; }} / *** OFEREÇO DE CARTO* @PARAM INDEX A posição do acordo** / public Card Deal (int index) {Return Cards [Index]; } / ** * Classe de cartão (um pedaço de poker) * [classe interna] * @author luo hao * * / cartão público {private string suite; // se adequa ao rosto privado int; // Pontos de cartão público (suíte de string, int face) {this.suite = suite; this.face = face; } @Override public string tostring () {string facest = ""; switch (face) {case 1: facest = "a"; quebrar; caso 11: facest = "j"; quebrar; caso 12: facest = "q"; quebrar; caso 13: facest = "k"; quebrar; padrão: facest = string.valueof (face); } retornar suíte + facest; }}} Classe de teste:
pacote com.lovo; classe Pokertest {public static void main (string [] args) {poker poker = new poker (); poker.shuffle (); // shuffle poker.card c1 = poker.deal (0); // serve o primeiro cartão // para cartões de classe interna não estáticos // pode ser criado apenas o objeto de cartão por meio de seu objeto de poker de classe externo poker.card c2 = Poker.new Card ("Red Heart", 1); // Crie um cartão você mesmo System.out.println (C1); // o primeiro system.out.println (c2); // Print: Red Heart A}} // Por que perguntar sobre hovertree.com25. Haverá um vazamento de memória em Java? Descreva -o brevemente.
Resposta: Em teoria, o Java não terá vazamentos de memória porque possui um mecanismo de coleta de lixo (GC) (esse também é uma razão importante pela qual o Java é amplamente utilizado na programação do lado do servidor); No entanto, no desenvolvimento real, pode haver objetos inúteis, mas acessíveis, e esses objetos não podem ser reciclados por GC e vazamentos de memória ocorrerão. Um exemplo é que os objetos na sessão de Hibernate (cache de nível um) são persistentes e o coletor de lixo não reciclará esses objetos, mas pode haver objetos inúteis de lixo nesses objetos. O exemplo a seguir também mostra o vazamento de memória em Java:
pacote com.lovo; // Por que perguntar sobre hovertree.comimport java.util.arrays; importar java.util.EmptyStackException; public class Mystack <T> {private T [] Elements; private int tamanho = 0; private estático final int init_capacity = 16; public MyStack() { elements = (T[]) new Object[INIT_CAPACITY]; } public void push(T elem) { ensureCapacity(); elements[size++] = elem; } public T pop() { if(size == 0) throw new EmptyStackException(); return elements[--size]; } private void ensureCapacity() { if(elements.length == size) { elements = Arrays.copyOf(elements, 2 * size + 1); }}}上面的代码实现了一个栈(先进后出(FILO))结构,乍看之下似乎没有什么明显的问题,它甚至可以通过你编写的各种单元测试。然而其中的pop方法却存在内存泄露的问题,当我们用pop方法弹出栈中的对象时,该对象不会被当作垃圾回收,即使使用栈的程序不再引用这些对象,因为栈内部维护着对这些对象的过期引用(obsolete reference)。在支持垃圾回收的语言中,内存泄露是很隐蔽的,这种内存泄露其实就是无意识的对象保持。如果一个对象引用被无意识的保留起来了,那么垃圾回收器不会处理这个对象,也不会处理该对象引用的其他对象,即使这样的对象只有少数几个,也可能会导致很多的对象被排除在垃圾回收之外,从而对性能造成重大影响,极端情况下会引发Disk Paging(物理内存与硬盘的虚拟内存交换数据),甚至造成OutOfMemoryError。
26、抽象的(abstract)方法是否可同时是静态的(static),是否可同时是本地方法(native),是否可同时被synchronized修饰?
答:都不能。抽象方法需要子类重写,而静态的方法是无法被重写的,因此二者是矛盾的。本地方法是由本地代码(如C代码)实现的方法,而抽象方法是没有实现的,也是矛盾的。synchronized和方法的实现细节有关,抽象方法不涉及实现细节,因此也是相互矛盾的。
27、静态变量和实例变量的区别?
答:静态变量是被static修饰符修饰的变量,也称为类变量,它属于类,不属于类的任何一个对象,一个类不管创建多少个对象,静态变量在内存中有且仅有一个拷贝;实例变量必须依存于某一实例,需要先创建对象然后通过对象才能访问到它。静态变量可以实现让多个对象共享内存。在Java开发中,上下文类和工具类中通常会有大量的静态成员。
28、是否可以从一个静态(static)方法内部发出对非静态(non-static)方法的调用?
答:不可以,静态方法只能访问静态成员,因为非静态方法的调用要先创建对象,因此在调用静态方法时可能对象并没有被初始化。
29、如何实现对象克隆?
答:有两种方式:
1.实现Cloneable接口并重写Object类中的clone()方法;
2. Implement the Serializable interface, and implement cloning through object serialization and deserialization, which can realize true deep cloning. O código é o seguinte.
package com.lovo; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; public class MyUtil { private MyUtil() { throw new AssertionError(); } public static <T> T clone(T obj) throws Exception { ByteArrayOutputStream bout = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bout); oos.writeObject(obj); ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bin); return (T) ois.readObject(); // 说明:调用ByteArrayInputStream或ByteArrayOutputStream对象的close方法没有任何意义// 这两个基于内存的流只要垃圾回收器清理对象就能够释放资源} } //何问起hovertree.com下面是测试代码:
package com.lovo; import java.io.Serializable; /** * Human* @author Luo Hao* */ class Person implements Serializable { private static final long serialVersionUID = -9102017020286042305L; private String name; // Name private int age; // Age private Car car; // Car public Person(String name, int age, Car car) { this.name = name; this.age = idade; this.car = car; } public string getName () {return name; } public void setName(String name) { this.name = name; } public int getage () {Age de retorno; } public void setAge(int age) { this.age = age; } public Car getCar() { return car; } public void setCar(Car car) { this.car = car; } @Override public String toString() { return "Person [name=" + name + ", age=" + age + ", car=" + car + "]"; } } /** * Car class* @author Luo Hao* */ class Car implements Serializable { private static final long serialVersionUID = -5713945027627603702L; private String brand; // Brand private int maxSpeed; // Top speed public Car(String brand, int maxSpeed) { this.brand = brand; this.maxSpeed = maxSpeed; } public String getBrand() { return brand; } public void setBrand(String brand) { this.brand = brand; } public int getMaxSpeed() { return maxSpeed; } public void setMaxSpeed(int maxSpeed) { this.maxSpeed = maxSpeed; } @Override public String toString() { return "Car [brand=" + brand + ", maxSpeed=" + maxSpeed + "]"; } } //Why ask about hovertree.comclass CloneTest { public static void main(String[] args) { try { Person p1 = new Person("Hao LUO", 33, new Car("Benz", 300)); Person p2 = MyUtil.clone(p1); // Deep cloning p2.getCar().setBrand("BYD"); // Modify the brand attributes of the cloned Person object p2-associated car object// The original Person object p1-associated car will not be affected in any way// Because when cloning Person object, the car object associated with it is also cloned System.out.println(p1); } catch (Exception e) { e.printStackTrace(); }}}注意:基于序列化和反序列化实现的克隆不仅仅是深度克隆,更重要的是通过泛型限定,可以检查出要克隆的对象是否支持序列化,这项检查是编译器完成的,不是在运行时抛出异常,这种是方案明显优于使用Object类的clone方法克隆对象。
30、GC 是什么?为什么要有GC?
答: GC是垃圾收集的意思,内存处理是编程人员容易出现问题的地方,忘记或者错误的内存回收会导致程序或系统的不稳定甚至崩溃,Java提供的GC功能可以自动监测对象是否超过作用域从而达到自动回收内存的目的,Java语言没有提供释放已分配内存的显示操作方法。Java程序员不用担心内存管理,因为垃圾收集器会自动进行管理。要请求垃圾收集,可以调用下面的方法之一:System.gc() 或Runtime.getRuntime().gc() ,但JVM可以屏蔽掉显示的垃圾回收调用。
垃圾回收可以有效的防止内存泄露,有效的使用可以使用的内存。垃圾回收器通常是作为一个单独的低优先级的线程运行,不可预知的情况下对内存堆中已经死亡的或者长时间没有使用的对象进行清除和回收,程序员不能实时的调用垃圾回收器对某个对象或所有对象进行垃圾回收。在Java诞生初期,垃圾回收是Java最大的亮点之一,因为服务器端的编程需要有效的防止内存泄露问题,然而时过境迁,如今Java的垃圾回收机制已经成为被诟病的东西。移动智能终端用户通常觉得iOS的系统比Android系统有更好的用户体验,其中一个深层次的原因就在于Android系统中垃圾回收的不可预知性。
补充:垃圾回收机制有很多种,包括:分代复制垃圾回收、标记垃圾回收、增量垃圾回收等方式。标准的Java进程既有栈又有堆。栈保存了原始型局部变量,堆保存了要创建的对象。Java平台对堆内存回收和再利用的基本算法被称为标记和清除,但是Java对其进行了改进,采用“分代式垃圾收集”。这种方法会跟Java对象的生命周期将堆内存划分为不同的区域,在垃圾收集过程中,可能会将对象移动到不同区域:
伊甸园(Eden):这是对象最初诞生的区域,并且对大多数对象来说,这里是它们唯一存在过的区域。
幸存者乐园(Survivor):从伊甸园幸存下来的对象会被挪到这里。
终身颐养园(Tenured):这是足够老的幸存对象的归宿。年轻代收集(Minor-GC)过程是不会触及这个地方的。当年轻代收集不能把对象放进终身颐养园时,就会触发一次完全收集(Major-GC),这里可能还会牵扯到压缩,以便为大对象腾出足够的空间。
与垃圾回收相关的JVM参数:
-Xms / -Xmx --- 堆的初始大小/ 堆的最大大小
-Xmn --- 堆中年轻代的大小
-XX:-DisableExplicitGC --- 让System.gc()不产生任何作用
-XX:+PrintGCDetail --- 打印GC的细节
-XX:+PrintGCDateStamps --- 打印GC操作的时间戳
31、String s=new String(“xyz”);创建了几个字符串对象?
答:两个对象,一个是静态存储区的"xyz",一个是用new创建在堆上的对象。
32、接口是否可继承(extends)接口? 抽象类是否可实现(implements)接口? 抽象类是否可继承具体类(concrete class)?
答:接口可以继承接口。抽象类可以实现(implements)接口,抽象类可继承具体类,但前提是具体类必须有明确的构造函数。
33、一个“.java”源文件中是否可以包含多个类(不是内部类)?有什么限制?
答:可以,但一个源文件中最多只能有一个公开类(public class)而且文件名必须和公开类的类名完全保持一致。
34、Anonymous Inner Class(匿名内部类)是否可以继承其它类?是否可以实现接口?
答:可以继承其他类或实现其他接口,在Swing编程中常用此方式来实现事件监听和回调。
35、内部类可以引用它的包含类(外部类)的成员吗?有没有什么限制?
答:一个内部类对象可以访问创建它的外部类对象的成员,包括私有成员。
36、Java 中的final关键字有哪些用法?
答: (1)修饰类:表示该类不能被继承;(2)修饰方法:表示方法不能被重写;(3)修饰变量:表示变量只能一次赋值以后值不能被修改(常量)。
37、指出下面程序的运行结果:
class A{ static{ System.out.print("1"); } public A(){ System.out.print("2"); } } class B extends A{ static{ System.out.print("a"); } public B(){ System.out.print("b"); } } //Why ask about hovertree.compublic class Hello{ public static void main(String[] args){ A ab = new B(); ab = new B(); }}答:执行结果:1a2b2b。创建对象时构造器的调用顺序是:先初始化静态成员,然后调用父类构造器,再初始化非静态成员,最后调用自身构造器。
38、数据类型之间的转换:
1)如何将字符串转换为基本数据类型?
2)如何将基本数据类型转换为字符串?
responder:
1)调用基本数据类型对应的包装类中的方法parseXXX(String)或valueOf(String)即可返回相应基本类型;
2)一种方法是将基本数据类型与空字符串(””)连接(+)即可获得其所对应的字符串;另一种方法是调用String 类中的valueOf(…)方法返回相应字符串
39、如何实现字符串的反转及替换?
答:方法很多,可以自己写实现也可以使用String或StringBuffer / StringBuilder中的方法。有一道很常见的面试题是用递归实现字符串反转,代码如下所示:
public static String reverse(String originStr) { if(originStr == null || originStr.length() <= 1) return originStr; return reverse(originStr.substring(1)) + originStr.charAt(0); } //何问起hovertree.com40、怎样将GB2312编码的字符串转换为ISO-8859-1编码的字符串?
答:代码如下所示:
String s1 = "你好";String s2 = newString(s1.getBytes("GB2312"), "ISO-8859-1");41、日期和时间:
1)如何取得年月日、小时分钟秒?
2)如何取得从1970年1月1日0时0分0秒到现在的毫秒数?
3)如何取得某月的最后一天?
4)如何格式化日期?
答:操作方法如下所示:
1)创建java.util.Calendar 实例,调用其get()方法传入不同的参数即可获得参数所对应的值
2)以下方法均可获得该毫秒数:
Calendar.getInstance().getTimeInMillis(); System.currentTimeMillis(); //何问起hovertree.com
3)示例代码如下:
Calendar time = Calendar.getInstance(); time.getActualMaximum(Calendar.DAY_OF_MONTH); //何问起hovertree.com
4)利用java.text.DataFormat 的子类(如SimpleDateFormat类)中的format(Date)方法可将日期格式化。
42、打印昨天的当前时刻。
responder:
public class YesterdayCurrent { public static void main(String[] args){ Calendar cal = Calendar.getInstance(); cal.add(Calendar.DATE, -1); System.out.println(cal.getTime()); } } //何问起hovertree.com43、比较一下Java 和JavaSciprt。
答: JavaScript 与Java是两个公司开发的不同的两个产品。Java 是原Sun 公司推出的面向对象的程序设计语言,特别适合于互联网应用程序开发;而JavaScript是Netscape公司的产品,为了扩展Netscape浏览器的功能而开发的一种可以嵌入Web页面中运行的基于对象和事件驱动的解释性语言,它的前身是LiveScript;而Java 的前身是Oak语言。
下面对两种语言间的异同作如下比较:
1)基于对象和面向对象:Java是一种真正的面向对象的语言,即使是开发简单的程序,必须设计对象;JavaScript是种脚本语言,它可以用来制作与网络无关的,与用户交互作用的复杂软件。它是一种基于对象(Object-Based)和事件驱动(Event-Driven)的编程语言。因而它本身提供了非常丰富的内部对象供设计人员使用;
2)解释和编译:Java 的源代码在执行之前,必须经过编译;JavaScript 是一种解释性编程语言,其源代码不需经过编译,由浏览器解释执行;
3)强类型变量和类型弱变量:Java采用强类型变量检查,即所有变量在编译之前必须作声明;JavaScript中变量声明,采用其弱类型。即变量在使用前不需作声明,而是解释器在运行时检查其数据类型;
4)代码格式不一样。
补充:上面列出的四点是原来所谓的标准答案中给出的。其实Java和JavaScript最重要的区别是一个是静态语言,一个是动态语言。目前的编程语言的发展趋势是函数式语言和动态语言。在Java中类(class)是一等公民,而JavaScript中函数(function)是一等公民。对于这种问题,在面试时还是用自己的语言回答会更加靠谱。
44、什么时候用assert?
答: assertion(断言)在软件开发中是一种常用的调试方式,很多开发语言中都支持这种机制。一般来说,assertion用于保证程序最基本、关键的正确性。assertion检查通常在开发和测试时开启。为了提高性能,在软件发布后, assertion检查通常是关闭的。在实现中,断言是一个包含布尔表达式的语句,在执行这个语句时假定该表达式为true;如果表达式计算为false,那么系统会报告一个AssertionError。
断言用于调试目的:
assert(a > 0); // throws an AssertionError if a <= 0
断言可以有两种形式:
assert Expression1;
assert Expression1 : Expression2 ;
Expression1 应该总是产生一个布尔值。
Expression2 可以是得出一个值的任意表达式;这个值用于生成显示更多调试信息的字符串消息。
断言在默认情况下是禁用的,要在编译时启用断言,需使用source 1.4 标记:
javac -source 1.4 Test.java
要在运行时启用断言,可使用-enableassertions 或者-ea 标记。
要在运行时选择禁用断言,可使用-da 或者-disableassertions 标记。
要在系统类中启用断言,可使用-esa 或者-dsa 标记。还可以在包的基础上启用或者禁用断言。可以在预计正常情况下不会到达的任何位置上放置断言。断言可以用于验证传递给私有方法的参数。不过,断言不应该用于验证传递给公有方法的参数,因为不管是否启用了断言,公有方法都必须检查其参数。不过,既可以在公有方法中,也可以在非公有方法中利用断言测试后置条件。另外,断言不应该以任何方式改变程序的状态。
45、Error 和Exception 有什么区别?
答: Error 表示系统级的错误和程序不必处理的异常,是恢复不是不可能但很困难的情况下的一种严重问题;比如内存溢出,不可能指望程序能处理这样的情况;Exception 表示需要捕捉或者需要程序进行处理的异常,是一种设计或实现问题;也就是说,它表示如果程序运行正常,从不会发生的情况。
Supplement: During the interview with Motorola in 2005, I asked a question like "If a process reports a stack overflow run-time error, what's the most possible cause?", giving four options a. lack of memory; b. write on an invalid memory space; c. recursive function calling; d. array index out of boundary. Java programs may also encounter StackOverflowError when running. This is an error that cannot be recovered, so I can only re-modify the code. The answer to this interview question is c. If you write recursion that cannot converge quickly, it is very likely to cause a stack overflow error, as shown below:
package com.lovo; public class StackOverflowErrorTest { public static void main(String[] args) { main(null); } } //何问起hovertree.com因此,用递归编写程序时一定要牢记两点:1. 递归公式;2. 收敛条件(什么时候就不再递归而是回溯了)。
46、try{}里有一个return语句,那么紧跟在这个try后的finally{}里的code会不会被执行,什么时候被执行,在return前还是后?
答:会执行,在方法返回调用者前执行。Java允许在finally中改变返回值的做法是不好的,因为如果存在finally代码块,try中的return语句不会立马返回调用者,而是记录下返回值待finally代码块执行完毕之后再向调用者返回其值,然后如果在finally中修改了返回值,这会对程序造成很大的困扰,C#中就从语法上规定不能做这样的事。
47、Java 语言如何进行异常处理,关键字:throws、throw、try、catch、finally分别如何使用?
答: Java 通过面向对象的方法进行异常处理,把各种不同的异常进行分类,并提供了良好的接口。在Java 中,每个异常都是一个对象,它是Throwable 类或其子类的实例。当一个方法出现异常后便抛出一个异常对象,该对象中包含有异常信息,调用这个对象的方法可以捕获到这个异常并进行处理。Java 的异常处理是通过5 个关键词来实现的:try、catch、throw、throws和finally。一般情况下是用try来执行一段程序,如果出现异常,系统会抛出(throw)一个异常,这时候你可以通过它的类型来捕捉(catch)它,或最后(finally)由缺省处理器来处理;try用来指定一块预防所有“异常”的程序;catch 子句紧跟在try块后面,用来指定你想要捕捉的“异常”的类型;throw 语句用来明确地抛出一个“异常”;throws用来标明一个成员函数可能抛出的各种“异常”;finally 为确保一段代码不管发生什么“异常”都被执行一段代码;可以在一个成员函数调用的外面写一个try语句,在这个成员函数内部写另一个try语句保护其他代码。每当遇到一个try 语句,“异常”的框架就放到栈上面,直到所有的try语句都完成。如果下一级的try语句没有对某种“异常”进行处理,栈就会展开,直到遇到有处理这种“异常”的try 语句。
48、运行时异常与受检异常有何异同?
答:异常表示程序运行过程中可能出现的非正常状态,运行时异常表示虚拟机的通常操作中可能遇到的异常,是一种常见运行错误,只要程序设计得没有问题通常就不会发生。受检异常跟程序运行的上下文环境有关,即使程序设计无误,仍然可能因使用的问题而引发。Java编译器要求方法必须声明抛出可能发生的受检异常,但是并不要求必须声明抛出未被捕获的运行时异常。异常和继承一样,是面向对象程序设计中经常被滥用的东西,神作《Effective Java》中对异常的使用给出了以下指导原则:
不要将异常处理用于正常的控制流(设计良好的API不应该强迫它的调用者为了正常的控制流而使用异常)
对可以恢复的情况使用受检异常,对编程错误使用运行时异常避免不必要的使用受检异常(可以通过一些状态检测手段来避免异常的发生)
优先使用标准的异常每个方法抛出的异常都要有文档保持异常的原子性不要在catch中忽略掉捕获到的异常
49、列出一些你常见的运行时异常?
responder:
ArithmeticException(算术异常)
ClassCastException (类转换异常)
IllegalArgumentException (非法参数异常)
IndexOutOfBoundsException (下表越界异常)
NullPointerException (空指针异常)
SecurityException (安全异常)
50、final, finally, finalize 的区别?
答: final:修饰符(关键字)有三种用法:如果一个类被声明为final,意味着它不能再派生出新的子类,即不能被继承,因此它和abstract是反义词。将变量声明为final,可以保证它们在使用中不被改变,被声明为final 的变量必须在声明时给定初值,而在以后的引用中只能读取不可修改。被声明为final 的方法也同样只能使用,不能在子类中被重写。finally:通常放在try…catch的后面构造总是执行代码块,这就意味着程序无论正常执行还是发生异常,这里的代码只要JVM不关闭都能执行,可以将释放外部资源的代码写在finally块中。finalize:Object类中定义的方法,Java中允许使用finalize() 方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在销毁对象时调用的,通过重写finalize() 方法可以整理系统资源或者执行其他清理工作。
O exposto acima é todo o conteúdo deste artigo. I hope it will be helpful to everyone in the Java interview, and I hope everyone will support Wulin.com more.