Java에 직렬화 가능한 인터페이스가 필요한 이유는 무엇입니까?


114

우리는 직렬화를 많이 사용하며 사용하는 모든 객체에 직렬화 가능 태그를 지정해야하는 것은 부담이됩니다. 특히 우리가 실제로 변경할 수없는 제 3 자 클래스 일 때.

질문은 : Serializable은 빈 인터페이스이고 Java는 일단 추가하면 강력한 직렬화를 제공 implements Serializable하기 때문에 모든 것을 직렬화 가능하게 만들지 않은 이유는 무엇입니까?

내가 무엇을 놓치고 있습니까?


자신의 객체를 직렬화 가능하게 만들고 싶다면? 아니면 내가 뭔가를 오해 했나요?
Joe Phillips

내 개체의 모든 필드가 직렬화 가능해야하기 때문에 여전히 NotSerializableException이 발생합니다
Yoni Roit

Pop Catalin과 Dmitry의 "이 직렬화 가능한 트릭은 10 ~ 2 년 전에 취해진 잘못된 결정일뿐입니다"에 완전히 동의합니다. 직렬화가 얼마나 위험 할 수 있는지는 중요하지 않습니다. 그리고 선언이 명시 적이기 때문에 "당신은 특별한주의를 기울여야한다는 것을 알고 있습니다"는 사실이 아닙니다. 그것을 필요로하는 모든 사람들은 먼저 "구현"을하고, 무언가 잘못되었을 경우 함축적 인 의미를 생각합니다. 특별한 경우에 적용 할 "Unserializable"인터페이스를 제공했다면 더 분명했을 것입니다.
Jack

답변:


120

직렬화에는 함정이 있습니다. 이 형식의 자동 직렬화 지원은 클래스 내부를 공용 API의 일부로 만듭니다 (이것이 javadoc 이 클래스지속 된 형식을 제공하는 이유 입니다 ).

장기 지속성을 위해 클래스는이 양식을 디코딩 할 수 있어야하며, 이는 클래스 디자인에 적용 할 수있는 변경 사항을 제한합니다. 이것은 캡슐화를 깨뜨립니다.

직렬화는 보안 문제로 이어질 수도 있습니다. 참조가있는 모든 객체를 직렬화 할 수 있으므로 클래스는 일반적으로 할 수없는 데이터에 액세스 할 수 있습니다 (결과 바이트 데이터를 구문 분석하여).

내부 클래스의 직렬화 된 형식이 제대로 정의되지 않는 것과 같은 다른 문제가 있습니다.

모든 클래스를 직렬화 가능하게 만들면 이러한 문제가 악화됩니다. 체크 아웃 효과적인 자바 Second Edition의 , 특히 항목 74 : 직렬화를 적절하게 구현합니다 .


2
이것은 분명히 더 나은 대답입니다. 선택한 대답이 이것이 아니라는 점이 실망 스럽습니다. 게시 된 사람은 "일련 화 가능한 것을 선언해야하므로 짜증이납니다"의제를 염두에두고 선택한 것 같습니다. 과거에 이미 배운 보안 및 디자인 교훈에주의를 기울이지 않고 지옥에서 벗어나기를 원하는 사람의 예입니다.
gbtimmon

@McDowell 클래스의 지속적인 형태는 무엇을 의미합니까? 나는 그 링크를 따랐지만 무슨 뜻인지 이해할 수 없습니까? 설명해 주시겠습니까?
Geek

@Geek-URL 유형의 직렬화 된 형식 (예 :)은 유형이 가져야하는 비공개 필드와 선언해야하는 순서를 정의합니다.
McDowell

@McDowell 왜 주문이 중요합니까?
Geek

12
@McDowell 안녕하세요. 저는 4 년 전이 질문의 원래 포스터이며, 의미가있는 경우 귀하의 답변을 수락 된 것으로 선택했습니다. 나는 당신의 대답이 정말로 더 좋은 것이라고 생각하고, 당시에는 그렇게보기에는 너무 미숙했을 것입니다. :) 지금은 고정
요니 Roit

32

이번에는 Java와 .Net 사람들이 모두 잘못 알고 있다고 생각합니다. 기본적으로 모든 것을 직렬화 할 수 있도록하고 대신 안전하게 직렬화 할 수없는 클래스 만 표시하면되었을 것입니다.

예를 들어 Smalltalk (70 년대에 만들어진 언어)에서는 모든 객체가 기본적으로 직렬화 가능합니다. 대부분의 객체가 직렬화하기에 안전하고 일부만 그렇지 않다는 사실을 고려할 때 Java에서 이것이 왜 그렇지 않은지 모르겠습니다.

객체를 직렬화 가능 (인터페이스 사용)으로 표시하는 것은 마술처럼 객체를 직렬화 할 수있는 것이 아닙니다. 계속해서 직렬화 가능 했습니다. 이제 시스템이 스스로 발견 할 수있는 것을 표현했기 때문입니다. 직렬화는 지금의 방식입니다.

디자이너가 내린 잘못된 결정이거나 직렬화가 나중에 고려되었거나 플랫폼이 기본적으로 모든 객체에 대해 안전하고 일관되게 직렬화를 수행 할 준비가되지 않았다고 생각합니다.


26
인스턴스가 합리적인 방식으로 직렬화되도록 클래스를 설계하고 구현할 때 특별한주의가 필요합니다. 실제로 평균 Serializable 인터페이스 : "나는, 프로그래머로서, 직렬화의 결과를 이해하고 JVM은이 직렬화 허가"
롤프 Rander

@Rolf Rander : 대부분의 경우 전혀 신경 쓸 필요가 없습니다. 클래스를 직렬화 가능으로 표시하기 만하면됩니다. 모든 객체에 대해 기본적으로 직렬화가 켜져 있었다면 모든 개발자의 사고 방식도 다를 수 있었을 것입니다. 클래스를 직렬화 할 수 있도록 만드는 것이 자연스러운 일이었을 것입니다.
Pop Catalin

예를 들어 메모리 누수가 없는지 확인하는 것과 같이 좋은 클래스 디자인 목록에 또 다른 관심사를 추가했을 것입니다. 그래도 좋은 클래스 디자인을 위해서는 클래스가 가능할 때 직렬화 할 수 있어야합니다.
Pop Catalin

3
@StaxMan, 나중에 클래스를 직렬화해야하지만 할 수 없다는 사실을 깨닫는 것은 비용이 많이들 수 있기 때문입니다. 약간의 추가 노력이 지불되는 경우 중 하나입니다. 이것은 라이브러리를 작성하고 다른 누군가가 소스 코드없이 그것을 소비 할 경우 특히 그렇습니다
Pop Catalin

2
이 답변에 전적으로 동의합니다. 많은 언어에서 모든 것이 기본적으로 직렬화 가능합니다. 리플렉션을 사용하여 프라이빗이든 아니든 모든 클래스 멤버에 액세스하기 쉽기 때문에 실제로 JVM과 동일합니다. 이 Serializable트릭은 10 ~ 2 년 전에 취해진 잘못된 결정일 뿐이며, 표준 라이브러리의 수집 및 문자열 처리의 일부 결함과 같이 순수한 Java를 다룰 때 성가심에 또 하나 추가됩니다. 다행히 Kryo가 있지만 의존성이며 먼저 찾아야합니다. 이것이 내장 직렬화가 수행되어야하는 방법입니다.
dmitry

20

모든 것이 진정으로 직렬화 가능한 것은 아닙니다. 예를 들어 네트워크 소켓 연결을 사용하십시오. 소켓 객체의 데이터 / 상태를 직렬화 할 수 있지만 활성 연결의 본질은 손실됩니다.


이것은 내 문제가 될 것이고 소켓을 직렬화하려고 할만 큼 똑똑하지 않으면 디버깅 중에 내 오류를 발견했습니다. 그러나 지금은 아무런 이유없이 Serializable을 구현하지 않는 타사 클래스로 인해 Java 직렬화를 전혀 사용할 수없는 상황에 처해 있습니다.
Yoni Roit

4
제 3 자 클래스의 키 데이터를 읽고 쓰는 방법을 알고있는 Serializable 래퍼 클래스를 작성하는 것과 같이이 경우를 처리하는 몇 가지 적절한 방법이 있습니다. 래핑 된 인스턴스를 일시적으로 만들고 writeObject 및 readObject를 재정의합니다.
Greg Case

API의 필수 객체에서 상속하고 해당 클래스를 직렬화 할 수 있습니까?
Joel Coehoorn

@Joel : 좋은 생각이지만 여전히 해킹입니다. 이 모든 것이 또 다른 절충안이 잘못되었다고 생각합니다. 귀하의 의견에 감사드립니다.
Yoni Roit

1
이것은 우리 모두가 정말 필요가 있음을 보여 드문 사례 중 하나입니다 implements NotSerializable:)
롭 그랜트

13

Java에서 Serializable의 주요 역할은 기본적으로 다른 모든 객체를 직렬화 불가능하게 만드는 것입니다. 직렬화는 특히 기본 구현에서 매우 위험한 메커니즘입니다. 따라서 C ++의 우정과 마찬가지로 직렬화 가능하게 만드는 데 약간의 비용이 들더라도 기본적으로 꺼져 있습니다.

직렬화는 구조 호환성이 보장되지 않기 때문에 제약 조건과 잠재적 인 문제를 추가합니다. 기본적으로 꺼져있는 것이 좋습니다.

나는 표준 직렬화가 내가 원하는 것을 수행하는 사소한 클래스를 거의 보지 못했다는 것을 인정해야한다. 특히 복잡한 데이터 구조의 경우. 따라서 클래스 직렬화를 적절하게 만드는 데 드는 노력은 인터페이스 추가 비용을 줄여줍니다.


9

일부 클래스, 특히 파일, 소켓, 스레드 또는 DB 연결과 같이 더 물리적 인 것을 나타내는 클래스의 경우 인스턴스를 직렬화하는 것은 전혀 의미가 없습니다. 다른 많은 경우 직렬화는 고유성 제약 조건을 파괴하거나 단순히 원하지 않는 클래스의 다른 버전의 인스턴스를 처리하도록 강제하기 때문에 문제가 될 수 있습니다.

논란의 여지가 있지만, 기본적으로 모든 것을 Serializable로 만들고 키워드 또는 마커 인터페이스를 통해 클래스를 직렬화 할 수 없게 만드는 것이 더 나았을 수도 있지만,이 옵션을 사용해야하는 사람들은 아마 그것에 대해 생각하지 않을 것입니다. 즉, Serializable을 구현해야하는 경우 Exception이 표시됩니다.


4

나는 프로그래머로서 당신의 객체가 내가 직렬화된다는 것을 알고 있는지 확인하는 것이라고 생각합니다.



1

특정 클래스의 인스턴스가 직렬화 가능하다는 것을 명시 적으로 설명해야하는 경우 언어는이를 허용해야하는지 생각하게합니다. 단순한 값 객체의 경우 직렬화는 사소하지만 더 복잡한 경우에는 실제로 생각해야합니다.

JVM의 표준 직렬화 지원에 의존하면 모든 종류의 불쾌한 버전 관리 문제에 노출됩니다.

고유성, '실제'리소스에 대한 참조, 타이머 및 기타 여러 유형의 아티팩트는 직렬화 후보가 아닙니다.



0

제 대답은 이것이 정당한 이유가 아니라는 것입니다. 여러분의 의견을 통해 이미 배웠 음을 알 수 있습니다. 다른 언어는 기꺼이 10까지 세고 트리에서 점프하지 않는 모든 것을 직렬화하려고합니다. 객체는 기본적으로 직렬화 가능해야합니다.

따라서 기본적으로해야 할 일은 타사 클래스의 모든 속성을 직접 읽는 것입니다. 또는 그것이 당신을위한 옵션이라면 : 디 컴파일하고 거기에 빌어 먹을 키워드를 넣고 다시 컴파일하십시오.


2
직렬화는 구조 호환성이 보장되지 않기 때문에 제약 조건과 잠재적 인 문제를 추가합니다. IMHO, 기본적으로 꺼져있는 것이 좋습니다.
Uri

"구조 호환성"이 무엇을 의미하는지 잘 모르겠습니다.
nes1983 09 년

0

Java에는 런타임에 따라 다르기 때문에 직렬화 할 수없는 몇 가지가 있습니다. 스트림, 스레드, 런타임 등과 같은 것 및 일부 GUI 클래스 (기본 OS에 연결됨)도 직렬화 할 수 없습니다.


0

여기에서 다른 답변의 요점에 동의하지만 실제 문제는 역 직렬화에 있습니다. 클래스 정의가 변경되면 역 직렬화가 작동하지 않을 실제 위험이 있습니다. 기존 필드를 수정하지 않는 것은 라이브러리 작성자에게 매우 중요한 약속입니다! API 호환성을 유지하는 것은 그 자체로 충분합니다.


이것은 올바르지 않습니다. Object Serialization Specification 의 Versioning of Serializable Objects 장 을 읽어야 합니다. 여기서 주장하는 내용이 사실이라면 존재하지 않을 것입니다. 클래스 정의는 이전 직렬화와 호환되지 않기 전에 다소 넓은 제한 내에서 변경 될 수 있습니다. 그리고 그것은 확실히 질문에 대한 답이 아닙니다. 진짜 이유는 보안 문제와 관련이 있습니다.
Marquis of Lorne

@EJP, 진실성을 위해 귀하의 요점을 반영하기 위해 내 답변을 편집했습니다. 정서가 있지만, 선택 해제보다는 확실히 선택해야하는 큰 약속입니다
CurtainDog

0

파일이나 다른 미디어에 유지되어야하는 클래스는 JVM이 클래스 객체를 직렬화 할 수 있도록 Serializable 인터페이스를 구현해야합니다. Object 클래스가 직렬화되지 않은 이유는 모든 JVM이 ObjectOutputStream 을 사용할 때만 클래스를 직렬화 한 후 어떤 클래스도 인터페이스를 구현할 필요가 없습니다. 즉, JVM이 직렬화 할 수 있도록 컨트롤이 여전히 내 손에 있음을 의미합니다.

Object 클래스가 기본적으로 직렬화되지 않는 이유는 클래스 버전이 주요 문제이기 때문입니다. 따라서 직렬화에 관심이있는 각 클래스는 명시 적으로 Serializable로 표시하고 버전 번호 serialVersionUID를 제공해야합니다.

경우 의 serialVersionUID는 JVM이 발생하는 이유 후 제공되지 않는 개체를 deserialzing 동안 우리가 예상치 못한 결과를 얻을, 즉 인해 InvalidClassException을 경우 의 serialVersionUID가 일치하지 않습니다. 따라서 모든 클래스는 Serializable 인터페이스 를 구현 하고 serialVersionUID 를 제공 하여 양쪽 끝에 제시된 클래스가 동일한 지 확인해야합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.