terça-feira, 7 de dezembro de 2010

[Arq e Org] Conceitos básicos


Arquitetura de computadores

Conceitos Básicos

Em ciência da computação, arquitetura de computadores é o design conceitual e estrutura operacional fundamental de um sistema de computador. É um desenho e descrição funcional dos requisitos e implementações de design para as várias partes de um computador, concentrando-se principalmente na maneira em que a Unidade Central de Processamento (CPU) trabalha internamente e acessa os endereço na memória.

Os computadores atuais são baseados no conceito introduzido por John von Neumann, a arquitetura de von Neumann. A arquitetura de Von Neumann é uma arquitetura de computador que se caracteriza pela possibilidade de uma máquina armazenar seus programas no mesmo espaço de memória que os dados, podendo assim manipular tais programas.

O ciclo de instruções de um programa é dividido nos estados: Cálculo do endereço de instrução; Busca da instrução (instruction fetch); Decodificação da instrução; Cálculo do endereço do operando; Busca do operando (operand fetch); Execução da instrução e Armazenamento do resultado. Nos computadores modernos é presente o conceito de interrupção de instrução para diminuir o tempo ocioso do processador, assim o ciclo de execução das instruções ganham alguns estados adicionais. Os tipos de interrupções mais comuns são interrupções de software, de clock e de Entrada/Saída e de falha de hardware.

A máquina proposta por Von Neumann reúne os seguintes componentes:
  1. Memória Principal
  2. Unidade Lógica e Aritmética
  3. Unidade de controle
  4. Equipamento de entrada e saída


Organização de computadores

Conceitos Básicos

Em ciência da computação, organização de computadores é a maneira que um conjunto de instruções (ISA - Instruction Set Architecture) é implementada em um processador. Um conjunto de instruções pode ser implementado com diferentes microarquiteturas. Implementações podem variar devido a diferentes objetivos de um certo design ou devido a mudanças na tecnologia.

Sendo assim, uma mesma arquitetura pode ser implementada por meio de diferentes organizações.

Para Tanenbaum (Organização Estruturada de computadores), organização é um modelo de abstração para facilitar o projeto e implementação de arquiteturas de computadores. Um computador é dividido em níveis ou camadas. Em cada nível, o computador pode ser programado utilizando a linguagem e os recursos daquele nível. Um nível pode ser visto como uma máquina virtual para execução de programas escritos no nível imediatamente superior. Como mostra a figura abaixo.

terça-feira, 27 de julho de 2010

[Java] Descrever programas para manipular suas próprias exceções


Na linguagem de programação Java, o programador pode definir suas próprias exceções. Exceções definidas pelo programador são criadas estendendo-se a classe Exception. As classes de exceções contêm os mesmos membros que a classe “normal” contém. Abaixo segue um exemplo de uma classe de exceção definida pelo programador que contém um construtor, algumas variáveis e métodos.
public class ServerTimedOutException extends Exception {
private int porta;

// Usa o método getMessage da classe Exception para retornar o motivo da exceção
public ServerTimedOutException(String m, int p) {
super(m);
this.porta = p;
}

public int getPorta() {
return porta;
}
}

Assim, para lançar essa exceção recém criada, use a seguinte forma:
throw new ServerTimedOutException("Não foi possível conectar ao servidor", 80);
Para demonstrar como utilizar esta nova exceção, veja o exemplo a seguir. Considere um programa cliente-servidor. Nele, se houver uma tentativa de conexão e o servidor não responder por mais de 5 segundos será lançada a exceção criada acima. Assim o exemplo ficaria da seguinte forma:
public void Conecta(Servidor nomeDoServidor) thows ServerTimedOutException {
private boolean conectou;
private int portaConectar = 80;

// abrir é um método fictício para tentar abrir uma conexão e retorna true com sucesso e false caso contrário.
Conectou = abrir(nomeDoServidor, portaConectar);

if(!conectou){
throw new ServerTimedOutException("Não foi possível conectar ao servidor", portaConectar);
}
}

E para capturar a exceção lançada pelo método Conecta:
public void ProcuraServidor() {
try{
Conecta(primeiroServidor);
}
catch(ServerTimedOutException e1){
System.out.printl(“Não foi possível conectar ao primeiro servidor. Tentando conectar ao outro servidor”);
//É possivel fazer try-catch aninhados. Veja abaixo
try{
Conecta(outroServidor);
}
catch(ServerTimedOutException eOutro){
System.out.printl(“Erro: ” + eOutro.getMessage + “ conectando na porta ” + eOutro.getPorta());
}
}
}

[Java] Identificar exceções comuns


A linguagem de programação Java fornece diversas exceções predefinidas. Algumas das exceções mais comuns são:



domingo, 25 de julho de 2010

[Java] Descrever as categorias de exceções


Hierarquia das exceções

Os objetos que herdam da classe Throwable incluem descendentes diretos e indiretos. A figura abaixo ilustra a hierarquia de classes da classe Throwable e suas subclasses mais significativas. A classe Throwable possui dois descendentes diretos: Error e Exception.


Classe Error

A classe Error e suas descendentes representam situações anormais que poderiam acontecer na JVM. Errors acontecem raramente e não devem ser capturados ou lançados por aplicativos.

Classe Exception

A classe Exception e suas descendentes representam situações não comuns que podem ocorrer durante a execução de um programa (Por exemplo, sinais IllegalAccessException indicam que um determinado método não pode ser encontrado, enquanto NegativeArraySizeException indica que um programa tentou criar uma matriz com um tamanho negativo) e podem ser capturadas pelo aplicativo. A maioria dos programas lança e captura objetos que derivam da classe Exception. Uma exceção indica que ocorreu um problema, mas não um problema grave do sistema. A maioria dos programas escritos irá lançar e capturar exceções ao contrário de erros. A subclasse de Exception, RuntimeException, é reservada para as exceções que indicam o uso incorreto de uma API.

Classe RuntimeException

RuntimeException indica um problema de design ou de implementação. Ou seja, ele indica as condições que nunca deve acontecer se o programa está funcionando adequadamente. Uma exceção ArrayIndexOutOfBoundsException, por exemplo, nunca deve ser acionada se os índices de array não estender além dos limites do vetor. Isto também se aplica, por exemplo, a referenciar uma variável de objeto nulo. Como um programa corretamente projetado e implementado nunca enfrenta este tipo de exceção, é de costume deixá-la sem tratamento. Isso resulta em uma mensagem em tempo de execução, e assegura que sejam tomadas medidas para corrigir o problema, ao invés de escondê-lo onde (você acha que) ninguém vai notar.

Exceções verificadas e não verificadas

As exceções em Java diferenciam em dois grupos de exceções: exceções verificadas e exceções não verificadas. As exceções verificadas são aquelas em que é obrigatório capturar ou declarar as exceções, enquanto as não verificadas não possuem a obrigatoriedade de capturar ou declarar as exceções. Se o requisito de capturar ou declarar uma exceção verificada não for satisfeito, o compilador irá retornar uma mensagem de erro informando que a exceção deve ser capturada ou declarada. O tipo de uma exceção é que define se é uma exceção verificada ou não verificada. Assim sendo, veja a figura abaixo (lembrando que nela possuem apenas as classes mais significativas). As subclasses das classes de cor vermelha são consideradas exceções verificadas, as demais são consideradas não verificadas.


Resumindo: Todas as classes que herdam de Exception e não herdam de RuntimeException serão verificadas enquanto todas as classes que herdam de RuntimeException, Error e suas descendentes serão não verificadas.

[Java] Uso das instruções try, catch e finally


O tratamento de exceções em Java ocorre transferindo a execução normal de um programa para o devido código de tratamento de exceções quando uma exceção ocorre. Para realizar o desvio da execução do programa utiliza-se as palavras chaves try, catch ou finally.

Instrução try

A sintaxe da instrução try se dá da seguinte forma:

try {
//código passível de exceções
}


Quando uma exceção é lançada em um bloco try o restante do código após à exceção é ignorado, transferindo o fluxo de execução para o tratamento dessa exceção.

Instrução catch

A instrução que captura (recebe) e trata da exceção lançada pela instrução try é a catch. Cada instrução try pode ter zero ou mais instruções catchs. A sintaxe de uma instrução catch é:

catch(<TipoDaExceção> <exceção>) {
//código de tratamento de exceção
}


Os blocos catchs devem ser posicionados logo após o término do bloco try, sem linhas de comando ou blocos entre eles. Assim como um bloco try e um catch devem ser dispostos juntos, dois blocos catchs também devem seguir este mesmo padrão. Deve-se atentar, porém, para a ordem dos blocos catchs. Blocos catchs devem ser dispostos de forma que primeiro estejam as exceções específicas e por último as gerais, pois quando uma exceção é lançada no bloco try, os blocos catchs são verificados um por um seguindo a sequencia das linhas do código. Assim se uma exceção específica for colocada após uma geral a específica nunca será executada.

Instrução finally

Apesar das instruções try e catch serem ótimas elas não são perfeitas. Quando uma exceção é lançada em uma instrução try, todo o restante do código é descartado e o fluxo é transferido para a instrução catch, assim se for necessário, por exemplo, de uma reinicialização de variáveis, ou desalocação de recursos. A instrução finally resolve este problema, pois será sempre excecutada após os comandos (a qualquer ponto) de um bloco try, lançando ou não uma exceção. A instrução finally é tão poderosa que até mesmo antes de um comando return do bloco try ela é executada. Desta forma, o bloco finally é o perfeito bloco para se fechar sockets, e realizar qualquer operação de limpeza que seu código necessite.

Comando try-catch-finally

Quando um bloco try é escrito, pelo um bloco catch ou finally deverá ser escrito. Como mostra a tabela abaixo:

try {}

-

-

INVÁLIDO

try {}

catch {}

-

VÁLIDO

try {}

finally {}

-

VÁLIDO

try{}

catch {}

finally{}

VÁLIDO

try{}

finally{}

catch {}

INVÁLIDO

catch {}

finally{}

-

INVÁLIDO

catch {}

-

-

INVÁLIDO

finally{}

catch {}

-

INVÁLIDO

finally{}

-

-

INVÁLIDO



[Java] Identificar os usos adequados e inadequados de declarações


Como foi introduzido no post anterior, as declarações ou assertivas são utilizadas para verificar a validade de um programa quanto a falhas de lógica de programação e bugs. A instrução assert é utilizada para fazer a validação das declarações e não modificar o código após a verificação. Para que a instrução assert seja vericada durante a execução do código é necessária a habilitação da opção. Para executar o programa com assertivas digite: java -ea <NomeDaClasse>. A instrução assert avalia uma expressão boolean e determina se é verdadeira ou falsa.

Sintaxe da instrução assert

assert <expressão_booleana>;
assert <expressão_booleana> : &ltexpressão_de_detalhes>;

A primeira forma da sintaxe da instrução assert avalia a expressão booleana e lança um AssertionException se a mesma for false. A segunda forma da sintaxe avalia a expressão booleana e lança um AssertionExeption com a expressão de detalhes como a mensagem de erro se a expressão booleana for false.

Uso adequado de declarações

Utilizar declarações para documentar e validar a lógica interna de um único método:
  • Invariante interno

  • if (x > 0) {
    // Faz uma coisa
    } else {
    assert ( x == 0 );
    // Faz outra coisa, a não ser que x seja negativo
    }


  • Invariante de controle de fluxo

  • switch (naipe) {
    case Naipe.PAUS: // ...
    break;
    case Naipe.OUROS: // ...
    break;
    case Naipe.COPAS: // ...
    break;
    case Naipe.ESPADAS: // ...
    break;
    default: assert false : "Naipe indeterminado!!";
    break;
    }


  • Invariante de pós condição

  • public Object pop() {
    int size = this.getElementCount();
    if (size == 0) {
    throw new RuntimeException("Tentativa de retirar elemento de uma pilha vazia!");
    }

    Object result = /* Código para recuperar o elemento retirado */ ;

    // testa a pós condição
    assert (this.getElementCount() == size - 1);

    return result;
    }
Uso inadequado de declarações
  • Não utilize declarações para verificar os parâmetros de um método público. Bem como para validar parâmetros da linha de comando.

  • public void fazAlgo(int x) {
    assert (x > 0); // INAPROPRIADO!!
    // faz coisas com x
    }


    Como você não possui controle dos códigos que fazem chamada a um método público não é recomendado fazer a verificação dos parâmetros recebidos de um método público, pois a instrução assert pode ser desativada e sua verificação não mais será realizada. Ao invés de usar assert, utilize exceções!

  • Não utilize declarações que possam desencadear efeitos colaterais.

  • public void fazAlgumaCoisa() {
    assert (modificaAlgo());
    // ...
    }
    public boolean modificaAlgo() {
    y = x++;//Mudança no valor de variável
    return true;
    }


    Não é recomendado utilizar declarações que possam desencadear efeitos colaterais, pois a regra geral da utilização da instrução assert é de testar algo e deixar o código exatamente como antes do teste. Instruções assert nem sempre ocorrem, assim é impossível manter o controle do código se a instrução produzir efeistos colaterais.

quarta-feira, 21 de julho de 2010

[Java] Exceções e Declarações (Assertivas)


Exceções

Uma exceção é o evento que ocorre durante a execução de um programa que interrompe o fluxo normal das instruções. Para resolver os problemas ocorridos em uma exceção deve-se fazer o tratamento de exceção. O tratamento das exceções de um programa faz com que o mesmo fique mais robusto e tolerante às falhas. O tratamento de exceções foi projetado para processar erros síncronos (como por exemplo índice fora do intervalo do vetor, estouro aritmético, divisão por zero) e não erros assíncronos (como por exemplo chegadas de mensagens de rede, cliques do mouse), que ocorrem paralelamente com o fluxo de controle do programa e independente dele.

Tratamento de exceções

No tratamento de exceções primeiramente executa-se uma tarefa, depois se testa se a tarefa foi executada corretamente. Se não estiver sido executada corretamente, então se realiza o tratamento da exceção. Porém, mesclar o tratamento de exceções com a lógica do programa pode prejudicar a leitura do código, principalmente se o aplicativo for grande.

Declarações ou Assertivas

Para realizar a depuração de uma classe ou trecho de código, é útil declarar as condições verdadeiras em um certo ponto do código. A essas condições dá-se o nome de Declarações ou Assertivas (Assertions). Elas ajudam a assegurar a validade do programa identificando possíveis erros de lógica e bugs.