CS지식

CS 공부 자바 -2

pows1011 2023. 3. 12. 23:09

DAO DTO VO

 

더보기

DAO (Data Access Object)

  • 데이터 베이스의 Data에 접근하기 위한 객체, DataBase에 접근하기 위한 로직 & 비즈니스 로직을 분리하기위해 사용합니다.

DTO (Data Transfer Object)

  • 계층간 데이터교환을 하기 위해 사용하는 객체 자바 beans를 말합니다, DTO는 로직을 가지지 않는 순수한 데이터 객체 (Getter,Setter 만 가진 클래스)입니다.

VO (Value Object)

  • 값 오브젝트로써 값을 위해 쓰입니다. Read-Only 특징 ( 사용하는 도중 변경이 불가능하며 오직 읽기만)을 가집니다.
  • DTO와 유사하지만 DTO는 Setter를 가지고 있어 값이 변할 수 있다.

생성자

더보기

생성자는 간단하게 얘기하면 인스턴스가 생성될 때 호출되는 '인스턴스 초기화 메소드'이다.

따라서 인스턴스 변수의 초기화 작업에 사용되고, 인스턴스 생성 시에 실행되어야 할 작업을 위해서 사용된다.

synchronized키워드

더보기

synchronized 키워드는 동기화가 필요한 메소드나 코드블럭앞에 사용하여 동기화 할 수 있습니다. synchronized로 지정된 임계영역은 한 스레드가 이 영역에 접근하여 사용할때 lock이 걸림으로써 다른 스레드가 접근할 수 없게 됩니다. 이후 해당 스레드가 이 임계영역의 코드를 다 실행 후 벗어나게되면 unlock 상태가 되어 그때서야 대기하고 있던 다른 스레드가 이 임계영역에 접근하여 다시 lock을 걸고 사용할 수 있게 됩니다. 

lock은 해당 객체당 하나씩 존재하며, synchronized로 설정된 임계영역은 lock 권한을 얻은 하나의 객체만이 독점적으로 사용하게됩니다.

직렬화

더보기

자바에서 입출력에 사용되는 것은 스트림이라는 데이터 통로를 통해 이동합니다. 하지만 객체는 바이트형이 아니기 때문에 스트림을 통해서 저장하거나 네트워크로 전송하는 것이 불가능합니다. 따라서 객체를 스트림으로 입출력하기 위해서 바이트 배열로 변환하는 것을 직렬화라고 합니다.

반대로 스트림으로 받은 직렬화된 객체를 다시 원래로 돌리는 건 역직렬화라고 말합니다.

리플렉션

더보기

리플렉션은 구체적인 클래스 타입을 알지 못해도, 그 클래스의 메소드, 타입, 변수들에 접근할 수 있도록 해주는 자바 API

리플렉션은 컴파일러를 무시한 채 런타임 상황에서 메모리에 올라간 클래스나 메서드 등의 정의를 동적으로 찾아 조작할 수 있는 행위를 말합니다.

자바는 정적인 언어라 부족한 부분이 많은데 이 동적인 문제를 해결하기 위해서 리플렉션을 사용합니다.

리플렉션은 애플리케이션 개발에서보다는 프레임워크, 라이브러리에서 많이 사용됩니다.
프레임워크, 라이브러리는 사용하는 사람이 어떤 클래스를 만들지 모릅니다. 이럴 때 동적으로 해결해주기 위해서 리플렉션을 사용합니다.
대표적인 사용 예로는 스프링의 DI(dpendency injection), Proxy, ModelMapper 등이 있습니다.

제네릭 , 와일드 카드

더보기

클래스를 선언할 때 타입을 결정하지 않고 객체 생성 시 유동적으로 재사용하기 위한 것을 말합니다.

1) 재사용성 증가 

제네릭 타입은 여러 타입의 파라미터를 삽입해 객체를 생성할 수 있기 때문에 코드를 간결하게 하고 재사용성을 높입니다.  

동일한 기능을 하는 메서드에서 파라미터 타입만 다르게 사용할 경우, 제네릭 타입이 유용하게 쓰일 수 있습니다. 

2) 컴파일 시 타입 에러 발견 가능

제네릭 타입의 경우 컴파일시 잘못 사용되는 타입 문제점을 제거하기 위해 강하게 타입 체크를 수행합니다. 

이덕분에 컴파일 이후 런타임 단계에서 타입 문제가 발생될 가능성을 방지해줍니다. 

3) 컴파일러가 타입 변환 수행 ( 불필요한 형변환을 줄여줌 )

컴파일 단계에서 컴파일러가 타입 캐스팅을 수행해주기 때문에 불필요하게 코드에서 타입 캐스팅을 해줄 필요가 없습니다. 

 

 

와일드 카드

제네릭과 와일드카드에 대해 이해하기 위해서 우리는 먼저 공변과 불공변에 대해 알아야 한다. 공변과 불공변은 각각 다음과 같다.

  • 공변(covariant) : A가 B의 하위 타입일 때, T <A> 가 T<B>의 하위 타입이면 T는 공변
  • 불공변(invariant) : A가 B의 하위 타입일 때, T <A> 가 T<B>의 하위 타입이 아니면 T는 불공변

대표적으로 배열은 공변이며, 제네릭은 불공변인데 이를 코드로 살펴보도록 하자. 예를 들어 배열의 요소들을 출력하는 메소드가 있다고 하자. 이때 우리가 변수의 선언 타입은 Integer로, 메소드의 선언 타입은 Object로 해두었다고 하자.

@Test
void genericTest() {
    Integer[] integers = new Integer[]{1, 2, 3};
    printArray(integers);
}

void printArray(Object[] arr) {
    for (Object e : arr) {
        System.out.println(e);
    }
}

위의 메소드는 정상적으로 실행이 된다. 왜냐하면 배열은 공변이기 때문에 Integer가 Object의 하위 타입이므로 Integer[] 역시 Object[]의 하위 타입이기 때문이다. 하지만 제네릭은 불공변이라서 제네릭을 사용하는 컬렉션을 보면 다음의 코드는 컴파일 에러가 발생한다.

@Test
void genericTest() {
    List<Integer> list = Arrays.asList(1, 2, 3);
    printCollection(list);   // 컴파일 에러 발생
}


void printCollection(Collection<Object> c) {
    for (Object e : c) {
        System.out.println(e);
    }
}

Integer는 Object의 하위 타입이다. 하지만 제네릭은 불공변이므로 List<Integer>는 List<Object>의 하위타입이 아니다. 둘은 아무런 관계가 없다. 그래서 위의 코드를 실행하면 다음과 같은 컴파일 에러가 발생한다. 이러한 제네릭의 불공변 때문에 와일드카드(제네릭의 ?타입)가 등장할 수 밖에 없는데, 제네릭부터 와일드카드까지 살펴보도록 하자

제네릭 타입은 상하관계가 없다

반면에 제네릭의 타입 파라미터(꺾쇠 괄호) 끼리는 타입이 아무리 상속 관계에 놓인다 한들 캐스팅이 불가능하다. 왜냐하면 제네릭은 무공변 이기 때문이다. 제네릭은 전달받은 딱 그 타입으로만 서로 캐스팅이 가능하다.

.

 

 

모든 타입을 대신할 수 있는 와일드카드 타입(<?>)을 추가하였다. 와일드카드는 정해지지 않은 unknown type이기 때문에 Collection<?>로 선언함으로써 모든 타입에 대해 호출이 가능해졌다. 그래서 제네릭의 활용성을 높일 수 있게 되었는데, 여기서 중요한 것은 와일드카드가 any type이 아닌 unknown type이라는 점이다.

즉, 꺾쇠 괄호 부분을 제외한 원시 타입(Raw Type) 부분은 공변성이 적용되지만, 꺾쇠 괄호 안의 실제 타입 매개변수에 대해서는 적용이 되지 않는다고 정리할 수 있는 것이다.

 

  • 와일드카드는 물음표 ?로 표시하며 Java에서 unknown type이다. 와일드칻그는 매개변수, 필드 또는 지역 변수의 유형 때론 반환 유형으로 다양한 상황에서 사용할 수 있습니다.

 

 

 

상수와 리터럴

더보기

상수(constant)는 변수와 마찬가지로 '값을 저장할 수 있는 공간' 이지만 변수와 달리 한 번 값을 저장하면 다른 값으로 변경할 수 없다는 특징이 있습니다.

사용 방법은 변수의 타입 앞에 'final' 키워드를 붙여서 사용합니다.

리터럴은 데이터(값) 그 자체를 뜻한다. 즉, 변수에 넣는 변하지 않는 데이터를 의미하는 것.

 

상수는 메모리 위치(공간)이며, 메모리 값을 변경할 수 없다.
리터럴은 메모리 위치(공간) 안에 저장되는 값이다.

 

String , StringBuilder , StringBuffer , String이 불변인 이유

더보기

String 과 StringBuilder StringBuffer의 가장 큰 차이점은 String은 불변성을 가지고 있다는 것.

String은 기존 값에 변경을 줄 경우 원래 저장되어 있는 메모리의 값을 변경하는게 아닌 새로운 메모리영역을 만들어 그 영역안에 값을 넣어 준다. 그리고 전에 가지고 있던 메모리영역은 GC가 제거 해준다.

( 문자열의 추가, 수정 ,삭제등이 빈번한 경우 힙메모리에 많은 임시 가비지가 생성되어 어플리케이션 성능에 저하가 올수 도 있다 .)

 

StringBuffer는 동기화(synchronized)키워드를 가지고 있어 멀티 쓰레드 환경에서 안전하다(Thread_Safe)

StringBuilder는 동기화를 지원하지 않아 멀티쓰레드환경에서는 사용하는 것이 적합하지 않지만 .단일 쓰레드에서의 성능은 StringBuffer보다 뛰어나다.

 

 

String                :  문자열 연산이 적고 멀티쓰레드 환경일 경우

StringBuffer     :  문자열 연산이 많고 멀티쓰레드 환경일 경우
StringBuilder   :  문자열 연산이 많고 단일쓰레드이거나 동기화를 고려하지 않아도 되는 경우 

 

String이 불변인 이유

  • 캐싱 기능에 의한 메모리 절약과 속도 향상
    • Java에서 String 객체들은 Heap의 String Constant Pool이라는 공간에 저장되는데, 참조하려는 문자열이 String Pool에 존재하는 경우 새로 생성하지 않고 Pool에 있는 객체를 사용하기 때문에 특정 문자열 값을 재사용하는 빈도가 높을 수록 상당한 성능 향상을 기대 할 수 있다.
  • thread-safe
    • String 객체는 불현이기 때문에 여러 쓰레드에서 동시에 특정 String 객체를 참조해도 안전하다.
  • 보안 기능
    • 중요한 데이터를 문자열로 다루는 경우 강제로 해당 참조에 대한 문자열 값을 바꾸는 것이 불가능 하기 때문에 보안에 유리하다.

final , finally , finalize의 차이

더보기

final

클래스 ,메소드 ,변수 ,인자를 선언할 때 사용할 수 있으며, 한 번만 할당하고 싶을 때 사용한다.

  • final 변수는 한번 초기화 되면 그 이후에 변경이 불가능하다.
  • final 메소드는 다른 클래스가 이 클래스를 상속 할 때 메소드 오버라이딩을 금지합니다.
  • final 클래스는 다른 클래스에서 이 클래스를 상속 할 수 없습니다.

finally

try-catch와 함께 사용되며, try-catch가 종료될 때 finally block이 항상수행되기 때문에 마무리 해줘야 하는 작업이 존재하는 경우에 해당 코드를 작성해주는 코드 블록입니다.

 

finalize

자바의 특징 중 하나는 자동으로 참조하지 않는 배열이나 객체를 Garbage Collector를 사용해 힙 영역에서 사용되지 않는 객체를 제거시킨다. 이러한 기능을 수행하는 메서드가 바로 finalize()이다.

java.lang.Object클래스로 부터 상속받아 모든 클래스의 객체가 가지고 있는 메서드다. 

GC가 발생하는 시점이 불분명하기 때문에 해당 메서드가 실행된다는 보장이 없고,

finalize()메서드가 오버라이딩 되어 있으면 GC가 이루어 질때 바로 Garbage Collecting 되지 않습니다.

GC가 지연되면서 OOME(Out Of Memory Excepion)이 발생할 수 있기 때문에 finalize()메서드를 오버라이딩 하여 구현하는 것을 권장하지 않는다.

System.out.print()를 사용하면 안되는 이유

더보기
  1. 로그가 표준 출력으로 출력된다. 즉 파일로 저장되지 않고 한번 출력한뒤 사라진다 ( 휘발성 )
  2. 인자로 전달한 문자열만 출력한다. 문제가 발생한 날짜, 시간 그리고 문제의 수준 ,발생한 위치 등 최소한의 정보가 기록되지 않는다
  3. 로그 출력 레벨을 사용 할 수없다.
    • 로컬에서 개발 할 때에는 디버깅을 위한 아주 상세한 정보가 출력되어야 하고, 프로덕션에서 동작하는 코드는 에러/장애가 발생할 때 문제를 진달 할 수있는 정보만 남겨야 하는데 , Sys는 그에대한 출력 단계를 조절할 수 없다.
  4. 성능 저하의 원인이 될수도 있다.

Sys의 구현을 살펴보면 newLine이라는 메서드를 호출하고 있는데 newLine의 내부에는 Synchronized 즉 쓰레드의 동기화를 지원하고 있다. 그말은 즉 이녀석은 하나의 스레드가 사용중일 때 다른 스레드에서는 접근이 불가하다는말이다. 그렇게 되면 스레드의 오버헤드가 발생 할 수 있기 때문에 성능이 저하 될 수 가 있다.

call by value , call by reference

더보기

Call by value(값에 의한 호출)

  • 함수 호출시 전달되는 변수의 값을 복사하여 함수의 인자로 전달한다.
  • 복사된 인자는 함수 안에서 지역적으로 사용하는 변수이다.
  • JAVA의 경우 함수에 전달되는 인자의 데이터타입에 따라서 (기본자료형/참조자료형) 함수 호출 방식이 다름
    • 기본 : call by value로 동작 (int,shor,long,float,double,char,boolean , WrapperClass (auto unboxing때문) )
    • 참조 : call by reference로 동작 (Array , class , instance ,interface)
public class CallbyValue { 
	public static void run(int x, int y) { 
    	x = 30; y = 40; 
    } 
    
    public static void main(String [] args) { 
    	int a = 10, b = 20; 
        System.out.println("a = " + a + ", b = " +b); 
        
        run(a,b); 
        System.out.println("a = " + a + ", b = " +b); 
    } 
}
a = 10, b = 20 
a = 10, b = 20
a와 b를 파라미터로 전달하지만 run 메서드 내에 두 파라미터 값 x, y는 각각 a, b의 두 '값'만을 복사하여 x, y에 저장한다. a 와 b의 값을 직접적으로 바꾸지 못하고 새로운 변수 x, y를 새로 만드는 셈이다.
  • Call by Value의 '값에 의한 호출' 의미는 파라미터로 전달받은 변수의 값을 받고 그 값을 복사해 새로 변수를 만들어 저장한다는 의미이다.

Call by reference (참조에 의한 호출)

  • 함수가 호출될 때, 메모리 공간 안에서는 함수를 위한 별도의 임시 공간이 생성된다. (함수 종료시 사라짐)
  • call by reference 참조에 의한 호출방식은 함수 호출시 인자로 전달되는 변수의 레퍼런스를 전달한다. (해당 변수를 가르킨다.) 
  • 함수 안에서 인자의 값이 변경되면, 함수 호출시에 있던 변수들도 값이 바뀐다. 
  • JAVA에서 Call by reference는 해당 객체의 주소값을 직접 넘기는 게 아닌 객체를 보는 또 다른 주소값을 만들어서 넘기다는 사실을 꼭 기억하자. 

 

 

결론적으로 말하자면 자바는 Call by Value 로 작동한다.
클래스, 배열은 Call by Reference처럼 작동하는걸로 보여질 뿐이다.
나머지 Primitive type과 String type의 변수들은 모두 Call by Value 로 작동한다 .

Java는 Call by Pointer라고도 불리기도 한다.

CheckedException , UnCheckedException

더보기
  • CheckedException은 실행하기 전에 예측 가능한 예외를 말하고, 반드시 예외 처리를 해야합니다.
    • ex) IOException , ClassNotFoundException 등
  • UnCheckedException은 실행하고 난 후에 알 수 있는 예외를 말하고, 따로 예외처리를 하지 않아도 된다.
    • ex ) NullPointerException , ArrayIndexOutOfBoundException 등
  • RuntimeException은 UnCheckedException을 상속한 클래스이고,RuntimeException이 아닌것은 CheckedExcpetion을 상속한 클래스 입니다.

 

Error와 Exception의 차이

더보기
  • Error는 실행 중 일어날 수있는 치명적인 오류를 말합니다.
  • 컴파일 시점에서 체크 할 수 없고, 오류가 발생하면 프로그램은 비정상 종료되며 예측 불가능한 UncheckedExcpetion에 속합니다.

 

  • 반면 Excpetion은 Error보다 비교적 경미한 오류이며, try-catch 예외처리로 비정상 종료를 막을 수 있습니다.