가운데
흔하다
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());
}
}
}
이 경우 컴포지션은 Car 와 Engine 사이의 연결입니다 . 자동차의 성능은 엔진 객체의 존재 여부에 직접적으로 좌우되기 때문입니다. 왜냐하면 engine = null 이면 NullPointerException 을 수신하기 때문입니다 . 결과적으로 엔진은 기계 없이는 존재할 수 없으며(기계 없이 엔진이 필요한 이유는 무엇입니까?) 동시에 여러 기계에 속할 수 없습니다. 이는 Car 개체를 삭제하면 Engine 개체 에 대한 참조가 더 이상 없으며 곧 Garbage Collector 에 의해 삭제된다는 의미입니다 . 보시다시피 이 관계는 매우 엄격합니다(강함). 집계는 Car 와 Passenger 간의 연결입니다 . Car 의 성능은 Passenger 유형의 객체 와 그 수 에 전혀 의존하지 않기 때문입니다. 그들은 차에서 내리거나( removePassengerByIndex(Long index)) 새 차를 입력할 수 있습니다( addPassenger(Passenger 승객)) . 그럼에도 불구하고 차는 계속해서 제대로 작동합니다. 결과적으로 Passenger 객체는 Car 객체 없이도 존재할 수 있습니다 . 아시다시피 이것은 구성에서 보는 것보다 훨씬 약한 연결입니다. 그러나 이것이 전부는 아닙니다. 집합을 통해 다른 개체와 연결된 개체는 동일한 시점에 다른 개체와 특정 연결을 가질 수도 있습니다. 예를 들어, Java 학생인 귀하는 같은 시점에 영어, OOP 및 로그 과정에 등록되어 있지만 동시에 정상적인 기능이 불가능한 그 과정에서 결정적으로 필요한 부분은 아닙니다(예: 교사).
3. 실제로 어떤 GoF 패턴을 사용했나요? 예를 들다.
이 질문에 대해서는 이전에 답변한 적이 있으므로 분석 링크 만 남겨 두겠습니다 . 첫 번째 질문을 참조하세요. 또한 디자인 패턴에 대한 훌륭한 치트 시트 기사를 찾았는데 , 이를 잘 보관해 두는 것이 좋습니다.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은 내부와 외부로 연결되어 있습니다). 이 패턴에 대한 자세한 내용은 여기를 참조하세요 .
5. Java 8에서는 어떤 혁신이 발표되었습니까?
Java 8이 가져온 혁신은 다음과 같습니다.-
기능적 인터페이스가 추가되었습니다. 이것이 어떤 종류의 짐승인지 여기에서 읽어보세요 .
-
기능적 인터페이스와 밀접하게 관련된 람다 표현식의 사용법에 대한 자세한 내용은 여기를 참조하세요 .
-
데이터 수집을 편리하게 처리하기 위해 Stream API를 추가했습니다 . 자세한 내용은 여기를 참조하세요 .
-
메소드에 대한 링크를 추가했습니다 .
-
forEach() 메소드가 Iterable 인터페이스 에 추가되었습니다 .
-
java.time 패키지 에 새로운 날짜 및 시간 API가 추가되었습니다 . 자세한 분석은 여기를 참조하세요 .
-
향상된 동시 API .
-
null 값을 올바르게 처리하는 데 사용되는 Optional 래퍼 클래스를 추가하면 여기에서 이 주제에 대한 훌륭한 기사를 찾을 수 있습니다 .
-
정적 및 기본 메소드(본질적으로 Java를 다중 상속에 더 가깝게 만드는)를 사용하는 인터페이스 기능을 추가합니다 . 자세한 내용은 여기를 참조하세요 .
-
Collection(removeIf(), Spliterator()) 클래스 에 새 메서드를 추가했습니다 .
-
Java Core가 약간 개선되었습니다.
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;
}
그래서 훨씬 쉬워졌고, 연결도 훨씬 약해졌죠?
이런!
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();
콘솔에서는 다음을 받게 됩니다:
- 인터페이스의 메서드 이름을 서로 다르도록 바꿉니다.
- 구현 클래스에서 논쟁의 여지가 있는 메서드를 재정의합니다.
- 논쟁의 여지가 있는 이러한 메서드를 구현하는 클래스에서 상속합니다(그러면 클래스에서 해당 구현을 그대로 사용하게 됩니다).
8. final, finally 및 finalize() 메소드의 차이점은 무엇입니까?
final 은 클래스, 메서드 또는 변수에 제약 조건을 적용하는 데 사용되는 키워드입니다. 제약 조건의 의미는 다음과 같습니다.- 변수의 경우 - 초기 초기화 후에는 변수를 재정의할 수 없습니다.
- 메서드의 경우 하위 클래스(후속 클래스)에서 메서드를 재정의할 수 없습니다.
- 클래스의 경우 - 클래스는 상속될 수 없습니다.
GO TO FULL VERSION