-
Java - io 직렬화(Serialization)언어/JAVA 2023. 7. 24. 11:20
- 직렬화란, 객체를 저장했다가 다시 꺼내쓰거나, 네이트워크를 통해 객체를 주고받을 수 있게 만드는 방법이다.
1. 직렬화란?
- 객체를 데이터 스트림으로 만드는 것을 뜻한다.
- 객체에 저장된 데이터를 스트림에 쓰기 위해 연속적인 데이터로 변환하는 것을 말한다.
- 반대의 의미로 역직렬화가 있다.
- 자바 프로그램에 메모리 구조에서 살펴봤듯이, 인스턴스 메서드도 객체에 포함되지 않는다
-> 동일 클래스에 여러 인스턴스 별로 다른 값을 가지지 않음 -> 힙 영역이 아닌 타 영역에 생성된 메서드 참조해서 사용
- 객체를 저장한다는 것은 인스턴스 변수값을 저장한다는 의미이다.
2. 객체 스트림
- 직렬화와 역직렬화를 위한 스트림으로 ObjectInputStream, ObjectOutputStream이 있다.
- 보조 스트림이므로, 기반 스트림 InputStream, OutputStream과 연결되어 사용된다.
ObjectInputStream ois = new ObjectInputStream(바이트 입력 스트림);// 역직렬화 ObjectOutputStream oos = new ObjectOutputStream(바이트 출력 스트림);// 직렬화 oos.writeObject(객체) //직렬화 참조변수 변수 = (타입) ois.readObject(); // 역직렬화 //FileInput,Output stream이 기반 스트림으로 자주 사용
- readObject()와 wirteObject()외에도 다양한 메서드 제공한다.
3. Serializable 인터페이스와 transient
- 자바에서는 Serializable 인터페이스를 구현한 클래스만 직렬화할 수 있다.
- 위 인터페이스는 멤버가 없는 빈 인터페이스지만, 객체를 직렬화할 수 있다고 표시하는 역할을 수행한다.
- transient로 선언된 필드는 직렬화에서 제외되므로 출력되지 않는다. (직렬화시 필드의 기본값으로 직렬화 된다.)
- 직렬화 시에 필드로 포함된 각 객체들은 직렬화 가능한 객체면 직렬화되고, 아니면 안된다.
public class UserInfo implements java.io.Serializable{ } //인터페이스 구현한 클래스 상속받아도 직렬화 가능한 클래스가 됨 // 단 자손 직렬화 시, 조상도 필드도 함께 직렬화 됨 public class superUser implements java.io.Seializable{ // field } public class userinfo extends superUser{ // field 2 Object obj = new Object(); // 직렬화에서 빠짐 Object는 Serializable을 구현하지 않음 Object obj2 = new String("s");// String은 직렬화 가능 (객체에서 항상 중요한 것은 참조변수가 아닌 실제 생성된 객체가 무엇인지 임) }
4. SerialVersionUID필드
- 객체가 직렬화 될 때 클래스에 정의된 멤버 정보를 이용해서 serialVersionUID라는 클래스 버전이 자동생성되어 직렬화 내용에 포함된다. (역직렬화시 클래스 버전을 비교한다)
- 직렬화할 때 사용된 클래스와 역직렬화 시에 사용된 클래스는 기본적으로 동일한 클래스여야한다.
- 만약 클래스 이름이 같더라도 내용이 다르면 역직렬화에 실패한다.(*단 직렬화에 포함되지 않는 필드는 상관 없다)
- 클래스 내용이 다르다 할지라도 직렬화된 필드를 공통으로 포함하고 있다면, 역직렬화 할 수 있는 방법이 있다.
- 두 클래스가 동일한 SerialVersionUID 상수값을 가지고 있으면 된다.
//이 상태로 직렬화됨 public class Member implements Serialiazable{ static final long serialVersionUID =1; int field1; int field2; } // 역직렬화 전에 새로운 필드 추가 되었지만, 역직렬화 가능 public class member implements Seializable { static final long serialVersionUID =1; int field1; int field2; int field3; }
- serialVersionUID는 자동생성하는 기능을 제공하기 때문에 이를 이용하도록 하자.
* 네트워크로 객체를 직렬화하여 전송하는 경우, 보내는 쪽과 받는 쪽이 모두 같은 버전의 클래스를 가지고 있어야한다.
-> 클래스가 조금만 변경되어도 에러가 발생 (클래스에 내용이 추가되면 직렬화시
-> 이럴때 에러발생을 없애기 위해 수동으로 버전을 관리할 필요가 있다
(버전을 명시해두고, 추후 변동사항이 생기더라도, 버전이 자동으로 바뀌지않도록 관리) -> serialVersionUID
-> cmd를 이용해서도 버전을 알 수 있다
예제1
-직렬화 and 역직렬화
public class Product implements Serializable { private static final long serialVersionUID = 1L; private String name; private int price; public Product(String name, int price){ this.name = name; this.price = price; } @Override public String toString(){ return name+":"+price; } }
public class Member implements Serializable { private static final long serialVesrionUID = 1L; private String id; private String name; public Member(String id, String name){ this.id = id; this.name = name; } @Override public String toString(){ return id +":"+name; } }
public static void main(String[] args) throws Exception{ FileOutputStream fos = new FileOutputStream("C:\\Users\\won\\IdeaProjects\\untitled2\\src\\Seializableex"+ File.separator+"objects.bat"); ObjectOutputStream oos = new ObjectOutputStream(fos); Member me1 = new Member("jj","j"); Product p2 = new Product("놋북",1000); int[] arr1 = {1,2,3}; oos.writeObject(me1); oos.writeObject(p2); oos.writeObject(arr1); oos.flush();oos.close();fos.close(); //FileInputStream에 ObjectInputStream 보조스트림 연결 FileInputStream fis = new FileInputStream("C:\\Users\\won\\IdeaProjects\\untitled2\\src\\Seializableex"+ File.separator+"objects.bat"); ObjectInputStream ois = new ObjectInputStream(fis); Member m2 = (Member)ois.readObject(); Product p22 = (Product) ois.readObject(); int[] arr2 = (int[])ois.readObject(); ois.close();fis.close(); System.out.println(m2); System.out.println(p22); System.out.println(Arrays.toString(arr2)); }
-직렬화 역직렬화 예제 2
ublic class UserInfo implements Serializable { String name; String pass; int age; public UserInfo(){ this("Unknown","1111",0); } public UserInfo(String name ,String pass ,int age){ this.name = name; this.pass = pass; this.age = age; } @Override public String toString(){ return name+":"+pass+":"+age; } }
public class SerialEx1 { public static void main(String[] args){ try{ String fileName = "UserInfo.ser"; FileOutputStream fos = new FileOutputStream(fileName); BufferedOutputStream bos = new BufferedOutputStream(fos); ObjectOutputStream oos = new ObjectOutputStream(bos); UserInfo u1 = new UserInfo(); UserInfo u2 = new UserInfo("ss","232",26); ArrayList<UserInfo> list =new ArrayList<>(); list.add(u1); list.add(u2); oos.writeObject(u1); oos.writeObject(u2); oos.writeObject(list); oos.close(); System.out.println("직렬화 완료"); }catch (IOException e){ e.printStackTrace(); } } }
public class SerialEx2 { public static void main(String[] args){ try{ String filename ="UserInfo.ser"; FileInputStream fis = new FileInputStream(filename); BufferedInputStream bis = new BufferedInputStream(fis); ObjectInputStream ois = new ObjectInputStream(bis); //객체 읽는 순서는 출력순서와 동일 UserInfo u1 = (UserInfo) ois.readObject(); UserInfo u2 = (UserInfo) ois.readObject(); ArrayList<UserInfo> list = (ArrayList<UserInfo>) ois.readObject(); System.out.println(u1); System.out.println(u2); System.out.println(list); ois.close(); }catch (IOException e){ e.printStackTrace(); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } } }
+추가 상속 직렬화 역직렬화
//UserInfo에서 직렬화 인터페이스 제거
public class UserInfo { String name; String pass; int age; public UserInfo(){ this("Unknown","1111",0); } public UserInfo(String name ,String pass ,int age){ this.name = name; this.pass = pass; this.age = age; } @Override public String toString(){ return name+":"+pass+":"+age; } }
public class UserInfo2 extends UserInfo implements Serializable { String address; public UserInfo2(){ this("Unknown","1111",0,"NAN"); } public UserInfo2(String name, String pass ,int age,String address){ super(name,pass,age); this.address = address; } private void writeObject(ObjectOutputStream out) throws IOException { out.writeUTF(name); out.writeUTF(pass); out.writeInt(age); out.defaultWriteObject(); //자신에게 정의된 address 직렬화 } private void readObject(ObjectInputStream in) throws IOException,ClassNotFoundException { name = in.readUTF(); pass = in.readUTF(); age = in.readInt(); in.defaultReadObject(); //자신에게 정의된 address 역직렬화 } }
- 직렬화되지 않은 조상으로 부터 멤버를 받았을 때 이에 대한 직렬화 처리를 구현한 것이다.
'언어 > JAVA' 카테고리의 다른 글
Servlet-JSP MVC07 파일 수정하기 (0) 2023.07.24 Java - File클래스 (0) 2023.07.24 nextStep - 자동차 경주 일급 컬렉션 만들기 (0) 2023.07.23 nextStep - 모든 원시값과 문자열 포장 (0) 2023.07.23 nextstep-자동차경주 (테스트하기 어려운 코드 테스트하기) (0) 2023.07.23