Object-Oriented Thinking Object oriented programming was developed to manage complexity. This is done by modelling only the essential aspects of a problem. The closer your program models the problem you are trying to solve, the easier it is to understand the program (and hence write and maintain it). The term problem domain refers to all the essential information about the problem. A well designed object-oriented program will be filled with objects from the problem domain. At the first level of design you will think about how these objects interact, and what their state and capabilities are. An objects state is the values it holds. Its capabilities are what it can be told to do. Humans create models to manage complexity and to help in understanding the problems to be solved. A map is an example of a model that can be used to solve the problem of finding your way from Paisley to Barrhead. To be useful a model should be a simplification. To get from Paisley to Barrhead you don't need details of individual houses or factories, all that is in the problem domain is details of the road system. A model must be faithful to those aspects of the problem domain that are relevant, eg our map should represent distances accurately. A good object-oriented design is an accurate model of the problem you are trying to solve, this allows you to examine the relevant details of the problem without confusion. Classes and Objects The most important "buzzwords" in object-oriented programming are the class and the object: A class defines a new type of thing. The class defines the common characteristics of every object of that new type. An object is an individual instance of a class. An object is just a thing. Look around you and you will see things: pen, pencil, paper, table, seat, etc. The pen is an instance of a more general type of thing, ie a type of implement for writing on paper. The pencil is also an instance of this general type. The theory behind object-oriented programming is that for computer programs to accurately model the world, the programs should reflect the human tendency to think about individual things and types of things. In Java you do that by creating a class to define a type and creating an object to model a thing. When you define a class you describe the characteristics and behaviour of objects of that type. In Java you describe the characteristics with instance variables. These are used to hold each object's state, eg the state of an Employee object might be defined by (among other things) their current salary, management level, and performance rating. You define the behaviour of your class with methods; these contain code to perform an action. An Employee class might contain methods for adjusting salary, submitting annual reviews, and evaluating performance objectives. Methods can manipulate the state of the object by changing the values in instance variables, or a method could interact with other objects of its own type or with objects of other types. This interaction among objects is crucial to object-oriented programming. Designing a good object-oriented program is not unlike forming a good team; you look for players (or objects, in the case of a program) with different skills to whom you can assign the various tasks you must accomplish. Those players cooperate with each another to get the job done. In a good object-oriented program, you will design objects that represent things in your problem domain. You will then divide the work of the program among your objects, assigning responsibilities to objects based on their ability. Class Relationships The heart of object-oriented design is establishing relationships among the classes. Classes interact and relate to each other in various ways. The simplest interaction is when a method in one class is used to call a method in a second class, eg the Manager class might have a method that calls the UpdateSalary method on an object of type Employee. We can then say that the Manager class and the Employee class are associated. Association among classes simply means they interact. Some complicated types are composed of other types, eg a car might be composed of wheels, engine, gearbox, etc. You might model this by creating a Wheel class, an Engine class, and a Gearbox class. You could then create a Car class, and each car would have four instances of the Wheel class, and one instance each of the Engine and Gearbox classes. Another way to view this relationship is to say that the Car class aggregates the Wheel, Engine, and Gearbox classes. Aggregation is a has-a relationship. This process of aggregation allows you to build very complex classes from relatively simple classes. The Java API provides a String class to handle text strings. You might create your own Address class out of five text strings (address line 1, address line 2, town/city, county, postcode). You might then create a second class, Employee, which has as one of its members an instance of Address. Encapsulation The idea behind encapsulation is that you want to keep each class discreet and self-contained. This allows you to change the implementation of one class without affecting any other class. A class that provides a method that other classes can use is called a server. A class that uses that method is called a client. The goal of encapsulation is that you can change the details of how a server does its work without breaking anything in the implementation of the client. This is accomplished by drawing a line in the sand between the public interface of a class and its private implementation. The public interface is a contract issued by your class that says, I promise to be able to do this work. Specifically, a public interface says call this method with these parameters and I'll do this work and return this value. A client can rely on a public interface not to change. If the public interface does change, then the client must be recompiled and perhaps redesigned. The private implementation, on the other hand, is private to the server. The designer of the server class is free to change how it does the work promised in the public interface, so long as it continues to fulfil the terms of its implicit contract: it must take the given parameters, do the proposed work and return the promised value. Specialization Specialization is implemented in Java by declaring that a new class derives from an existing class. When you do so, the specialized class inherits the characteristics of the more general class. The specialized class is called a derived class (or subclass), while the more general class is known as a base class (or superclass). The specialization relationship is referred to as the is-a relationship. A Car is a Vehicle (Car would be derived from the base class Vehicle). Specialization allows you to create a family of objects. These characteristics and behaviours of the superclass are inherited by all of its subclasses. This allows for a very powerful form of reuse. Rather than cutting and pasting code from one class to another, the shared variables and methods are inherited by the subclass class. If you change how a shared behaviour is implemented, you do not have to update code in every derived type; they inherit the changes. For example, A Manager is a special type of Employee. The Manager adds new capabilities (hiring, firing, rewarding, praising) and new state (annual objectives, management level, etc). The Manager, however, also inherits the characteristics and capabilities common to all Employees. Thus a Manager has an address, a name, an Employee ID, and Managers can be given raises, can be laid off, and so on. Specialization allows you to establish hierarchical relationships among your classes, eg a Manager can be a specialized type of an Employee, which in turn can be a specialized type of Person, and so on. Polymorphism Polymorphism refers to the ability of a single class to take on many forms. The essence of polymorphism is this: at times you will know you have a collection of a general type, eg a collection of Vehicles. You do not know (or care) what the specific subtype each of your vehicles is (one may be a car, another a truck, etc). The important thing is that you know they all inherit shared abilities (eg the turn-left method) and that you can treat them all as vehicles. If you write a program instruction that tells each control to turn-left, this is implemented properly on a per-vehicle basis. You do not need to know how each derived class accomplishes this; you only need to know that each class is defined to be able to turnleft. Polymorphism allows you to treat a collection of disparate derived classes (cars, trucks, vans) as a group. You treat the general group of objects in the same way, and each individual object does the right thing according to its specific type. Defining Classes Object behaviour is created by writing methods. A method is a small routine that every object of the class can execute, eg an Array class might have a sort method. Object state is maintained by variables. Variables can be primitive types (eg an int to hold the age of a person), or variables can be objects of other classes (eg an Employee class may have a field of type Address). To define a new class, you first declare it and then define its methods and variables In Java everything happens within a class; no method can run outside a class. Methods The name "method" is part of object-oriented terminology. Methods replace functions and procedures from non-object-oriented languages. Methods may or may not return a value, and the may or may not have one or more parameters. To make your methods as flexible as possible, you can define parameters: information passed into the method when the method is called. Thus, rather than having to write one method when you want to sort your ListBox from A-Z and a second to sort it Z-A, you define a more general sort() method and pass in a parameter specifying the order of the sort. Methods can take any number of parameters. When a method is defined, the parameter list follows the method name and is encased in parentheses. Each parameter's type is identified before the name of the parameter. For example, the following declaration defines a method named MyMethod() which takes two parameters, an integer and a double: public void MyMethod(int firstParam, double secondParam) { ' ' ' } The parameters act as local variables, as if you had declared them in the body of the method and initialised them with the values passed in. All parameters in Java are passed by value. Pass by value means that a local copy is made of the value and assigned to the parameter inside the method. It is this local copy that is used inside the method. When the parameter is of a primitive data type, any change to the value of that parameter within the method, does not change anything in the calling method. It is a little more complicated when the parameter is an object. In this case what is passed as the parameter is a reference to the object. A local copy is made of this reference but this will refer to the same object as the original reference. Thus any changes to the object's state made are made on the original object. Using Static Members The variables and methods of a class can either be instance members or static members. Instance members are associated with instances of the type, while static members are associated with the class, and not with any particular instance. Methods are instance methods unless explicitly marked with the keyword Static. The majority of methods will be instance methods. The semantics of an instance method are that you are taking an action on a specific object. However it is sometimes convenient to be able to invoke a method without having an instance of the class, and for that you use a static method. You normally access a static member through the name of the class in which it is declared. This allows you to access the static member without having an instance of the class. A common use of static variables is to keep track of the number of instances (objects) that currently exist for your class. It is also legal to access static members using an object name but the compiler will substitute the class of the object at compile time. Although it is normal to use the class name when referring to static members the SCJP exam may well try to confuse you by sometimes using an object name. Constructors Consider this statement that creates an Employee object, it looks as though it is calling a method: Employee barney = new Employee() Actually, a method is called whenever you instantiate an object. This method is called a constructor. Each time you define a class, you are free to define your own constructor, but if you don't, the compiler will provide one for you invisibly and automatically. The job of the constructor is to create the object specified by a class and to put it into a valid state. Destroying Objects You do not normally need to worry about cleaning up after your objects, they are automatically destroyed when you are done with them (ie the memory they use is freed-up and so can be reused). Java has a "garbage collector" which does this for you. Thus you do not need "Destructor" methods.