ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [JAVA] 직렬화(Serialization) / ObjectInputStream, ObjectOutputStream / Serializable, transient
    DEV/JAVA 2024. 4. 23. 20:39

    객체데이터 스트림으로 만드는것.

    = 객체에 저장된 데이터를 스트림에 쓰기(write)위해 연속적인(serial) 데이터로 변환하는 것

    자바 시스템 내부에서 사용되는 Object 또는 Data를 외부의 자바 시스템에서도 사용할 수 있도록 byte 형태로 변환하는 기술이다.

    🌟 역직렬화 : 반대로 스트림으로부터 데이터를 읽어 객체를 만드는것

     

    ObjectInputStream, ObjectOutputStream

    스트림에 객체를 출력하는 직렬화는 ObjectOutputStream을 사용하고,

    스트림으로부터 객체를 입력하는 역직렬화는 ObjectInputStream을 사용한다.

    각각 InputStream과 OutputStream을 직접 상속받지만 기반 스트림을 필요로하는 보조스트림이다.

    //파일에 객체 저장(직렬화)
    FileOutputStream fos = new FileOutputStream("objectfile.ser");
    ObjectOutputStream out = new ObjectOutputStream(fos);
    
    //objectfile.ser파일에 UserInfo객체 직렬화하여 저장
    out.writeObject(new UserInfo());
    

    역직렬화 역시 입력스트림으로 변경하고 writeObject()대신 readObject()를 사용하여 저장된 객체를 읽기만 하면 객체로 역직렬화된다.

    단, readObejct()의 반환타입이 Object이기때문에 객체의 원래 타입으로 형변환 해주어야함

    FileInputStream fis = new FileInputStream("objectfile.ser");
    ObjectInputStream in = new ObjectInputStream(fis);
    
    UserInfo info = (UserInfo)in.readObject();

    readObject()와 writeObject() 이외에도 여러가지 타입의 값을 입출력할 수 있는 메서드를 제공함.

    그중 defaultReadObject()와 defaultWriteObject()는 자동 직렬화를 수행한다.

    Serializable, transient : 직렬화가 가능한 클래스 만들기

    직렬화하고자하는 클래스가 java.io.Serializable인터페이스를 구현

    🌟 Serializable 인터페이스는 아무런 내용이 없는 빈 인터페이스이지만 직렬화를 고려하여 작성한 클래스인지 판단하는 기준이 됨. (=마커인터페이스)

    - 마커 인터페이스 : 아무런 메서드도 선언하지 않은 껍데기 인터페이스로 주로 단순 타입 체크 등에 사용하기 위한 인터페이스이다.

     

    다음과 같이 Serializable을 구현한 클래스를 상속받으면, Serializable을 구현하지않아도됨.

    UserInfo는 Serializable을 구현하지않았지만 조상이 Serializable를 구현하였으므로 직렬화 가능.

    public class SuperUserInfo implements Serializable {
    	String name;
    	String password;
    }
    
    public class UserInfo extends SuperUserInfo {
    	int age;
    }
    

    UserInfo를 직렬화하면 조상에 정의된 인스턴스변수 name, password도 함께 직렬화됨

    그러나 다음과같이 조상클래스가 Serializable을 구현하지 않았다면 자손클래스를 직렬화 할때 name과 password는 직렬화 대상에서 제외됨

    public class SuperUserInfo {
    	String name;
    	String password;
    }
    
    public class UserInfo extends SuperUserInfo implements Serializable{
    	int age;
    }
    

    조상클래스에 정의된 인스턴스 변수 name과 password를 포함시키기 위해서는 조상클래스가 Serializable을 구현하도록하거나, UserInfo에서 조상의 인스턴스변수들이 직렬화되도록 처리하는 코드를 직접 추가해야한다.

    public class UserInfo extends SuperUserInfo implements Serializable{
    	int age;
    	
    	private void writeObject(ObjectOutputStream out) throws IOException{
    		out.writeUTF(name);
    		out.writeUTF(password);
    		out.defaultWriteObject(); //age
    	}
    	
    	private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundExecption {
    		name = in.readUTF();
    		password = in.readUTF();
    		in.defaultReadObject(); //age
    	}
    }
    

    🌟 모든 클래스의 최고 조상인 Object클래스는 Serializable을 구현하지 않아서 직렬화 할 수 없다.

    public class UserInfo implements Serializable {
    	String name;
    	String password;
    	int age;
    	
    	Object obj = new Object(); //NotSerializableException 발생
    	
    	Object obj = new String("abe"); //실제로 저장되는 객체는 String 인스턴스이므로 직렬화 가능
    }
    

    직렬화하고자하는 클래스에 직렬화가 안되는 객체에 대한 참조를 포함하고 있거나, 보안상 직렬화되면 안되는 값에 대해 제어자 transient를 붙여서 직렬화 대상에서 제외되도록 할 수 있다.

    = transient가 붙은 인스턴스변수의 값은 그 타입의 기본값으로 직렬화

    다음의 클래스를 역직렬화하면 password와 obj의 값은 null이 됨

    public class UserInfo implements Serializable {
    	String name;
    	transient String password; //직렬화 제외
    	int age;
    	
    	transient Object obj = new Object(); //직렬화 제외
    }
    

    댓글

Designed by Tistory.