[백엔드 개발자 면접] Java

diense_kk·2024년 11월 23일
0

Interview

목록 보기
1/8

신입 개발자 기술면접 질문 정리 - 자바

Java의 특징

Java는 객체지향 프로그래밍 언어이다.

  1. 기본 자료형을 제외한 모든 요소들이 객체로 표현되고, 객체 지향 개념의 특징인 캡슐화, 상속, 다형성이 잘 적용된 언어이다.

  2. 기본 자료형들은 메모리 효율성을 위해 객체로 표현되지 않으며, 값을 직접 메모리에 저장합니다. 하지만 자바는 각 기본 자료형에 대응하는 Wrapper 클래스를 제공하여 기본 자료형을 객체로 다룰 수 있게 한다.

기본 자료형 - byte, int, char, boolean 등이 있다.

자바의 장점

  • JVM 위에서 동작하기 때문에 운영체제에 독립적이다.
  • GC를 통한 자동적인 메모리 관리가 가능하다. (개발자가 아닌 JVM이 관리)

단점

  • JVM 위에서 동작하기 때문에 실행 속도가 상대적으로 느리다.

😲 속도가 느린이유는❓
일반 애플리케이션의 코드는 OS만 거치고 하드웨어로 전달되는데, Java 애플리케이션은 JVM을 한 번 더 거칠뿐 아니라 하드웨어에 맞게 완전히 컴파일 된 상태가 아닌, 실행 시에 해석(interpret)되기 때문에 속도가 느리다는 단점이 있다. JVM이 컴파일하여 생성한 byte code를 사용하긴 하지만, JVM이 실행중에 Byte Code를 Native Code(Machine Code)로 변환하는 시간을 필요로 하기 때문이다. 그러나 기계어로 빠르게 변환해주는 JVM 내부의 최적화된 JIT 컴파일러를 통해서 속도의 격차는 많이 줄어들고 있다.

JVM의 역할

JVM은 스택 기반으로 동작하며, Byte Code를 OS에 맞게 해석(Native Code로 변환) 해주는 역할을 하고 가비지컬렉션을 통해 자동적인 메모리 관리를 해줍니다.

자바 컴파일 순서

  1. 개발자가 자바 코드(.java) 작성

  2. 자바 컴파일러가 자바 소스코드를 컴파일.
    이때 나오는 파일은 자바 바이트 코드(.class)파일로 아직 컴퓨터가 읽을 수 없는 JVM이 이해할 수 있는 코드이다.

  3. 컴파일 된 바이트 코드를 JVM의 클래스로더(Class Loader)에게 전달

  4. 클래스 로더는 동적로딩을 통해 필요한 클래스들을 로딩 및 링크하여 런타임 데이터 영역, 즉 JVM의 메모리에 올린다. (바로 메모리 영역에 올리지 않음)

동적 로딩이란, 필요한 시점에 클래스 파일을 메모리에 로드하는 것을 의미합니다. 이를 통해 프로그램 실행 시 불필요한 메모리 사용을 줄이고, 효율적으로 클래스를 관리할 수 있습니다.

  1. 실행엔진(Execution Engine)은 JVM 메모리에 올라온 바이트 코드들을 명령어 단위로 하나씩 가져와서 실행한다. 실행 엔진은 두가지 방식으로 변경한다.

    1. 인터프리터: 바이트코드를 한 줄씩 해석하고, 즉시 실행다. 빠르게 실행을 시작할 수 있지만, 반복되는 코드에서 비효율적일 수 있다.
    2. JIT(Just-In-Time) 컴파일러: 반복적으로 실행되는 바이트코드를 기계어로 컴파일하여, 다음 실행 시 더 빠르게 처리. JIT 컴파일러가 최적화된 기계어 코드로 변환하기 때문에 성능을 향상시킬 수 있습니다.

Java Version

8, 11, 17, 21버전이 LTS 버전으로
LTS 버전은 Long Term Support의 약자로 출시 후 일반적으로 8년이라는 긴 기간동안 보안 업데이트와 버그 수정을 지원할 것임을 선언한 버전이다.

자바 8

1) Lambda
함수를 보다 단순하게 표현하는 방법이다.
2) Stream
람다를 활용해 배열과 컬렉션을 함수형으로 간단하게 처리할 수 있는 기술이다.
3) Interface Default Method
인터페이스에 구현 메서드를 정의 가능하다.
4) Optional
NullPointerException이 발생하지 않도록 도와준다.
5) new Date And Time API(LocalDateTime, ...)

자바 11

1) String과 File 기능 향상
String -> isBlank(), strip() File -> writeString(), readString()
2) var 키워드 사용 가능
지역 변수의 타입 추론을 위한 키워드로, 변수 선언시에 타입을 생략 가능하다.

자바 17

1) Spring Boot 3.x.x 버전은 JDK 17 이상부터 지원
2) record class 도입
불변 객체를 쉽게 생성할 수 있도록 하는 유형의 클래스이다.
record class를 사용하면 생성자 메서드, Getter 메서드, equals 메서드, hashcode 메서드, toString 메서드를 컴파일 타임에 컴파일러가 코드를 추가해준다.

자바 21

1) Spring Boot 3.2 버전부터 지원
2) 가상 스레드
3) UTF-8이 기본값으로 사용

왜 8버전을 가장 많이 사용할까?

자바 8 사용 비율이 72%에 육박한다.
자바 8을 사용하는 가장 큰 이유는 가장 기본적인 자바 기능이 구축되어 있는 버전이기 때문이다.
라이센스 문제, 버전 8의 안정성 및 호환성, 첫 LTS 버전이라는 이유로 가장 많이 사용된다.

오버라이딩과 오버로딩

오버라이딩(Overriding)은 부모 클래스에 있는 메서드를 자식 클래스에서 재정의 하는 것이다.
오버로딩(Overloading)은 매개변수의 개수나 타입을 다르게 하여 같은 이름의 메서드를 여러 개 정의하는 것을 말한다.
오버로딩의 장점 - 생성자 오버로딩, 코드의 가독성이 좋아짐

객체지향이란?

객체란 값을 저장할 변수와 작업을 수행할 메서드를 서로 연관된 것들끼리 묶어서 만든 것을 의미한다.
객체지향이란 이러한 객체를 만들고 객체 간의 상호작용을 통해 로직을 구성하는 프로그래밍 방법이다.
즉, 기능이 아닌 객체가 중심이며 "누가 어떤 일을 할 것인가?"가 핵심이다.
특징으로는 캡슐화, 상속, 다형성, 추상화 등이 있고, 모듈 재사용으로 확장 및 유지보수가 용이하다.

  • 추상화 : 불필요한 정보는 숨기고 중요한 정보만을 표현함으로써 프로그램을 간단하게 만드는 것
  • 캡슐화 : 변수와 함수를 하나로 묶어서 낮은 결합도를 유지할 수 있도록 설계하는 것
  • 다형성 : 하나의 변수명, 메서드명이 상황에 따라 다른 의미로 해석 될 수 있는 것
  // 다형성 적용
  Animal cat = new Cat();
  Animal dog = new Dog();

  // 같은 메서드 호출, 하지만 출력은 다름
  cat.makeSound(); // "Meow"
  dog.makeSound(); // "Bark"
  • 상속 : 새로운 클래스가 기존의 클래스의 변수와 메서드를 이용할 수 있게 하는 기능
    상속의 장/단점
    장점 - 재사용으로 인한 코드가 줄어든다.
    단점 - 상위 클래스의 변경이 어려워진다.

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

장점 - 클래스를 재사용하거나 상속을 통해 확장함으로써 코드 재사용이 용이하다.
단점 - 처리 속도가 상대적으로 느리다, 설계시 많은 시간과 노력이 필요하다.

try-catch-resources

try-catch-resources는 try-catct-finally의 문제점을 보완하기 위해 나온 개념이다.
try(FileInputStream is = new FileInputStream("file.txt")) 이런식으로 자원 객체를 전달하면, try 블록이 끝나고 자동으로 자원을 해제해준다.
finally 구문이나 모든 catch 구문에 종료 처리를 하지 않아도 되는 장점이 있다.

불변 객체란?

불변 객체는 객체 생성 이후 내부의 상태가 변하지 않는 객체를 말한다.
Java에서는 필드가 원시 타입인 경우 final 키워드를 사용해 불변 객체를 만들 수 있다.

불변 객체나 final 키워드를 사용하면 Thread-Safe하여 병렬 프로그래밍에 유용하며, 동기화를 고려하지 않아도 된다.
(공유 자원이 불변이기 때문에 항상 동일한 값을 반환하기 때문이다.)

추상 클래스와 인터페이스

추상 클래스는 클래스 내 추상 메서드가 하나 이상 포함되거나 클래스 자체에 abstract로 정의된 경우를 말한다.
인터페이스는 모든 메서드가 추상 메서드로만 이루어져 있는 것을 말한다.

공통점

  • new 연산자로 인스턴스 생성 불가능
  • 사용하기 위해서는 하위 클래스에서 확장/구현 해야한다.

차이점

  • 인터페이스는 그 인터페이스를 구현하는 모든 클래스에 대해 인터페이스의 모든 메서드가 반드시 존재하도록 강제함에 있다.
  • 추상클래스는 상속받는 클래스들의 공통적인 로직을 추상화 하고, 기능 확장을 위해 사용한다.
  • 추상 클래스는 다중 상속이 불가능, 인터페이스는 다중 상속 가능

리플렉션

구체적인 Class Type을 알지 못하더라도 해당 Class의 method, type, Variable에 접근할 수 있도록 해주는 자바 API이며, 컴파일된 바이트 코드를 통해 런타임에 동적으로 특정 클래스의 정보를 추출할 수 있는 프로그래밍 기법이다.

주로 동적으로 Class를 사용해야 하는 경우 사용된다. (파라미터를 기준으로 특정 객체를 반환해주는 경우)

ClassA class = Class.forName("ClassA");

정적 바인딩 VS 동적 바인딩

바인딩이란 프로그램에 사용된 구성요소의 실제 값 또는 프로퍼티를 결정짓는 행위를 의미한다.
바인딩을 결정짓는 시점에 따라 정적 바인딩과 동적 바인딩으로 나뉜다.

정적 바인딩

1) 컴파일 시점에 결정
2) 프로그램이 실행돼도 변하지 않음
3) 오버로딩
4) private, static, final이 붙은 메서드

동적 바인딩

1) 런타임 시점에 결정
2) 늦은 바인딩이라고도 부름
3) 오버라이딩
4) Java에서의 다형성, 상속이 가능한 이유

정적 바인딩을 대표하는 예제가 오버로딩이다. 컴파일 과정에서 어떤 메서드를 호출할지 결정하기 때문에 코드 작성 단계에서 어떤 메서드를 사용할지 구분할 수 있고 잘못 사용했을 경우 컴파일 에러를 발생시킨다.

동적 바인딩을 대표하는 예제는 오버라이딩이다. 상속을 이용한 부모-자식 관계일 경우 부모에서 정의한 메서드를 자식에서 오버라이딩 했다 가정했을 때, 해당 메서드를 이용할 경우 런타임에 어떤 메서드를 호출할지 결정된다.

동적 바인딩은 실행 시간에 바인딩 되기 떄문에, 정적 바인딩 보다는 성능상 오버헤드가 있지만, 동적 바인딩을 통해 상속과 다형성 등 다양한 기능을 사용할 수 있는 장점이 있다.

Reflection은 런타임에 Class Type을 모르는 채로 객체를 생성하고 이용하기 떄문에 동적 바인딩을 제공한다.

리플렉션 사용 방법

Reflection은 Class/Interface, Constructor, Method, Field 와 같은 정보를 가져올 수 있으며, 해당 정보들을 통해 객체 생성, 메서드 호출, 변수 값을 변경할 수 있다.

    Class car = Class.forName("com.reflection.test.Car");
    
    Car realCar = car.newInstance();

싱글톤 패턴

싱글톤 패턴은 단 하나의 인스턴스를 생성해 사용하는 디자인 패턴이다.
인스턴스가 1개만 존재해야 한다는 것을 보장하고 싶은 경우와 동일한 인스턴스를 자주 생성해야 하는 경우에 주로 사용한다.(메모리 낭비 방지)

대표적인 예시

싱글톤 패턴의 대표적인 예시는 Spring Bean이다.
스프링 컨테이너에 등록된 모든 빈들은 싱글톤으로 관리하고, 이후 필요한 곳에 DI를 통해 자동으로 할당된다.

가바지 컬렉션(GC)

가비지 컬렉션은 JVM의 메모리 관리 기법 중 하나로 시스템에서 동적으로 할당됐던 메모리 영역 중에서 필요없어진 메모리 영역을 회수하여 메모리를 관리해주는 기법이다.

가비지 컬렉션 과정

가비지 컬렉션 작업을 수행하기 위해 JVM이 애플리케이션 실행을 잠시 멈추고, GC를 실행하는 쓰레드를 제외한 모든 쓰레드들의 작업을 중단 후, 사용하지 않는 메모리를 제거하고 작업이 재개된다.

객체지향의 설계원칙(SOLID)

SRP - 단일 책임 원칙 : 한 클래스는 하나의 책임만 가져야 된다. 여기에서 "책임"이란 변경의 이유를 의미한다. 즉, 클래스는 단일 기능만을 수행해야 하며, 변경이 필요한 이유가 단 하나여야 된다.

OCP - 개방-폐쇄 원칙 : 확장에는 열려있고, 수정에는 닫혀있어야 된다. 즉, 기존의 코드를 변경하지 않으면서 새로운 기능을 추가 할 수 있도록 설계되어야 한다는 원칙이다.

LSP - 리스코프 치환 원칙 : 자식 클래스는 최소한 부모 클래스에서 가능한 메서드의 수행이 모두 보장되어야 된다. 예를 들어 자동차 인터페이스에서 액셀 기능은 앞으로만 움지이는 기능이고 만약 뒤로 움직이도록 구현했다면 이는 LSP를 위반한 코드이다.

ISP - 인터페이스 분리 원칙 : 인터페이스를 사용 목적에 맞게 분리할 수 있어야 된다는 원칙으로, 범용적인 인터페이스보다는 클라이언트의 목적에 맞게 효율적으로 인터페이스를 분리하여 제공해야 된다.

DIP - 의존관계 역전 원칙 : 구체적인 클래스보다 상위 클래스, 인터페이스, 추상클래스와 같이 변하지 않을 가능성이 높은 클래스와 관계를 맺는 것이다.

//private MemberRepository memberRepository = new MemberRepository();
private MemberRepository memberRepository = new JdbcMemberRepository();

클래스와 객체

클래스는 객체를 만들어내기 위한 설계도 혹은 틀이다.
객체는 설계도(클래스)를 기반으로 생성되며, 자신의 고유 이름과 상태, 행동을 갖는다.
여기서 상태는 필드, 행동은 메서드라고 표현한다.
객체에 메모리가 할당되어 실제로 활용되는 실체는 인스턴스라고 부른다.

생성자

생성자는 클래스와 같은 이름의 메서드로, 객체가 생성될 때 호출되는 메서드이다.
명시적으로 생성자를 만들지 않아도 default로 만들어지며, 생성자는 파라미터를 다르게하여 오버라이딩 할 수 있다.
이때, 생성자 메서드가 하나 이상 작성되어 있다면, JVM은 기본생성자가 없어도 기본 생성자를 자동으로 생성하지 않습니다.

new String()과 리터럴("")의 차이

new String()은 new 키워드로 새로운 객체를 생성하기 때문에 Heap 메모리 영역에 저장된다.
""는 Heap 안에 있는 String Constant Pool 영역에 저장된다.

String, StringBuffer, StringBuilder

String은 불변의 속성을 가지지만 StringBuffer와 StringBuilder는 가변의 속성을 가진다.
StringBuffer는 동기화를 지원하여 멀티 쓰레드 환경에서 주로 사용된다.
StringBuilder는 동기화를 지원하지 않아 싱글 쓰레드 환경에서 주로 사용된다.

String이 불변인 이유는 보안, 복사가 필요 없는 빠른 재사용성, 동기화 때문이다.

  • 보안 - String은 주로 Host, Port 등의 민감한 정보를 저장하기 위해 사용됨

  • 복사가 필요 없는 빠른 재사용성 - Strin을 String Constant Pool에서 관리하여 Heap 영역의 메모리를 절약할 수 있다. 같은 값에 대해서는 String 객체를 다시 만들지 않고 이미 존재하는 객체를 참조할 수 있기 때문이다.

  • 동기화 - 스레드가 값을 변경하면 동일한 String을 수정하는 대신 String Constant Pool에 새 문자열이 생성되기 때문에 멀티스레드 환경에서 Thread-safe 하다는 장점이 있다.

접근 제한자(Access Modifier)

변수 또는 메서드에 접근 범위를 설정하기 위해서 사용한다.
public - 접근 제한이 없다.
protected - 해당 패키지 내, 다른 패키지에서 상속받아 자식 클래스에서 접근 가능하다.
default - 해당 패키지 내에서만 접근 가능
private - 해당 클래스에서만 접근 가능

클래스 멤버 변수 초기화 순서

static 변수 -> 필드 변수 -> 생성자

static

static 키워드를 사용한 변수나 메소드는 객체를 생성하지 않아도 사용 가능하다.

static 사용 이유
static은 자주 변하지 않는 값이나 공통으로 사용되는 값 같은 공용자원에 대한 접근에 있어서 매번 메모리에 로딩하거나 값을 읽어들이는 것보다 일종의 "전역변수"와 같은 개념을 통해 접근하는 것이 비용도 줄이고 효율을 높일 수 있다.

Main 메서드가 static인 이유
Java의 main 메서드가 static인 이유는 main 메서드가 JVM에서 프로그램을 실행하기 위한 진입점(Entry Point)이기 때문이다.
프로그램 시작시 JVM이 독립적으로 실행 흐름을 관리해야 된다.
프로그램 시작시 아직 어떤 객체도 생성되지 않았기 때문에, main 메서드는 객채에 의존하지 않고 호출할 수 있는 클래스 수준의 메서드여야 된다.

Inner Class(내부 클래스)

Inner Class는 하나의 클래스 내부에 선언된 또 다른 클래스를 의미한다.
주로 하나의 클래스 또는 메소드에서만 사용되는 클래스일 때 사용된다.

Inner Class의 장점
1. 내부 클래스에서 외부 클래스의 변수나 메서드에 쉽게 접근 가능하다.
2. 외부에서는 내부 클래스에 접근할 수 없으므로, 코드의 보안성을 높일 수 있다.

Error와 Exception의 차이

Error는 실행 중 일어날 수 있는 치명적 오류를 의미한다. 컴파일 시점에 체크할 수 없고, 오류가 발생하면 프로그램은 비정상 종료되며 예측 불가능한 UncheckedException에 속한다.
Exception은 Error보다 비교적 경미한 오류이며, try-catch를 이용해 프로그램의 비정상 종료를 막을 수 있다.

CheckedException, UnCheckedException

CheckedException은 실행하기 전에 예측 가능한 예외를 말하고, 반드시 예외 처리를 해야된다.

  • 대표적으로 IOException, ClassNotFoundException 등이 있다.
    UnCheckedException은 실행하고 난 후에 알 수 있는 예외를 말하고, 따로 예외처리를 하지 않아도 된다.
  • 대표적으로 NullPointerException, ArrayIndexOutOfBoundException 등이 있다.

RuntimeException은 UnCheckedException을 상속한 클래스이다. RuntimeException이 아닌 것은 CheckedException을 상속한 클래스이다.

Optional API

개발할때 가장 많이 발생하는 예외 중 하나가 NPE(NullPointerException)이다.
NPE를 피하려면 null 여부를 필연적으로 검사 하게 되는데 만약 null 검사를 해야하는 변수가 많은 경우 코드가 복잡해진다. 하지만 Optional을 사용하면 null로 인한 예외가 발생하지 않도록 도와주고 Optional 클래스의 메서드를 통해 null을 컨트롤 할 수 있다.

컬렉션 프레임워크

자바 컬렉션에는 List, Set, Map 인터페이스를 기준으로 여러 구현체가 존재하고, Stack, Queue 인터페이스도 존재한다.

  • List는 순서가 있는 데이터의 집합이며, 데이터의 중복을 허용한다. 대표적인 구현체로는 ArrayList가 있고, 이는 Vector를 개선한 것이다.
  • Set은 순서가 없는 데이터의 집합이며, 데이터의 중복을 허용하지 않는다. 대표적인 구현체로는 HashSet이 있고, 순서를 보장하기 위해서는 LinkedHashSet을 사용한다.
  • Map은 키와 값이 한 쌍으로 이루어져 있고, 키를 기준으로 중복을 허용하지 않으며, 순서가 없다. Key의 순서를 보장하기 위해서는 LinkedHashMap을 사용한다.

제네릭

제네릭은 데이터의 타입을 하나로 지정하지 않고 사용할 때마다 범용적이고 포괄적으로 지정한다는 의미이다.
제네릭 타입을 사용하면 잘못된 타입이 사용될 수 있는 문제를 컴파일 과정에서 제거할 수 있어 에러를 사전에 방지할 수 있다.

0개의 댓글