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

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

Random-KO 그룹에 게시되었습니다
불꽃! 세상은 끊임없이 움직이고 있고 우리도 끊임없이 움직이고 있습니다. 이전에는 Java 개발자가 되려면 Java 구문을 조금 아는 것만으로도 충분했고 나머지는 올 것입니다. 시간이 지남에 따라 Java 개발자가 되기 위해 필요한 지식 수준은 크게 성장했으며, 경쟁이 치열해지면서 필요한 지식의 낮은 기준도 계속 높아지고 있습니다. Java 개발자 인터뷰의 질문과 답변을 분석합니다.  파트 14 - 1정말로 개발자가 되고 싶다면 그것을 당연하게 여기고, 당신과 같은 초보자들 사이에서 두각을 나타낼 수 있도록 철저한 준비가 필요합니다.오늘 우리가 할 일은, 즉 250개 이상의 질문을 계속해서 분석할 것입니다 . 이전 기사에서는 중급 문제를 모두 검토했으며 오늘은 중급 문제를 다루겠습니다. 비록 이것이 100% 중급 수준의 질문은 아니라는 점을 알고 있지만, 중급 수준의 인터뷰에서 대부분을 만날 수 있습니다. 왜냐하면 이론적 기반에 대한 자세한 조사가 이루어지는 반면 중학생의 경우에는 질문은 그의 경험을 조사하는 데 더 중점을 둡니다. Java 개발자 인터뷰의 질문과 답변을 분석합니다.  파트 14 - 2하지만 더 이상 고민하지 말고 시작해 보겠습니다.

가운데

흔하다

1. 절차적/함수형 프로그래밍과 비교할 때 OOP의 장점과 단점은 무엇입니까?

주니어 질문 분석 중에 이런 질문이 있어서 이미 답변을 드렸습니다. 기사의 부분 인 질문 16번과 17번에서 이 질문과 그에 대한 답을 찾아보세요 .

2. 집계는 구성과 어떻게 다릅니까?

OOP에는 "Has-A Relationship"이라는 일반적인 개념으로 통합된 개체 간의 여러 유형의 상호 작용이 있습니다. 이 관계는 한 개체가 다른 개체의 구성 요소임을 나타냅니다. 동시에 이 관계에는 두 가지 하위 유형이 있습니다. 구성 - 하나의 개체가 다른 개체를 생성하고 다른 개체의 수명은 작성자의 수명에 따라 달라집니다. 집계 - 객체는 생성 프로세스 중에 다른 객체에 대한 링크(포인터)를 받습니다(이 경우 다른 객체의 수명은 작성자의 수명에 의존하지 않습니다). 더 나은 이해를 위해 구체적인 예를 살펴보겠습니다. 우리는 특정 자동차 클래스인 Car 를 가지고 있습니다 . 여기에는 Engine 유형 승객 목록인 List<Passenger> 유형 의 내부 필드가 있으며 , 이동을 시작하는 방법인 startMoving() 도 있습니다 .
public class Car {

 private Engine engine;
 private List<Passenger> passengers;

 public Car(final List<Passenger> passengers) {
   this.engine = new Engine();
   this.passengers = passengers;
 }

 public void addPassenger(Passenger passenger) {
   passengers.add(passenger);
 }

 public void removePassengerByIndex(Long index) {
   passengers.remove(index);
 }

 public void startMoving() {
   engine.start();
   System.out.println("Машина начала своё движение");
   for (Passenger passenger : passengers) {
     System.out.println("В машине есть пассажир - " + passenger.getName());
   }
 }
}
이 경우 컴포지션은 CarEngine 사이의 연결입니다 . 자동차의 성능은 엔진 객체의 존재 여부에 직접적으로 좌우되기 때문입니다. 왜냐하면 engine = null 이면 NullPointerException 을 수신하기 때문입니다 . 결과적으로 엔진은 기계 없이는 존재할 수 없으며(기계 없이 엔진이 필요한 이유는 무엇입니까?) 동시에 여러 기계에 속할 수 없습니다. 이는 Car 개체를 삭제하면 Engine 개체 에 대한 참조가 더 이상 없으며 곧 Garbage Collector 에 의해 삭제된다는 의미입니다 . 보시다시피 이 관계는 매우 엄격합니다(강함). 집계는 CarPassenger 간의 연결입니다 . Car 의 성능은 Passenger 유형의 객체 와 그 수 에 전혀 의존하지 않기 때문입니다. 그들은 차에서 내리거나( removePassengerByIndex(Long index)) 새 차를 입력할 수 있습니다( addPassenger(Passenger 승객)) . 그럼에도 불구하고 차는 계속해서 제대로 작동합니다. 결과적으로 Passenger 객체는 Car 객체 없이도 존재할 수 있습니다 . 아시다시피 이것은 구성에서 보는 것보다 훨씬 약한 연결입니다. Java 개발자 인터뷰의 질문과 답변을 분석합니다.  파트 14 - 3그러나 이것이 전부는 아닙니다. 집합을 통해 다른 개체와 연결된 개체는 동일한 시점에 다른 개체와 특정 연결을 가질 수도 있습니다. 예를 들어, Java 학생인 귀하는 같은 시점에 영어, OOP 및 로그 과정에 등록되어 있지만 동시에 정상적인 기능이 불가능한 그 과정에서 결정적으로 필요한 부분은 아닙니다(예: 교사).

3. 실제로 어떤 GoF 패턴을 사용했나요? 예를 들다.

이 질문에 대해서는 이전에 답변한 적이 있으므로 분석 링크 만 남겨 두겠습니다 . 첫 번째 질문을 참조하세요. 또한 디자인 패턴에 대한 훌륭한 치트 시트 기사를 찾았는데 , 이를 잘 보관해 두는 것이 좋습니다.

4. 프록시 객체란 무엇입니까? 예를 들다

프록시는 실제 객체 대신 특수한 대체 객체, 즉 프록시 객체를 대체할 수 있도록 하는 구조적 디자인 패턴입니다. 이러한 프록시 개체는 원본 개체에 대한 호출을 가로채서 호출이 원본 개체에 전달되기 이나 후에 일부 논리를 삽입할 수 있도록 합니다. Java 개발자 인터뷰의 질문과 답변을 분석합니다.  파트 14 - 4프록시 객체 사용의 예:
  • 원격 프록시로서 - 로컬로 표현되어야 하는 원격 개체(다른 주소 공간의 개체)가 필요할 때 사용됩니다. 이 경우 프록시는 연결 생성, 인코딩, 디코딩 등을 처리하고 클라이언트는 이를 로컬 공간에 있는 원본 개체인 것처럼 사용합니다.

  • 가상 프록시로 - 리소스 집약적인 개체가 필요할 때 사용됩니다. 이 경우 프록시 객체는 실제로 아직 존재하지 않는 실제 객체의 이미지와 같은 역할을 합니다. 실제 요청(메서드 호출)이 이 개체로 전송되면 원래 개체가 로드되고 메서드가 실행됩니다. 이 접근 방식은 지연 초기화라고도 하는데, 어떤 상황에서는 원본 객체가 유용하지 않아 객체를 생성하는 데 비용이 들지 않기 때문에 매우 편리할 수 있습니다.

  • 보안 프록시로 - 클라이언트 권한을 기반으로 일부 개체에 대한 액세스를 제어해야 할 때 사용됩니다. 즉, 액세스 권한이 없는 클라이언트가 원본 개체에 액세스하려고 하면 프록시가 이를 가로채서 허용하지 않습니다.

가상 프록시의 예를 살펴보겠습니다. 핸들러 인터페이스가 있습니다.
public interface Processor {
 void process();
}
구현 시 너무 많은 리소스를 사용하지만 동시에 애플리케이션이 시작될 때마다 사용되지 않을 수도 있습니다.
public class HiperDifficultProcessor implements Processor {
 @Override
 public void process() {
   // некоторый сверхсложная обработка данных
 }
}
프록시 클래스:
public class HiperDifficultProcessorProxy implements Processor {
private HiperDifficultProcessor processor;

 @Override
 public void process() {
   if (processor == null) {
     processor = new HiperDifficultProcessor();
   }
   processor.process();
 }
}
main 에서 실행해보자 :
Processor processor = new HiperDifficultProcessorProxy();
// тут тяжеловсеного оригинального an object, ещё не сущетсвует
// но при этом есть an object, который его представляет и у которого можно вызывать его методы
processor.process(); // лишь теперь, an object оригинал был создан
많은 프레임워크가 프록싱을 사용하며 Spring 의 경우 이것이 핵심 패턴입니다(Spring은 내부와 외부로 연결되어 있습니다). 이 패턴에 대한 자세한 내용은 여기를 참조하세요 . Java 개발자 인터뷰의 질문과 답변을 분석합니다.  파트 14 - 5

5. Java 8에서는 어떤 혁신이 발표되었습니까?

Java 8이 가져온 혁신은 다음과 같습니다.
  • 기능적 인터페이스가 추가되었습니다. 이것이 어떤 종류의 짐승인지 여기에서 읽어보세요 .

  • 기능적 인터페이스와 밀접하게 관련된 람다 표현식의 사용법에 대한 자세한 내용은 여기를 참조하세요 .

  • 데이터 수집을 편리하게 처리하기 위해 Stream API를 추가했습니다 . 자세한 내용은 여기를 참조하세요 .

  • 메소드에 대한 링크를 추가했습니다 .

  • forEach() 메소드가 Iterable 인터페이스 에 추가되었습니다 .

  • java.time 패키지 에 새로운 날짜 및 시간 API가 추가되었습니다 . 자세한 분석은 여기를 참조하세요 .

  • 향상된 동시 API .

  • null 값을 올바르게 처리하는 데 사용되는 Optional 래퍼 클래스를 추가하면 여기에서 이 주제에 대한 훌륭한 기사를 찾을 수 있습니다 .

  • 정적기본 메소드(본질적으로 Java를 다중 상속에 더 가깝게 만드는)를 사용하는 인터페이스 기능을 추가합니다 . 자세한 내용은 여기를 참조하세요 .

  • Collection(removeIf(), Spliterator()) 클래스 에 새 메서드를 추가했습니다 .

  • Java Core가 약간 개선되었습니다.

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

6. 높은 응집력과 낮은 결합력이란 무엇입니까? 예를 들다.

높은 응집력(High Cohesion) 또는 높은 응집력(High Cohesion) 은 특정 클래스가 서로 밀접하게 관련되어 있으며 목적에 맞게 결합된 요소를 포함하는 개념입니다. 예를 들어 User 클래스의 모든 메서드는 사용자 동작을 나타내야 합니다. 관련되지 않은 요소가 포함된 클래스는 응집력이 낮습니다. 예를 들어 이메일 주소 확인 방법을 포함하는 User 클래스는 다음과 같습니다.
public class User {
private String name;
private String email;

 public String getName() {
   return this.name;
 }

 public void setName(final String name) {
   this.name = name;
 }

 public String getEmail() {
   return this.email;
 }

 public void setEmail(final String email) {
   this.email = email;
 }

 public boolean isValidEmail() {
   // некоторая логика валидации емейла
 }
}
사용자 클래스는 사용자의 이메일 주소를 저장하는 일을 담당할 수 있지만 이를 확인하거나 이메일을 보내는 일은 담당하지 않습니다. 따라서 높은 일관성을 달성하기 위해 유효성 검사 방법을 별도의 유틸리티 클래스로 이동합니다.
public class EmailUtil {
 public static boolean isValidEmail(String email) {
   // некоторая логика валидации емейла
 }
}
그리고 필요에 따라(예를 들어 사용자를 저장하기 전) 사용합니다. 낮은 결합 또는 낮은 결합은 소프트웨어 모듈 간의 낮은 상호 의존성을 설명하는 개념입니다. 본질적으로 상호의존성은 하나를 변경하면 다른 하나도 변경해야 하는 방식입니다. 두 클래스가 밀접하게 관련되어 있는 경우 강한 결합(또는 긴밀한 결합)을 갖습니다. 예를 들어 서로에 대한 참조를 저장하고 서로의 메서드를 호출하는 두 개의 구체적인 클래스가 있습니다. 느슨하게 결합된 클래스는 개발 및 유지 관리가 더 쉽습니다. 이들은 서로 독립적이므로 병렬로 개발하고 테스트할 수 있습니다. 또한 서로 영향을 주지 않고 변경 및 업데이트할 수 있습니다. 강력하게 결합된 클래스의 예를 살펴보겠습니다. 우리는 학생 수업이 있습니다:
public class Student {
 private Long id;
 private String name;
 private List<Lesson> lesson;
}
여기에는 강의 목록이 포함되어 있습니다.
public class Lesson {
 private Long id;
 private String name;
 private List<Student> students;
}
각 수업에는 참석하는 학생에 대한 링크가 포함되어 있습니다. 믿을 수 없을 만큼 강한 그립감, 그렇지 않나요? 어떻게 줄일 수 있나요? 먼저, 학생들이 과목 목록이 아닌 식별자 목록을 갖고 있는지 확인하세요.
public class Student {
 private Long id;
 private String name;
 private List<Long> lessonIds;
}
둘째, 수업 수업에서는 모든 학생에 대해 알 필요가 없으므로 해당 목록을 모두 삭제하겠습니다.
public class Lesson {
 private Long id;
 private String name;
}
그래서 훨씬 쉬워졌고, 연결도 훨씬 약해졌죠? Java 개발자 인터뷰의 질문과 답변을 분석합니다.  파트 14 - 7

이런!

7. Java에서 다중 상속을 어떻게 구현할 수 있나요?

다중 상속은 클래스가 둘 이상의 상위 클래스로부터 속성을 상속받을 수 있는 객체 지향 개념의 기능입니다. 슈퍼 클래스와 서브 클래스 모두에 동일한 시그니처를 가진 메서드가 있는 경우 문제가 발생합니다. 메서드를 호출할 때 컴파일러는 어떤 클래스 메서드를 호출해야 하는지 결정할 수 없으며, 심지어 우선 순위가 높은 클래스 메서드를 호출할 때도 마찬가지입니다. 따라서 Java는 다중 상속을 지원하지 않습니다! 그러나 일종의 허점이 있는데, 이에 대해서는 다음에 이야기하겠습니다. 앞서 언급했듯이 Java 8이 출시되면서 기본 메소드를 갖는 기능이 인터페이스 에 추가되었습니다 . 인터페이스를 구현하는 클래스가 이 메서드를 재정의하지 않으면 이 기본 구현이 사용됩니다(추상 메서드 구현과 같이 기본 메서드를 재정의할 필요는 없습니다). 이 경우 하나의 클래스에서 다양한 인터페이스를 구현하고 해당 기본 메서드를 사용할 수 있습니다. 예를 살펴보겠습니다. 기본 fly() 메소드를 사용하는 전단지 인터페이스가 있습니다 .
public interface Flyer {
 default void fly() {
   System.out.println("Я лечу!!!");
 }
}
기본 walk() 메소드를 사용하는 워커 인터페이스 :
public interface Walker {
 default void walk() {
   System.out.println("Я хожу!!!");
 }
}
swim() 메소드를 사용하는 수영인 인터페이스 :
public interface Swimmer {
 default void swim() {
   System.out.println("Я плыву!!!");
 }
}
이제 이 모든 것을 하나의 duck 클래스에 구현해 보겠습니다.
public class Duck implements Flyer, Swimmer, Walker {
}
그리고 우리 오리의 모든 메소드를 실행해 봅시다:
Duck donald = new Duck();
donald.walk();
donald.fly();
donald.swim();
콘솔에서는 다음을 받게 됩니다:
나 간다!!! 나는 날고있다!!! 나 수영하고 있어!!!
이는 다중 상속이 실제로는 아니지만 올바르게 묘사했음을 의미합니다. Java 개발자 인터뷰의 질문과 답변을 분석합니다.  파트 14 - 8또한 클래스가 동일한 메소드 이름과 이러한 메소드의 인수를 갖는 기본 메소드로 인터페이스를 구현하는 경우 컴파일러는 실제로 어떤 메소드를 사용해야 하는지 이해하지 못하기 때문에 비호환성에 대해 불평하기 시작할 것입니다. 여러 가지 방법이 있습니다.
  • 인터페이스의 메서드 이름을 서로 다르도록 바꿉니다.
  • 구현 클래스에서 논쟁의 여지가 있는 메서드를 재정의합니다.
  • 논쟁의 여지가 있는 이러한 메서드를 구현하는 클래스에서 상속합니다(그러면 클래스에서 해당 구현을 그대로 사용하게 됩니다).

8. final, finally 및 finalize() 메소드의 차이점은 무엇입니까?

final 은 클래스, 메서드 또는 변수에 제약 조건을 적용하는 데 사용되는 키워드입니다. 제약 조건의 의미는 다음과 같습니다.
  • 변수의 경우 - 초기 초기화 후에는 변수를 재정의할 수 없습니다.
  • 메서드의 경우 하위 클래스(후속 클래스)에서 메서드를 재정의할 수 없습니다.
  • 클래스의 경우 - 클래스는 상속될 수 없습니다.
finally 는 예외를 처리할 때 try 블록 과 함께, 그리고 catch 블록과 함께(또는 상호교환적으로) 사용되는 코드 블록 앞의 키워드입니다. 이 블록의 코드는 예외 발생 여부에 관계없이 어떤 경우에도 실행됩니다. 기사의 이 부분(질문 104) 에서는 이 블록이 실행되지 않는 예외적인 상황에 대해 논의합니다. finalize() 는 Object 클래스 의 메서드로 , 가비지 수집기에 의해 각 개체가 삭제되기 전에 호출됩니다. 이 메서드는 (마지막) 호출되며 점유된 리소스를 정리하는 데 사용됩니다. 모든 개체가 상속하는 Object 클래스 의 메서드에 대한 자세한 내용은 기사의 질문 11을 참조하세요. 자, 오늘은 여기서 마치겠습니다. 다음 부분에서 만나요! Java 개발자 인터뷰의 질문과 답변을 분석합니다.  파트 14 - 9
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION