Welcome to the first lesson of the object-oriented Java programming course.
We finished the Java basic constructs course last time
with the article about Mathematical
functions in Java. In this course, you'll learn to program in an
object-oriented way and will also develop an object-oriented way of
thinking. It's a bit different than anything we've done until now. For
starters, we will no longer treat programs like several lines of code that the
interpreter executes one by one.

The invention of object-oriented programming, OOP hereinafter, was no
coincidence, but the result of a development which led to its creation. It's a
modern software development methodology supported by most programming languages.
A common mistake is that people think that OOP should only be used for certain
kinds of programs and that for other cases it would be needlessly complicated;
however, the opposite has proven to be true. OOP is a philosophy. It's a new way
of looking at a program and the communication between its parts. We should
always use it whenever we create a simple utility or a complex database system.
The OOP is not just a technology or a recommended program structure, it's mainly
a new way of thinking. A new perspective from which we could analyze problems
and a new era in software development.

As first, we'll look quickly into the history of how people programmed before
OOP and what specific problems OOP resolves. It's important for us to fully
understand why the OOP had to be created.

The evolution of methodologies

There is a big difference between programming nowadays and programming 40
years ago. The first computers didn't have great performance and their software
was quite simple. The evolution of hardware is so fast that the number of
transistors in microprocessors doubles every year (Moore's Law). Unfortunately,
people aren't able to evolve as quickly as hardware does. Faster computers
require more and more sophisticated and complex software (people want more and
more from their computers). So much that at one point, we found out that about
90% of all software doesn't meet deadlines, requires additional costs or isn't
finished at all. Developers started looking for new ways to write programs.
Several new approaches took turns. More precisely, paradigms (ways of thinking).
Listed as follows:

1. Machine code

The program was just a set of instructions where we weren't able to name
variables or enter mathematical expressions. The source code was obviously
specific for the then current hardware (processor). This paradigm was replaced
soon after it was established.

2. The unstructured paradigm

The unstructured approach is similar to the assembly languages, it's a set of
instructions which are executed line by line. The source code wasn't dependent
on the hardware and was human readable. This approach enabled the creation of
more complex programs for a while. There were still many pitfalls: the only way
to repeat something or to branch off a code was the GOTO statement. GOTO allowed
to "jump" to different locations within the program. Locations were previously
specified by a line number in the source code, which is obviously impractical.
When we insert a new line of code somewhere, the numbers no longer match and the
code is broken. Later, it was made possible to define what they then called
"labels". This approach served as a way of simulating loops. This method of
writing programs is of course very confusing and soon failed to be sufficient
for the development of complex programs.

Consider that the huge adoption of personal computers over the past few
decades caused the growth of software demand and, naturally, a demand of more
people who make programs (programmers). Certainly, there are people who can
write bulletproof programs even in the ASM or other low-level languages, but how
many are there? How much does this superhuman job cost? It's necessary to write
programs in a way that even less experienced programmers could write
high-quality programs and not need to go through 5 years of experience to
achieve the same results.

3. The structured programming

Structured programming is the first paradigm that lasted for a longer time
and was quite sufficient for the development of new programs. They would mostly
program using loops and branching. Conceptually, we are in the structured
programming era based on what we have learned from the first course.

The program would be decomposed into functions, methods, that we haven't
discussed yet since Java, which is an object-oriented language, doesn't even
allow us to declare them. There is a way to do it anyway, but I'd rather skip
this intermediate step and get right into OOP. In structured programming, we
meet a functional decomposition principle. A problem is decomposed into several
subproblems and each subproblem is then solved with some parametrized function.
The disadvantage to it is that a function can only do one thing and when we want
a different behavior, we have to write a new one. There is no way to reuse an
old code and modify it. We need to write it again and again - it creates
unnecessary, potentially costly, errors. This disadvantage can be partially
worked around by using parametrized functions or using global variables.
However, such universal functions usually require a lot of parameters to pass
and are hard to use and maintain.

With global data, there's another pitfall. Functions can access the data of
other functions. This is the beginning of the end, we can't guarantee that
global data isn't being overwritten somewhere between functions. It leads to
uncontrollable problems. The entire program will consist of unencapsulated code
blocks and can hardly be maintained. Any modification increases the complexity
of the program, and then the program will necessarily come to a situation where
the cost of adding new features will overbalance the value added by these
features. Languages using this approach are, for example, the C language and
Pascal.

Between structured programming and the object-oriented programming, there was
one more intermediate approach called modular programming. It involved the
encapsulation of specific functionality into modules. Regardless, there was no
way to modify and reuse already written code.

As I mentioned at the beginning of the article, it's sometimes said that
simple programs mustn't be written in an object-oriented way, but structural,
which isn't true. If we program in a structural fashion, we will end up making a
blob that will be almost unreadable by most people. Then again, it would come to
a point where the program wouldn't even be upgradable and we'd either have to
throw it away or rewrite it using the OOP.

The non-object-oriented methods of writing code are called "spaghetti code"
because of their lack of clarity (everything is tangled together like
spaghetti).

The object-oriented approach

OOP is a philosophy and a way of thinking, designing and implementing
solutions that focus on reusability. This approach is inspired
by the industrial revolution - the invention of basic components. For example,
when we build our house, we don't burn our own bricks and forge the nails, we
order them.

Making a "component program" is smarter and cheaper. Components don't fail,
they're tested and maintained. If there's a problem, it is most likely in the
code you have written in one specific location. We're motivated
to write clear code since it can be used by others or by ourselves in other
projects. Let's face it, humans are lazy by nature and if we thought that our
code wouldn't ever be reused, we wouldn't write it in the first place ).

Of course, we'll use the knowledge we have gained until now. The main
difference is that now, our code will be structured differently into multiple
communicating objects.

How the OOP works

It tries to simulate reality as we're used to see it. We can say that
we abandon the idea how the program is seen by the computer (machine)
and write it from the programmer's (human's) point of view. As we had
replaced the assembler with human-readable mathematical notations, now we're
going even further and replace those, too. OOP is, therefore, a certain level of
abstraction above the program. This has significant advantages because it's more
natural and readable for us.

The basic component is object which corresponds with some
object from the real world, e.g. a human object or a database
object).

Objects have attributes and methods.

Attributes

Object attributes are properties or data that it stores,
e.g. the human's name and age. For the database object
it could be the password or whatever the objects requires to work.
Attributes are just like ordinary variables with which we've worked a hundred
times. Sometimes they're called the "object's internal state".

Object variables are called "fields" in Java. However, other
languages often call them properties (PHP) or attributes and I
might call them attributes as well in further texts.

Methods

Methods are abilities that the object can perform. Human,
for example, could have the methods goToWork(), greet() or blink(). For the
database, it could be addEntry() or search(). Methods can have parameters and
can also return values. You've actually used them before without even knowing!
Remember the split() method on the String object? A String is
actually an object that represents text. You can see that we can easily imagine
that we're dealing with text. We can modify it, ask it to return its length,
combine it with other strings, etc.. It contains methods that a text can
perform, copying, deleting, splitting, and also has fields, e.g. length which
contains its length.

In older languages, methods didn't belong to objects but were loosely placed
in modules (units). We could call them by typing split(text) instead of
text.split(). The disadvantage to that was, of course, that the split() method
didn't belong to anything. There was no way to list what String could do and the
code was messy. Additionally, we couldn't have two methods with the same name.
In OOP we can have both user.remove() and article.remove(). It's very clear and
simple. In a structured program we'd have to write: remove_user(user) and
remove_article(ar­ticle). We'd have to create thousands of silly, unnecessary
methods. If you're thinking, hey, isn't that what the PHP language does? You are
absolutely right. PHP is terrible when it comes to things like this, and for
that same reason, its design is considered old. It became fully object-oriented
later, but its foundations will probably never change. Java is a modern language
and its libraries are strongly built on objects.

In this article, we're going to explain the basics of how to create objects
and how to encapsulate their internal logic. Other OOP features, mainly
inheritance, will be explained in the following lessons, to not overload your
brain today

Class

We have already encountered the term "class". We understood it as a set of
commands. A class, however, allows us to do so much more. A class is a
pattern that we use to create objects. It defines their
properties and abilities.

An object created according to a class is called an
instance. Instances have the same interface as
the class according to which they were created, but they mutually differ in
their internal data (fields). For example, let's consider a Human
class and creating the Carl and Jack instances from
it. Both instances will have the same fields as the class (e.g.
name and age) and methods (goToWork() and
greet()), but the values in them will be different. For example,
the first instance will have the value Carl in the name attribute
and 22 in age, the second will have the values
Jack and 45.

The communication between objects is performed by messaging which makes the
syntax clear. The message usually looks like this:
recipient.methodName(parameters). For example,
carl.greet(neighbor) could cause the carl instance to
greet the neighbor instance.

OOP is based upon three core concepts:

encapsulation

inheritance

polymorphism

Let's look into the first of them:

Encapsulation

Encapsulation allows us to hide some methods and fields so
they can remain available only from inside of the class. The object can be
thought of as a black box that provides an
interface through which we can pass instructions/data to be
processed by it.

We don't know how the object works internally, but we know how it behaves on
the outside and how we should use it. We can't cause errors because we are only
allowed to use in a way its creator meant it to be used.

An example might be the Human class having a
birthDate field and a few more based on its value:
fullAged and age. If someone changed birthDate from
outside the object, the values in fullAged and age variables
could become invalid. Which means that the internal state of the object would be
inconsistent. This could happen to us in structured programming. In OOP,
however, we encapsulate the object and mark the birthDate attribute as
private so it won't be visible from the outside. To the outside, we'd provide a
changeBirthDate() method which would store a new birth date into a
birthDate variable and also perform a necessary age re-calculation and
full-age re-valuation. Using the object would be always safe and the application
stable.

Encapsulation forces programmers to use objects only in the right way.
The class interface is divided into publicly accessible (public) and
internal (private) members.

The author is a programmer, who likes web technologies and being the lead/chief article writer at ICT.social. He shares his knowledge with the community and is always looking to improve. He believes that anyone can do what they set their mind to.

The author learned IT at the Unicorn College - a prestigious college providing education on IT and economics.