User Konstantin
Level 36

Exploring questions and answers from a job interview for a Java developer position. Part 2

Published in the Random group
Hello again, everyone! We continue to search for answers to questions for junior, mid-level and senior Java developers. The questions are super interesting. I personally like analyzing them, because it helps me find gaps in my theoretical knowledge, and in the most unexpected places sometimes. Exploring questions and answers from a job interview for a Java developer position. Part 2 - 1The previous part can be found in this article. But before we begin, I want to remind you that:
  1. I will skip the questions that overlap with this series of articles in order to not unnecessarily duplicate information. I recommend reading these articles as they cover the most common (popular) Java Core interview questions.
  2. I could describe the answers in more detail, but I won't, because then each answer could drag on for the whole article. And nobody will ask you for that level of detail in any job interview.
I'll leave links for the deeper study if you want. Let's fly!

11. Name all of the methods of the Object class

The Object class has 11 methods:
  1. Class<?> getClass() — get the class of the current object;

  2. int hashCode() — get the hash code of the current object;

  3. boolean equals(Object obj) — compare the current object with another object;

  4. Object clone() — create and return a copy of the current object;

  5. String toString() — get the string representation of the object;

  6. void notify() — wake up one thread waiting on this object's monitor (the choice of thread is random);

  7. void notifyAll() — wake up all threads waiting on this object's monitor;

  8. void wait() — make the current thread wait on the current monitor (freeze the current thread) until a notify or notifyAll call wakes up the thread (only works in a synchronized block);

  9. void wait(long timeout) — make the current thread wait on the current monitor (on the current synchronized block), but with a timeout for exiting the waiting state (or again, until a notify or notifyAll call wakes up the thread);

  10. void wait(long timeout, int nanos) — this method is like the previous method, but with a more precise timeout;

  11. void finalize() — this method is called (finally) before the object is removed by the garbage collector. It is used to clean up acquired resources.

To use the hashCode, equals, clone, toString, and finalize methods correctly, they must be overridden according to the specifics of the current task.

12. What is the difference between try-with-resources and try-catch-finally when working with resources?

Typically, when using try-catch-finally, the final block is used to close resources. Java 7 introduces the new try-with-resources statement. It is analogous to try-catch-finally for freeing resources, but more compact and readable. Let's recall what try-catch-finally looks like:

String text = "some text......";
BufferedWriter bufferedWriter = null;
try {
   bufferedWriter = new BufferedWriter(new FileWriter("someFileName"));
} catch (IOException e) {
} finally {
   try {
   } catch (IOException e) {
Now let's rewrite this code, but using try-with-resources:

String text = "some text......";
try(BufferedWriter bufferedWriter =new BufferedWriter(new FileWriter("someFileName"))) {
} catch (IOException e) {
Now that's somehow simpler, don't you think? In addition to the simpler code, there are a couple of other points to note:
  1. In try-with-resources, the resources declared in the parentheses (resources that will be closed) must implement the AutoCloseable interface and its sole close() method.

    The close method is executed in an implicit finally block, otherwise, how would the program figure out exactly how to close the resource?

    But you will probably rarely write your own implementations of resources and their closing method.

  2. Blocks are executed in this order:

    1. The try block.
    2. The implicit finally block.
    3. The catch block, which catches exceptions that occur in the previous steps.
    4. The explicit finally block.

    As a rule, the exceptions thrown lower in the list interrupt those thrown higher up.

Imagine that you're using a try-catch-finally and you get an exception in the try block. Then the specified catch block immediately begins to execute, in which we have written another exception (for example, with a message that describes the error in more detail), and you want the method throw this exception upward. Then the finally block is executed, and an exception is thrown in it too. But a different one this time. Which of these two exceptions will this method ultimately throw? The exception thrown by the finally block! But now we've come to another point about try-with-resources. Let's consider how try-with-resources behaves in the same situation. We get an exception in the try block when trying to close resources in the close() method, i.e. in the implicit finally block. Which of these exceptions will the catch block catch? The one thrown by the try block! The exception from the implicit finally block (from the lose() method) will be ignored. This ignoring of exceptions is also called exception suppression.

13. What are bitwise operations?

Bitwise operations are operations on sequences of bits. They include logical operations and bitwise shifts. Logical operators:
  • bitwise AND — Compares bit values. Any bit set to 0 (false) sets the corresponding bit in the result to 0. That is, if a bit is 1 (true) in both of the compared values, then the resulting bit will also be 1.

    Denoted as AND or &

    Example: 10111101 & 01100111 = 00100101

  • bitwise OR — This operation is the opposite of the previous one. Any bit set to 1 sets the corresponding bit in the result to 1. Accordingly, if the bit is 0 in both compared values, then the resulting bit will also be 0.

    Denoted as OR or |

    Example: 10100101 | 01100011 = 11100111

  • bitwise NOT — This operator is applied to a single value. It flips (inverts) the bits. That is, the bits that were 1 become 0; and those that were 0 become 1.

    Denoted as NOT or ~

    Example: ~10100101 = 01011010

  • bitwise exclusive OR — Compares bit values. If both bits are 1, then the resulting bit is 0. If both bits are 0, then the resulting bit is 0. In other words, in order for the resulting bit to be 1, just one of the bits must be 1, and the other bit must be 0.

    Denoted as XOR or ^

    Example: 10100101 ^ 01100011 = 11000110

Bitwise shifts (>> and <<) shift the operand's bits in the specified direction, by the specified number of places. Vacated positions are filled with zeros. For example:
  1. 01100011 >> 4 = 00000110
  2. 01100011 << 3 = 00011000
The exception is when you shift a negative number to the right. As you will recall, the first bit of a signed number indicates the sign. If this bit is 1, then the number is negative. If you shift a negative number, the vacated positions are not filled with zeros, but rather with ones, since the sign bit must be preserved. For example: 10100010 >> 2 = 11101000 That said, Java has an additional unsigned right shift operator (>>>). This operator is analogous to >>, but when shifted, vacated positions are filled with 0, regardless of whether the operand is a negative number or a positive number. For example: 10100010 >>> 2 = 00101000 Read more about bitwise operations here. Exploring questions and answers from a job interview for a Java developer position. Part 2 - 2You can take the hash() method in HashMaps as an example of bitwise shifts in Java. This method is used to determine the key's special internal hashcode: Exploring questions and answers from a job interview for a Java developer position. Part 2 - 3This method lets you evenly distribute the data in a HashMap, in order to minimize the number of collisions.

14. What standard immutable objects are there in Java?

An object is immutable if it does not allow its original values to change. It may have methods that return new objects of the same type with different values. Some standard immutable objects include:
  • undoubtedly, Java's most famous immutable type is String;
  • instances of the wrapper classes that wrap standard types: Boolean, Character, Byte, Short, Integer, Long, Double, Float;
  • BigInteger and BigDecimal objects, which are usually used for especially BIG numbers;
  • StackTraceElement objects that make up a stack trace (for example, the stack trace of an exception);
  • an object of the File class — it can modify files, but at the same time the object itself remains unchanged;
  • UUIDs, which are often used to uniquely identify elements;
  • all objects of classes in the java.time package;
  • Locale objects, which are used to identify a geographic, political, or cultural region.

15. What are the advantages of immutable object over ordinary objects?

  1. Immutable objects are safe to use in a multithreaded environment. They make it so you don't have to worry about data loss due to race conditions. This is different than when you're working with ordinary objects. In that case, you have to think and come up with good mechanisms when using the object in a parallel environment.

  2. Immutable objects are good as keys in a map. If you use a mutable object as a HashMap key and then the object's state changes, then the data structure could get confused: the object will still be present, but if you use containsKey(), you might not find it.

  3. Immutable objects are great for storing immutable (constant) data that should never be changed while the program is running.

  4. Another advantage is failure atomicity. If an immutable object throws an exception, it will not be left in an unwanted (broken) state.

  5. These classes are easy to test.

  6. You don't need any additional mechanisms such as a copy constructor or implementation of object cloning.

Questions about OOP

16. What are the advantages of OOP in general and in comparison with procedural programming?

Okay, advantages of OOP:
  1. Complex applications are easier to write using OOP than procedural programming since everything is broken down into small modules — objects that interact with each other — and as a result, programming is reduced to relationships between objects.

  2. Applications written with OOP are much easier to modify (when design principles are properly observed).

  3. Since both the data and data operations form a single entity, they are not smeared all over the application (which is often the case in procedural programming).

  4. The principle of encapsulation protects the most critical data from the user.

  5. The same code can be reused with different data because classes let you create many objects, each with its own values.

  6. Inheritance and polymorphism also let you reuse and extend existing code (instead of duplicating similar functionality).

  7. Extending an application is simpler than with a procedural approach.

  8. The OOP approach makes it possible to abstract away implementation details.

17. Tell us what disadvantages OOP has

Unfortunately, they also exist:
  1. OOP requires a lot of theoretical knowledge that must be mastered before you can write anything.

  2. OOP ideas are not so easy to understand and apply in practice (you need to be a little philosopher at heart).

  3. OOP reduces a program's performance slightly due to the increased complexity of the system.

  4. The OOP approach requires more memory since everything consists of classes, interfaces, methods, which take up much more memory than ordinary variables.

  5. The time required for the initial analysis is greater than for a procedural approach.

18. What is static polymorphism versus dynamic polymorphism?

Polymorphism allows objects of the same class or interface to behave differently. There are two types of polymorphism, which are also known as early and late binding. Static polymorphism, or early binding:
  • occurs at compile time (early in the program's life cycle);
  • decides which method to execute at compile time;
  • method overloading is an example of static polymorphism;
  • early binding includes private, static, and final methods;
  • inheritance is not involved in early binding;
  • static polymorphism does not involve specific objects, but rather information about the class type that appears to the left of a variable name.
Dynamic polymorphism, or late binding:
  • occurs at runtime (while the program is running);
  • dynamic polymorphism decides which specific implementation a method will have at runtime;
  • method overriding is an example of dynamic polymorphism;
  • late binding means assigning a specific object, a reference of its type, or its superclass;
  • inheritance is associated with dynamic polymorphism.

19. Provide a definition of the principle of abstraction in OOP

In OOP, abstraction is a way to isolate a set of meaningful characteristics of an object, while excluding insignificant details. That is, when designing a program with an OOP approach, you focus on general models, without going into the details of their implementation. In Java, abstraction is realized through interfaces. For example, you have a car and that will be an interface. And the various interactions with it — for example, starting the engine, shifting gears — are functions, which we use without delving into implementation details. Indeed, when you're driving, you don't think about exactly how the gearbox fulfills its purpose, or how the key starts the engine, or how exactly the steering wheel turns the wheels. And if you replace the implementation of some functionality (for example, the engine), you might not even notice it. It doesn't matter to you: you don't delve into the implementation details. What matters to you is that the action is carried out. In essence, this abstracting away implementation details. At this point, we will stop today: to be continued! Exploring questions and answers from a job interview for a Java developer position. Part 2 - 4
Read more: