Serializzazione e Deserializzazione in Java [ Java Internals Part 6 ] [ Seconda parte]
In questo articolo vedremo come avviene la serializzazione in Java utilizzando la classe ObjectOutputStream. Vedremo le implementazioni interne delle cose che usiamo nelle nostre classi ObjectOutputStream prima di arrivare alla sua implementazione in modo da avere una storia di comprensione!
discutiamo qui delle classi menzionate di seguito:
- Interfaccia ObjectOutput
- BlockDataOutputStream
- ManigliaTavolo
- Sostituisci tabella
estende OutputStream implementa ObjectOutput , ObjectStreamConstants
- scrive i tipi di dati primitivi e i grafici degli oggetti Java nello stream
- class di ogni oggetto serializzabile è codificata utilizzando
2. firma della classe
3. valori dei campi oggetto e degli array
4. chiusura di qualsiasi altro oggetto
- Il metodo writeObject viene utilizzato per scrivere in un flusso
- tipi di dati primitivi -> scritti nel flusso utilizzando i metodi Dataoutput
- la stringa può essere scritta usando il metodo writeUTF
- la forma serializzata di una costante enum consiste solo nel suo nome
- i valori di campo delle costanti non vengono trasmessi
- il processo mediante il quale le costanti enum vengono serializzate non può essere personalizzato
- qualsiasi metodo writeObject , writeReplace specifico della classe viene ignorato
- vengono ignorate anche eventuali dichiarazioni di campi serialPersistantFields o SerialVersionUID
- il tipo enum ha corretto serialVersionUUID => OL
estende DataOutput , AutoCloseable .
Interfaccia di uscita dati :
ci sono diversi metodi sotto questa interfaccia:
nella nostra interfaccia ObjectOutput abbiamo:
- void writeObject(Oggetto oggetto)
- void write(int b) => scrive un byte , questo metodo si blocca fino a quando non viene scritto un byte
- void write(byte b[]) -> scrive un array di byte, bloccherà fino a quando non sarà scritto
- void write(byte b[] , int off , int len) -> scrive un sottoarray di byte
- void flush() -> svuota lo stream , scriverà tutti i byte di output bufferizzati
- void close() -> chiude lo stream
variabili membro:
BlockDataOutputStream :
Costruttore :
BlockDataOutputStream(OutputStream out)
{
this.out = out;
dout = new DataOutputStream(this);
}
- imposta la modalità dati
- se la modalità corrente è la stessa dell'input, non viene intrapresa alcuna azione
- altrimenti per prima cosa scarichiamo() i dati bufferizzati
- restituiamo il valore della modalità precedente
scrive tutti i dati bufferizzati da questo flusso al flusso sottostante.
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;
}
void write(int b) :
controlla se la posizione è maggiore della dimensione massima del blocco -> drain()
altrimenti scrive i dati nel buffer.
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;
}
}
}
ManigliaTabella:
è un'implementazione simile molto interessante con hashTable .
Tabella hash di identità leggera che mappa gli oggetti su handle interi, assegnati in ordine crescente.
Costruttore :
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();
}
Assegna l'handle disponibile successivo all'oggetto specificato e restituisce il valore dell'handle. Gli handle vengono assegnati in ordine crescente a partire da 0.
- qui controlliamo prima se la nostra dimensione ha superato la capacità dell'array next.length [l'array successivo memorizza il prossimo valore di handle candidato]
- nel caso in cui size ≥ next.length aumentiamo la nostra capacità della tabella hash allungando gli array di voci .
e copiamo il nostro prossimo array in un nuovo array di dimensioni
così come creiamo un nuovo array per gli oggetti
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;
}
quindi il nostro nuovo array di spine diventa di dimensioni prevSpineSize * 2 + 1
ora abbiamo una nuova soglia i,e spine.length * loadFactor
ora chiamiamo il metodo insert per memorizzare gli oggetti nella tabella hash here .
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);
}
}
troviamo semplicemente l'indice con hash ottenendo l'hashValue dell'oggetto % spine. Lunghezza .
quindi aggiungiamo l'oggetto ai nostri objs.
aggiungiamo il candidato successivo all'handle con il valore all'indice nel dorso e infine aggiorniamo il valore spine[index] .
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++;
}
ci aiuta a dare l'handle associato all'oggetto
fondamentalmente iteriamo dal valore [indice] della spina dorsale e continuiamo a passare al valore successivo fino a quando i nostri oggetti corrispondono a .
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 tabella di sostituzione viene utilizzata per eseguire il mapping all'oggetto di sostituzione nel flusso.
utilizza internamente la Handle Table che abbiamo visto prima
e mantiene un oggetto di sostituzioni.
membri :
costruttore:
ReplaceTable(int initialCapacity, float loadFactor) {
htab = new HandleTable(initialCapacity, loadFactor);
reps = new Object[initialCapacity];
}
prende l' indice da htab assegnando l'oggetto lì,
controlla se possiamo aggiungere il valore all'indice dato nel caso in cui abbiamo bisogno di far crescere la tabella, quindi chiamiamo la funzione grow e infine inseriamo il valore all'indice di sostituzione.
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;
}
- SerialCallBackContext
- DebugTraceInfoStack
Grazie per aver letto !
Sostieni la nostra pubblicazione seguendola
Identificazione efficiente dei numeri primi (1 — n) in JavaScript : una guida passo passo — Immergiti nel... I cattivi Parte 1: Principali (1-5) domande frequenti su JavaScript Perché Java è il linguaggio di programmazione più amato del pianeta Integrazione Splunk con SpringBoot Parte — 1 Il click jacking e le sue misure preventive Integrazione Splunk con SpringBoot — Parte 2 2023 React JS Trends guardare
![Che cos'è un elenco collegato, comunque? [Parte 1]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)



































