[Java] Java 면접 질문

6 minute read

Java

블로그, 도서, 동영상 등 참고하였습니다

OOP

객체지향 프로그래밍이란 인간 중심적 프로그래밍 패러다임.
현실세계를 프로그래밍으로 옮겨와 프로그래밍 하는 것

객체지향 설계 원칙의 종류

  1. 단일 책임 원칙 : 클래스는 단 하나의 책임을 가져야하며 변경하는 이유 역시 단 하나의 이유여야함
  2. 개방 폐쇄 원칙 : 확장에는 열려있고 변경 닫혀있어야 함
  3. 리스코프 치환 원칙 : 상위 타입의 객체를 하위 타입의 객체로 치환해도 정상 동작해야함
  4. 인터페이스 분리 원칙 : 인터페이스는 그 인터페이스를 사용하는 클라이언트를 기준으로 분리
  5. 의존 역전 원칙 : 고수준 모듈은 저수준 모듈 구현에 의존해서는 안됨

JVM 구조

  1. 프로그램이 실행되면 OS로부터 프로그램이 필요로하는 메모리 할당
  2. 자바 컴파일러를 통해 개발자가 작성한 코드(.java)를 바이트코드(.class)로 변환
  3. Class Loader에서 바이트코드를 JVM에 로딩
  4. 로딩 된 바이트코드를 Execution engine을 통해 기계어로 해석
  5. 해석된 바이트코는 Runtime data areas에 배치되어 수행

JVM의 메모리 구조

  • 메서드 영역(method area)
    프로그램 실행 중 어떤 클래스가 사용되면 해당 클래스파일을 읽어서 분석, 정보를 이 영역에 저장. 클래스변수(class variable)도 함께 생성

  • 힙(heap)
    인스턴스(instance variable)가 생성되는 공간.

  • 호출스택(call stack 또는 execution stack)
    메서드 작업에 필요한 메모리 공간 제공. 메서드가 작업을 수행하는 동안 지역변수(매개변수 포함)들과 연산의 중간결과 등을 저장.
    작업이 종료되면 할당된 메모리 공간은 반환됨.
    따라서 호출스택 제일 상위에 위치한 메서드가 현재 실행 중인 메서드이며, 나머지는 대기상태.

직렬화(Serialization)

Java에서 입출력 할 때는 스트림이라는 통로를 통해 데이터가 이동
객체는 바이트형이 아니라 스트림을 통해 저장하거나 네트워크 전송 불가

직렬화 : 바이트를 배열로 변환
역직렬화 : 스트림을 통해서 받은 직렬화된 객체를 원래 모양으로 만드는 과정

Boxing vs Unboxing

Boxing : 기본자료형(Primitive data type) → Wrapper class
Unboxing : Wrapper class → 기본자료형(Primitive data type)

Wrapper class 왜 사용할까?

  1. 객체 또는 클래스가 제공하는 메서드 또는 생성자에 필요. 즉 기본형이 아닌 객체로 저장되어야 할 때 (ex:Map<String, Integer>)
  2. 클래스가 제공하는 상수 사용 (ex:MIN_VALUE and MAX_VALUE)
  3. 숫자, 문자로의 형 변환 또는 진법 변환 (ex:Integer.parseInt)

HashTable vs HashMap vs ConcurrentHashMap

  • HashTable
    • put, get과 같은 주요 메소드에 Synchronized 키워드가 선언되어 있음
    • Key, Value에 null 허용하지 않음

import java.util.Hashtable;

public class HashTableBasicTest {
	public static void main(String[] args) {
		Hashtable<String, Integer> ht = new Hashtable<>();

		ht.put("key", 0);

		 // Hashtable은 값에 null 허용 안함

		try{
			ht.put("key2", null); // error!
		} catch( Exception e ){
			e.printStackTrace();
		}


		// Hashtable은 키값에 null을 허용 안함

		try{
			ht.put(null, 0); // error!
		} catch( Exception e ){
			e.printStackTrace();
		}

		// 해당 키 값을 가져온다.
		ht.get("key"); // 0
	}
}
  • HashMap
    • 주요 메서드에 Synchronized 키워드가 없음
    • Key, Value에 null 허용

import java.util.HashMap;
import java.util.Map.Entry;

public class HashMapBasicTest {
	public static void main(String[] args) {
		HashMap<String, Integer> hm = new HashMap<>();

		// 값 입력
		hm.put("key", 0);

		// HashMap은 값에 null 허용
		hm.put("key2", null);

		// HashMap은 키값에 null을허용
		hm.put(null, 0);

		// 값 출력
		hm.get("key");

		// 반복 처리 with keySet
		for( String s : hm.keySet() ){
			System.out.println(hm.get(s));
		}

		// 반복 처리 with entrySet
		for( Entry<String, Integer> s : hm.entrySet() ){
			System.out.println(s.getKey()+" "+s.getValue());
		}
	}
}
  • ConcurrentHashMap
    • HashMap을 Thread-safe 하도록 만든 클래스
    • Key, Value에 null 허용 안함
    • putIfAbsent 라는 메소드를 가지고 있음
import java.util.concurrent.ConcurrentHashMap;

public class ConcurrentHashMapBasicTest {

	public static void main(String[] args) {

		ConcurrentHashMap<String, Integer> chm = new ConcurrentHashMap<>();

		// 값 입력
		chm.put("key", 0);

		// ConcurrentHashMap은 값에 null을 허용 안험

		try{
			chm.put("key1", null); // error!
		} catch( Exception e ){
			e.printStackTrace();
		}

		// ConcurrentHashMap은 키값에 null을 허용 안함

		try{
			chm.put(null, 0); // error!
		} catch( Exception e ){
			e.printStackTrace();
		}

		/*
		 * putIfAbsent 메소드는 키값이 존재하면 기존의 값을 반환하고
		 * 없다면 입력한 값을 저장한 뒤 반환.
		 * 따라서 아래의 코드는 이미 key라는 값에 0이라는 값이 있으므로
		 * key 값은 0을 반환.
		 */
		chm.putIfAbsent("key", 1);

		/*
		 * 아래 코드는 key2의 값이 없기 때문에 -1을 저장하고 반환.
		 */
		chm.putIfAbsent("key2", -1);

		for( String s : chm.keySet() ){
			System.out.println(chm.get(s)); // print -1, 0
		}
	}
}

제어자

🐽 Java기초이론 : 제어자

interface vs abstract

  • interface(인터페이스) : 기본 설계도
    • 다중 상속 구현 가능
    • 추상 메서드, 상수만 선언 가능
    • 생성자, 일반 변수 가질 수 없음
    • 협업할 때 추상메서드를 통해 결과 예측하고 작업 가능, 구현 객체의 동일성 보장
  • abstract(추상클래스) : 미완성 설계도
    • 다중 상속 불가
    • 상속을 위한 클래스이기 때문에 따로 객체 생성 불가
    • 추상 메서드 1개 이상, 일반 변수, 일반 메서드 선언 가능
    • 생성자, 일반 변수 가질 수 있음
    • 메서드 부분 구현 가능 (부분 구현된 메서드를 상속해 확장시키기 위함)
  • 차이점
    추상클래스는 IS - A “~이다”. 인터페이스는 HAS - A “~을 할 수 있는”.

String vs StringBuffer / StringBuilder

  • String : 한번 생성되면 할당된 공간이 변하지 않음(불변)
  • StringBuffer / StringBuilder : 객체의 공간이 부족해지는 경우 버퍼의 크기를 유연하게 늘려줌(가변)

StringBuffer vs StringBuilder

  • StringBuffer
    • 각 메소드 별로 Synchronized keyword가 존재
    • 멀티스레드 상태에서 동기화 지원
  • StringBuilder
    • 동기화를 지원하지 않기때문에 멀티스레드 환경에 적합하지 않음
    • 단일스레드에서는 성능이 StringBuffer보다 뛰어남

String vs StringBuffer vs StringBuilder

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

try-with-resource

Java7부터 Try-with-resources 구문을 지원하고 이것을 사용하면 자원을 쉽게 해제할 수 있음. try-with-resources는 try(…)에서 선언된 객체들에 대해서 try가 종료될 때 자동으로 자원을 해제해주는 기능.
try에서 선언된 객체가 AutoCloseable을 구현하였다면 Java는 try구문이 종료될 때 객체의 close() 메소드를 호출.

Synchronized(동기화)

동기화는 여러 개의 쓰레드가 하나의 자원에 접근하려 할 때 주어진 순간에는 단 하나의 쓰레드만이 접근 가능하도록 하는 것

Synchronized(동기화)를 하기 위한 방법

synchronized 함수(메서드)를 만들어 사용.
synchronized 블록(block) 사용.

자바의 메모리 영역

🐽 Java기초이론 : JVM 메모리 구조

컬렉션 프레임워크

  • 다수의 데이터를 쉽고 효과적으로 처리할 수 있는 표준화된 방법을 제공하는 클래스의 집합을 의미
  • 데이터를 저장하는 자료 구조에 따라 인터페이를 정의

    1. List 인터페이스
      • 순서가 있는 데이터의 집합
      • 데이터의 중복을 허용
      • Vector, ArrayList, LinkedList, Stack, Queue
    2. Set 인터페이스
      • 순서가 없는 데이터의 집합
      • 데이터의 중복을 허용하지 않음
      • HashSet, TreeSet
    3. Map 인터페이스
      • 키와 값의 한쌍으로 이루어지는 데이터의 집합
      • 순서가 없음
      • 키는 중복을 허용하지 않지만 값은 중복을 허용
      • HashMap, TreeMap, HashTable, ProperTies

제네릭

  • 형식에 의존하지 않고 하나의 값이 여러 다른 데이터 타입들을 가질 수 있도록 하는 방법
  • 클래스 내부에서 특정 타입을 지정하는 것이 아닌 외부에서 사용자의 필요에 의해 지정되는 것을 의미.

  • 장점
    1. 잘못된 타입이 들어오는 것을 컴파일 단계에서 방지
    2. 타입을 따로 체크하고 변환해줄 필요가 없어 관리하기 편함
    3. 비슷한 기능을 지원하는 경우 코드의 재사용성이 높아짐
  타입 | 설명
  <T>	Type
  <E>	Element
  <K>	Key
  <V>	Value
  <N>	Number

Vector vs ArrayList

  • Vector
    • 동기화된 상태 (Thread safe) : 한번에 하나의 스레드만 액세스 가능
    • 상대적으로 속도가 느림
    • 필요에 따라 크기를 동적으로 저절할 수 있는 동적배열을 구현
    • 배열과 마찬가지로 정수 인덱스를 이용하여 배열에 액세스 할 수 있음
  • ArryaList
    • 동기화가 안된 상태 : 동시에 여러 스레드가 작업할 수 있음
    • 상대적으로 속도가 빠름
    • 멀티스레드 환경이 아닐 경우 사용 권장

ArrayList vs LinkedList

  • ArrayList
    • 내부적으로 데이터를 배열로 관리하고 데이터 추가, 삭제 시 임시 배열을 생성해 데이터를 복사
    • 데이터별 인덱스가 있어 검색에 유리
    • 임시 배열을 사용하기 때문에 데이터의 추가, 삭제 경우에는 불리
  • LinkedList
    • 내부적으로 노드 단위로 데이터를 관리. 자신의 앞 뒤 노드만 인지하는 상태
    • 인덱스가 없어 검색시 전 노드를 순회해야하므로 불리
    • 데이터 추가, 삭제 시 불필요한 데이터 복사가 없어 유리

오버로딩(Overloading) vs 오버라이딩(Overriding)

🐽 Java기초이론 : 오버로딩 vs. 오버라이딩

CheckedException vs UnCheckedException

  CheckedException UnCheckedException
처리여부 반드시 예외를 처리 예외 처리를 강제하지 않음
확인시점 컴파일 단계 실행(Runtime) 단계
예외발생시 트랜잭션 처리 Roll-Back 하지 않음 Roll-Back 함
예외종류 Runtime Exception을 제외한 모든 예외 Runtime Exception 하위 예외

final / finally / finalize()

  • final
    • final class : 다른 클래스에서 상속할 수 없음
    • final method : 다른 메소드에서 오버라이딩 할 수 없음
    • final variable : 변하지 않는 상수값이 되어 새로 할당할 수 없는 변수가 됨
  • finally
    try-catch, try-catch-resource 구문을 사용할 때 정상적으로 작업을 한 경우와 에러가 발생했을 경우를 모두 포함하여 마무리 해줘야 하는 작업이 존재하는 경우에 해당하는 코드를 작성해주는 코드 블록

  • finalize()
    GC에 의해 호출되는 함수로 절대 호출해서는 안되는 함수. GC가 발생하는 시점이 불분명하기 때문에 해당 메소드가 실행된다는 보장이 없음

new String()과 “”의 차이

  • ”” : Java에서 문자열은 Heap 영역 String pool에서 관리하고 해당 값은 참조 값을 가지게 됨
  • new String() : Heap 영역에서 새로운 객체를 등록하게 됨