-
CS 스터디 - Java & SpringCS 2023. 8. 15. 03:37
#1 ) 자바는 인터프리터 언어인가요? 컴파일 언어인가요?
자바는 2가지 방식을 모두 혼합하여 사용합니다. (하이브리드 언어라고도 함)
자바 컴파일러가 .java 파일을 컴파일을 통해 .class파일(바이트코드)로 만듭니다. (이 파일까진 운영체제 독립적. )
이후 JVM의 인터프리터가 각 운영체제에 맞게 이진 코드로 변환합니다. (참고로 JVM은 각 운영체제에 맞게 설치되어야 합니다)
* 참고로 번거롭더라도 이렇게 하는 이유는 자바의 세일링 포인트가 once compiled, use anywhere라는 것이 모토이기 때문이다.
컴파일 - 인터프리터 조합이기 때문에 다소 시간이 느리더라도 이 과정 덕분에 java라는 언어자체는 운영체제에 독립적이다.
또한 JIT 컴파일 방식도 도입돠고, 예전보다 하드웨어 성능들이 발전했기 때문에 느리다는 성능상의 단점을 어느정도 극복하였다.인터프리트 방식 : 실행 할 때마다 기계어로 변환
컴파일 방식 : 실행 전 미리 변환 과정 수행- 자바 컴파일러가 컴파일을 통해 .class 파일을 만들고, 자바 인터프리터가 한 줄 씩 읽으며 이진 코드로 변환하여 실행시킨다.
- 원래 자바 인터프리터만 있었다가, 이 이진코드 변환 과정에 JIT 컴파일러 도입하여 성능 이슈를 해결
- 인터프리터와 JIT컴파일러 모두 이진 코드로 변환하는데 쓰인다.
- 변환 작업은 인터프리터에 의해 지속적으로 수행되지만, 필요한 코드의 정보는 캐시에 담아두었다가 (메모리에 올려두었다가) 재사용 한다.
- 반복되는 코드들은 JIT컴파일러에 의해 컴파일되어 있어 인터프리터의 일을 덜어주는 효과가 있다.
- 컴파일 임계치 (JVM내 메소드 호출 횟수 + 종료된 횟수) 초과하면 JIT 컴파일이 트리거되어 기계어를 캐싱한다고 한다.
#2 ) 자바 프로그램의 수행과정에 대해 말씀해주세요.
자바 소스 코드를 프로그래머가 작성(.java) → 자바 컴파일러가 바이트 코드로 변환 (.class) → JVM 클래스로더로 전달, 클래스 로더가 JVM의 Runtime Data Area(JVM이 OS로부터 할당받는 메모리영역)에 올린다. → 실행 엔진이 바이트 코드들을 명령어 단위로 하나씩 가져와서 실행합니다.
참고 ) 클래스로더의 동작과정 / 물론 클래스로더도 계층 구조라 나뉘어진다.. 이거보다 더 복잡- 클래스로더는 아래 과정을 거쳐 JVM의 Runtime Data Area(JVM이 OS로부터 할당받는 메모리영역)에 올린다.
- 클래스 파일을 가져와서 JVM의 메모리에 로드한다.
- 검증과정을 거친 후 메모리를 할당, 클래스의 상수 풀 내 모든 심볼릭 레퍼런스를 다이렉트 레퍼런스로 변경
- 심볼릭 레퍼런스 : 메모리 주소를 직접 참조하는 것이 아닌, 객체의 이름으로 참조하는 것
- 다이렉트 레퍼런스 : 참조하는 클래스의 특정 메모리 주소를 참조하는 것
- Constant pool에 저장되어 있다가 객체에 접근할 필요가 있으면 해당 객체의 메모리 주소를 찾아 동적으로 연결
- 클래스 변수들을 적절한 값으로 초기화(static initializer 실행, static fielid들을 설정된 값으로 초기화)
#3 ) Major GC와 Minor GC 차이점이 무엇인가요?
Minor GC
Young 영역에서 일어나는 Garbage Collection으로, 속도가 빠르고 Eden, Survivor영역에 속한 젊은 객체들에 대한 Garbage Collection을 의미합니다.
Major GC
Old 영역에서 일어나는 Garbage Collection으로, Minor GC에 비해 속도가 느리고 Old 영역에 속한 오래된 객체들에 대한 Garbage Collection을 의미합니다.
* Stop the World?- GC를 실행하는 스레드를 제외하고 모든 스레드의 작업이 중단되는 현상
- 길어질 수록 서비스 이용에 차질이 생긴다. 이 시간을 최소화시키는 것이 포인트이다.
- 참고로 Minor GC에서도 이 현상은 발생하지만, 무시가능할 정도로 짧은 시간이기 때문에 없다고 보는 경우가 많다.
#4 ) Java의 String 클래스에서 equals()와 == 차이가 무엇인가요?
equals()는 참조 변수의 값이 같은지 비교하는 메소드로, 재정의 되어있지 않다면 객체의 주소값을 비교하여 동일 객체인지 비교후 boolean 값을 리턴하는 메소드입니다.
== 연산자는 primitive type일 경우 값을, 그 외에는 주소값을 비교하는 연산자입니다.
String 클래스에 equals를 사용할 경우에는 주소값이 아닌 데이터 값을 비교하여 동일 여부를 판정합니다. String 클래스 내부에서 equals()를 재정의하였기 때문입니다. 따라서 문자열이 같다면 다른 주소 상에 있어도 동일하다고 판정합니다.
==을 사용하는 경우, 같은 문자열을 가지고 있어도 객체끼리 주소가 다르기 때문에 다르다고 판정합니다.
* 이 답안은 new String( ) 방식으로 객체를 생성한 경우를 가정한 답안이다.
만약 String a = "hello" String b = "hello" 이런식으로 문자열로 생성할 경우 a를 생성할 때 String constant pool에 먼저 hello를 저장해둬서, b를 생성할 때 기존에 있는 hello를 참조하는 방식으로 생성한다. 이 경우엔 == 연산의 결과가 true이다.String a = "Hello"; String b = "Hello"; String c = new String("Hello"); String d = new String("Hello"); System.out.println("a equals b ? " + a.equals(b)); // true System.out.println("a equals c ? " + a.equals(c)); // true System.out.println("a == b? " + (a==b)); // true System.out.println("a == c? " + (a==c)); // false System.out.println("d == c? " + (d==c)); // false System.out.println("a == d? " + (a==d)); // false
#5 ) equals() 함수만 오버라이딩하고 hashCode()는 오버라이딩 하지 않으면 어떤 문제가 발생할까요?
equals()는 객체의 주소값을 비교하여 동일 객체인지 비교후 boolean 값을 리턴하는 메소드입니다.
hashCode()는 객체의 주소값을 이용하여 해싱 기법을 적용한 결과값을 리턴하는 메소드입니다.
자바의 해시 컬렉션은 동일 객체인지 판정할 때 hashCode() 값이 같은지 먼저 확인하고 같다면 equals()함수를 호출하여 같은지 총 2번의 확인 작업을 거칩니다.
equals()만 오버라이딩할 경우, equals()로 객체를 비교할 때에는 동일하다고 판정하였으나 hashCode() 값이 달라 다른 객체로 판단되어 hash Collections 자료구조에 동일하다고 의도했던 두 객체가 모두 추가되는 등 원치 않은 방향으로 동작할 수 있기 때문에 equals()와 hashCode() 두 메소드 모두 재정의해주는 과정이 필요합니다.
* 재정의하지 않을 경우Set<Fruit> fruits = new HashSet<>(); fruits.add(new Fruit(1000, "자두")); fruits.add(new Fruit(1000, "자두")); System.out.println(fruits.size()); // 2가 출력
아래 코드 처럼 equals()만 오버라이딩한 경우, 위 코드 수행시 그대로 사이즈 2 나온다.
private static class Fruit { int price; String name; public Fruit(int price, String name) { this.price = price; this.name = name; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Fruit fruit = (Fruit) o; return price == fruit.price && Objects.equals(name, fruit.name); } } }
아래 코드처럼 hashCode()까지 오버라이딩 해줘야 제대로 사이즈 1나온다.
참고로 인텔리제이에서는 커맨드 + N키로 간편하게 오버라이딩이 가능하다.import java.util.*; public class Main { public static void main(String[] args) { Set<Fruit> fruits = new HashSet<>(); fruits.add(new Fruit(1000, "자두")); fruits.add(new Fruit(1000, "자두")); System.out.println(fruits.size()); } private static class Fruit { int price; String name; public Fruit(int price, String name) { this.price = price; this.name = name; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Fruit fruit = (Fruit) o; return price == fruit.price && Objects.equals(name, fruit.name); } @Override public int hashCode() { return Objects.hash(price, name); } } }
# 6 ) Java는 매개변수를 call by value로 전달하나요 call by reference로 전달하나요?
Call by value는 메소드의 매개변수로 값을 넘길때 원래 값은 놔두고, 새롭게 값을 복제하여 넘기는 방식입니다.
Call by reference는 매개변수로 넘어온 값의 참조 자체를 넘기는 방식입니다.
자바의 참조형때문에 call by reference라고 생각하기 쉽지만, call by reference도 결국 주소 값을 전달하는 방식입니다.
참조 자체를 넘긴다면 new로 할당을 했을 때 methodA의 원래 값도 new로 할당한 값으로 변화해야 하지만, 자바는 그렇지 않기 때문입니다. (포인터처럼 메모리에 직접 new 할당하면 해당 객체 쓰는 메소드들 다 영향 받음)
public void methodA() { Pokemon pokemon = new Pokemon(1, "피카츄"); System.out.println("Before methodB -----------------------"); System.out.println("pokemon.name = " + pokemon.name); System.out.println("pokemon.gen = " + pokemon.gen); methodB(pokemon); System.out.println("After methodB -----------------------"); System.out.println("pokemon.name = " + pokemon.name); System.out.println("pokemon.gen = " + pokemon.gen); } public void methodB(Pokemon pokemon) { pokemon = new Pokemon(2, "치코리타"); System.out.println("in method B -------------------"); System.out.println("pokemon.name = " + pokemon.name); System.out.println("pokemon.gen = " + pokemon.gen); }
위 코드의 출력값은 아래와 같다.
Before methodB ----------------------- pokemon.name = 피카츄 pokemon.gen = 1 in method B ------------------- pokemon.name = 치코리타 pokemon.gen = 2 After methodB ----------------------- pokemon.name = 피카츄 pokemon.gen = 1
# 7 ) Spring과 Spring Boot의 차이점이 무엇인가요?
Spring은 프레임워크로, 개발자가 직접 설정파일 작성을 통해 컨테이너 구성, 빈 객체 등록, 의존성 설정 등을 수동으로 해줘야 했습니다.
Spring Boot는 Spring에서 제공하는 여러 기능들을 자동으로 설정하여 Spring을 쉽게 사용하도록 만든 도구입니다. 프로젝트의 설정, 라이브러리 의존성 관리 등 다양한 기능들을 제공하여 편리하다는 장점이 있습니다.
Spring Boot는 내장 톰캣 서버를 사용하여 따로 톰캣을 설치할 필요가 없고, starter를 통해 dependency를 자동화하며(버전관리 자동화), jar file을 이용해 자바 옵션만으로 손쉽게 배포가 가능하다는 차이가 있습니다.# 8 ) Bean Scope 종류는 어떤 것이 있나요?
Bean Scope란 문자 그대로 빈이 존재할 수 있는 범위를 의미하며, 스프링 빈은 기본적으로 싱글톤 스코프로 관리되어 스프링 컨테이너의 시작과 함께 생성, 컨테이너 종료될 때까지 유지됩니다.
싱글톤 스코프 외에 스프링 컨테이너가 빈 생성에서 의존관계 주입까지만 관여하는 프로토타입 스코프, 웹 어플리케이션 환경에서 사용하는 웹 스코프인 request, session, application 스코프가 있습니다.
싱글톤- 기본 스코프, 스프링 컨테이너의 시작과 종료까지 유지되는 가장 넓은 스코프
- 명시적으로 scope를 지정하지 않으면 싱글톤으로 관리된다.
프로토타입
- 빈 생성 ~ 의존관계 주입까지만 스프링 컨테이너가 관리하고 더는 관리하지 않는 스코프(@Predestroy 같은 메서드 호출 X)
웹 관련 스코프 (request, session, global session의 Scope는 일반 Spring 어플리케이션이 아닌, Spring MVC Web Application에서만 사용)
- request : 웹 요청이 들어오고 나갈 때까지 유지되는 스코프
- session : 웹 세션 생성 ~ 종료까지 유지되는 스코프
- application : 웹의 서블릿 컨텍스트와 같은 범위로 유지되는 스코프
# 9 ) Spring Bean 라이프사이클에 대해 간단히 설명해주세요.
스프링 컨테이너가 생성되고, 컨테이너에 의해 빈이 생성됩니다. 빈이 생성되면 의존관계가 주입되고, 초기화 콜백을 통해 의존관계 주입 완료를 알립니다. 그 후 빈을 사용하고, 빈 스코프에 따라 소멸 시 소멸전 콜백이 일어나고 (싱글톤이라면 스프링 컨테이너 종료 전 등) 스프링이 종료됩니다.
#10 ) Spring이 Request를 처리하는 과정에 대해 설명해주세요.
- Client 요청을 Dispatcher Servlet이 받음
- Dispatcher Servlet ?
- HTTP 프로토콜로 들어오는 모든 요청을 가장 먼저 받아 적합한 Controller에 위임
- 개발자는 컨트롤러만 구현하면 얘가 알아서 적합한 컨트롤러로 넘겨준다. (모든 서블릿을 다 등록해줄 필요가 없다는 장점)
- Servelet
- 클라이언트의 요청을 처리하고, 그 결과를 반환하는 Servlet 클래스의 구현 규칙을 지킨 자바 웹 프로그래밍 기술
- 참고할만한 글 : https://mangkyu.tistory.com/14
- Dispatcher Servlet ?
- Handler Mapping이 요청 정보를 통해 요청을 위임할 Controller 찾는다.
- 요청을 Controller로 위임할 Handler Adpater를 찾아 전달
- Controller 구현 방식이 다양하기 때문에, 구현방식에 상관없이 요청을 위임하도록 Handler Adapter라는 인터페이스 사용
- Handler Adpater가 Controller로 요청을 위임
- 요청에 맞게 비즈니스 로직을 처리 후 Controller 가 반환값을 반환
- Handler Adapter가 반환값을 반환
- 최종적으로 Dispatcher Servlet이 클라이언트에게 서버의 응답 반환
- 경우에 따라 View Resolver가 View이름에 맞게 내려주기도 함.
기타 참고 자료'CS' 카테고리의 다른 글
CS 스터디 - 데이터베이스(2) (0) 2023.09.06 CS 스터디 - Java (0) 2023.08.31 CS 스터디 - 운영체제 Q&A(1) (0) 2023.08.10 CS 스터디 - 네트워크 Q&A (1) (0) 2023.08.03 CS 스터디 - 운영체제 (0) 2023.07.21