Bitsmi Blog
OCP7 09 – Pricipios básicos de E/S
Streams
El término define una corriente de datos de una fuente a un destino.
Todos los datos fluyen a través de un ordenador desde una entrada (fuente) hacia una salida (destino).
Los fuentes y destinos de datos son nodos de los flujos en la comunicación del ordenador. Todos los flujos presentan el mismo modelo a todos los programas Java que los utilizan:
- flujo de entrada: para leer secuencialmente datos desde una fuente (un archivo, un teclado por ejemplo). Llamado también como input stream.
- flujo de salida: para escribir secuencialmente datos a un destino (una pantalla, archivo, etc). Llamado también como outputstream. Estos nodos pueden ser representados por una fuente de datos, un programa, un flujo, etc..
Flujos de Datos (Bytes y carácteres)
La tecnología Java admite dos tipos de datos en los flujos: bytes y carácteres.
En el lenguaje Java los flujos de datos se detallan mediante clases que forman jerarquías según sea el tipo de dato char Unicode de 16 bits o byte de 8 bits.
A su vez, las clases se agrupan en jerarquías según sea su función de lectura (Read) o de escritura (Write).
La mayoría de las clases que se utilizan con Streams
se encuentran ubicadas en el paquete java.io
. En la cabecera del código fuente debe escribirse el importe del paquete import java.io.*
;
- Métodos básicos de lectura de Streams
- Clase InputStream (Bytes)
- int read()
- int read(byte[] buffer)
- int read(byte[] buffer, int offset, int length)
- Clase Reader (Caracteres)
- int read()
- int read(char[] buffer)
- int read(char[] buffer, int offset, int length)
- Clase InputStream (Bytes)
- Métodos básicos de escritura de Streams
- Clase OutputStream (Bytes)
- void write(int c)
- void write(byte[] buffer)
- void write(byte[] buffer, int offset, int length)
- Clase Writer (Caracteres)
- void write(int c)
- void write(char[] buffer)
- void write(char[] buffer, int offset, int length)
- void write(String string)
- void write(String string, int offset, int length)
- Clase OutputStream (Bytes)
Lectura/escritura en ficheros
Los tipos fundamentales de nodos o elementos a los que puede entrar y salir un flujo de datos que se pueden encontrar en el JDK 1.7 de Java son los siguientes:
Todos los flujos deben cerrarse una vez haya finalizado su uso, forzando un close
dentro de la cláusula finally
.
Flujos en Memoria Intermedia
Para la lectura de archivos cortos de texto es mejor utilizar FileInputStream
en conjunción con FileReader
. A continuación se añaden algunos ejemplos con código fuente para la memoria intermedia.
Ejemplo TestBufferedStreams
package bufferedstreams;
import java.io.*;
public class TestBufferedStreams {
public static void main(String[] args) {
try (
BufferedReader bufInput =
new BufferedReader(new FileReader("src\bufferedstreams\file1.txt"));
BufferedWriter bufOutput =
new BufferedWriter(new FileWriter("src\bufferedstreams\file2.txt"))
){
String line = bufInput.readLine();
while (line != null) {
bufOutput.write(line, 0, line.length());
bufOutput.newLine();
line = bufInput.readLine();
}
} catch (IOException e) {
System.out.println("Exception: " + e);
}
}
}
Ejemplo TestCharactersStreams
package bufferedstreams;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class TestCharactersStreams {
public static void main(String[] args) {
try (FileReader input = new FileReader("src\bufferedstreams\file1.txt");
FileWriter output = new FileWriter("src\bufferedstreams\file2.txt")) {
int charsRead;
while ((charsRead = input.read()) != -1) {
output.write(charsRead);
}
} catch (IOException e) {
System.out.println("IOException: " + e);
}
}
}
Entrada y salida estándar
Existen en Java 7 tres flujos estándar principales:
- System.in. Campo estático de entrada de tipo InputStream lo que permite leer desde la entrada estándar.
- System.out. Campo estático de salida de tipo PrintStream lo que permite escribir en la salida estándar.
- System.err. Campo estático de salida de tipo PrintStream lo que permite escribir en el error estándar.
A continuación se indican los métodos principales print
y println
de la clase PrintStream
- Métodos
print
con parámetros distintos- void print(boolean b) void print(char c) void print(char[] s) void print(double d) void print(float f) void print(int i) void print(long l) void print(Object obj) void print(String s)
- Métodos
println
con parámetros distintos void println() void println(boolean x) void println(char x) void println(char[] x) void println(double x) void println(float x) void println(int x) void println(long x) void println(Object x) void println(String x)
Ambos métodos son métodos sobrecargados de la clase PrintStream. A continuación se añade un ejemplo con código fuente para la entrada y salida estándar.
Ejemplo KeyboardInput
import java.io.*;
public class KeyboardInput {
public static void main(String[] args) {
try (BufferedReader in = new BufferedReader(new InputStreamReader(System.in))) {
String s = "";
while (s != null) {
System.out.print("Type xyz to exit: ");
s = in.readLine().trim();
System.out.println("Read: " + s);
System.out.println("");
if (s.equals("xyz")) {
System.exit(0);
}
}
} catch (IOException e) {
System.out.println("Exception: " + e);
}
}
}
Persistencia
La persistencia consiste en el proceso de serialización (secuencia de bytes) y la deserialización (reconstrucción del objeto obteniendo una copia a partir de los bytes) de un objeto en Java.
Un objeto tiene capacidad de persistencia cuándo puede almacenarse en disco o mediante cualquier otro dispositivo de almacenamiento o enviado a otra máquina y mantener su estado actual correctamente.
Dentro de una aplicación Java, cualquier clase que quiera ser serializada debe implementar la interfaz java.io.Serializable, marcador utilizado para indicar que la clase puede ser serializada.
Puede producirse la excepción NotSerializableException cuándo un objeto no se puede serializar.
Los campos marcados con los modificadores static o transient no pueden ser serializados por lo que al deserializar un objeto dichos campos apuntaran a un valor nulo o cero al finalizar la reconstrucción del objeto. A continuación se añade un ejemplo con código fuente para obtener la persistencia de los datos de un estudiante. Se incluyen la definición del objeto Student, y las clases para la persistencia junto a la clase de ejecución.
Ejemplo DeserializeMyClass
package persistence;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class DeserializeMyClass {
public static void main(String[] args) {
MyClass myclass = null;
try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("file1.ser"))) {
myclass = (MyClass) in.readObject();
} catch (ClassNotFoundException | IOException e) {
System.out.println("Exception deserializing file1.ser: " + e);
}
System.out.println("a = " + myclass.a);
System.out.println("b = " + myclass.b);
System.out.println("cad1 = " + myclass.getCad1());
System.out.println("cad2 = " + myclass.getCad2());
}
}
Ejemplo MyClass
package persistence;
import java.io.Serializable;
public class MyClass implements Serializable {
public int a = 0;
private String cad1 = "";
static int b = 0;
private transient String cad2 = "";
Student student = new Student();
public String getCad1() {
return cad1;
}
public void setCad1(String cad1) {
this.cad1 = cad1;
}
public String getCad2() {
return cad2;
}
public void setCad2(String cad2) {
this.cad2 = cad2;
}
}
Ejemplo SerializeMyClass
package persistence;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class SerializeMyClass {
public static void main(String[] args) {
MyClass myclass = new MyClass();
myclass.a = 100;
myclass.b = 200;
myclass.setCad1("Hello World");
myclass.setCad2("Hello student");
try (ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream("file1.ser"))) {
o.writeObject(myclass);
} catch (IOException e) {
System.out.println("Exception serializing file1.ser: " + e);
}
}
}
Ejemplo Student
package persistence;
public class Student {
String name = "Darío";
int age = 3;
}
Recordatorio
Las clases BufferedReader
y BufferedWriter
aumentan la eficacia de las operaciones de entrada y salida.
Estas clases permiten gestionar el búfer y escribir o leer línea por línea.
A continuación se añade un ejemplo sencillo utilizando un BufferedReader
para leer la cadena xyz
y finalizar la ejecución.1.-Ejemplo utilizando BufferedReader
try (BufferedReader in = new BufferedReader(
new InputStreamReader(system.in)))) {
String s = "";
System.out.print("Type xyz to exit");
s = in.readline().trim();
System.out.print("Read "+s);
// ...
}
OCP7 12 – Conexión JDBC en Java 7
Introducción
JDBC (Java Database Connectivity) es un acrónimo que identifica la API mediante la cuál las aplicaciones Java pueden conectarse a sistemas gestores de bases de datos (BBDD). Esta conexión se obtiene por la utilización de interfícies de conexión llamadas controladores JDBC (o conocidos también como drivers). Estas bases de datos acostumbran a ser en general relacionales, aunque también existen otros drivers para otros tipos de BBDD (nosql, ficheros planos, hojas de cálculo, etc).
OCP7 02 – Diseño de una clase Java
OCP7 03 – Diseño de una clase Java
En este apartado se introducen conceptos relacionados con el diseño de una clase.
OCP7 05 – Herencia en las interfaces Java
Uso de las interfaces Java
Una interfaz representa una alternativa a la herencia multiple de objetos. Son similares a las clases abstractas ya que contienen únicamente métodos públicos y abstractos. Ninguno de sus métodos pueden ser implementados (ni siquiera con un conjunto vacío de llaves). La declaración de una interfaz es similar a la declaración de una clase. Se usa la palabra reservada interface. Para la implementación de una interface se añade implements a la clase que implementa la interfaz. Una interfaz puede utilizarse como un tipo de referencia. Puede utilizarse el operador instanceof con las interfaces para detectar si un objeto es del tipo de referencia indicado por la interfície implementada. Interfaces de Marcador: definen un tipo concreto pero no describen los métodos que deben ser implementados por una clase, sólo sirven para la comprobación de tipos.
Existen dos tipos:
java.io.Serializable
és una interfaz de marcador utilizado por la biblioteca de E/S de Java para determinar si un objeto puede tener su estado serializado.
Como convertir de un tipo de dato al tipo de la interfaz
Antes de generar una excepción en la conversión de tipos de unos objetos a otros objetos se comprueba que dicha conversión sea posible mediante la utilización del operador instanceof (ya comentado anteriormente).
En general, cuándo se utilicen referencias, éstas deben utilizar el tipo más genérico posible, es decir, que sirvan para cualquier tipo de interfaz o clase padre. Así la referencia no se vincula a una clase particular.
Una clase puede heredar de una clase padre e implementar una o varias interfaces pero siempre en este orden: primero hereda – extends – y después implementa – implements – separando las interfaces mediante comas.
Una interfaz puede heredar de otra interfaz. Java no permite la herencia múltiple de clases pero sí la herencia múltiple de interfaces:
Si escribimos una clase que hereda de una clase que implementa una interfaz, entonces la clase que estamos escribiendo hereda también de dicha interfaz. La refactorización consiste en realizar modificaciones en el código para mejorar su estructura interna sin alterar su comportamiento externo.
Composición
Este patrón de diseño permite la creación de objetos más complejos a partir de objetos más simples. Se crea una nueva clase con referencias a otras clases. A esta nueva clase le agregaremos los mismos métodos que tienen las demás clases.
public class ComplexClass {
private Single1 c1 = new Single1();
private Single2 c2 = new Single2();
private Single3 c3 = new Single3();
}
Referencia al polimorfismo
Vamos a describir un ejemplo de polimorfismo. Si existe una clase nueva llamada Hombre que dispone de un método addCoche con la configuración de clases establecida en la siguiente imagen:
No se le puede pasar como argumento al método addCoche de Hombre cualquier tipo de coche. Solución: Para soportar el polimorfismo en las composiciones cada clase usada en la composición debe disponer de una interfaz definida y así se le podrá pasar cualquier tipo de coche al método en cuestión.
OCP7 13 – Localización
Localización
La localización o regionalización es el proceso mediante el cual un producto internacionalizado se configura para una determinada región, aprovechando las opciones que la internacionalización previa de este producto ha permitido (i18n). Por ejemplo, la internacionalización puede permitir utilizar distintos formatos de fecha, y la localización consiste en escoger el adecuado para una región específica.
OCP7 11 – Hilos (01) – Introducción
En este apartado se exponen los conceptos básicos referentes del diseño de programas concurrentes y paralelos y los mecanismos que la especificación estándar de Java pone a disposición del programador para conseguirlo.
Se tratarán los siguientes conceptos:
- Cómo el sistema operativo gestiona los procesos e hilos.
- Ciclo de vida de un hilo de ejecución.
- Sincronización y comunicación de datos entre hilos de ejecución.
OCP7 07 – Manejo de Cadenas
Argumentos y formatos de cadenas
El método main contiene el parámetro String[] args
. Puede recibir cero o más argumentos.
La implementación de la clase es la siguiente.
public class Echo {
public static void main(String[] args) {
for (String s: args) {
System.out.println(s);
}
}
}
La clase Echo
puede recibir los siguientes parámetros por línea de consola de comandos.
java Echo Enero Febrero Marzo Abril
Pasándole como argumentos en la línea de comandos los cuatro primeros meses del año.
Cada uno de los meses separados por un salto de línea. Esto es así debido a que el carácter espacio se utiliza para separar los parámetros unos de otros mediante un salto de línea entre cada uno.
Si se quiere mostrar un frase o texto completo, por ejemplo
java Echo "Enero, Febrero, Marzo, Abril son los cuatro primeros meses..."
se debe indicar la apertura y el cierre del texto mediante comillas dobles ""
.
Debe tenerse en cuenta que los arrays SIEMPRE empiezan con el valor 0, nunca con el 1.
Para recuperar los parámetros recibidos mediante el método main se utilizan los indices del vector.
Así con el código args[0]
se recupera el primer parámetro, con args[1]
se recupera el segundo, etc.
Adicionalmente, si una aplicación necesita recibir argumentos de tipo numérico, debe convertirse el argumento de tipo String
a un argumento que represente un número,
como por ejemplo el 34
, a su valor numérico equivalente, el 34
.
Este snippet transforma un argumento de consola de comandos en un tipo entero:
int primerArg;
if (args.length > 0) {
try {
primerArg = Integer.parseInt(args[0]);
System.out.println("Se ha convertido la cadena "+args[0]+" en el número siguiente -> "+primerArg);
} catch (NumberFormatException e) {
System.err.println("Argumento " + args[0] + " debe ser un número entero.");
System.exit(1);
}
}
primerArg
lanza una excepción del tipo NumberFormatException si el formato de args[0]
no es valido.
Todas las clases de tipo Number
— Integer
, Float
, Double
y demás — disponen de métodos de conversión que transforman un String representando un número en un objeto de su tipo específico.
Formatos de cadena
Los distintos argumentos de conversión que podemos utilizar para modificar el aspecto de una objeto String son los siguientes:
Cómo ejemplo inicial para limitar el número de caracteres que se visualizan por pantalla es suficiente con utilizar %2.2x
dónde x
corresponde al argumento de conversión que se haya pasado.
PrintWriter
PrintWriter
es una nueva clase de impresión bastante similar a Printf
perteneciente a la librería java.io
. Puede encontrarse su Javadoc aquí.
PrintWriter pw = new PrintWriter(System.io, true);
pw.printf("Texto escrito mediante Pw");
Procesamiento de Cadenas
Dentro del JDK en su versión del Java 7 existen varias clases Java que permiten manipular y tratar los elementos de tipo cadena. Son clases ya existentes en versiones anteriores del JDK.
String
Representa una cadena de caracteres inalterable. Al modificar un objeto String
lo que estamos haciendo en realidad es crear otro objeto String
. No es la más eficiente ni la mejor.
Ideal para tratar con cadenas de texto cuyo valor sabemos que no se modificará: mensajes de alerta, texto, informativo, etc.
StringBulider / StringBuffer
Debe utilizarse cuándo debamos trabajar con cadenas de texto que deban modificar su contenido en tiempo de ejecución.
Ambas disponen del mismo API de desarrollo.
Como norma general utilizaremos StringBuilder
en lugar de StringBuffer
.
Razón: Los métodos de StringBuffer
son sincronizados por lo que pueden ser utilizados de forma segura en un ambiente multihilo.
Los métodos de StringBuilder
no son sincronizados por lo que su uso implica un mejor rendimiento cuándo se usan localmente.
En general, la concatenación de Strings ocurre con variables locales a un método por lo que es recomendable utilizar de forma general StringBuilder
en lugar de StringBuffer
.
Cuan rápido es StringBuilder sobre StringBuffer?
StringBuilder
puede resultar un 50% más rápido para concatenar String
.
Para esta implementación.
public class StringBuilder_vs_StringBuffer {
public static void main(String[] args) {
StringBuffer sbuffer = new StringBuffer();
long inicio = System.currentTimeMillis();
for (int n = 0; n < 1000000; n++) {
sbuffer.append("zim");
}
long fin = System.currentTimeMillis();
System.out.println("Time using StringBuffer: " + (fin - inicio));
StringBuilder sbuilder = new StringBuilder();
inicio = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
sbuilder.append("zim");
}
fin = System.currentTimeMillis();
System.out.println("Time using StringBuilder: " + (fin - inicio));
}
}
Obteniendo casi una mejora del 50% utilizando StringBuilder
.
Clases Auxiliares en la Manipulación de cadenas
StringTokenizer
Extrae información (en forma de tokens) de una cadena de texto cuyos caracteres están separados por un carácter o símbolo especial o separador. Recorre la cadena de texto y obtiene las cadenas obtenidas (tokens) a partir del símbolo especial o separador indicado en la llamada al método.
// Inicialización
StringTokenizer st = new StringTokenizer("this is a test");
// Utilización
while(st.hasMoreTokens()) {
System.out.println(st.nextToken())
}
Puede encontrarse información más detallada dentro de la API de Java 7 en la siguiente dirección.
Scanner
Extrae información de una cadena o flujo de datos como StringTokenizer. Cambia el tipo de dato a las divisiones mientras se itera sobre ellas (StringTokenizer
no puede),
es decir, ante un flujo de datos podremos capturar enteros, decimales, etc en función de nuestro método de iteración utilizando Scanner
.
Error de codificación Unicode en un mensaje Javascript
Descripción del Problema
Cuando se publica un mensaje que contiene un carácter en formato Unicode no muestra el texto correctamente. No se renderiza el carácter Unicode en Javascript sino que se muestra directamente la codificación
Análisia del Problema
En el fichero JSP contenedor se encuentra la siguiente llamada:
<a onclick="return(confirm('Mensaje de llamada previo al mu00E9todo?'));"
href="<ruta_web_navegacion>"
>
Cuando el navegador renderiza el botón y procesa la funcionalidad del atributo onclick no renderiza correctamente el texto contenido dentro de los confirm como carácter Unicode desde el Javascript, sino que lo interpreta como un string que forma parte de la cadena contenida dentro del confirm .
Solución
Hay que añadir en el JSP una sección Javascript donde se implementa la siguiente función:
<script>
function metodo() {
return confirm("Mensaje de llamada previo al mu00E9todo?");
}
</script>
Esta nueva función renderiza directamente el texto contenido en el mensaje desde Javascript no desde HTML.
En el código HTML del enlace hay que indicar en el atributo onclick el siguiente código para ejecutar correctamente la ventana confirm y posteriormente el envío del resultado al onclick del botón:
<a onclick="return(metodo());"
href="<ruta_web_navegacion>"
>
Esto permite que la codificación Unicode sea interpretada correctamente y se obtiene el carácter é lugar de la codificación ue00E9 cuando se muestra el mensaje por pantalla.
Detalle
El método confirm devuelve el resultado de la opción elegida cuando se abre la ventana mediante Javascript.
Lo que se está haciendo se redirigir este regreso (sea true o false) hacia el botón onclick de manera que se ejecute directamente o no según la selección de las opciones del confirm Javascript que se haya efectuado.
Error Adobe AcroExch. al insertar un PDF en un documento de Word
Se dispone de un documento PDF de unos 2 Mb i es desea incrustar en un documento de Word estándar (independiente de versión de Office).
Al intentar realizar la incrustación se abre una ventana de alerta con el siguiente mensaje de error:
El programa usado para crear este objetivo es AcroExch. Dicho programa no está instalada en el equipo o no responde. Para editar este Objeto, Instale AcroExch o asegúrese de que todos los cuadros de diálogo de AcroExch están cerrados.
Es un problema relacionado con una opción de seguridad del Adobe Acrobat Reader. El programa por defecto dispuesta de la opción Activar modo protegido al iniciar. Así para solucionar el problema lo que hay que hacer es seguir los siguientes pasos:
- Abrir la Adode PDF Reader (múltiples versiones)
- Acceder a la opción Editar
- Acceder a la opción Preferencias
- Dependiendo de la versión del Acrobat puede ser la opción de General (Adobe Acrobat X) o la opción Seguridad (mejorada) (Adobe Acrobat XI). En otras versiones el nombre de la opción es el mismo, hay que buscarlo para encontrar el lugar donde aparezca.
- Hay que desactivar la opción Activar modo protegido al iniciar.
Ahora si se realiza de nuevo la inserción del documento PDF dentro del documento de Word no se volverá a mostrar el error y finalizará la incrustación correctamente.