자바 핵심 개념 정리1

Song Chae Won·2023년 3월 10일
0
post-thumbnail

🌿 자바 핵심 개념 정리 1

➡️ Java에서 제공하는 원시 타입들에 무엇이 있고, 각각 몇 바이트를 차지하나요?

  • boolean: true/false 값을 저장하는 논리 자료형으로, 크기는 1byte

  • byte: -128 ~ 127까지의 정수 값을 저장하는 자료형으로, 크기는 1byte

  • short: -32768 ~ 32767까지의 정수 값을 저장하는 자료형으로, 크기는 2byte

  • int: -2147483648 ~ 2147483647까지의 정수 값을 저장하는 자료형으로, 크기는 4byte

  • long: 매우 큰 정수 값을 저장하는 자료형으로, 크기는 8byte

  • float: 소수점 이하 7자리까지 정밀도를 가지는 실수 값을 저장하는 자료형으로, 크기는 4byte.

  • double: 소수점 이하 15자리까지 정밀도를 가지는 실수 값을 저장하는 자료형으로, 크기는 8byte.

  • char: 유니코드 문자 하나를 저장하는 자료형으로, 크기는 2byte.

➡️ 오버라이딩(OverRiding)과 오버로딩(OverLoading)에 대해 설명해주세요.

오버라이딩(Overriding)과 오버로딩(Overloading)은 모두 객체 지향 프로그래밍에서 메서드(method)를 다루는 기술

  • 오버라이딩(Overriding)
    오버라이딩은 부모 클래스에서 상속받은 메서드를 자식 클래스에서 재정의하는 것을 말합니다. 즉, 부모 클래스의 메서드와 같은 이름, 매개변수 리스트, 반환 타입을 갖는 메서드를 자식 클래스에서 구현하여 부모 클래스의 메서드를 덮어쓰는 것입니다. 오버라이딩을 사용하면 부모 클래스의 메서드를 자식 클래스에서 변경하거나 추가적인 기능을 추가할 수 있습니다. 이때 자식 클래스에서 오버라이드한 메서드가 호출되면 부모 클래스의 메서드가 아닌 자식 클래스에서 구현한 메서드가 실행됩니다. 오버라이딩을 하려면 메서드 이름, 매개변수 리스트, 반환 타입이 부모 클래스와 동일해야 하며, 접근 제어자는 부모 클래스의 메서드보다 좁은 범위로 변경할 수 없습니다.

  • 오버로딩(Overloading)
    오버로딩은 같은 이름의 메서드를 매개변수 리스트를 달리하여 여러 개 정의하는 것을 말합니다. 즉, 같은 이름의 메서드를 여러 개 작성하여, 매개변수의 개수, 타입, 순서가 다르더라도 동일한 이름으로 메서드를 호출할 수 있게 하는 것입니다. 오버로딩을 사용하면 하나의 이름으로 여러 개의 메서드를 사용할 수 있습니다. 이때 메서드를 호출할 때 전달하는 인자의 타입, 개수, 순서에 따라 적절한 메서드가 선택되어 실행됩니다. 오버로딩을 하려면 메서드 이름은 동일해야 하지만, 매개변수 리스트는 서로 달라야 합니다. 반환 타입은 오버로딩을 구현하는 데 아무런 영향을 주지 않습니다. 즉, 오버라이딩은 상속 관계에서 부모 클래스의 메서드를 자식 클래스에서 재정의하는 것이며, 오버로딩은 같은 이름의 메서드를 다른 매개변수 리스트로 여러 개 정의하는 것입니다.

➡️ 객체지향 프로그래밍(OOP)에 대해 설명해주세요.

객체지향 프로그래밍(Object-Oriented Programming, OOP)은 컴퓨터 프로그램을 설계하는 방법 중 하나입니다.

  • 객체지향 프로그래밍은 현실 세계의 객체(Object)를 소프트웨어의 객체로 모델링하여 프로그램을 작성합니다. 객체는 상태와 행동으로 구성되어 있으며, 프로그램은 객체들간의 상호작용을 통해 실행됩니다. 객체지향 프로그래밍에서는 객체가 중심이 되며, 프로그램은 객체들의 집합으로 구성됩니다. 객체는 클래스라는 틀에서 생성되며, 클래스는 객체들이 가져야할 속성과 행동을 정의하는 설계도와 같은 역할을 합니다.

  • 객체지향 프로그래밍은 캡슐화(Encapsulation), 상속(Inheritance), 다형성(Polymorphism)이라는 세 가지 개념을 기반으로 합니다.

  • 캡슐화(Encapsulation)
    캡슐화는 객체의 데이터와 메서드를 하나의 단위로 묶어서 외부에서 직접 접근하지 못하게 하는 것을 말합니다. 객체의 내부 상태를 보호하고, 외부에서의 불필요한 접근을 차단하여 객체의 안정성과 보안성을 높이는데 중요한 역할을 합니다.

  • 상속(Inheritance)
    상속은 부모 클래스(Parent Class)에서 자식 클래스(Child Class)로 특정한 속성과 메서드를 물려주는 것을 말합니다. 이를 통해 자식 클래스는 부모 클래스의 모든 멤버를 상속받아서 재사용할 수 있습니다. 또한, 상속을 통해 객체 간의 유사한 구조를 유지하면서도 새로운 클래스를 만들 수 있습니다.

  • 다형성(Polymorphism)
    다형성은 같은 이름의 메서드나 연산자를 여러 클래스에서 다양하게 구현할 수 있도록 하는 것을 말합니다. 즉, 같은 이름의 메서드가 다른 클래스에서는 서로 다른 구현을 가질 수 있다는 것입니다. 이를 통해 코드의 재사용성을 높이고, 유지보수를 쉽게 할 수 있습니다.

객체지향 프로그래밍은 코드의 가독성, 재사용성, 유지보수성 등의 이점을 제공합니다. 또한, 현실 세계와 유사한 모델링을 통해 프로그램의 설계와 개발을 보다 직관적이고 쉽게 할 수 있습니다.

➡️ 추상 클래스와 인터페이스에 대해 설명해주시고, 차이에 대해 설명해주세요.

  • 추상 클래스(Abstract Class)
    추상 클래스는 하나 이상의 추상 메서드(Abstract Method)를 포함하는 클래스입니다. 추상 메서드는 메서드의 구현부가 없이 선언만 되어 있는 메서드로, 실제로는 이를 상속받은 자식 클래스에서 구현해야 합니다. 추상 클래스는 자체적으로 객체를 생성할 수 없으며, 상속을 통해 자식 클래스에서 구현되어야 합니다. 추상 클래스는 일반적인 클래스와 마찬가지로 필드, 생성자, 메서드 등을 가질 수 있습니다. 추상 클래스는 일반적인 클래스보다 더 추상적인 개념으로, 추상 클래스를 상속받는 자식 클래스에서 공통적인 기능을 구현하도록 유도하는 역할을 합니다.

  • 인터페이스(Interface)
    인터페이스는 추상 메서드와 상수(Constant)를 정의할 수 있는 추상 클래스와 유사한 개념입니다. 인터페이스는 모든 메서드가 추상 메서드이므로, 추상 클래스보다 더 추상적인 개념으로 볼 수 있습니다. 인터페이스는 클래스가 아니며, 구현된 메서드나 변수를 가지지 않습니다. 인터페이스는 클래스와 클래스간의 상호작용을 위한 일종의 계약서로, 인터페이스를 구현하는 클래스는 반드시 인터페이스에 정의된 모든 메서드를 구현해야 합니다.

➕ 추상 클래스와 인터페이스의 차이점

  • 추상 클래스는 일반 클래스와 마찬가지로 필드와 메서드를 가질 수 있지만, 인터페이스는 상수와 추상 메서드만을 가질 수 있습니다.

  • 추상 클래스는 자식 클래스에서 추상 메서드를 구현할 수 있으나, 인터페이스는 구현된 메서드를 가질 수 없으며, 구현되지 않은 추상 메서드만을 가질 수 있습니다.

  • 추상 클래스는 자식 클래스에서 상속받은 메서드를 오버라이딩하여 구현할 수 있으나, 인터페이스에서는 메서드 시그니처만을 상속받고, 구현부는 자식 클래스에서 직접 구현해야 합니다.

  • 추상 클래스는 인스턴스 생성이 불가능하지만, 인터페이스는 인스턴스 생성이 불가능합니다.

  • 추상 클래스는 공통된 기능을 가진 클래스들의 상위 클래스로 사용될 수 있으며, 인터페이스는 다른 객체와의 상호작용을 위한 계약서로 사용됩니다.

➡️ 가비지 컬렉션(gc)란 무엇일까요?

가비지 컬렉션(Garbage Collection)은 메모리 관리 기법 중 하나로, 프로그램에서 사용한 메모리 중에서 더 이상 필요하지 않은 객체들을 자동으로 찾아내어 메모리를 회수하는 과정을 말합니다.

Java, C#과 같은 언어에서는 가비지 컬렉션을 자동으로 수행하여 프로그래머가 명시적으로 메모리를 해제하지 않아도 되도록 하고 있습니다. 이를 통해 메모리 누수(Memory Leak)나 다른 메모리 관련 오류를 예방할 수 있습니다.

가비지 컬렉션은 각 언어나 런타임 환경에서 다르게 구현되어 있습니다. 일반적으로 가비지 컬렉션은 더 이상 참조되지 않는 객체들을 찾아내고, 이를 해제하여 메모리를 회수합니다. 이를 위해 가비지 컬렉션 알고리즘을 사용하여, 참조하는 객체가 존재하지 않는 객체들을 검사하고, 이를 삭제합니다.

가비지 컬렉션은 개발자가 메모리 관리에 대한 부담을 줄여주고, 프로그램 실행 시간을 향상시켜줍니다. 하지만 가비지 컬렉션이 실행되는 동안 프로그램이 일시 중지될 수 있다는 단점도 있습니다. 따라서 가비지 컬렉션은 최적화를 위한 설정 등을 통해 조정할 수 있습니다.

➡️ JVM의 동작 방식에 대해 설명해 주세요.

JVM(Java Virtual Machine)은 Java 언어로 작성된 프로그램을 실행하기 위한 가상 머신입니다. JVM은 Java 코드를 실행하기 위해 바이트코드로 번역하고, 메모리 관리와 가비지 컬렉션을 수행합니다.

JVM의 동작 방식은?

  • 컴파일러에 의해 Java 소스 코드가 컴파일되어 바이트코드(.class 파일)로 변환됩니다.

  • 클래스 로더는 바이트코드를 JVM 내부에 로딩합니다. 이때, 클래스 로더는 해당 클래스의 의존성을 찾아내고, 필요한 클래스를 JVM 내부에 로딩합니다.

  • 로딩된 클래스는 JVM의 메모리 영역 중 메소드 영역(Method Area)에 저장됩니다. 이때, 각 클래스마다 상수 풀(Constant Pool), 필드(Field), 메소드(Method) 등이 메모리에 할당됩니다.

  • JVM은 스택(Stack)과 힙(Heap) 두 개의 메모리 영역을 사용합니다. 스택은 각 쓰레드(Thread)마다 별도로 할당되며, 지역 변수(Local Variable)와 메소드 호출(Call) 등을 관리합니다. 힙은 객체(Object)를 저장하는데 사용되며, 가비지 컬렉션(Garbage Collection)에 의해 관리됩니다.

  • 프로그램이 실행될 때, JVM은 바이트코드를 해석하고 실행합니다. 이때, JVM은 Just-In-Time 컴파일러(JIT Compiler)를 사용하여 바이트코드를 네이티브 코드(Native Code)로 변환하고, 실행 속도를 향상시킵니다.

  • 프로그램이 실행되는 도중, JVM은 가비지 컬렉션을 수행하여 더 이상 사용되지 않는 객체를 해제합니다.

  • 프로그램이 종료되면, JVM은 할당된 메모리를 모두 반환하고, 자원을 해제합니다.

이와 같은 방식으로 JVM은 Java 코드를 실행하며, 메모리 관리와 가비지 컬렉션을 수행합니다. 또한, JVM은 플랫폼에 독립적인 실행 환경을 제공하여, Java 코드를 어떤 플랫폼에서든 동일하게 실행할 수 있도록 합니다.

➡️ 불변 객체란 무엇이고, final은 무엇일까요? 사용하는 이유와 함께 설명해주세요.

불변 객체(Immutable Object)는 생성된 이후에 내부 상태를 변경할 수 없는 객체를 말합니다. 즉, 불변 객체는 한 번 생성된 이후에는 내부 상태를 변경할 수 없으며, 새로운 객체를 생성하여 상태를 변경하는 방식으로 동작합니다.

➕ 불변 객체의 장점

  • 스레드 안전성(Thread Safety): 불변 객체는 여러 스레드에서 동시에 접근하여 사용할 수 있으며, 내부 상태가 변경되지 않기 때문에 스레드 안전성을 보장합니다.

  • 보안성(Security): 불변 객체는 내부 상태를 변경할 수 없기 때문에, 객체의 불변성이 보장된다면 외부에서 내부 상태를 변경할 수 없어 보안성이 향상됩니다.

  • 캐시(Caching): 불변 객체는 생성된 이후에 내부 상태가 변경되지 않기 때문에, 한 번 생성된 객체를 캐시(Cache)에 저장하여 재사용할 수 있습니다. 이는 객체 생성 비용을 줄이고 성능을 향상시키는 효과가 있습니다.

  • Java에서는 final 키워드를 사용하여 변수, 메소드, 클래스를 선언할 수 있습니다. final 변수는 값을 변경할 수 없는 상수(Constant)를 선언할 때 사용되며, final 메소드는 오버라이딩(Overriding)을 방지하여 메소드의 불변성을 보장합니다. final 클래스는 상속을 방지하여 클래스의 불변성을 보장합니다.

➕ final 키워드를 사용하는 이유

  • 값의 변경을 방지: final 변수는 값의 변경을 방지하여 안정적인 프로그램을 작성할 수 있도록 돕습니다.

  • 오버라이딩을 방지: final 메소드는 오버라이딩을 방지하여 메소드의 불변성을 보장합니다.

  • 상속을 방지: final 클래스는 상속을 방지하여 클래스의 불변성을 보장합니다.

  • 성능 향상: final 변수는 컴파일러가 상수 값을 컴파일 시간에 결정할 수 있기 때문에, 실행 시간에 변수 값을 계산할 필요가 없어 성능을 향상시킵니다.

따라서, 불변 객체와 final 키워드는 Java에서 안정적이고 성능이 우수한 프로그램을 작성하는데 도움을 주는 중요한 개념입니다.

➡️ 자바의 메모리 영역에 대해 설명해주세요.

Java는 실행 시간에 메모리를 관리하는데, 이때 메모리 영역을 다음과 같이 나누어 관리합니다.

  • 메소드 영역(Method Area): 클래스에 대한 정보, 클래스 변수(static variable), 상수(constant) 등이 저장되는 공간입니다. JVM이 시작될 때 생성되며, 프로그램이 종료될 때까지 유지됩니다.

  • 힙 영역(Heap Area): 객체가 생성되는 공간입니다. new 키워드를 사용하여 객체를 생성하면, 해당 객체는 힙 영역에 생성됩니다. JVM은 Garbage Collector를 사용하여 사용하지 않는 객체를 제거하고, 메모리를 해제합니다.

  • 스택 영역(Stack Area): 지역 변수(local variable)와 매개 변수(parameter)가 저장되는 공간입니다. 메소드가 호출될 때마다 해당 메소드를 위한 스택 프레임(Stack Frame)이 생성되고, 메소드가 종료될 때 해당 스택 프레임이 제거됩니다.

  • PC(Program Counter) 레지스터: 현재 실행 중인 명령어의 주소를 저장하는 레지스터입니다.

  • 네이티브 메소드 스택(Native Method Stack): 자바 외부에서 실행되는 코드(native code)를 위한 스택 영역입니다.

메모리 영역은 JVM이 자동으로 관리하기 때문에 개발자가 메모리를 직접 관리할 필요가 없습니다. 그러나 개발자는 객체 생성 및 사용 시 메모리 사용에 대한 고민을 해야 하며, 메모리 누수(Memory Leak)가 발생하지 않도록 주의해야 합니다.

➡️ new String()과 리터럴(" ")의 차이에 대해 설명해주세요.

new String()과 리터럴(" ")은 문자열을 생성하는 방법 중 두 가지 방법입니다.

➕ 메모리 할당 방식

  • new String(): 힙(heap) 메모리 영역에 새로운 객체를 생성하여 할당합니다.
  • 리터럴(" "): 상수 풀(constant pool)이라는 특별한 메모리 영역에 저장되며, 이미 존재하는 객체를 재사용합니다.

➕ 불변성(Immutability)

  • new String(): 새로운 객체를 생성하므로, 값이 변경 가능합니다.
  • 리터럴(" "): 상수로 선언되어 있으므로, 값이 변경되지 않습니다.

➕ 성능(Performance)

  • new String(): 새로운 객체를 생성하므로, 메모리 사용이 더 많고 성능이 떨어질 수 있습니다.
  • 리터럴(" "): 이미 존재하는 객체를 재사용하기 때문에, 메모리 사용이 덜하고 성능이 더 우수합니다.

따라서, 문자열이 변경될 가능성이 있거나 동적으로 생성해야 하는 경우에는 new String()을 사용하고, 문자열이 변경되지 않는 상수 값을 사용하는 경우에는 리터럴(" ")을 사용하는 것이 좋습니다.

⭐️ 추가 과제: 람다(lambda)에 대해 알아볼까요?

람다(lambda)는 JAVA8에서 등장한 함수형 프로그래밍 패러다임을 지원하기 위한 기능입니다. 람다는 간결하고 효과적인 방법으로 함수를 표현할 수 있게 해줍니다.

람다식은 익명 함수(anonymous function)를 만들기 위한 표현식으로, 메소드를 하나의 식으로 표현하는 것입니다. 람다식은 함수형 인터페이스(functional interface)를 구현하는 클래스의 인스턴스로 컴파일됩니다.

➕ 람다식의 문법

(매개변수) -> {실행 코드}

예를 들어, 다음과 같은 메소드를 람다식으로 표현할 수 있습니다.

public static int add(int a, int b) {
    return a + b;
}

// 람다식으로 표현한 add 메소드
(a, b) -> a + b

람다식은 함수형 프로그래밍에서 중요한 역할을 담당합니다. 함수형 프로그래밍에서는 함수를 일급 객체로 취급하며, 함수를 변수에 저장하거나 매개변수로 전달하는 등의 기능을 제공합니다. 이를 통해 간결하고 유연한 코드를 작성할 수 있습니다.

⭐️ 추가 과제: 스트림(stream)에 대해 알아볼까요?

스트림(Stream)은 JAVA8에서 추가된 컬렉션(Collection)의 요소를 하나씩 차례로 처리할 수 있는 기능입니다. 스트림은 데이터 소스를 변경하지 않으며, 병렬처리가 가능합니다.

➕ 스트림의 특징

  • 선언형(Declarative) 프로그래밍: 스트림은 선언적인 방식으로 데이터를 처리할 수 있습니다. 즉, 구현 방식이 아니라 처리할 데이터의 종류와 결과에만 집중할 수 있습니다.

  • 내부 반복(Internal iteration): 스트림은 자체 반복 기능을 갖고 있으며, 요소를 순회하는 작업을 개발자가 처리하지 않고 내부적으로 처리합니다. 이를 통해 병렬처리가 가능합니다.

  • 중간 연산과 최종 연산: 스트림은 중간 연산과 최종 연산으로 구성됩니다. 중간 연산은 필터링(filtering), 매핑(mapping), 정렬(sorting) 등의 작업을 수행하고, 최종 연산은 결과를 생성하거나 출력하는 작업을 수행합니다.

➕ 스트림의 사용법

  • 스트림 생성: 컬렉션, 배열, 파일 등의 데이터 소스에서 스트림을 생성합니다.

  • 중간 연산: 필터링, 매핑, 정렬 등의 중간 연산을 수행합니다.

  • 최종 연산: 결과를 생성하거나 출력하는 최종 연산을 수행합니다.

➕ 스트림을 사용한 예제 코드

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

// 스트림 생성
Stream<Integer> stream = numbers.stream();

// 중간 연산: 짝수만 필터링
Stream<Integer> evenStream = stream.filter(n -> n % 2 == 0);

// 최종 연산: 합계 출력
System.out.println(evenStream.mapToInt(Integer::intValue).sum());  // 6 출력

위 코드에서, numbers 리스트를 스트림으로 변환한 뒤, 중간 연산에서는 짝수만 필터링하고 최종 연산에서는 합계를 출력하는 코드를 작성하였습니다.

profile
@chhaewxn

0개의 댓글