Serialización y deserialización en Java [Java Internals Part 6] [Segunda parte]
En este artículo veremos cómo ocurre la serialización en Java utilizando la clase ObjectOutputStream. ¡Veremos las implementaciones internas de las cosas que usamos en nuestras clases ObjectOutputStream antes de llegar a su implementación para que tengamos un historial de comprensión!
discutimos aquí sobre las clases mencionadas a continuación:
- Interfaz de salida de objeto
- BlockDataOutputStream
- MangoMesa
- ReemplazarTabla
extiende OutputStream implementa ObjectOutput, ObjectStreamConstants
- escribe tipos de datos primitivos y gráficos de objetos Java en el flujo
- La clase de cada objeto serializable se codifica usando
2. firma de la clase
3. valores de los campos de objetos y matrices
4. cierre de cualquier otro objeto
- El método writeObject se usa para escribir en una secuencia
- tipos de datos primitivos -> escritos para transmitir usando los métodos de salida de datos
- la cadena se puede escribir usando el método writeUTF
- la forma serializada de una constante de enumeración consiste solo en su nombre
- los valores de campo de las constantes no se transmiten
- el proceso mediante el cual se serializan las constantes de enumeración no se puede personalizar
- se ignoran los métodos writeObject, writeReplace específicos de cualquier clase
- cualquier declaración de campo serialPersistantFields o SerialVersionUID también se ignora
- el tipo de enumeración tiene serialVersionUUID fijo => OL
extiende DataOutput, AutoCloseable .
Interfaz de salida de datos :
Hay varios métodos en esta interfaz:
en nuestra interfaz ObjectOutput tenemos:
- void escribirObjeto(Objeto obj)
- void write(int b) => escribe un byte, este método bloquea hasta que se escribe un byte
- void write(byte b[]) -> escribe una matriz de bytes, se bloqueará hasta que se escriba
- void write(byte b[] , int off , int len) -> escribe un subarreglo de bytes
- void flush () -> vacía la secuencia, escribirá cualquier byte de salida almacenado en el búfer
- void close() -> cierra la secuencia
variables miembro:
BlockDataOutputStream:
Constructor:
BlockDataOutputStream(OutputStream out)
{
this.out = out;
dout = new DataOutputStream(this);
}
- establece el modo de datos
- si el modo actual es el mismo que el de la entrada, no se realiza ninguna acción
- de lo contrario, primero drenamos () los datos almacenados en búfer
- devolvemos el valor del modo anterior
escribe todos los datos almacenados en el búfer de esta secuencia en la secuencia subyacente.
void drain() throws IOException {
if (pos == 0) {
return;
}
if (blkmode) {
writeBlockHeader(pos);
}
out.write(buf, 0, pos);
pos = 0;
}
Writes block data header. Data blocks shorter than 256 bytes are prefixed
with a 2-byte header; all others start with a 5-byte header.
private void writeBlockHeader(int len) throws IOException {
if (len <= 0xFF) {
hbuf[0] = TC_BLOCKDATA;
hbuf[1] = (byte) len;
out.write(hbuf, 0, 2);
} else {
hbuf[0] = TC_BLOCKDATALONG;
Bits.putInt(hbuf, 1, len);
out.write(hbuf, 0, 5);
}
}
boolean setBlockDataMode(boolean mode) throws IOException {
if (blkmode == mode) {
return blkmode;
}
drain();
blkmode = mode;
return !blkmode;
}
escritura vacía (int b) :
comprueba si la posición es mayor que el tamaño máximo del bloque -> drenaje ()
de lo contrario, escribe los datos en el búfer.
public void write(int b) throws IOException {
if (pos >= MAX_BLOCK_SIZE) {
drain();
}
buf[pos++] = (byte) b;
}
public void flush() throws IOException {
drain();
out.flush();
}
public void close() throws IOException {
flush();
out.close();
}
void write(byte[] b, int off, int len, boolean copy)
throws IOException
{
if we are not having blkmode or no need to copy then we can write
the bytes directly
if (!(copy || blkmode)) { // write directly
drain();
out.write(b, off, len);
return;
}
we simply keep writting untill our len > 0 , in case our pos goes out
we drain else we increment the offset and decrement the len as we go on
writting
while (len > 0) {
if (pos >= MAX_BLOCK_SIZE) {
drain();
}
if (len >= MAX_BLOCK_SIZE && !copy && pos == 0) {
// avoid unnecessary copy
writeBlockHeader(MAX_BLOCK_SIZE);
out.write(b, off, MAX_BLOCK_SIZE);
off += MAX_BLOCK_SIZE;
len -= MAX_BLOCK_SIZE;
} else {
int wlen = Math.min(len, MAX_BLOCK_SIZE - pos);
System.arraycopy(b, off, buf, pos, wlen);
pos += wlen;
off += wlen;
len -= wlen;
}
}
}
Manejar Mesa :
es una implementación similar muy interesante con hashTable .
Tabla hash de identidad ligera que asigna objetos a identificadores de enteros, asignados en orden ascendente.
Constructor:
HandleTable(int initialCapacity, float loadFactor) {
this.loadFactor = loadFactor;
spine = new int[initialCapacity];
next = new int[initialCapacity];
objs = new Object[initialCapacity];
threshold = (int) (initialCapacity * loadFactor);
clear();
}
Asigna el siguiente identificador disponible al objeto dado y devuelve el valor del identificador. Los identificadores se asignan en orden ascendente a partir de 0.
- aquí primero verificamos si nuestro tamaño ha excedido nuestra capacidad de matriz next.length [la siguiente matriz almacena el próximo valor de identificador candidato]
- en el caso de que el tamaño ≥ next.length , aumentemos nuestra capacidad de la tabla hash al alargar las matrices de entrada.
y copiamos nuestra siguiente matriz a una matriz de nuevo tamaño
así como creamos una nueva matriz para objetos
private void growEntries() {
int newLength = (next.length << 1) + 1;
int[] newNext = new int[newLength];
System.arraycopy(next, 0, newNext, 0, size);
next = newNext;
Object[] newObjs = new Object[newLength];
System.arraycopy(objs, 0, newObjs, 0, size);
objs = newObjs;
}
por lo que nuestra nueva matriz de columna adquiere el tamaño prevSpineSize * 2 + 1
ahora tenemos un nuevo umbral i,e spine.length * loadFactor
ahora llamamos al método de inserción para almacenar los objetos en la tabla hash aquí.
private void growSpine() {
spine = new int[(spine.length << 1) + 1];
threshold = (int) (spine.length * loadFactor);
Arrays.fill(spine, -1);
for (int i = 0; i < size; i++) {
insert(objs[i], i);
}
}
simplemente encontramos el índice hash obteniendo el hashValue del objeto % de la columna vertebral. Longitud .
luego agregamos el objeto a nuestro objs.
agregamos el próximo candidato al identificador con el valor en el índice en la columna vertebral y finalmente actualizamos el valor de la columna vertebral [índice] .
private void insert(Object obj, int handle) {
int index = hash(obj) % spine.length;
objs[handle] = obj;
next[handle] = spine[index];
spine[index] = handle;
}
int assign(Object obj) {
if (size >= next.length) {
growEntries();
}
if (size >= threshold) {
growSpine();
}
insert(obj, size);
return size++;
}
nos ayuda a dar el identificador asociado con el objeto
básicamente iteramos desde el valor de la columna vertebral [índice] y seguimos avanzando hacia el siguiente valor hasta que nuestros objetos coincidan.
int lookup(Object obj) {
if (size == 0) {
return -1;
}
int index = hash(obj) % spine.length;
for (int i = spine[index]; i >= 0; i = next[i]) {
if (objs[i] == obj) {
return i;
}
}
return -1;
}
La tabla de reemplazo se usa para asignar el objeto de reemplazo en la secuencia.
utiliza internamente la tabla de manijas que vimos antes
y mantiene un objeto de reemplazos.
miembros:
constructor:
ReplaceTable(int initialCapacity, float loadFactor) {
htab = new HandleTable(initialCapacity, loadFactor);
reps = new Object[initialCapacity];
}
toma el índice de htab asignando el objeto allí,
comprueba si podemos agregar el valor en el índice dado en caso de que necesitemos hacer crecer la tabla, luego llamamos a la función de crecimiento y finalmente insertamos el valor en el índice de reemplazo.
void assign(Object obj, Object rep) {
int index = htab.assign(obj);
while (index >= reps.length) {
grow();
}
reps[index] = rep;
}
private void grow() {
Object[] newReps = new Object[(reps.length << 1) + 1];
System.arraycopy(reps, 0, newReps, 0, reps.length);
reps = newReps;
}
Object lookup(Object obj) {
int index = htab.lookup(obj);
return (index >= 0) ? reps[index] : obj;
}
- SerialCallBackContextSerialCallBackContext
- DebugTraceInfoStackDebugTraceInfoStack
Gracias por leer !
Apoya nuestra publicación siguiéndola
Identificación eficiente de números primos (1 — n) en JavaScript: una guía paso a paso: sumérjase en… Introducción a la arquitectura Spark Obtenga más información sobre los objetos complementarios en Kotlin Domine el arte de los hábitos: una guía completa para crear mejores hábitos y romperlos Bad Ones Parte 1: Top (1-5) Preguntas comunes de JavaScript Por qué Java es el lenguaje de programación más querido en el planeta Integración de Splunk con SpringBoot Parte — 1 El secuestro de clics y sus medidas preventivas Integración de Splunk con SpringBoot — Parte 2 2023 React JS Trends ver
![¿Qué es una lista vinculada, de todos modos? [Parte 1]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)



































