3.2 Literales, asignaciones y variables - ajpaez/OCA GitHub Wiki

(OCA Objectives 2.1, 2.2, 2.3, and Upgrade Objective 1.2)

Valores literales para todos los tipos primitivos

Un literal primitivo es simplemente una representación de código fuente de los datos primitivos, en otras palabras, un numero entero, un numero de punto flotante, un boleano o el carácter que se setea mientras escribes el código. Algunos ejemplos de literales primitivos son:

  • 'b' // char literal
  • 42 // int literal
  • false // boolean literal
  • 2546789.343 // double literal

Literales para enteros

Hay cuatro formar de representar números enteros en el lenguaje Java, decimal, octal, hexadecimal y binario.

Literales numéricos con guiones bajos

A partir de Java 7, literales numéricos se pueden declarar con _, solo para mejorar la legibilidad.

int pre7  = 1000000;     // pre Java 7 – we hope it's a million
int with7 = 1_000_000;   // much clearer!
int i2 = 10_0000_0;     // legal, but confusing
int i5 = 0B10_1010;  
int i6 = 0x2_a;      

NOTA: Se puede establecer el _ en cualquier posición, pero NUNCA al principio o al final del literal.

Se puede usar el carácter en cualquier tipo numérico, doubles y float, pero NUNCA se puede añadir _ justo al lado del separador decimal.

Literales decimales

int length = 343;

Literales binarios

Nuevo en Java 7. Solo permiten el uso de 0 y 1. Deben comenzar con 0B o 0b.

int b1 = 0B101010;   // set b1 to binary 101010 (decimal 42)
int b2 = 0b00011;    // set b2 to binary 11 (decimal 3)

Literales octales

Se representan solo con los dígitos del 0 al 7. Se representan con un cero delante del numero:

    int six = 06;     // Equal to decimal 6
    int seven = 07;   // Equal to decimal 7
    int eight = 010;  // Equal to decimal 8
    int nine = 011;   // Equal to decimal 9

Puede tener hasta 21 dígitos en un número octal, sin incluir el cero inicial.

Literales hexadecimales

Usan los símbolos: 0 1 2 3 4 5 6 7 8 9 a b c d e f, para contar del 0 al 15. Acepta las letras tanto en minúscula como mayúscula. Permite un máximo de 16 dígitos en un numero hexadecimal sin incluir el prefijo 0x (o 0X) o el sufijo opcional L

    int x = 0X0001; // = 1
    int y = 0x7fffffff; // = 2147483647
    int z = 0xDeadCafe; // = -559035650

Los cuatro literales de enteros (binarios, octal, decimal y hexadecimal) se definen como enteros, pero se puede especificar que sea de tipo long colocando el sufijo L o l despues del numero.

    long jo = 110599L;
    long so = 0xFFFFl;  // Note the lowercase 'l'

Literales punto flotante

Los números en punto flotante se definen con un numero, un símbolo decimal y mas numero que representan la parte decimal:

double d = 1211.12121;

Los literales en punto flotante son definidos como double (64bits) por defecto, para definirlos como float (32 bits) simplemente añadimos el sufijo F o f al numero. También existe el sufijo D o d para indicar que es double pero no es necesario porque es su comportamiento por defecto.

Literales para boleanos

Se define como true o false. No se puede usar valores numéricos.

boolean t = true;  // Legal
boolean  f = 0;    // Compiler error!!!!!

Literales para caracteres

Un literal char es representado con un único carácter encerrado entre comillas simples, también se puede usar la representación Unicode del valor:

char a = 'a';
char b = '@';
char letraN = '\u004E'; 

Recuerda, los caracteres son enteros sin signo de 16 bits. Por tanto, se puede asignar un numero literal a un char, sin sobre pasar el limite de 16 bits (65535), por ejemplo:

char a = 0x892;        // hexadecimal literal
char b = 982;          // int literal
char c = (char)70000;  // The cast is required; 70000 is out of char range
char d = (char) -98;   // Ridiculous, but legal
//ilegal Possible loss of precision; needs a cast
char e = -29;
char f = 70000; 

También se permite guardar el carácter de escapado (\) se desea representar algún carácter especial:

char c = '\"';    // A double quote
char d = '\n';    // A newline
char tab = '\t';  // A tab

Literales para string

Un literal String es la representación del valor del objeto String.

String s = "Hello!";

Operador de asignación

Las variables son simplemente pequeños contenedores, con un tipo designado. Tu puedes tener un contenedor int, un contenedor double o un contenedor Button. Dentro de ese contenedor hay un montón de bits que representan un valor. Para los primitivos, los bits representan un valor numérico, Un byte con un valor de 6, por ejemplo, significa que el patrón de bit en la variable es 000000110, representado por 8 bits. Por lo que el valor de una variable primitiva esta claro, pero que hay dentro de un contenedor de Object? No hay nada, simplemente la variable tiene la referencia al object. Una variable de referencia contiene los bit que representan la forma de llegar al objeto. No sabemos cual es su formato. La forma en la que el objeto referido se almacena esta especificado por la maquina virtual (es un puntero a algo, pero no podemos saber de lo que realmente se trata). Todo lo que que podemos decir con certeza es que el valor de la variables no es el objeto, sino mas bien un valor que representa un objeto especifico en el heap. O el valor null. Si la variable de referencia no tiene asignado valor o ha sido asignado explícitamente el valor null, la variable contiene bits que representan el valor null.

Asignaciones de primitivos

El signo igual (=) es usado para asigna el valor a una variable, y es simplemente llamado operador de asignación. Se puede asignar una variable primitiva mediante un literal o con el resultado de una expresión. El punto mas importante a recordar es que un literal entero (como el 7) siempre es de forma implícita un int. Se permite la asignación directa de cualquier literal entero a cualquier tipo de variable menor que int, ya que el compilador automáticamente añade el casting byte b = (byte) 27; Esto ocurre tanto con byte, como con char, como con short, siempre y cuando el valor sea suficientemente pequeño como para poder almacenarlo en el tipo correspondiente. Debemos saber que el resultado de una expresión también será siempre un int. Si sumamos dos byte obtendremos un int, si multiplicamos un int y un short obtendremos un int, por tanto, el resultado de estas operaciones debe ser almacenado en un contenedor del tipo mas grande, sino el compilador se queja!!!

Asignaciones en la misma linea

Es perfectamente legal declarar varias variables del mismo tipo en la misma linea:

int a, b, c;

Al igual que podemos inicializarlas a la vez:

int a=1, b, c=34;

También podemos combinarlas entre ellas para realizar operaciones:

int a=1, b=a, c=a+b;

Pero el orden es importante!!!! no podemos usar una variable hasta que no sea declarada e inicializada

int a=1, b=c, c=a+b; //c no esta inicializada antes de que b la use.

Casting de primitivos

El casting permite convertir valores de un tipo a otro tipo. Los casting pueden ser implícitos o explícitos. Un casting implícito significa que no tienes que escribir el código para hace el casting, la conversión se hace automáticamente. Este tipo de casting ocurre cuando hacemos ampliaciones, es decir, ponemos algo pequeño en un contenedor mas grande. En el lugar opuesto tenemos el casting implícito, este debe aparecer cuando queremos poner algo grande en un contenedor mas pequeño, conlleva perdida de precisión y el peligro de que en tiempo de ejecución rompa todo!!! Casting implícito:

int a = 100;
long b = a; // casting implícito, un valor int siempre entra en un long
//otra forma en la que se realiza un casting implícito es con la siguiente operación 
byte z = 3;
z += 5;

El operador de asignación compuesto += le permite añadir a z el literal 5, sin poner en una conversión explícita. De hecho, +=, -=, *= y /= tendrán el mismo comportamiento.

Casting explícito:

float a = 100.001f;
int b = (int) a; // casting explícito, posible perdida de información

Para casting entre tipos enteros y en punto flotante, un casting de int a double no tiene problemas, es implícito, ya que cualquier valor int se ajusta a un double de 64-bits. Pero para castear un double en un int si que se requiere un casting implícito, int x = (int) 3957.229; con este casting se pierde toda la parte decimal.

También se puede castear un valor entero largo (long) en un mas pequeño (byte), pero como es mas pequeño requiere un casting explicito byte b = (byte) 555L; Este código compila y se puede ejecutar, simplemente no obtendremos el valor esperado por la perdida de precisión, después de la asignación el valor que contiene la variable b es -126. Al realizar el casting con un valor superior al que puede contener y reducir el tipo de dato, el bit mas a la izquierda simplemente desaparece. Si el bit mas significativo (el del signo) en cualquier tipo numérico pasa a ser 1 y por tanto su valor es negativo.

Asignación de números en punto flotante

La asignación de los números en punto flotante difieren un poco en cuanto a comportamiento con los enteros. Recuerde que cada literal en punto flotante es un double, no un float, por tanto no se puede hacer una asignación directa (casting implícito) de numero en punto flotante a float. Sin embargo con un casting explicito todo es correcto, float f = (float) 12.34;

Asignación de literales demasiado grandes para el tipo de variable

También obtendremos un error de compilador si intentamos asigna un valor literal demasiado grande para el tipo de variable. Lo que hace Java simplemente es truncar todos los bit de la izquierda que no sobrepasan del tamaño de la variable.

Asignación de una variable primitiva a otra variable primitiva

Cuando se asigna una variable primitiva a otra variable primitiva el contenido de la derecha se copia a la variable de la izquierda. Así que, ambas variables tienen el mismo patrón de bit que representa el valor pero las dos variables no tiene otra relación. Pero si se cambia el contenido de cualquier de las dos, la otra variable no se verá afectada. En otras palabras, ambas variables no se refieren al mismo lugar en la memoria, no comparten un valor único, ellas tiene copias idénticas.

Asignación de variables de referencia

Se puede asignar un nuevo objeto creado a una variable de referencia de la siguiente forma: Integer i = new Integer(); Esta asignacion se traduce en:

  • Crea una variable de referencia llamada bi de tipo Integer.
  • Crea un nuevo objeto Integer en el heap
  • Asigna el nuevo objeto Integer creado a la variable de referencia i

Si en lugar de crear un nuevo objeto simplemente declaramos la variable Integer i; estaríamos creando el espacio para una variable de referencia de tipo Integer, pero no se crea ningún objeto.

Como vimos antes también se pueden usar variables de referencia para referirse a cualquier objeto que represente una subclase del tipo de la variable de referencia creada. Por tanto, se puede asignar una subclase del tipo creado, pero no una superclase.

public class Foo {
  public void doFooStuff() { }
}
public class Bar extends Foo {
  public void doBarStuff() { }
}
class Test {
  public static void main (String [] args) {
     Foo reallyABar = new Bar();  // Legal because Bar is a subclass of Foo
     Bar reallyAFoo = new Foo();  // Illegal! Foo is not a subclass of Bar
  }
}

Tenemos que recordar siempre la regla IS-A para realizar estas asignaciones Bar IS-A Foo pero Foo NOT IS-A BAR!!!