JavaRush /Java Blog /Random-KO /Java 개발자 인터뷰의 질문과 답변을 분석합니다. 15부

Java 개발자 인터뷰의 질문과 답변을 분석합니다. 15부

Random-KO 그룹에 게시되었습니다
안녕 안녕! Java 개발자는 얼마나 알아야 합니까? 이 문제에 대해 오랫동안 논쟁을 벌일 수 있지만 사실 인터뷰에서 이론에 따라 최대한 노력하게 될 것입니다. 업무에 사용할 기회가 없는 지식 영역에서도 마찬가지입니다. Java 개발자 인터뷰의 질문과 답변을 분석합니다.  파트 15 - 1글쎄, 당신이 초보자라면 이론적 지식이 매우 진지하게 받아 들여질 것입니다. 아직 경험도 없고 큰 성과도 없기 때문에 남은 것은 지식 기반의 강도를 확인하는 것뿐입니다. 오늘 우리는 Java 개발자에게 가장 인기 있는 면접 질문을 검토하여 이러한 기반을 계속해서 강화할 것입니다. 날아보자!

자바 코어

9. Java의 정적 바인딩과 동적 바인딩의 차이점은 무엇입니까?

나는 정적 및 동적 다형성에 관한 질문 18에서 이 질문에 이미 답변했습니다 . 읽어 보시기 바랍니다.

10. 인터페이스에서 개인 변수나 보호 변수를 사용할 수 있습니까?

아니 당신은 할 수 없습니다. 인터페이스를 선언할 때 Java 컴파일러는 자동으로 인터페이스 메서드 앞에 publicabstract 키워드를 추가하고 데이터 멤버 앞에 public , staticfinal 키워드를 추가하기 때문입니다. 실제로 private 또는 protected를 추가 하면 충돌이 발생하고 컴파일러는 "'<selected modifier>' 수정자는 여기서 허용되지 않습니다."라는 메시지와 함께 액세스 수정자에 대해 불평합니다. 컴파일러가 public , staticfinal을 추가하는 이유는 무엇입니까? 인터페이스의 변수? 그것을 알아 봅시다 :
  • 공개 - 인터페이스를 통해 클라이언트가 객체와 상호 작용할 수 있습니다. 변수가 공개되지 않은 경우 클라이언트는 해당 변수에 액세스할 수 없습니다.
  • static - 인터페이스(또는 해당 객체)를 생성할 수 없으므로 변수는 정적입니다.
  • final - 인터페이스는 100% 추상화를 달성하는 데 사용되므로 변수는 최종 형식을 갖습니다(변경되지 않음).

11. 클래스로더란 무엇이며 어떤 용도로 사용됩니까?

클래스 로더 (또는 클래스 로더)는 Java 클래스 로딩을 제공합니다. 보다 정확하게는 하위 항목인 특정 클래스 로더에 의해 로딩이 보장됩니다. ClassLoader 자체는 추상적입니다. 예를 들어 해당 클래스의 생성자 또는 정적 메서드를 호출한 후 .class 파일이 로드될 때마다 이 작업은 ClassLoader 클래스의 하위 클래스 중 하나에 의해 수행됩니다 . 상속인에는 세 가지 유형이 있습니다.
  1. Bootstrap ClassLoader 는 JVM 수준에서 구현되는 기본 로더이며, JVM 커널의 일부이고 기본 코드로 작성되므로 런타임 환경으로부터 피드백을 받지 않습니다. 이 로더는 다른 모든 ClassLoader 인스턴스의 상위 역할을 합니다.

    $JAVA_HOME/jre/lib 디렉토리 에 있는 JDK 내부 클래스(일반적으로 rt.jar 및 기타 핵심 라이브러리)를 로드하는 일을 주로 담당합니다 . 플랫폼마다 이 클래스 로더의 구현이 다를 수 있습니다.

  2. 확장 클래스로더 는 기본 로더 클래스의 자손인 확장 로더입니다. 표준 Java 기본 클래스의 확장 로드를 관리합니다. JDK 확장 디렉터리(일반적으로 $JAVA_HOME/lib/ext 또는 java.ext.dirs 시스템 속성에 언급된 다른 디렉터리)에서 로드됩니다(이 옵션은 확장 로드를 제어하는 ​​데 사용할 수 있음).

  3. System ClassLoader 는 모든 애플리케이션 수준 클래스를 JVM에 로드하는 작업을 담당하는 JRE 수준에서 구현된 시스템 로더입니다. 클래스 환경 변수 -classpath 또는 -cp 명령줄 옵션에 있는 파일을 로드합니다.

Java 개발자 인터뷰의 질문과 답변을 분석합니다.  파트 15 - 2클래스 로더는 Java 런타임의 일부입니다. JVM이 클래스를 요청하는 순간 클래스 로더는 클래스를 찾고 클래스의 정규화된 이름을 사용하여 클래스 정의를 런타임에 로드하려고 시도합니다. java.lang.ClassLoader.loadClass() 메소드는 런타임에 클래스 정의를 로드하는 역할을 합니다. 전체 이름을 기반으로 클래스를 로드하려고 시도합니다. 클래스가 아직 로드되지 않은 경우 요청을 상위 클래스 로더에 위임합니다. 이 프로세스는 재귀적으로 발생하며 다음과 같습니다.
  1. 시스템 클래스로더는 캐시에서 클래스를 찾으려고 시도합니다.

    • 1.1. 클래스를 찾으면 로딩이 성공적으로 완료된 것입니다.

    • 1.2. 클래스를 찾을 수 없으면 로딩이 Extension Classloader에 위임됩니다.

  2. Extension Classloader는 자체 캐시에서 클래스를 찾으려고 시도합니다.

    • 2.1. 클래스가 발견되면 성공적으로 완료됩니다.

    • 2.2. 클래스를 찾을 수 없으면 로딩이 Bootstrap Classloader에 위임됩니다.

  3. Bootstrap Classloader는 자체 캐시에서 클래스를 찾으려고 시도합니다.

    • 3.1. 클래스를 찾으면 로딩이 성공적으로 완료된 것입니다.

    • 3.2. 클래스를 찾을 수 없으면 기본 Bootstrap 클래스 로더가 해당 클래스를 로드하려고 시도합니다.

  4. 로드하는 경우:

    • 4.1. 성공 - 클래스 로딩이 완료되었습니다.

    • 4.2. 실패하면 제어가 Extension Classloader로 이전됩니다.

  5. 5. 확장 클래스 로더는 클래스 로드를 시도하며, 로드하는 경우 다음을 수행합니다.

    • 5.1. 성공 - 클래스 로딩이 완료되었습니다.

    • 5.2. 실패하면 제어가 시스템 클래스로더로 이전됩니다.

  6. 6. 시스템 클래스로더는 클래스 로드를 시도하며, 로드하는 경우 다음을 수행합니다.

    • 6.1. 성공 - 클래스 로딩이 완료되었습니다.

    • 6.2. 성공적으로 통과하지 못했습니다. 예외가 생성되었습니다. - ClassNotFoundException.

클래스 로더에 관한 주제는 방대하므로 무시해서는 안 됩니다. 이에 대해 더 자세히 알아보려면 이 기사를 읽어 보시기 바랍니다 . 그러면 더 이상 머뭇거리지 않고 계속 진행하겠습니다.

12. 런타임 데이터 영역이란 무엇입니까?

런타임 데이터 영역 - JVM 런타임 데이터 영역입니다. JVM은 프로그램 실행 중에 필요한 일부 런타임 데이터 영역을 정의합니다. 그 중 일부는 JVM이 시작될 때 생성됩니다. 다른 것들은 스레드 로컬이며 스레드가 생성될 때만 생성됩니다(그리고 스레드가 소멸될 때 소멸됩니다). JVM 런타임 데이터 영역은 다음과 같습니다. Java 개발자 인터뷰의 질문과 답변을 분석합니다.  파트 15 - 3
  • PC 레지스터는 각 스레드에 대해 로컬이며 스레드가 현재 실행 중인 JVM 명령어의 주소를 포함합니다.

  • JVM 스택은 로컬 변수 및 임시 결과를 저장하는 데 사용되는 메모리 영역입니다. 각 스레드에는 자체 별도의 스택이 있습니다. 스레드가 종료되자마자 이 스택도 삭제됩니다. 힙에 비해 스택의 장점은 성능인 반면 힙은 확실히 스토리지 규모면에서 장점이 있다는 점은 주목할 가치가 있습니다.

  • 네이티브 메소드 스택 - 네이티브(비Java) 메소드를 실행하기 위해 JVM 스택과 유사한 데이터 요소를 저장하는 스레드별 데이터 영역입니다.

  • 힙 - 런타임 시 생성되는 개체, 클래스 메타데이터, 배열 등을 포함하는 저장소로 모든 스레드에서 사용됩니다. 이 영역은 JVM이 시작될 때 생성되고 종료될 때 소멸됩니다.

  • 메소드 영역 - 이 런타임 영역은 모든 스레드에 공통되며 JVM이 시작될 때 생성됩니다. 런타임 상수 풀, 생성자 및 메소드 코드, 메소드 데이터 등과 같은 각 클래스의 구조를 저장합니다.

13. 불변 객체란 무엇입니까?

기사의 이 부분 인 질문 14와 15 에는 이미 이 질문에 대한 답변이 있으므로 시간을 낭비하지 말고 살펴보시기 바랍니다.

14. String 클래스의 특별한 점은 무엇입니까?

분석 초반에 우리는 String의 특정 기능에 대해 반복적으로 설명했습니다 (이에 대해서는 별도의 섹션이 있었습니다). 이제 String 의 기능을 요약해 보겠습니다 .
  1. Java에서 가장 널리 사용되는 객체이며 다양한 목적으로 사용됩니다. 사용빈도 측면에서는 원시형에 비해 열등하지 않습니다.

  2. 이 클래스의 객체는 new 키워드를 사용하지 않고 따옴표를 통해 직접 생성될 수 있습니다. String str = "string"; .

  3. String 은 불변 클래스 입니다 . 이 클래스의 객체를 생성할 때 해당 데이터는 변경할 수 없습니다(특정 문자열에 + "다른 문자열"을 추가하면 결과적으로 새로운 세 번째 문자열을 얻게 됩니다). String 클래스는 불변성으로 인해 스레드로부터 안전합니다.

  4. String 클래스 는 마무리되었으므로( final 수정자가 있음 ) 상속될 수 없습니다.

  5. String 에는 생성된 문자열 값을 캐시하는 힙의 메모리 영역인 자체 문자열 풀이 있습니다 . 이 시리즈의 62번 문제에서 나는 스트링 풀에 대해 설명했습니다.

  6. Java에는 문자열 과 함께 작동하도록 설계된 String과 유사한 StringBuilderStringBuffer 가 있지만 변경 가능하다는 차이점이 있습니다. 이 기사에서 이에 대한 자세한 내용을 읽을 수 있습니다 .

Java 개발자 인터뷰의 질문과 답변을 분석합니다.  파트 15 - 4

15. 유형 공분산이란 무엇입니까?

공분산을 이해하기 위해 예를 살펴보겠습니다. 동물 클래스가 있다고 가정해 보겠습니다.
public class Animal {
 void voice() {
   System.out.println("*тишина*");
 }
}
그리고 이를 확장하는 일부 Dog 클래스는 다음과 같습니다 .
public class Dog extends Animal {

 @Override
 public void voice() {
   System.out.println("Гав, гав, гав!!!");
 }
}
우리가 기억하는 것처럼, 상속자 유형의 객체를 상위 유형에 쉽게 할당할 수 있습니다.
Animal animal = new Dog();
이것은 다형성에 지나지 않습니다. 편리하고 유연하지 않나요? 그렇다면 동물 목록은 어떻습니까? 일반적인 Animal이 포함된 목록에 Dog 개체가 포함된 목록을 제공할 수 있나요 ?
List<Dog> dogs = new ArrayList<>();
List<Animal> animals = dogs;
이 경우 개 목록을 동물 목록에 할당하는 줄에 빨간색 밑줄이 표시됩니다. 컴파일러는 이 코드를 전달하지 않습니다. 이 할당이 상당히 논리적인 것처럼 보임에도 불구하고(결국 Dog 객체를 Animal 유형의 변수에 할당할 수 있음 ) 이를 수행할 수 없습니다. 허용된다면 목록에 Dogs 만 있다고 생각하면서 원래 Dog 로 의도된 목록에 Animal 객체를 넣을 수 있기 때문입니다 . 그런 다음, 예를 들어 get() 메서드를 사용하여 목록 에서 개체를 가져와 개라고 생각하고 Animal 에는 없는 Dog 개체의 일부 메서드를 호출합니다 . 아시다시피 이것은 불가능합니다. 오류가 발생합니다. 그러나 다행스럽게도 컴파일러는 하위 목록을 상위 목록에 할당할 때(또는 그 반대의 경우) 이러한 논리적 오류를 놓치지 않습니다. Java에서는 제네릭이 일치하는 목록 변수에만 목록 개체를 할당할 수 있습니다. 이것을 불변이라고 합니다. 만약 그들이 이것을 할 수 있다면, 그것은 공분산(covariance)이라고 불릴 것입니다. 즉, 공분산 은 ArrayList<Dog> 유형의 객체를 List<Animal> 유형의 변수로 설정할 수 있는 경우입니다 . Java에서는 공분산이 지원되지 않는 것으로 나타났습니다. 아무리 그렇더라도! 그러나 이것은 그 나름의 특별한 방식으로 이루어집니다. 디자인은 무엇에 사용됩니까 ? Animal 을 확장합니다 . 이는 목록 객체를 설정하려는 변수의 제네릭과 하위 항목의 제네릭과 함께 배치됩니다. 이 일반 구성은 Animal 유형의 자손인 모든 유형이 수행된다는 것을 의미합니다 (그리고 Animal 유형 도 이 일반화에 속합니다). 결과적으로 Animal은 클래스일 뿐만 아니라 인터페이스일 수도 있습니다( extends 키워드 에 속지 마십시오 ). 이전 과제를 다음과 같이 수행할 수 있습니다. Java 개발자 인터뷰의 질문과 답변을 분석합니다.  파트 15 - 5
List<Dog> dogs = new ArrayList<>();
List<? extends Animal> animals = dogs;
결과적으로 컴파일러가 이 구성에 대해 불평하지 않는다는 것을 IDE에서 볼 수 있습니다. 이 디자인의 기능을 확인해 보겠습니다. 전달된 모든 동물이 소리를 내도록 하는 방법이 있다고 가정해 보겠습니다.
public static void animalsVoice(List<? extends Animal> animals) {
 for (Animal animal : animals) {
   animal.voice();
 }
}
그에게 개 목록을 제공합시다:
List<Dog> dogs = new ArrayList<>();
dogs.add(new Dog());
dogs.add(new Dog());
dogs.add(new Dog());
animalsVoice(dogs);
콘솔에는 다음과 같은 출력이 표시됩니다.
으으으으으으으으으으으!!! 으으으으으으으으으으으!!! 으으으으으으으으으으으!!!
이는 공분산에 대한 이러한 접근 방식이 성공적으로 작동함을 의미합니다. 이 일반 항목이 목록에 포함되어 있음을 알려 드리겠습니다 . Extension Animal에서는 어떤 유형의 새 데이터도 삽입할 수 없습니다. Dog 유형 이나 Animal 유형도 마찬가지입니다 .
List<Dog> dogs = new ArrayList<>();
List<? extends Animal> animals = dogs;
animals.add(new Dog());
dogs.add(new Animal());
실제로 마지막 두 줄에서 컴파일러는 개체 삽입을 빨간색으로 강조 표시합니다. 이는 어떤 유형의 객체 목록이 일반 <? 확장 동물> . 또한 반변성(Contravariance)Java 개발자 인터뷰의 질문과 답변을 분석합니다.  파트 15 - 6 에 대해서도 이야기하고 싶습니다 . 일반적으로 이 개념은 항상 공분산(Covariance)과 함께 사용되며 일반적으로 이에 대해 함께 질문되기 때문입니다. 이 개념은 공분산의 반대입니다. 왜냐하면 이 구성은 상속인 유형을 사용하기 때문입니다. Dog 객체 의 조상이 아닌 유형 객체의 목록을 할당할 수 있는 목록을 원한다고 가정해 보겠습니다 . 그러나 어떤 구체적인 유형이 될지는 미리 알 수 없습니다. 이 경우 양식의 구성은 ? 모든 유형이 적합한 super Dog - Dog 클래스의 조상 :
List<Animal> animals = new ArrayList<>();
List<? super Dog> dogs = animals;
dogs.add(new Dog());
dogs.add(new Dog());
Dog 유형의 객체를 그러한 generic 을 사용하여 목록에 안전하게 추가할 수 있습니다 . 왜냐하면 어떤 경우에도 해당 객체에는 조상의 모든 구현된 메서드가 있기 때문입니다. 그러나 Animal 유형의 객체를 추가할 수는 없습니다 . 예를 들어 Dog가 아닌 이 유형의 객체가 내부에 있을 것이라는 확신이 없기 때문입니다 . 결국 우리는 이 목록의 요소에서 Animal이 갖지 않는 Dog 클래스의 메소드를 요청할 수 있습니다 . 이 경우 컴파일 오류가 발생합니다. 또한 이전 방법을 구현하고 싶지만 다음과 같은 일반적인 방법을 사용하는 경우:
public static void animalsVoice(List<? super Dog> dogs) {
 for (Dog dog : dogs) {
   dog.voice();
 }
}
반환된 목록에 Dog 유형의 개체가 포함되어 있고 해당 메서드를 자유롭게 사용할 수 있는지 확인할 수 없기 때문에 for 루프 에서 컴파일 오류가 발생합니다 . 이 목록에서 dogs.get(0) 메소드를 호출하면. - Object 유형의 객체를 얻습니다 . 즉, AnimalsVoice() 메서드가 작동하려면 최소한 유형 데이터를 좁혀 작은 조작을 추가해야 합니다.
public static void animalsVoice(List<? super Dog> dogs) {
 for (Object obj : dogs) {
   if (obj instanceof Dog) {
     Dog dog = (Dog) obj;
     dog.voice();
   }
 }
}
Java 개발자 인터뷰의 질문과 답변을 분석합니다.  파트 15 - 7

16. Object 클래스에는 어떤 메소드가 있나요?

시리즈의 이 부분 인 11단락 에서 나는 이미 이 질문에 답했습니다. 아직 읽지 않으셨다면 꼭 읽어 보시기를 강력히 권합니다. 오늘은 여기서 마치겠습니다. 다음 부분에서 만나요! Java 개발자 인터뷰의 질문과 답변을 분석합니다.  파트 15 - 8
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION