디자인 모델 - 단일 모델 [실현, 직렬 화, 반사]
[toc]
1. 실현
단일 모델 의 실현 은 여러 가지 가 있 고 분류 방식 도 다양 하 다. 예 를 들 어 예비 로드 와 게 으 른 로드, 그리고 스 레 드 안전 의 실현 과 스 레 드 안전 하지 않 은 실현 으로 나 뉜 다.
1.1. 스 레 드 가 안전 하지 않 음
1.1.1 굶 주 림 식
호출 시 인 스 턴 스 가 초기 화 되 었 는 지, 없 으 면 초기 화 되 고 할당 되 었 는 지 판단 합 니 다.장점:
적용: 확실 하지 않 으 면 다 중 스 레 드 로 호출 되 지 않 습 니 다. 그렇지 않 으 면 사용 을 권장 하지 않 습 니 다.
public static PlainNotSafe getInstance() {
if (instance == null) {
instance = new PlainNotSafe();
}
return instance;
}
1.2. 스 레 드 안전
1.2.1 배 부 른 한식
클래스 초기 화 과정 에서 인 스 턴 스 생 성:
장점:
class LoadAhead implements Singleton{
private static LoadAhead instance = new LoadAhead();
private LoadAhead(){}
public static LoadAhead getInstance(){
return instance;
}
}
1.2.2 단일 동기 잠 금
syncronized 키 워드 를 사용 하여 getInstance 방법 을 수식 합 니 다.
public static synchronized LazyLoadWithOneSynchronization getInstance(){
if (instance == null) {
instance = new LazyLoadWithOneSynchronization();
}
return instance;
}
장점:
적용: 어떤 필드 에 도 적용 되 지 않 음
1.2.3 이중 검사 + 동기 잠 금
인 스 턴 스 생 성 과정 이 한 번 만 동기 화 되 어야 한 다 는 점 을 감안 하여 다음 과 같은 동기 화 는 필요 하지 않 습 니 다. 따라서 인 스 턴 스 가 생 성 되 지 않 았 을 때 만 동기 화 됩 니 다.
public static LazyLoadWithDoubleCheckSynchronization getInstance(){
if (instance == null) {
synchronized (LazyLoadWithDoubleCheckSynchronization.class) {
if (instance == null) {
instance = new LazyLoadWithDoubleCheckSynchronization();
}
}
}
return instance;
}
장점:
단점:
1.2.4 내부 클래스
내부 클래스 를 통 해 유일한 인 스 턴 스 를 가지 고 있 으 며, 클래스 로드 체 제 를 통 해 게 으 른 로드 와 스 레 드 안전 을 확보 합 니 다.
/**
* @Author: kkyeer
* @Description: 3, , ,
* @Date:Created in 14:57 2019/6/24
* @Modified By:
*/
class LazyLoadWithInnerClass implements Singleton{
private LazyLoadWithInnerClass(){}
private static class Inner{
static LazyLoadWithInnerClass instance = new LazyLoadWithInnerClass();
}
public static LazyLoadWithInnerClass getInstance(){
return Inner.instance;
}
}
장점:
1.2.5 매 거
매 거 진 실현 사례 를 통 해 이 방식 을 사용 하 는 것 을 추천 합 니 다. 여러 차원 에서 안전 을 보장 할 수 있 습 니 다.
enum LazyLoadWithEnum implements Singleton{
INSTANCE;
Singleton getInstance(){
return INSTANCE;
}
}
2. 기타 생 성 대상 방식 이 단일 사례 에 대한 유일한 파괴
단일 모드 의 핵심 은 설 정 된 컨 텍스트 에서 지정 한 인 스 턴 스 는 하나 입 니 다. 이 컨 텍스트 는 수요 에 따라 JVM, 같은 SpringContext 등 을 말 할 수 있 습 니 다. 그러나 우 리 는 모두 배 웠 습 니 다. 대상 을 만 드 는 데 4 가지 방식 이 있 습 니 다.
new Object()
objectInputStream.readObject()
Object.class.getDeclaredConstructor().newInstance()
obj.clone()
2.1 clone 방법 은 단일 대상 을 따로 만 들 고 단일 사례 의 유일 성 을 파괴 합 니 다.
clone 방법 은 Object 의 방법 으로 이론 적 으로 모든 대상 을 계승 합 니 다. 그러나 이 방법 은 proctected 방법 이 고 반드시 명시 적 인 implement Cloneable 인터페이스 가 필요 합 니 다. 다시 말 하면 본 류 (또는 부모 류) 의 명시 적 으로 clone 방법 을 실현 하고 이 를 Public 권한 제한 으로 확대 해 야 하기 때문에 clone 방법 은 단일 모델 의 유일 성 을 파괴 할 수 있 습 니 다.그러나 더 많은 것 은 단일 클래스 를 정의 할 때 override clone 방법 을 정의 할 때 발생 하 는 오류 이기 때문에 토론 하지 않 습 니 다.
2.2 대상 의 반 직렬 화 파괴 사례 의 유일 성
비 Enum 의 단일 사례 실현 에 있어 대상 의 반 직렬 화 는 단일 모델 의 유일 성 을 파괴 할 수 있다.
private static void testSerialization(){
Singleton created = LazyLoadWithInnerClass.getInstance();
System.out.println(created.hashCode());
File testFile = new File("obj.txt");
try {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(testFile));
objectOutputStream.writeObject(created);
objectOutputStream.close();
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(testFile));
Singleton dematerializedObject = (Singleton) objectInputStream.readObject();
objectInputStream.close();
System.out.println(dematerializedObject.hashCode());
Assertions.assertTrue(dematerializedObject == created," ");
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
} finally {
testFile.delete();
}
}
실행 결과:
1995265320
1880587981
Exception in thread "main" java.lang.AssertionError:
at utils.Assertions.assertTrue(Assertions.java:27)
at design.pattern.singleton.TestCase.testSerialization(TestCase.java:116)
at design.pattern.singleton.TestCase.main(TestCase.java:25)
2.2.1 소스 코드 분석
Object InputStream. readObject 방법 내부 에 서 는 역 직렬 화 대상 의 유형 을 판단 합 니 다. 일반 대상 (비 String, Class, * Object StreamClass, array, or enum constant) 에 대해 서 는 다음 방법 으로 역 직렬 화 합 니 다.
private Object readOrdinaryObject(boolean unshared)
throws IOException
{
//
// ↓↓↓↓↓↓↓↓↓ ↓↓↓↓↓↓↓↓↓↓
obj = desc.isInstantiable() ? desc.newInstance() : null;
//
// ↓↓↓↓↓↓↓↓↓ readResolve ↓↓↓↓↓↓↓↓↓↓
if (obj != null &&
handles.lookupException(passHandle) == null &&
desc.hasReadResolveMethod())
{
Object rep = desc.invokeReadResolve(obj);
//
if (rep != obj) {
//
handles.setObject(passHandle, obj = rep);
}
}
return obj;
}
프로 세 스:
Object newInstance()
throws InstantiationException, InvocationTargetException,
UnsupportedOperationException
{
//
return cons.newInstance();
//
}
변수 cons private Constructor> cons;
는 안전 검사 부분 을 무시 하고 실제 반 사 를 통 해 새로운 인 스 턴 스 대상 을 만 듭 니 다. 이 새로 만 든 대상 을 최종 결과 로 하면 단일 사례 의 유일 성 private Object readResolve(){
return Inner.instance;
}
상기 반 직렬 화 된 소스 코드 분석 과 유사 합 니 다. class 대상 의 new Instance 방법 이나 Constructor 대상 을 가 져 와 인 스 턴 스 를 만 들 때 새로운 인 스 턴 스 를 다시 생 성하 여 단일 사례 의 유일 성 을 파괴 합 니 다. 물론 구조 기 에서 하나의 flag 변 수 를 유지 하고 여러 번 구조 할 때 이상 을 던 질 수 있 습 니 다 (어느 정도)이 문 제 를 피하 기:
private static boolean initFlag = false;
private LazyLoadWithDoubleCheckSynchronization(){
if (initFlag) {
throw new RuntimeException(" , ");
}
initFlag = true;
//
}
2.4 매 거 진 을 사용 하여 직렬 화 와 반사 과정 에서 단일 사례 에 대한 파 괴 를 피한다.
매 거 진 을 사용 하여 단일 모델 을 실현 하면 직렬 화 와 반사 과정 에서 단일 사례 에 대한 파 괴 를 방지 할 수 있다.
2.4.1 단일 사례 모델 은 직렬 화 과정 에서 단일 사례 에 대한 유일한 파 괴 를 피한다.
단일 사례 의 반 직렬 화 에 대해 흐름 분석 대상 과정 에서 다음 과 같은 방법 을 호출 합 니 다.
private Enum> readEnum(boolean unshared) throws IOException {
//
String name = readString(false);
Enum> result = null;
Class> cl = desc.forClass();
if (cl != null) {
Enum> en = Enum.valueOf((Class)cl, name);
result = en;
//
}
return result;
}
보 이 는 스 트림 은 매 거 진 name, 반 직렬 화 시 name 에 따라 Enum 의 value Of 방법 을 호출 하여 JVM 이 초기 화 된 인 스 턴 스 를 가 져 옵 니 다. 따라서 단일 모드 는 매 거 진 을 사용 하여 이 루어 집 니 다. 반 직렬 화 는 단일 사례 의 유일 성 을 파괴 하지 않도록 보장 할 수 있 습 니 다.
2.4.2 단일 모델 은 반사 파괴 사례 의 유일 성 을 피한다.
매 거 진 클래스 는 반사 호출 을 할 수 없습니다. 실제 적 으로 아래 코드 를 사용 하여 반사 적 으로 매 거 진 인 스 턴 스 를 만 드 는 것 을 고려 합 니 다.
private static void testReflection() {
try {
Singleton created = LazyLoadWithEnum.INSTANCE.getInstance();
Constructor constructor = LazyLoadWithEnum.class.getDeclaredConstructor(String.class, int.class);
constructor.setAccessible(true);
Singleton instanceWithReflection = constructor.newInstance();
System.out.println(created.hashCode());
System.out.println(instanceWithReflection.hashCode());
Assertions.assertTrue(instanceWithReflection == created," ");
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
매 거 진 Constructor 대상 획득 과 new Instance 방법 은 모두 일반 클래스 와 다 릅 니 다.
final class LazyLoadWithEnum extends Enum
implements Singleton
{
public static LazyLoadWithEnum[] values()
{
return (LazyLoadWithEnum[])$VALUES.clone();
}
public static LazyLoadWithEnum valueOf(String name)
{
return (LazyLoadWithEnum)Enum.valueOf(design/pattern/singleton/LazyLoadWithEnum, name);
}
private LazyLoadWithEnum(String s, int i)
{
super(s, i);
}
Singleton getInstance()
{
return INSTANCE;
}
public static final LazyLoadWithEnum INSTANCE;
private static final LazyLoadWithEnum $VALUES[];
static
{
INSTANCE = new LazyLoadWithEnum("INSTANCE", 0);
$VALUES = (new LazyLoadWithEnum[] {
INSTANCE
});
}
}
관찰 한 결과 이러한 정의 되 지 않 은 무 참 구조 기 는 private LazyLoadWithEnum(String s, int i)
입 니 다. 이 로 인해 구조 기 를 가 져 올 때 매개 변수 목록 을 (String, int) public T newInstance(Object ... initargs)
throws InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException
{
//
if ((clazz.getModifiers() & Modifier.ENUM) != 0)
throw new IllegalArgumentException("Cannot reflectively create enum objects");
//
}
따라서 Enum 을 사용 하여 단일 사례 를 실현 하면 반사 조 때문에 단일 사례 의 유일 성 을 파괴 하지 않 을 것 을 보증 할 수 있다.
3. 참고
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.