Docsity
Docsity

Prepara tus exámenes
Prepara tus exámenes

Prepara tus exámenes y mejora tus resultados gracias a la gran cantidad de recursos disponibles en Docsity


Consigue puntos base para descargar
Consigue puntos base para descargar

Gana puntos ayudando a otros estudiantes o consíguelos activando un Plan Premium


Orientación Universidad
Orientación Universidad


teoria java, Apuntes de Algoritmos y Programación

Asignatura: Elements de programacio, Profesor: , Carrera: Enginyeria Informàtica, Universidad: UB

Tipo: Apuntes

Antes del 2010

Subido el 18/12/2008

maxfiber
maxfiber 🇪🇸

5

(1)

3 documentos

1 / 37

Toggle sidebar

Esta página no es visible en la vista previa

¡No te pierdas las partes importantes!

bg1
2. Tipos, Valores y Variables
2.1. Tipos
Java es un lenguaje con control fuerte de Tipos (Strongly Typed). Esto significa que cada variable y cada
expresión tiene un Tipo que es conocido en el momento de la compilación. El Tipo limita los valores que
una variable puede contener, limita las operaciones soportadas sobre esos valores y determina el
significado de la operaciones. El control fuerte de tipos ayuda a detectar errores en tiempo de
compilación.
En Java existen dos categorias de Tipos:
Tipos Primitivos
Referencias
Las referencias se usan para manipular objetos. Se verán en una lección posterior.
2.2. Tipos primitivos
Los tipos primitivos son los que permiten manmipular valores numéricos (con distintos grados de
precisión), caracteres y valores booleanos (verdadero / falso). Los Tipos Primitivos son:
boolean : Puede contener los valores true o false.
byte : Enteros. Tamaño 8-bits. Valores entre -128 y 127.
short : Enteros. Tamaño 16-bits. Entre -32768 y 32767.
int : Enteros. Tamaño 32-bits. Entre -2147483648 y 2147483647.
long : Enteros. Tamaño 64-bits. Entre -9223372036854775808 y 9223372036854775807.
float : Números en coma flotante. Tamaño 32-bits.
double : Números en coma flotante. Tamaño 64-bits.
char : Caracteres. Tamaño 16-bits. Unicode. Desde '\u0000' a '\uffff' inclusive. Esto
es desde 0 a 65535
El tamaño de los tipos de datos no depende de la implementación de Java. Son siempre los mismos.
2.3. Variables
Una variable es un área en memoria que tiene un nombre y un Tipo asociado. El Tipo es o bien un Tipo
primitivo o una Referencia.
Es obligatorio declarar las variables antes de usarlas. Para declararlas se indica su nombre y su Tipo, de la
siguiente forma:
tipo_variable nombre ;
Ejemplos:
int i; // Declaracion de un entero
char letra; // Declaracion de un caracter
boolean flag; // Declaracion de un booleano
El ; es el separador de sentencias en Java.
El símbolo // indica comentarios de línea.
En Java las mayúsculas y minúsculas son significativas. No es lo mismo el nombre letra que
Letra.
Todas las palabras reservadas del lenguaje van en minúsculas.
Todas las palabras que forman parte del lenguaje van en negrita a lo largo de todos los apuntes.
Se pueden asignar valores a las variables mediante la instrucción de asignación. Por ejemplo:
i = 5; // a la variable i se le asigna el valor 5
letra = 'c'; // a la variable letra se le asigna el
valor 'c'
pf3
pf4
pf5
pf8
pf9
pfa
pfd
pfe
pff
pf12
pf13
pf14
pf15
pf16
pf17
pf18
pf19
pf1a
pf1b
pf1c
pf1d
pf1e
pf1f
pf20
pf21
pf22
pf23
pf24
pf25

Vista previa parcial del texto

¡Descarga teoria java y más Apuntes en PDF de Algoritmos y Programación solo en Docsity!

2. Tipos, Valores y Variables

2.1. Tipos

Java es un lenguaje con control fuerte de Tipos ( Strongly Typed ). Esto significa que cada variable y cada expresión tiene un Tipo que es conocido en el momento de la compilación. El Tipo limita los valores que una variable puede contener, limita las operaciones soportadas sobre esos valores y determina el significado de la operaciones. El control fuerte de tipos ayuda a detectar errores en tiempo de compilación.

En Java existen dos categorias de Tipos:

  • (^) Tipos Primitivos
  • Referencias

Las referencias se usan para manipular objetos. Se verán en una lección posterior.

2.2. Tipos primitivos

Los tipos primitivos son los que permiten manmipular valores numéricos (con distintos grados de precisión), caracteres y valores booleanos (verdadero / falso). Los Tipos Primitivos son:

  • boolean : Puede contener los valores true o false.
  • byte : Enteros. Tamaño 8-bits. Valores entre -128 y 127.
  • short : Enteros. Tamaño 16-bits. Entre -32768 y 32767.
  • int : Enteros. Tamaño 32-bits. Entre -2147483648 y 2147483647.
  • long : Enteros. Tamaño 64-bits. Entre -9223372036854775808 y 9223372036854775807.
  • float : Números en coma flotante. Tamaño 32-bits.
  • double : Números en coma flotante. Tamaño 64-bits.
  • char : Caracteres. Tamaño 16-bits. Unicode. Desde '\u0000' a '\uffff' inclusive. Esto es desde 0 a 65535

El tamaño de los tipos de datos no depende de la implementación de Java. Son siempre los mismos.

2.3. Variables

Una variable es un área en memoria que tiene un nombre y un Tipo asociado. El Tipo es o bien un Tipo primitivo o una Referencia.

Es obligatorio declarar las variables antes de usarlas. Para declararlas se indica su nombre y su Tipo, de la siguiente forma:

tipo_variable nombre ;

Ejemplos:

int i ; // Declaracion de un entero char letra ; // Declaracion de un caracter boolean flag ; // Declaracion de un booleano

  • El ; es el separador de sentencias en Java.
  • El símbolo // indica comentarios de línea.
  • (^) En Java las mayúsculas y minúsculas son significativas. No es lo mismo el nombre letra que Letra.
  • Todas las palabras reservadas del lenguaje van en minúsculas.
  • Todas las palabras que forman parte del lenguaje van en negrita a lo largo de todos los apuntes.

Se pueden asignar valores a las variables mediante la instrucción de asignación. Por ejemplo:

i = 5; // a la variable i se le asigna el valor 5 letra = 'c'; // a la variable letra se le asigna el valor 'c'

flag = false ; // a la variable flag se le asigna el valor false

La declaración y la combinación se pueden combinar en una sola expresión: int i = 5 ; char letra = 'c' ; boolean flag = false;

2.4. Literales

En los literales numéricos puede forzarse un tipo determinado con un sufijo:

  • Entero largo: l ó L.
  • Float: f ó F
  • Double: d ó D. Por ejemplo: long l = 5L; float numero = 5f;

En los literales numéricos para float y double puede usarse el exponente (10 elevado a...) de la forma: 1.5e3D (equivale a 1500) Literales hexadecimales: Al valor en hexadecimal se antepone el símbolo 0x. Por ejemplo: 0xf (valor 15) Literales booleanos: true (verdadero) y false (falso)

Literales caracter: Un caracter entre apóstrofes (') o bien una sequencia de escape (también entre '). Las secuencias de escape están formadas por el símbolo \ y una letra o un número. Algunas secuencias de escape útiles:

  • \n: Salto de línea
  • \t: Tabulador
  • \b: Backspace.
  • \r: Retorno de carro
  • \uxxxx: donde xxxx es el código Unicode del carácter. Por ejemplo \u0027 es el apostrofe (')

2.5. Operaciones sobre Tipos primitivos

La siguiente tabla muestra un resumen de los operadores existentes para las distrintas clases de tipos primitivos. El grupo 'Enteros' incluye byte, short, int, long y char. El grupo 'Coma flotante' incluye float and double.

Tipos Grupo de operadores Operadores

Enteros Operadores de comparación que devuelven un valor boolean

< (menor) , <= (menor o igual) , > (mayor), >= (mayor o igual), == (igual), != (distinto) Operadores numéricos, que devuelven un valor int o long

  • (unario, positivo), - (unario, negativo), + (suma) , - (resta) , * (multiplicación), / (división), % (resto), ++ (incremento), -- (decremento), <<, >>, >>> (rotación) , ~ (complemento a nivel de bits), & (AND a nivel de bits), | (OR a nivel de bits) ^(XOR a nivel de bits)

Coma flotante

Operadores de comparación que devuelven un valor boolean

< (menor) , <= (menor o igual) , > (mayor), >= (mayor o igual), ==

Por ejemplo:

int x; x = sumaEnteros(2,3);

Nota: Esta sintaxis no está completa, pero sirve para nuestros propósitos en este momento. La sintaxis completa se verá cuando se hable de objetos.

Aunque el método no reciba ningún argumento, los paréntesis en la llamada son obligatorios. Por ejemplo para llamar a la función haceAlgo, simplemente se pondría:

haceAlgo();

Observese que como la función tampoco devuelve ningún valor no se asigna a ninguna variable. (No hay nada que asignar).

4. Clases - Introducción

4.1. Clases

Las clases son el mecanismo por el que se pueden crear nuevos Tipos en Java. Las clases son el punto central sobre el que giran la mayoría de los conceptos de la Orientación a Objetos.

Una clase es una agrupación de datos y de código que actua sobre esos datos, a la que se le da un nombre.

Una clase contiene:

  • Datos (se denominan Datos Miembro). Estos pueden ser de tipos primitivos o referencias.
  • Métodos (se denominan Métodos Miembro).

La sintaxis general para la declaración de una clase es:

modificadores class nombre_clase { declaraciones_de_miembros ; }

Por ejemplo:

class Punto { int x; int y; }

Los modificadores son palabras clave que afectan al comportamiento de la clase. Los iremos viendo progresivamente en los sucesivos capítulos.

4.2. Objetos, miembros y referencias

Un objeto es una instancia (ejemplar) de una clase. La clase es la definición general y el objeto es la materialización concreta (en la memoria del ordenador) de una clase.

El fenómeno de crear objetos de una clase se llama instanciación.

Los objetos se manipulan con referencias. Una referencia es una variable que apunta a un objeto. Las referencias se declaran igual que las variables de Tipos primitivos (tipo nombre). Los objetos se crean (se instancian) con el operador de instanciación new.

Ejemplo:

Punto p; p = new Punto();

La primera línea del ejemplo declara una referencia (p) que es de Tipo Punto. La referencia no apunta a ningún sitio. En la segunda línea se crea un objeto de Tipo Punto y se hace que la referencia p apunte a él. Se puede hacer ambas operaciones en la misma expresión:

Punto p = new Punto();

A los miembros de un objeto se accede a través de su referencia. La sintaxis es:

nombre_referencia. miembro

En el ejemplo, se puede poner:

p.x = 1; p.y = 3;

Se puede visualizar gráficamente los datos primitivos, referencias y objetos de la siguiente forma:

  • Datos primitivos: int i = 5;
  • Referencias y objetos: Punto p = new Punto(); p.x = 1; p.y = 3;

Es importante señalar que en el ejemplo, p no es el objeto. Es una referencia que apunta al objeto.

Los métodos miembro se declaran dentro de la declaración de la clase, tal como se ha visto en el capítulo anterior. Por ejemplo:

class Circulo { Punto centro; // dato miembro. Referencia a un objeto punto int radio; // dato miembro. Valor primitivo float superficie() { // método miembro. return 3.14 * radio * radio; } / / fin del método superficie } / / fin de la clase Circulo

El acceso a métodos miembros es igual que el que ya se ha visto para datos miembro. En el ejemplo:

Circulo c = new Circulo(); c.centro.x = 2; c.centro.y = 3; c.radio = 5; float s = c.superficie();

Es interesante observar en el ejemplo:

  • Los datos miembro pueden ser tanto primitivos como referencias. La clase Circulo contiene un dato miembro de tipo Punto (que es el centro del círculo).
  • El acceso a los datos miembros del Punto centro se hace encadenando el operador. en la expresión c.centro.x que se podría leer como 'el miembro x del objeto (Punto) centro del objeto (Circulo) c'.
  • Aunque el método superficie no recibe ningún argumento los paréntesis son obligatorios (Distinguen los datos de los métodos).
  • Existe un Objeto Punto para cada instancia de la clase Circulo (que se crea cuando se crea el objeto Circulo).

4.3. Conceptos básicos. Resumen

  • Una Clase es una definición de un nuevo Tipo, al que se da un nombre.
  • Una Clase contiene Datos Miembro y Métodos Miembro que configuran el estado y las operaciones que puede realizar.
  • Un Objeto es la materialización (instanciación) de una clase. Puede haber tantos Objetos de una Clase como resulte necesario.
  • Los Objetos se crean (se les asigna memoria) con el Operador new.
  • Los Objetos se manipulan con Referencias.
  • Una Referencia es una Variable que apunta a un Objeto.

Desde un constructor puede invocarse explicitamente a otro constructor utilizando la palabra reservada this. this es una referencia al propio objeto. Cuando this es seguido por parénteses se entiende que se está invocando al constructor del objeto en cuestión. Puedes ver el uso más habitual de this aquí. El ejemplo anterior puede reescribirse de la siguiente forma:

class Punto { int x , y ; Punto ( int a , int b ) { x = a ; y = b ; } Punto () { this (0,0); } }

Cuando se declaran varios constructores para una misma clase estos deben distinguirse en la lista de argumentos, bien en el número, bien en el tipo.

Esta característica de definir métodos con el mismo nombre se denomina sobrecarga y es aplicable a cualquier método miembro de una clase como veremos más adelante.

Clases - Miembros estáticos

Datos estáticos

Un dato estático es una variable miembro que no se asocia a un objeto (instancia) de una clase, sino que se asocia a la clase misma; no hay una copia del dato para cada objeto sino una sola copia que es compartida por todos los objetos de la clase.

Por ejemplo:

class Punto { int x , y ; static int numPuntos = 0;

Punto ( int a , int b ) { x = a ; y = b; numPuntos ++ ; } }

En el ejemplo numPuntos es un contador que se incrementa cada vez que se crea una instancia de la clase Punto.

Observese la forma en que se declara numPuntos, colocando el modificador static delante del tipo. La sintaxis general para declarar una variable es:

[ modificadores ] tipo_variable nombre ;

static es un modificador. En los siguientes capítulos se irán viendo otros modificadores. Los [ ] en la expresión anterior quieren decir que los modificadores son opcionales.

El acceso a las variables estáticas desde fuera de la clase donde se definen se raliza a través del nombre de la clase y no del nombre del objeto como sucede con las variables miembro normales (no estáticas). En el ejemplo anterior puede escribirse:

int x = Punto.numPuntos;

No obstante también es posible acceder a las variables estáticas a través de una referencia a un objeto de la clase. Por ejemplo:

Punto p = new Punto(); int x = p.numPuntos;

Las variables estáticas de una clase existen, se inicializan y pueden usarse antes de que se cree ningún objeto de la clase.

Métodos estáticos

Para los métodos, la idea es la misma que para los datos: los métodos estáticos se asocian a una clase, no a una instancia.

Por ejemplo:

class Punto { int x , y ; static int numPuntos = 0;

Punto ( int a , int b ) { x = a ; y = b; numPuntos ++ ; }

static int cuantosPuntos() { return numPuntos; } }

La sintaxis general para la definición de los métodos es, por tanto, la siguiente:

[modificadores ]^ Tipo_Valor_devuelto nombre_método ( lista_argumentos ) { bloque_de_codigo; }

El aceso a los métodos estáticos se hace igual que a los datos estáticos, es decir, usando el nombre de la clase, en lugar de usar una referencia:

int totalPuntos = Punto.cuantosPuntos();

Dado que los métodos estáticos tienen sentido a nivel de clase y no a nivel de objeto (instancia) los métodos estáticos no pueden acceder a datos miembros que no sean estáticos.

El método main

Un programa Java se inicia proporcionando al intérprete Java un nombre de clase. La JVM carga en memoria la clase indicada e inicia su ejecución por un método estático que debe estar codificado en esa clase. El nombre de este método es main y debe declararse de la siguiente forma:

static void main ( String [] args)

  • Es un método estático. Se aplica por tanto a la clase y no a una instancia en particular, lo que es conveniente puesto que en el momento de iniciar la ejecución todavía no se ha creado ninguna instancia de ninguna clase.
  • Recibe un argumento de tipo String []. String es una clase que representa una cadena de caracteres ( se verá más adelante),
  • Los corchetes [] indican que se trata de un array que se verán en un capítulo posterior.

No es obligatorio que todas las clases declaren un método main. Sólo aquellos métodos que vayan a ser invocados directamente desde la línea de comandos de la JVM necesitan tenerlo. En la práctica la mayor parte de las clases no lo tienen.

Inicializadores estáticos

En ocasiones es necesario escribir código para inicializar los datos estáticos, quizá creando algún otro objeto de alguna clase o realizando algún tipo de control. El fragmento de código que realiza esta tarea se llama inicializador estático. Es un bloque de sentencias que no tiene nombre, ni recibe argumentos, ni devuelve valor. Simplemente se declara con el modificador static.

La forma de declarar el inicializador estático es:

static { bloque_codigo }

Por ejemplo:

class Punto { int x , y ; static int numPuntos;

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

7.3. 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 explican 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.

7.4. 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 y.metodoSobrecargado() la JVM no sabría que método invocar.

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

7.5. 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.

7.6. 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; } }

7.7. 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.

8. Control de la ejecución

8.1. Resumen de operadores

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

8.3. 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.

8.4. 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 (;).

8.5. 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.

8.6. 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.

8.7. 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.

9. Arrays

9.1. Declaración y acceso

Un array es una colección ordenada de elementos del mismo tipo, que son accesibles a través de un índice.

Un array puede contener datos primitivos o referencias a objetos.

Los arrays se declaran:

[ 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. Más exactamente 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 array se dimensiona el mismo y se reserva la memoria necesaria.

Sin embargo también es posible crear un String directamente, sin usar el operador new, haciendo una asignación simple (como si se tratara de un dato primitivo): String s = "Esto es una cadena de caracteres";

Ambas expresiones conducen al mismo objeto. Los Strings no se modifican una vez que se les ha asignado valor. Si se produce una reasignación se crea un nuevo objeto String con el nuevo contenido. Además la clase String proporciona constructores para crear Strings a partir de arrays de caracteres y arrays de bytes. Consultar la documentación del API del JDK para más detalles.

10.3. Concatenación de Strings

Java define el operador + (suma) con un significado especial cuando las operandos son de tipo String. En este caso el operador suma significa concatenación. El resultado de la concatenación es un nuevo String compuesto por las dos cadenas, una tras otra. Por ejemplo: String x = "Concatenar" + "Cadenas"; da como resultado el String "ConcatenarCadenas".

También es posible concatenar a un String datos primitivos, tanto numéricos como booleanos y char. Por ejemplo, se puede usar: int i = 5; String x = "El valor de i es " + i;

Cuando se usa el operador + y una de las variables de la expresión es un String, Java transforma la otra variable (si es de tipo primitivo) en un String y las concatena. Si la otra variable es una referencia a un objeto entonces invoca el método toString() que existe en todas las clases (es un método de la clase Object).

10.4. Otros métodos de la clase String

La clase String dispone de una amplia gama de métodos para la manipulación de las cadenas de caracteres. Para una referencia completa consultar la documentación del API del JDK. El siguiente cuadro muestra un resumen con algunos de los métodos más significativos:

Método Descripción

char charAt(int index) (^) Devuelve el carácter en la posición indicada por index. El rango de index va de 0 a length() - 1. boolean equals(Object obj) (^) Compara el String con el objeto especificado. El resultado es true si y solo si el argumento es no nulo y es un objeto String que contiene la misma secuencia de caracteres.

boolean equalsIgnoreCase (String s)

Compara el String con otro, ignorando consideraciones de mayúsculas y minúsculas. Los dos Strings se consideran iguales si tienen la misma longitud y, los caracteres correspondientes en ambos Strings son iguales sin tener en cuenta mayúsculas y minúsculas.

int indexOf(char c) Devuelve el indice donde se produce la primera aparición de c. Devuelve -1 si c no está en el string. int indexOf(String s) (^) Igual que el anterior pero buscando la subcadena representada por s.

int length() Devuelve la longitud del String (número de caracteres)

String substring(int begin, int end)

Devuelve un substring desde el índice begin hasta el end

static String valueOf(int i) (^) Devuelve un string que es la representación del entero i. Observese que este método es estático. Hay métodos equivalentes donde el argumento es un float, double, etc. char[] toCharArray() String toLowerCase() String toUpperCase()

Transforman el string en un array de caracteres, o a mayúsculas o a minúsculas.

10.5. La clase StringBuffer

Dado que la clase String sólo manipula cadenas de caracteres constantes resulta poco conveniente cuando se precisa manipular intensivamente cadenas (reemplazadno caracteres, añadiendo o suprimiendo, etc.). Cuando esto es necesario puede usarse la clase StringBuffer definida también en el package java.lang. del API. Esta clase implanta un buffer dinámico y tiene métodos que permiten su manipulación comodamente. Ver la documentación del API.

11. Packages

11.1 Claúsula package

Un package es una agrupación de clases afines. Equivale al concepto de librería existente en otros lenguajes o sistemas. Una clase puede definirse como perteneciente a un package y puede usar otras clases definidas en ese o en otros packages.

Los packages delimitan el espacio de nombres (space name). El nombre de una clase debe ser único dentro del package donde se define. Dos clases con el mismo nombre en dos packages distintos pueden coexistir e incluso pueden ser usadas en el mismo programa.

Una clase se declara perteneciente a un package con la clausula package, cuya sintaxis es:

package nombre_package;

La clausula package debe ser la primera sentencia del archivo fuente. Cualquier clase declarada en ese archivo pertenece al package indicado.

Por ejemplo, un archivo que contenga las sentencias:

package miPackage;

... class miClase { ...

declara que la clase miClase pertenece al package miPackage.

La claúsula package es opcional. Si no se utiliza, las clases declaradas en el archivo fuente no pertenecen a ningún package concreto, sino que pertenecen a un package por defecto sin nombre.

La agrupación de clases en packages es conveniente desde el punto de vista organizativo, para mantener bajo una ubicación común clases relacionadas que cooperan desde algún punto de vista. También resulta importante por la implicación que los packages tienen en los modificadores de acceso, que se explican en un capítulo posterior.

11.2 Claúsula import

Cuando se referencia cualquier clase dentro de otra se asume, si no se indica otra cosa, que ésta otra está declarada en el mismo package. Por ejemplo:

package Geometria;

... class Circulo { Punto centro; ... }

En esta declaración definimos la clase Circulo perteneciente al package Geometria. Esta clase usa la clase Punto. El compilador y la JVM asumen que Punto pertenece también al package Geometria, y tal como está hecha la definición, para que la clase Punto sea accesible (conocida) por el compilador, es necesario que esté definida en el mismo package.

Si esto no es así, es necesario hacer accesible el espacio de nombres donde está definida la clase Punto a nuestra nueva clase. Esto se hace con la clausula import. Supongamos que la clase Punto estuviera definida de esta forma:

package GeometriaBase; class Punto { int x , y; }

Entonces, para usar la clase Punto en nuestra clase Circulo deberiamos poner:

package GeometriaAmpliada;

Nota: Los modificadores de acceso se explicarán detalladamente en un capítulo posterior.

Si una clase no se declara public sólo puede ser usada por clases que pertenezcan al mismo package.

12. Compilación y ejecución de programas

En este apartado se asume que se ha instalado el JDK (J2SE) distribuido por SUN Microsystems y que tanto el compilador (javac) como la JVM (java) están accesibles. Asumiremos que los comandos se emitirán desde una ventana DOS en un sistema Windows, siendo la sintaxis en un entorno UNIX muy parecida. En este capítulo se verán todos los pasos necesarios para crear, compilar y ejecutar un programa Java.

12.1. Creación y Compilación de un programa Java

PASO 1: Con un editor de texto simple (incluso notepad sirve, aunque resulta poco aconsejabe) creamos un archivo con el contenido siguiente:

package Programas.Ejemplo1;

class HolaMundo { public static void main ( String [] args) { System.out.println("Hola a todos"); } }

Guardamos el fichero fuente con nombre HolaMundo.java en la carpeta: C:\ApuntesJava \Programas\Ejemplo1.

PASO 2: Abrimos una ventana DOS y en ella:

C:> cd C:\ApuntesJava C:\ApuntesJava>javac Programas\Ejemplo \HolaMundo.java

Si no hay ningún error en el programa se producirá la compilación y el compilador almacenará en el directorio C:\ApuntesJava\Programas\Ejemplo1 un fichero de nombre HolaMundo.class, con el código ejecutable correspondiente a la clase HolaMundo.

Recuerda que en Java las mayúsculas y minúsculas son significativas. No es lo mismo la clase ejemplo que la clase Ejemplo1. Esto suele ser fuente de errores, sobre todo al principio. Sin embargo, ten en cuenta que en algunos sistemas operativos como Windows, o más concretamente en una ventana DOS, esta distinción no existe. Puedes poner cd C:\ApuntesJava o cd C:\APUNTESJAVA indistintamente: el resultado será el mismo (no así en cualquier UNIX, que sí distingue unas y otras). Asegurate por tanto, de que las palabras están correctamente escritas.

Cuando pones javac Programas\Ejemplo1\HolaMundo.java estás indicando al compilador que busque un archivo de nombre HolaMundo.java en la ruta Programas\Ejemplo1, a partir del directorio actual; es decir, estás especificando la ruta de un archivo.

En el ejemplo se utiliza la clase del API de Java System. Sin embargo el programa no tiene ningún import. No obstante el compilador no detecta ningún error y genera el código ejecutable directamente. Esto se debe a que la clase System está definida en el package java.lang, que es el único del que no es necesario hacer el import , que es hecho implicitamente por el compilador. Para cualquier clase del API, definida fuera de este package es necesario hacer el impor correspondiente.

12.2. Ejecución de un programa Java

PASO 3: Ejecutar el programa: Desde la ventana DOS.

C:\ApuntesJava>java Programas.Ejemplo1.HolaMundo

Se cargará la JVM, cargará la clase HolaMundo y llamará a su método main que producirá en la ventana DOS la salida:

Hola a todos

Los archivos .class son invocables directamente desde la línea de comandos (con la sintaxis java nombreDeClase) si tienen un método main definido tal como se vio en un capítulo anterior.

Se puede indicar a la JVM que busque las clases en rutas alternativas al directorio actual. Esto se hace con el parámetro -classpath (abreviadamente -cp) en la línea de comandos. Por ejemplo si el directorio actual es otro, podemos invocar el programa de ejemplo de la forma:

C:\Windows>java -cp C:\ApuntesJava Programas.Ejemplo1.HolaMundo

Con el parámetro -cp se puede especificar diversas rutas alternativas para la búsqueda de clases separadas por ;

Cuando pones java Programas.Ejemplo1.HolaMundo estás indicando a la JVM que cargue y ejecute la clase HolaMundo del Package Programas, subpackage Ejemplo1. Para cumplir está orden, expresada en términos Java de clases y packages la JVM buscará el archivo HolaMundo.class en la ruta Programas\Ejemplo1 que es algo expresado en términos del sistema de archivos, y por tanto del Sistema Operativo.

12.3. Archivos fuente (.java) y ejecutables (.class)

El esquema habitual es tener un archivo fuente por clase y asignar al archivo fuente el mismo nombre que la clase con la extensión .java (el nombre .java para la extensión es obligatorio). Esto generará al compilar un archivo .class con el mismo nombre que el fuente ( y que la clase). Fuentes y módulos residirán en el mismo directorio.

Lo habitual es tener uno o varios packages que compartan un esquema jerárquico de directorios en función de nuestras necesidades (packages por aplicaciones, temas, etc.)

Es posible definir más de una clase en un archivo fuente, pero sólo una de ellas podrá ser declarada public (es decir podrá ser utilizada fuera del package donde se define). Todas las demás clases declaradas en el fuente serán internas al package. Si hay una clase public entonces, obligatoriamente, el nombre del fuente tiene que coincider con el de la clase declarada como public. Los modificadores de acceso (public, es uno de ellos) se verán en el capítulo siguiente.13. Modificadores de acceso

13.1. Modificadores

Los modificadores son elementos del lenguaje que se colocan delante de la definición de variables locales, datos miembro, métodos o clases y que alteran o condicionan el significado del elemento. En capítulos anteriores se ha descrito alguno, como es el modificador static que se usa para definir datos miembros o métodos como pertenecientes a una clase, en lugar de pertenecer a una instancia. En capítulos posteriores se tratarán otros modificadores como final , abstract o synchronized. En este capítulo se presentan los modificadores de acceso, que son aquellos que permiten limitar o generalizar el acceso a los componentes de una clase o a la clase en si misma.

13.2. Modificadores de acceso

Los modificadores de acceso permiten al diseñador de una clase determinar quien accede a los datos y métodos miembros de una clase.

Los modificadores de acceso preceden a la declaración de un elemento de la clase (ya sea dato o método), de la siguiente forma:

[ modificadores ] tipo_variable nombre ; [ modificadores ] tipo_devuelto nombre_Metodo ( lista_Argumentos ) ;

Existen los siguientes modificadores de acceso:

  • public - Todo el mundo puede acceder al elemento. Si es un dato miembro, todo el mundo puede ver el elemento, es decir, usarlo y asignarlo. Si es un método todo el mundo puede invocarlo.
  • private - Sólo se puede acceder al elemento desde métodos de la clase, o sólo puede invocarse el método desde otro método de la clase.
  • protected - Se explicará en el capítulo dedicado a la herencia.
  • sin modificador - Se puede acceder al elemento desde cualquier clase del package donde se define la clase.

Pueden utilizarse estos modificadores para cualquier tipo de miembros de la clase, incluidos los constructores (con lo que se puede limitar quien puede crear instancias de la clase).