본문 바로가기
백엔드(Back-End)/면접 정리

[Java 기술면접] - 신입 Java 개발자 기술 면접 정리 (1)

by TwoJun 2023. 10. 25.

 Java / Spring 백엔드 개발 기술면접을 준비하기 위해 관련된 내용을 정리한 글입니다.

 

 

1. ~.java 파일의 컴파일 및 실행 과정

https://jungeu1509.github.io/interview/JAVA_Compile/

(1) 자바 컴파일러(javac)가 자바 소스 코드(.java)를 읽어 들여 자바 바이트 코드(.class)로 변환한다.

 

(2) Class loader를 통해 클래스 파일(.class)들을 JVM으로 로딩시킨다.

 

(3)  로딩된 클래스 파일들은 Runtime Data Area이라는 JVM 메모리 영역에 배치된다.

 

(4) Runtime Data Area에서 다양한 작업이 수행된다. 이러한 실행 과정 속에서 JVM은 필요에 따라 Thread Synchronization, Gabage collection과 같은 관리 작업을 수행한다.

 

(5) 마자막으로 바이트 코드들은 Execution Engine으로 이동되어 기계가 해석할 수 있는 기계어의 형태로 변환되고 실질적인 명령어 실행이 이루어진다.

 

 

 

 

 

2.  Compiler & Interpreter의 차이점

- 우선 자바는 컴파일러와 인터프리터를 모두 사용하는 것으로 간주된다. 따라서 이 두 개의 차이점에 대해 알아보자.

 

2-1. Compiler(컴파일러)

(1) 컴파일러의 경우 자바의 소스 코드를 자바 가상 머신이 해석할 수 있는 중간 언어의 형태인 바이트 코드로 해석하는 역할을 수행한다.

 

 

 

2-2. Interpreter(인터프리터)

(1) 바이트 코드는 Execution Engine에서 인터프리터에 의해 명령어 단위로 한 줄씩 읽어서 실질적인 명령을 실행한다.

 

(2) 한 줄씩 수행하기 때문에 속도가 느리다는 단점이 있다.

 

(3) Execution Engine은 인터프리터 방식을 개선한 JIT 컴파일러를 기능을 제공하는데, JIT 컴파일러는 인터프리터 방식으로 실행하다가 적절한 시점에 바이트 코드를 전체 컴파일하여 기계어로 변환하고 더 이상 Interpreting 하지 않은 상태에서 해석된 기계어를 직접 실행시키는 방식이다.

 

 

 

2-3. 추가적으로 JVM에 대해 궁금하다면?

(1) 하단 링크를 클릭하시면 제가 따로 정리해 놓은 JVM에 대한 포스팅이 있습니다.

https://twojun-space.tistory.com/168

 

[Java] - 자바 가상 머신, JVM(Java Virtual Machine)

Java, Kotlin 기반의 애플리케이션이 실행된다면 반드시 JVM 위에서 실행된다. 이러한 자바 가상 머신이 어떻게 동작하는지, 어떤 구성 요소로 이루어졌는지 정리해 보려고 한다. 1. JVM(Java Virtual Machi

twojun-space.tistory.com

 

 

 

 

 

3. String, StringBuilder, StringBuffer 클래스

(1) 자바에서는 문자열을 다루는 클래스로 String, StringBuffer, StringBuilder라는 세 가지 클래스를 지원하고 있다.

 

(2) 위의 3가지 클래스는 모두 문자열을 다루는 데 있어 사용된다는 공통점이 있지만 사용하고자 하는 목적, 상황에 따라 사용해야 하는 클래스는 모두 다르다. 

 

 

 

3-1. String vs StringBuffer / StringBuilder

(1) String 객체는 한 번 값이 할당되면 이후 내부 데이터를 수정할 수 없는 Immutable 객체이다.

 

(2) String은 이처럼 불변성을 가지기 때문에 변하지 않는 문자열을 자주 읽어 들일 때 사용하면 좋은 성능을 기대해 볼 수 있지만 문자열의 추가, 수정, 삭제 등이 빈번할 경우 String 클래스를 사용하면 Heap 메모리 영역에 수많은 Gabage가 발생되어 애플리케이션 성능 저하에 영향을 줄 수 있으니 주의해야 한다.

 

(2) StringBuffer /  StringBuilder 객체는 한 번 값이 할당되더라도 이후 내부의 데이터를 수정할 수 있고 할당된 공간이 변하는 Mutable 객체이다.

 

 

 

3-2. StringBuffer, StringBuilder 클래스

(1) StringBuffer, StringBuilder 두 클래스 모두 문자열을 연산할 때 주로 사용하는 클래스고 AbstractStringBuilder라는 추상 클래스를 상속받아 구현되어 있다.

 

(2) 단순 String을 통해 문자열 연산을 할 수 있지만 연산 결과값에 따른 새로운 String 인스턴스를 생성하게 되어 메모리 공간의 낭비, 속도도 느려진다는 단점이 존재한다.

 

(3) 이 부분을 해결하기 위해 자바는 문자열 연산을 따로 처리할 수 있는 클래스를 제공하는데 StringBuffer 클래스는 내부적으로 버퍼라고 하는 독립적인 공간을 갖고 문자열을 연산할 수 있다. 수정된 문자열의 크기만큼 문자열을 저장하는 공간을 유동적으로 변화시킬 수 있다. 메모리 공간의 낭비가 없고 속도가 빠르다는 장점이 있다.

 

(4) StringBuffer와  StringBuilder의 궁극적 차이는 Synchronization 유무이다.

 

(5) StringBuffer의 경우 동기화 키워드를 제공하여 멀티 스레드 환경에서 안전하다는 특징(Thread-safe)을 가지고 있고 StringBuilder는 동기화를 지원하지 않아 멀티 스레드 환경에서 사용하는 것은 적합하지 않지만 동기화를 고려하지 않는 단일 스레드 환경에서는 StringBuffer보다 뛰어나다는 특징이 있다.

 

 

 

3-3. 정리

(1) 튜닝에 따라 다른 결과들이 나올 수 있지만 일반적인 경우에는 아래와 같이 사용한다.

※ String : 문자열 연산이 적고 멀티 스레드 환경일 때

StringBuffer : 문자열 연산이 많고 멀티 스레드 환경일 경우

StringBuilder : 문자열 연산이 많고 단일 스레드이거나 동기화를 고려하지 않는 경우

 

 

 

 

 

4. String이 Immutable Object인 이유?

(1) String 객체의 경우 한 번 생성되면 변경할 수 없고 대신 새로운 String  객체가 생성되는 불변성을 가지고 있다.

 

 

4-1.  String 객체 자체의 캐싱 기능

(1) 메모리 절약을 위한 캐싱 기능을 사용하기 위함

 

(2) 초기 자바 설계 당시  String이 자바 애플리케이션에서 가장 많이 사용되는 데이터 타입이라는 것을 미리 예측했고 이에 따라 최적화의 필요성을 고려하게 됨

 

(3) 따라서 Heap 영역 내부 String Pool에 리터럴을 포함하여 String 객체를 공유하고 일시적으로 생성된 String 객체를 줄여주기 위한 것

 

(4) 결과적으로 객체가 생성되는 동작이 줄어들기 때문에 같은 문자열이 재사용되는 빈도가 높아질수록 성능 향상을 기대해 볼 수 있다.

 

 

 

4-2. 보안상의 이유

(1) String의 경우 네트워크를 연결할 때 호스트 및 포트의 형식, 파일을 읽어 들이기 위한 디렉토리 경로, 데이터베이스 연결에 필요한 URL 등 다양한 형식으로 사용되고 있기 때문에 불변 객체가 아니라면 임의적인 조작이 가능하기 때문에 보안상 큰 이슈가 발생할 수 있다.

 

(2) 이에 따라 String을 불변 객체로 설계함으로써 보안 이슈를 어느 정도 개선할 수 있다.

 

 

 

4-3. Hash

(1) 또한 문자열은 HashMap, Hashtable 같은 Hash 기반 컬렉션의 Key의 형태로 많이 사용되기 때문에 동일한 값으로 저장된 객체의 Value를 검색할 수 있도록 불변성을 가지는 것이 중요하다.

 

(2) 만약 불변하지 않아서 Key로 지정된 문자열의 내용이 수정될 경우 삽입 및 검색 시 두 개의 다른 Hash 코드를 생성하기 때문에 잠재적으로 Map에서 값 객체를 잃을 수도 있다.

 

 

 

 

 

5. Thread Safe란?

5-1. 정의

(1) 멀티 스레드 프로그래밍 환경에서 변수나 객체에 대해 여러 스레드로부터 접근이 이루어져도 프로그램을 실행하는 데 아무런 문제가 없는 상태를 의미한다. 즉 어떤 자료구조나 객체 또는 시스템이 여러 스레드로부터 동시에 접근되어도 예기치 않은 결과가 발생하지 않는 것을 말한다.

 

 

5-2. Thread-safe가 가지는 특징 : Race Condition 방지

(1) 여러 스레드가 동시에 접근할 때 서로 경쟁하는 상황인 Race Condition을 방지할 수 있다.

 

 

5-3. Thread-safe가 가지는 특징 : 상태 무결성 유지

(1) 여러 스레드가 동시에 객체의 상태를 변경하더라도 객체의 내부 상태가 유효한 상태임을 유지한다.

 

 

5-4. Thread-safe가 가지는 특징 : Dead-Lock 방지

(1) Thread-safe한 객체는 데드락과 같은 다중 스레드 환경에서 발생할 수 있는 문제를 방지한다.

 

 

5-5. Thread-safe가 가지는 특징 : Synchronization

(1) 내부적으로 필요한 경우 동기화 메커니즘이 적용되어 여러 스레드 간의 동시 접근을 조절할 수 있다.

 

 

 

 

 

6. 자바의 접근 제어자(Access Modifier)의 종류와 특징

6-1. 접근 제어자?

(1) 자바에서 접근 제어자는 클래스, 변수, 메서드 등의 멤버에 대한 접근 권한을 지정할 수 있는 키워드이다. 이를 통해 다른 클래스나 외부에서 해당 멤버에 접근할 수 있는지에 대한 여부를 결정한다.

 

 

 

6-2. 접근 제어자 비교

접근 제어자 특징
public 모든 클래스에서 접근 가능하며 다른 패키지에 속한 클래스에서도 접근 가능
protected 동일 패키지에 속한 클래스, 해당 클래스를 상속받은 다른 패키지의 클래스에서 접근 가능
default 접근 제어자를 명시하지 않은 상태, 기본적으로 해당 멤버는 동일한 패키지 내부에서만 접근 가능
private 동일 클래스 내부에서만 접근 가능, 다른 클래스에서 직접적으로 접근할 수 없다.

 

 

 

 

7. OOP(Object Oriented Programming)의 4가지 특징

(1) 객체 지향 프로그래밍이란 데이터를 넣고 순차적으로 처리 결과를 기다리는 절차 지향적인 프로그래밍에서 벗어나서 데이터와 기능을 가진 단위인 객체를 설계하고 이들 간의 유기적인 결합, 상호작용, 협력 등을 통해 주어진 비즈니스 문제를 풀어나가고자 하는 프로그래밍 패러다임을 의미한다.

 

(2) 객체 지향 프로그래밍을 통해 코드의 재사용성, 유지보수성, 확장성을 높일 수 있다.

 

(3) 객체 지향 프로그래밍이 갖는 4가지 특징은 아래와 같다.

 

 

 

7-1. Abstraction

(1) 추상화는 복잡한 시스템에서 핵심적인 개념 또는 기능을 간추려내는 것을 의미한다.

 

(2) 특정 데이터와 데이터를 다루는 메서드(기능)를 묶어 하나의 추상적인 개념으로 정의한다. 이를 통해 현실 세계의 개체나 개념을 프로그래밍 패러다임으로 녹여낼 수 있다.

 

 

 

7-2. Polymorphism

(1) 다형성의 기본적인 뜻은 "여러 가지 형태를 가질 수 있는 능력"을 말하지만, 객체 지향 프로그래밍에서의 다형성은 부모 타입으로 자식 타입의 객체를 다룰 수 있는 것을 의미한다.

 

(2) 같은 인터페이스나 추상 클래스를 상속받은 여러 클래스들이 각자 자신의 특성에 맞게 구현되어 있어도 공통된 부모 클래스 타입으로 이들을 다룰 수 있는 능력이다.

 

(3) 다형성은 코드의 유연성을 높이고 객체 간의 결합도를 낮추는 중요한 역할을 수행한다.

 

 

 

7-3. Encapsulation

(1) 캡슐화는 데이터와 해당 데이터를 다루는 메서드를 하나의 단위로 묶는 것을 의미하며 객체 내부 상태를 외부에서 직접적으로 접근하지 못하도록 제한하는 것을 말한다. 이를 통해 코드의 안전성, 유지보수성을 높일 수 있다.

 

 

 

7-4. Inheritance

(1) 상속은 기존 부모 클래스의 특성을 그대로 받아 새로운 자식 클래스를 생성하는 것을 말한다. 이를 통해 부모 클래스의 기능을 확장하거나 변경할 수 있다.

 

(2) 이를 통해 코드의 재사용성을 높이고 계층 구조를 통해 객체를 분류하고 정렬할 수 있다.

 

 

 

 

 

 

8. OOP의 5대 원칙 : SOLID

(1) 객체 지향 프로그래밍에서의 SOLID는 좋은 객체 지향적 소프트웨어를 설계할 때 유지보수성, 확장성, 가독성 등 코드의 품질을 높이기 위한 객체지향 프로그래밍에서의 중요한 5대 원칙을 말한다.

 

 

8-1. SRP(Single Responsibility Principle)

(1) 클래스는 단 하나의 책임만을 가져야 한다. 이는 클래스가 변경되는 이유는 단 하나뿐이어야 한다는 중요한 특성을 가지게 된다. 이는 클래스를 작고 관리 가능한 단위로 유지하고 유지보수를 용이하게 한다.

 

(2) 하나의 클래스에 여러 개의 책임이 있다면 해당 클래스를 수정하고자 할 때 많은 파급 효과가 따라오는 부정적인 결과가 나타난다. 따라서 하나의 클래스에 하나의 책임만 부여하여 이후 발생하는 파급 효과를 최대한 줄여야 SRP를 잘 지킨 코드가 된다.

 

 

 

8-2. OCP(Open/Closed Principle)

(1) 소프트웨어의 각 구성요소(클래스, 모듈, 메서드)는 확장에 열려 있어야 하지만 변경에는 닫혀 있어야 한다. 즉, 새로운 기능을 추가할 때는 기존 코드를 수정하지 않고 확장 가능해야 한다.

 

 

 

8-3 LSP(Liskov Substitution Principle)

(1) 하위 타입은 상위 타입으로 교체될 수 있어야 한다. 즉, 상위 클래스 타입의 객체가 사용되는 곳에 하위 클래스 타입의 객체를 대체해도 프로그램의 의미나 동작이 변하지 않아야 한다.

 

(2) 자식 타입에서 부모 타입에서 정의된 행위의 수행이 모두 이루어져야 한다는 의미이기도 하다.

 

 

 

8-4. ISP(Interface Segregation Principle)

(1) 클라이언트는 자신이 사용하지 않는 메서드에 의존하면 안 된다. 즉, 하나의 범용적인 인터페이스보다는 클라이언트의 목적에 맞게 효율적인 인터페이스를 제공해야 한다는 것.

 

 

 

8-5. DIP(Dependency Inversion Principle)

(1) 고수준 모듈은 저수준 모듈에 의존하면 안 된다. 두 개의 모듈 모두 추상화된 것에 의존해야 한다. 즉, 의존 관계가 추상화에 의해 정의되어야 하며 구체적인 구현에 의존하면 안 된다.

 

(2) 구현 클래스에 의존하지 않고 추상화된 인터페이스에 의존해야 한다.

 

 

 

 

 

 

9. JVM 구조 

(1) 하단 링크를 클릭하시면 제가 따로 정리해 놓은 JVM에 대한 포스팅이 있습니다.

https://twojun-space.tistory.com/168

 

 

 

 

 

10. 캡슐화(Encapsulation)와 은닉화(Information Hiding)의 차이?

(1) 캡슐화와 은닉화는 밀접한 관련이 있는 개념으로 볼 수 있지만 약간의 차이점이 존재한다.

 

(2) 캡슐화는 관련 있는 데이터와 메서드를 하나로 묶어서 객체를 형성하고, 객체의 내부 구현 방식을 외부로부터 보호하는 것, 정보 은닉은 객체의 상태와 구현을 외부로부터 감춤으로써 오직 공개된 인터페이스만을 통해서만 객체로 접근할 수 있게 해 주는 것을 말한다.

 

(3) 정보 은닉화는 캡슐화의 일부로써 객체의 내부를 보호하여 외부에서의 무분별한 접근을 막는 데 초점이 맞춰져 있다.

 

 

 

 

 

11. 클래스, 객체, 인스턴스의 차이

(1) 클래스란 객체를 정의할 수 있는 설계도라고 보면 된다.

 

(2) 객체는 클래스의 인스턴스로 실제 소프트웨어 세계에 구현할 대상이며 인스턴스를 포함하는 일반적인 의미이다.

 

(3) 인스턴스는 실제 이러한 객체가 소프트웨어 세계에 구현된 대상을 의미하고 메모리에 로드되어 실행될 수 있는 상태를 의미한다.

 

 

 

 

 

12. 추상 클래스(Abstract Class)와 인터페이스(Interface)의 공통점,  차이점

(1) 추상 클래스와 인터페이스는 모두 객체 지향 프로그래밍에서 다형성을 구현하기 위한 방법이지만 목적과 사용법에 따라 중요한 차이점이 존재한다.

 

 

11-1. 추상 클래스

(1) 추상 클래스는 하나의 추상 메서드를 포함할 수 있는 클래스를 의미한다 따라서 일반적인 메서드와 멤버 변수를 포함할 수 있다.

 

(2) 추상 메서드는 실제로 선언만 되어 있고 실제 구현은 서브 클래스에서 이루어진다.

 

(3) 직접적인 인스턴스 생성은 불가능하다. 해당 추상 클래스를 상속받은 구현 클래스에서 인스턴스를 생성해야 한다.

 

 

 

11-2. 추상 클래스의 용도

(1) 공통된 코드나 필드를 여러 클래스에 상속해야 할 때, 서브 클래스에게 구현을 강제하고자 할 때 사용한다.

 

 

 

11-3. 인터페이스

(1) 인터페이스는 상수와 추상 메서드만으로 이루어진 집합을 의미한다.

 

(2) 상수와 static final 필드만을 가질 수 있다.

 

(3) 다중 상속이 가능하고 여러 인터페이스를 구현할 수 있다.

 

 

 

11-4. 인터페이스의 용도

(1) 다중 상속이 필요하거나 다양한 클래스에게 동일한 메서드의 명세를 부여하고자 할 때 사용한다.

 

(2) 클래스 간의 공통된 동작, 다양한 객체를 동일한 인터페이스를 통해 사용하고자 할 때 사용한다.

 

 

 

11-5. 추상 클래스와 인터페이스의 공통점, 차이점

(1) 공통점

- 메서드의 선언만 존재하고 모두 구현 내용이 존재하지 않는다.

 

- 단독으로 객체 생성이 불가능하다.

 

 

(2) 차이점

- 추상 클래스는 extends를 통해 상속하고 다중 상속은 불가능하다.

 

- 추상 클래스는 이를 상속할 객체들의 공통점을 찾아 추상화한 클래스로, 상속 관계를 타고 올라가면 같은 부모 클래스를 상속한다. 일반적으로 부모 클래스가 가진 기능을 구현해야 하는 경우에 사용한다.

 

- 추상 클래스는 자신의 기능을 하위로 확장시키고자 할 때 사용하고 인터페이스는 정의된 메서드를 각 클래스의 목적에 맞게 구현할 때 사용한다. 

 

- 인터페이스는 implements를 통해 구현하고 다중 상속이 가능하다.

 

- 인터페이스는 상속 관계를 타고 올라갔을 때 다른 부모 클래스를 상속하더라도 같은 기능이 필요한 경우에 사용한다.

 

- 최종적으로 정리해 보면, 자바는 한 개의 클래스만 상속이 가능하기 때문에 해당 클래스의 구분을 추상 클래스의 상속을 통해서 해결하고 할 수 있는 공통된 기능들을 인터페이스의 다중 상속을 통해 구현한다.

 

 

 

 

 

 

12. CheckedException, UncheckedException의 차이점

https://youn12.tistory.com/32

12-1. CheckedException (체크 예외)

(1) 컴파일러가 강제적으로 예외 처리를 요구하는 예외를 의미한다.

 

(2) java.lang.Exception 클래스를 상속하고 주로 외부 리소스와의 상호작용(파일 입출력, 네트워크 연결 등) 과정에서 발생하며 예외 상황을 처리하는 것이 바람직한 경우에 사용된다.

 

(3) try-catch 블록을 통해 예외를 처리하거나 예외를 던질 수 있다면 throws 키워드로 예외를 전파시킨다.

 

(4) IOException, SQLException 등이 CheckedException의 대표적인 예외이다.

 

 

 

12-3. UncheckedException (언체크 예외, 런타임 예외)

(1) 컴파일러가 예외 처리를 강제하지 않는 예외를 의미한다. 주로 프로그램의 논리나 런타임 환경에서 발생하는 예외로 개발자의 실수나 예기치 못한 상황에서 발생한다.

 

(2) java.lang.RuntimeException 클래스를 상속한다.

 

(3) 예외를 처리하지 않아도 컴파일이 가능하다. 하지만 예외가 발생한 경우 해당 프로그램은 종료된다.

 

(4) NullPointerException, ArrayIndexOutOfBoundsException 등이 대표적인 예외이다.

 

 

 

 

 

13. Call by Value, Call by Reference의 차이점

13-1. Call by Value

(1) 메서드 호출 시 전달되는 것은 변수의 값 자체임을 의미한다.

 

(2) 전달받은 값을 복사하여 처리한다. 전달받은 값을 변경해도 원본은 변경되지 않는다.

 

 

 

13-2. Call by Reference

(1) 메서드 호출 시 참조(주소)를 직접 전달한다.

 

(2) 전달받은 값을 직접 참조하기 때문에 전달받은 값을 변경할 경우 원본도 함께 변경된다.

 

 

 

13-3. 결론

(1) 자바에서는 Call by Reference가 존재하지 않는다. 모든 건 Call by Value로 처리된다.

 

 

 

 

 

 

14. 오버로딩(Overloading), 오버라이딩(Overriding)의 차이

14-1. 오버로딩

(1) 오버로딩은 하나의 클래스 내부에 동일한 이름을 가진 메서드를 여러 개 정의하는 것을 말한다.

 

(2) 매개변수의 개수나 타입이 달라야 한다.

 

 

 

14-2. 오버라이딩

(1) 부모 레벨의 클래스에서 이미 정의된 메서드를 하위 클래스에서 목적에 맞게 재정의하는 것을 말한다.

 

(2) 메서드의 이름, 매개변수, 반환 타입이 동일해야 한다.

 

(3) 접근 제어자는 부모 레벨보다 확장할 수 있지만 축소는 불가능하다.

 

(4) 부모 클래스의 메서드가 final, private인 경우 오버라이딩할 수 없다.

 

 

 

 

 

※ 해당 포스팅에 대해 내용 추가가 필요하다고 생각되면 기존 포스팅 내용에 다른 내용이 추가될 수 있습니다.

개인적으로 공부하며 정리한 내용이기에 오타나 틀린 부분이 있을 수 있으며, 이에 대해 댓글로 알려주시면 감사하겠습니다!

댓글