본문 바로가기

배부장님의 백엔드 면접 질문 - Java

Q. JVM의 구조와 Java의 실행방식

자바로 작성된 코드는 자바 컴파일러에 의해 바이크 코드로 변환되어 자바 가상머신위에서 실행됩니다. 바이트 코드, 즉 클래스 파일을 JVM으로 로드시켜 주는 것이 Class Loader 입니다. JVM은 크게 Class Loader, GC, Execution Engine, Memory 4개로 나눌 수 있습니다.

 

 Class Loader는 클래스 파일을 JVM으로 로드시키고, 유효한지 검사합니다.

 

Execution Engine은 JVM 메모리에 저장된 바이트코드를 명령어 단위로 실행합니다.초기에는 인터프리터 방식으로 실행하여 느렸지만, JIT 컴파일러를 이용해 바이너리 코드로 변경시켜 실행시켰습니다.

 

메모리 영역은 메소드 영역, 힙, 스택, 레지스터, 네이티브 메소드 스택으로 나눌 수 있습니다. 이 때, 스레드는 메소드 영역과 힙을 공유합니다.

 메소드 영역에는 static 변수, 메소드 데이터, 클래스 데이터, 객체 구조 등이 저장됩니다.

 힙 영역에는 자바 객체가 저장됩니다. 스레드끼리 공유하므로, Thread Safe하지 않으므로 동시성을 지켜줘야 합니다. 이 영역은 GC의 대상이 되어 메모리 관리가 됩니다.

 스택은 메소드 호출 시 사용되는 값들을 저장하고, 메소드가 종료되면 사용된 값을 삭제합니다. 사용되는 값에는 지역변수, operand stack 등이 있습니다.

 레지스터에는 각 스레드들이 실행할 메소드의 주소값이 저장됩니다.

 네이티브 메소드 영역에는 Java가 아닌 다른 언어로 작성된 메소드가 저장됩니다.

 


Q. 자바에서 컬렉션 프레임워크는 무엇인가?

컬렉션 프레임워크는 객체와 데이터를 효율적으로 조회, 삽입, 삭제, 검색 하기 위해 사용되는 라이브러리를 의미합니다. 컬렉선 프레임워크의 장점에는 재사용성, 제너릭 타입 사용으로 인한 안정성 증가 등이 있습니다.

 

 종류는 크게 Set과 List, Map으로 나뉩니다. List에는 어래이리스트, 링크드리스트, 벡터 등이 존재합니다. 특징으로는 중복이 허용되고 순서가 존재합니다. Set에는 해시 set, 트리 셋이 있으며 중복값을 허용하지 않으며 순서가 없습니다. Map은 키과 값을 동시에 저장하며 키의 중복을 허용하지 않습니다. 종류에는 해시맵, 트리맵 등이 있습니다.

 

 Map은 List와 Set과는 다르게 Collection 인터페이스를 정의하지 않습니다. 왜냐하면 Map은 컬렉션이 아니기 때문입니다. 한개의 값이 아니라 2개의 값을 가집니다. 그래서 공통으로 사용될 것으로 보이는 메소드의 동작원리가 다릅니다.

 

Q. 컬렉션 프레임워크가 Cloneable 과 Serializable 인터페이스를 상속받지 않는 이유?

컬렉션은 하나의 객체가 아닌 추상 인터페이스입니다. 데이터가 어떻게 유지되고, 관리될지에 대한 가이드만 제시합니다, 그래서 어떻게 사용될 지는 컬렉션을 구현한 구현체에서 결정해야 합니다.

 

 

Q. HashMap 과 TreeMap을 설명해라

HashMap은 키와 값 쌍으로 구성되어있으며 해싱 알고리즘을 사용합니다. 키값을 해싱시켜 어디에 저장할지 결정합니다. 저장되는 곳은 Entry라는 배열에 저장됩니다. 이 때, 배열은 여러개의 링크드리스트로 구현되어 있습니다. 그래서 해시 충돌 문제를 해결했습니다.

값을 삽입할 때는 put 메소드를 사용하도 값을 찾아서 갖고올 때는 get 메소드를 사용합니다.

 put 메소드를 사용할 때는 일단 hashCode 메소드를 호출해서 key값을 해시값으로 변환시켜, 해당 위치에 값을 저장합니다.

 get 메소드를 사용할 때는 키값을 해싱시켜 배열에 바로 접근해서 값을 가져옵니다. 키 값을 확인할 때는 equals 메소드를 이용해서 확인합니다. 즉, 키값이 같기 위해서는 해싱된 값이 같고, equals 메소드가 true를 반환해야 합니다.

 검색과 삽입에 O(1) 이 소요됩니다.

 

TreeMap은 레드 블랙 트리 형태로 저장됩니다. 즉, 삽입과 동시에 키값을 기준으로 정렬이 이뤄집니다. 그래서 삽입과 검색에 O(logN) 이 소요됩니다.

 

* Map 구조에서 equals와 hashCode 메소드는 중요하다. 만약 키나 값이 직접 만든 클래스라면 equals와 hashCode 를 둘 다 오버라이딩 해주거나 하지 말아야 한다. 가장 좋은 경우는 Key값으로 사용되는 클래스를 불변으로 만들어서 값이 변해서 생기는 문제를 해결할 수 있다. hashCode와 equals는 저장되는 값이 같은지 다른지 확인하는 용도로 사요되기 때문입니다.

Q. HashMap 과 HashTable 차이를 설명해라

작동원리는 비슷합니다. HashTable은 키 값을 해싱해서 키, 값 형태로 버킷에 저장합니다. 만약 키 값이 같을 경우 충돌이 생기므로 링크드 리스트 형태로 저장합니다. 이 떄 키 값이 같을 때 다른 객체가 있다면, equals 메소드를 이용해 true를 반환하면 덮어쓰고, false를 반환하면 링크드 리스트 뒤에 추가합니다.

 

둘 의 차이는 여러가지 있습니다.

 

1. Thread-Safe

해시테이블은  Thread-Safe하고 해시맵은 아닙니다. 그래서 멀티스레드 환경에서는 해시테이블이 좋지만, 그렇지 않으면 해시맵보다 성능이 떨어집니다.

 

2. Null 허용 안함

해시테이블은 키 값이 Null를 허용하지 않지만 해시맵은 허용합니다.

 

3. 중복키의 처리

만약 키값이 같고, equals 메소드로 true를 반환한다면 해시테이블은 값을 덮어씌우지 않습니다. 그러나 해시맵은 값을 덮어씌웁니다,

 

 

 


Q. 오버라이딩과 오버로딩을 설명해라

오버라이딩은 부모 클래스에서 상속받은 메소드를 재정의 하는 것이고, 오버로딩은 같은 이름이지만, 파라미터가 다른 메소드를 만드는 것입니다.

 

 오버로딩은 같은 Scope 내의 이름이 같은 메소드를 만드는 것입니다. 이 때, 메소드의 구분은 파라미터의 타입과 수로 합니다. 리턴타입은 구분의 기준이 될 수 없습니다. 이 때, 접근제어자도 자유롭게 지정해줄 수 있지만 오버로딩의 기준이 되지는 않습니다. 오버로딩은 다형성을 구현하는 방법 중 하나입니다. 그래서, 메소드 호출 시, 파라미터의 타입이나 개수를 크게 신경쓰지 않고 호출할 수 있습니다. 대표적인 예시에는 println 메소드가 있습니다.

 

 오버라이딩은 상속받은 매소드를 재정의하는 것입니다. 이 때 상속받은 메소드는 private 메소드, static 메소드, final 메소드를 제외한 모든 메소드입니다. 오버라이딩에는 조건이 있습니다. 부모 클래스의 메소드보다 더 큰 예외를 선언할 수 없으며, 접근제어자의 범위를 더 좁힐 수 없습니다.메소드 이름, 파라미터, 리턴타입이 모두 부모 메소드와 같아야 합니다. 만약 파라미터가 다르다면 오버로딩이 되어 버립니다.

 만약 인터페이스의 메소드를 재정의 해서 사용하는 경우에는 반드시 public 메소드여야 합니다.

 

 static 메소드가 상속되지 않는 이유는 객체의 메소드가 아닌, 클래스 메소드이기 때문입니다. static은 컴파일 시 생성되므로 런타임시 실제 객체의 메소드를 호출하는 방식과는 차이가 있습니다. 

 

 

Q. 오버라이드 어노테이션

오버라이드 어노테이션은 적지 않아도 큰 상관은 없습니다. 오버라이딩이 잘못된 경우, 경고를 받기 위해서 사용합니다. 만약 부모 메소드가 변경되었다면 컴파일 오류가 일어나도록 해줍니다


Q. Annotation이 뭐냐?

어노테이션은 클래스, 메소드, 변수 앞에 @ 표시를 해서 데이터의 유효성 검사 등을 할 수 있게 해줍니다. 어노테이션을 작성해서 메타데이터를 추가할 수 있습니다 

 


Q. 인터페이스와 추상 클래스의 차이점을 설명하시오.

추상 클래스는 상속을 통해서, 자식 클래스에서 완성하도록 유도하는 클래스입니다. 인터페이스는 객체가 어떻게 작동해야 할지를 강제해줍니다.

 

 추상클래스는 상속을 위해서 만들어지므로 객체가 될 수 없으며 추상 메소드가 존재합니다. 즉 하나이상의 추상메소드가 있다면 추상클래스가 됩니다. 이 추상메소드는 반드시 오버라이딩 되야 합니다. 추상메소드만 있다면 생성자, 필드, 일반 메소드도 존재할 수 있습니다.

 

 인터페이스는 상속받은 클래스가 어떻게 작동해야 할지를 알려줍니다. 추상 메소드만 존재할 수 있으며 이 추상메소드는 반드시 오버라이딩 되야 합니다. 즉, 상속받은 클래스가 어떻게 동작해야할지를 나타내주는 기본 설계도라고 할 수 있습니다.

 

가장 큰 차이는 다중상속의 가능여부입니다. 클래스의 상속은 1개만 가능하지만 인터페이스는 다중상속이 가능합니다,.클래스의 다중상속은 메소드 출처의 모호성 등으로 허용하지 않고 있으며 인터페이스는 허용합니다. 

 

 상속을 받은 클래스의 관계는 is A 관계가 됩니다. 그러나 인터페이스를 구현한 클래스는 has A 관계가 됩니다. 이 의미는 다중상속의 가능여부를 나타냅니다.

 추상클래스는 부모-자식 관계가 명확하고, 부모의 메소드를 그대로 사용하는 경우가 있을 경우 사용되고, 인터페이스는 그 외의 경우에 사용됩니다. 예를 들어, 개의 조상인 동물, 포유류는 추상클래스가 되야 합니다. 그러나, 네발이 달린 동물의 행동을 정의하기 위해서는 인터페이스를 사용하는 것이 적절합니다.

 


Q. 접근 제어자에는 어떤 것들이 있냐?

접근제어자는 클래스 내부를 캡슐화하기 위해서 사용됩니다. 자바에는 4가지 접근제어자가 존재하며, 종류에 따라 접근 여부가 달라집니다.

public은 전체 프로젝트에서 사용할 수 있고, protected는 자식클래스까지 사용할 수 있으며, default는 같은 패키지에서 사용할 수 있습니다. private는 같은 클래스 내에서만 사용 가능합니다. 생략시 default로 자동 설정 됩니다.

 

 대상에 따라 사용가능한 접근제어자가 다른데, 지역변수는 사용불가하고, 클래스는 public과 default만 가능합니다. 메소드와 멤버변수는 모두 가능합니다.

 


Q. 객체 지향에 대해 설명해라

실제 세계의 논리적인 것이나 물질적인 것을 컴퓨터 세계로 가져오는 것을 추상화라고 합니다. 이 추상화를 통해서 만들어진 것들을 객체라고 합니다.

 

객체 지향 프로그래밍이라는 뜻은 객체들간의 상효작용을 통해 프로그램을 만드는 것을 의미합니다. 특징으로는 캡슐화, 상속, 다형성, 추상화가 있습니다.

 기존의 절차지향은 프로그램을 기능 중심으로 바라봤습니다. 프로그램이 어떤 절차로 동작하는지가 핵심이었지만, 객체지향은 객체가 핵심입니다. 객체들이 어떤 일을 할것인가, 일을 어떤 객체가 할 것인지가 핵심입니다.

 

그래서 많은 기능을 구현하고, 복잡한 코드의 경우 객체지향이 유리합니다. 객체 지향 프로그래밍을 통해 생산성을 늘릴 수 있으며 실세계에 대한 모델링을 쉽게 할 수 있습니다. 그리고 캡슐화를 통해 보안성 향상을 할 수 있습니다. 그러나, 캡슐화의 격리구조와 객체에 대한 메모리 연산과 크기 때문에 속도가 느리다는 단점이 있습니다.


Q. SOLID 에 대해서 설명해라.

순서대로 단일책임, 개방폐쇄, 리스코프치환, 인터페이스 분리, 의존 역전원칙을 의미합니다.

 

단일책임원칙이란, 하나의 클래스는 자신의 기능만 해야한다는 것입니다. 만약 동물이라는 클래스가 있다면 담당해야 할 기능이 너무 많습니다. 각 동물들의 특징들을 모두 메소드로 구현하기에는 많기 때문에 공통된 기능을 제외하고는 상속구조를 통해서 역할을 나누는 것이 좋습니다.

 

개방폐쇄원칙이란, 소프트웨어 엔티티는 확장에 대해서 열려있지만 변경에 대해서 닫혀있다는 것입니다. 이 원칙을 기치기 워한 대표적인 패턴은 어댑터 패턴입니다. 클래스 끼리 바로 연결되어 있다면, 한 쪽 클래스의 변화가 다른 클래스에도 영향을 미칩니다. 그래서, 그 중간에 어댑터를 둬서 어댑터만 변경시키면 되도록 해줍니다. 예시로은 JDBC 드라이버가 있습니다. 데이터베이스가 MySQL을 사용하든, ORACLE을 사용하든 연결하는 부분만 변경해주면 실제 DB를 사용하는 코드는 변화없이 사용가능합니다.

 

리스코프 치환 원칙은 자식클래스는 부모클래스를 대체할 수 있어야 한다는 것입니다. 즉, 상속관계의 있는 클래스가 is A 관계를 잘 지키고, 인터페이스를 구현한 클래스는 is able to 를 잘 지키는 원칙입니다.

 

인터페이스 분리 원칙은 단일 책임 원칙의 다른 유형이라고 볼 수 있습니다. 여러가지 역할을 상속관계로 나눈 것이 단일 책임 원칙이면, 인터페이스 분리 원칙은 인터페이스로 나눈 것입니다.

 

의존 역전 원칙은 고차원 모듈은 저차원 모듈에 의존해서는 안된다는 것입니다. 만약, 자동차가 타이어에 의존하면 타이어가 변화할 시 자동차도 변화하므로 안됩니다. 그래서, 자동차를 타이어를 추상화한 인터페이스에 의존하게 만들면 타이어의 변화에 영향을 받지 않습니다.

즉, 하위클래스가 아닌 상위 클래스, 인터페이스 등을 통해 의존하라는 원칙입니다.

 


Q. 객체에서 Equals 메소드와 HashCode 메소드가 의미하는 바는?

equals 메소드는 객체끼리 내용을 비교할 수 있습니다. 연산자를 사용한 비교시, 대상의 주소값이 비교가 됩니다. 그래서 같은 문자열이라도 다르다고 나올 수 있습니다. 그래서 equals를 사용하면 내용을 비교할 수 있습니다. 직접 만든 클래스의 경우, equals 메소드를 오버라이딩해서 원하는 대로 비교할 수 있습니다.

 

HashCode 메소드는 각 객체의 주소값을 변환하여 생성하는 객체의 고유한 정수값입니다. 만약 equals 메소드가 true를 반환하면 HashCode는 같은 값을 반환해야 합니다. 그러나 Equals 메소드가 false를 반환해도 hashCode는 같은값을 반환 할 수 있습니다.

 

 


Q. MSA에 대해서 설명하시오

MSA는 마이크로서비스 아키텍쳐로 하나의 애플리케이션을 여러개의 작은 서비스로 나눠서 개발하는 방식입니다. 기존의 모놀리틱  아키텍쳐는 애플리케이션을 위한 구성요소가 하나의 서비스에 통합되어 개발하는 방식입니다. 이 아키텍쳐의 단점은 한 부분의 장애가 모든 부분에 여향을 미치고, 유지 보수가 오래 걸리고 변화시키기 어렵습니다.

 MSA는 서비스의 End Point를 외부에 노출시키고, 서비스 끼리 통신하는 방식으로 애플리케이션이 동작합니다. 


Q. REST API란?

REST API는 REST 아키텍쳐의 조건을 준수하는 API를 의미합니다. REST는 API 작동 방식에 제약을 주는 아키텍쳐입니다. REST 기반 아키텍쳐는 URI를 통해 자원을 나타내고, HTTP 메소드를 통해 해댱 자원에 대한 CRUD를 적용하는 형태입니다. 

 REST API를 사용하면 HTTP 를 이용하는 것이므로 별도의 인프라를 구축할 필요가 없으면 많은 곳에서 사용이 가능합니다. 또한 API 메시지를 통해서 어떤 자원이 어떻게 다뤄질 것인지를 알 수 있습니다.


Q. DI, AOP, IOC

 

IoC는 제어역전입니다. 객체의 생성, 제거, 관계 설정 등에 대한 제어를 애플리케이션이 하는 것인 아니라, 프레임 워크의 컨테이너에서 합니다. 즉 제어권을 넘깁니다. 객체에 대한 관리를 개발자가 아니라, 프레임워크가 대신 해주면서 개발자가 개발에 더 집중할 수 있습니다.

 

DI는 의존성 주입으로 하나의 객체가 다른 객체의 의존성을 제공해주는 기술입니다.의존성은 객체지향에서 상속 혹은 인터페이스로 나타낼 수 있습니다. 의존성을 외부에서 결정하는 것이 의존성 주입입니다. 생성자나 Setter 메소드를 통해 외부에서 의존성을 주입할 수 있습니다. 즉, IoC를 구현한 것이 DI 입니다.

 그래서 객체간의 의존성을 줄일 수 있습니다. 외부에서 결정하므로 하나의 객체가 변화해도 다른 객체는 영향을 받지 않습니다. 그래서 재사용성을 늘릴 수 있습니다.

 

두 개념 모두 객체간의 결합을 느슨하게 만들어 유연하고 확장성 뛰어난 코드를 작성하기 위해 사용합니다.

 

AoP는 관점 지향 프로그래밍으로 핵심 비즈니스 로직이 아닌 부가기능들을 모듈화 하는 방식입니다. AoP를 이용하면 기존 코드를 수정하지 않고도 부가기능들을 추가할 수 있습니다. 부가기능에는 로깅, 시간체크 등이 있습니다.

'Etc.' 카테고리의 다른 글

3월말평가  (0) 2022.03.25
NPM : Unsplash-js 유의사항  (0) 2021.05.04