JavaRush /Java Blog /Random EN /Analysis of questions and answers from interviews for a J...

Analysis of questions and answers from interviews for a Java developer. Part 14

Published in the Random EN group
Firework! The world is constantly moving and we are constantly moving. Previously, to become a Java developer, it was enough to know a little Java syntax, but the rest will come. Over time, the level of knowledge required to become a Java developer has increased significantly, as has competition that continues to push the bottom bar of required knowledge up. Analysis of questions and answers from interviews for a Java developer.  Part 14 - 1If you really want to become a developer, you need to take it for granted and carefully prepare in order to stand out among newcomers like you. What we will do today, namely, we will continue to analyze questions 250+. In previous articles, we have dealt with all the questions of the jun level, and today we will take up the questions of the middle level. Although I will note that these are not 100% middle level questions, most of them you can meet at the junior level interview, because it is at such interviews that you are thoroughly probing your theoretical base, while the middle questions are already more focused on probing his experience . Analysis of questions and answers from interviews for a Java developer.  Part 14 - 2But, without further ado, let's get started.

Middle

Are common

1. What are the advantages and disadvantages of OOP when compared to procedural/functional programming?

In the analysis of questions for Juinior there was this question, and accordingly I have already answered it. Look for this question and the answer to it in this part of the article, questions 16 and 17.

2. What is the difference between aggregation and composition?

In OOP, there are several types of interaction between objects, united under the general concept of "Has-A Relationship". This relationship indicates that one object is a component of another object. At the same time, there are two subspecies of this relationship: Composition - one object creates another object and the lifetime of another object depends on the lifetime of the creator. Aggregation - an object receives a link (pointer) to another object in the process of construction (while the lifetime of another object does not depend on the lifetime of the creator). To better understand, let's look at a specific example. We have a certain class of car - Car , which in turn has internal fields of the type - engine - Engine and a list of passengers -List<Passenger> , it also has a start moving method - 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());
   }
 }
}
In this case, the Composition is the connection between Car and Engine , since the performance of the car directly depends on the presence of the engine object, because if engine = null , then we will get a NullPointerException . In turn, the engine cannot exist without a car (why do we need an engine without a car?) and cannot belong to several cars at the same time. This means that if we delete the Car object, then the Engine object will no longer be referenced, and the Garbage Collector will soon delete it . As you can see, this relationship is very strict (strong). Aggregation is the relationship between Carand Passenger , since the performance of Car is in no way dependent on objects of type Passenger and their number. They can either leave the car — removePassengerByIndex(Long index) , or enter new ones — addPassenger(Passenger passenger) , despite this, the car will continue to function properly. In turn, Passenger objects can exist without a Car object . As you understand, this is a much weaker connection than we see in the composition. Analysis of questions and answers from interviews for a Java developer.  Part 14 - 3But that's not all, an object that is aggregated to another can also have that link to other objects at the same time. For example, you, as a Java student, are enrolled in English, OOP and logarithm courses at the same time, but at the same time you are not a critically necessary part of them, without which normal functioning is impossible (like a teacher, for example).

3. What GoF patterns have you used in practice? Give examples.

I already answered this question earlier, so I'll just leave a link to the analysis , see the first question. I also found a great cheat sheet on design patterns that I highly recommend keeping handy.

4. What is a proxy object? Give examples

A proxy is a structural design pattern that allows you to substitute special substitute objects, or in other words, proxy objects, for real objects. These proxy objects intercept calls to the original object, allowing some logic to be thrown in before or after the call is passed to the original. Analysis of questions and answers from interviews for a Java developer.  Part 14 - 4Examples of using the proxy object:
  • As a remote proxy - used when we need a remote object (an object in a different address space) that needs to be represented locally. In this case, the proxy will deal with connection creation, encoding, decoding, and so on, while the client will use it as if it were the original object residing in local space.

  • As a virtual proxy - used when a resource-intensive object is needed. In this case, the proxy object serves as a kind of image of a real object, which in fact does not yet exist. When a real request (method call) is sent to this object, only then the original object is loaded and the method is executed. This approach is also called lazy initialization, it can be very convenient, because in some situations the original object may not be useful, then there will be no cost to create it.

  • As a security proxy - used when you need to control access to some object based on client rights. That is, if a client with missing access rights tries to access the original object, the proxy will intercept it and not let it go.

Consider an example of a virtual proxy: We have some handler interface:
public interface Processor {
 void process();
}
The implementation of which uses too many resources, but at the same time it can not be used every time the application is launched:
public class HiperDifficultProcessor implements Processor {
 @Override
 public void process() {
   // некоторый сверхсложная обработка данных
 }
}
Proxy class:
public class HiperDifficultProcessorProxy implements Processor {
private HiperDifficultProcessor processor;

 @Override
 public void process() {
   if (processor == null) {
     processor = new HiperDifficultProcessor();
   }
   processor.process();
 }
}
Let's run it in main :
Processor processor = new HiperDifficultProcessorProxy();
// тут тяжеловсеного оригинального an object, ещё не сущетсвует
// но при этом есть an object, который его представляет и у которого можно вызывать его методы
processor.process(); // лишь теперь, an object оригинал был создан
I note that proxying is used by many frameworks, and for Spring this is a key pattern at all (Spring is stitched up and down with it). Read more about this pattern here . Analysis of questions and answers from interviews for a Java developer.  Part 14 - 5

5. What innovations are announced in Java 8?

The innovations brought by Java 8 are the following:
  • Functional interfaces have been added, read about what kind of beast it is here .

  • Lambda expressions, which are closely related to functional interfaces, read more about their use here .

  • Added Stream API for convenient processing of data collections, read more here .

  • Added links to methods .

  • The forEach() method has been added to the Iterable interface .

  • A new date and time API has been added in the java.time package , detailed analysis here .

  • Improved the Concurrent API .

  • Adding an Optional wrapper class that is used to correctly handle null values, you can find an excellent article on this topic here .

  • Adding interfaces the ability to use static and default methods (which, in fact, brings Java closer to multiple inheritance), more details here .

  • Added new methods to the Collection(removeIf(), spliterator()) class .

  • Minor improvements to Java Core.

Analysis of questions and answers from interviews for a Java developer.  Part 14 - 6

6. What is High Cohesion and Low Coupling? Give examples.

High Cohesion or High Cohesion is the concept when a certain class contains elements that are closely related to each other and are united according to their purpose. For example, all methods in the User class must represent user behavior. A class has low cohesion if it contains unrelated elements. For example, the User class containing the email address validation method:
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() {
   // некоторая логика валидации емейла
 }
}
The user class may be responsible for storing the user's email address, but not for validating it or sending the email. Therefore, in order to achieve high connectivity, we move the validation method into a separate utility class:
public class EmailUtil {
 public static boolean isValidEmail(String email) {
   // некоторая логика валидации емейла
 }
}
And we use it as needed (for example, before saving the user). Low Coupling or Low Coupling is a concept that describes low interdependence between software modules. Essentially, interdependence is how changing one requires changing the other. Two classes are strongly related (or densely related) if they are closely related. For example, two concrete classes that store references to each other and call each other's methods. Loosely coupled classes are easier to develop and maintain. Since they are independent of each other, they can be developed and tested in parallel. In addition, they can be changed and updated without affecting each other. Consider an example of strongly coupled classes. We have some student class:
public class Student {
 private Long id;
 private String name;
 private List<Lesson> lesson;
}
Which contains a list of lessons:
public class Lesson {
 private Long id;
 private String name;
 private List<Student> students;
}
Each lesson contains a link to visiting students. Incredibly strong grip, don't you think? How can you reduce it? First, let's make it so that students have not a list of subjects, but a list of their identifiers:
public class Student {
 private Long id;
 private String name;
 private List<Long> lessonIds;
}
Secondly, the lesson class does not need to know about all the students, so let's remove their list altogether:
public class Lesson {
 private Long id;
 private String name;
}
So it became much easier, and the connection became much weaker, don't you think? Analysis of questions and answers from interviews for a Java developer.  Part 14 - 7

OOP

7. How can multiple inheritance be implemented in Java?

Multiple inheritance is a feature of the object-oriented concept where a class can inherit properties from more than one parent class. The problem arises when there are methods with the same signature in both the super class and the sub class. When a method is called, the compiler cannot determine which class method should be called, and even when calling a class method, which takes precedence. Therefore, Java does not support multiple inheritance! But there is a kind of loophole, which we will talk about next. As I mentioned earlier, with the release of Java 8, interfaces have been added the ability to have default methods .methods. If the class that implements the interface does not override this method, then this default implementation will be used (it is not necessary to override the default method, such as implementing an abstract one). In this case, it is possible to implement different interfaces in one class and use their default methods. Consider an example. We have some flyer interface, with a default fly() method :
public interface Flyer {
 default void fly() {
   System.out.println("Я лечу!!!");
 }
}
The walker interface, with a default walk() method :
public interface Walker {
 default void walk() {
   System.out.println("Я хожу!!!");
 }
}
The swimmer interface, with the swim() method :
public interface Swimmer {
 default void swim() {
   System.out.println("Я плыву!!!");
 }
}
Well, now let's implement all this in one duck class:
public class Duck implements Flyer, Swimmer, Walker {
}
And let's run all the methods of our duck:
Duck donald = new Duck();
donald.walk();
donald.fly();
donald.swim();
In the console we get:
I go!!! I'm flying!!! I'm swimming!!!
And this means that we correctly depicted multiple inheritance, although this is not it. Analysis of questions and answers from interviews for a Java developer.  Part 14 - 8I also note that if a class implements interfaces with default methods that have the same method names and the same arguments in these methods, the compiler will start swearing at incompatibility, since it does not understand which method really needs to be applied. There are several outputs here:
  • Rename methods in interfaces so that they differ from each other.
  • Override such controversial methods in the implementation class.
  • Inherit from the class that implements these controversial methods (then your class will use its implementation).

8. What is the difference between final, finally, and finalize() methods?

final is a keyword that is used to impose restrictions on a class, method, or variable, a restriction meaning:
  • For a variable - after the initial initialization, the variable cannot be redefined.
  • For a method, the method cannot be overridden in a subclass (class heir).
  • For a class, the class cannot be inherited.
finally is a keyword before a code block, used in exception handling, together with a try block , and together (or interchangeably) with a catch block. The code in this block is executed in any case, regardless of whether an exception is thrown or not. In this part of the article, in the 104th question, exceptional situations are analyzed in which this block will not be executed. finalize() - method of the Object class , is called before the removal of each object by the garbage collector, this method will be called (finally), used to clean up the occupied resources. For more information about the methods of the Object class that each object inherits, see this part.articles in the 11th question. Well, that's where we end today. See you in the next part! Analysis of questions and answers from interviews for a Java developer.  Part 14 - 9 Analysis of questions and answers from interviews for a Java developer.  Part 14 - 10
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION