Arreglos en Java

Declaración y acceso

Un arreglo (array) es una colección ordenada de elementos del mismo tipo, que son accesibles a través de un índice.
Un arreglo puede contener datos primitivos o referencias a objetos.
Los arrays se deben declarar:
[modificadores] tipo_variable[ ] nombre;
Por ejemplo:
int [ ] a;
Punto [ ] p;
La declaración dice que a es un array de enteros y p un array de objetos de tipo Punto. De manera más precisa a es una referencia a una colección de enteros, aunque todavía no se sabe cuantos elementos tiene el array. p es una referencia a una colección de referencias que apuntarán objetos Punto.

Un array se crea como si se tratara de un objeto (de hecho las variables de tipo array son referencias):
a = new int [5];
p = new Punto[3];
En el momento de la creación del arreglo se dimensiona el mismo y se reserva la memoria necesaria.

También puede crearse de forma explícita asignando valores a todos los elementos del array en el momento de la declaración, de la siguiente forma:
int [ ] a = { 2 , 3 , 5 };
El acceso a los elementos del array se realiza indicando entre corchetes el elemento del array que se desea, teniendo en cuenta que siempre el primer elemento del array es el índice 0. Por ejemplo a[1]. En este ejemplo los índices del array de tres elementos son 0, 1 y 2. Si se intenta usar un índice que está fuera del rango válido para ese arreglo se produce un error (en realidad una excepción. Las excepciones serán tratadas en un capítulo posterior) de 'Indice fuera de rango'. En el ejemplo anterior se produce esta excepción si el índice es menor que 0 o mayor que 2.

Se puede conocer el número de elementos de un array usando la variable length. En el ejemplo a.length contiene el valor 3.

Un arreglo, como cualquier otra referencia puede formar parte de la lista de parámetros o constituir el valor de retorno de un método. En ambos casos se indica que se trata de un array con los corchetes que siguen al tipo. Por ejemplo:
String [ ] metodoConArrays ( Punto [ ] ) { . . }
El método metodoConArrays recibe como parámetro un array de Puntos y devuelve un array de Strings. El método podría invocarse de la siguiente forma:
Punto p [ ] = new Punto [3];
. . .
String [ ] resultado = metodoConArrays(p);

Arreglos multidimensionales

Es posible declarar arrays de más de una dimensión. Los conceptos son los mismos que para los arreglos monodimensionales.

Por ejemplo:
int [ ][ ] a = { { 1 , 2 } , { 3 , 4 } , { 5 , 6 } };
int x = a [1][0];   // contiene 3
int y = a [2][1];   // contiene 6
Se pueden recorrer los elementos de un array multidimensional, de la siguiente forma:
int [ ][ ] a = new int  [3][2];
for ( int i = 0 ; i < a.length ; i++ ) {
    for ( int j = 0 ; j < a[i].length ; j++) {
        a[i][j] = i * j;
    }
}
Obsérvese en el ejemplo la forma de acceder al tamaño de cada dimensión del array.

REFERENCIAS:       arrakis

Control de la ejecución

Resumen de operadores

La siguiente tabla muestra un resumen de los operadores clasificados por grupos:

Grupo de operador Operador Significado
Aritméticos +
-
*
/
%
Suma
Resta
Multiplicación
División
Resto
Relacionales >
>=
<
<=
==
!=
Mayor
Mayor o igual
Menor
Menor igual
Igual
Distinto
Lógicos &&
||
!
AND
OR
NOT
A nivel de bits &
|
^
<<
>>
>>>
AND
OR
NOT
Desplazamiento a la izquierda
Desplazamiento a la derecha rellenando con 1
Desplazamiento a la derecha rellenando con 0
Otros +
++
--
=
+=
-=
*=
/=
?:
Concatenación de cadenas
Autoincremento (actua como prefijo o sufijo)
Autodecremento (actua como prefijo o sufijo)
Asignación
Suma y asignación
Resta y asignación
Multiplicación y asignación
División y asignación
Condicional

Ejecución condicional

El formato general es:
if (expresion_booleana)
    sentencia
[else
    sentencia]
sentencia (a todo lo largo de este capítulo) puede ser una sola sentencia o un bloque de sentencias separadas por ; y enmarcadas por llaves { y }. Es decir
if (expresion_booleana) {
    sentencia;
    sentencia;
    . . .
}
else {
    sentencia;
    sentencia;
    . . .
}
expresion_booleana es una expresión que se evalua como true o false (es decir, de tipo booleano). Si el resultado es true la ejecución bifurca a la sentencia que sigue al if . En caso contrario se bifurca a la sentencia que sigue al else.

Los corchetes en el formato anterior indican que la clausula else es opcional.

Iteraciones con while

Sintaxis formato 1:
while (expresion_booleana)
    sentencia
Sintaxis formato 2:
do
    sentencia
while (expresion_booleana)
La sentencia o bloque se sentencias (se aplica la misma idea que para el if-else) se ejecuta mientras que la expresion_booleana se evalue como true.

La diferencia entre ambos formatos es que en el primero la expresión se evalua al principio del bloque de sentencias y en el segundo se evalua al final.

Iteraciones con for

El formato es:
for ( inicializacion ; expresion_booleana ; step )
    sentencia
inicializacion es una sentencia que se ejecuta la primera vez que se entra en el bucle for. Normalmente es una asignación, es opcional.

expresion_booleana es una expresión que se evalua antes de la ejecución de la sentencia, o bloque de sentencias, para cada iteración. La sentencia o bloque de sentencias se ejecutan mientras que la expresion_booleana se evalue como cierta. Es opcional.

step es una sentencia que se ejecuta cada vez que se llega al final de la sentencia o bloque de sentencias. Es opcional.

Una utilización clásica de un bucle de tipo for se muestra a continuación para evaluar un contador un número fijo de veces:
for ( int i = 1 ; i <= 10 ; i++ )
    sentencia
La sentencia (o bloque de sentencias) se evaluará 10 veces. En cada ejecución (pasada) el valor de la variable i irá variando desde 1 hasta 10 (inclusive). Cuando salga del bloque de sentencias i estará fuera de su ámbito (porque se define en el bloque for).

Si se omiten las tres clausulas del bucle se obtiene un bucle infinito:
for ( ; ; )
    sentencia
Obsérvese que se pueden omitir las clausulas pero no los separadores (;).

Evaluación múltiple

El formato es:
switch ( expresion_entera ) {
    case valor_entero:
        sentencia;
    break;
    case valor_entero:
        sentencia;
    break;
    . . .
    default:
    sentencia;
}
Cuidado: en el switch la expresión que se evalua no es una expresión booleana como en el if-else, sino una expresión entera.

Se ejecuta el bloque case cuyo valor coincida con el resultado de la expresión entera de la clausula switch. Se ejecuta hasta que se encuentra una sentencia break o se llega al final del switch.

Si ningún valor de case coincide con el resultado de la expresión entera se ejecuta el bloque default (si está presente).

default y break son opcionales.

Devolución de control

El formato es:
return valor
Se utiliza en los métodos para terminar la ejecución y devolver un valor a quien lo llamó.

valor debe ser del tipo declarado en el método.

valor es opcional. No debe existir cuando el método se declara de tipo void. En este caso, la claúsula return al final del método es opcional, pero puede usarse para devolver el control al llamador en cualquier momento.

Expresiones

La mayor parte del trabajo en un programa se hace mediante la evaluación de expresiones, bien por sus efectos tales como asignaciones a variables, bien por sus valores, que pueden ser usados como argumentos u operandos en expresiones mayores, o afectar a la secuencia de ejecución de instrucciones.

Cuando se evalua una expresión en un programa el resultado puede denotar una de tres cosas:
  • Una variable. (Si por ejemplo es una asignación)
  • Un valor. (Por ejemplo una expresión aritmética, booleana, una llamada a un método, etc.)
  • Nada. (Por ejemplo una llamada a un método declarado void)
Si la expresión denota una variable o un valor, entonces la expresión tiene siempre un tipo conocido en el momento de la compilación. Las reglas para determinar el tipo de la expresión varían dependiendo de la forma de las expresiones pero resultan bastante naturales. Por ejemplo, en una expresión aritmética con operandos de diversas precisiones el resultado es de un tipo tal que no se produzca pérdida de información, realizandose internamente las conversiones necesarias. El análisis pormenorizado de las conversiones de tipos, evaluaciones de expresiones, etc, queda fuera del ámbito de estos apuntes. En general puede decirse que es bastante similar a otros lenguajes, en particular C, teniendo en cuenta la característica primordial de Java de tratarse de un lenguaje con control fuerte de tipos.

REFERENCIAS:       arrakis

Variables y otros aspectos en Java

Inicialización de variables

Desde el punto de vista del lugar donde se declaran existen dos tipos de variables:
  • Variables miembro: Se declaran en una clase, fuera de cualquier método.
  • Variables locales: Se declaran y usan en un bloque de código dentro de un método.
Las variables miembro son inicializadas automáticamente, de la siguiente forma:
  • Las numéricas a 0.
  • Las booleanas a false.
  • Las char al caracter nulo (hexadecimal 0).
  • Las referencias a null.
Nota: null es un literal que indica referencia nula.

Las variables miembro pueden inicializarse con valores distintos de los anteriores en su declaración.

Las variables locales no se inicializan automáticamente. Se debe asignarles un valor antes de ser usadas. Si el compilador detecta una variable local que se usa antes de que se le asigne un valor produce un error. Por ejemplo:
int p;
int q = p;    // error
El compilador también produce un error si se intenta usar una variable local que podría no haberse inicializado, dependiendo del flujo de ejecución del programa. Por ejemplo:
int p;
if (. . . ) {
      p = 5 ;
}
int q = p;    // error
El compilador produce un error del tipo 'La variable podría no haber sido inicializada', independientemente de la condición del if.

Ambito de las variables

El ámbito de una variable es el área del programa donde la variable existe y puede ser utilizada. Fuera de ese ámbito la variable, o bien no existe o no puede ser usada (que viene a ser lo mismo).

El ámbito de una variable miembro (que pertenece a un objeto) es el de la usabilidad de un objeto. Un objeto es utilizable desde el momento en que se crea y mientras existe una referencia que apunte a él. Cuando la última referencia que lo apunta sale de su ámbito el objeto queda 'perdido' y el espacio de memoria ocupado por el objeto puede ser recuperado por la JVM cuando lo considere oportuno. Esta recuperación de espacio en memoria se denomina 'recogida de basura' y es descrita un poco más adelante.

El ámbito de las variables locales es el bloque de código donde se declaran. Fuera de ese bloque la variable es desconocida.

Ejemplo:
{
    int x;    // empieza el ámbito de x. (x es conocida y utilizable)
    {
        int q;    // empieza el ámbito de q. x sigue siendo conocida.
        . . .
    }    // finaliza el ámbito de q (termina el bloque de código)
    . . .    // q ya no es utilizable
}    // finaliza el ámbito de x

Recogida de basura

Cuando ya no se necesita un objeto simplemente puede dejar de referenciarse. No existe una operación explícita para 'destruir' un objeto o liberar el área de memoria usada por él.

La liberación de memoria la realiza el recolector de basura (garbage collector) que es una función de la JVM. El recolector revisa toda el área de memoria del programa y determina que objetos pueden ser borrados porque ya no tienen referencias activas que los apunten. El recolector de basura actua cuando la JVM lo determina (tiene un mecanismo de actuación no trivial).

En ocasiones es necesario realizar alguna acción asociada a la acción de liberar la memoria asignada al objeto (como por ejemplo liberar otros recursos del sistema, como descriptores de ficheros). Esto puede hacerse codificando un método finalize que debe declararse como:
protected void finalize() throws Throwable { }
Nota: las clausulas protected y throws se explicarán en capítulos posteriores.

El método finalize es invocando por la JVM antes de liberar la memoria por el recolector de basura, o antes de terminar la JVM. No existe un momento concreto en que las áreas de memoria son liberadas, sino que lo determina en cada momento la JVM en función de sus necesidades de espacio.

Sobrecarga de métodos

Una misma clase puede tener varios métodos con el mismo nombre siempre que se diferencien en el tipo o número de los argurmentos. Cuando esto sucede se dice que el método está sobrecargado. Por ejemplo, una misma clase podría tener los métodos:
int metodoSobrecargado() { . . .}
int metodoSobrecargado(int x) { . . .}
Sin embargo no se puede sobrecargar cambiando sólo el tipo del valor devuelto. Por ejemplo:
int metodoSobrecargado() { . . .}
void metodoSobrecargado() { . . .}    // error en compilación
con esta definición, en la expresión metodoSobrecargado() la JVM no sabría que método invocar.

Se puede sobrecargar cualquier método miembro de una clase, así como el constructor.

La referencia this

En ocasiones es conveniente disponer de una referencia que apunte al propio objeto que se está manipulando. Esto se consigue con la palabra reservada this. this es una referencia implicita que tienen todos los objetos y que apunta a si mismo. Por ejemplo:
class Circulo {
    Punto centro;
    int radio;
    . . .
    Circulo elMayor(Circulo c) {
        if (radio > c.radio) return this;
        else return c;
    }
}
El método elMayor devuelve una referencia al círculo que tiene mayor radio, comparando los radios del Circulo c que se recibe como argumento y el propio. En caso de que el propio resulte mayor el método debe devolver una referencia a si mismo. Esto se consigue con la expresión return this.

La referencia null

Para asignar a una referencia el valor nulo se utiliza la constante null. El ejemplo del caso anterior se podría completar con:
class Circulo {
    Punto centro;
    int radio;
    . . .
    Circulo elMayor(Circulo c) {
        if (radio > c.radio) return this;
        else if (c.radio > radio) return c;
        else return null;
    }
}

Ocultamiento de variables

Puede ocurrir que una variable local y una variable miembro reciban el mismo nombre (en muchos casos por error). Cuando se produce esto la variable miembro queda oculta por la variable local, durante el bloque de código en que la variable local existe y es accesible. Cuando se sale fuera del ámbito de la variable local, entonces la variable miembro queda accesible. Observese esto en el ejemplo siguiente:
. . .
String x = "Variable miembro";
. . .
void variableOculta() {
    System.out.println(x);
    {
        String x = "Variable local";
        System.out.println(x);
        }
    System.out.println(x);
}
Nota: El uso de Strings se verá en un capítulo posterior, aunque su uso aquí resulta bastante intuitivo. La llamada System.out.println envia a la consola (la salida estándar habitual) las variables que se pasan como argumentos.

La llamada al método variableOculta() producirá la siguiente salida:
Variable miembro
Variable local
Variable miembro
Se puede acceder a la variable miembro oculta usando la referencia this. En el ejemplo anterior la expresión:
System.out.println(this.x);
siempre producirá la salida "Variable miembro", puesto que this.x se refiere siempre a la variable miembro.

REFERENCIAS:arrakis

Líneas Microstrip

Línea Microstrip

La línea microstrip es uno de los tipos más populares de líneas de transmisión planas, principalmente debido a que puede ser fabricado mediante procesos fotolitográficos y es fácilmente miniaturizado e integrado con los dos tipos de dispositivos de microondas, pasivos y activos. La geometría de una línea microstrip se muestra en la Figura 1a. Un conductor de ancho W se imprime en un sustrato dieléctrico delgado, con conexión a tierra de espesor d y permitividad relativa Er; un esbozo de las líneas de campo se muestra en la figura 1b.

Figura 1. Línea de transmisión Microstrip. (a) Geometría. (b) Líneas de campo eléctrico y magnético.
Si el sustrato dieléctrico no está presente (E r = 1), tendríamos una línea de dos conductores que consiste en un conductor de cinta plana sobre un plano de tierra, incrustado en un medio homogéneo (aire). Esto constituiría una línea de transmisión TEM simple, con velocidad de fase vp = c y constante de propagación β = k0.
La presencia del dieléctrico, en particular el hecho de que el dieléctrico no llena la región por encima de la banda (y > d), complica el comportamiento y el análisis de la línea microstrip. A diferencia de una stripline, en el que todos los campos están contenidos dentro de una región dieléctrica homogénea, la línea microstrip tiene algunos (por lo general la mayoría) de sus líneas de campo en la región dieléctrica entre el conductor de tira y el plano de tierra y una fracción en la región de aire sobre el sustrato. Por esta razón la línea microstrip no puede soportar una onda TEM pura, ya que la velocidad de fase de los campos TEM en la región dieléctrica sería c / √Er, mientras que la velocidad de fase de los campos TEM en la región de aire sería c, por lo que una condición de coincidencia de fases en la interfase aire-dieléctrico sería imposible de aplicar.

En la actualidad, los campos exactos de una línea microstrip constituyen una onda TM-TE híbrido y requieren técnicas de análisis más avanzadas. En las aplicaciones más prácticas, sin embargo, el sustrato dieléctrico es eléctricamente muy delgado (d << lambda), y por ello los campos son cuasi-TEM. En otras palabras, los campos son esencialmente los mismos que los del caso estático (DC). Por lo tanto, buenas aproximaciones para la velocidad de fase, constante de propagación, y la impedancia característica se pueden obtener a partir de soluciones estáticas o cuasi-estáticas. Luego, la velocidad de fase y la constante de propagación se pueden expresar como

Ecuación 1. Velocidad de fase

Ecuación 2. Constante de propagación

Donde Ee es la constante dieléctrica efectiva de la línea microstrip. Debido a que algunas de las líneas de campo están en la región dieléctrica y algunas están en el aire, la constante dieléctrica efectiva satisface la relación

y depende de la constante dieléctrica del sustrato, del espesor del sustrato, del ancho del conductor, y la frecuencia. Seguidamente se presentan las fórmulas de diseño aproximadas para la constante dieléctrica efectiva, impedancia característica, y la atenuación de la línea microstrip; estos resultados son aproximaciones rigurosas de ajuste de curva a soluciones cuasi-estática. A continuación, se van a discutir los aspectos adicionales de línea microstrip, incluidos los efectos dependientes de la frecuencia, modos de orden superior, y los efectos parásitos.

Fórmulas para la constante dieléctrica efectiva, Impedancia característica, y atenuación

La constante dieléctrica efectiva de una línea microstrip está dada aproximadamente por

 

La constante dieléctrica efectiva se puede interpretar como la constante dieléctrica de un medio homogéneo que equivalentemente reemplaza al aire y regiones dieléctricas de la línea microstrip, como se muestra en la Figura 2. La velocidad de fase y constante de propagación están dadas por (1) y (2). Dadas las dimensiones de una línea microstrip, la impedancia característica puede ser calculada como


Para una impedancia característica Z0 dada y una constante dieléctrica Er, la relación W/d puede ser hallada de



Figura 2. Geometría equivalente de una línea microstrip cuasi-TEM. (a) geometría original. (b) geometría equivalente, en el que el sustrato dieléctrico de permitividad relativa Er se sustituye con un medio homogéneo de permitividad relativa efectiva Ee
Donde


Considerando una línea microstrip como una línea cuasi-TEM, se puede determinar la atenuación debido a la pérdida dieléctrica como


donde tanδ es la tangente de pérdida del dieléctrico. Este resultado se obtiene a partir de (3) mediante la multiplicación por un "factor de relleno,"

Ecuación 3. Atenuación debido a la pérdida dieléctrica (línea TEM).

 

La cual representa el hecho de que los campos alrededor de la línea microstrip están en parte en el aire (sin pérdida) y en parte en el dieléctrico (con pérdida). La atenuación debida a la pérdida del conductor está dada aproximadamente por 


donde Rs = √(ωμ0 / 2σ) es la resistividad de la superficie del conductor. Para la mayoría de los sustratos microstrip, la pérdida del conductor es más importante que la pérdida dieléctrica; sin embargo, pueden producirse excepciones con algunos sustratos semiconductores.

REFERENCIAS:        D. M. Pozar, Microwave Engineering, 2nd Ed.