Administrativ
Laboratoare
Tema
Teste
Resurse utile
Alte resurse
Arhiva Teme
Administrativ
Laboratoare
Tema
Teste
Resurse utile
Alte resurse
Arhiva Teme
This is an old revision of the document!
Au fost 20 de întrebări, 4 variante de răspuns, un singur răspuns corect. 4 numere, aceleași 20 de întrebări în ordine diferită.
Metoda de evaluare: grilă franceză, -1/4 din punctajul unei întrebări la răspuns greșit, 0 dacă nu este marcat niciun răspuns.
Analizăm aici întrebările în ordinea variantei A a testului.
Fie interfața Runnable
cu singura metodă public void run()
. Clasa Thread
are un constructor ce primește un Runnable
ca parametru și expune o metodă public void start()
. Ce concluzie trageți de la următorul cod?
new Thread(new Runnable() { public void run() { while(true) { System.out.println("Nyan cat!"); } } } ).start();
public void run()
nu poate fi implementat “pe loc”new Runnable()
este incorect sintactic, Runnable
este o interfațănew Thread(…).start()
este incorect sintactic
Explicație: scenariul din întrebare este un exemplu clasic de instanțiere a unei clase anonime (e un scenariu real, Thread
și Runnable
stau la baza multithreading-ului în Java). Expresia new Runnable()
scrisă fără implementarea care ar urma ar fi într-adevăr o eroare de compilare (nu se pot instanția tipuri abstracte). Dar, ca și în explicația de la laboratorul de clase anonime, new Runnable() { public void run() {…} }
spune de fapt compilatorului să creeze o nouă clasă care implementează Runnable
și oferă implementarea cu Nyan Cat (lol), după care și creează un obiect de tipul respectiv. Cât despre new Thread(…).start()
, sintaxa e corectă.
Ce obținem la rularea următorului cod:
class A { public static boolean val; public A() { val = true; } public static void print() { System.out.println("First"); } } public class Main { public static void main(String[] args) { A a = null; if (!a.val) a.print(); else System.out.println("Second"); } }
Explicație: Programul e corect sintactic, deci eroarea de compilare cade. Ideea principală este că se pot accesa membrii statici pentru obiectele a căror valoare e null
. De ce funcționează: runtime-ul nu “dereferențiază” efectiv obiectul null
, ci accesează membrii clasei obiectului, pe baza tipului declarat. De aceea, nu se aruncă excepție și se printează “First”.
Ce cuvânt cheie introdus la (xxx) va permite compilarea programului:
abstract class A { int x; abstract void set(); } public class Test { public static void main(String args[]) { (xxx) int num = 30; A a = new A() { public void set() { x = num; } }; } }
Explicație: Pentru a folosi variabile locale din afara contextului (scope-ului) unei clase interne (cum e și cazul nostru), e necesar cuvântul cheie final
. Motivul este layout-ul în memorie al claselor și variabilelor, iar final
permite (prin copiere) și garantează accesul la valoarea corectă a variabliei x
la orice moment. static
și public
nu au oricum sens pentru variabile locale, iar diferențierea dintre “nu e nevoie de modificator” și “final” este motivul precedent.
Ce se întâmplă la rularea următorului cod:
String[] strings = {"hello", "world"}; Object[] objects = strings; // linia 2 List<?> list = new ArrayList<Object>(); // linia 3 for (Object obj : objects) { System.out.println(obj); list.add(obj); // linia 6 }
Explicație: Linia 2 e corectă sintactic, deși dacă apoi încercăm ceva de tipul objects[1] = 1
vom obține un ArrayStoreException la runtime. Linia 3 e perfect în regulă, pentru că ArrayList
implementează List
și wildcard-ul se potrivește cu Object
. Ce nu putem face e să apelăm metoda add
pe variabila list
, pentru că la compilare nu se cunoaște tipul obiectelor conținute în listă. Doar null
poate fi folosit ca parametru, pentru că null
aparține oricărui tip referință.
Despre excepțiile unchecked:
Explicație: Excepțiile unchecked derivă din RuntimeException
. Ca orice alt tip referință, se pot crea noi excepții unchecked după cum simțim nevoia, deci primul răspuns cade. Specificația lor include răspunsul de mai sus în bold, care este și cel corect. Excepțiile unchecked pot, dar nu trebuie neapărat să fie declarate în clauzele throws
și pot, dar nu trebuie neapărat să fie prinse de blocurile try-catch.
Ce afișează următorul cod:
class Shape { public String type = " s "; public Shape() { System.out.print("shape ");} } public class Rectangle extends Shape { public Rectangle() { System.out.print("rectangle ");} public static void main(String[] args) { new Rectangle().go(); } void go() { type = "r "; System.out.print(this.type + super.type); } }
Explicație: Expresia new Rectangle()
cheamă întâi super-constructorul, anume Shape()
, care printează “shape”, după care se execută instrucțiunile din constructorul propriu al Rectangle
, care printează “rectangle”. Apoi pe instanța de Rectangle se cheamă metoda go
din clasa Rectangle (ar trebui să fie clar), care setează type=“r”
. Rectangle nu are un câmp type
propriu, deci this.type
și super.type
vor avea aceeași valoare “r”. Deducem deci că se va afișa “shape rectangle r r”.
Care afirmații sunt corecte? A) Statement-ul package
poate fi pus doar pe prima linie necomentată dintr-un fișier B) Specicatorii de acces pentru clase externe sunt doar public și default C) Un fișier poate avea mai multe clase publice D) Este obligatoriu pentru un fișier sa conțină statement-ul package E) Un fișier poate avea mai multe statement-uri import
Explicație: să luăm afirmațiile pe rând: A) declararea pachetului poate fi doar prima linie necomentată de cod, fără dubii, deci corect B) specificatorii pentru clase externe sunt doar public și default (adică fără modificator); ceilalți nu au sens decât în contextul unei alte clase (private, protected față de cine?) - corect C) un fișier poate avea o singură clasă publică - greșit D) dacă un fișier se află într-un pachet default, nu trebuie să aibă statement-ul package - greșit E) un fișier poate avea, desigur, mai multe import-uri (și este deseori cazul) - corect
Fie:
interface ITest { protected int x = 10; int y; int z = 20; abstract void foo(); final int f(int x); }
Care linii din corpul interfeței (numerotate de la 1 la 5) sunt corecte?
Explicație: Esențial este faptul că orice variabilă în corpul unei interfețe este automat (dedus) public static final
, iar metodele sunt implicit abstract
. Înarmați cu aceste cunoștințe, să luăm acum fiecare linie pe rând:
protected
și public
(dedus) intră în contradicție - greșity
e final (implicit) și nu e inițializat - greșitabstract
este oricum dedus - corectabstract
; final + abstract = compiler headbang. Greșit.
Care variantă reprezintă suprascrierea corectă a metodei: protected int computeX(int a, float b) {…}
?
int computeX(int a, float b) {…}
public int computeX(int a, float b) {…}
protected int computeX(Integer a, Float b) {…}
protected Integer computeX(int a, float b) {…}
Explicație: suprascrierea este corectă dacă se păstrează aceeași semnătură (tip de întors, nume + parametri, deci variantele 3 și 4 sunt greșite), iar modificatorul de acces este cel puțin protected. Modificatorul default poate sau nu să fie mai vizibil decât protected (exercițiu - gândiți-vă la scenarii), fapt pentru care compilatorul interzice prima variantă ca fiind suprascriere (i.e. dacă adăugați adnotarea @Override
veți primi eroare).
Ce afișează următorul program?
class Main { public static void main(String[] args) { Boolean b = new Boolean(true); if (b.equals(true)) { System.out.println("1"); b = false; if (b == new Boolean(false)) { System.out.println("2"); } } } }
Explicație: codul compilează; singura potențială problemă ar fi atribuirea unei valori boolean
(tip primitiv) unei variabile Boolean
(tip referință), dar știm că Java face autoboxing (adică se creează un nou Boolean cu false
în interiorul lui). Esența întrebării este diferența dintre == și metoda equals
. În mod clar b.equals(true)
va fi adevărat, deci se va printa 1. Apoi, pentru că operatorul == compară referințele (adică verifică dacă cei doi operanzi sunt de fapt același obiect), b == new Boolean(false)
va fi false
, deci nu se va printa și 2.
Care cuvânt dintre următoarele este rezervat în Java?
Explicație: Cuvintele “method” și “array” pică imediat. “Unsigned” nu există în Java, iar “native” denumește metodele pe care le puteți scrie pentru a interfața cu programele scrise special pentru sistemul vostru (arhitectura de calcul, sistem de operare etc). Consultați laboratorul de Reflection. Nu aveați nevoie de informații detaliate despre ce face Java Native Interface, ci doar că există astfel de metode.
Dacă dorim să stocăm un șir de elemente fără duplicate într-o colecție fără să ne intereseze ordinea elementelor sau sortarea lor, clasa cea mai potrivită este
Explicație: Când auziți “fără duplicate”, primul impuls trebuie să fie o implementare de Set
, deci deja Vector
cade, iar HashSet
este aproape sigur. Să ne uităm totuși și la celelalte variante. TreeMap
ține minte chei comparabile, deci neinteresându-ne de ordinea elementelor, pică. HashMap
poate fi de asemenea folosit ca și HashSet
cu obiectele noastre ca și chei, dar pentru a-l folosi corect, trebuie să stocăm și valori asociate cheilor, cu toate că nu ne interesează efectiv. HashSet
e deci varianta optimă.
Care afirmații sunt corecte? (Ci denotă clase, Ii denotă interfețe)
A) C1 extends I1; B) I1 extends I2, I3; C) I1 implements I2; D) C1 implements I1,I2; E) C1 extends C2, C3.
Explicație: Este o întrebare standard despre fundamentele limbajului Java. Esențial este că o clasă poate extinde o singură altă clasă și poate implementa oricâte interfețe. Afirmația E este deci greșită, deci pică din start 2 variante. Afirmațiile A și C iarăși nu au sens, o clasă extinde altă clasă și o interfață extinde altă interfață. Afirmația D este corectă și apare des în viața reală. Ce este potențial tricky la această întrebare este afirmația B, care este corectă. De ce este corectă: în virtutea implementării mai multor interfețe, compilatorul este doar interesat de adunat semnături de metode în cadrul interfețelor. Vă puteți pune întrebarea ce se întâmplă dacă I1 și I2 conțin aceeași semnătură de metodă și pe care dintre ele o moștenește I. Un răspuns (cu niște simplificări) e că nu contează; ceea ce contează este ca acea semnătură de metodă să se afle în I. Deci faptul că o interfață extinde mai multe alte intefețe cu semnături de metode potențial identice nu deranjează compilatorul, pentru că nu există și implementări diferite care să declanșeze eventuale ambiguități.
Cu ce poate fi înlocuită linia (xxx) pentru a obține o instanță a B?
class A { class B {} } // in main A a = new A(); (xxx)
B b = new B();
A.B b = new B();
A.B b = new A().new B();
A.B b = new a.B();
Explicație: Întrebare standard despre sintaxa Java la instanțierea unei clase interne nestatice. Corect este (obiect de tip A).new B()
, deci singura variantă care respectă această sintaxă este cea din bold.