본문 바로가기
면접

Java 질문

by L3m0n S0ju 2023. 3. 28.

 

 

슬슬 무언가 발표 비스무리하게 말을 해야할 순간이 다가오는 것 같아서 하루에 질문 3개씩 준비를 하려고 한다.

 

 

 


Java를 사용하는 이유

Java는 객체 지향 프로그래밍을 대표하는 언어로 기본 자료형을 제외한 모든 요소들이 객체로 표현됩니다. 객체들 간의 상호작용을 통해서 복잡한 현실 세계를 코드로 나타낼 수 있습니다. Java 같은 객체 지향 프로그래밍 언어를 사용하는 이유는 웹 애플리케이션과 같이 구현해야 할 요소들이 복잡한 경우 기존의 절차지향적인 프로그래밍 방법 보다는 객체 간의 상호작용을 통해서 현실 세계를 구현하는 방법이 더 쉽습니다. 그리고 모듈화를 통해서 코드 수정이 필요한 경우 다른 영역에 영향을 최소화 할 수 있어서 유지보수도 편하게 할 수 있는 객체 지향 프로그래밍을 많이 사용합니다.

 

 


extends, implements, abstract 차이점

1. extends: 부모의 메소드를 자식이 그대로 사용할 수 있다. 자식이 따로 메소드를 추가할 수 있다. 부모에서 구현한 메소드도 자식이 오버라이딩 할 수 있다.

2. implements(interface): 부모 객체는 선언만 하고 정의는 자식에서 오버라이딩해서 사용함, 자식에서 모두 상속받은 요소들을 구현해야 함. 자식이 따로 메소드를 추가할 수 있다.

3. abstract: abstract 키워드가 선언되어 있으면 자식이 반드시 구현해야 합니다.

 

 

 


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

1. 모든 메서드를 자식 클래스에서 구현해야 하는 인터페이스와 달리 추상 클래스는 일반 메서드도 포함할 수 있다.

2. 추상 클래스는 단일 상속 원칙으로 하나의 부모 클래스만 가질 수 있습니다. 반면에 인터페이스는 다중 상속이 가능하여 여러 개의 인터페이스를 구현 가능합니다.

3. 인터페이스는 public 접근 제어자만 사용 가능하므로 만약 캡슐화를 해야한다면 추상 클래스를 사용해야 합니다.

 

 

 

 


클래스와 객체의 차이점은?

클래스는 객체를 정의하는 틀을 의미하고, 객체는 클래스를 통해 생성한 식별 가능한 개체 또는 사물을 말합니다.

 

 

 


Java vs C언어

1. 컴파일 언어 vs 인터프리터 언어

Java는 인터프리터 언어로 코드를 바이트코드로 변환한 후 인터프리터에 의해서 다시 기계어로 변환된 후 실행이 됩니다. 반면 C 언어는 컴파일 언어이며, 코드를 기계어로 바로 변환하기 때문에 성능 더 좋습니다.

 

2. 플랫폼 독립성
Java 언어는 플랫폼 독립성을 제공합니다. Java 언어가 JVM 위에서 동작하기 때문에 운영체제에 상관없이 동작 가능합니다. 반면 C 언어는 운영체제에 따라 코드를 수정해야 할 수 있습니다.


3. 메모리 관리
Java 언어에서는 가비지 컬렉션 기능을 제공하여, 개발자가 메모리를 직접 관리하지 않아도 되기 때문에 오류가 발생할 가능성이 낮습니다. 반면 C 언어에서는 개발자가 메모리를 직접 할당하고 해제해야 하기 때문에 오류가 발생할 가능성이 높습니다. 

웹 개발 같이 확장성이나 안정성이 중요할 때는 Java 언어를 선택하는 것이 좋습니다. 반면 성능이 중요할 때는 C 언어를 선택하는 것이 좋습니다.

 

 


객체 지향 프로그래밍 장단점?

장점

- 모듈화: 객체 지향 프로그래밍은 각각의 객체를 독립적인 모듈로 만듭니다. 코드를 수정하거나 유지보수할 때 다른 객체들에게 영향을 미치지 않습니다.

 

- 코드 재사용성: 객체 지향 프로그래밍은 코드를 재사용하기 쉽게 만들어 줍니다. 이는 코드의 생산성을 높이고, 개발 시간을 줄이며, 코드 유지보수를 용이하게 합니다.

 

- 추상화: 필요한 부분만을 표현하고, 불필요한 세부 사항을 숨김으로써 사용자가 편하게 메소드를 사용할 수 있습니다.

 

- 상속성: 객체 지향 프로그래밍은 상속을 지원합니다. 이를 통해 코드를 재사용하면서 새로운 기능을 추가하거나 기존 기능을 변경할 수 있습니다.

 

- 다형성: 같은 이름의 메서드를 매개변수에 따라 여러번 정의할 수 있습니다.

 

 

단점

  1. 성능이 낮을 수 있음: 객체 지향 프로그래밍은 많은 객체와 메서드를 사용하기 때문에 성능이 낮아질 수 있습니다.
  2. 설계가 복잡할 수 있음: 객체 지향 프로그래밍은 객체 간의 관계가 복잡할 수 있습니다. 이로 인해 설계가 복잡해질 수 있으며, 오류가 발생할 가능성이 높아집니다.

 

 

 


main 메소드에 static을 선언하는 이유

 

Java에서 main 메소드를 선언할 때 static으로 선언해야 하는 이유는 Java가 JVM(Java Virtual Machine)에서 동작하기 때문입니다. JVM은 Java 어플리케이션을 실행하기 위해 필요한 메모리 공간을 할당하고, Java 코드를 해석하고 실행합니다. 그리고 Java 클래스를 로드할 때 static으로 선언된 메소드와 변수는 메모리 공간을 먼저 할당받습니다. main 메소드가 static으로 선언되어 있지 않으면, JVM이 Java 클래스를 로드할 때 main 메소드를 찾을 수 없어서 프로그램이 실행되지 않습니다. 



 


Exception과 RuntimeException 예외 클래스 차이점

Java에서 Exception과 RuntimeException은 예외(Exception) 처리를 위한 클래스입니다.

Exception 클래스는 예외 처리를 강제하는 검사 예외(checked exception)이며, 코드에서 반드시 예외 처리를 해야합니다. 예외 처리를 하지 않으면 컴파일 오류가 발생합니다. IOException, SQLException, ClassNotFoundException 등은 모두 Exception 클래스를 상속하는 검사 예외의 예입니다.

RuntimeException 클래스는 예외 처리를 강제하지 않는 비검사 예외(unchecked exception)입니다. 따라서 코드에서 예외 처리를 하지 않아도 컴파일 오류가 발생하지 않습니다. 대신 예외가 발생하면 런타임 에러가 발생하며, 프로그램이 강제로 종료됩니다. NullPointerException, IndexOutOfBoundsException, ArithmeticException 등은 모두 RuntimeException 클래스를 상속하는 비검사 예외의 예입니다.

검사 예외는 예외 상황을 예측하고 복구할 수 있는 경우에 사용하며, 비검사 예외는 예측하지 못하거나 복구 불가능한 예외 상황에서 사용합니다. 그래서 보통 복구가 불가능하기 때문에 비검사 예외를 주로 사용합니다. 

 

 


래퍼 클래스란?

래퍼 클래스(Wrapper class)는 기본 자료형(primitive data type)을 객체로 감싸는 역할을 합니다. 예를 들어, int 자료형을 포함하는 Integer 클래스, double 자료형을 포함하는 Double 클래스 등이 있습니다.

 

래퍼 클래스를 사용하는 이유는 여러 가지가 있습니다.

1. 객체 지향 프로그래밍에서는 모든 것이 객체로 표현되어야 합니다. 기본 자료형은 객체가 아니기 때문에 객체로 다루기 어렵습니다. 래퍼 클래스를 사용하면 기본 자료형을 객체로 감싸서 객체로 다룰 수 있습니다.

 

2. 래퍼 클래스는 객체이므로 객체로부터 상속받은 메서드를 사용할 수 있습니다. 예를 들어, Integer 클래스는 Number 클래스를 상속받기 때문에 Number 클래스에 정의된 메서드들을 사용할 수 있습니다.


3. 래퍼 클래스는 기본 자료형을 객체로 포장하기 때문에, 메서드 호출 시에 매개변수로 전달하기 쉽습니다. 예를 들어, 메서드의 매개변수로 Object 타입을 받는 경우, 기본 자료형을 전달할 수 없지만 래퍼 클래스를 사용하면 객체로 전달할 수 있습니다.


4. 래퍼 클래스는 null 값을 포함할 수 있습니다. 기본 자료형은 null 값을 가질 수 없지만, 래퍼 클래스를 사용하면 null 값을 포함할 수 있습니다.

 

 

 


제너릭이란?

제너릭은 자바에서 타입 안정성을 보장하기 위해 도입된 기능으로, 클래스, 인터페이스, 메서드 등을 작성할 때, 타입을 파라미터로 받아서 사용하는 것을 말합니다. 제너릭을 사용하는 방법은 간단합니다. 클래스나 인터페이스를 정의할 때, 타입 매개변수를 설정하고 생성자를 통해 생성할 때 원하는 타입을 입력하면 됩니다.

제네릭의 주요 장점은 다음과 같습니다:

 

1. 타입 안정성: 컴파일 시점에 타입 체크를 수행하여 타입 안정성을 보장합니다.

 

2. 타입 변환 감소: 타입 변환을 자동으로 처리해줍니다. 개발자가 타입 변환 코드를 작성할 필요가 없으므로 코드가 더 간결해지고 가독성이 향상됩니다.

 

3. 재사용성: 여러 타입에서 재사용할 수 있습니다. 따라서 유연하고 확장 가능한 코드를 작성할 수 있습니다.

 

4. 코드 가독성: 타입 정보가 명시되므로 코드를 이해하고 유지보수하기 쉽습니다.

 

 

 

 


컬렉션 프레임워크란?

컬렉션 프레임워크는 자바에서 데이터를 저장하고 처리하는 데에 사용되는 API입니다. 컬렉션 프레임워크는 다양한 종류의 자료구조를 제공하며 컬렉션을 통해서 데이터를 효율적으로 관리할 수 있습니다. 컬렉션 프레임워크 주요 인터페이스로는 List, Set, Map 등이 있습니다. 각 인터페이스는 다양한 구현체를 가지고 있습니다.

List 인터페이스는 순서가 있는 데이터를 저장하는 자료구조를 제공하며, ArrayList, LinkedList 등이 대표적인 구현체입니다. Set 인터페이스는 중복을 허용하지 않는 데이터를 저장하는 자료구조를 제공하며, HashSet, TreeSet 등이 대표적인 구현체입니다. Map 인터페이스는 키와 값으로 데이터를 저장하는 자료구조를 제공하며, HashMap, TreeMap 등이 대표적인 구현체입니다.

 

 

 


ArrayList와 LinkedList 클래스 차이점

ArrayList와 LinkedList는 모두 자바에서 제공하는 컬렉션 프레임워크의 List 인터페이스를 구현한 클래스입니다. 하지만 두 클래스는 내부적으로 데이터를 저장하고 처리하는 방식이 다르기 때문에, 성능과 사용 용도에서 차이점이 있습니다.

ArrayList는 내부적으로 배열을 사용하여 데이터를 저장하며, 데이터의 추가나 삭제 시 해당 위치의 인덱스를 찾아서 작업을 수행합니다. 따라서, 인덱스를 이용한 빠른 데이터 접근이 가능하며, 데이터의 검색이나 순회에 적합합니다. 하지만, 데이터를 추가하거나 삭제할 때는 해당 위치 이후의 모든 데이터를 이동시켜야 하기 때문에, 시간이 많이 소요됩니다. 또한, 크기가 변경될 때마다 배열을 새로 생성해야 하므로, 메모리 공간을 낭비할 수 있습니다.

반면, LinkedList는 내부적으로 노드(Node)를 사용하여 데이터를 저장하며, 노드 간의 연결(link)로 데이터를 처리합니다. 따라서, 데이터의 추가나 삭제 시 해당 노드에 대한 링크를 변경하는 것만으로 작업을 수행할 수 있어, 시간이 적게 소요됩니다. 또한, LinkedList는 데이터의 크기가 변경될 때마다 새로운 공간을 할당하지 않고, 필요한 만큼의 노드를 추가하거나 삭제하여 메모리 공간을 절약할 수 있습니다. 하지만, 인덱스를 이용한 데이터 접근이 불가능하며, 데이터의 검색이나 순회에는 비교적 느릴 수 있습니다.

따라서, ArrayList는 인덱스를 이용한 빠른 데이터 접근이 중요하거나, 크기가 고정적인 경우에 적합하며, LinkedList는 데이터의 추가나 삭제가 빈번하거나, 크기가 동적으로 변하는 경우에 적합합니다.

 

 

 


생성자 public ArrayList(Collection<? extends E> c)에서 ?의 역할

 

제네릭에서 ?는 와일드카드(Wildcard) 타입으로 사용됩니다. Collection<? extends E>은 "E의 하위 클래스 타입"을 의미합니다. 따라서, c 매개변수로 전달된 Collection 객체는 E 타입이거나 E의 하위 클래스 타입으로 제한됩니다.

 

 


함수형 인터페이스란?

함수형 인터페이스(Functional Interface)는 Java에서 함수형 프로그래밍을 지원하기 위해 도입된 개념입니다. 보통 람다식으로 접하게 됩니다. 함수형 인터페이스는 다음과 같은 특징을 가지고 있습니다:

- 단일 추상 메서드: 함수형 인터페이스는 오직 하나의 추상 메서드만을 가지고 있어야 합니다. 이를 함수형 인터페이스의 "추상 메서드 규칙"이라고 합니다. 인터페이스에는 default 메서드나 static 메서드가 존재할 수 있지만, 추상 메서드는 하나만 있어야 합니다.


- 람다식과의 연결: 함수형 인터페이스는 람다식과 함께 사용되는 것이 일반적입니다. 람다식은 함수형 인터페이스를 구현하는 익명 함수를 간결하게 표현하는 방법입니다. 함수형 인터페이스의 추상 메서드와 람다식의 매개변수 및 반환값의 타입이 일치해야 합니다.

 

자바에서는 여러 가지 내장된 함수형 인터페이스를 제공하고 있습니다. 주요한 함수형 인터페이스로는 아래와 같은 것들이 있습니다:

 

Supplier<T>: 매개변수 없이 값을 제공하는 함수입니다.
Consumer<T>: 값을 받아서 소비하는 함수입니다.
Predicate<T>: 주어진 조건에 따라 참 또는 거짓을 반환하는 함수입니다.
Function<T, R>: 하나의 값에서 다른 값으로의 변환을 수행하는 함수입니다.
BiFunction<T, U, R>: 두 개의 값에서 다른 값으로의 변환을 수행하는 함수입니다.

 

이러한 함수형 인터페이스를 사용하면 더 간결하고 유연한 코드를 작성할 수 있으며, 병렬 처리, 이벤트 기반 프로그래밍 등의 함수형 프로그래밍의 장점을 활용할 수 있습니다.

 

 


가비지 콜렉션이 어떻게 작동하는지 설명해보시오.

 

가비지 콜렉션은 다양한 알고리즘을 사용하여 더 이상 사용되지 않는 객체를 식별하고 메모리에서 해제하는 프로세스입니다. 보통 Java Virtual Machine (JVM) 에서 기능을 제공합니다. 가장 일반적으로 사용되는 가비지 콜렉션 알고리즘에는 다음과 같은 것들이 있습니다:

 

1. 표시-정리 알고리즘 (Mark-Sweep Algorithm):

이 알고리즘은 먼저 가비지 객체를 표시한 다음, 표시되지 않은 객체들을 메모리에서 제거합니다.
객체가 도달 가능한지 여부를 확인하기 위해 루트 세트(root set)와 같은 시작점을 사용하여 객체 그래프를 탐색합니다.
탐색된 객체를 표시하고, 표시되지 않은 객체를 정리하여 메모리를 회수합니다.

 

2. 복사 알고리즘 (Copying Algorithm):

이 알고리즘은 힙(heap)을 두 부분으로 나눈 후, 한 부분에만 객체를 저장하고 나머지 부분은 비워둡니다.
가비지 콜렉션 실행 시, 사용 중인 객체들을 나머지 부분으로 복사하고 가비지 객체가 있는 부분은 비워집니다.
이 방식은 가비지 콜렉션 시간을 줄이기 위해 살아남은 객체들을 다른 곳으로 이동시킵니다.

 

 

위에서 말한 알고리즘을 이용해서 가비지 콜렉션을 진행하는데 여기서 주기를 어떻게 할 지에 관한 알고리즘도 있습니다. 첫번째로는 그냥 일정한 주기로 컬렉션을 하는 방법이 있고 두번째로 세대별 가비지 콜렉션이라고 객체를 생성된 세대에 따라 분류하고, 각 세대에 대한 가비지 콜렉션을 다르게 적용합니다. 첫 번째 세대에서만 빈번한 가비지 콜렉션을 수행하고, 오래된 세대에서는 덜 빈번한 가비지 콜렉션을 수행합니다. 이렇게 하면 오래된 객체가 더 오래 살아남아서 가비지 콜렉션의 오버헤드를 줄일 수 있습니다.

 

 

 


String은 어떤 자료형인가?

 

자바 자료형은 기본 자료형과 참조 자료형으로 나뉜다.

 

참조형 기본 자료형을 기초로 하여 만들어진 자료형이다. 대표적으로 자바에서 제공하는 String, Array, Map, Set 등과 같은 클래스(Class) 인터페이스(Interface), 열거형(Enum)이 여기에 해당한다. 추가적으로 필요에 따라 사용자가 참조형 타입을 정의할 수도 있다.

 

 


자바 메모리 구조

 

1) 메서드 영역

  • 전역변수, Static 변수, 상수가 저장되는 공간이다.
  • 모든 스레드가 공유하는 영역으로, JVM이 시작될 때 생성되고 프로그램이 종료될 때까지 유지됩니다.

2) 힙(Heap)

  • 동적으로 생성된 객체, 배열이 저장되는 공간입니다.
  • 가비지 컬렉션(Garbage Collection)이 수행되어 더 이상 사용되지 않는 객체를 제거합니다.
  • Reference Type 의 데이터가 저장되는 공간
  • 모든 스레드에서 정보가 공유된다.

3) Stack

  • 지역변수, 메소드의 매개변수와 같이 잠시 사용되고 필요가 없어지는 데이터가 저장되는 공간
  • 만약, 지역변수 이지만 Reference Type일 경우에는 Heap 에 저장된 데이터의 주소값을 Stack 에 저장해서 사용하게 된다.
  • 스레드마다 하나씩 존재한다.
  • 메서드가 호출될 때마다 해당 메서드의 정보가 스택에 push되고, 메서드가 종료될 때 pop됩니다.

4) PC Register

  • 각 스레드가 다음에 실행할 명령어의 주소를 가리키는 포인터입니다.
  • 스레드가 현재 실행 중인 메서드의 상태를 나타냅니다.

5) Native Method Stack

  • Java 가 아닌 다른 언어 (C, C++) 로 구성된 메소드를 실행이 필요할 때 사용되는 공간

 

 

 


try with resouce

 

Closable 클래스를 상속하는 값이 try의 파라미터로 들어가면 자동으로 자원해제를 해줌

 

 

 

 

 


 

final 필드

 final 필드는 한번 초기화하면 더이상 그 값을 변경할 수 없도록한다. 즉, 상수가 되버린다. 그래서 상수화를 위해 사용한다. 상수는 모든 클래스에서 공통적으로 사용될 수 있고 여러개 생성하면 오히려 메모리 낭비이기 때문에 static키워드를 붙어 한번만 생성하고 모든 클래스가 공유하도록 한다.

 

final 클래스

상속이 불가능한 class이다.

 

final 메소드

클래스는 상속은 가능하지만 특정 메소드에 오버라이딩을 금지한다. 

 

 

 

 

 


 

Java Synchronize

 

 

'면접' 카테고리의 다른 글

정규 표현식  (0) 2023.06.30
데이터베이스 질문  (0) 2023.05.17
Spring 질문  (0) 2023.05.17
DevOps 이것저것  (0) 2023.04.06
하는김에 프론트엔드 면접도...  (0) 2023.04.06

댓글