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

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

Published in the Random EN group
Hi Hi! How much does a Java developer need to know? You can argue for a long time on this issue, but the truth is that at the interview you will be driven to the fullest by theory. Even in those areas of knowledge that you will not have the opportunity to use in your work. Analysis of questions and answers from interviews for Java developer.  Part 15 - 1Well, if you are a beginner, your theoretical knowledge will be taken very seriously. Since there is no experience and great achievements yet, all that remains is to check the strength of the knowledge base. Today we will continue to strengthen this very base by examining the most popular interview questions for Java developers. Let's fly!

Java Core

9. What is the difference between static and dynamic binding in Java?

I have already answered this question in this article in question 18 about static and dynamic polymorphism, I advise you to read it.

10. Is it possible to use private or protected variables in an interface?

No you can not. Because when you declare an interface, the Java compiler automatically adds the public and abstract keywords before the interface methods and the public , static and final keywords before the data members. Actually, if you add private or protected , a conflict will arise, and the compiler will complain about the access modifier with the message: “Modifier '<selected modifier>' not allowed here.” Why does the compiler add public , static and final variables in the interface? Let's figure it out:
  • public - the interface allows the client to interact with the object. If the variables weren't public, clients wouldn't have access to them.
  • static - interfaces cannot be created (or rather, their objects), so the variable is static.
  • final - since the interface is used to achieve 100% abstraction, the variable has its final form (and will not be changed).

11. What is Classloader and what is it used for?

Classloader - or Class Loader - provides loading of Java classes. More precisely, loading is ensured by its descendants - specific class loaders, because ClassLoader itself is abstract. Every time a .class file is loaded, for example, after calling a constructor or static method of the corresponding class, this action is performed by one of the ClassLoader class's descendants . There are three types of heirs:
  1. Bootstrap ClassLoader is a basic loader, implemented at the JVM level and has no feedback from the runtime environment, since it is part of the JVM kernel and written in native code. This loader serves as the parent of all other ClassLoader instances.

    Mainly responsible for loading JDK internal classes, usually rt.jar and other core libraries located in the $JAVA_HOME/jre/lib directory . Different platforms may have different implementations of this class loader.

  2. Extension Classloader is an extension loader, a descendant of the base loader class. Takes care of loading the extension of standard Java base classes. Loaded from the JDK extensions directory, typically $JAVA_HOME/lib/ext or any other directory mentioned in the java.ext.dirs system property (this option can be used to control the loading of extensions).

  3. System ClassLoader is a system loader implemented at the JRE level that takes care of loading all application-level classes into the JVM. It loads files found in the class environment variable -classpath or -cp command line option.

Analysis of questions and answers from interviews for Java developer.  Part 15 - 2Class loaders are part of the Java runtime. The moment the JVM requests a class, the class loader tries to find the class and load the class definition into the runtime using the fully qualified name of the class. The java.lang.ClassLoader.loadClass() method is responsible for loading the class definition at runtime. It tries to load a class based on its full name. If the class has not yet been loaded, it delegates the request to the parent class loader. This process occurs recursively and looks like this:
  1. System Classloader tries to find the class in its cache.

    • 1.1. If the class is found, the loading is completed successfully.

    • 1.2. If the class is not found, loading is delegated to the Extension Classloader.

  2. Extension Classloader tries to find the class in its own cache.

    • 2.1. If the class is found, it completes successfully.

    • 2.2. If the class is not found, loading is delegated to the Bootstrap Classloader.

  3. Bootstrap Classloader tries to find the class in its own cache.

    • 3.1. If the class is found, the loading is completed successfully.

    • 3.2. If the class is not found, the underlying Bootstrap Classloader will try to load it.

  4. If loading:

    • 4.1. Successful - loading of the class is completed.

    • 4.2. If it fails, control is transferred to the Extension Classloader.

  5. 5. Extension Classloader tries to load the class, and if loading:

    • 5.1. Successful - loading of the class is completed.

    • 5.2. If it fails, control is transferred to the System Classloader.

  6. 6. System Classloader tries to load the class, and if loading:

    • 6.1. Successful - loading of the class is completed.

    • 6.2. Did not pass successfully - an exception is generated - ClassNotFoundException.

The topic of class loaders is a vast one and should not be neglected. To get acquainted with it in more detail, I advise you to read this article , and we will not linger and move on.

12. What are Run-Time Data Areas?

Run-Time Data Ares - JVM runtime data areas. The JVM defines some runtime data areas needed during program execution. Some of them are created when the JVM starts. Others are thread-local and are created only when the thread is created (and destroyed when the thread is destroyed). The JVM runtime data areas look like this: Analysis of questions and answers from interviews for Java developer.  Part 15 - 3
  • PC Register is local to each thread and contains the address of the JVM instruction that the thread is currently executing.

  • JVM Stack is a memory area that is used as storage for local variables and temporary results. Each thread has its own separate stack: as soon as the thread terminates, this stack is also destroyed. It is worth noting that the advantage of stack over heap is performance, while heap certainly has an advantage in storage scale.

  • Native Method Stack - A per-thread data area that stores data elements, similar to the JVM stack, for executing native (non-Java) methods.

  • Heap - used by all threads as a storage that contains objects, class metadata, arrays, etc., which are created at runtime. This area is created when the JVM starts and is destroyed when it shuts down.

  • Method area - This runtime area is common to all threads and is created when the JVM starts. It stores structures for each class, such as the Runtime Constant Pool, code for constructors and methods, method data, etc.

13. What is an immutable object?

In this part of the article, in questions 14 and 15, there is already an answer to this question, so take a look without wasting your time.

14. What is special about the String class?

Earlier in the analysis, we repeatedly talked about certain features of String (there was a separate section for this). Now let's summarize the features of String :
  1. It is the most popular object in Java and is used for a variety of purposes. In terms of frequency of use, it is not inferior even to primitive types.

  2. An object of this class can be created without using the new keyword - directly through quotes String str = “string”; .

  3. String is an immutable class: when creating an object of this class, its data cannot be changed (when you add + “another string” to a certain string, as a result you will get a new, third string). The immutability of the String class makes it thread safe.

  4. The String class is finalized (has the final modifier ), so it cannot be inherited.

  5. String has its own string pool, an area of ​​memory in the heap that caches the string values ​​it creates. In this part of the series , in question 62, I described the string pool.

  6. Java has analogues of String , also designed to work with strings - StringBuilder and StringBuffer , but with the difference that they are mutable. You can read more about them in this article .

Analysis of questions and answers from interviews for Java developer.  Part 15 - 4

15. What is type covariance?

To understand covariance, we will look at an example. Let's say we have an animal class:
public class Animal {
 void voice() {
   System.out.println("*тишина*");
 }
}
And some Dog class extending it :
public class Dog extends Animal {

 @Override
 public void voice() {
   System.out.println("Гав, гав, гав!!!");
 }
}
As we remember, we can easily assign objects of the heir type to the parent type:
Animal animal = new Dog();
This will be nothing more than polymorphism. Convenient, flexible isn't it? Well, what about the list of animals? Can we give a list with a generic Animal a list with Dog objects ?
List<Dog> dogs = new ArrayList<>();
List<Animal> animals = dogs;
In this case, the line for assigning the list of dogs to the list of animals will be underlined in red, i.e. the compiler will not pass this code. Despite the fact that this assignment seems to be quite logical (after all, we can assign a Dog object to a variable of type Animal ), it cannot be done. This is because if it were allowed, we would be able to put an Animal object into a list that was originally intended to be a Dog , while thinking that we only had Dogs in the list . And then, for example, we’ll use the get() method to take an object from that dogs list , thinking that it’s a dog, and call some method of the Dog object on it, which Animal doesn’t have . And as you understand, this is impossible - an error will occur. But, fortunately, the compiler does not miss this logical error with assigning a list of descendants to a list of parents (and vice versa). In Java, you can only assign list objects to list variables with matching generics. This is called invariation. If they could do this, it would be called and is called covariance. That is, covariance is if we could set an object of type ArrayList<Dog> to a variable of type List<Animal> . It turns out that covariance is not supported in Java? No matter how it is! But this is done in its own special way. What is the design used for ? extends Animal . It is placed with a generic of the variable to which we want to set the list object, with a generic of the descendant. This generic construction means that any type that is a descendant of the type Animal will do (and the type Animal also falls under this generalization). In turn, Animal can be not only a class, but also an interface (don’t be fooled by the extends keyword ). We can do our previous assignment like this: Analysis of questions and answers from interviews for Java developer.  Part 15 - 5
List<Dog> dogs = new ArrayList<>();
List<? extends Animal> animals = dogs;
As a result, you will see in the IDE that the compiler will not complain about this construction. Let's check the functionality of this design. Let's say we have a method that causes all animals passed to it to make sounds:
public static void animalsVoice(List<? extends Animal> animals) {
 for (Animal animal : animals) {
   animal.voice();
 }
}
Let's give him a list of dogs:
List<Dog> dogs = new ArrayList<>();
dogs.add(new Dog());
dogs.add(new Dog());
dogs.add(new Dog());
animalsVoice(dogs);
In the console we will see the following output:
Woof woof woof!!! Woof woof woof!!! Woof woof woof!!!
This means that this approach to covariance works successfully. Let me note that this generic is included in the list ? extends Animal we cannot insert new data of any type: neither the Dog type , nor even the Animal type :
List<Dog> dogs = new ArrayList<>();
List<? extends Animal> animals = dogs;
animals.add(new Dog());
dogs.add(new Animal());
Actually, in the last two lines the compiler will highlight the insertion of objects in red. This is due to the fact that we cannot be one hundred percent sure which list of objects of what type will be assigned to the list with data by the generic <? extends Animal> . Analysis of questions and answers from interviews for Java developer.  Part 15 - 6I would also like to talk about contravariance , since usually this concept always goes together with covariance, and as a rule they are asked about them together. This concept is somewhat the opposite of covariance, since this construct uses the heir type. Let's say we want a list that can be assigned a list of type objects that are not ancestors of the Dog object . However, we do not know in advance what specific types they will be. In this case, a construction of the form ? super Dog , for which all types are suitable - the progenitors of the Dog class :
List<Animal> animals = new ArrayList<>();
List<? super Dog> dogs = animals;
dogs.add(new Dog());
dogs.add(new Dog());
We can safely add objects of type Dog to the list with such a generic , because in any case it has all the implemented methods of any of its ancestors. But we will not be able to add an object of type Animal , since there is no certainty that there will be objects of this type inside, and not, for example, Dog . After all, we can request from an element of this list a method of the Dog class, which Animal will not have . In this case, a compilation error will occur. Also, if we wanted to implement the previous method, but with this generic:
public static void animalsVoice(List<? super Dog> dogs) {
 for (Dog dog : dogs) {
   dog.voice();
 }
}
we would get a compilation error in the for loop , since we cannot be sure that the returned list contains objects of type Dog and are free to use its methods. If we call the dogs.get(0) method on this list . - we will get an object of type Object . That is, for the animalsVoice() method to work , we at least need to add small manipulations with narrowing the type data:
public static void animalsVoice(List<? super Dog> dogs) {
 for (Object obj : dogs) {
   if (obj instanceof Dog) {
     Dog dog = (Dog) obj;
     dog.voice();
   }
 }
}
Analysis of questions and answers from interviews for Java developer.  Part 15 - 7

16. How are there methods in the Object class?

In this part of the series, in paragraph 11, I have already answered this question, so I strongly advise you to read it if you have not done so yet. That's where we'll end for today. See you in the next part! Analysis of questions and answers from interviews for Java developer.  Part 15 - 8
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION