본문 바로가기

CS Study

8월 2주차 - 개인 질문

 

 


개인 질문

1. 싱글톤, 싱글톤 패턴, 싱글톤 컨테이너 차이 설명

2. 값에 의한 전달, 참조에 의한 전달

3. 참조형(Reference Type)과 기본형(Primitive Type)의 차이

 


싱글톤, 싱글톤 패턴, 싱글톤 컨테이너 차이 설명

요약

  • 싱글톤: 하나의 인스턴스만 존재해야 하는 객체의 개념.
  • 싱글톤 패턴: 하나의 인스턴스만 생성하고 관리하는 디자인 패턴의 구현 방법.
  • 싱글톤 컨테이너: 프레임워크에서 객체의 생명주기를 싱글톤으로 관리하는 컨테이너(예: 스프링의 IoC 컨테이너).

이 세 가지 개념은 모두 싱글톤 인스턴스의 생성과 관리에 관련되어 있지만, 적용되는 범위와 방식에서 차이가 있습니다.

 

1. 싱글톤 (Singleton)

개념:

  • 싱글톤은 애플리케이션 내에서 특정 클래스의 인스턴스를 하나만 생성하도록 제한하는 디자인 패턴 또는 개념입니다.
  • 특정 객체가 애플리케이션 전체에서 단 하나만 존재해야 할 때 사용됩니다.

 


2. 싱글톤 패턴 (Singleton Pattern)

개념:

  • 싱글톤 패턴은 싱글톤 개념을 구현하기 위한 디자인 패턴입니다.
  • 이 패턴을 사용하면 클래스가 하나의 인스턴스만을 생성하고, 그 인스턴스를 어디에서든 접근할 수 있도록 합니다.

특징:

  • 인스턴스의 유일성: 클래스가 처음으로 호출될 때 하나의 인스턴스를 생성하고, 이후에는 그 인스턴스를 반환합니다.
  • 글로벌 접근점: 애플리케이션 어디서나 인스턴스에 접근할 수 있습니다.
  • 게으른 초기화: 필요할 때까지 인스턴스를 생성하지 않음으로써 불필요한 자원 낭비를 막습니다
public class Singleton {
    private static Singleton instance;

    private Singleton() {
        // Private constructor to prevent instantiation
        // 생성자를 private로 해서 외부에서 new 생성을 못하게 막는다
    }

    public static Singleton getInstance() {
    // 그래서 무조건 getInstance로 객체를 가지고 오게한다
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

 

객체가 여러번 생성되어서 메모리 낭비

 

싱글톤 패턴 만드는 코드


3. 싱글톤 컨테이너 (Singleton Container)

개념:

  • 싱글톤 컨테이너는 주로 프레임워크에서 사용되는 개념으로, 특정 객체들을 싱글톤으로 관리하는 컨테이너를 의미합니다.
  • 스프링 프레임워크 같은 IoC 컨테이너에서 빈(Bean)으로 등록된 객체들이 싱글톤으로 관리됩니다. 즉, 컨테이너가 애플리케이션 내에서 객체의 생명주기를 관리하고, 필요한 곳에 같은 인스턴스를 제공하는 역할을 합니다.

특징:

  • DI와 싱글톤: 의존성 주입(Dependency Injection)과 함께 사용되어, 객체 간의 의존성을 쉽게 관리할 수 있습니다.
  • 중앙 집중 관리: 애플리케이션 내 여러 객체를 중앙에서 관리하고 제공하여 객체 생성과 관리를 최적화합니다.
  • 확장성: 프레임워크가 객체 생명주기를 관리하므로, 확장성과 유지보수가 용이합니다.
@Configuration
public class AppConfig {
    @Bean
    public MyService myService() {
        return new MyServiceImpl();
    }
}

// 위의 설정에서 myService() 메서드가 반환하는 객체는 싱글톤으로 관리됩니다.

값에 의한 전달, 참조에 의한 전달 

1. 값에 의한 전달

값에 의한 전달 (Pass by Value)**는 함수나 메서드에 인수를 전달할 때, 그 인수의 값을 복사하여 전달하는 방식을 의미합니다.

값에 의한 전달에서 함수나 메서드 내부에서 인수의 값을 변경해도 원본 변수에는 영향을 미치지 않습니다.

 

개념

  • 값 복사: 함수나 메서드에 인수를 전달할 때, 원본 변수의 값이 복사되어 전달됩니다. 따라서 함수나 메서드 내부에서 이 값을 변경해도 원본 변수에는 아무런 영향이 없습니다.
  • 독립성: 함수나 메서드 내부의 매개변수는 원본 변수와는 별개의 메모리 공간을 가집니다.
  • 즉, 호출자와 피호출자 간의 데이터가 완전히 독립적으로 처리됩니다.
public class PassByValueExample {
    public static void main(String[] args) {
        int num = 10;
        modifyValue(num);
        System.out.println("After modifyValue: " + num);  // 출력: 10
    }

    public static void modifyValue(int value) {
        value = 20;  // 이 변경은 main 메서드의 num에 영향을 주지 않음
    }
}

 


2. 참조에 의한 전달 (Pass by Reference)

참조에 의한 전달은 인수로 객체의 참조를 전달하며,

전달된 참조를 통해 객체의 내부 상태를 수정하면 원본 객체에도 영향을 미칩니다. (Java는 객체를 참조형으로 전달하지만,

이는 참조 값의 복사이므로 기본적으로 Java는 값에 의한 전달 방식을 사용합니다).

 

public class PassByReferenceExample {
    public static void main(String[] args) {
        int[] numbers = {1, 2, 3};
        modifyArray(numbers);
        System.out.println("After modifyArray: " + numbers[0]);  // 출력: 10
    }

    public static void modifyArray(int[] array) {
        array[0] = 10;  // 이 변경은 main 메서드의 numbers 배열에 영향을 줌
    }
}

 


값에 의한 전달, 모든 변수 유형에 적용

이는 기본형(primitive) 변수와 참조형(reference) 변수 모두에 해당합니다.

중요한 차이점은 기본형 변수참조형 변수가 메서드에 전달될 때의 동작 방식입니다.

 

 

  • 기본형 변수: 메서드에 전달될 때 값 자체가 복사되므로, 메서드 내부에서 그 값을 변경해도 원본 변수에 영향을 미치지 않습니다.
  • 참조형 변수: 메서드에 전달될 때 객체의 참조 값이 복사되므로, 메서드 내부에서 참조된 객체의 속성을 변경하면 원본 객체에도 영향을 미칩니다. 하지만, 참조 자체를 다른 객체로 변경하더라도 원본 참조 변수는 영향을 받지 않습니다.

 


기본형 변수 (Primitive Types)

기본형 변수는 int, float, double, char 등의 데이터 타입을 말합니다.

public class PassByValueExample {
    public static void main(String[] args) {
        int x = 10;
        modifyPrimitive(x);
        System.out.println("After modifyPrimitive: " + x); // 출력: 10
    }

    public static void modifyPrimitive(int value) {
        value = 20;  // value의 복사본을 변경
    }
}

 

 

참조형 변수 (Reference Types)

참조형 변수는 배열, 객체, 클래스 등의 참조를 나타냅니다.

class MyObject {
    int value;
}

public class PassByValueExample {
    public static void main(String[] args) {
        MyObject obj = new MyObject();
        obj.value = 10;

        modifyReference(obj);
        System.out.println("After modifyReference: " + obj.value); // 출력: 20
    }

    public static void modifyReference(MyObject object) {
        object.value = 20;  // 객체의 속성을 변경
    }
}

 

메서드 내부에서 참조 변수의 참조 대상(객체)을 변경하더라도, 호출한 쪽의 원본 참조 변수는 여전히 원래의 객체를 가리키고 있으며,

그 객체의 상태에는 변화가 없습니다.

class MyObject {
    int value;
}

public class PassByValueExample {
    public static void main(String[] args) {
        MyObject obj = new MyObject();
        obj.value = 10;

        modifyReference(obj);
        System.out.println("After modifyReference: " + obj.value); // 출력: 10
    }

    public static void modifyReference(MyObject object) {
        // 메서드에 전달된 참조를 새로운 객체로 변경
        object = new MyObject();
        object.value = 20;  // 이 변경은 main 메서드의 obj에 영향을 주지 않음
    }
}

 

객체를 메서드에 전달하는 경우

class MyObject {
    int value;
}

public class ReferenceExample {
    public static void main(String[] args) {
        MyObject obj = new MyObject();
        obj.value = 10;

        modifyObject(obj);// 객체를 전달!!!!!!!!!!!!!!!!!!
        System.out.println("After modifyObject: " + obj.value);  // 출력: 20
    }

    public static void modifyObject(MyObject object) {
        object.value = 20;  // 객체의 속성을 변경
    }
}

 

배열을 메서드에 전달하는 경우

public class ReferenceExample {
    public static void main(String[] args) {
        int[] numbers = {1, 2, 3};

        modifyArray(numbers);// 배열 전달!!!!!!!!!!!!!
        System.out.println("After modifyArray: " + numbers[0]);  // 출력: 10
    }

    public static void modifyArray(int[] array) {
        array[0] = 10;  // 배열의 첫 번째 요소를 변경
    }
}

 

참조를 통한 객체 상태 변경과 참조 자체 변경의 차이

class MyObject {
    int value;
}

public class ReferenceExample {
    public static void main(String[] args) {
        MyObject obj1 = new MyObject();
        MyObject obj2 = new MyObject();
        obj1.value = 10;

        changeReference(obj1, obj2);// 객체2개 !!!!!!!!!!!!!
        System.out.println("After changeReference: " + obj1.value);  // 출력: 10
         System.out.println("After changeReference: " + obj2.value);  // 출력: 20
    }

    public static void changeReference(MyObject objA, MyObject objB) {
        objA = objB;  // objA의 참조가 objB로 변경됨, 하지만 main의 obj1에는 영향 없음
        objA.value = 20;  // objB의 value를 변경 (참조된 객체가 바뀌었기 때문)
    }
}

 

 

객체의 참조를 다른 객체로 변경하려고 시도하는 경우

class MyObject {
    int value;
}

public class ReferenceExample {
    public static void main(String[] args) {
        MyObject obj = new MyObject();
        obj.value = 10;

        replaceObject(obj);// 객체 전달!!!!!!!!!!!!!!!!!!
        System.out.println("After replaceObject: " + obj.value);  // 출력: 10
    }

    public static void replaceObject(MyObject object) {
        object = new MyObject();  // 참조가 다른 객체로 변경됨 생성!!!!!!!!!!
        object.value = 20;
    }
}

 


3. 참조형(Reference Type)과 기본형(Primitive Type)의 차이

  • 기본형(Primitive Types):
    • Java에서 기본형 데이터 타입에는 int, char, float, double, boolean, byte, short, long이 있습니다.
    • 이들은 값 자체를 저장
    • 값에 대한 참조를 사용하지 않습니다.
  • 참조형(Reference Types):
    • String, 배열, 클래스, 인터페이스 등은 참조형 데이터 타입입니다.
    • 이들은 객체에 대한 참조(메모리 주소)를 저장
    • 실제 값은 해당 참조가 가리키는 객체에 저장됩니다.

String은 primative 처럼 생겼는데 왜 reference 타입이일까?

String의 특징

  • 불변성(Immutability):
    • String 객체는 한 번 생성되면 변경할 수 없습니다.
    • String 객체의 내용을 수정하려면 새로운 String 객체가 생성됩니다.
  • 참조형(Reference Type):
    • String은 객체이며, 변수는 이 객체의 참조를 저장합니다.
    • String 변수를 다른 변수에 할당하거나 메서드에 전달할 때, 실제 문자열 데이터가 아니라 그 문자열을 가리키는 참조 값이 복사됩니다.
public class Main {
    public static void main(String[] args) {
        String str1 = "Hello";
        String str2 = str1;

        str1 = "World";// str1 변경함

        System.out.println("str1: " + str1); // 출력: World
        System.out.println("str2: " + str2); // 출력: Hello
    }
}

 

 

배열도 int[] 이런식으로 선언하면 초기값이 0으로 채워지니까 primative 아닌가?

배열이 int[]처럼 기본형 타입을 담고 있다 하더라도, 배열 자체는 참조형(Reference Type) 입니다.

 

배열의 초기화와 기본형 타입

  • 기본형 타입 배열: int[], char[], boolean[] 등은 기본형 데이터를 저장하는 배열입니다.
  • 하지만 이 배열 자체는 참조형입니다.
  • 즉, int[] arr = new int[5];라고 선언하면, 배열 arr은 실제로는 기본형 데이터(int)를 저장하는 객체를 참조하고 있는 것입니다.
  • 배열 요소는 기본형 타입이지만, 배열 변수 arr은 참조형 타입입니다.
    • 초기화: 기본형 배열은 생성 시 자동으로 초기화됩니다.
      • int[]는 0으로 초기화
      • char[]는 \u0000 (null 문자)로 초기화
      • boolean[]는 false로 초기화
      • double[]는 0.0으로 초기화

참조형 타입으로서의 배열

  • 참조형 타입: 배열은 객체이기 때문에, 배열을 선언하면 그 변수에는 배열 객체의 참조(메모리 주소)가 저장됩니다.
  • 배열의 특성: 배열은 힙(Heap) 메모리에 할당되며, 배열을 가리키는 변수는 스택(Stack) 메모리에서 그 배열의 주소를 참조합니다. 이는 모든 배열이 참조형 타입으로 취급되는 이유입니다.
public class Main {
    public static void main(String[] args) {
        int[] arr = new int[5]; // 길이가 5인 int 배열 선언

        System.out.println(arr[0]); // 출력: 0, 자동으로 0으로 초기화됨
        arr[0] = 10;
        System.out.println(arr[0]); // 출력: 10

        int[] anotherArr = arr; // 참조 복사
        anotherArr[1] = 20;
        System.out.println(arr[1]); // 출력: 20, anotherArr은 arr과 같은 배열을 참조
    }
}

 

public class Main {
    public static void main(String[] args) {
        Person person = new Person(); // Person 객체 생성 reference 타입

        System.out.println("Age: " + person.age); // 출력: Age: 0
        System.out.println("Name: " + person.name); // 출력: Name: null
    }
}

class Person {
    int age;// primative 타입
    String name;// reference 타입
}

'CS Study' 카테고리의 다른 글

8월 4주차 GC  (0) 2024.08.19
8월 4주차 JDK, JRE, JVM  (0) 2024.08.19
8월 4주차 jar, war  (0) 2024.08.19
8월 3주차  (0) 2024.08.12
8월 2주차 - 공통 질문  (0) 2024.08.05