Java-interview: vragen over OOP
1. Wat zijn de kenmerken van Java?
Antwoord:-
OOP-concepten:
- object oriëntatie
- erfenis
- inkapseling
- polymorfisme
- abstractie
-
Platformoverschrijdend: een Java-programma kan zonder wijzigingen op elk platform worden uitgevoerd. Dit vereist natuurlijk een geïnstalleerde JVM (Java virtual machine).
-
Hoge prestaties: de Just-In-Time (JIT)-compiler maakt hoge prestaties mogelijk. De JIT-compiler converteert de bytecode naar machinecode en vervolgens begint de JVM met de uitvoering.
- Multithreading: De JVM maakt een uitvoeringsthread genaamd de
main thread
. Een programmeur kan meerdere threads maken door af te leiden van de klasse Thread of door deRunnable
interface te implementeren.
2. Wat is overerving?
Overerving betekent dat een klasse een andere klasse kan erven (met behulp van het sleutelwoord extends ). Dit betekent dat u code kunt hergebruiken van de klasse die u erft. De bestaande klasse staat bekend als desuperclass
en de nieuw gecreëerde klasse is de subclass
. Mensen zeggen ook dat je de termen parent en child
.
public class Animal {
private int age;
}
public class Dog extends Animal {
}
waar Animal
is de parent
en Dog
is de child
.
3. Wat is inkapseling?
Deze vraag wordt vaak gesteld in sollicitatiegesprekken voor vacatures voor Java-ontwikkelaars. Inkapseling is het verbergen van de implementatie door toegangsmodifiers, getters en setters te gebruiken. Dit wordt gedaan om externe toegang te voorkomen waar ontwikkelaars denken dat dit nodig is. Een simpel voorbeeld uit het echte leven is de auto. We hebben geen directe toegang tot de werking van de motor. Het enige wat we hoeven te doen is de sleutel in het contact steken en de motor starten. De processen die onder de motorkap plaatsvinden zijn onze zaken niet. Bovendien, als we ons zouden bemoeien met de activiteit van de motor, zou dit kunnen leiden tot een onvoorspelbare situatie, mogelijk met schade aan de auto en lichamelijk letsel tot gevolg. Precies hetzelfde gebeurt bij het programmeren. Dit staat goed beschreven op Wikipedia. Op CodeGym staat ook een artikel over inkapseling .4. Wat is polymorfisme?
Polymorfisme is het vermogen van een programma om objecten met dezelfde interface op dezelfde manier te behandelen, zonder informatie over het specifieke type van het object. Zoals het gezegde luidt: "één interface - veel implementaties". Met polymorfisme kunt u verschillende soorten objecten combineren en gebruiken op basis van gedeeld gedrag. We hebben bijvoorbeeld een Animal-klasse met twee afstammelingen: Dog en Cat. De generieke Animal-klasse heeft een gedrag dat door iedereen wordt gedeeld, namelijk het vermogen om geluid te maken. We gebruiken polymorfe mogelijkheden wanneer we alles moeten verzamelen dat de klasse Animal erft en de methode "maak geluid" moeten uitvoeren. Zo ziet het eruit:
List<Animal> animals = Arrays.asList(new Cat(), new Dog(), new Cat());
animals.forEach(animal -> animal.makeSound());
Met andere woorden, polymorfisme is nuttig. En dit geldt ook voor polymorfe (overbelaste) methoden. Hoe polymorfisme te gebruiken
Interviewvragen over Java-syntaxis
5. Wat is een constructor in Java?
Constructeurs hebben de volgende kenmerken:- Wanneer een nieuw object wordt gemaakt, gebruikt het programma de juiste constructor om het te maken.
- Een constructor is als een methode. De onderscheidende kenmerken liggen in het feit dat er geen retourwaarde is (inclusief ongeldig) en dat de naam dezelfde is als de naam van de klasse.
- Als er geen constructor expliciet wordt gemaakt, wordt er automatisch een lege constructor gemaakt.
- Een constructor kan worden overschreven.
- Als u een constructor met parameters declareert, maar er ook een zonder parameters nodig heeft, moet u deze apart maken, aangezien deze niet automatisch wordt gemaakt.
6. Welke twee klassen erven Object niet?
Laat u niet misleiden door strikvragen - dergelijke klassen bestaan niet. Alle klassen erven de klasse Object rechtstreeks of via voorouders!7. Wat is een lokale variabele?
Dit is een andere populaire interviewvraag voor Java-ontwikkelaars. Een lokale variabele is een variabele die binnen een methode is gedefinieerd en bestaat zolang de methode wordt uitgevoerd. Zodra de uitvoering stopt, houdt de lokale variabele op te bestaan. Hier is een programma dat een lokale variabele genaamd helloMessage gebruikt in de main() methode:
public static void main(String[] args) {
String helloMessage;
helloMessage = "Hello, World!";
System.out.println(helloMessage);
}
8. Wat is een instantievariabele?
Een instantievariabele is een variabele die binnen een klasse wordt gedeclareerd. Het bestaat zolang een object bestaat. We hebben bijvoorbeeld een klasse Bee, die twee instantievariabelen heeft: nectarLoad en maxNectarLoad:
public class Bee {
/**
* Current nectar load
*/
private double nectarLoad;
/**
* Maximum nectar that can the bee can collect.
*/
private double maxNectarLoad = 20.0;
...
}
9. Wat zijn toegangsmodifiers?
Toegangsmodificatoren zijn een mechanisme voor het aanpassen van de toegang tot klassen, methoden en variabelen. De volgende modifiers bestaan, weergegeven in volgorde van toenemende toegang:private
— Deze toegangsmodificator wordt gebruikt op methoden, velden en constructors. De toegang is beperkt tot de klasse waarin ze zijn gedeclareerd.package-private (default)
— Dit is het standaard toegangsniveau voor klassen. Toegang is beperkt tot het specifieke pakket waarin een klasse, methode, variabele of constructor is gedeclareerd.protected
— Deze toegangsmodificator biedt hetzelfde toegangsniveau alspackage-private
met de toevoeging van toegang voor klassen die een klasse erven met deprotected
modifier.public
— Dit toegangsniveau wordt ook gebruikt voor lessen. Dit toegangsniveau betekent dat er volledige toegang is tot de hele applicatie.
10. Wat is methode-overriding?
We overschrijven methoden wanneer een onderliggende klasse het gedrag van de bovenliggende klasse wil wijzigen. Als we ook moeten doen wat in de parent-methode staat, kunnen we super.methodName() in de child gebruiken, die de parent-methode zal uitvoeren. Daarna kunnen we onze aanvullende logica toevoegen. Vereisten die in acht moeten worden genomen:- de handtekening van de methode moet hetzelfde zijn
- de retourwaarde moet hetzelfde zijn
11. Wat zijn methodehandtekeningen?
Een methodehandtekening is de combinatie van de naam van de methode en de argumenten die de methode nodig heeft. De methodehandtekening is de unieke identificatie van een methode bij het overbelasten van methoden.12. Wat is methode-overbelasting?
Methode-overbelasting is een kenmerk van polymorfisme waarbij we de handtekening van de methode wijzigen om meerdere methoden te creëren die dezelfde actie uitvoeren:- dezelfde naam
- verschillende argumenten
- er kunnen verschillende soorten retourzendingen zijn
ArrayList
De methode van de klasse kan bijvoorbeeld add()
overbelast zijn, waardoor we op verschillende manieren kunnen toevoegen, afhankelijk van de invoerargumenten:
add(Object o)
— Deze methode voegt gewoon een object toeadd(int index, Object o)
— Deze methode voegt een object toe aan een specifieke indexadd(Collection<Object> c)
— Deze methode voegt een lijst met objecten toeadd(int index, Collection<Object> c)
— Deze methode voegt een lijst met objecten toe vanaf een specifieke index.
13. Wat is een interface?
Java ondersteunt geen meervoudige overerving. Om deze beperking te overwinnen, werden interfaces toegevoegd in de vorm die we kennen en waar we van houden ;) Lange tijd hadden interfaces alleen methoden zonder enige implementatie. Laten we er in de context van dit antwoord over praten. Bijvoorbeeld:
public interface Animal {
void makeSound();
void eat();
void sleep();
}
Hieruit volgen enkele details:
- Alle methoden in een interface zijn openbaar en abstract
- Alle variabelen zijn publiek statisch definitief
- Klassen erven geen interfaces (dwz we gebruiken het sleutelwoord extends niet). In plaats daarvan implementeren klassen ze (dwz we gebruiken het sleutelwoord implements). Bovendien kunt u zoveel interfaces implementeren als u wilt.
- Klassen die een interface implementeren, moeten een implementatie bieden van alle methoden die zich in de interface bevinden.
public class Cat implements Animal {
public void makeSound() {
// Method implementation
}
public void eat() {
// Implementation
}
public void sleep() {
// Implementation
}
}
14. Wat is een standaardmethode in een interface?
Laten we het nu hebben over standaardmethoden. Waar zijn die voor? Voor wie zijn ze bedoeld? Deze methoden zijn toegevoegd om "beide handen" te dienen. Waar heb ik het over? Wel, aan de ene kant was er behoefte om nieuwe functionaliteit toe te voegen: lambda's en de Stream API. Aan de andere kant was het nodig om vast te houden waar Java om bekend staat: achterwaartse compatibiliteit. Hiervoor hadden interfaces een aantal nieuwe kant-en-klare oplossingen nodig. Dit is hoe standaardmethoden tot ons kwamen. Een standaardmethode is een geïmplementeerde methode in een interface, gemarkeerd met hetdefault
trefwoord. Bijvoorbeeld de bekende stream()
methode in de Collection
interface. Geloof me, deze interface is niet zo eenvoudig als het lijkt. Of ook de al even bekende forEach()
methode in deIterable
koppel. Het bestond ook niet totdat de standaardmethoden werden toegevoegd. Je kunt er trouwens ook hier over lezen op CodeGym .
15. Hoe erven we dan twee identieke standaardmethoden?
Het vorige antwoord over wat een standaardmethode is, roept een andere vraag op. Als je methoden in interfaces kunt implementeren, dan kun je in theorie twee interfaces met dezelfde methode implementeren. Hoe doen we dat? Hier zijn twee verschillende interfaces met dezelfde methode:
interface A {
default void foo() {
System.out.println("Foo A");
}
}
interface B {
default void foo() {
System.out.println("Foo B");
}
}
En we hebben een klasse die deze twee interfaces implementeert. Maar hoe kiezen we een specifieke methode in interface A of B? De volgende speciale constructie maakt dit mogelijk: A.super.foo()
:
public class C implements A, B {
public void fooA() {
A.super.foo();
}
public void fooB() {
B.super.foo();
}
}
fooA()
De methode gebruikt dus de foo()
standaardmethode van de A
interface, terwijl de methode de methode van de interface fooB()
gebruikt . foo()
B
16. Wat zijn abstracte methoden en klassen?
In Javaabstract
is dit een gereserveerd woord. Het wordt gebruikt om abstracte klassen en methoden aan te duiden. Ten eerste hebben we definities nodig. Een abstracte methode is een methode die wordt gedeclareerd met behulp van het abstract
trefwoord zonder een implementatie in een abstracte klasse. Dat wil zeggen, dit is een methode zoals in een interface, maar met toevoeging van een trefwoord, bijvoorbeeld:
public abstract void foo();
Een abstracte klasse is een klasse die ook is gemarkeerd met het abstract
trefwoord:
public abstract class A {
}
Een abstracte klasse heeft verschillende kenmerken:
- je kunt geen object van een abstracte klasse maken
- het kan abstracte methoden hebben
- het kan ook geen abstracte methoden hebben
17. Wat is het verschil tussen String, StringBuilder en StringBuffer?
String
waarden worden opgeslagen in een constante tekenreekspool. Zodra een string is aangemaakt, verschijnt deze in deze pool. En je kunt het niet verwijderen. Bijvoorbeeld:
String name = "book";
De variabele verwijst naar de constante stringpool. Als we de naamvariabele op een andere waarde zetten, hebben we:
name = "pen";
De pool met constante tekenreeksen ziet er als volgt uit: Met andere woorden, beide waarden blijven behouden. Tekenreeksbuffer:
String
waarden worden opgeslagen in een stapel. Als een waarde wordt gewijzigd, vervangt de nieuwe waarde de oude.String Buffer
is gesynchroniseerd en is daarom thread-safe.- Vanwege draadveiligheid zijn de prestaties slecht.
StringBuffer name = “book”;
Zodra de waarde van de naamvariabele verandert, verandert de waarde in de stack: StringBuilder is precies hetzelfde als StringBuffer
, alleen is het niet thread-safe. Als gevolg hiervan is het merkbaar sneller dan StringBuffer
.
18. Wat is het verschil tussen een abstracte klasse en een interface?
Abstracte klasse:- Abstracte klassen hebben een standaardconstructor. Het wordt elke keer aangeroepen als een afstammeling van de abstracte klasse wordt gemaakt.
- Ze kunnen zowel abstracte methoden als niet-abstracte methoden bevatten. Over het algemeen hoeft een abstracte klasse geen abstracte methoden te hebben.
- Een klasse die een abstracte erft, moet alleen abstracte methoden implementeren.
- Een abstracte klasse kan instantievariabelen hebben (zie vraag #5).
- Een interface heeft geen constructor en kan niet worden geïnitialiseerd.
- Alleen abstracte methoden kunnen worden toegevoegd (behalve standaardmethoden).
- Klassen die de interface implementeren, moeten alle methoden implementeren (behalve standaardmethoden).
- Interfaces kunnen alleen constanten hebben.
19. Waarom is toegang tot een element in een array O(1)?
Deze vraag werd letterlijk gesteld in mijn laatste interview. Zoals ik later leerde, is het doel van deze vraag om te zien hoe een persoon denkt. Het is duidelijk dat deze kennis weinig praktische waarde heeft. Alleen weten dat het genoeg is. Eerst moeten we verduidelijken dat O(1) de notatie is voor de tijdcomplexiteit van een "constante tijd"-algoritme. Met andere woorden, deze aanduiding geeft de snelste uitvoeringstijd aan. Om deze vraag te beantwoorden, moeten we nadenken over wat we weten over arrays. Om eenint
array te maken, moeten we het volgende schrijven:
int[] intArray = new int[100];
Uit deze syntaxis kunnen verschillende conclusies worden getrokken:
- Wanneer een array wordt gedeclareerd, is het type bekend. Als het type bekend is, is de grootte van elke cel in de array bekend.
- De grootte van de hele array is bekend.
Dus hoe komen we bij O (1) bij toegang tot objecten in een ArrayList?
Deze vraag volgt onmiddellijk op de vorige. De waarheid is dat wanneer we werken met een array die primitieven bevat, we van tevoren (op het moment van creatie) de grootte van het elementtype kennen. Maar wat doen we als we dit soort overervingshiërarchie hebben en we een verzameling willen maken voor elementen van type A en verschillende implementaties willen toevoegen (B, C en D):
List<A> list = new ArrayList();
list.add(new B());
list.add(new C());
list.add(new D());
list.add(new B());
Hoe berekenen we in deze situatie de grootte van elke cel? Elk object zal immers anders zijn, eventueel met andere aanvullende velden. Wat moeten we doen? Hier wordt de vraag gesteld op een manier die bedoeld is om u in verwarring te brengen. We weten dat de collectie niet direct objecten opslaat. Het slaat alleen verwijzingen naar objecten op. En alle referenties hebben dezelfde grootte, en het is bekend. Daarom berekenen we hier adressen op dezelfde manier als in de vorige vraag.
21. Autoboxen en uitpakken
Historische achtergrond: autoboxing en unboxing zijn enkele van de belangrijkste innovaties in JDK 5. Autoboxing is het proces van automatische conversie van een primitief type naar een overeenkomstige wrapperklasse. Unboxing is precies het tegenovergestelde van autoboxing. Het is het proces waarbij een wrapper-klasse wordt omgezet in een primitieve. Maar als de waarde van een wrapper isnull
, NullPointerException
wordt er een gegenereerd tijdens het uitpakken.
Primitieven en hun bijbehorende wikkels
Primitief | Wrapper klasse |
---|---|
booleaans | Booleaans |
int | Geheel getal |
byte | Byte |
char | Karakter |
vlot | Vlot |
lang | Lang |
kort | Kort |
dubbele | Dubbele |
// Autoboxing gebeurt:
-
bij het toewijzen van een primitieve aan een verwijzing naar een wrapperklasse:
VOOR Java 5:
// Manual boxing (the way it was BEFORE Java 5). public void boxingBeforeJava5() { Boolean booleanBox = new Boolean(true); Integer intBox = new Integer(3); // And so on for other types } After Java 5: // Automatic boxing (the way it became in Java 5). public void boxingJava5() { Boolean booleanBox = true; Integer intBox = 3; // And so on for other types }
-
wanneer een primitieve als argument wordt doorgegeven aan een methode die een wrapper verwacht:
public void exampleOfAutoboxing() { long age = 3; setAge(age); } public void setAge(Long age) { this.age = age; }
// Unboxing gebeurt:
-
wanneer we een instantie van een wrapper-klasse toewijzen aan een primitieve variabele:
// 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;
-
Tijdens rekenkundige bewerkingen. De bewerkingen zijn alleen van toepassing op primitieve typen, dus unboxing naar primitieven is noodzakelijk.
// BEFORE Java 5: Integer integerBox1 = new Integer(1); Integer integerBox2 = new Integer(2); // A comparison used to require this: integerBox1.intValue() > integerBox2.intValue() // In Java 5 integerBox1 > integerBox2
-
bij het doorgeven van een instantie van een wrapper-klasse aan een methode die de overeenkomstige primitieve neemt:
public void exampleOfAutoboxing() { Long age = new Long(3); setAge(age); } public void setAge(long age) { this.age = age; }
22. Wat is het laatste zoekwoord en waar wordt het gebruikt?
Hetfinal
sleutelwoord kan worden gebruikt voor variabelen, methoden en klassen.
- De waarde van een laatste variabele kan niet worden gewijzigd nadat deze is geïnitialiseerd.
- Een laatste klas is onvruchtbaar :) Het kan geen kinderen krijgen.
- Een laatste methode kan niet worden overschreven door een afstammeling.
Laatste variabelen
Java biedt ons twee manieren om een variabele te declareren en er een waarde aan toe te wijzen:- U kunt een variabele declareren en later initialiseren.
- U kunt een variabele declareren en meteen een waarde toekennen.
public class FinalExample {
// A static final variable that is immediately initialized:
final static String FINAL_EXAMPLE_NAME = "I'm likely the final one";
// A final variable that is not initialized, but will only work if you
// initialize it 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);
// The final FinalExample.FINAL_EXAMPLE_NAME field cannot be accessed
// FinalExample.FINAL_EXAMPLE_NAME = "Not you're not!";
// The final Config.creationTime field cannot be accessed
// finalExample.creationTime = 1L;
}
}
Kan een laatste variabele als een constante worden beschouwd?
Aangezien we geen nieuwe waarden kunnen toekennen aan eindvariabelen, lijkt het erop dat dit constante variabelen zijn. Maar alleen op het eerste gezicht: als het gegevenstype van de variabele isimmutable
, dan is het inderdaad een constante. Maar als het gegevenstype mutable
, dat wil zeggen veranderlijk is, dan is het mogelijk om methoden en variabelen te gebruiken om de waarde te wijzigen van het object waarnaar wordt verwezen door een final
variabele. Hierdoor kan het geen constante worden genoemd. Het volgende voorbeeld laat zien dat sommige eindvariabelen echte constanten zijn, terwijl andere dat niet zijn, omdat ze kunnen worden gewijzigd.
public class FinalExample {
// Immutable final variables
final static String FINAL_EXAMPLE_NAME = "I'm likely the final one";
final static Integer FINAL_EXAMPLE_COUNT = 10;
// Mutable final variables
final List<String> addresses = new ArrayList();
final StringBuilder finalStringBuilder = new StringBuilder("Constant?");
}
Lokale eindvariabelen
Wanneer eenfinal
variabele binnen een methode wordt gemaakt, wordt deze een local final
variabele genoemd:
public class FinalExample {
public static void main(String[] args) {
// You can do this
final int minAgeForDriveCar = 18;
// Or you can do this, in a for-each loop:
for (final String arg : args) {
System.out.println(arg);
}
}
}
We kunnen het laatste trefwoord gebruiken in een verbeterde for-lus, omdat er na elke iteratie van de lus een nieuwe variabele wordt gemaakt. Houd er rekening mee dat dit niet van toepassing is op een normale for-lus, dus we krijgen een compileerfout.
// The final local j variable cannot be assigned
for (final int i = 0; i < args.length; i ++) {
System.out.println(args[i]);
}
Laatste klasse
Een klasse die is gedeclareerd alsfinal
kan niet worden uitgebreid. Simpel gezegd, geen enkele andere klasse kan het erven. Een uitstekend voorbeeld van een final
klasse in de JDK is String. De eerste stap bij het maken van een onveranderlijke klasse is deze te markeren als final
, waardoor wordt voorkomen dat deze wordt uitgebreid:
public final class FinalExample {
}
// Compilation error!
class WantsToInheritFinalClass extends FinalExample {
}
Laatste methoden
Wanneer een methode als definitief is gemarkeerd, wordt het een definitieve methode genoemd (logisch, toch?). Een laatste methode kan niet worden overschreven in een onderliggende klasse. Overigens zijn de methoden wait() en notification() van de klasse Object definitief, dus we kunnen ze niet overschrijven.
public class FinalExample {
public final String generateAddress() {
return "Some address";
}
}
class ChildOfFinalExample extends FinalExample {
// Compilation error!
@Override
public String generateAddress() {
return "My OWN Address";
}
}
Hoe en waar final te gebruiken in Java
- Gebruik het laatste trefwoord om enkele constanten op klasseniveau te definiëren;
- Maak definitieve variabelen voor objecten die u niet wilt wijzigen. Bijvoorbeeld objectspecifieke eigenschappen die we kunnen gebruiken voor logging-doeleinden.
- Als je niet wilt dat een les wordt verlengd, markeer deze dan als definitief.
- Als u een onveranderlijke klasse moet maken, moet u deze definitief maken.
- Als u wilt dat de implementatie van een methode niet verandert in zijn afstammelingen, markeer de methode dan als
final
. Dit is erg belangrijk om er zeker van te zijn dat de implementatie niet verandert.
23. Wat zijn veranderlijke en onveranderlijke typen?
Veranderlijk
Veranderlijke objecten zijn objecten waarvan de status en variabelen na creatie kunnen worden gewijzigd. Voorbeelden van veranderlijke klassen zijn StringBuilder en StringBuffer. Voorbeeld:
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());
// We are updating the name field, so this is a mutable object
obj.setAddress("Updated address");
System.out.println(obj.getAddress());
}
}
Onveranderlijk
Onveranderlijke objecten zijn objecten waarvan de status en variabelen niet kunnen worden gewijzigd nadat het object is gemaakt. Een geweldige sleutel voor een HashMap, vind je niet? :) Bijvoorbeeld String, Integer, Double, enzovoort. Voorbeeld:
// We'll 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;
}
// We remove the setter
public static void main(String[] args) {
ImmutableExample obj = new ImmutableExample("Old address");
System.out.println(obj.getAddress());
// There is no way to change this field, so it is an immutable object
// obj.setName("new address");
// System.out.println(obj.getName());
}
}
In het volgende deel gaan we in op vragen en antwoorden over collecties. Mijn profiel op GitHub Top 50 sollicitatievragen en antwoorden voor Java Core. Deel 2
GO TO FULL VERSION