Skip to main content

An Introduction to Java programming

Posted by hellofadude on October 16, 2013 at 8:59 AM PDT

Each of these objects can be described in terms of the classes or types to which they belong for instance a maple tree belongs to the class of tree or is a type of tree, a bicycle is a type of cycle, a car is a type of vehicle. OOP is thus a very powerful language abstraction that allows you to simplify the complexity of a problem while retaining its value. Java is an Object Oriented Programming language that allows you to define abstract classes or data types that encapsulate succinctly the complexities inherent in the problem space. For the moment, it is enough not to concern yourself with how it does this, just be content to know that it does.

Everything is an object

In Java, the term 'everything is an object' is to be taken quite literally. You may conceive of an object in Java as having attributes or data fields which constitute its state and providing operations or methods which describe its behaviour or functionality.
We speak of objects as belonging to a type or class. A class describes a set of objects that have identical characteristics (attributes) and behaviour (functionality). A class in Java is really a data type that can be defined to fit the problem you are trying to solve.

The advantage of this kind of abstraction is that it allows us to have what we call base types or root objects to which all other derived types share some commonality. For instance, consider a Shape class that has attributes like colour and position and a method like draw(). A circle object can be derived from the shape class that will provide the same interface as the base class. You define a class in Java by specifying the class keyword, like this:-

   class Shape { ... }

The requests an object can accept are defined by its interface, and its type or class is what determines the composition of this interface. In the shape example, any object that is derived from shape must also provide draw() method.

In terms of design, it is helpful to think of objects as providing some kind of service to the user. The user may be the programmer or other objects. Thinking of an object as a service provider provides the additional benefit of improving cohesion.
All of these concepts can be implemented by some means in Java by writing some code. This comprises the implementation.

Hiding implementation

Implementation hiding is an important part of convention in software programming. The reason for this is to allow changes to be made to a library with minimum effort as well as to provide some access control.

Java uses access specifiers to set the boundaries within a class. These include the keywords public, private and protected. These keywords determine the type of access for the definitions they precede. The public keyword provides access to the element following it to everyone, everytime. The private keyword restricts access to the element it precedes to everyone except the creator of the type, within methods of that type. The protected keyword restricts access to everyone except inheriting classes or types.

Java also has a default access which occurs when none of the above specifiers is used. In this case, the member defaults to package access which means only members within the same package (library component) can access the class.

Composition: reusing implementation

One of the biggest advantages of OOP is that it provides a framework for producing highly reusable code. This reasoning can easily be gleaned from the logic inherent in treating elements in the problem space as objects, belonging to a class or type and a root object as having multiple types; in this way, the many is comprised of at least the one, making reuse almost inevitable.

Java allows you to extend the functionality of its libraries by reusing and customising its classes. The easiest way to accomplish this is to create an object of the class directly. It is also possible to place an object of a class within a new class. A class can be made up of any number of objects in any combination necessary to achieve your desired functionality.

When you use objects in this way it is called composition because you are effectively composing a new class from existing classes. Composition is often referred to as a "has-a" relationship. When composition takes place dynamically, this is referred to as aggregation. For example to use an object of the Icecream class in your class, you would do something like this:-

  Icecream sundae = new Icecream();

Composition offers a great deal of flexibility to the library designer who wants to be in a position to make changes without disturbing existing client code. When you use composition to create member objects of a new class, they are typically declared private, making them inaccessible to the client programmer, in this way changes can be made with little effort.

Inheritance: extending functionality

Inheritance is another way of extending the functionality of classes in Java. The basic idea is to take an existing class, clone it and then make additions or modifications to the clone. Inheritance expresses the similarity between classes in terms of base types and derived types. The existing class is referred to as the base class or type while the clone or modified class is referred to as the derived class. A base type contains all of the characteristics and behaviour that is shared among types that may be derived from it. By inheriting from a base class you create a new type that contains all of the members of the existing type and more specifically, provides the same interface.

For instance, a vehicle has certain characteristics like the number of wheels it has or the number of passengers it can carry. It may also have behaviours or methods like drive, park, reverse etc.. This could represent the base class. A car class could inherit or be derived from the vehicle class, in such a way that makes all the characteristics and behaviour of the vehicle class available to the car class. You could also further extend the car class by providing additional methods like steer. A train class could also inherit from the vehicle class thus having some commonality with the car class. To inherit in Java, you use the extends keyword like this:-

public class Car extends Vehicle { ... }

There are two ways to differentiate a derived class from the original base class. One way would be to add new methods or behaviour to the derived class that do not already exist in the base class like we described in the car class. When you do this the relationship between the base class and the derived class can be described as the derived class being-like the base class.

Another way is to modify the behaviour of existing methods in the base class in the derived class. This is called method overriding and can be thought of as a form of pure substitution. The relationship here can be thought of as the derived class being-a type of the base class. Both of these methods provide advantages when applied to a host of different scenarios.

Polymorphism: casting

With type hierarchies, a class that inherits or is derived from another class is a subclass. A subclass may itself be a superclass if there are other classes that inherit from it. In OOP there are certain advantages to decoupling your code from the concept of types. For instance, when you have multiple objects inheriting from the same type, you will find that it is more effective to write methods that treat derived-type objects as their generic base types. This is to say, you can write methods that manipulate generic vehicles, not minding if they are cars, trains, airplanes or some other type of vehicle that is as yet undefined.

When you write methods that use the generic base class in this way, the compiler can not know at compile-time exactly what piece of code will be executed. For instance, if you write a method that made a call to the generic vehicle base class to park, there is no way for the compiler to know exactly which vehicle objects park method will be called at run-time and this is exactly the point. The park method can be applied equally to a car, train or airplane object and the beauty of polymorphism is you are guaranteed that the proper binding will be made at run time. This is to say at run time, the object will execute the proper code depending on its specific type. This is essentially what polymorphism is about.

Polymorphism in OOP uses the concept of late binding. What this means is when you send a message to an object, the code being called is not determined until run-time even though the compiler checks that the method exists and performs type checking on the arguments and return value. The process of treating a derived type as though it were its base type is referred to as upcasting.

Containers

In the normal course of writing your code, it is often the case that you will inevitably require multiple objects to solve a particular problem. Often times you can not expect to know the number or lifetime of the objects you create, much less how much space you will need to store them.

Java provides container objects that are able to expand dynamically to accommodate any number of objects you place into them. Containers in Java have different efficiencies for different operations. For instance the List class can implement a container that is particularly good at holding sequences, Maps (also known as associative arrays) implement containers that associate objects with other objects, Sets implement containers that hold one of each type of object. Other containers in Java include queues, trees, stacks and one or two others..

Generics: Parameterised types

Previously, we defined upcasting as when you cast up the inheritance chain, in other words going from a more specific type to a more generic type. This is essentially a safe operation as the derived type is guaranteed to have all the characteristics and behaviour of the generic or base type. With downcasting however it is not quite so simple or safe as you are going down the inheritance chain from a more generic type to a more specific type. In this sort of situation, there is no way to tell if you are casting to the correct type.

Generics or parameterised types is a mechanism whereby a container can be automatically customised by the compiler to work with particular types. This is particularly useful when casting down the inheritance chain as the container is customised to know exactly what type it holds.

Java provides support for parameterised types which are easily distinguishable by their use of angle brackets. For instance, to customise a container to hold shape objects you would write something like this:-

ArrayList< Shape > shapes = new ArrayList< Shape >();

Downcasting and runtime checks require extra time and effort and consequently parameterised types provide a way to eliminate these and avoid possible mistakes.

Exceptions: handling errors

Exceptions provide a way to deal with errors in your program and understanding how and when to handle exceptions is very important if you want to be able to write useful code.

An exception is a particular kind of error that can not be ignored which is usually 'thrown' at the point from which it originates and can be 'caught' by an appropriate exception handler which the programmer must take care to write to provide a way to reliably recover from a bad situation.

Exception handling is wired into the Java programming language and is required usage for programs of any complexity. When you get an exception in Java, an object is thrown which can then be dealt with by an appropriate exception handler. This provides a certain level of consistency in your programs that makes error handling a little less problematic than you get with other programming languages.

Multithreading: Concurrency

Multithreading provides a way to partition a program into separately running pieces of tasks in order to make the program seem more responsive. The general concept is known as concurrency or parallelisation which allows one to employ a divide and conquer strategy by breaking a program into smaller chunks and then making them appear to execute in parallel, or if in the case of a system with multiple processors, actually do execute in parallel.

The programmer need not worry about the number of processors as the program itself is logically divided into tasks and if a machine has more than one processor, then the program will run faster without any special adjustments.

However the programmer must be careful to manage access to shared resources. For instance, when you have more than one task that expects to access the same resource for instance a printer, Java provides a lock mechanism whereby a task may lock a resource while it uses it. In this way other tasks are prevented from acquiring the lock until it is released by the current task.

Summary

In this discourse we have woven the beginnings of a path through the minefield that is object oriented programming with Java. We have provided a synopsis of sorts around core themes in Java including the conceptualisation of objects in the problem space as a powerful progress of abstraction, hiding implementation and access control with access control specifiers, reusing implementation by means of composition, extending functionality through inheritance, polymorphism and casting, generics and parameterised types, exception handling and of course concurrency.
At this point, it does not matter too much if you do not completely understand some of these concepts particularly if you are new to programming. In later discussions we expand on these topics to provide a more complete view of our own understanding.

contact me @ kaseosime@btinternet.com

Comments

Programmer must take care to write to provide a way to ...

Programmer must take care to write to provide a way to reliably recover from a bad situation. - Larry Starr Sarasota