기술 면접 준비

[기술면접] JAVA - 1/4

Lea Hwang 2023. 3. 2. 18:03

Java 핵심 키워드

  • JVM
  • GC
    • 1. 스탑더월드 / 마크앤스윕 동작원리
      2. G1GC
  • OOP
  • SOLID
  • 오버로딩 & 오버라이딩
  • 제네릭

 

 

[기술면접] JAVA - 1/4의 목차

  • Java의 특징
  • Java의 실행 방식
  • JVM의 역할
  • Java의 컴파일 과정
  • Java에서 제공하는 원시 타입들에 무엇이 있고, 각각 몇 바이트를 차지하나요?
  • 오버라이딩(Overriding)과 오버로딩(Overloading)
  • 객체지향 프로그래밍(OOP)
  • 불변 객체, 대표적인 Java의 예시를 설명해주세요.
  • 추상 클래스와 인터페이스 설명 및 차이
  • 싱글톤 패턴
  • 가비지 컬렉션(Garbage Collection)
  • 객체지향의 설계원칙
  • 자바의 메모리 영역
  • 클래스와 객체, 인스턴스
  • 생성자(Constructor)
  • Wrapper Class란 무엇이며, Boxing과 UnBoxing
  • Synchronized
  • new String()과 리터럴("")의 차이
  • String, StringBuffer, StringBuilder의 차이
  • 접근 제한자(Access Modifier)
  • 클래스 멤버 변수 초기화 순서
  • static



 


 

 

 

 😎 Java의 특징을 설명해주세요.

  • Java는 객체지향 프로그래밍 언어입니다.
  • 기본 자료형을 제외한 모든 요소들이 객체로 표현되고, 객체 지향 개념의 특징인 캡슐화, 상속, 다형성이 잘 적용된 언어입니다.
  • 장점
    • JVM(자바가상머신) 위에서 동작하기 때문에 운영체제에 독립적이다. (이식성이 높다)
    • GabageCollector를 통한 자동적인 메모리 관리가 가능하다.
    • 객체지향 언어로 유지보수가 쉽고 확장성이 높다.
    • 멀티 스레드 구현이 쉽다.
      • 스레드 생성 및 제어와 관련된 라이브러리 API를 제공한다.
    • 동적 로딩을 지원한다.
      • 실행 시에 모든 클래스가 로딩되는 것이 아니라 필요한 시점에 클래스를 로딩한다.
      • 애플리케이션의 변경 사항도 비교적 적은 작업으로 처리가 가능하다.
    • 분산 환경을 지원한다.
      • 기본적으로 TCP/IP 라이브러리가 포함되어 있고, HTTP 프로토콜을 지원한다.
    • 오픈 소스 라이브러리가 풍부하다.
  • 단점
    • JVM 위에서 동작하기 때문에 실행 속도가 상대적으로 느리다.
    • 다중 상속이나 타입에 엄격하며, 제약이 많다.

 

▶꼬리질문 - 왜 Java언어를 사용했나요?

▶꼬리질문 - Java8에서 새롭게 추가된 기능을 말씀해주세요.

▶꼬리질문 - Java11에 새로 추가된 기능을 설명해주세요.

▶꼬리질문 - Java8과 Java11 중 무엇을 선택하면 좋을까요?

 

▶꼬리질문 - 인자(Argument)란?

▶꼬리질문 - 매개변수(Parameter)란?

▶꼬리질문 - Java는 Call by value일까 Call by reference일까?

 

 

 

왜 Java언어를 사용했나요?

Java는 객체지향 프로그래밍을 가장 대중적이고 편하게 구현할 수 있는 언어라고 생각했습니다.

많은 엔터프라이즈급 회사에서 오랜 기간 사용된 만큼 안정성이 보장된 언어라고 판단했다.

또한 자바 진영은 하위 호환성을 중요하게 여기는 점과 레퍼런스와 커뮤니티가 활성화 되어있는 점이 매력적으로 다가왔다. 이러한 이유로 Java를 주 언어로 사용하여 개발하고 있다.

 

 

 

Java8에서 새롭게 추가된 기능을 말씀해주세요.

  • 인터페이스에 디폴트 메소드와 정적 메소드 추가
  • 함수형 인터페이스, 람다 표현식, 메소드 참조 기능 추가
  • 스트림 API 도입
  • 새로운 날짜 관련 라이브러리 추가
  • Optional 지원
  • 병렬 처리 지원

 

 

Java11에 새로 추가된 기능을 설명해주세요.

  • Spring에 새로운 메소드 추가
  • Files 클래스에 새로운 메소드 추가
  • 컬렉션 인터페이스에 새로운 메소드 추가
  • Predicate 인터페이스에 새로운 메소드 추가
  • 람다에서 로컬 변수 Var 사용
  • 자바 파일 실행 방식 단순화

 

 

Java8과 Java11 중 무엇을 선택하면 좋을까요?

현재 기업을 보면 Java 8 혹은 Java 11을 주로 채택하여 사용하고 있는 것 같다.

개인적으로는 Java 8의 기능을 수용하면서 더 나은 라이브러리가 추가 되었고, 성능이 개선된 G1 GC를 사용하는 Java 11을 사용하는 편이다.

 

또한 Java 11은 LTS 버전이므로 장기적인 지원이 보장된 버전이므로 앞으로도 유익한 기능이 나올 것으로 기대한다.

 

 

인자(Argument)란?

메소드 호출 시에 전달되는 값

 

 

매개변수(Parameter)란?

메소드에서 전달 받은 값

 

 

Java는 Call by value일까 Call by reference일까?

자바에서 매개변수를 넘기는 과정에 직접적인 참조를 넘긴 게 아닌, 주소 값을 복사해서 넘기기 때문에

이는 call by value이다.

 

 

 

😎 Java의 실행 방식에 대해 설명해주세요.

  1. 자바 프로그램이 실행되면 JVM은 OS로부터 프로그램이 필요로 하는 메모리를 할당 받는다.
  2. 자바 파일이 자바 컴파일러에 의해 자바 바이트 코드로 변환된다.
  3. 클래스 로더를 통해 자바 바이트 코드를 JVM으로 필요한 시점에 로딩한다.
  4. 해석된 바이트 코드는 런타임 데이터 영역에 배치되어 수행이 이루어지게 된다.
  5. 실행 과정 속에서 JVM은 필요에 따라 GC같은 관리 작업을 수행한다.

 

 

😎 JVM의 역할에 대해 설명해주세요.

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

 

▶ 꼬리질문 - JVM의 특징

  • 스택 기반의 가상 머신
  • 심볼릭 레퍼런스
  • 가비지 컬렉션(garbage collection)
  • 기본 자료형을 명확하게 정의하여 플랫폼 독립성 보장

▶ 꼬리질문 - JVM의 구조

JVM은 크게 세 가지 영역으로 나눌 수 있다.

  • 클래스 로더
  • 런타임 데이터 영역(메모리)
  • 실행 엔진

 

 

 

😎 Java의 컴파일 과정에 대해 설명해주세요.

  1. 개발자가 .java 파일을 생성한다.
  2. build를 한다.
  3. java compiler의 javac의 명령어를 통해 바이트코드(.class)를 생성한다.
  4. Class Loader를 통해 JVM 메모리 내로 로드한다.
  5. 실행엔진을 통해 컴퓨터가 읽을 수 있는 기계어로 해석된다.(각 운영체제에 맞는 기계어)

 

 

 

😎 Java에서 제공하는 원시 타입들에 무엇이 있고, 각각 몇 바이트를 차지하나요?

  • 정수형 byte, short, int, long 실수형 float, double 문자형 char 논리형 boolean이 있고,
  • 정수형 1, 2, 4, 8, 실수형 4, 8, 문자형 2, 논리형 1 바이트를 차지합니다.

 

 

▶ 꼬리질문 - 원시 타입과 참조 타입의 차이를 설명해주세요.

  • 성능 관점
    • 원시 타입은 스택 영역에 존재한다. 반면 참조 타입은 스택 영역에는 참조 값만 있고, 실제 값은 힙 영역에 존재한다. 참조 타입은 최소 2번 메모리 접근을 해야 하고, 일부 타입의 경우 값을 필요로 할 때 언박싱 과정(ex. Double → double, Integer → int)을 거쳐야 하므로 원시 타입과 비교해서 접근 속도가 느린 편이다.
  • 메모리 관점
    • 원시 타입보다 참조 타입이 사용하는 메모리 양이 압도적으로 높다. 
  • NULL 관점
    • 원시 타입은 null을 담을 수 없지만, 참조 타입은 null을 담을 수 있다. 이것은 원시 타입의 경우, 값이 없으면 디폴트 값을 반환하기 때문이다. (ex. int은 0, boolean은 false)
  • 제네릭 관점
    • 원시 타입은 제네릭 타입에서 사용할 수 없지만, 참조 타입은 가능하다.

 

 

 

😎 오버라이딩(Overriding)과 오버로딩(Overloading)에 대해 설명해주세요.

  • 오버로딩(Overloading)
    • 반환타입 관계 없음, 메소드명 같음, 매개변수 개수나 타입을 다르게 해서 같은 이름의 메소드를 여러개 정의
    • 오버로딩 장점
      • 생성자가 여러개 필요한 경우 유용합니다.
      • 유사한 기능을 하는 동일한 이름의 메소드들이 묶여있기 때문에 메소드명을 절약할 수 있다.
      • 기능 예측이 쉬워져서 소스코드의 가독성이 향상된다는 장점이 있습니다.
      • 또한 매개변수 값에 따라 다양한 처리를 할 수 다형성을 구현할 수 있다.
  • 오버라이딩(Overriding)
    • 반환타입, 메소드명, 매개변수 모두 같음
    • 부모 클래스로부터 상속받은 메소드를 재정의하는 것
    • 결합도를 낮추기 위한 방법 중 하나로 interface 사용이 있으며, 이 과정에서 오버라이딩이 적극 사용됩니다.
    • 오버라이딩 조건 
      • 부모 클래스의 메소드와 동일한 시그니처를 갖고 있어야한다.
      • 접근 제어자는 부모 클래스보다 좁은 범위로 변경할 수 없다.
      • 부모 클래스의 메서드보다 많은 수의 예외를 선언할 수 없다는 조건이 있다.

 

 

😎 객체지향 프로그래밍(OOP)에 대해 설명해주세요.

우리가 실생활에서 쓰는 모든 것을 객체라 하며, 객체 지향 프로그래밍은 프로그램 구현에 필요한 객체를 파악하고 상태와 행위를 가진 객체를 만들고 각각의 객체들의 역할이 무엇인지를 정의하여 객체들 간의 상호작용을 통해 프로그램을 만드는 것을 말합니다.


즉, 기능이 아닌 객체가 중심이며 "누가 어떤 일을 할 것인가?"가 핵심
특징으로는 캡슐화, 상속, 다형성, 추상화 등이 있고, 모듈 재사용으로 확장 및 유지보수가 용이합니다.

 

 

 

😎 try-with-resources에 대해 설명해주세요.

  • try-with-resources는 try-catch-finally의 문제점을 보완하기 위해 나온 개념입니다.
  • try( ... ) 안에 자원 객체를 전달하면, try블록이 끝나고 자동으로 자원 해제 해주는 기능을 말합니다.
  • 따로 finally 구문이나 모든 catch 구문에 종료 처리를 하지 않아도 되는 장점이 있습니다.

▶꼬리질문 - try - with - resources로 자원해제 처리

자바에서  외부 자원에 접근하는 경우 주의해야할 점은 외부자원을 사용한 뒤 제대로 자원을 닫아줘야 한다.

자원을 닫을 때 try - catch - finally 구문 대신 try - with - resources 구문을 사용하면 코드의 가독성이 더 증가한다.

  • try-catch-finally로 자원 해제
  • try-with-resources로 자원 쉽게 해제

 

 

try-catch-finally로 자원 해제

try-catch-finally 구문으로 자원을 해제하려면 코드가 길어지고  지저분하다.

아래는 try-catch-finally을 사용한 자원해제의 예제 코드이다.

FileOutputStream out = null;
        try {
            out = new FileOutputStream("exFile.txt");
            // ... 이후 입출력 로직 처리 ...
        }catch (FileNotFoundException e) {
            e.printStackTrace();
        }finally {
            if(out != null) { //스트림이 null인지 체크
                try {
                    out.close();
                }catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

 

 

try-with-resources로 자원 쉽게 해제

Java 7에서 try - with - resources 구문이 추가되었다.

try - with - resources를 사용하기 위해서는 close메소드를 정의하기 위한 AutoCloseable 인터페이스를 구현해야 한다.

위에서 기존의 입출력 처리시의 예외처리는 다음과 같이 바뀌어 사용된다.

 

아래의 코드는 try-with-resources 를 사용하여 자원을 쉽게 해제하는 예제이다. 실행결과는 위의 예제와 동일하다.

 try(FileOutputStream out = new FileOutputStream("exFile.txt")) {
            // ...이후 입출력 로직 처리...
        }catch (IOException e) {
            e.printStackTrace();
        }

try - with - resources 는 위의 코드와 같이

try(. . .)에 자원 객체를 전달하면, try 코드 블록이 끝나고 자동으로 자원을 해제해주는 기능이다.

즉, 따로 finally 구문이나 모든 catch 구문에 종료 처리를 하지 않아도 된다.

 

 

이 때, try에 전달할 수 있는 자원은 AutoCloseable 인터페이스의 구현체로 한정된다.

AutoCloseable은 JDK 1.7부터 추가된 인터페이스다.
/**
 * @author Josh Bloch
 * @since 1.7
 */
public interface AutoCloseable {
    void close() throws Exception;
}

 

아래와 같이 try( ) 안에 복수의 자원 객체를 전달할 수 있다.

try(Something1 s1 = new Something1();
    Something2 s2 = new Something2()) {
 
} catch(...) {
    ...
}

 

 

 

 

😎 불변 객체가 무엇인지 설명하고 대표적인 Java의 예시를 설명해주세요.

  • 불변 객체는 객체 생성 이후 내부의 상태가 변하지 않는 객체를 말합니다.
  • Java에서는 필드가 원시 타입인 경우 final 키워드를 사용해 불변 객체를 만들 수 있고,
  • 참조 타입일 경우엔 추가적인 작업이 필요합니다.

 

▶꼬리질문 - 참조 타입일 경우 추가적인 작업은 어떤게 있는지 설명해주세요.

▶꼬리질문 - 불변 객체나 final을 굳이 사용해야 하는 이유가 있을까요?

 

▶꼬리질문 - 가변 객체와 불변 객체를 설명해주세요.

▶꼬리질문 - 불변 객체의 장점을 설명해주세요.
▶꼬리질문 - 불변 객체를 만드는 방법을 말씀해주세요.
▶꼬리질문 - 방어적 복사를 사용하면 항상 불변성을 보장하나요?

 

 

참조 타입일 경우 추가적인 작업은 어떤게 있는지 설명해주세요.

참조 타입은 대표적으로 1.객체를 참조할 수도 있고, 2.배열이나 3.List 등을 참조할 수 있습니다.

 

1. 참조 변수가 일반 객체인 경우 객체를 사용하는 필드의 참조 변수도 불변 객체로 변경해야 합니다.

2. 배열일 경우 배열을 받아 copy해서 저장하고, getter를 clone으로 반환하도록 하면 됩니다.

(배열을 그대로 참조하거나, 반환할 경우 외부에서 내부 값을 변경할 수 있음. 때문에 clone을 반환해 외부에서 값 변경하지 못하게 함)

3. 리스트인 경우에도 배열과 마찬가지로 생성시 새로운 List를 만들어 값을 복사하도록 해야 합니다.

배열과 리스트는 내부를 복사하여 전달하는데, 이를 방어적 복사(defensive-copy)라고 합니다.

 

 

 

불변 객체나 final을 굳이 사용해야 하는 이유가 있을까요?

불변 객체나 final 키워드를 사용해 얻는 이점은 다음과 같습니다.

 

1. Thread-Safe하여 병렬 프로그래밍에 유용하며, 동기화를 고려하지 않아도 된다.

(공유 자원이 불변이기 때문에 항상 동일한 값을 반환하기 때문)

2. 실패 원자적인 메소드를 만들 수 있다.

(어떠한 예외가 발생되더라도 메소드 호출 전의 상태를 유지할 수 있어 예외 발생 전과 똑같은 상태로 다음 로직 처리 가능)

3. 부수효과를 피해 오류를 최소화 할 수 있다.

※ 부수효과 : 변수의 값이 바뀌거나 객체의 필드 값을 설정하거나 예외나 오류가 발생하여 실행이 중단되는 현상

4. 메소드 호출 시 파라미터 값이 변하지 않는다는 것을 보장할 수 있다.

5. 가비지 컬렉션 성능을 높일 수 있다.

(가비지 컬렉터가 스캔하는 객체의 수가 줄기 때문에 Gc 수행 시 지연시간도 줄어든다.)

 

 

 

가변 객체와 불변 객체를 설명해주세요.

가변 객체

 

가변 객체는 Java에서 Class의 인스턴스가 생성된 이후에 내부 상태가 변경 가능한 객체이다. 가변 객체는 멀티 스레드 환경에서 사용하려면 별도의 동기화 처리가 필요합니다.

대표적인 가변 객체로 ArrayList, HashMap, StringBuilder, StringBuffer 등이 존재한다.

이외에도 프로그래머가 커스텀 객체를 생성하여 내부 상태를 변경할 수 있게 만든다면, 그것도 가변 객체가 된다.

 

 

 

불변 객체

 

불변 객체는 가변 객체와 반대로 Java에서 Class의 인스턴스가 생성된 이후에 내부 상태를 변경할 수 없는 객체이다. 불변 객체는 멀티 스레드 환경에서도 안전하게 사용할 수 있다는 신뢰성을 보장합니다.

대표적인 불변 객체로 String 등이 존재한다. 이외에도 프로그래머가 커스텀 객체를 생성하여 내부 상태가 변경되지 않게 만들면, 그것도 불변 객체가 된다.

 

 

 

불변 객체의 장점

  • Thread-safe하여 병렬 프로그래밍에 유용하며, 동기화를 고려하지 않아도 된다.
  • 실패 원자적인(Failure Atomic) 메소드를 만들 수 있다.
  • Cache, Map, Set 등의 요소로 활용하기에 적합하다.
  • 부수 효과(Side Effect)를 피해 오류 가능성을 최소화할 수 있다.
  • 다른 사람이 작성한 함수를 예측 가능하며 안전하게 사용할 수 있다.



불변 객체를 만드는 방법

  • 모든 필드에 대해 final을 설정한다.
  • 필드에 참조 타입이 있을 경우, 해당 객체도 불변성을 보장해야 한다.
  • 필드에 컬렉션이 존재할 경우, 생성자 및 getter에 대해 방어적 복사를 수행해야 한다.

 


방어적 복사를 사용하면 항상 불변성을 보장하나요?

그렇지 않다. 방어적 복사는 컬렉션의 요소에 대해 얕은 복사를 수행하므로 컬렉션의 참조 타입이 가변 객체라면, 복사하려는 컬렉션의 요소가 변경될 경우 불변성이 깨진다.

 

 

 

 

 

 

😎 추상 클래스와 인터페이스를 설명해주시고, 차이에 대해 설명해주세요.

  • 추상 클래스는 클래스 내 추상 메소드가 하나 이상 포함되거나 abstract로 정의된 경우를 말합니다.
    • 단일 상속을 지원합니다.
    • 변수를 가질 수 있습니다.
    • 자식 클래스에서 상속을 통해 abstract 메소드를 구현합니다. (extends)
  • 인터페이스모든 메소드가 추상 메소드로만 이루어져 있는 것을 말합니다.
    • 다중 상속을 지원합니다.
    • 변수를 가질 수 없습니다. 상수는 가능합니다.
    • 모든 메소드는 선언부만 존재합니다.
    • 구현 클래스는 선언된 모든 메소드를 overriding 합니다.
  • 공통점
    • new 연산자로 인스턴스 생성 불가능
    • 사용하기 위해서는 하위 클래스에서 확장/구현 해야 한다.
  • 차이점
    • 인터페이스는 그 인터페이스를 구현하는 모든 클래스에 대해 특정한 메소드가 반드시 존재하도록 강제함에 있고,
    • 추상클래스는 상속받는 클래스들의 공통적인 로직을 추상화 시키고, 기능 확장을 위해 사용한다.
    • 추상클래스는 다중상속이 불가능하지만, 인터페이스는 다중상속이 가능하다.

 

 

 

😎 싱글톤 패턴에 대해 설명해주세요.

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

싱글톤 패턴의 대표적인 예시는 Spring Bean 입니다.

스프링의 빈 등록 방식은 기본적으로 싱글톤 스코프이고, 스프링 컨테이너는 모든 빈들을 싱글톤으로 관리합니다.

스프링은 요청할 때마다 새로운 객체를 생성해서 반환하는 기능도 제공한다. (프로토타입 빈, @Scope("prototype"))

 

 

 

 

😎 가비지 컬렉션(Garbage Collection)에 대해 설명해주세요.

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

▶꼬리질문 - 가비지 컬렉션 과정에 대해 설명해주세요.

GC의 작업을 수행하기 위해 JVM이 어플리케이션의 실행을 잠시 멈추고, GC를 실행하는 쓰레드를 제외한 모든 쓰레드들의 작업을 중단  (Stop The World 과정) 사용하지 않는 메모리를 제거(Mark and Sweep 과정)하고 작업이 재개됩니다.

GC의 작업은 Young 영역에 대한 Minor GC와 Old 영역에 대한 Major GC로 구분됩니다.

 

 

 

😎 객체지향의 설계원칙에 대해 설명해주세요.

  1. SRP - 단일 책임 원칙 : 한 클래스는 하나의 책임만 가져야 한다.
  2. OCP - 개방-폐쇄 원칙 : 확장에는 열려있고, 수정에는 닫혀있어야 한다.
  3. LSP - 리스코프 치환 원칙 : 상위 타입은 항상 하위 타입으로 대체할 수 있어야 한다.
  4. ISP - 인터페이스 분리 원칙 : 인터페이스 내에 메소드는 최소한 일수록 좋다. (하나의 일반적인 인터페이스보다 여러 개의 구체적인 인터페이스가 낫다.)
  5. DIP - 의존관계 역전 원칙 : 구체적인 클래스보다 상위 클래스, 인터페이스, 추상클래스와 같이 변하지 않을 가능성이 높은 클래스와 관계를 맺어라. DIP 원칙을 따르는 가장 인기 있는 방법은 의존성 주입(DI)이다.

 

 

😎 자바의 메모리 영역에 대해 설명해주세요.

  • 자바의 메모리 공간은 크게 Method 영역, Stack 영역, Heap 영역으로 구분되고, 데이터 타입에 따라 할당됩니다.
  • 메소드(Method) 영역 : 전역변수와 static변수를 저장하며, Method영역은 프로그램의 시작부터 종료까지 메모리에 남아있다.
  • 스택(Stack) 영역 : 지역변수와 매개변수 데이터 값이 저장되는 공간이며, 메소드가 호출될 때 메모리에 할당되고 종료되면 메모리가 해제된다. LIFO(Last In First Out) 구조를 갖고 변수에 새로운 데이터가 할당되면 이전 데이터는 지워진다.
  • 힙(Heap) 영역 : new 키워드로 생성되는 객체(인스턴스), 배열 등이 Heap 영역에 저장되며, 가비지 컬렉션에 의해 메모리가 관리되어 진다.

 

▶ 꼬리질문 - 각 메모리 영역이 할당되는 시점은 언제인가요?

  • Method 영역 : JVM이 동작해서 클래스가 로딩될 때 생성
  • Stack 영역 : 컴파일 타임 시 할당
  • Heap 영역 : 런타임시 할당

※ 컴파일 타임 : 소스코드가 기계어로 변환되어 실행가능한 프로그램이 되는 과정

※ 런타임 : 컴파일 타임 이후 프로그램이 실행되는 때

 

 

 

😎 클래스와 객체 그리고 인스턴스에 대해 설명해주세요.

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

 

 

 

😎 생성자(Constructor)에 대해 설명해주세요.

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

 

 

 

😎 Wrapper Class란 무엇이며, Boxing과 UnBoxing은 무엇인지 설명해주세요.

기본 자료형(Primitive data type)에 대한 객체 표현을 Wrapper class라고 합니다.

 

 

자바의 자료형은 크게 기본 타입(primitive type)과 참조타입(reference type)으로 나누어진다.

대표적으로 기본 타입은 char, int, float, double, boolean 등이 있고 참조 타입은 class, interface 등이 있는데

프로그래밍을 하다 보면 기본 타입의 데이터를 객체로 표현해야 하는 경우가 종종 있다.

 

이럴 때에 기본 자료타입(primitive type)을 객체로 다루기 위해서 사용하는 클래스들을 래퍼 클래스(wrapper class)라고 한다.

 

자바는 모든 기본타입(primitive type) 값을 갖는 객체를 생성할 수 있다.

래퍼 클래스로 감싸고 있는 기본 타입 값은 외부에서 변경할 수 없다. 만약 값을 변경하고 싶다면 새로운 포장 객체를 만들어야 한다.

  • 기본 자료형 → Wrapper class로 변환하는 것을 Boxing이라 하며,
  • Wrapper class → 기본 자료형으로 변환하는 것을 UnBoxing이라 합니다.

 

▶ 꼬리질문 - 래퍼 클래스의 종류

자바에서는 모든 경우는 아니지만 대부분의 경우에 자동으로 boxing / unboxing을 해준다.

 

 

 

😎 Synchronized에 대해 아는 대로 말해주세요.

  • 여러 개의 쓰레드가 한 개의 자원을 사용하고자 할 때, 현재 데이터를 사용하고 있는 쓰레드를 제외하고 나머지 쓰레드들은 데이터에 접근할 수 없게 막는 개념입니다.
  • 데이터의 thread-safe를 하기 위해 자바에서 Synchronized 키워드를 제공해 멀티 쓰레드 환경에서 쓰레드간 동기화를 시켜 데이터의 thread-safe를 보장합니다.
  • Synchronized는 변수와 메소드에 사용해서 동기화 할 수 있으며, Synchronized 키워드를 남발하게 되면 오히려 프로그램의 성능저하를 일으킬 수 있습니다.


▶ 꼬리질문 - 동기와 비동기의 차이점에 대해 말씀해주세요.

  • 동기(Synchronous) 방식
    • 요청을 보내고 실행이 끝나면 다음 동작을 처리하는 방식
    • 순서에 맞추어 진행되기 때문에 제어하기 쉽다.
    • 여러가지 요청을 동시에 처리할 수 없어 효율이 떨어진다.
    • 동기 방식의 예시로는 콜센터 종업원이 일을 처리하는 방식이 될 수 있다. 콜센터의 직원은 한 손님의 전화 응대가 끝난 후에 다음 손님의 응대를 진행할 수 있다.
  • 비동기(Asynchronous) 방식
    • 요청을 보내고 해당 동작의 처리 여부와 상관없이 다음 요청이 동작하는 방식
    • 작업이 완료되는 시간을 기다릴 필요가 없기 때문에 자원을 효율적으로 사용할 수 있다.
    • 작업이 완료된 결과를 제어하기 어렵다.
    • 비동기 방식의 예제로는 이메일이 있다. 우리는 한 사람에게 이메일을 보냈을 때 답변을 받지 않고도 이메일을 다시 보낼 수 있다. 

 

 

▶ 꼬리질문 - 자바에서 동기화 문제를 신경써야 하는 이유는 무엇인가요?

 

자바 멀티 스레드 환경에서는 스레드들끼리 `static` 영역과 `heap` 영역을 공유하기 때문에, 공유 자원에 대한 동기화 문제를 신경써야 합니다.

 

 

 

▶ 꼬리질문 - Synchronized의 4가지 방법에 대해 설명해주세요.

synchronized lock 을 이용해 동기화를 시키는데 총 4가지 사용법이 있다.

  • synchronized method
    • 동기화를 시키고 싶은 클래스의 메소드에 synchronized키워드를 붙이면 되고, synchronized method는 인스턴스 단위의 synchronized키워드가 붙은 메소드에 대해서만 lock공유한다.
  • static synchronized method
    • 인스턴스가 아닌 클래스 단위로 lock을 공유한다.
    • 만약 synchronized method과 함께 사용하게 되면 인스턴스 락과 클래스 락은 공유가 안 되기 때문에 동기화 이슈가 발생할 수 있다는 점을 주의해야 한다.
  • synchronized block
    • block단위로 lock을 걸며, 2가지 사용 방법이 있다.
      • 첫 번째는 synchronized의 인자 값에 this를 사용하는 방식이다. 이 방식은 여러 스레드가 들어와 서로 다른 block을 호출해도 this를 사용해 자기 자신에 lock을 걸었기 때문에 기다려야 한다.
      • 두 번째는 synchronized의 인자 값에 Object를 사용하는 방식이다. 이 방식은 block마다 서로 다른 lock을 걸리게해 훨씬 효율적인 코드를 작성할 수 있다.
  • static synchronized block
    • static synchroinzed method방식과의 공통점은 클래스 단위로 `lock`을 공유한다는 점이고 차이점은 lock객체를 지정하고 block으로 범위를 한정지을 수 있다는 점이다.

 

 

▶ 꼬리질문 - Singleton 객체에 synchronized 키워드를 사용하면 생기는 문제점은?

 

Singleton synchronized메소드가 많을수록 멀티 스레드는 병목현상을 겪게 된다.
즉, 멀티 스레드를 사용하지만 `Singleton` 을 사용할 때는 싱글 스레드처럼 동작하는 문제가 발생한다.

 

▶ 꼬리질문 - Singleton 객체에 synchronized 키워드를 사용하면 생기는 문제점의 해결 방법은?

 

LazyHolder방식을 사용하면 된다. 이 방법은 JVM의 클래스 초기화 과정에 보장되는 thread-safe특성을 이용해 Singleton의 초기화 책임을 JVM에게 넘겨 개발자가 직접 동기화 코드를 작성할 때 생기는 문제를 회피할 수 있다.

또한 내부 static class를 참조하기 전까지 클래스 로딩이 발생하지 않기 때문에 메모리를 효율적으로 사용할 수 있다.

 

 

 

 

😎 new String()과 리터럴("")의 차이에 대해 설명해주세요.

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

 

 

 

 😎 String, StringBuffer, StringBuilder의 차이를 설명해주세요.

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

꼬리질문 - String 객체가 불변인 이유에 대해 아는대로 설명해주세요.

꼬리질문 - String, StringBuffer, StringBuilder의 차이와 장단점 상세히 말씀해주세요.

 

 

 

String 객체가 불변인 이유에 대해 아는대로 설명해주세요.

1. 캐싱 기능에 의한 메모리 절약과 속도 향상

Java에서 String 객체들은 Heap의 String Pool 이라는 공간에 저장되는데, 참조하려는 문자열이 String Pool에 존재하는 경우 새로 생성하지 않고 Pool에 있는 객체를 사용하기 때문에 특정 문자열 값을 재사용하는 빈도가 높을 수록 상당한 성능 향상을 기대할 수 있다.

 

2. thread-safe

String 객체는 불변이기 때문에 여러 쓰레드에서 동시에 특정 String 객체를 참조하더라도 안전하다.

 

3. 보안기능

중요한 데이터를 문자열로 다루는 경우 강제로 해당 참조에 대한 문자열 값을 바꾸는 것이 불가능하기 때문에 보안에 유리하다.

 

 

 

String, StringBuffer, StringBuilder의 차이와 장단점 상세히 말씀해주세요.

Java에서 문자열을 다루는 대표적인 클래스로 String, StringBuffer, StringBuilder가 있다.

알아보기에 앞서 이 클래스들의 공통점은 모두 다 String(문자열)을 저장하고 관리하는 클래스들이 라는 것이다.

 

연산이 많지 않을때는 위에 나열된 어떤 클래스를 사용하더라도 이슈가 발생할 가능성은 거의 없다.

그러나 연산횟수가 많아지거나 멀티쓰레드, Race condition 등의 상황이 자주 발생한다면,

각 클래스의 특징을 이해하고 상황에 맞는 적절한 클래스를 사용해야 할 것이다.

 

 

1. String   vs   StringBuffer / StringBuilder

String과 StringBuffer/StringBuilder 클래스의 가장 큰 차이점은 String 불변(immutable)의 속성을 갖는다는 점이다.

아래의예제에서 "hello" 값을 가지고 있던 String 클래스의 참조변수 str이 가리키는 곳에 저장된 "hello"에 "world" 문자열을 더해 "hello world"로 변경한 것으로 착각할 수 있다.

String  str  =  "hello";       //  String str = new String("hello");
str  =  str  +   "world";        //  [  hello  world  ]

 

하지만 기존에 "hello"값이 들어가있던 str이 "hello world"라는 값을 가지고 있는 새로운 메모리영역을 가리키게 변경되고,

처음 선언했던 "hello"로 값이 할당 되어 있던 메모리 영역은 Garbage로 남아있다가 GC(garbage collection)에 의해 사라지게 된다.

String 클래스는 불변하기 때문에 문자열을 수정하는 시점에 새로운 String 인스턴스가 생성된 것이다.

 

위와 같이 String은 불변성을 가지기 때문에 변하지 않는 문자열을 자주 읽어들일 경우 String을 사용하면 좋은 성능을 발휘한다.

그러나 문자열 추가, 수정, 삭제 등의 연산이 빈번하게 발생하는 경우에 String 클래스를 사용하면 힙 메모리(Heap)에 많은 Garbage가 생성되어 힙 메모리 부족으로 성능에 치명적인 영향을 끼친다.

 

 

 

2. 이를 해결하기 위해 Java에서는 가변(mutable)성을 가지는 StringBuffer   StringBuilder 클래스를 도입했다.

String 과는 반대로 StringBuffer / StringBuilder 는 가변성을 가지기 때문에 .append( )  .delete( ) 등의 API를 이용하여

동일 객체 내에서 문자열을 변경하는 것이 가능하다. 따라서 문자열의 추가, 수정, 삭제가 빈번하게 발생할 경우라면

String 클래스가 아닌 StringBuffer / StringBuilder를 사용해야 한다.

StringBuffer  sb  =  new  StringBuffer("hello");
sb.append("world");

 

 

 

3. StringBuffer   vs   StringBuilder

동일한 API를 가지고 있는 StringBuffer  StringBuilder의 차이점은 무엇일까?

가장 큰 차이점은 동기화의 유무로써 StringBuffer는 동기화 키워드를 지원하여 멀티쓰레드 환경에서 안전(thread-safe)하다.

참고로 String도 불변성을 가지기 때문에 마찬가지로 멀티쓰레드 환경에서의 안정성(thread-safe)을 가지고 있다.

 

반대로 StringBuilder는 동기화를 지원하지 않기 때문에 멀티쓰레드 환경에서 사용하는 것은 적합하지 않지만

동기화를 고려하지 않는 만큼 단일쓰레드에서의 성능은 StringBuffer보다 뛰어나다.

 

 

 

4. 결론

단순히 성능만 놓고 본다면 연산이 많은 경우

StringBuilder  >  StringBuffer  >>>  String 정도로 보면 된다.

하지만 각 클래스들은 성능 이슈 외에도 사용 편의성, 멀티 스레드 환경 등 여러가지 고려해야할 요인이 있으므로

아래와 같은 경우에 맞게 사용하면 될 것이다.

 

 

String   :  문자열  연산이  적고  멀티쓰레드  환경일  경우

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

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

 

 

 

😎 접근 제한자(Access Modifier)에 대해 설명해주세요.

  • 변수 또는 메소드의 접근 범위를 설정해주기 위해서 사용하는 Java의 예약어를 의미하며, 총 4 가지 종류가 있습니다.
  • public - 접근 제한이 없다. (같은 프로젝트 내 어디서든 사용 가능)
  • protected - 해당 패키지 내, 다른 패키지에서 상속받아 자손 클래스에서 접근 가능하다.
  • (default) - 해당 패키지 내에서만 접근 가능
  • private - 해당 클래스에서만 접근 가능

 

 

 

😎 클래스 멤버 변수 초기화 순서에 대해 설명해주세요.

  1. static 변수 선언부 : 클래스가 로드 될 때 변수가 제일 먼저 초기화 된다.
  2. 필드 변수 선언부 : 객체가 생성될 때 생성자 block 보다 앞서 초기화 된다.

 

 

😎 static에 대해 설명해주세요.

  • static 키워드를 사용한 변수나 메소드는 클래스가 메모리에 올라갈 때 자동으로 생성되며 클래스 로딩이 끝나면 바로 사용할 수 있습니다. 즉, 인스턴스(객체) 생성 없이 바로 사용 가능합니다.
  • 모든 객체가 메모리를 공유한다는 특징이 있고, GC 관리 영역 밖에 있기 때문에 프로그램이 종료될 때까지 메모리에 값이 유지된 채로 존재하게 됩니다.

▶꼬리질문 - static을 사용하는 이유에 대해 설명해주세요.

▶꼬리질문 - static 변수와 static 메소드

 

 

 

static을 사용하는 이유에 대해 설명해주세요.

  • static은 자주 변하지 않는 값이나 공통으로 사용되는 값 같은 공용자원에 대한 접근에 있어서 매번 메모리에 로딩하거나 값을 읽어들이는 것보다 일종의 '전역변수'와 같은 개념을 통해 접근하는 것이 비용도 줄이고 효율을 높일 수 있습니다.
  • 인스턴스 생성 없이 바로 사용 가능하기 때문에 프로그램 내에서 공통으로 사용되는 데이터들을 관리할 때 이용합니다.

 

static 변수와 static 메소드

  • 정적(static)이란?
  • static 변수
  • static 메소드
  • 결론

 

정적(static)이란?

정적(static)은 고정된이란 의미를 가지고 있다.

static이라는 키워드를 사용하여 static변수와 static메소드를 만들 수 있는 데 다른말로 정적필드와 정적메소드라고도 하며, 이 둘을 합쳐 정적 멤버(= 클래스 멤버)라고 한다.

 

정적필드와 정적메소드는 객체(인스턴스)에 소속된 멤버가 아니라 클래스에 고정된 멤버이기에 클래스 로더가 클래스를 로딩해서 메소드 메모리 영역에 적재할때 클래스별로 관리된다. 따라서 클래스의 로딩이 끝나는 즉시 바로 사용할 수 있다.

  • 객체 생성을 하지 않고 클래스 변수나 메소드를 호출하도록 하는 제어자.

 

static 키워드를 통해 생성된 정적멤버들은 Heap영역이 아닌 Static영역에 할당된다.

Static영역에 할당된 메모리는 모든 객체가 공유하여 하나의 멤버를 어디서든지 참조할 수 있는 장점을 가지지만,

Garbage Collector의 관리 영역 밖에 존재하기 때문에 프로그램이 종료될 때까지 메모리에 값이 유지된 채로 존재하게 된다. 그렇기에 static을 남발하게되면 성능에 악영향을 줄 수 있다.

 

 

static 변수

예를들어 게시물의 좋아요를 누를때마다 좋아요 값을 증가시키는 LikeCount 기능을 구현한다는 가정을 해보자.

package study;
 
public class LikeCount {
 
    int count;
    //static int count;
 
    public LikeCount() {
        this.count++;
        System.out.println("좋아요 개수 : " + count);
    }
 
    public static void main(String[] args) {
        LikeCount lc1 = new LikeCount();
        LikeCount lc2 = new LikeCount();
 
    }
}

위의 예제를 먼저 보면, lc1, lc2 객체가 생성될 때 lc1의 count와 lc2의 count가 서로 다른 메모리를 할당 받게된다.

좋아요 개수 : 1
좋아요 개수 : 1

 

 

그러나 아래와 같이 static변수를 사용하면 두 객체가 생성될 때 lc1, lc2 객체는 하나의 메모리를 공유하게 되는 것이다.

public class LikeCount {
 
//    int count;
    static int count;
 
    public LikeCount() {
        this.count++;
        System.out.println("좋아요 개수 : " + count);
    }
 
    public static void main(String[] args) {
        LikeCount lc1 = new LikeCount();
        LikeCount lc2 = new LikeCount();
 
    }
}

좋아요 개수 : 1
좋아요 개수 : 2

 

 

 

static 메소드

static메소드는 객체의 생성 없이 호출이 가능하고, 객체에서는 호출이 불가능하다.또한 static메소드 안에서는 인스턴스 변수 접근이 불가능 하다.

위와 같이 static메소드 안에선 인스턴스 변수는 접근이 불가능하다.

 

static메소드 안에선 static변수만 접근이 가능한 것을 볼 수 있다.

 

 

 

결론

1. 인스턴스에 공통적으로 사용해야하는 것에 static을 붙인다.

  • 인스턴스를 생성하면, 각 인스턴스들은 서로 다른 독립적인 메모리를 할당받기 때문에 서로 다른 값을 유지한다.
  • 경우에 따라 인스턴스들이 공통적인 값이 유지되어야 하는 경우에는 static을 붙인다.

2. static이 붙은 멤버변수는 인스턴스를 생성하지 않아도 사용할 수 있다.

  • static이 붙은 멤버변수(클래스변수)는 클래스가 메모리에 올라갈 때 이미 자동적으로 생성되기 때문이다.

3. static이 붙은 메소드(함수)에선 인스턴스 변수를 사용할 수 없다.

  • static메소드는 인스턴스 생성 없이 호출 가능한 반면, 인스턴스 변수는 인스턴스를 생성해야만 존재하기 때문에 static이 붙은 메소드를 호출할 때 인스턴스가 생성되어 있을 수도 아닐 수도 있기 때문에 static이 붙은 메소드에서 인스턴스 변수의 사용을 허용하지 않는다.
  • 반대로, 인스턴스변수·메소드에선 static이 붙은 멤버들을 사용하는 것은 가능(인스턴스변수가 존재한다는 것은 static 멤버들은 이미 메모리에 존재한다는 것을 의미하기 때문)

4. 메소드 내에서 인스턴스 변수를 사용하지 않는다면, static을 붙이는 것을 고려한다.

  • 메소드 호출시간이 짧아지기 때문에 효율이 높아진다.

5. 클래스 설계시 static의 사용지침

  • 클래스의 멤버변수중 모든 인스턴스에 공통된 값을 유지해야하는 것이 있는지 보고 있다면 static을 붙여준다.
  • 작성한 메소드 중 인스턴스 변수를 사용하지 않는 메소드에 대해서 static을 붙일 것을 고려한다.

 

일반적으로 인스턴스변수와 관련된 작업을 하는 메소드는 인스턴스메소드 (static X)이고,

static변수(클래스변수)와 관련된 작업을 하는 메소드는 클래스메소드 (static O)이다.

 

 

 

 

 

 

 

 

 

관련 포스팅

[기술면접] JAVA - 1/4
[기술면접] JAVA - 2/4 (주로 예외처리Exception)
[기술면접] JAVA - 3/4 (주로 컬렉션 프레임워크)
[기술면접] JAVA - 4/4

 

 

 

 


출처: 

https://github.com/jvm-hater

https://mangkyu.tistory.com/94
https://dev-coco.tistory.com/153
https://gyoogle.dev/blog/