Java tok za serijalizaciju: serijalizacija i deserijalizacija Java objekata
Java tok za serijalizaciju (ObjectInputStream i ObjectOutputStream) je vrsta toka koja može serijalizovati i deserijalizovati Java objekte.
Serijalizacija je proces pretvaranja objekta u niz bajtova (koji sadrži podatke objekta, tip objekta i atribute uskladištene u objektu i slične informacije), kako bi se mogao prenositi preko mreže, čuvati u fajlu ili razmenjivati između programa. U Javi se serijalizacija ostvaruje implementacijom interfejsa java.io.Serializable — samo objekti koji implementiraju interfejs Serializable mogu biti serijalizovani.
Deserijalizacija je proces pretvaranja niza bajtova nazad u objekat, kako bi se mogao koristiti u programu.

01,ObjectOutputStream
Klasa java.io.ObjectOutputStream nasleđuje iz klase OutputStream, pa se serijalizovani niz bajtova može upisati u izlazne tokove kao što su fajlovi, mrežne veze i slično.
Pogledajmo konstruktor klase ObjectOutputStream: ObjectOutputStream(OutputStream out)
Ovaj konstruktor prima objekat OutputStream kao parametar, čime se serijalizovani niz bajtova izbacuje u zadati izlazni tok. Na primer:
FileOutputStream fos = new FileOutputStream("file.txt");
ObjectOutputStream oos = new ObjectOutputStream(fos);Da bi se objekat mogao serijalizovati, moraju biti ispunjena dva uslova:
- Klasa mora implementirati interfejs
java.io.Serializable, inače se baca izuzetakNotSerializableException. - Sva polja klase moraju biti serijabilna. Ako neko polje ne treba da se serijalizuje, mora se označiti ključnom rečju
transient.
Primer korišćenja:
public class Employee implements Serializable {
public String name;
public String address;
public transient int age; // transient označava prolazno polje — ne serijalizuje se
}Sada da porazgovaramo o metodu writeObject(Object obj) — to je metod klase ObjectOutputStream koji serijalizuje objekat u niz bajtova i izbacuje ga u izlazni tok; pritom može obraditi referentne odnose između objekata, odnose nasleđivanja, statička polja i transient polja.
public class ObjectOutputStreamDemo {
public static void main(String[] args) {
Person person = new Person("Chenmo Wang Er", 20);
try {
FileOutputStream fos = new FileOutputStream("logs/person.dat");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(person);
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
class Person implements Serializable {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}U gore navedenom kodu, prvo se kreira objekat Person, a zatim se pomoću FileOutputStream i ObjectOutputStream serijalizuje i upisuje u fajl person.dat. U klasi Person je implementiran interfejs Serializable, što znači da se objekti te klase mogu serijalizovati.
02,ObjectInputStream
ObjectInputStream može pročitati niz bajtova koje je upisao ObjectOutputStream i deserijalizovati ga u odgovarajući objekat (koji sadrži podatke objekta, tip objekta i atribute uskladištene u objektu i slične informacije).
Jednostavnije rečeno: kako god je objekat izgledao pre serijalizacije, tako će izgledati i posle deserijalizacije.
Pogledajmo konstruktor: ObjectInputStream(InputStream in): kreira ObjectInputStream sa zadatim InputStream-om.
Metod readObject klase ObjectInputStream služi za čitanje objekta iz zadatog fajla. Primer:
String filename = "logs/person.dat"; // Ime fajla za deserijalizaciju
try (FileInputStream fileIn = new FileInputStream(filename);
ObjectInputStream in = new ObjectInputStream(fileIn)) {
// Čita objekat iz zadatog ulaznog toka fajla i deserijalizuje ga
Object obj = in.readObject();
// Deserijalizovani objekat se eksplicitno kastuje u zadati tip
Person p = (Person) obj;
// Štampa informacije o deserijalizovanom objektu
System.out.println("Deserialized Object: " + p);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}Prvo smo naveli ime fajla za deserijalizaciju (onog koji smo ranije serijalizovali pomoću ObjectOutputStream), a zatim kreirali objekat FileInputStream i objekat ObjectInputStream. Nakon toga pozivamo metod readObject klase ObjectInputStream da pročitamo objekat iz zadatog fajla i kastujemo ga u tip Person. Na kraju štampamo informacije o deserijalizovanom objektu.
03,Kryo
U praksi se retko koristi serijalizacija i deserijalizacija ugrađena u JDK, iz sledećih razloga:
- Slaba prenosivost: specifično za Javu, ne može se vršiti serijalizacija i deserijalizacija između različitih jezika.
- Loše performanse: serijalizovani niz bajtova je veliki, čime rastu troškovi prenosa i čuvanja.
- Bezbednosni problemi: napadači mogu konstruisati zlonamerne podatke kojima se ostvaruje udaljeno izvršavanje koda, što predstavlja ozbiljnu pretnju sistemu. Dodatno čitanje: Tragedija ranljivosti Java deserijalizacije.
Kryo je odlična Java biblioteka za serijalizaciju i deserijalizaciju — odlikuje se visokim performansama, velikom efikasnošću i lakoćom upotrebe i proširivanja, čime efikasno rešava bolne tačke mehanizma serijalizacije ugrađenog u JDK.
GitHub adresa: https://github.com/EsotericSoftware/kryo
Primer korišćenja:
Prvi korak: dodajte zavisnost u pom.xml.
<!-- Uvodimo Kryo alat za serijalizaciju -->
<dependency>
<groupId>com.esotericsoftware</groupId>
<artifactId>kryo</artifactId>
<version>5.4.0</version>
</dependency>Drugi korak: kreirajte objekat Kryo i registrujte klasu objekta pomoću metoda register(). Zatim, metodom writeObject() serijalizujte Java objekat u binarni tok, a metodom readObject() deserijalizujte binarni tok nazad u Java objekat. Na kraju, ispišite deserijalizovani Java objekat.
public class KryoDemo {
public static void main(String[] args) throws FileNotFoundException {
Kryo kryo = new Kryo();
kryo.register(KryoParam.class);
KryoParam object = new KryoParam("Chenmo Wang Er", 123);
Output output = new Output(new FileOutputStream("logs/kryo.bin"));
kryo.writeObject(output, object);
output.close();
Input input = new Input(new FileInputStream("logs/kryo.bin"));
KryoParam object2 = kryo.readObject(input, KryoParam.class);
System.out.println(object2);
input.close();
}
}
class KryoParam {
private String name;
private int age;
public KryoParam() {
}
public KryoParam(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "KryoParam{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}04,Rezime
U ovoj sekciji smo predstavili mehanizam serijalizacije u Javi i preporučili visoko-performantnu Java biblioteku Kryo kao zamenu za mehanizam serijalizacije ugrađen u JDK — koja se već široko koristi u kompanijama poput Twitter, Groupon, Yahoo, kao i u brojnim poznatim open-source projektima (kao što su Hive i Storm).
To bi bilo sve — nadam se da vam je ovaj tekst pomogao.
