Desplázate para cambiar de página
Computer Science A · AP Computer Science A
valores, referencias y los métodos que los usan
Una variable es una caja con nombre en la memoria. Una variable primitiva guarda su valor; una variable de referencia guarda la ubicación de un objeto.
int score = 42; int copy = score; // copy recibe su propio 42 copy = 7; // score sigue siendo 42 String name = "Ada"; // una referencia String other = name; // la misma referencia
La figura ejecuta estas líneas. copy = score copia el valor 42 en una segunda caja, así que cuando copy pasa a ser 7, score no se toca. name no guarda ningún carácter: su caja guarda una flecha (roja) hacia un objeto String creado en otro lugar de la memoria. other = name copia la flecha, no el objeto: dos flechas, un objeto.
Esa diferencia atraviesa todo Java y este capítulo: lo que guarda una variable, lo que recibe y devuelve un método, lo que crea new y por qué un String no cambia una vez que existe.
Este capítulo es la Unidad 1 de AP Computer Science A, Uso de objetos y métodos, el 15–25% de la calificación de opción múltiple: compilación y errores (1.1), tipos, expresiones y asignación (1.2–1.6), bibliotecas, documentación y métodos (1.7–1.11), objetos y su creación (1.12–1.14) y Strings (1.15). Las 42 preguntas de opción múltiple del examen vienen con la Java Quick Reference, que enumera todos los métodos de biblioteca que pueden usarse; su primera pregunta de respuesta libre llama a métodos de String.
y los tres tipos de error
Un algoritmo es un proceso paso a paso para realizar una tarea o resolver un problema. Se puede escribir con palabras, dibujar como diagrama o escribir como programa.
La secuenciación pone los pasos en orden, y se ejecutan uno a la vez: un método de Java ejecuta sus instrucciones de arriba abajo, a menos que algo cambie el flujo.
El código se puede escribir en cualquier editor de texto, pero la mayoría de los programadores usa un entorno de desarrollo integrado (IDE), que tiene herramientas para escribirlo, compilarlo y ejecutarlo. El compilador revisa el código fuente en busca de algunos errores y lo traduce; el compilador de Java genera bytecode, que ejecuta la máquina virtual de Java (JVM). Los errores que encuentra el compilador deben corregirse antes de que el programa pueda ejecutarse.
La figura envía tres programas por la línea, cada uno con un tipo de error.
int total = 7 + 3 // falta ; System.out.println(total); Un error de sintaxis rompe las reglas del lenguaje. El compilador lo encuentra (error: ';' expected) y nada se ejecuta.
int count = 0; System.out.println(10 / count);
Un error en tiempo de ejecución ocurre mientras el programa se ejecuta, y normalmente lo termina de forma anormal. Este es una excepción: un error que el compilador no detectó, que interrumpe el flujo normal de ejecución (ArithmeticException: / by zero).
double avg = (7 + 8) / 2; System.out.println(avg); // prints 7.0 Un error lógico compila y se ejecuta, pero se comporta de forma incorrecta: el promedio de 7 and 8 debería ser 7.5. Solo las pruebas con datos específicos, comparadas con el resultado esperado, lo encuentran. (Por qué imprime 7.0: página 1.3.)
int, double, boolean y referencias
Un tipo de dato es un conjunto de valores y las operaciones sobre ellos. Todo tipo es primitivo o de referencia.
El curso usa tres tipos primitivos, para números y valores booleanos:
| type | values | examples |
|---|---|---|
int | integers | 7, -40, 0 |
double | números reales | 2.5, 7.0, -0.01 |
boolean | true or false | true |
Java tiene otros cinco tipos primitivos (long, short, byte, float, char); quedan fuera del curso y del examen. Todo lo que no es primitivo, como un String, es un objeto, y su tipo es un tipo de referencia.
Una variable es un espacio de almacenamiento que guarda un valor, el cual puede cambiar mientras el programa se ejecuta. Toda variable tiene un nombre y un tipo, y una variable de tipo primitivo guarda un valor de ese tipo.
int n = 7; double d = 2.5; boolean ok = true; double w = 7; // stores 7.0 int m = 2.5; // no compila
Un int cabe en un double: double w = 7; guarda 7.0. Un double no cabe en un int, así que int m = 2.5; es un error de compilación: incompatible types: possible lossy conversion from double to int.
Elige el tipo según lo que el valor puede ser: el número de estudiantes es un int, un precio o un promedio es un double, y si un foco está encendido es un boolean.
print, println y secuencias de escape
System.out.print y System.out.println muestran información en la pantalla. println mueve después el cursor a una línea nueva; print lo deja donde está.
Un literal es la representación en código de un valor fijo: 7, 2.5, true. Un literal de cadena es una secuencia de caracteres entre comillas dobles: "CSA".
Algunos caracteres no se pueden escribir directamente dentro de las comillas. Una secuencia de escape empieza con una diagonal inversa y tiene un significado especial. El curso usa tres:
| secuencia de escape | means |
|---|---|
\" | unas comillas dobles |
\\ | una diagonal inversa |
\n | un salto de línea |
System.out.print("AP "); System.out.println("CSA"); System.out.println("say \"hi\""); System.out.print("a\\b\nc"); La consola muestra entonces (la figura lo escribe poco a poco):
AP CSA say "hi" a\b c El primer print deja el cursor después de AP , así que CSA queda en la misma línea. En la última línea, \\ imprime una diagonal inversa y \n empieza una línea nueva en medio de la cadena.
división entera, residuo y precedencia
Una expresión aritmética combina valores numéricos, variables y los operadores +, -, *, / y %. Dos valores int dan un int; al menos un double da un double.
System.out.println(17 / 5); // 3 System.out.println(17 % 5); // 2 System.out.println(17.0 / 5); // 3.4
Al dividir dos ints solo se conserva la parte entera del cociente: 17 bloques forman 3 grupos completos de 5. El operador de residuo % da lo que sobra: 2. Con un double en la división, el resultado es el cociente mismo, 3.4. (El curso usa % solo con a ≥ 0 y b > 0.)
Precedencia: *, / y % se aplican antes que + y -; los operadores de igual precedencia van de izquierda a derecha; los paréntesis cambian la agrupación. El compilador agrupa así los valores con sus operadores en tiempo de compilación.
| expression | value |
|---|---|
2 + 3 * 4 | 14 |
(2 + 3) * 4 | 20 |
10 - 4 - 3 | 3 |
7 + 9 % 4 | 8 |
1 / 2 * 4.0 | 0.0 |
1.0 / 2 * 4 | 2.0 |
En 1 / 2 * 4.0, 1 / 2 es una división entera y ya vale 0 antes de que aparezca el double.
Dividir un int entre el int cero lanza una ArithmeticException cuando se ejecuta la línea. (Dividir entre cero con un double, y los valores especiales de double que resultan, quedan fuera del curso.)
se evalúa el lado derecho y se guarda en el izquierdo
El operador de asignación = evalúa la expresión de su derecha hasta obtener un solo valor y lo guarda en la variable de su izquierda.
A una variable se le debe dar un valor antes de usarla en una expresión, y el valor debe ser de un tipo compatible. La primera asignación la inicializa. Cada asignación posterior reemplaza el valor: el anterior se pierde.
int a = 3; // a 3 int b = 8; // a 3, b 8 int temp = a; // a 3, b 8, temp 3 a = b; // a 8, b 8, temp 3 b = temp; // a 8, b 3, temp 3
Por eso intercambiar dos variables requiere una tercera. Sin temp, a = b destruiría el 3 de a antes de que b pudiera recibirlo. La figura mueve cada copia de una caja a otra: la fuente conserva su valor.
Cuando el programa se ejecuta, una expresión se evalúa a un solo valor, y ese valor tiene un tipo: a + b es un int, a + 0.5 es un double. Una instrucción como count = count + 1 no es una ecuación; lee count, suma 1 y guarda el resultado de vuelta.
A una variable de referencia se le puede asignar un objeto nuevo o null. El literal null significa que la referencia no está asociada a ningún objeto.
Las asignaciones dentro de expresiones, como a = b = 4;, quedan fuera del curso.
La entrada puede ser táctil, de audio, visual o de texto. La clase Scanner es una forma de leer texto escrito con el teclado; el examen no evalúa ninguna forma específica de entrada del usuario.
truncar, ampliar y redondear
Los operadores de conversión (int) y (double) convierten un valor de un tipo primitivo al otro.
Convertir un double a int trunca: se cortan los dígitos después del punto decimal, así que el valor se mueve hacia cero, sea cual sea su signo.
| expression | value |
|---|---|
(int) 3.99 | 3 |
(int) -3.99 | -3 |
7 / 2 | 3 |
(double) 7 / 2 | 3.5 |
(double) (7 / 2) | 3.0 |
7 / 2.0 | 3.5 |
1 + 2.5 | 3.5 |
Una conversión se aplica al valor que sigue justo después. (double) 7 / 2 convierte el 7 y luego divide como doubles; (double) (7 / 2) hace primero la división entera y convierte su resultado, 3.0.
Algunos códigos amplían un int a double automáticamente: en 1 + 2.5, y cuando un int se guarda en una variable double (página 1.2).
Para redondear un double al entero más cercano, desplázalo media unidad alejándose de cero y luego trunca: (int) (x + 0.5) para x no negativo, (int) (x - 0.5) para x negativo.
| x | (int) x | rounded |
|---|---|---|
2.7 | 2 | 3 |
-2.7 | -2 | -3 |
3.2 | 3 | 3 |
-1.4 | -1 | -1 |
La figura convierte cada uno de estos: el rojo trunca hacia cero; el azul se desplaza media unidad y luego trunca.
desbordamiento de enteros y error de redondeo
Un int se almacena en una cantidad fija de memoria, 4 bytes, por lo que tiene un rango: de Integer.MIN_VALUE a Integer.MAX_VALUE, inclusive.
| constant | value |
|---|---|
Integer.MAX_VALUE | 2147483647 |
Integer.MIN_VALUE | -2147483648 |
int n = Integer.MAX_VALUE - 2; // n es 2147483645 n = n + 1; // n es 2147483646 n = n + 1; // n es 2147483647 n = n + 1; // n es -2147483648 n = n + 1; // n es -2147483647
Si una expresión se evaluara a un int fuera del rango, ocurre un desbordamiento de enteros. El resultado es un int dentro del rango, pero no el valor esperado: uno más que MAX_VALUE es MIN_VALUE. La figura dibuja los ints como un anillo (sin escala) con la unión arriba. No hay ningún mensaje de error; el programa sigue con el número equivocado.
Los double tienen otro límite. Un double también se almacena en una cantidad fija de memoria, así que un resultado más preciso de lo que cabe se redondea al valor más cercano que se puede representar: un error de redondeo.
System.out.println(0.1 + 0.2); // 0.30000000000000004 Ni 0.1 ni 0.2 tienen una forma binaria exacta, y los pequeños errores se notan en la suma. Para evitar el error de redondeo, usa valores int cuando puedas: cuenta el dinero en centavos, no en pesos. (Los tipos decimales especiales que lo evitan quedan fuera del curso.)
+=, −=, *=, /=, %=, ++ y −−
Un operador de asignación compuesta hace la operación aritmética entre el valor de la variable y el valor de la derecha, y luego asigna el resultado a la variable.
score += 5 hace lo mismo que score = score + 5. Lo mismo vale para -=, *=, /= y %=; /= sobre un int es una división entera.
El operador de post-incremento ++ suma 1 a una variable numérica y lo guarda; el operador de post-decremento -- resta 1.
int score = 10; // score es 10 score += 5; // score es 15 score -= 3; // score es 12 score *= 2; // score es 24 score /= 5; // score es 4 score %= 3; // score es 1 score++; // score es 2 score--; // score es 1
La figura ejecuta las líneas y guarda cada valor en una cinta. Rastrea en papel de la misma manera: una línea, un valor nuevo.
Las formas de prefijo (++x) y los incrementos dentro de otras expresiones (arr[x++]) quedan fuera del curso.
cómo usar clases que no escribiste
Una biblioteca es una colección de clases. Una especificación de interfaz de programación de aplicaciones (API) le dice a un programador cómo usarlas.
Una clase define un tipo de referencia. Las clases de la biblioteca se agrupan en paquetes: String y Math están en java.lang, Scanner y ArrayList en java.util. Se pueden usar clases existentes para crear objetos sin ver su código; su documentación es lo que las hace utilizables.
Los atributos de una clase son los datos relacionados con ella, guardados en variables: los caracteres de un String. Sus comportamientos son lo que sus instancias pueden hacer, o lo que se puede hacer con ellas, y se definen con métodos: length(), substring.
Los comentarios explican el código al programador original y a otros. El compilador los ignora y nunca se ejecutan. Java tiene tres tipos:
// a comment to the end of the line /* a block of comment, over several lines */ /** a Javadoc comment, used to make API documentation */
La figura toma String del estante y abre su documentación de substring.
Una precondición debe ser verdadera justo antes de que un método se ejecute para que funcione como se espera. No se espera que el método la verifique: llamar a substring con to más allá del final rompe la precondición, y el resultado es una excepción, no una respuesta amable.
Una poscondición debe ser siempre verdadera después de que el método se ejecuta. Describe el resultado: lo que se devuelve o los valores de los atributos del objeto después.
parámetros, argumentos, valores de retorno
Un método es un bloque de código con nombre que se ejecuta solo cuando se le llama. Un bloque es cualquier código entre llaves.
Abstracción procedimental: puedes usar un método sabiendo qué hace, sin saber cómo se escribió. Su encabezado te dice lo que necesitas:
public static double pow(double base, double exponent)
Un parámetro es una variable declarada en el encabezado (base, exponent), que se usa dentro del método. La firma del método es el nombre del método y la lista ordenada de los tipos de sus parámetros: pow(double, double). Un método sin parámetros tiene una lista vacía: length().
Un argumento es un valor que se pasa cuando se llama al método. Los argumentos deben coincidir con los parámetros en número y orden. Se pasan por valor: cada parámetro empieza como una copia de su argumento. En la figura, 2 y 10 se convierten en las copias 2.0 y 10.0.
double r = Math.pow(2, 10); System.out.println(r); // 1024.0 Un método no void devuelve un valor de su tipo de retorno; para usarlo, guárdalo en una variable o úsalo en una expresión. Un método void no devuelve nada, así que no puede ser parte de una expresión: int x = System.out.println(5); no compila.
Los métodos están sobrecargados cuando varios tienen el mismo nombre y firmas distintas: Math.abs(int) y Math.abs(double). Los argumentos deciden cuál se ejecuta.
Una llamada interrumpe la secuencia de instrucciones: primero se ejecutan las instrucciones del método y, cuando se ha ejecutado la última (o un return), el control regresa justo después de la llamada.
métodos estáticos y Math.random()
Los métodos de clase pertenecen a la clase, no a un objeto en particular. Su encabezado lleva la palabra clave static, y se llaman con el nombre de la clase y el operador punto: Math.sqrt(2.0). Dentro de la clase que los define, se puede omitir el nombre de la clase.
La clase Math está en java.lang, así que está disponible sin import, y solo tiene métodos de clase. La Java Quick Reference enumera cinco:
| method | returns |
|---|---|
static int abs(int x) | el valor absoluto de un int |
static double abs(double x) | el valor absoluto de un double |
static double pow(double base, double exponent) | base elevada al exponente |
static double sqrt(double x) | la raíz cuadrada no negativa |
static double random() | un double ≥ 0.0 y < 1.0 |
El rango de Math.random() incluye 0.0 (un extremo inclusivo) y excluye 1.0 (uno exclusivo). La aritmética y la conversión de tipo lo llevan al rango que quieres:
int roll = (int) (Math.random() * 6) + 1;
La figura lo hace por pasos. * 6 estira [0.0, 1.0) a [0.0, 6.0). (int) trunca cada parte a 0, 1, …, 5: 6 partes iguales, así que 6 valores igualmente probables. + 1 los desplaza a 1, …, 6. En general, (int) (Math.random() * count) + low da uno de count ints a partir de low.
Sus 30 tiradas cayeron 5 on 1, 5 on 2, 6 on 3, 4 on 4, 6 on 5, 4 on 6: igualmente probable no significa igualmente frecuente en una serie corta.
el plano y lo que produce
Una clase es la implementación formal, o plano, de los atributos y comportamientos de un objeto. Un objeto es una instancia específica de una clase, con sus propios valores para esos atributos.
La figura usa una clase hipotética, Book, con dos atributos (un título y un número de páginas) y dos comportamientos (getTitle() y getPages()). Cada Book creado a partir de ella tiene su propio título y sus propias páginas: "Maps", 120; "Birds", 340; "Stars", 96.
Las clases relacionadas pueden compartir atributos y comportamientos comunes poniéndolos en una sola clase, una superclase. Las clases que la extienden, las subclases, aprovechan esos atributos y comportamientos sin escribirlos de nuevo: una relación de herencia. (Diseñar y escribir herencia queda fuera del curso.)
Toda clase en Java es subclase de la clase Object, por eso todo objeto tiene métodos como toString() y equals.
Una variable de un tipo de referencia guarda una referencia al objeto, que puede pensarse como la dirección de memoria del objeto: la flecha en las figuras, no el objeto.
Book b; // b puede guardar una referencia a un Book String s; // s puede guardar una referencia a un String
new, constructores y null
Un objeto se crea con la palabra clave new seguida de una llamada a uno de los constructores de la clase.
Un constructor tiene el mismo nombre que su clase. Su firma es ese nombre y la lista ordenada de los tipos de sus parámetros; su encabezado enumera los tipos y los nombres de los parámetros. La documentación de la clase hipotética Book enumera:
Book(String title, int pages) Book(String title) String getTitle() int getPages()
Dos constructores con firmas distintas: los constructores están sobrecargados, y los argumentos deciden cuál se llama. new Book("Maps") llama al segundo.
Book b = new Book("Maps", 120); Book c = null; La figura ejecuta la primera línea en orden: new reserva memoria para un Book; se ejecuta el constructor, cuyos parámetros fijan los valores iniciales de los atributos; luego la referencia al nuevo objeto se guarda en b. Igual que una llamada a un método, la llamada a un constructor interrumpe la secuencia de instrucciones, y el control regresa justo después de la llamada cuando se ha ejecutado su última instrucción.
Los argumentos del constructor deben ser compatibles en número y orden con los tipos de los parámetros, y se pasan por valor: new Book(120, "Maps") no compila, porque ningún constructor recibe un int y luego un String.
Una variable de un tipo de referencia guarda una referencia a un objeto o, si no hay objeto, null: c no se refiere a nada.
el operador punto y null
Los métodos de instancia se llaman sobre un objeto de la clase, con el nombre del objeto, el operador punto y el método: b.getPages().
int p = b.getPages(); // p es 120 int q = c.getPages(); // NullPointerException
La llamada sigue la referencia en b hasta su objeto, ejecuta getPages() sobre ese objeto, y el int que devuelve (120) se guarda en p. Otro objeto Book respondería con sus propias páginas.
c es null: no hay objeto al que seguir la flecha. Llamar a un método sobre una referencia null lanza una NullPointerException cuando se ejecuta la línea. Sí compila: el compilador solo verifica que un Book tenga un getPages().
Compáralo con el método de clase de la página 1.10: Math.sqrt(x) se llama sobre la clase; s.length() se llama sobre un objeto String y responde solo por esa cadena.
inmutables; la concatenación crea nuevos
Un objeto String representa una secuencia de caracteres. Se crea a partir de un literal de cadena, "cat", o con el constructor, new String("cat"). String está en java.lang, disponible de manera predeterminada.
Un objeto String es inmutable: una vez creado, sus caracteres nunca cambian. Los métodos llamados sobre un String no lo modifican; devuelven un valor nuevo.
String s = "cat"; // s es "cat" s = s + "s"; // s es "cats" String t = s + 1 + 2; // t es "cats12" String v = 1 + 2 + s; // v es "3cats"
+ y += concatenan Strings y crean un objeto String nuevo. En la figura, s = s + "s" construye "cats" y hace que s apunte a él; el objeto "cat" sigue siendo exactamente "cat".
Un primitivo concatenado con un String se convierte en String. + trabaja de izquierda a derecha, así que importa dónde aparece primero el String: en s + 1 + 2, cada número se une como texto; en 1 + 2 + s, 1 + 2 se suma primero, como ints.
Cualquier objeto se puede concatenar con un String: Java llama al método toString del objeto, que toda clase hereda de Object y que devuelve una cadena que representa al objeto. Las subclases a menudo lo sobrescriben (un método público con la misma firma que el de la superclase, con un comportamiento propio); escribir esa sobrescritura queda fuera del curso.
índices, length, substring, indexOf
Los caracteres de un String tienen índices de 0 a uno menos que su longitud. Usar un índice fuera de ese rango lanza una StringIndexOutOfBoundsException.
La Java Quick Reference enumera estos métodos de String (y el constructor; split viene con los archivos de texto, en el tema 4.6):
| method | returns |
|---|---|
int length() | el número de caracteres |
String substring(int from, int to) | los caracteres desde el índice from hasta el índice to - 1 |
String substring(int from) | substring(from, length()) |
int indexOf(String str) | el índice de la primera aparición de str; −1 si no se encuentra |
boolean equals(Object other) | página siguiente |
int compareTo(String other) | página siguiente |
String s = "BANANAS"; s.length(); // 7 s.substring(1, 4); // "ANA" s.substring(4); // "NAS" s.indexOf("NA"); // 2 s.indexOf("X"); // -1 s.substring(3, 4); // "A"
to es el primer índice que se deja fuera, así que substring(from, to) tiene to - from caracteres. indexOf encuentra solo la primera aparición: "NA" también empieza en el índice 4.
Una cadena con el único carácter en index es substring(index, index + 1).
equals, compareTo y no ==
equals pregunta si dos Strings tienen la misma secuencia de caracteres. compareTo pregunta cuál va primero.
String a = "pear"; String b = new String("pear"); System.out.println(a == b); // false System.out.println(a.equals(b)); // true System.out.println(a.compareTo("peach") > 0); // true a y b se refieren a dos objetos distintos que tienen los mismos caracteres. a.equals(b) compara los caracteres, par por par: true. a == b compara las referencias, es decir, si apuntan al mismo objeto: false. Compara Strings con equals. (La comparación de referencias con == vuelve en el tema 2.6.)
a.compareTo(other) devuelve un valor menor que 0 si a va antes que other, 0 si son iguales, y mayor que 0 si a va después. Los Strings se ordenan según el alfabeto: deciden los primeros caracteres que difieren. "pear" y "peach" coinciden hasta el índice 3, donde 'r' va después de 'c', así que el resultado es positivo. Confía solo en su signo: es lo único que promete la Quick Reference.
El curso usa equals solo para comparar un String con otro String.