Java-intervju: frågor om OOP
1. Vad kännetecknar Java?
Svar:-
OOP-koncept:
- objektorientering
- arv
- inkapsling
- polymorfism
- abstraktion
-
Cross-platform: Ett Java-program kan köras på vilken plattform som helst utan några ändringar. Naturligtvis kräver detta en installerad JVM (virtuell Java-maskin).
-
Hög prestanda: Just-In-Time (JIT) kompilatorn gör hög prestanda möjlig. JIT-kompilatorn konverterar bytekoden till maskinkod och sedan startar JVM exekvering.
- Multithreading: JVM skapar en exekveringstråd som kallas
main thread
. En programmerare kan skapa flera trådar genom att härleda från klassen Thread eller implementera gränssnittetRunnable
.
2. Vad är arv?
Arv innebär att en klass kan ärva en annan klass (med hjälp av nyckelordet extends ). Det betyder att du kan återanvända kod från klassen du ärver. Den befintliga klassen är känd somsuperclass
och den nyskapade klassen är subclass
. Folk säger också använda termerna förälder och child
.
public class Animal {
private int age;
}
public class Dog extends Animal {
}
var Animal
är parent
och Dog
är child
.
3. Vad är inkapsling?
Denna fråga ställs ofta i intervjuer för Java-utvecklarpositioner. Encapsulation döljer implementeringen genom att använda åtkomstmodifierare, getters och seters. Detta görs för att förhindra extern åtkomst där utvecklare anser att det är nödvändigt. Ett enkelt exempel från verkligheten är bilen. Vi har ingen direkt tillgång till motorns drift. Allt vi behöver göra är att sätta nyckeln i tändningen och starta motorn. De processer som sker under huven är inte vår sak. Dessutom, om vi skulle störa motorns aktivitet, kan det leda till en oförutsägbar situation, eventuellt skada bilen och leda till kroppsskada. Exakt samma sak händer i programmering. Detta beskrivs bra på Wikipedia. Det finns även en artikel om inkapsling på CodeGym .4. Vad är polymorfism?
Polymorfism är ett programs förmåga att behandla objekt med samma gränssnitt på samma sätt, utan information om objektets specifika typ. Som ordspråket säger, "ett gränssnitt — många implementeringar". Med polymorfism kan du kombinera och använda olika typer av objekt baserat på delade beteenden. Vi har till exempel en Djurklass som har två ättlingar: Hund och Katt. Den generiska djurklassen har ett beteende som delas av alla, förmågan att göra ett ljud. Vi använder polymorfa förmågor när vi behöver samla ihop allt som ärver Animal-klassen och köra "make sound"-metoden. Så här ser det ut:
List<Animal> animals = Arrays.asList(new Cat(), new Dog(), new Cat());
animals.forEach(animal -> animal.makeSound());
Med andra ord är polymorfism till hjälp. Och detta gäller även polymorfa (överbelastade) metoder. Hur man använder polymorfism
Intervjufrågor om Java-syntax
5. Vad är en konstruktor i Java?
Konstruktörer har följande egenskaper:- När ett nytt objekt skapas använder programmet lämplig konstruktor för att skapa det.
- En konstruktör är som en metod. Dess särdrag ligger i det faktum att det inte finns något returvärde (inklusive void) och att dess namn är detsamma som namnet på klassen.
- Om ingen konstruktor skapas explicit skapas en tom konstruktor automatiskt.
- En konstruktör kan åsidosättas.
- Om du deklarerar en konstruktor med parametrar men också behöver en utan parametrar, måste du skapa den separat, eftersom den inte skapas automatiskt.
6. Vilka två klasser ärver inte Object?
Låt dig inte luras av knepfrågor – det finns inga sådana klasser. Alla klasser ärver objektklassen antingen direkt eller genom förfäder!7. Vad är en lokal variabel?
Detta är en annan populär intervjufråga för Java-utvecklare. En lokal variabel är en variabel som är definierad i en metod och som finns så länge metoden exekveras. Så snart exekveringen avslutas upphör den lokala variabeln att existera. Här är ett program som använder en lokal variabel som heter helloMessage i main()-metoden:
public static void main(String[] args) {
String helloMessage;
helloMessage = "Hello, World!";
System.out.println(helloMessage);
}
8. Vad är en instansvariabel?
En instansvariabel är en variabel som deklareras i en klass. Det finns så länge som ett objekt existerar. Till exempel har vi en Bee-klass, som har två instansvariabler - nectarLoad och maxNectarLoad:
public class Bee {
/**
* Current nectar load
*/
private double nectarLoad;
/**
* Maximum nectar that can the bee can collect.
*/
private double maxNectarLoad = 20.0;
...
}
9. Vad är åtkomstmodifierare?
Åtkomstmodifierare är en mekanism för att anpassa åtkomst till klasser, metoder och variabler. Följande modifierare finns, listade i ordning efter ökad åtkomst:private
— Denna åtkomstmodifierare används på metoder, fält och konstruktorer. Tillgången är begränsad till den klass där de är deklarerade.package-private (default)
— Det här är standardåtkomstnivån för klasser. Åtkomsten är begränsad till det specifika paketet där en klass, metod, variabel eller konstruktor deklareras.protected
— Denna åtkomstmodifierare erbjuder samma åtkomstnivå sompackage-private
med tillägg av åtkomst för klasser som ärver en klass med modifierarenprotected
.public
— Denna åtkomstnivå används också för klasser. Denna åtkomstnivå innebär att det finns full åtkomst i hela applikationen.
10. Vad är metodöverordnad?
Vi åsidosätter metoder när en barnklass vill ändra beteendet hos sin föräldraklass. Om vi också behöver göra det som finns i den överordnade metoden kan vi använda super.methodName() i den underordnade, som kommer att köra den överordnade metoden. Vi kan lägga till vår ytterligare logik efter det. Krav som måste följas:- metodsignaturen måste vara densamma
- returvärdet måste vara detsamma
11. Vad är metodsignaturer?
En metodsignatur är kombinationen av metodnamnet och de argument som metoden tar. Metodsignaturen är en metods unika identifierare vid överbelastning av metoder.12. Vad är metodöverbelastning?
Metodöverbelastning är en egenskap hos polymorfism där vi ändrar metodsignaturen för att skapa flera metoder som utför samma åtgärd:- samma namn
- olika argument
- det kan finnas olika returtyper
ArrayList
klassens add()
metod överbelastas, vilket gör att vi kan lägga till på olika sätt beroende på inmatningsargumenten:
add(Object o)
— Den här metoden lägger helt enkelt till ett objektadd(int index, Object o)
— Denna metod lägger till ett objekt vid ett specifikt indexadd(Collection<Object> c)
— Den här metoden lägger till en lista med objektadd(int index, Collection<Object> c)
— Den här metoden lägger till en lista över objekt som börjar från ett specifikt index.
13. Vad är ett gränssnitt?
Java stöder inte multipelt arv. För att övervinna denna begränsning lades gränssnitt till i formen som vi känner och älskar ;) Under lång tid hade gränssnitt bara metoder utan någon implementering. I samband med detta svar, låt oss prata om dem. Till exempel:
public interface Animal {
void makeSound();
void eat();
void sleep();
}
Några detaljer följer av detta:
- Alla metoder i ett gränssnitt är offentliga och abstrakta
- Alla variabler är offentliga statiska slutgiltiga
- Klasser ärver inte gränssnitt (dvs vi använder inte nyckelordet extends). Istället implementerar klasser dem (dvs vi använder nyckelordet implements). Dessutom kan du implementera så många gränssnitt du vill.
- Klasser som implementerar ett gränssnitt måste tillhandahålla en implementering av alla metoder som finns i gränssnittet.
public class Cat implements Animal {
public void makeSound() {
// Method implementation
}
public void eat() {
// Implementation
}
public void sleep() {
// Implementation
}
}
14. Vad är en standardmetod i ett gränssnitt?
Låt oss nu prata om standardmetoder. Vad är de till för? Vem är de till för? Dessa metoder lades till för att tjäna "båda händer". Vad pratar jag om? Jo, å ena sidan fanns det ett behov av att lägga till ny funktionalitet: lambdas och Stream API. Å andra sidan var det nödvändigt att behålla det som Java är känt för - bakåtkompatibilitet. För att göra detta behövde gränssnitt några nya färdiga lösningar. Så här kom standardmetoderna till oss. En standardmetod är en implementerad metod i ett gränssnitt, markerad meddefault
nyckelordet. Till exempel den välkända stream()
metoden i Collection
gränssnittet. Tro mig, det här gränssnittet är inte så enkelt som det verkar. Eller också den lika kända forEach()
metoden iIterable
gränssnitt. Det fanns inte heller förrän standardmetoderna lades till. Du kan förresten även läsa om det på CodeGym här .
15. Hur ärver vi då två identiska standardmetoder?
Det tidigare svaret om vad en standardmetod är väcker en annan fråga. Om du kan implementera metoder i gränssnitt, så kan du teoretiskt implementera två gränssnitt med samma metod. Hur gör vi det? Här är två olika gränssnitt med samma metod:
interface A {
default void foo() {
System.out.println("Foo A");
}
}
interface B {
default void foo() {
System.out.println("Foo B");
}
}
Och vi har en klass som implementerar dessa två gränssnitt. Men hur väljer vi en specifik metod i gränssnitt A eller B? Följande specialkonstruktion tillåter detta: A.super.foo()
:
public class C implements A, B {
public void fooA() {
A.super.foo();
}
public void fooB() {
B.super.foo();
}
}
Således fooA()
kommer metoden att använda standardmetoden foo()
för A
gränssnittet, medan fooB()
metoden kommer att använda foo()
metoden för B
gränssnittet.
16. Vad är abstrakta metoder och klasser?
I Javaabstract
är ett reserverat ord. Det används för att beteckna abstrakta klasser och metoder. Först behöver vi definitioner. En abstrakt metod är en metod som deklareras med nyckelordet abstract
utan implementering i en abstrakt klass. Det vill säga, detta är en metod som i ett gränssnitt, men med tillägg av ett nyckelord, till exempel:
public abstract void foo();
En abstrakt klass är en klass som också är märkt med abstract
nyckelordet:
public abstract class A {
}
En abstrakt klass har flera funktioner:
- du kan inte skapa ett objekt av en abstrakt klass
- det kan ha abstrakta metoder
- det kanske inte heller har abstrakta metoder
17. Vad är skillnaden mellan String, StringBuilder och StringBuffer?
String
värden lagras i en konstant strängpool. Så snart en sträng skapas, dyker den upp i denna pool. Och du kan inte ta bort den. Till exempel:
String name = "book";
Variabeln kommer att peka på den konstanta strängpoolen Genom att ställa in namnvariabeln till ett annat värde har vi:
name = "pen";
Den konstanta strängpoolen ser ut så här: Med andra ord, båda värdena förblir där. Strängbuffert:
String
värden lagras i en stack. Om ett värde ändras kommer det nya värdet att ersätta det gamla.String Buffer
är synkroniserad och är därför trådsäker.- På grund av trådsäkerheten är dess prestanda dålig.
StringBuffer name = “book”;
Så fort värdet på namnvariabeln ändras ändras värdet i stacken: StringBuilder är exakt samma som , StringBuffer
bara det är inte trådsäkert. Som ett resultat är det märkbart snabbare än StringBuffer
.
18. Vad är skillnaden mellan en abstrakt klass och ett gränssnitt?
Abstrakt klass:- Abstrakta klasser har en standardkonstruktor. Det kallas varje gång en ättling till den abstrakta klassen skapas.
- De kan innehålla både abstrakta metoder och icke-abstrakta. I allmänhet behöver en abstrakt klass inte ha abstrakta metoder.
- En klass som ärver en abstrakt måste endast implementera abstrakta metoder.
- En abstrakt klass kan ha instansvariabler (se fråga #5).
- Ett gränssnitt har ingen konstruktor och kan inte initieras.
- Endast abstrakta metoder kan läggas till (förutom standardmetoder).
- Klasser som implementerar gränssnittet måste implementera alla metoder (förutom standardmetoder).
- Gränssnitt kan bara ha konstanter.
19. Varför är åtkomst till ett element i en array O(1)?
Denna fråga ställdes bokstavligen i min senaste intervju. Som jag lärde mig senare är syftet med denna fråga att se hur en person tänker. Det är uppenbart att det finns lite praktiskt värde i denna kunskap. Att bara veta det räcker. Först måste vi klargöra att O(1) är notation för tidskomplexiteten för en "konstant tid"-algoritm. Med andra ord indikerar denna beteckning den snabbaste exekveringstiden. För att svara på denna fråga måste vi överväga vad vi vet om arrayer. För att skapa enint
array måste vi skriva följande:
int[] intArray = new int[100];
Flera slutsatser kan dras från denna syntax:
- När en array deklareras är dess typ känd. Om typen är känd är storleken på varje cell i arrayen känd.
- Storleken på hela arrayen är känd.
Så hur kommer vi fram till O(1) när vi kommer åt objekt i en ArrayList?
Denna fråga följer omedelbart efter den föregående. Sanningen är att när vi arbetar med en array som innehåller primitiver vet vi i förväg (vid skapandet) storleken på elementtypen. Men vad gör vi om vi har den här typen av arvshierarki och vi vill skapa en samling för element av typ A och lägga till olika implementeringar (B, C och D):
List<A> list = new ArrayList();
list.add(new B());
list.add(new C());
list.add(new D());
list.add(new B());
I den här situationen, hur beräknar vi storleken på varje cell? När allt kommer omkring kommer varje objekt att vara olika, eventuellt med olika tilläggsfält. Vad ska man göra? Här ställs frågan på ett sätt som är tänkt att förvirra dig. Vi vet att samlingen inte direkt lagrar föremål. Den lagrar bara referenser till objekt. Och alla referenser har samma storlek, och det är känt. Som ett resultat beräknar vi adresser här på samma sätt som i föregående fråga.
21. Autoboxning och unboxning
Historisk bakgrund: autoboxning och unboxing är några av de viktigaste innovationerna i JDK 5. Autoboxning är processen för automatisk konvertering från en primitiv typ till en motsvarande omslagsklass. Unboxing är raka motsatsen till autoboxning. Det är processen att omvandla en omslagsklass till en primitiv. Men om värdet på ett omslag ärnull
, kommer a NullPointerException
att kastas under uppackning.
Primitiver och deras motsvarande omslag
Primitiv | Omslagsklass |
---|---|
booleskt | Boolean |
int | Heltal |
byte | Byte |
röding | Karaktär |
flyta | Flyta |
lång | Lång |
kort | Kort |
dubbel | Dubbel |
// Autoboxning händer:
-
när du tilldelar en primitiv till en referens till en omslagsklass:
INNAN 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 }
-
när en primitiv skickas som ett argument till en metod som förväntar sig ett omslag:
public void exampleOfAutoboxing() { long age = 3; setAge(age); } public void setAge(Long age) { this.age = age; }
// Unboxing händer:
-
när vi tilldelar en instans av en wrapper-klass till en primitiv variabel:
// 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;
-
Under aritmetiska operationer. Operationerna gäller endast för primitiva typer, så uppackning till primitiva är nödvändigt.
// 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
-
när du skickar en instans av en omslagsklass till en metod som tar motsvarande primitiva:
public void exampleOfAutoboxing() { Long age = new Long(3); setAge(age); } public void setAge(long age) { this.age = age; }
22. Vilket är det sista nyckelordet och var används det?
Nyckelordetfinal
kan användas på variabler, metoder och klasser.
- Värdet på en slutlig variabel kan inte ändras efter att den har initierats.
- En sista klass är steril :) Den kan inte få barn.
- En slutlig metod kan inte åsidosättas av en ättling.
Slutliga variabler
Java ger oss två sätt att deklarera en variabel och tilldela den ett värde:- Du kan deklarera en variabel och initiera den senare.
- Du kan deklarera en variabel och tilldela ett värde direkt.
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 en slutlig variabel betraktas som en konstant?
Eftersom vi inte kan tilldela nya värden till slutvariabler verkar det som att dessa är konstanta variabler. Men bara vid första anblicken: Om variabelns datatyp är ,immutable
så är det en konstant. Men om datatypen är mutable
, det vill säga ändringsbar, kommer det att vara möjligt att använda metoder och variabler för att ändra värdet på objektet som refereras till av en final
variabel. På grund av detta kan det inte kallas en konstant. Följande exempel visar att vissa slutvariabler verkligen är konstanter, medan andra inte är det, eftersom de kan ändras.
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?");
}
Lokala slutvariabler
När enfinal
variabel skapas inom en metod kallas den för en local final
variabel:
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);
}
}
}
Vi kan använda det sista nyckelordet i en förbättrad for loop, eftersom en ny variabel skapas efter varje iteration av loopen. Tänk på att detta inte gäller en normal for loop, så vi kommer att få ett kompileringsfel.
// The final local j variable cannot be assigned
for (final int i = 0; i < args.length; i ++) {
System.out.println(args[i]);
}
Avslutande klass
En klass som deklareras somfinal
kan inte förlängas. Enkelt uttryckt kan ingen annan klass ärva det. Ett utmärkt exempel på en final
klass i JDK är String. Det första steget för att skapa en oföränderlig klass är att markera den som , och final
på så sätt förhindra att den utökas:
public final class FinalExample {
}
// Compilation error!
class WantsToInheritFinalClass extends FinalExample {
}
Slutliga metoder
När en metod markeras som slutgiltig kallas den för en slutlig metod (är vettigt, eller hur?). En sista metod kan inte åsidosättas i en barnklass. För övrigt är Object-klassens wait() och notify() metoder slutgiltiga, så vi har inte möjlighet att åsidosätta dem.
public class FinalExample {
public final String generateAddress() {
return "Some address";
}
}
class ChildOfFinalExample extends FinalExample {
// Compilation error!
@Override
public String generateAddress() {
return "My OWN Address";
}
}
Hur och var man använder final i Java
- Använd det sista nyckelordet för att definiera några konstanter på klassnivå;
- Skapa slutliga variabler för objekt som du inte vill ska ändras. Till exempel objektspecifika egenskaper som vi kan använda för loggningsändamål.
- Om du inte vill att en klass ska förlängas, markera den som slutgiltig.
- Om du behöver skapa en oföränderlig klass måste du göra den slutgiltig.
- Om du vill att en metods implementering inte ska ändras i dess avkomlingar markerar du metoden som
final
. Detta är mycket viktigt för att vara säker på att implementeringen inte förändras.
23. Vad är föränderliga och oföränderliga typer?
Föränderlig
Föränderliga objekt är objekt vars tillstånd och variabler kan ändras efter att de skapats. Exempel på föränderliga klasser inkluderar StringBuilder och StringBuffer. Exempel:
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());
}
}
Oföränderlig
Oföränderliga objekt är objekt vars tillstånd och variabler inte kan ändras efter att objektet har skapats. En bra nyckel för en HashMap, tycker du inte? :) Till exempel String, Integer, Double, och så vidare. Exempel:
// 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());
}
}
I nästa del tar vi upp frågor och svar om samlingar. Min profil på GitHub Topp 50 jobbintervjufrågor och svar för Java Core. Del 2
GO TO FULL VERSION