Java interview: OOP questions
1. What features does Java have?
Answer:-
OOP concepts:
- object orientation;
- inheritance;
- encapsulation;
- polymorphism;
- abstraction.
-
Cross-platform: A Java program can be run on any platform without any modifications. The only thing you need is an installed JVM (java virtual machine).
-
High performance: JIT(Just In Time compiler) allows high performance. JIT converts the bytecode into machine code and then the JVM starts execution.
- Multithreading: A thread of execution known as
Thread
. The JVM creates a thread calledmain thread
. A programmer can create multiple threads by inheriting from the Thread class or by implementing an interfaceRunnable
.
2. What is inheritance?
Inheritance means that one class can inherit (“ extends ”) another class. This way you can reuse code from the class from which you inherit. The existing class is known assuperclass
, and the one being created is known as subclass
. They also say parent
and child
.
public class Animal {
private int age;
}
public class Dog extends Animal {
}
where Animal
is parent
, and Dog
- child
.
3. What is encapsulation?
This question often comes up during Java developer interviews. Encapsulation is hiding the implementation using access modifiers, using getters and setters. This is done in order to close access for external use in those places where the developers deem it necessary. An accessible example from life is a car. We do not have direct access to the engine's operation. For us, the job is to put the key in the ignition and turn on the engine. And what processes will take place under the hood is none of our business. Moreover, our interference in this activity can lead to an unpredictable situation, due to which we can break the car and harm ourselves. Exactly the same thing happens in programming. Well described on Wikipedia . There is also an article about encapsulation on JavaRush .4. What is polymorphism?
Polymorphism is the ability of a program to identically use objects with the same interface without information about the specific type of that object. As they say, one interface - many implementations. With polymorphism, you can combine and use different types of objects based on their common behavior. For example, we have a class Animal, which has two descendants - Dog and Cat. The generic Animal class has a common behavior for all - make a sound. In the case when we need to put together all the descendants of the Animal class and execute the “make a sound” method, we use the possibilities of polymorphism. This is what it will look like:List<Animal> animals = Arrays.asList(new Cat(), new Dog(), new Cat());
animals.forEach(animal -> animal.makeSound());
So polymorphism helps us. Moreover, this also applies to polymorphic (overloaded) methods. Practice of using polymorphism
Interview Questions - Java Syntax
5. What is a constructor in Java?
The following characteristics are valid:- When a new object is created, the program uses the appropriate constructor to do so.
- A constructor is like a method. Its peculiarity is that there is no returning element (including void), and its name is the same as the name of the class.
- If no constructor is written explicitly, an empty constructor will be created automatically.
- The constructor can be overridden.
- If a constructor with parameters was created, but is also needed without parameters, it must be written separately, since it is not created automatically.
6. Which two classes do not inherit from Object?
Do not be fooled by provocations, there are no such classes: all classes directly or through ancestors are inherited from the Object class!7. What is Local Variable?
Another popular question during a Java developer interview. A local variable is a variable that is defined inside a method and exists until the moment the method is executed. Once execution ends, the local variable will cease to exist. Here is a program that uses the helloMessage local variable in the main() method:public static void main(String[] args) {
String helloMessage;
helloMessage = "Hello, World!";
System.out.println(helloMessage);
}
8. What is Instance Variable?
Instance Variable is a variable that is defined inside a class, and it exists until the moment the object exists. An example is the Bee class, which has two variables nectarCapacity and maxNectarCapacity:public class Bee {
/**
* Current nectar capacity
*/
private double nectarCapacity;
/**
* Maximal nectar that can take bee.
*/
private double maxNectarCapacity = 20.0;
...
}
9. What are access modifiers?
Access modifiers are a tool that allows you to customize access to classes, methods, and variables. There are the following modifiers, ordered in order of increasing access:private
- used for methods, fields and constructors. The access level is only the class within which it is declared.package-private(default)
- can be used for classes. Access only in a specific package in which a class, method, variable, constructor is declared.protected
— the same access aspackage-private
+ for those classes that inherit from a class with the modifierprotected
.public
- also used for classes. Full access throughout the entire application.
10. What is overriding methods?
Method overriding occurs when the child wants to change the behavior of the parent class. If you want what is in the parent method to be executed, you can use a construction like super.methodName() in the child, which will do the work of the parent method, and only then add logic. Requirements to be met:- the method signature must be the same;
- the return value should be the same.
11. What is a method signature?
A method signature is a set of the name of the method and the arguments that the method accepts. A method signature is a unique identifier for a method when overloading methods.12. What is method overloading?
Method overloading is a property of polymorphism in which by changing the method signature, you can create different methods for the same actions:- same method name;
- different arguments;
- there may be a different return type.
add()
of ArrayList
can be overloaded as follows and will perform the addition in a different way, depending on the incoming arguments:
add(Object o)
- simply adds an object;add(int index, Object o)
— adds an object to a specific index;add(Collection<Object> c)
— adds a list of objects;add(int index, Collection<Object> c)
— adds a list of objects, starting from a certain index.
13. What is Interface?
Multiple inheritance is not implemented in Java, so to overcome this problem, interfaces as we know them were added ;) For a long time, interfaces only had methods without implementing them. As part of this answer, we’ll talk about them. For example:
public interface Animal {
void makeSound();
void eat();
void sleep();
}
Some nuances follow from this:
- all methods in the interface are public and abstract;
- all variables are public static final;
- classes do not inherit them (extends), but implement them (implements). Moreover, you can implement as many interfaces as you like.
- classes that implement an interface must provide implementations of all the methods that the interface has.
public class Cat implements Animal {
public void makeSound() {
// method implementation
}
public void eat() {
// implementation
}
public void sleep() {
// implementation
}
}
14. What is default method in Interface?
Now let's talk about default methods. For what, for whom? These methods were added to make everything “both yours and ours.” What am I talking about? Yes, on the one hand, it was necessary to add new functionality: lambdas, Stream API, on the other hand, it was necessary to leave what Java is famous for - backward compatibility. To do this, it was necessary to introduce ready-made solutions into the interfaces. This is how default methods came to us. That is, the default method is an implemented method in the interface that has the keyworddefault
. For example, the well-known method stream()
in the Collection
. Check it out, this interface is not as simple as it seems ;). Or also an equally well-known method forEach()
from the Iterable
. It also didn’t exist until default methods were added. By the way, you can also read about this on JavaRush .
15. How then to inherit two identical default methods?
Based on the previous answer about what the default method is, you can ask another question. If you can implement methods in interfaces, then theoretically you can implement two interfaces with the same method, and how to do this? There are two different interfaces with the same method:interface A {
default void foo() {
System.out.println("Foo A");
}
}
interface B {
default void foo() {
System.out.println("Foo B");
}
}
And there is a class that implements these two interfaces. To avoid uncertainty and to compile the code, we need to override the method foo()
in the class C
, and we can simply call a method foo()
of any of the interfaces in it - A
or B
. But just how to choose a specific interface method А
or В
? There is a structure like this for this A.super.foo()
:
public class C implements A, B {
@Override
public void foo() {
A.super.foo();
}
}
or:
public class C implements A, B {
@Override
public void foo() {
B.super.foo();
}
}
Thus, a foo()
class method C
will use either the default method foo()
from the interface A
or a method foo()
from the interface B
.
16. What are abstract methods and classes?
Java has a reserved wordabstract
that is used to denote abstract classes and methods. First, some definitions. An abstract method is a method that is created without an implementation with a keyword abstract
in an abstract class. That is, this is a method like in the interface, only with the addition of a keyword, for example:
public abstract void foo();
An abstract class is a class that also has abstract
the word:
public abstract class A {
}
An abstract class has several features:
- an object cannot be created on its basis;
- it can have abstract methods;
- it may not have abstract methods.
17. What is the difference between String, String Builder and String Buffer?
The valuesString
are stored in a constant string pool. Once a row is created, it will appear in this pool. And it will not be possible to delete it. For example:
String name = "book";
...the variable will refer to the string pool Constant string pool If you set the variable name to a different value, you will get the following:
name = "pen";
Constant string pool So these two values will remain there. String Buffer:
- values
String
are stored on the stack. If the value is changed, then the new value will be replaced with the old one; String Buffer
synchronized and therefore thread safe;- Due to thread safety, the speed of operation leaves much to be desired.
StringBuffer name = "book";
As soon as the value of name changes, the value on the stack changes: StringBuilder Exactly the same as StringBuffer
, only it is not thread safe. Therefore, its speed is clearly higher than in StringBuffer
.
18. What is the difference between an abstract class and an interface?
Abstract class:- abstract classes have a default constructor; it is called every time a child of this abstract class is created;
- contains both abstract and non-abstract methods. By and large, it may not contain abstract methods, but still be an abstract class;
- a class that inherits from an abstract one must implement only abstract methods;
- an abstract class can contain an Instance Variable (see question #5).
- has no constructor and cannot be initialized;
- only abstract methods should be added (not counting default methods);
- classes that implement an interface must implement all methods (not counting default methods);
- interfaces can only contain constants.
19. Why does accessing an element in an array take O(1)?
This question is literally from the last interview. As I learned later, this question is asked in order to see how a person thinks. It is clear that there is little practical meaning in this knowledge: just knowing this fact is enough. First, we need to clarify that O(1) is a designation for the time complexity of an algorithm when the operation takes place in constant time. That is, this designation is the fastest execution. To answer this question, we need to understand what we know about arrays? To create an arrayint
, we must write the following:
int[] intArray = new int[100];
Several conclusions can be drawn from this recording:
- When creating an array, its type is known. If the type is known, then it is clear what size each cell of the array will be.
- It is known what size the array will be.
How do you get O(1) in accessing objects in an ArrayList?
This question immediately follows the previous one. It’s true that when we work with an array and there are primitives there, we know in advance what the size of this type is when it is created. But what if we have a scheme like the one in the picture: and we want to create a collection with elements of type A, and add different implementations - B, C, D:List<A> list = new ArrayList();
list.add(new B());
list.add(new C());
list.add(new D());
list.add(new B());
In this situation, how can you understand what size each cell will be, because each object will be different and may have different additional fields (or be completely different). What to do? Here the question is posed in such a way as to confuse and confuse. We know that in fact, the collection does not store objects, but only links to these objects. And all links have the same size, and it is known. So counting space here works the same way as in the previous question.
21. Autoboxing and unboxing
Historical background: autoboxing and autounboxing is one of the main innovations of JDK 5. Autoboxing is the process of automatic conversion from a primitive type to the appropriate wrapper class. Auto-unboxing - does exactly the opposite of auto-boxing - converts the wrapper class into a primitive. But if there is a wrapper valuenull
, then an exception will be thrown during unpacking NullPointerException
.
Matching primitive - wrapper
Primitive | Wrapper class |
---|---|
boolean | Boolean |
int | Integer |
byte | Byte |
char | Character |
float | Float |
long | Long |
short | Short |
double | Double |
Autopacking occurs:
-
when assigning a primitive a reference to the wrapper class:
BEFORE Java 5:
//manual packaging or how it was BEFORE Java 5. public void boxingBeforeJava5() { Boolean booleanBox = new Boolean(true); Integer intBox = new Integer(3); // and so on to other types } после Java 5: //automatic packaging or how it became in Java 5. public void boxingJava5() { Boolean booleanBox = true; Integer intBox = 3; // and so on to other types }
-
when passing a primitive as an argument to a method that expects a wrapper:
public void exampleOfAutoboxing() { long age = 3; setAge(age); } public void setAge(Long age) { this.age = age; }
Auto-unpacking occurs:
-
when we assign a primitive variable to the wrapper class:
//before Java 5: int intValue = new Integer(4).intValue(); double doubleValue = new Double(2.3).doubleValue(); char c = new Character((char) 3).charValue(); boolean b = Boolean.TRUE.booleanValue(); //and after JDK 5: int intValue = new Integer(4); double doubleValue = new Double(2.3); char c = new Character((char) 3); boolean b = Boolean.TRUE;
-
In cases with arithmetic operations. They apply only to primitive types; for this you need to do unboxing to the primitive.
// Before Java 5 Integer integerBox1 = new Integer(1); Integer integerBox2 = new Integer(2); // for comparison it was necessary to do this: integerBox1.intValue() > integerBox2.intValue() //в Java 5 integerBox1 > integerBox2
-
when passed to a wrapper in a method that accepts the corresponding primitive:
public void exampleOfAutoboxing() { Long age = new Long(3); setAge(age); } public void setAge(long age) { this.age = age; }
22. What is the final keyword and where to use it?
The keywordfinal
can be used for variables, methods and classes.
- A final variable cannot be reassigned to another object.
- final class is sterile)) it cannot have heirs.
- final method cannot be overridden on an ancestor.
final variables
;Java gives us two ways to create a variable and assign some value to it:- You can declare a variable and initialize it later.
- You can declare a variable and assign it immediately.
public class FinalExample {
//final static variable, which is immediately initialized:
final static String FINAL_EXAMPLE_NAME = "I'm likely final one";
//final is a variable that is not initialized, but will only work if
//initialize this in the constructor:
final long creationTime;
public FinalExample() {
this.creationTime = System.currentTimeMillis();
}
public static void main(String[] args) {
FinalExample finalExample = new FinalExample();
System.out.println(finalExample.creationTime);
// final field FinalExample.FINAL_EXAMPLE_NAME cannot be assigned
// FinalExample.FINAL_EXAMPLE_NAME = "Not you're not!";
// final field Config.creationTime cannot be assigned
// finalExample.creationTime = 1L;
}
}
Can the Final variable be considered a constant?
Since we won't be able to assign a new value to a final variable, it appears that these are constant variables. But this is only at first glance. If the data type the variable refers to isimmutable
, then yes, it is a constant. But if the data type mutable
is mutable, using methods and variables it will be possible to change the value of the object to which final
the variable refers, and in this case it cannot be called a constant. So, the example shows that some of the final variables are really constants, but some are not, and they can be changed.
public class FinalExample {
//immutable final variables:
final static String FINAL_EXAMPLE_NAME = "I'm likely final one";
final static Integer FINAL_EXAMPLE_COUNT = 10;
// mutable filter variables
final List<String> addresses = new ArrayList();
final StringBuilder finalStringBuilder = new StringBuilder("constant?");
}
Local final variables
Whenfinal
a variable is created inside a method, it is called local final
a variable:
public class FinalExample {
public static void main(String[] args) {
// This is how you can
final int minAgeForDriveCar = 18;
// or you can do it this way, in the foreach loop:
for (final String arg : args) {
System.out.println(arg);
}
}
}
We can use the keyword final
in extended loop for
because after completing an iteration of the loop, for
a new variable is created each time. But this doesn't apply to a normal for loop, so the code below will throw a compile-time error.
// final local changed j cannot be assigned
for (final int i = 0; i < args.length; i ++) {
System.out.println(args[i]);
}
Final class
You cannot extend a class declared asfinal
. Simply put, no class can inherit from this one. A great example final
of a class in the JDK is String
. The first step to creating an immutable class is to mark it as final
, so that it cannot be extended:
public final class FinalExample {
}
// Compilation error here
class WantsToInheritFinalClass extends FinalExample {
}
Final methods
When a method is marked final, it is called a final method (logical, right?). The Final method cannot be overridden in a descendant class. By the way, the methods in the Object class - wait() and notify() - are final, so we do not have the opportunity to override them.public class FinalExample {
public final String generateAddress() {
return "Some address";
}
}
class ChildOfFinalExample extends FinalExample {
// compile error here
@Override
public String generateAddress() {
return "My OWN Address";
}
}
How and where to use final in Java
- use the final keyword to define some class-level constants;
- create final variables for objects when you don't want them to be modified. For example, object-specific properties that we can use for logging purposes;
- if you don't want the class to be extended, mark it as final;
- if you need to create an immutable< class, you need to make it final;
- if you want the implementation of a method not to change in its descendants, designate the method as
final
. This is very important to ensure that the implementation does not change.
23. What is mutable immutable?
Mutable
Mutable are objects whose states and variables can be changed after creation. For example, classes such as StringBuilder, StringBuffer. Example:public class MutableExample {
private String address;
public MutableExample(String address) {
this.address = address;
}
public String getAddress() {
return address;
}
// this setter can change the name field
public void setAddress(String address) {
this.address = address;
}
public static void main(String[] args) {
MutableExample obj = new MutableExample("first address");
System.out.println(obj.getAddress());
// update the name field, so this is a mutable object
obj.setAddress("Updated address");
System.out.println(obj.getAddress());
}
}
Immutable
Immutable are objects whose states and variables cannot be changed after the object is created. Why not a great key for a HashMap, right?) For example, String, Integer, Double and so on. Example:// make this class final so no one can change it
public final class ImmutableExample {
private String address;
ImmutableExample (String address) {
this.address = address;
}
public String getAddress() {
return address;
}
//remove the setter
public static void main(String[] args) {
ImmutableExample obj = new ImmutableExample("old address");
System.out.println(obj.getAddress());
// Therefore, do not change this field in any way, so this is an immutable object
// obj.setName("new address");
// System.out.println(obj.getName());
}
}
24. How to write an immutable class?
After you figure out what mutable and immutable objects are, the next question is natural - how to write it? To write an immutable immutable class, you need to follow simple steps:- make the class final.
- make all fields private and create only getters for them. Setters, of course, are not needed.
- Make all mutable fields final so that the value can only be set once.
- initialize all fields through the constructor, performing a deep copy (that is, copying the object itself, its variables, variables of variables, and so on)
- clone mutable variable objects in getters to return only copies of values and not references to actual objects.
/**
* An example of creating an immutable object.
*/
public final class FinalClassExample {
private final int age;
private final String name;
private final HashMap<String, String> addresses;
public int getAge() {
return age;
}
public String getName() {
return name;
}
/**
* Clone the object before returning it.
*/
public HashMap<String, String> getAddresses() {
return (HashMap<String, String>) addresses.clone();
}
/**
* In the constructor, deep copy the mutable objects.
*/
public FinalClassExample(int age, String name, HashMap<String, String> addresses) {
System.out.println("Performing a deep copy in the constructor");
this.age = age;
this.name = name;
HashMap<String, String> temporaryMap = new HashMap<>();
String key;
Iterator<String> iterator = addresses.keySet().iterator();
while (iterator.hasNext()) {
key = iterator.next();
temporaryMap.put(key, addresses.get(key));
}
this.addresses = temporaryMap;
}
}
GO TO FULL VERSION