728x90

컬렉션이란 배열처럼 동일한 타입을 묶어서 관리하는 자료구조입니다. 저장 공간의 크기 (capacity)를 동적으로 관리합니다.

프레임워크란 클래스를 정의하거나 설계할 때 어떤 원칙이나 구조를 가지고 클래스와 인터페이스를 설계한 것을 프레임워크라고합니다. ( 클래스와 인터페이스의 모임)

컬렉션 프레임워크란❓

컬렉션 프레임워크(Collection Framework)리스트, 스택, 큐, 트리 등의 자료구조에 정렬 , 탐색 등의 알고리즘을 구조화 해놓은 프레임워크이자 java에서 데이터 그룹을 다루기 위한 클래스들의 집합입니다.

프레임워크는 데이터 그룹을 저장하고 조작하기 위한 표준화된 방법을 제공하여 개발자들이 데이터 구조를 효율적으로 관리할 수 있도록 돕습니다. 이러한 컬렉션 프레임워크는 자바의 인터페이스(interface)를 사용하여 구현됩니다.

컬렉션 프레임워크는 다양한 인터페이스와 클래스로 구성되어 있으며, 각각의 클래스는 특정한 데이터 구조와 동작 방식을 가지고 있습니다. 주요한 인터페이스와 클래스로는 다음과 같은 것들이 있습니다

컬렉션 프레임워크는 데이터 그룹을 다루기 위한 다양한 기능들을 제공합니다. 데이터의 추가, 삭제, 검색, 정렬 등을 쉽게 처리할 수 있으며, 각각의 클래스는 내부적으로 데이터 구조를 효율적으로 관리하도록 구현되어 있습니다. 이를 통해 개발자는 데이터 구조에 대한 세부적인 관리 없이 간편하게 데이터를 다룰 수 있습니다.

1. List<E>

순서가 있는 데이터 그룹을 저장하는 인터페이스입니다. 컬렉션은 제네릭타입으로 선언되고 동일한 타입을 저장할 때, 제네릭 타입으로 선언된 데이터들만 초기화가 가능합니다. 중복된 요소를 허용하며, 대표적인 클래스로는 ArrayList, LinkedList, Vector 등이 있습니다.

배열의 경우, 초기화되면 저장크기가 고정이 되어 이 후 각 인덱스에 저장된 값들을 제거하거나 추가하더라도 인덱스의 총 길이는 동적으로 바뀌지가 않습니다. LIST의 경우, 동적 컬렉션을 생성하여 데이터의 추가 삭제가 가능합니다. 그에 따른 저장공간의 길이도 자동으로 늘어나고 줄게 됩니다.

💡 List <E> 컬렉션의 객체 생성 방법

 

List <E> interface의 구현 클래스 생성자로 동적 컬렉션 생성 (데이터의 추가 삭제 가능)

  • 기본 생성자의 경우 원소 개를 저장할 수 있는 저장공간(capacity)를 확보합니다. 필요한 경우에 추가한다면 JVM이 동적으로 저장공간의 크기를 자동적으로 추가합니다. ex) List<Integer> aList1 = new ArrayList<Integer>(); → capacity(10)
  • 생성자 매개변수로 저장공간의 크기를 직접 넘겨줄 수 있습니다. (단, LinkedList<E>는 제외, capacity 지정 불가능) ex) List<Integer> aList2 = new ArrayList<Integer>(20) → capacity(20)

Arrays.asList( T(개수가 지정되지 않은 매개변수) ..) 메서드를 이용하여 정적 컬렉션 생성

  • 데이터의 추가(add) 삭제(remove) 불가능하며, 데이터의 변경(set)은 가능합니다. → 고정된 데이터를 저장하고 활용하고자 할 때 사용
    ex) List<제네릭타입지정> asList1 = Arrays.asList(제네릭타입데이터들);
    List<Integer> aList1 = Arrays.asList(1,2,3,4);
    내부적으로는 1,2,3,4가 담겨져 있는 Integer배열이 만들어졌고, 해당 배열을 List로 감싼 객체가 생성됩니다.
    여기서 내부적으로는 데이터가 고정적인 배열이 생성된 것이라 add() , remove()는 사용이 불가하고,
    데이터의 수는 변하지 않고 값만 변하게 하는 set() 메서드만 사용가능합니다.
    → 배열처럼 수집(collect)한 원소(Element)를 인덱스(index)로 관리 List<E>의 구현 클래스는 아래의 모든 메서드를 포함합니다.



https://velog.io/@mmy789/Java-컬렉션-프레임워크-1

 

  • toArray()메서드 : toArray() 메서드는 ArrayList의 요소들을 배열로 변환하는 메서드입니다. 이 메서드를 호출하면 ArrayList의 요소들이 배열로 복사되어 반환됩니다. toArray() 메서드에는 두 가지 형태가 있습니다
    1. 매개변수가 없는 toArray():
      • 이 형태의 toArray() 메서드는 Object 배열로 요소들을 복사하여 반환합니다.
      • 반환된 배열의 타입은 Object 배열이므로, 원하는 타입으로 형변환을 해주어야 합니다.
    2. 매개변수가 있는 toArray(T[] a):
      • 이 형태의 toArray() 메서드는 지정한 타입의 배열 **a**를 매개변수로 받아, 해당 배열로 요소들을 복사하여 반환합니다.
      • 만약 매개변수로 전달된 배열의 크기가 ArrayList의 크기보다 작을 경우, 새로운 배열이 생성되어 요소들이 복사됩니다.
      • 만약 매개변수로 전달된 배열의 크기가 ArrayList의 크기보다 크거나 같을 경우, ArrayList의 요소들이 매개변수로 전달된 배열에 복사되고, 남는 공간은 null로 채워집니다.
            Object[] object = aList3.toArray();
            System.out.println(Arrays.toString(object));
    
            Integer[] integer1 = aList3.toArray(new Integer[0]);
            System.out.println(Arrays.toString(integer1));  //출력 : [1,2,3]
            -> list인 aList3의 size()보다 작성된 선언공간이 크면 자동적으로 list의 size 크기를 가지는 배열이 생성
    
            Integer[] integer2 = aList3.toArray(new Integer[5]);
            System.out.println(Arrays.toString(integer2)); //출력 [1,2,3,null,null]
            -> list인 aList3의 size()가 작성된 선언공간 new Integer[5]보다 작은 경우 데이터가 채워진
               이 후의 공간에는 null값이 담기게 된다.


대표적인 List<E> 컬렉션의 클래스

  1. ArrayList<E> : ArrayList<E>는 제네릭을 사용하여 요소를 저장하는 동적 배열 클래스입니다. <E>는 제네릭 타입 매개변수로, ArrayList가 저장할 요소의 타입을 지정하는데 사용됩니다.
    ArrayList를 사용하려면 import java.util.ArrayList; 문을 통해 java.util 패키지에서 해당 클래스를 임포트한 후, 다음과 같이 ArrayList 객체를 생성하고 요소를 추가하고 접근할 수 있습니다

            import java.util.ArrayList;
    
            public class Main {
                public static void main(String[] args) {
                    ArrayList<String> list = new ArrayList<String>();
    
                    // 요소 추가
                    list.add("사과");
                    list.add("바나나");
                    list.add("딸기");
    
                    // 요소 접근
                    String fruit = list.get(1);
                    System.out.println(fruit); // 출력: 바나나
    
                    // 요소 삭제
                    list.remove(0);
                    System.out.println(list); // 출력: [바나나, 딸기]
                }
            }
    
            위의 예제에서 <String>은 ArrayList가 문자열 요소를 저장한다는 것을 명시하고 있습니다. 
            따라서 ArrayList에는 문자열만 저장할 수 있습니다. 만약 다른 타입의 요소를 저장하려면 
            <E> 자리에 해당 타입을 지정하면 됩니다.
  2. Vector<E> : Vector<E>는 Java의 컬렉션 프레임워크에서 제공하는 클래스로, 동적으로 크기가 조정되는 배열 형태의 자료구조입니다. ArrayList와 아주 유사하게 동일한 타입의 객체수집 (collection)` , 메모리의 동적할당, 데이터의 추가, 변경, 삭제 등의 메서드를 사용할 수 있는 공통적 기능을 제공하지만, 큰 차이점으론 Vector는 스레드 동기화(synchronized)가 되어 있어 멀티스레드 환경에서 안전하게 사용할 수 있습니다.

  3. LinkedList<E> : LinkedList<E>는 연결 리스트(linked list) 데이터 구조를 기반으로한 리스트(List) 구현체입니다. 앞뒤 요소(Element)로 정보의 데이터 위치를 관리합니다. LinkedList는 요소들을 연결한 노드(Node)로 구성되며, 각 노드는 데이터 요소와 다음 노드를 가리키는 참조(주소)로 이루어져 있습니다.

    각 데이터들의 왼쪽 오른쪽 데이터들의 위치를 알고 있습니다. 이러한 구조로 인해 요소들의 추가, 삭제, 삽입이 상대적으로 빠르게 이루어질 수 있지만, 검색(get)은 속도가 더 늦게 진행됩니다. (순서를 세어서 해당 토드를 찾아야 하기때문에)

  4. ArrayList<E>와 유사하지만 1) 디폴트 저장공간(10)만 사용하며 2) 생성자로 저장공간의 크기 지정이 불가한 점, 3) 데이터의 내부 저장방식이 index가 아닌 앞 뒤 객체의 위치정보를 저장한다는 차이점이 있습니다. 

        ArrayList<E>와 LinkedList<E> 가장 큰 차이점은 내부적인 데이터 구조입니다.

  1. 내부 데이터 구조:
    • ArrayList: 배열(Array)을 기반으로한 동적 배열 구조입니다. 요소들은 인덱스에 따라 순차적으로 저장됩니다. 인덱스로 직접적인 접근이 가능하며, 요소의 삽입과 삭제가 느리지만 요소의 조회가 빠릅니다.
    • LinkedList: 연결 리스트(Linked List)를 기반으로한 노드(Node)들이 연결된 구조입니다. 각 요소는 이전 노드와 다음 노드의 참조(주소)로 연결되어 있습니다. 요소의 삽입과 삭제가 빠르지만 인덱스로 직접적인 접근이 불가능하며, 요소의 조회가 느립니다.
  2. 데이터 접근 시간:
    • ArrayList: 인덱스로 요소에 직접 접근할 수 있으므로 요소의 조회가 빠릅니다. 삽입과 삭제가 배열의 크기 변동에 따라 발생하므로 비용이 크고, 요소를 중간에 삽입 또는 삭제하는 경우에는 뒤따라오는 요소들을 한 칸씩 이동해야 합니다.
    • LinkedList: 요소를 순차적으로 따라가야 하므로 인덱스로 직접 접근하는 것보다 접근 시간이 더 오래 걸립니다. 하지만 요소의 삽입과 삭제는 노드의 연결 관계만 변경하면 되므로 비교적 빠릅니다.
  3. 메모리 사용:
    • ArrayList: 내부적으로 배열을 사용하기 때문에 요소들을 연속적인 메모리 공간에 저장합니다. 따라서 공간 효율성이 높습니다.
    • LinkedList: 노드들이 연결되어 있기 때문에 각 요소마다 추가적인 노드 객체와 참조(주소)를 저장해야 하므로 메모리 사용량이 더 큽니다.

두 클래스는 사용 목적과 상황에 따라 선택되어야 합니다. ArrayList는 요소의 조회가 많고, 중간에 삽입/삭제가 적은 경우에 유리하며, LinkedList는 삽입/삭제가 빈번하고 순차적인 접근이 주로 발생하는 경우에 유리합니다.

 

 

728x90
반응형
728x90

 

✅ 제네릭 클래스 vs 제네릭 메서드의 차이점

  1. 범위: 제네릭 메서드는 특정 메서드 내에서만 제네릭 타입을 사용하고, 해당 메서드의 매개변수 타입이나 반환 타입을 제네릭으로 정의합니다. 반면에 제네릭 클래스는 클래스 전체에서 제네릭 타입을 사용하고, 클래스의 필드, 메서드, 생성자 등에서 제네릭 타입을 사용할 수 있습니다.

  2. 사용 방법: 제네릭 메서드는 메서드 선언 시에 타입 매개변수를 선언하고, 해당 메서드 내에서만 해당 타입을 사용합니다. 제네릭 클래스는 클래스 선언 시에 타입 매개변수를 선언하고, 클래스 전체에서 해당 타입을 사용할 수 있습니다.

  3. 타입 유연성: 제네릭 메서드는 각 메서드 호출 시에 사용하는 타입을 동적으로 지정할 수 있으므로, 호출할 때마다 다른 타입을 사용할 수 있습니다. 제네릭 클래스는 클래스 인스턴스를 생성할 때 타입을 지정하고, 해당 인스턴스의 타입은 고정됩니다.

  4. 코드 재사용성: 제네릭 메서드는 특정 메서드 내에서만 사용되므로 해당 메서드가 호출되는 범위 내에서만 재사용이 가능합니다. 제네릭 클래스는 여러 메서드에서 공통으로 사용되는 제네릭 타입을 정의하므로, 클래스 전체에서 재사용이 가능합니다.
    • 와일드 카드 : 불특정한 타입을 나타내는 데에 사용되며, 주로 제네릭 타입의 매개변수나 변수를 선언할 때 사용됩니다. 제네릭 클래스를 생성할 때, 참조변수에 지정된 제네릭 타입과 생성자에 지정돤 제네릭 타입은 일치해야 합니다. 만약 일치하지 않으면 컴파일 에러가 발생합니다.
      • 이때 제네릭 타입으로 ‘와일드 카드’를 사용하면 extends와 super로 상한과 하한을 제한할 수 있습니다.
      1. ? - 물음표 와일드카드(Unbounded Wildcard): 어떤 타입도 가능한 경우를 의미합니다. 예를 들어, **List<?>**는 어떤 종류의 요소든지 포함할 수 있는 리스트를 나타냅니다.
      2. ? extends 타입 - 상한 와일드카드(Upper Bounded Wildcard): 특정 타입 또는 그 타입을 상속받은 타입들만 가능한 경우를 의미합니다. . 예를 들어, List<? extends Number>는 Number 타입 또는 Number를 상속받은 타입들만인Integer, Double, Float 등을 받을 수 있는 리스트를 나타냅니다.
      3. ? super 타입 - 하한 와일드카드(Lower Bounded Wildcard) : 특정 타입의 상위 타입을 나타냅니다. 예를 들어, **List<? super Integer>**는 Integer 클래스와 그 상위 타입인 Number, Object 등을 받을 수 있는 리스트를 나타냅니다. 이렇게 사용되면 해당 타입과 그 상위 타입들에 대해서만 동작할 수 있습니다.

 

🎈 제네릭 타입 범위 제한

제네릭 타입 범위 제한(Generics Type Bounds)제네릭 클래스나 메서드에서 허용되는 타입의 범위를 제한하는 기능입니다. 이를 통해 제네릭 타입을 사용할 때 특정한 종류의 타입만을 허용하도록 제한할 수 있습니다. 이는 코드의 안정성과 타입 안정성을 높이는 데 도움이 됩니다.

  1. 타입 안정성 보장 : 특정한 종류의 타입만을 허용하므로, 잘못된 타입이 사용되는 것을 방지할 수 있습니다. 이로 인해 컴파일 시점에서 타입 체크를 수행하여 런타임 에러를 방지할 수 있습니다.

  2. 코드 재사용성 향상 : 특정한 종류의 타입으로 제한하면 해당 타입에 대한 특정한 동작 또는 기능을 구현할 수 있는 제네릭 클래스나 메서드를 작성할 수 있습니다. 이를 통해 코드의 재사용성과 유지보수성이 향상됩니다.

 

🎈 제네릭 타입 범위 제한 종류

  1. 제네릭 클래스의 타입 제한
    • 상위 경계 (Upper Bound)를 사용하여 제한할 수 있습니다. 상위 경계 (Upper Bound) : extends 키워드를 사용하여 상위 클래스나 인터페이스를 제한합니다. 상위 경계를 설정하면 해당 클래스 또는 인터페이스와 그 자손 클래스들만이 타입 인자로 사용될 수 있습니다.
    • 타입 제한 기본 문법 구조 접근지정자 class 클래스명< T extends 상위클래스명> { / 타입 T를 사용한 코드 / } → 클래스 / 인터페이스 상관없이 항상 extends 사용 (인터페이스로 작성되었음에도 implements를 사용하지 않습니다.)
  2. 제네릭 메서드의 타입 제한
    • 제네릭 메서드의 타입 제한은 메서드에 전달되는 타입 인자의 범위를 제한하는 방법입니다.
      이를 통해 특정한 타입 또는 그 타입의 상위 또는 하위 타입만을 인자로 받을 수 있도록 제한할 수 있습니다.
    • 접근지정자<T extends 상위클래스명>T 메서드이름 (T t) {/ 부모 클래스의 메서드 사용 가능 /}
      (인터페이스로 작성되었음에도 implements를 사용하지 않습니다.)
  3. 일반 메서드인데 매개변수로서 제네릭을 사용할 때의 제네릭 클래스 타입제한
    • 메서드의 매개변수로 제네릭 클래스 객체가 오는 경우의 타입제한

 

✅ 제네릭 타입 범위 제한 예시 코드

 

        1. 특정 클래스를 상속받은 타입 제한
        T는 SomeClass 클래스 또는 그의 하위 클래스만을 인자로 받을 수 있습니다.
        public <T extends SomeClass> void methodName(T arg) {
            // 메서드 내용
        }

        2. 인터페이스를 구현한 타입 제한
        T는 SomeInterface 인터페이스를 구현한 타입만을 인자로 받을 수 있습니다.
        public <T implements SomeInterface> void methodName(T arg) {
            // 메서드 내용
        }

        3. 여러 개의 타입 제한을 조합
        T는 SomeClass 클래스를 상속받고 동시에 SomeInterface 인터페이스를 구현한 타입만을 인자로 받을 수 있습니다.
        public <T extends SomeClass & SomeInterface> void methodName(T arg) {
            // 메서드 내용
        }

 

하위 경계 (Lower Bound) : super 키워드를 사용하여 하위 클래스를 제한합니다. 하위 경계를 설정하면 해당 클래스와 그 상위 클래스들만이 타입 인자로 사용될 수 있습니다.

제한된 타입 파라미터 (Multiple Bounds) : 하나의 타입 파라미터에 대해 여러 개의 상위 경계를 지정할 수 있습니다. extends 키워드를 사용하여 여러 클래스나 인터페이스를 상위 경계로 지정할 수 있습니다.

 

🎈 제네릭(Genric)의 상속

  • 부모클래스가 제네릭인 경우 자식클래스 또한 제네릭 (부모의 제네릭 변수를 그대로 물려받음)
  • 하위 클래스에서 타입 매개변수에 대한 추가적인 제한이나 확장도 가능합니다. 예를 들어, 상위 클래스의 타입 매개변수를 상속받은 타입 매개변수로 제한하거나, 추가적인 타입 매개변수를 정의할 수도 있습니다.
728x90
반응형
728x90

 

제네릭(Generic)은 Java에서 도입된 기능으로 컬렉션 클래스나 메서드의 매개변수, 반환 타입 등을 일반화하는 방법을 제공하는 기능입니다. 제네릭은 클래스, 인터페이스, 메서드에서 사용할 수 있습니다. 클래스나 인터페이스에 제네릭을 적용하면 해당 클래스나 인터페이스의 필드, 메서드, 생성자에서 사용되는 타입을 일반화할 수 있습니다. 메서드에 제네릭을 적용하면 해당 메서드에서 사용되는 타입을 일반화할 수 있습니다.
제네릭은 자바에서 데이터 타입을 파라미터와(Parameterize) 할 수 있는 방법을 제공하는 프로그래밍 기법입니다.그 자체로 타입은 아니지만, 클래스나 인터페이스, 메서드를 정의할 때 사용하는 타입 매개변수를 가리킵니다.

 

https://www.youtube.com/@doit4312/playlists

 

제네릭은 Java 5부터 도입되었으며, 컴파일 시점에서 타입 체크가 이루어지기 때문에 타입 안정성을 보장하고 형변환을 줄일 수 있습니다. 또한, 제네릭을 사용하면 코드의 가독성도 높아지고 오류를 사전에 방지할 수 있습니다.
예로 들면, 제네릭을 사용하면 컬렉션에 저장되는 객체의 타입을 컴파일 시점에 체크할 수 있고, 타입 안정성을 보장받을 수 있습니다. 이를 통해 잘못된 타입의 객체를 저장하거나 꺼낼 때 발생하는 런타임 오류를 사전에 방지할 수 있습니다.

 

https://www.youtube.com/@doit4312/playlists

 

 

🎈 Generic 클래스 / 인터페이스 정의 문법 구조

  • 제네릭을 사용하기 위해서는 타입 매개변수를 선언해야 합니다. 타입 매개변수는 일반적으로 대문자로 표기하며, 클래스나 메서드에 선언됩니다. 제네릭 타입을 사용할 때에는 실제 타입으로 대체하여 사용하게 됩니다. 제네릭 클래스는 객체를 만들 때 타입이 정해집니다.

  • 일반적으로 <T>와 같이 꺽쇠(<>) 안에 대문자로 표현된 타입 매개변수를 사용합니다. 이후에 해당 타입 매개변수를 클래스의 필드, 메서드의 매개변수, 반환 타입 등에 사용하여 일반적인 코드를 작성할 수 있습니다. 실제 사용할 때는 타입 매개변수에 구체적인 타입을 지정하여 사용합니다. < > 꺽쇠 안에 작성되는 건 T, K , V 같은 변수이름을 작성하는데, 변수 이름이라 사용자가 마음대로 작성한 후 기입해도 되지만 일반적으로 영 대문자 하나를 사용합니다.


    ex) 제네릭 타입 변수명이 하나인 경우 
             접근지정자 class(또는 interface) 클래스명 <T> {
                       //타입 T를 사용한 코드
           }

     ▶   public class MyClass<T> { 
           //여기서의 T는 아직 정해지지 않았고, 이 후 어떤 타입을 작성하냐에 따라 달라집니다.
                  private T t;
                  public T get() {
                  return t;
              }
                  public void set (T t) {
                      this.t = t;
              }
      }



    ex) 제네릭 타입 변수명이 두개인 경우    
             접근지정자 class(또는 interface) 클래스명 <K, V> {
                       //타입 K, V를 사용한 코드
           }

     ▶   public interface MyInterface<K, V> { 
           //이 후, K와 V의 타입은 객체를 생성하면서 정해주려고 제네릭으로 선언해두었습니다.
                 public abstract void setKey( K k);
                 public abstract void setValue( V v);
                 public abstract void getKey( );
                 public abstract void setKey( );

           }

 

 

예를 들어, ArrayList 클래스는 제네릭을 사용하여 여러 종류의 객체를 저장할 수 있습니다. ArrayList<Integer>는 정수형 객체만을 저장할 수 있는 리스트를 의미하고, ArrayList<String>은 문자열 객체만을 저장할 수 있는 리스트를 의미합니다. 이렇게 제네릭을 사용하면 타입에 따라 다양한 종류의 리스트를 생성할 수 있습니다.

 

 

🎈 Generic 클래스 / 인터페이스 객체 생성 문법 구조

  • 객체 생성** 객체 생성 시 제네릭 타입을 지정하지 않으면 올 수 있는 Type 중 최상위 클래스(Object)로 인식 MyClass mc = new MyClass(); == MyClass<Object< mc = new MyClass<>(); 동일한 의미!
  • 클래스명<실제 제네릭 타입> 참조변수명 = new 클래스명<실제 제네릭 타입>(); or 클래스명 <실제 제네릭 타입 > 참조변수명 = new 클래스명<>(); → 생성자의 경우 내부의 타입 생략 가능

 

//T를 매개변수로 하는 제네릭 클래스 생성
class MyClass<T> {
	private T t;
	public T get() {
		return t;
	}
	public void set(T t) {
		this.t = t;
	}
}

public class Ex10_SingleGenericBasic {

	public static void main(String[] args) {

		MyClass	<String> mc1 = new MyClass<String>();
		mc1.set("안녕하세요");
		System.out.println(mc1.get());
		
		
		
		MyClass <Integer> mc2 = new MyClass<>(); //= 뒤의 Integer는 생략가능
		mc2.set(8);
		System.out.println("제 나이는 " + mc2.get() + "살입니다.");
		
		MyClass<Integer> mc3 = new MyClass<>();
		//mc3.set("안녕"); //강한 타입체크 Integer로 정해진 타입에 String 타입을 넣어서 바로 에러가 발생
	}

}

 

💡 제네릭의 기본 개념은 클래스 내에 사용되는 타입을 클래스가 정의될 때가 아닌 객체를 생성할 때 정의하겠다는 의미

 

🎈 Generic(제네릭) method(메서드)

 

제네릭 메서드는 일반 클래스안에 선언된 메서드 선언 시에 타입 매개변수를 사용하여 다양한 타입의 매개변수를 처리할 수 있도록 하는 기능입니다. 일반클래스 안에 만들어지기 때문에 클래스를 만드는 시점에서는 <T>를 정할 수 없고 메서드를 호출할 때, 제네릭 타입을 정할 수 있습니다. 단, 메서드의 매개변수 타입의 제네릭 변수가 사용된다면 반환 타입이나 매개변수의 타입에 일반적인 타입 대신 타입 매개변수를 사용하여 타입의 작성의 생략이 가능하는 등의 유연성을 제공합니다

  • 제네릭 메서드는 메서드 내에서 사용되는 타입 매개변수를 선언하여 메서드의 매개변수, 반환 값, 지역 변수 등에서 사용할 수 있도록 합니다.
  • 일반적으로 메서드의 시그니처에 <T>와 같은 형태로 타입 매개변수를 선언하며, 메서드 내에서 해당 타입을 사용할 수 있습니다.
  • 제네릭 메서드는 호출할 때 매개변수의 타입을 명시적으로 지정하거나, 컴파일러가 타입을 추론하여 암묵적으로 결정할 수 있습니다.

 

  • Generic 메서드 문법 예시
        public <T> void printArray(T[] array) {
            for (T element : array) {
                System.out.println(element);
            }
        }

        메서드의 타입 매개변수로 T를 사용하고 있습니다. 
        이제 T는 메서드 내부에서 사용할 수 있는 타입으로 간주됩니다.

        printArray 메서드는 배열을 받아서 배열의 모든 요소를 출력하는 역할을 합니다. 
        이때 배열의 타입은 제네릭 타입 T로 지정되어 있으므로, 어떤 타입의 배열이든지 처리할 수 있습니다. 
        예를 들어 Integer[], String[], Double[] 등을 인자로 전달할 수 있습니다.

        Integer[] intArray = { 1, 2, 3, 4, 5 };
        printArray(intArray); // Integer 배열을 출력

        String[] strArray = { "Hello", "World" };
        printArray(strArray); // String 배열을 출력
728x90
반응형
728x90

 

Spring Bean (스프링 빈)이란❓


Spring Bean은 Spring Framework에서 관리되는 객체를 의미합니다. 의존성 주입(Dependency Injection, DI)객체 간의 의존 관계를 개발자가 직접 관리하지 않고, 외부에서 의존하는 객체를 주입하여 사용하는 디자인 패턴입니다.
DI는 객체 간의 결합도를 낮추고 유연하고 재사용 가능한 코드를 작성할 수 있도록 도와줍니다.
Spring Framework은 의존성 주입(Dependency Injection)을 통해 객체들을 생성, 관리, 연결하는데, 이러한 객체들을 “Spring Bean”이라고 합니다.

Spring Bean은 일반적으로 자바 클래스로 표현되며, Spring 컨테이너(ApplicationContext)에 의해 생성되고 관리됩니다. Spring Bean은 개발자가 정의한 설정 파일(XML 또는 Java Config)에 기반하여 컨테이너에 등록됩니다. 컨테이너는 Spring Bean의 생명 주기를 관리합니다.

 

Spring Bean의 주요 특징:

  1. 스프링 컨테이너에 등록: Spring Bean은 스프링 컨테이너에 등록되어야 합니다. 등록된 Bean은 컨테이너에서 관리되고 필요한 곳에서 주입(Dependency Injection)되어 사용됩니다.

  2. 라이프사이클 관리: Spring 컨테이너는 Spring Bean의 라이프사이클을 관리합니다. Bean의 생성, 초기화, 소멸 등을 컨테이너가 담당합니다.

  3. 의존성 주입: Spring Bean은 의존성 주입(Dependency Injection)을 통해 다른 Bean과 연결됩니다. 이를 통해 객체 간의 결합도를 낮출 수 있고, 유연하고 재사용 가능한 애플리케이션을 만들 수 있습니다.

  4. 스코프: Spring Bean은 스코프에 따라 다르게 관리됩니다. 주요 스코프로는 Singleton, Prototype, Request, Session 등이 있으며, 각 스코프에 따라 Bean의 생성과 소멸 시점이 달라집니다.

  5. 어노테이션과 XML 설정: Spring Bean은 어노테이션을 사용하여 자동으로 등록할 수도 있고, XML 설정 파일을 통해 수동으로 등록할 수도 있습니다. 주로 XML 설정 방식은 과거에 사용되었으며, 현재는 주로 어노테이션을 사용하는 방식이 일반적입니다.

 

일반적으로 객체 간의 의존 관계는 하나의 객체가 다른 객체를 생성하거나 참조하여 사용하는 것으로 나타납니다. 이러한 의존 관계는 코드 내에서 강한 결합도를 만들어내고, 객체의 생성과 관리를 담당하는 클래스가 다른 객체들에 대한 정보를 갖고 있어야 한다는 문제를 발생시킵니다. 이로 인해 코드의 유연성과 재사용성이 저하되는 문제가 발생할 수 있습니다.

DI는 이러한 문제를 해결하기 위해 의존하는 객체를 외부에서 주입하는 방식을 채택합니다. 주입된 객체는 해당 객체가 필요한 시점에 사용됩니다. 주로 생성자(Constructor) 주입, Setter 주입, 인터페이스 주입 등의 방식을 사용합니다.

 

        package hello.hellospring.controller;


        import hello.hellospring.service.MemberService;
        import org.springframework.beans.factory.annotation.Autowired;
        import org.springframework.stereotype.Controller;

        //스프링이 처음에 뜰 때, 스프링 컨테이너라는 박스가 생기는데 만약 그곳에 컨트롤러 애노테이션이 있으면 
	//MemberController객체를 생성해서 담아준 후 스프링 빈이 관리해줍니다.
        @Controller
        public class MemberController {

            1. DI(Dipendency Injection) 필드 주입 방식
            @Autowired private MemberService memberService();


            2.DI(Dipendency Injection) setter 방식
            private MemberService memberService;

            @Autowired
            public void setMemberService(MemberService memberService) {
                this.memberService = memberService;
            }


            3.DI(Dipendency Injection) 생성자 주입 방식

            private final MemberService memberService;

            @Autowired
            public MemberController (MemberService memberService) {
                this.memberService = memberService;
            }
        }

 

DI의 주요 이점은 다음과 같습니다:

  1. 결합도 감소: 객체 간의 의존 관계를 외부에서 관리하기 때문에 객체들 간의 결합도가 낮아집니다. 이로 인해 하나의 객체 수정이 다른 객체에 미치는 영향을 최소화하고, 코드의 유지 보수성이 향상됩니다.

  2. 재사용성과 확장성: DI를 통해 의존하는 객체를 외부에서 주입받기 때문에 해당 객체의 변경 없이 쉽게 다른 객체와 조합하여 재사용하거나 기능을 확장할 수 있습니다.

  3. 테스트 용이성: 의존 객체를 모의(Mock) 객체로 대체하여 단위 테스트를 수행할 수 있습니다. 의존 객체의 동작을 외부에서 주입하여 테스트를 진행할 수 있기 때문에 테스트 용이성이 높아집니다.

Spring Framework는 DI를 통해 객체 간의 의존 관계를 관리하고 주입하는 기능을 제공합니다.
이를 통해 Spring에서는 객체 지향적인 애플리케이션을 개발할 수 있으며, 유연하고 확장 가능한 코드를 작성할 수 있습니다.

728x90
반응형
728x90

 

쓰레드의 생성방법은 크게 2가지가 있습니다. 어느 쪽을 선택해도 별 차이는 없지만 Thread 클래스를 상속받으면
다른 클래스를 상속받을 수 없기때문에, Runnable 인터페이스를 구현하는 방법이 일반적입니다.

Thread 클래스 상속 : java에서는 java.lang.Thread 클래스를 상속하여 쓰레드를 생성할 수 있습니다. Thread 클래스를 상속받은 사용자 정의 클래스를 만들고, run() 메서드를 오버라이딩하여 쓰레드가 실행할 작업을 정의합니다. 사용자 정의 클래스의 인스턴스를 생성한 후, statr( ) 메서드를 호출하여 쓰레드를 시작합니다.

 

     
     	//Thread class를 상속받아 run() 메서드 overrid한 클래스 정의( 또는 익명클래스)
        class MyThread extends Thread {
                @override 
            public void run() {
                // 쓰레드가 실행할 작업 정의
            }
        }

        // 쓰레드 객체 생성 및 실행
        MyThread thread = new MyThread(); 
        //또는 Thread thread = new Thread(); 
        //-> 이렇게 객체를 선언해도 어차피 Thread를 상속받은 상황이라 가능하다. 
        thread.start(); //statr()메서드를 이용하여 Thread 실행!

 

💡 Run() 메서드와 Start() 메서드

 

  1. run() 메서드 : run() 메서드는 Runnable 인터페이스나 Thread 클래스에서 구현해야 하는 메서드입니다. 쓰레드가 실행될 때 호출되는 메서드로, 쓰레드가 수행할 작업을 정의합니다. run() 메서드 내부에 작업의 로직을 구현하고, 이 메서드가 모두 실행되면 쓰레드의 실행이 종료됩니다. run() 메서드는 직접 호출하여 실행하는 것이 아니라, 쓰레드가 start() 메서드를 통해 실행될 때 내부적으로 호출됩니다.

  2. start() 메서드 : start() 메서드는 Thread 클래스에서 제공하는 메서드로, 쓰레드를 시작하는 역할을 합니다. start() 메서드를 호출하면, 쓰레드는 자신의 run() 메서드를 실행하기 위해 준비되고 , 내부적으로 새로운 실행 흐름을 만들고, 쓰레드가 독립적으로 실행될 수 있도록 준비를 합니다. start() 메서드를 호출한 후에는 별도의 작업이나 제어 없이 쓰레드가 자동으로 실행됩니다. start() 메서드는 한 번만 호출할 수 있습니다. 동일한 쓰레드에 대해 start() 메서드를 다시 호출하면 예외가 발생합니다.

 

Runnable 인터페이스 구현 : java에서는 java.lang.Runnable 인터페이스를 구현하여 쓰레드를 생성할 수 있습니다. Runnable 인터페이스를 구현한 사용자 정의 클래스를 만들고, 내부적으로 Runnable 객체의 run() 메서드를 구하여 쓰레드가 실행할 작업을 정의합니다.
사용자 정의 클래스의 인스턴스를 생성한 후 , Thread 클래스의 생성자에 Runnable 인터페이스를 구현한 객체를 전달합니다. Thread 클래스의 인스턴스의 start( ) 메서드를 호출하여 쓰레드를 시작합니다.

        class MyRunnable implements Runnable { 
        //Runnable interface를 구현한 클래스를 정의함
        //Runnable = Run()의 기능을 정의한 인터페이스(객체)
                @override
            public void run() {
                // 쓰레드가 실행할 작업 정의
            }
        }

        //Runnable  객체 생성 -> Threade 객체 생성 (생성자에 Runnable 객체를 전달)
        MyRunnable runnable = new MyRunnable();
        Thread thread = new Thread(runnable);

        //start() 메서드를 이용하여 Thread를 실행
        thread.start();

🚫 재정의한 메서드는 run() 이지만 Thread의 실행은 start() 메서드를 호출!

🚫 Thread 객체는 재사용 할 수 없습니다. ( → 하나의 객체는 한번만 start()가 가능합니다.)

🎈 익명 이너 클래스를 쓰레드 생성에 사용하기


익명 이너 클래스(Anonymous Inner Class)는 이름이 없는 내부 클래스로, 클래스를 정의하면서 동시에 인스턴스를 생성하는 방법입니다.
익명 이너 클래스를 사용하면 스레드에서 직접적으로 클래스를 정의하고 인스턴스를 생성할 수 있으므로, 코드의 간결성과 가독성을 높일 수 있습니다.
주로 간단한 작업이나 임시적인 스레드 생성에 활용됩니다.

        
        Thread thread = new Thread() {
            public void run() {
                // 스레드에서 실행할 코드 작성
            }
        };


        Runnable runnable = new Runnable() { 
        //인터페이스의 객체도 익명이너클래스를 사용하게되면 하위클래스를 직접 만들지 않아도 바로 생성이 가능함
        //new Runnable() { }의 중괄호 안에 미완성인 method run() 메서드를 overriding 하여 전부 기입하여 넘겨주었습니다.  
          public void run() {
                // 스레드에서 실행할 코드 작성
            }
        };

        Thread thread = new Thread(runnable);

 

🎈 Thread의 속성 ( Thread 객체 가져오기 / 이름 / Thread 개수 / 우선순위 / 데몬설정)


currentThread() 메서드 : Thread 클래스의 currentThread() 정적 static 메서드는 현재 실행 중인 쓰레드를 반환하는 정적 메서드입니다. 이 메서드를 호출하면 현재 실행 중인 쓰레드의 참조를 얻을 수 있습니다. 보통 currentThread() 메서드는 다중 쓰레드 환경에서 현재 실행 중인 쓰레드의 정보를 얻기 위해 사용됩니다. 이를 통해 각 쓰레드의 동작을 구분하거나, 특정 작업을 현재 쓰레드에서만 수행할 수 있도록 조건을 검사하는 등의 용도로 활용될 수 있습니다.
static Thread Thread.currentThread() → Thread 참조 객체가 없는 경우에 사용

 

        public class CurrentThreadExample {
            public static void main(String[] args) {
                Thread currentThread = Thread.currentThread(); // 현재 실행 중인 쓰레드 얻기
                System.out.println("현재 실행 중인 쓰레드: " + currentThread.getName());
            }
        }
728x90
반응형
728x90

 

🎈 비즈니스 요구사항 정리


단순한 비즈니스 로직에 대해서 진행해보기 데이터 : 회원 ID, 이름 기능 : 회원 등록, 조회 → 아직 데이터 저장소가 선정되지 않음 (가상의 시나리오) :아직 db가 선정되지 않은 상황에서 개발을 진행해야 하는 상황입니다.

  • 일반적인 웹 애플리케이션의 계층구조
    • 컨트롤러 : Web MVC의 컨트롤러 역할
    • 서비스 : 핵심 비즈니스 로직 구현
    • 리포지토리 : 데이터베이스에 접근, 도메인 객체를 DB에 저장하고 관리
    • 도메인 : 비즈니스 도메인 객체, 예) 회원, 주문, 쿠폰 등 주로 데이터 베이스에 저장하고 관리됨

 

💡 웹 개발에 필요한 기본방법

✅ 1. 정적 컨텐츠 (static content)


스프링(Spring) 프레임워크에서 자동으로 제공되는 정적 컨텐츠(static content)는 웹 애플리케이션에서 동적으로 생성되지 않고 고정된 형태로 제공되는 리소스를 말합니다.
이는 주로 HTML, CSS, JavaScript, 이미지 파일 등과 같은 클라이언트 측에서 사용되는 파일들을 의미합니다.

정적 컨텐츠는 웹 애플리케이션의 리소스로 사용되며, 웹 브라우저에 직접 제공됩니다. 일반적으로 정적 컨텐츠는 웹 애플리케이션의 클래스패스 내에 위치하며,
클라이언트 요청에 따라 해당 리소스가 서버로부터 전달되어 브라우저에 표시됩니다.

스프링에서 정적 컨텐츠를 제공하기 위해 ResourceHttpRequestHandler와 같은 핸들러를 사용합니다.
이 핸들러는 요청된 URI와 일치하는 정적 리소스를 찾아서 응답으로 전송합니다.
스프링은 클래스패스, 파일 시스템, 웹 JAR 파일 등 다양한 위치에서 정적 리소스를 찾을 수 있도록 지원합니다.

정적 컨텐츠는 스프링 MVC에서 웹 애플리케이션의 리소스를 제공하는 중요한 요소 중 하나이며, 클라이언트에게 웹 페이지의 디자인, 스타일, 스크립트 등을 제공하는 데 사용됩니다.

 

 

 

🎈 2. MVC(Model-View-Controller) 소프트웨어 디자인 패턴 & 템플릿 엔진


MVC(Model-View-Controller)는 소프트웨어 디자인 패턴으로, 애플리케이션의 구성 요소를 3가지 모델(Model), 뷰(View), 컨트롤러(Controller)로 분리하여 개발하는 방법론입니다.
각 구성 요소는 특정한 역할과 책임을 가지며, 애플리케이션의 유지 보수성, 재사용성, 확장성을 높이는 데 도움이 됩니다.

  1. 모델(Model): 데이터와 비즈니스 로직을 담당합니다. 데이터베이스에서 데이터를 가져오거나 수정하고, 데이터의 유효성을 검증하며, 비즈니스 로직을 수행합니다.
    모델은 도메인 객체, 데이터 액세스 객체, 서비스 객체 등으로 구성될 수 있습니다.

  2. 뷰(View): 사용자 인터페이스를 담당합니다. 모델의 데이터를 사용자에게 보여주고, 사용자 입력을 받아 컨트롤러에 전달합니다.
    뷰는 주로 HTML, CSS, JavaScript 등을 사용하여 웹 애플리케이션의 UI를 구성합니다. 사용자가 보는 화면을 담당하는 역할을 수행합니다.

  3. 컨트롤러(Controller): 모델과 뷰를 연결하고, 사용자의 요청을 처리하는 역할을 합니다. 사용자의 요청을 받아 해당 요청에 대한 작업을 수행하고,
    그 결과를 모델에 전달하여 업데이트하거나, 뷰에 전달하여 사용자에게 응답합니다. 컨트롤러는 사용자의 입력을 처리하고, 모델과 뷰 간의 상호작용을 조정하는 중개자 역할을 합니다.

템플릿 엔진을 MVC 방식으로 쪼개서 뷰를 템플릿 엔진으로 html을 더 프로그래밍 후 렌더링이 된 html을 클라이언트에게 전달해주는 것이 템플릿 엔진 방식이라고 합니다.

  • 템플릿 엔진(Template Engine)은 동적인 컨텐츠를 생성하는 데 사용되는 도구입니다. 템플릿 엔진은 템플릿 문서와 데이터를 결합하여 최종 결과물을 생성합니다. 주로 웹 애플리케이션에서 사용되며, HTML, XML, JSON 등 다양한 형식의 템플릿을 처리할 수 있습니다.
  • 템플릿 엔진은 템플릿 문서에 동적인 데이터를 삽입하고, 조건문, 반복문 등을 사용하여 동적인 로직을 처리할 수 있습니다. 예를 들어, 사용자가 요청한 데이터를 템플릿 엔진에 전달하면 엔진은 템플릿 문서 내에서 해당 데이터를 사용하여 최종 결과물을 생성하고 클라이언트에 전송합니다.
  • 스프링 프레임워크에서는 여러 가지 템플릿 엔진을 지원합니다. 대표적으로 Thymeleaf, Freemarker, Velocity, JSP 등이 있습니다. 이러한 템플릿 엔진을 사용하여 MVC 패턴에서 뷰를 생성하고 데이터를 바인딩하여 동적인 웹 페이지를 구성할 수 있습니다.
728x90
반응형

+ Recent posts