Java 101: Object-oriented language basics, Part 1: Classes and objects

Learn how to declare Java classes and create Java objects

Build what you know about object-oriented programming in Java from the ground up with this Java 101 series all about Java's fundamental programming concepts and syntax. Start right here by learning how to declare classes and create objects from classes.

Back in the mid-1980s, I used the prevailing software development methodology of the time -- structured programming -- to help me write computer programs. Structured programming emphasizes separating a program's data from its functionality. As I discovered, separating data from functionality typically leads to software that is difficult to maintain and understand -- especially in large programs.

In the early 1990s, I learned a new software development methodology -- object-oriented programming (OOP). OOP-created software more realistically models real-world entities -- such as cars, bank accounts, and dogs -- in source and executable code. Structured programming emphasizes making an entity's representation fit the constraints of the programming language; OOP emphasizes making the programming language fit the constraints of the representation. For example, in a structured programming language, a car is represented by a set of functions (named bodies of code) for starting, braking, parking, accelerating, and so on. A separate set of variables defines the car's color, number of doors, make, model, and so on. You initialize the car's variables and call some functions that operate on those variables -- and voilà, you have a car!

An OOP language sees a car as an integration of its behaviors -- the functions -- and the variables that hold the car's state -- the data values. The integration of state variables and behaviors results in an object. You create that object simply, at the same time initializing the variables. At any time, the object-oriented program identifies the object it wants to work with and calls the object's functions. Essentially, the object-oriented program thinks in terms of objects (e.g. cars) -- not in terms of separate functions (e.g. parking) and variables (e.g. number of doors).

The integration of state variables and behaviors into objects is called encapsulation. Encapsulation promotes information hiding -- a concept that facilitates program maintenance by hiding state variables and behaviors that don't need to be externally accessed -- and is one of the three fundamental principles of OOP. (I'll explore the other two, inheritance and polymorphism, in later series articles.)

OOP has been widely available to developers for about 15 years, mainly because of the popularity of the C++ programming language. However, because of its C heritage, C++ is not completely object-oriented; you can write C++ programs that do not use object-oriented features. In contrast, Java is completely object-oriented: every Java program requires the presence of at least one class (an object-oriented feature). Furthermore, Java's official language definition includes the term object-oriented. (See Resources.)

This article begins an in-depth examination of object-oriented programming from Java's perspective. I'll start by examining classes and objects; I'll also discuss the javadoc tool for documenting packages, classes, fields, and methods.

Objects don't just appear out of thin air. Just as a contractor needs access to an architect's blueprint before creating a building based on that blueprint, a running program needs access to a class before creating objects based on that class.

Declaring classes

A class is a source code blueprint for objects. That blueprint specifies each object's behaviors and state variables. Use the following Java syntax to declare a class in source code:

[ 'public' ] [ ( 'abstract' | 'final' ) ] 'class' class_name
'{'
     // behaviors and state variables are declared between the { and } characters
'}'

To introduce a class declaration, specify keyword class and follow it with an identifier -- represented by class_name -- that names the class. The identifier cannot be a reserved word. For example, class Account introduces a class declaration where Account names the class. (By convention, class names are capitalized.) Keywords public and/or abstract or final -- but not abstract and final, because the resulting class would be meaningless -- can optionally precede keyword class.

Use keyword public in conjunction with packages. Specify public to make class_name accessible to all classes in all packages. Do not specify public if class_name is to be accessed only by classes in the same package as class_name. (I'll explore packages in an upcoming article.)

Remember the following rule when declaring a public class: only one public class can be declared in a source file. Furthermore, the source filename must match class_name. Consider the following example:

// MyClass.java
public class MyClass
{
}

Java requires that MyClass be declared in a source file called MyClass.java. Declaring that class in another file -- such as Fred.java, or even myclass.java -- is unacceptable. Specify the exact identifier, used as class_name; case is significant. (For MyClass, you specify a capital letter M and a capital letter C. All other letters must be lowercase.)

You can choose any name for the source file if it does not contain a public class. However, the file still must end with a .java extension. The following example demonstrates that:

// Fred.java
class MyClass
{
}

The above example declares a nonpublic class called MyClass and specifies (via the comment) that the class declaration is stored in a file called Fred.java.

Use keyword abstract to identify class_name as the name of an abstract class. Objects cannot be created from abstract classes. (I will present the concept of abstract classes later in this series.)

Use keyword final to identify class_name as the name of a final class. You cannot create a class that inherits behaviors and state variables from a final class. (I'll explore the concepts of inheriting and final classes later in this series.) Keywords public, abstract, and final can be specified in any order. For example, specify public abstract class fred, final public fred, abstract fred, and so on.

Each class has a body where you declare behaviors (known as methods) and state variables (known as fields). The body begins with a { character and ends with a } character. Although you don't need to declare methods or fields between { and } (see the aforementioned MyClass examples), there is almost no point in declaring a class without methods or fields. Therefore, you'll almost always declare fields, methods, or some combination of both in your class bodies. (I will provide a detailed exploration of fields and methods later in this series.)

Classes and Java programs

Each Java program consists of one or more classes. One of those classes is known as the starting class, because it is the first program class that the JVM loads. Actually, the classfile that implements the starting class -- by providing code to implement behaviors and variables to hold state -- is loaded by the JVM.

As you saw in previous Java 101 articles, a Java application contains a starting class with a main() method. For a quick refresher on what a simple application looks like, check out Listing 1 for the CODemo1 application:

Listing 1. CODemo1.java

// CODemo1.java
class CODemo1
{
   public static void main (String [] args)
   {
      System.out.println ("Hello");
   }
}

CODemo1 -- class/object demo number 1 -- is a simple Java application. The CODemo1 class's body is a single method named main(). After you compile that code and issue the java CODemo1 command to run the program, the JVM loads the starting classfile -- CODemo1.class -- and executes the byte codes that constitute the main() method. That execution results in the Hello message appearing on the standard output device.

Creating objects

An object is an instance of a class. Use the following syntax to create an object:

'new' constructor

The new keyword, often called the creation operator, allocates an object's memory and initializes that memory to default values. An object's field values are stored in memory. Because new is an operator, it takes an operand: constructor, which is the name of a special method that constructs the object. Once new finishes allocating and initializing memory, it calls the constructor to perform object initialization.

Listing 1's CODemo1 source code demonstrates the simplest possible useful application. Although Listing 1 clearly shows that an application requires a class declaration, no objects have been created from that class. How do you go from executing code in a class's main() method to creating an object from that class and manipulating the object by accessing its fields and calling its methods? For an answer, check out Listing 2 for the CODemo2 application:

Listing 2. CODemo2.java

// CODemo2.java
class CODemo2
{
   int i = 3;
   public static void main (String [] args)
   {
      CODemo2 obj1 = new CODemo2 ();
      System.out.println ("obj1.i = " + obj1.i);
      obj1.printHello ();
      CODemo2 obj2 = new CODemo2 ();
      obj1.i = 5;
      System.out.println ("obj1.i = " + obj1.i);
      obj1.printHello ();
      System.out.println ("obj2.i = " + obj2.i);
      obj2.printHello ();
   }
   void printHello ()
   {
      System.out.println ("Hello! i = " + i + "\n");
   }
}

Field declaration statement int i = 3; specifies a field named i, which is of type integer (as specified by the int keyword) and initializes i to 3 (as specified by the integer literal 3).

CODemo2 obj1 = new CODemo2 (); creates an object from the CODemo2 class. CODemo2 obj2 = new CODemo2 (); creates a second object from CODemo2. Because an object is an instance of a class, class instance is often used as a synonym for object.

CODemo2 obj1 and CODemo2 obj2 declare variables in a manner similar to int count or double balanceOwing. Data type keywords int and double declare storage locations named count and balanceOwing for holding values of primitive data types: integer and double-precision floating-point, respectively. CODemo2, on the other hand, declares a storage location for holding a value of a reference -- or address -- data type. In other words, because obj1 and obj2 have the CODemo2 data type and CODemo2 is a reference data type, any value assigned to either obj1 or obj2 is a reference to (that is, the address of) an object created from the CODemo2 class.

CODemo2's new operator calls CODemo2() to build the object. But wait! No CODemo2() constructor method is declared in the CODemo2 class. What's going on? For an answer, check out the next article in this series.

Once the constructor has finished building the object, creation operator new returns the address of that object. In Listing 2, the first-returned CODemo2 object-address assigns to variable obj1; the second address assigns to variable obj2. Figure 1 conceptualizes both objects, referenced by their respective variables.

Figure 1. A conceptualization of two CODemo2 objects and their respective reference variables

Personally, I imagine an object as one or more arrows pointing to a circle. Each arrow represents a reference to the object, and originates from a rectangle containing the object's address.

Check out Listing 2's System.out.println ("obj1.i = " + obj1.i); method call. What does obj1.i mean? Well, i is an integer field variable that initializes to 3. An object created from CODemo2 is capable of having its own copy of that variable (with a different value), so we must distinguish between multiple variable copies. Therefore, we must tell the program which copy to use. The period character following variable name obj1 identifies the i variable that belongs to the object referenced by obj1 (as opposed to the i variable that belongs to the object referenced by obj2). The period character is often referred to as the dot operator. (I will say more about fields in next month's article.)

obj1.printHello (); calls the printHello() method. As in obj1.i, obj1. prefixes printHello(). Because Listing 2 also contains obj2.printHello ();, you might think there are two copies of printHello() -- one for each object. Actually, only one copy exists. Its byte codes are loaded when the CODemo2 classfile loads. How does the single copy of printHello() distinguish between the i field in the object referenced by obj1 and the i field in the object referenced by obj2? The answer involves the obj1. and obj2. prefixes. The dot operator that appears between obj1 and printHello() tells printHello() that it can access the i field belonging to obj1 (as opposed to accessing the i field belonging to obj2). The same idea holds for the dot operator that appears between obj2 and printHello(). (I will say more about methods in next month's article.)

Why doesn't obj1 or obj2 prefix the i variable in printHello()? If you're curious, check out next month's article.

1 2 Page
Join the discussion
Be the first to comment on this article. Our Commenting Policies
See more