[백엔드 면접 준비] - JAVA

이민재·2022년 12월 28일
1

CS 준비

목록 보기
1/3
  • JVM의 구조와 Java의 실행방식을 설명해주세요.

    자바 가상 머신의 약자를 따서 줄여 부르는 용어로 JVM의 역할은 자바 애플리케이션을 클래스 로더를 통해 읽어 자바 API와 함께 실행하는 것입니다. 메모리 관리(GC)을 수행하며 스택기반의 가상머신입니다.

    JVM의 구조는 Class Loader, Execution engine, Runtime Data Area, JNI, Native Method Library로 이루어져 있습니다.

    • 클래스 로더: JVM내로 클래스를 로드하고, 링크를 통해 배치하는 작업을 수행하는 모듈

    • 실행 엔진: 바이트 코드를 실행시키는 역할

    • 인터프리터: 바이트 코드를 한줄 씩 실행합니다.

    • JIT 컴파일러: 인터피르터 효율을 높이기 위한 컴파일러로 인터프리터가 반복되는 코드를 발견하면 JIT 컴파일러가 반복되는 코드를 네이티브 코드로 바꿔줍니다. 그 다음부터 인터프리터는 네이티브 코드로 컴파일된 코드를 바로 사용합니다.

    • GC(Garbage Collector): 가비지 컬렉터로 힙 영역에서 사용되지 않는 객체들을 제거하는 작업을 의미합니다.

    • Runtime Data Areas: 프로그램 실행 중에 사용되는 다양한 영역입니다.

    • PC Register: Thread가 시작될 때 생성되며 현재 수행 중인 JVM 명령의 주소를 갖고 있습니다.

    • Stack Area: 지역 변수, 파라미터 등이 생성되는 영역. 실제 객체는 Heap에 할당되고 해당 레퍼런스만 Stack에 저장됩니다.

    • Heap Area: 동적으로 생성된 오브젝트와 배열이 저장되는 곳으로 GC의 대상 영역입니다.

    • Method Area: 클래스 멤버 변수, 메소드 정보, Type 정보, Constant Pool, static, final 변수 등이 생성됩니다. 상수 풀(Constant Pool)은 모든 Symbolic Reference를 포함하고 있습니다.

    • JNI(Java Native Interface): 자바 애플리케이션에서 C, C++, 어셈블리어로 작성된 함수를 사용할 수 있는 방법을 제공해줍니다. Native 키워드를 사용하여 메서드를 호출합니다. 대표적인 메서드는 Thread의 currentThread()입니다.

    • Native Method Library: C, C++로 작성된 라이브러리 입니다.

    Java의 실행방식

    • 자바 컴파일러(javac)가 자바 소스코드(.java)를 읽어 자바 바이트코드(.class)로 변환시킵니다.

    • Class Loader를 통해 class 파일들을 JVM으로 로딩합니다.

    • 로딩된 class파일들은 Execution engine을 통해 해석됩니다.

    • 해석된 바이트코드는 Runtime Data Areas 에 배치되어 실질적인 수행이 이루어집니다.

  • GC가 무엇인지, 필요한 이유는 무엇인지, 동작방식에 대해 설명해주세요.

    GC는 힙 영역에서 사용하지 않는 객체들을 제거하는 작업을 총칭합니다. 이 객체를 제거하는 작업이 필요한 이유는 자바는 개발자가 메모리를 직접 해제해줄 수 없는 언어이기 때문입니다. 따라서 객체를 사용하고 제거하는 기능이 필요하게 됩니다.

  • 컬렉션 프레임워크에 대해서 설명해주세요.

    Java Collection은 널리 알려져 있는 자료구조를 바탕으로 객체, 데이터들을 효율적으로 관리 할 수 있는 자료구조들이 있는 라이브러리를 컬렉션 프레임워크라고 합니다.

  • 제네릭에 대해서 설명해주세요.

    제네릭은 자바의 타입 안정성을 맡고 있습니다. 컴파일 과정에서 타입체크를 해주는 기능으로 객체의 타입을 컴파일 시에 체크하기 때문에 객체의 타입 안정성을 높이고 형변환의 번거로움을 줄여줍니다.

  • 애노테이션에 대해서 설명해주세요.

    애노테이션은 인터페이스를 기반으로 한 문법으로 주석처럼 코드에 달아 클래스에 특별한 의미를 부여하거나 기능을 주입할 수 있습니다

  • 오버라이딩과 오버로딩이 무엇이며 어떤 차이가 있을까요?

    오버라이딩상위 클래스의 메소드를 재정의 하는 것을 의미합니다. 또, 런타임 다형성이기도 합니다.

    오버로딩같은 클래스 내에서 동일한 메소드 이름을 가지지만, 매개변수의 타입, 개수가 다르게 구현할 수 있는 것을 의미하며 컴파일 타임 다형성이기도 합니다. 따라서 오버라이딩 될 수 있습니다.

    추가로 @Override를 써야하는 이유를 꼭 생각해보세요. 이 애노테이션은 컴파일 타임에 오버라이딩에 대한 안정성을 부여해주기 때문에 반드시 써주는 것이 좋습니다.

  • 인터페이스와 추상클래스의 차이점에 대해 설명해주세요.

    추상클래스객체의 추상적인 상위 개념으로 공통된 개념을 표현할 때 사용합니다. 단일 상속만 가능합니다. 추상클래스를 상속하는 집합간에는 연관관계가 있습니다.

    인터페이스구현 객체가 같은 동작을 한다는 것을 보장하기 위해 사용합니다. 다중 상속이 가능합니다. 인터페이스를 구현하는 집합간에는 관계가 없을 수 있습니다.

  • 클래스는 무엇이고 객체는 무엇인가요?

    클래스는 객체를 정의하는 틀 또는 설계도와 같은 의미로 사용됩니다.

    객체는 식별 가능한 개체 또는 사물입니다. 객체는 구별 가능한 식별자, 특징적인 행동, 변경 가능한 상태를 가집니다. 인스턴스들을 통칭하는 용도로 사용합니다.

  • 정적(static)이란 무엇인가요?

    static은 클래스 멤버라고 하며, 클래스 로더가 클래스를 로딩해서 메소드 메모리 영역에 적재할 때 클래스별로 관리됩니다.

    static 키워드를 통해 생성된 정적멤버들은 PermGen 또는 Metaspace에 저장되며 저장된 메모리는 모든 객체가 공유하며 하나의 멤버를 어디서든지 참조할 수 있는 장점이 있습니다.

    그러나, GC의 관리 영역 밖에 존재하기 때문에 프로그램 종료시까지 메모리가 할당된 채로 존재합니다. 너무 남발하게 되면 시스템 성능에 악영향을 줄 수 있습니다.

  • 자바의 원시타입들은 무엇이 있으며 각각 몇 바이트를 차지하나요?

  • 접근 제어자의 종류와 이에 대해 설명해주세요.

    private, default, protected, public이 있습니다. private은 해당 클래스 내에서만 접근 가능하고, default는 해당 패키지, protected는 상속한 클래스, public은 전체 영역에서 접근 가능합니다.

    접근 제어자를 사용하는 이유는 외부에 보여주고 싶은 정보들을 선택적으로 제공하기 위함이고, 캡슐화와 통하는 면이 있습니다.

  • 객체지향에 대해서 설명해주세요.

    객체지향을 정의하면, 의존성 관리입니다.

    객체지향으로 의존성을 관리함으로써 변경 영향을 최소화하고 독립적인 배포가 가능해지며 독립적인 개발이 가능해집니다. 따라서 객체지향에서 가장 중요한 것은 DIP(Dependency Inversion Principle)를 통한 고수준 정책(High Level Policy)와 저수준 구현 세부사항(Low Level Details)의 분리라고 할 수 있습니다.

  • SOLID(객체지향 5대원칙)에 대해서 설명해주세요.

    SRP(단일책임원칙)은 한 클래스의 하나의 책임만 가져야 합니다.

    OCP(개방-폐쇄 원칙)은 확장에는 열려 있으나 변경에는 닫혀 있어야 하며, 다형성을 활용해야 합니다.

    LSP(리스코프 치환 원칙)은 프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야하는 원칙으로 상위 타입을 상속해서 재정의 했을 때 프로그램이 깨지지 않아야 합니다.

    ISP(인터페이스 분리 원칙)은 클라이언트는 자신이 사용하지 않는 메서드에 의존 관계를 맺으면 안되는 원칙입니다. 특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 더 낫습니다. 즉, 비대한 인터페이스보단 더 작고 구체적인 인터페이스로 분리해야합니다.

    DIP(의존관계 역전 원칙)은 추상적인 것은 자신보다 구체적인 것에 의존하지 않고, 변화하기 쉬운 것에 의존해서는 안된다는 원칙입니다. 구체적으론 구현 클래스에 의존하지 말고, 인터페이스에 의존해야 하는 원칙입니다.

  • 동일성(identity)와 동등성(equality)에 대해 설명해주세요. (equals(), ==)

    동일성은 객체의 주소를 비교하는 것이고, 동등성은 객체의 같음을 비교하는 것입니다.

    기본적으로 자바에서는 Object 클래스에 정의된 equals() 메소드가 동일성 비교를 합니다. 따라서, 개발자는 원한다면 equals() 메소드를 오버라이딩해서 동등성의 판단 기준을 정의해주면 됩니다.

  • 원시타입과 참조타입의 차이에 대해 설명해주세요.

  • String, StringBuilder, StringBuffer 각각의 차이에 대해 설명해주세요.

    String은 불변입니다. StringBuilder와 StringBuffer는 이런 String의 특징때문에 사용하는 가변타입이라고 볼 수 있습니다.

    StringBuilder와 StringBuffer는 Thread-safe 여부의 차이가 있습니다. StringBuilder는 Thread-safe하지 않습니다. 따라서 Multi-Thread 환경에서 사용할 때는 StringBuffer를 사용합니다.

  • Checked Exception과 Unchecked Exception에 대해 설명해주세요. 스프링 트랜잭션 추상화에서 rollback 대상은 무엇일까요?

  • Java8에서 추가된 기능에 대해서 설명해주세요.

    Java8에서는 Lambda식, Stream API, Optional, 날짜 시간 API, StringJoiner 등이 추가되었습니다.

    lambda는 함수형 프로그래밍을 지원하기 위한 기능이고, Stream API는 고차함수를 지원합니다. Optional은 Null-safety를 제공하며, Stream과 사용법이 유사합니다. 날짜 시간 API는 Joda-time등의 라이브러리에서 영향을 받아 괜찮은 API가 되었으며, StringJoiner는 문자열을 간단하게 구분자로 합칠 수 있는 기능을 제공합니다.

  • try-with-resource에 대해서 설명해주세요.

    try-with-resources는 자바 버전7에 도입된 문법입니다.

    자바 7 버전 이전에서 하나 이상의 리소스(java.lang.AutoCloseable을 구현한 객체 혹은 java.io.Closeable를 구현한 객체)를 사용할 경우 개발자가 임의로 finally 문에서 ~~.close()를 사용하여 자원 해제를 시켜줘야 했습니다.

    만약 개발자가 사용한 자원을 finally 문에서 해제시켜주지 않고 누락시켰다면 자원이 해제되지 않은 채로 프로그램이 오작동하게 되고, finally 문에서 자원을 해제 시켜주더라도 자원 해제를 위한 중복 코드가 발생하기 때문에 소스 코드의 가독성을 해치는 단점이 있었습니다.

    이를 해결하기 위해 try() 안에 사용할 리소스 객체를 명시적으로 선언하여 사용하면, try 블록 안에서 로직이 정상적으로 완료되었는지, 갑작스럽게 완료되었는지 여부와 관계 없이 JVM에서 자동으로 자원을 반납해주는 기능을 하도록 도입하였습니다.

    추가로, 자바 9 버전에서는 try() 문 안에 명시적으로 객체 선언을 하기 보다는 try 문 바깥에서 객체 선언을 하고 생성된 인스턴스의 변수를 넣어줄 수 있도록 바뀌었습니다.

    Java 7 : try(BufferedReader br = new BufferedReader())
    Java 9 : try(br)

  • 강한 결합과 느슨한 결합이 무엇인지 설명해주세요.

    결합도는 의존성의 정도를 나타내며 다른 모듈에 대해 얼마나 많은 정보를 알고 있는지에 대한 척도입니다.

    어떤 모듈이 다른 모듈에 너무 자세한 부분(구현 세부사항)까지 알고 있을 경우에 강한 결합도를 가진다고 합니다.

    어떤 모듈이 다른 모듈에 대해 필요한 정보(인터페이스로 추상화된 고수준 정책)만 알고 있다면 두 모듈은 낮은 결합도를 가진다고 합니다.

    객체지향 관점에서 결합도는 객체 또는 클래스가 협력에 필요한 적절한 수준의 관계만을 유지하고 있는지를 나타냅니다. 이러한 관점에서 강한 결합도는 반드시 지양해야 하며, 개발자는 적절한 결합도를 유지할 수 있도록 고민하고 설계해야 합니다.

  • 직렬화와 역직렬화에 대해서 설명해주세요.

    직렬화란 자바 시스템 내부에서 사용되는 객체 또는 데이터를 외부의 자바 시스템에서도 사용할 수 있도록 바이트 형태로 데이터 변환하는 기술과 바이트로 변환된 데이터를 다시 변환하는 기술(역직렬화)을 아울러서 이야기 합니다.

    자바 직렬화는 JVM의 메모리에서만 상주되어있는 객체 데이터를 영속화(Persistence)가 필요할 때 사용됩니다. 시스템이 종료되더라도 없어지지 않는 장점을 가지며 영속화된 데이터이기 때문에 네트워크로 전송이 가능합니다.

  • Mutable 객체와 Immutable 객체의 차이점에 대해 설명해주세요.

  • 자바에서 null을 안전하게 다루는 방법에 대해 설명해주세요.

    공개 메서드가 아닌 곳에는 assert를 사용하여 null을 방어할 수 있습니다. 또한 메서드의 인자를 받을 때 Objects.requireNonNull()을 사용하여 방어할 수 있습니다. 그리고 Optional을 사용해 리턴 타입에서 null을 반환하지 않도록 방어할 수 있습니다. 마지막으로 사전 조건과 사후 조건을 명확히 하여 계약에 의한 설계를 실천해야 합니다.


  • JAVA란 무엇인가요, (자바의 특징, OOP)

    자바는 객체지향적 프로그래밍 언어(OOP) 입니다.

    객체지향적 언어 OOP란, 프로그래밍에 사용 될 데이터의 상태와 행위를 객체로 만들어, 객체간의 상호작용을 통해 비지니스 로직을 구성하는 프로그래밍 기법입니다. (OOP의 특징은 아래에서)

  • 자바의 특징

    • JVM을 이용하기 때문에 운영체제에 독립적입니다.

    • 객체지향언어이기 때문에 캡슐화, 상속, 다형성, 추상화의 특징을 가집니다.

    • 런타임시 데이터 타입이 결정되는 동적 타입 언어입니다.

    • 컴파일 언어와 인터프리터 언어 2가지 동작방식을 복합적으로 사용하는 하이브리드 언어라고 불리기도 합니다.

    • GC를 지원합니다.

    • Abstraction(추상화)
      : 현상에 존재하는 객체의 주요 특징을 추출하는 과정.

    • Encapsulation(캡슐화)
      : 하나의 클래스 안에 데이터, 기능을 담아 정의하고(속성), 중요한 데이터나 복잡한 기능 등은 숨기고(private/protected), 외부에서 사용에 필요한 기능만을 공개하는 것.

    • Inheritance(상속)특정 Class를 상속 받아 그 Class의 변수와 기능을 재사용한다. Java에서는 다중상속이 불가능하다.

      • Generalization : 추출된 class의 공통적인 특성을 모아 super class로 정의.
      • Specialization : 비슷한 속성과 기능을 가지고 있는 다른 class를 상속받아 새로운 class를 정의할 수 있음.
        sub객체 생성 시 super도 함께 생성된다.
        : 객체 정의 시 기존에 존재하는 객체의 속성과 기능을 상속받아 정의 하는 것.
    • Polymorphism(다형성): 같은 타입의 변수가 다양한 형태의 객체를 참조하는 것.

  • 정적타입 언어와 동적 타입 언어의 차이

    타입(자료형)의 결정을 컴파일할 때 결정한다면 정적 타입 언어(Java, C, C++), 런타임 과정에서 결정한다면 동적 타입 언어(Python, JavaScript, Rudy)입니다.

  • 정적타입, 동적타입 언어의 장단점

    • 정적 타입 언어의 경우

      컴파일 때 미리 타입을 결정하기 때문에 실행속도가 빠르고 타입 에러로 인한 문제점을 초기에 발견할 수 있어 타입의 안정성이 높습니다.
      하지만, 매번 코드 작성시 변수형을 결정해줘야 하는 번거로움이 있습니다.

    • 동적 타입 언어의 경우

      런타임까지 타입에 대한 결정을 끌고 갈 수 있기 때문에 유연성이 높고, 컴파일시 타입을 명시해주지 않아도 되기 때문에 빠르게 코드를 작성할 수 있습니다.
      하지만, 실행 도중에 변수에 예상치 못한 타입이 들어와 타입에러가 발생할 수 있습니다.
      동적타입 언어는 런타임 시 확인할 수 밖에 없기 때문에, 코드가 길고 복잡해질 경우 타입 에러를 찾기가 어려워 집니다.

  • 스크립트 언어와 컴파일 언어 차이

    스크립트 언어는 Javascript, Python이 대표적인 스크립트 언어입니다.
    컴파일 언어는 C, C++이 있습니다.
    스크립트 언어와 컴파일 언어의 차이점은

    • 스크립트 언어

      인터프리터가 한 라인 한 라인 기계어로 번역하며 실행합니다.
      우리가 컴파일 에러라고 부르는 문법 오류를 사전에 방지하지 못하기 때문에 주의해야 합니다.

    • 컴파일 언어

      실행 전 소스코드를 컴파일하여 기계어로 변환 후 해당 파일을 실행합니다.
      이미 기계어로 변환된 것을 실행하므로 비교적 빠릅니다.
      컴파일 과정을 거쳐 기계어 코드로 번역이 되기 때문에 사전에 검증을 할 수 있고, 최적화를 해줄 수 있습니다.
      또한, 스크립트 언어는 운영체제를 신경 쓸 필요 없이 한 번만 작성하면 되지만, 컴파일 언어는 운영체제에 따라 다르게 작업해야 합니다.

  • 자바는 컴파일 언어인가 스크립트 언어인가

    Java는 컴파일 언어에 더 가깝다고 생각합니다. 자바 컴파일러가 기계어로 번역해주기 때문입니다.
    다만, Java는 조금 특수한 경우입니다.
    자바는 한 번의 컴파일링으로 실행 가능한 기계어가 만들어지지 않고 JVM에 의해 기계어로 번역되고 실행하는 과정을 거치기 때문에 C나 C++의 컴파일 단계에서 만들어지는 완전한 기계어보다는 속도가 느립니다. (Java는 컴파일 시에 Java Byte Code로 컴파일 되며, 이는 JVM에서 인터프리터 방식으로 동작함)
    그러나 최근의 JVM 구현은 JIT 컴파일러를 포함하여 일부 구간에서는 최적화를 수행하여 기계어 코드를 생성하여 바로 실행합니다.

  • try catch vs throw exception

    예외처리 try catch와 throw exception의 차이점은?

    • try-catch : 예외가 발생할 수 있는 부분을 try 블럭에 넣고 예외가 발생시 catch 블럭에서 처리하는 방법입니다.

    • throw : 의도적으로 예외를 발생시키는 것인데, 특정 로직에서 특정 예외를 상위 클래스에서 공통으로 예외 처리하기 위해 사용합니다.

profile
초보 개발자

0개의 댓글