Você está na página 1de 10

Java OO Interview Questions and Answers

If you asked me to pick a section that is most


popular with the interviewers, this is it. If
you don't perform well in Object Oriented
(i.e. OO) programming , your success rate in
interviews will be very low. Good
interviewers will be getting you to analyze
or code for a particular scenario. They will
be observing your decisions with interfaces
and classes, and question your decisions to
ascertain your technical skills, analytical
skills, and communication skills. You can't
memorize your answers. This section
requires some level of experience to fully
understand.

In this blog, I cover some OO interview


questions and answers. If you are interested
in more questions and answers, the "Core
Java Career Essentials" book has a whole
chapter dedicated for OO questions and
answers with enough examples to get
through your OO interview questions with
flying colors.

Q. How do you know that your classes are badly designed?


A.

 If your application is fragile – when making a change, unexpected parts of the


application can break.
 If your application is rigid – it is hard to change one part of the application
without affecting too many other parts.
 If your application is immobile – it is hard to reuse the code in another application
because it cannot be separated.

Overly complex design is as bad as no design at all. Get the granularity of your classes
and objects right without overly complicating them. Don't apply too many patterns and
principles to a simple problem. Apply them only when they are adequate. Don't anticipate
changes in requirements ahead of time. Preparing for future changes can easily lead to
overly complex designs. Focus on writing code that is not only easy to understand, but
also flexible enough so that it is easy to change if the requirements change.
Q. Can you explain if the following classes are badly designed?
The following snippets design the classes & interfaces for the following scenario. Bob,
and Jane work for a restaurant. Bob works as manager and a waiter. Jane works as a
waitress. A waiter's behavior is to take customer orders and a manager's behavior is to
manage employees.

view plainprint?
1. package badrestaurant;
2.
3. public interface Person {}

view plainprint?
1. package badrestaurant;
2.
3. public interface Manager extends Person {
4. public void managePeople( );
5. }

view plainprint?
1. package badrestaurant;
2.
3. public interface Waiter extends Person {
4. public void takeOrders( );
5. }

view plainprint?
1. package badrestaurant;
2.
3. public class Bob implements Manager, Waiter {
4.
5. @Override
6. public void managePeople( ) {
7. //implementation goes here
8. }
9.
10. @Override
11. public void takeOrders( ) {
12. //implementation goes here
13. }
14. }
view plainprint?
1. package badrestaurant;
2.
3. public class Jane implements Waiter {
4.
5. @Override
6. public List<string> takeOrders( ) {
7. //implementation goes here
8. }
9. }

The Restaurant class uses the above classes as shown below.

view plainprint?
1. package badrestaurant;
2.
3. public class Restaurant {
4.
5. public static void main(String[ ] args) {
6.
7. Bob bob = new Bob( );
8. bob.managePeople( );
9. bob.takeOrders( );
10.
11. Jane jane = new Jane( );
12. jane.takeOrders( );
13. }
14. }

A. The above classes are badly designed for the reasons described below.

The name should be an attribute, and not a class like Bob or Jane. A good OO design
should hide non-essential details through abstraction. If the restaurant employs more
persons, you don't want the system to be inflexible and create new classes like Peter,
Jason, etc for every new employee.

The above solution's incorrect usage of the interfaces for the job roles like Waiter,
Manager, etc will make your classes very rigid and tightly coupled by requiring static
structural changes. What if Bob becomes a full-time manager? You will have to remove
the interface Waiter from the class Bob. What if Jane becomes a manager? You will have
to change the interface Waiter with Manager.

The above drawbacks in the design can be fixed as shown below by asking the right
questions. Basically waiter, manager, etc are roles an employee plays. You can abstract it
out as shown below.

view plainprint?
1. package goodrestuarant;
2.
3. public interface Role {
4. public String getName( );
5. public void perform( );
6. }

view plainprint?
1. package goodrestuarant;
2.
3. public class Waiter implements Role {
4.
5. private String roleName;
6.
7. public Waiter(String roleName) {
8. this.roleName = roleName;
9. }
10.
11. @Override
12. public String getName( ) {
13. return this.roleName;
14. }
15.
16. @Override
17. public void perform( ) {
18. //implementation goes here
19. }
20. }

view plainprint?
1. package goodrestuarant;
2.
3. public class Manager implements Role {
4.
5. private String roleName;
6.
7. public Manager(String roleName) {
8. this.roleName = roleName;
9. }
10.
11. @Override
12. public String getName( ) {
13. return this.roleName;
14. }
15.
16. @Override
17. public void perform( ) {
18. //implementation goes here
19. }
20. }

The Employee class defines the employee name as an attribute as opposed to a class. This
makes the design flexible as new employees can be added at run time by instantiating
new Employee objects with appropriate names. This is the power of abstraction. You
don't have to create new classes for each new employee. The roles are declared as a list
using aggregation (i.e. containment), so that new roles can be added or existing roles can
be removed at run time as the roles of employees change. This makes the design more
flexible.

view plainprint?
1. package goodrestuarant;
2.
3. import java.util.ArrayList;
4. import java.util.List;
5.
6. public class Employee {
7.
8. private String name;
9. private List<role> roles = new ArrayList<role>(10);
10.
11. public Employee(String name){
12. this.name = name;
13. }
14.
15. public String getName( ) {
16. return name;
17. }
18.
19. public void setName(String name) {
20. this.name = name;
21. }
22.
23. public List<role> getRoles( ) {
24. return roles;
25. }
26.
27. public void setRoles(List<role> roles) {
28. this.roles = roles;
29. }
30.
31. public void addRole(Role role){
32. if(role == null){
33. throw new IllegalArgumentException("Role cannot be null");
34. }
35. roles.add(role);
36. }
37.
38. public void removeRole(Role role){
39. if(role == null){
40. throw new IllegalArgumentException("Role cannot be null");
41. }
42. roles.remove(role);
43. }
44. }

The following Restaurant class shows how flexible, extensible, and maintainable the
above design is.

view plainprint?
1. package goodrestuarant;
2.
3. import java.util.List;
4.
5. public class Restaurant {
6.
7. public static void main(String[ ] args) {
8.
9. Employee emp1 = new Employee ("Bob");
10. Role waiter = new Waiter("waiter");
11. Role manager = new Manager("manager");
12.
13. emp1.addRole(waiter);
14. emp1.addRole(manager);
15.
16. Employee emp2 = new Employee("Jane");
17. emp2.addRole(waiter);
18.
19. List<role> roles = emp1.getRoles( );
20. for (Role role : roles) {
21. role.perform( );
22. }
23.
24. //you can add more employees or change roles based on
25. //conditions here at runtime. More flexible.
26. }
27. }

Q. What do you achieve through good class and interface


design?
A.

 Loosely coupled classes, objects, and components


enabling your application to easily grow and adapt to
changes without being rigid or fragile.
 Less complex and reusable code that increases
maintainability, extendability and testability.

Q. What are the 3 main concepts of OOP?


A. Encapsulation, polymorphism, and inheritance are the 3
main concepts or pillars of an object oriented programming.
Abstraction is another important concept that can be applied to
both object oriented and non object oriented programming.
[Remember: a pie ? abstraction, polymorphism, inheritance,
and encapsulation.]

Q. What problem(s) does abstraction and encapsulation solve?


A. Both abstraction and encapsulation solve same problem of
complexity in different dimensions. Encapsulation exposes
only the required details of an object to the caller by
forbidding access to certain members, whereas an abstraction
not only hides the implementation details, but also provides a
basis for your application to grow and change over a period of
time. For example, if you abstract out the make and model of a
vehicle as class attributes as opposed to as individual classes
like Toyota, ToyotaCamry, ToyotaCorolla, etc, you can easily
incorporate new types of cars at runtime by creating a new car
object with the relevant make and model as arguments as
opposed to having to declare a new set of classes.

Q. How would you go about designing a “farm animals”


application where animals like cow, pig, horse, etc move from
a barn to pasture, a stable to paddock, etc? The solution should
also cater for extension into other types of animals like circus
animals, wild animals, etc in the future.
A.

view plainprint?
1. package subclass0;
2.
3. public abstract class Animal {
4. private int id; // id is encapsulated
5.
6. public Animal(int id) {
7. this.id = id;
8. }
9.
10. public int getId( ) {
11. return id;
12. }
13.
14. public abstract void move(Location location);
15. }

view plainprint?
1. package subclass0;
2.
3. public class FarmAnimal extends Animal {
4.
5. private Location location = null; // location is encapsulated
6.
7. public FarmAnimal(int id, Location defaultLocation) {
8. super(id);
9. validateLocation(defaultLocation);
10. this.location = defaultLocation;
11. }
12.
13. public Location getLocation( ) {
14. return location;
15. }
16.
17. public void move(Location location) {
18. validateLocation(location);
19. System.out.println("Id=" + getId( ) + " is moving from "
20. + this.location + " to " + location);
21. this.location = location;
22. }
23.
24. private void validateLocation(Location location) {
25. if (location == null) {
26. throw new IllegalArgumentException("location=" + location);
27. }
28. }
29. }

view plainprint?
1. package subclass0;
2.
3. public enum Location {
4. Barn, Pasture, Stable, Cage, PigSty, Paddock, Pen
5. }

view plainprint?
1. package subclass0;
2.
3. public class Example {
4.
5. public static void main(String[ ] args) {
6. Animal pig = new FarmAnimal(1, Location.Barn);
7. Animal horse = new FarmAnimal(2, Location.Stable);
8. Animal cow = new FarmAnimal(3, Location.Pen);
9.
10. pig.move(Location.Paddock);
11. horse.move(Location.Pen);
12. cow.move(Location.Pasture);
13. }
14. }

Output:

Id=1 is moving from Barn to Paddock


Id=2 is moving from Stable to Pen
Id=3 is moving from Pen to Pasture

In the above example, the class FarmAnimal is an abstraction used in place of an actual
farm animal like horse, pig, cow, etc. In future, you can have WildAnimal, CircusAnimal,
etc extending the Animal class to provide an abstraction for wild animals like zebra,
giraffe, etc and circus animals like lion, tiger, elephant, etc respectively. An Animal is a
further abstraction generalizing FarmAnimal, WildAnimal, and CircusAnimal. The
Location is coded as an enumeration for simplicity. The Location itself can be an abstract
class or an interface providing an abstraction for OpenLocation, EnclosedLocation, and
SecuredLocation further abstracting specific location details like barn, pen, pasture,
pigsty, stable, cage, etc. The location details can be represented with attributes like
“name”, “type”, etc.

The FarmAnimal class is also well encapsulated by declaring the attribute “location” as
private. Hence the “location” variable cannot be directly accessed. Assignment is only
allowed through the constructor and move(Location location) method, only after a
successful precondition check with the validateLocation(...) method. The
validateLocation(...) itself marked private as it is an internal detail that does not have to
be exposed to the caller. In practice, the public move(..) method can make use of many
other private methods that are hidden from the caller. The caller only needs to know what
can be done with an Animal. For example, they can be moved from one location to
another. The internal details as to how the animals are moved is not exposed to the caller.
These implementation details are specific to FarmAnimal, WildAnimal, and
CircusAnimal classes.

The above code does not satisfy the following questions.

1) Why does the Employee class need to be mutable?


2) Why aren't the roles defensive copied?
3) Why would the Employee need to know how to add and remove roles?
4) Waiter and Manager are placed in a collection but don't override hashcode and equals.
That will cause the contains method on a List to not behave as expected.
5) You check if the role is null then throw an IllegalArgumentException, that should
instead be a NullPointerException.
6) The code that checks for null roles being added is duplicated, thus defeating the DRY
principle.

Some of the above questions are answered in How to write immutable Java classes?

Você também pode gostar