Skip to main content

Java objects and data types

Posted by hellofadude on October 25, 2013 at 5:02 PM PDT

Much of what you do in Java is to define classes that package data and functionality together by concept to represent the desired problem-space element. When you instantiate a class, you create an object that has it's own piece of memory made up of other objects. Java has a peculiar means of manipulating these elements in memory. This is to say even though you treat everything as an object, you do not manipulate these objects directly. On the contrary, you manipulate an identifier that is a "reference" to the object. You may think of a reference as the physical address of the location of the object in memory or some other physical device. An identifier is simply a name or label for that reference. For instance, to create a reference to hold a word or sentence you would do something like this:-

String s;

At this point the identifier s does not reference anything until you initialise it with a string object like this:-

String s = "This is a string object";

Now the identifier s can be described as a reference to the string object "This is a string object". The point being in Java you do not manipulate objects directly, instead you access the object via its reference and in this particular case the identifier s.

The situation can be likened to any remote controlled device for instance a TV or VCR. The device being the object and the remote control the reference. With the remote control you can manipulate the device directly without necessarily having to physically disturb its situation.

Using references in this way allows for some flexibility in the way objects are stored and how they are allocated. For instance, when you create an object in Java, it is generally stored on the heap. The heap is a general purpose pool of memory in the RAM area that places no restrictions on the lifetime of objects perhaps because there is generally no limit to memory size in this area, added to the fact that it is not managed automatically by the CPU. To create an object on the heap, you would generally use the new keyword like this:-

String s = new String("This is a string object");

The downside to storage on the heap is that it provides relatively slower access and there is no guarantee of the efficient use of space as memory may become fragmented over time.

The stack is another area of storage within the general RAM area where object references (not objects themselves) are stored. In contrast to the heap, this area of storage is managed efficiently by the CPU and memory is unlikely to become fragmented even as it provides very fast access. On the flip side however, storage in this area is limited and OS dependent.

There are of course other storage mechanisms that depend on the type and location of data used within your program, but for the moment understanding where and how objects and object references are stored is a useful way to visualise certain aspects of how things are laid out while your program is running, particularly as it pertains to the arrangement and allocation of memory.

Primitive data types

Java provides a group of eight predefined or primitive types that get exceptional treatment in the way they are created and stored in memory. A primitive type is a small simple variable that is named by a reserved keyword. When you create an object of a primitive type using the new keyword, instead of creating an object on the heap as per normal, Java creates a kind of "automatic" variable that is not a reference. This variable holds the value directly and is placed on the stack making access more efficient. Java's primitive data types are listed below:-

  • byte - 8 bits
  • short - 16 bits
  • int - 32 bits
  • long - 64 bits
  • float - 32 bits
  • double - 64 bits
  • boolean - undefined
  • char - 16 bits



The sizes (listed to the right) of these primitive types are constant and do not change from one machine architecture to the next, one of the reasons adduced in literature for the portability of the Java programming language in comparison with other programming languages.

When instantiated as members of a class, primitive data types are guaranteed to get default values, if they are not initialised at the point of creation. These default values are reproduced here for ease of reference:-

  • byte - 0
  • short - 0
  • int - 0
  • long - 0L
  • float - 0.0f
  • double - 0.0d
  • boolean - false
  • char - '\u0000'



It should be noted that this guarantee does not apply to local variables i.e. variables created within a method definition.

Each primitive type has what we call a "wrapper" class that, with the exception of the char and int primitive types, has the same name as the primitive type, with the additional proviso that the first letter is capitalised. For instance the wrapper class for the float primitive type would be Float, byte would be Byte, boolean would be Boolean and et cetera.. For the primitive types of int and char the wrapper classes are named Integer and Character respectively.

Wrapper classes are used to make a non-primitive object on the heap to represent that primitive type. Java has what we call an autoboxing mechanism which enables the compiler to make an automatic conversion between primitive types and their corresponding object wrapper classes. Unboxing describes the reverse of this process.

Variable lifetime & scope

Scoping is a concept that is used to determine the visibility and lifetime of the variables you create. In Java, a scope is determined by the placement of curly braces { }. Variables created after an open { curly brace normally have a lifetime to the following close curly brace }. Consider the example below:-

 {
   double db = 3.1;
   // Only db available here
   {
      int x = 20;
      // Both db and x available here
   }
   // Only db available here
   // x is out of scope
}

Any text after the // is intended to be read as a comment. The example shows how curly braces are used to scope variables. Note how the variables x and db are both available within the inner curly braces and how only db is available outside of it. Objects however, do not have the same lifetime as primitives. Objects created using the new keyword tend to hang around even past the end of the scope. For example:-

  {
     String name = new String("Kingsley");
  }// End of scope

You will find that the object reference name disappears at the end of the scope. However, the string object referenced by this identifier, in this instance "Kingsley", tends to hang around past the end of scope occupying much needed space in memory, even as there is no way to reference it.
Fortunately, unlike some other programming languages, Java does not require you to write special clean-up code to take care of releasing or managing memory. Java has a garbage collector which looks at objects created on the heap with the new keyword and somehow figures out which ones are no longer being referenced and takes care of releasing the memory for these objects so the memory can be recycled for use by other objects. This mechanism eliminates a certain class of programming problem: the so-called "memory leak" in which the programmer forgets to release memory.

Creating new data types

To create a new data type in Java you use the class keyword like this:-

class NewDataType { /* Class body */ }

The syntax introduces a new type by assigning a name following the class keyword. The body of the class goes between the curly braces, though in this instance the body consists of only a comment (anything between the stars and slashes are also comments) to demonstrate our meaning. To create an object of this type, you use the new keyword like this:-

  NewDataType x = new NewDataType();

This example creates a new object of NewDataType on the heap, while the object reference, x in this case, is stored on the stack. There is however very little we can do with this object as we have yet to define a class body.

Fields and methods

The body of a class is made up of two types of elements: fields and methods. Fields are sometimes called data members or attributes and are normally objects of other data types or primitive types. When a field is a reference to an object, you are required to initialise the reference using the new keyword before attempting to call any methods. Primitives data types are however guaranteed to be initialised to a default value if you do not initialise them straightaway. We can add some fields to our NewDataType class like this:-

  class NewDataType {
    int x;
    double y;
    boolean b;
  }

To create an object of this NewDataType class you would do something like this:-

NewDataType objectReference = new NewDataType();

In the preceding example, objectReference is an identifier for the reference to the NewDataType object created with the use of the new keyword. To assign values to the fields in this object, you must refer to the data member by calling the object reference, followed by a period (dot) and then the name of the data member inside the object. For instance to initialise the fields in the NewDataType object you would write code using the object reference created above like this:-
  objectReference.x = 20;
  objectReference.y = 3.1;
  objectReference.b = false;

The NewDataType class does not do much at this stage except hold data because it has no methods.

Methods are sometimes known as member functions or operations and they determine the messages an object can receive. A method declaration consists of a number of required elements and is of the form:-

  ReturnType methodName( /* Parameter list */ ) {
     // Method body
  }

Return type describes the data type of the value returned by the method, the method name is self explanatory even though there are certain conventions that apply to naming a method; parameter list is a comma separated list of arguments preceded by their data type and the method body enclosed between the curly braces hold the method's code including any declaration of local variables.
The method name and argument list together comprise the method signature that uniquely identify the method. It is also possible to have a method with no argument list, in this instance, an empty pair of parenthesis will suffice.

Conversely, not all methods are required to return a value. A method with no return type is declared as void like this:-

   void methodName( /* Parameter list */ ) {
       // Method body
   }

Non-static methods can only be created as part of a class and can only be called for objects of that class. For instance we could add a method to our NewDataType class like this:-

  class NewDataType {
     int x;
     double y;
     boolean b;

      void objectMethod(Object ob) {
        System.out.println(ob.getClass());
      }
  }

We can reference our method in the same way we initialised data members earlier, except in this instance we are required to supply arguments to the parameter list like this :-

objectReference.objectMethod("This is a string object" );

Note that we would typically be making this call from the convenience of another class definition. You will also note that the method in the example is declared void and so does not return any value. We could modify our objectmethod() to return some value, a string value perhaps, like so:-
    String objectMethod(Object ob) {
        return ob.toString();
     }

Note that the return type of String is specified in front of the method name. The return keyword tells the program to exit the method and any value produced by the method is placed right after the return keyword. In the above example, the toString() method which is inherited from the root object, as are all objects in Java, is used to return the string value of whatever object we are disposed to include in the argument list.

When we call a method that returns some value, we are required to provide a 'container' of sorts of the same type as the return value with which to hold the value that is returned. Take a look at this:-

String container =  objectReference.objectMethod("This is a string object" );

Now the identifier container holds a reference to the string value of the object returned by calling our objectMethod().
In Object oriented programming, the act of calling a method is commonly referred to as "sending messages to objects". This is synonymous with saying an "object accepts requests". The requests an object can accept speak to its behaviour or functionality.

The static keyword

Data members are typically unique to the objects within which they exist. This is because storage is allocated for an object at the point the object is created using the new keyword. Methods become available at about the same time. The logic being you must first create an object and then use the object to access its members which normally consist of non-static fields and methods.

However, we can conceive of certain scenarios in which it might be convenient to have a single piece of storage for a particular field, no matter how many objects of that class are created, or even if no objects are created, or a method that is not associated with any particular object of our class. This is to say a method that we can call even if no object is created.

Declaring a particular field or method as static is a way of tying the member to a class as opposed to any particular object instance of that class. To make a field static, you simply place the static keyword immediately before the member definition. For example:-

   class StaticField {
      static int i = 20;
   }

In this example, no matter how many StaticField objects are created, there will be only one piece of storage for the i data member. You may refer to a static variable via an object of the same class, or conventionally directly through its class name like this:-

StaticField.i;

When the static keyword is applied to a field, data is created on a per-class basis as opposed to the non-static per object basis. So for instance, if you were to create two objects of the StaticField class:-
   StaticField sf1 = new StaticField();
   StaticField sf2 = new StaticField();

Both object references sf1 and sf2 would have the same value of 20 for the static member i because they both refer to the same piece of memory. The static member I is referred to as class data because it exists for the class as opposed to any particular object.
We define static methods in a similar way as we do static fields:-
  class StaticMethod {
    static void increment() { StaticField.i++; }
  }

The static increment() method in the StaticMethod class increments the static data member i in the StaticField class by referring to it directly through the class name, a feat that is not possible with a non-static field. We can likewise call the increment() method directly through its class like this:-

StaticMethod.increment();

Static methods apply a similar logic though are not quite so adventurous in the way they create and use data as static fields. Static methods are mainly used for direct method calls without the need to create an object.

final keyword

You can apply the final keyword to elements of your program to make them immutable and constant. The effect of the final keyword varies according to the element or member to which it is applied. For instance, when you make your data fields final, you essentially make something about their character immutable depending on if it is a primitive or an object reference.

When you make an primitive final, you make the value of that primitive constant, however when you use the final keyword in relation to an object reference, the effect is to make the reference a constant, which means that once the reference is initialised to an object, it can no longer be changed to point to another object, even though the object itself may be modified.

A constant can be initialised either at compile time or at run-time. By convention, fields that are both final and static are capitalised and underscores are used to separate words.

import java.util.Random;

class AClassDefinition {
     double aDouble;
     public AClassDefinition(Double aRef) {
         this.aDouble = aRef;
     }
}
public class FinalFields {
     private static Random numberGenerator = new Random(30);
     private final int compileTimeConstant = 240;
     private final int runTimeConstant = numberGenerator.nextInt();
     static final int STATIC_CONSTANT = 500;
     private AClassDefinition aRef = new AClassDefinition(6.2);
     private final AClassDefinition anotherRef = new AClassDefinition(3.4);
     private static final AClassDefinition thirdRef = new AClassDefinition(2.4);
     private static final int[] arrayInt = { 5, 4, 3, 2, 1, 0 };
     public static void main(String[] args) {
         FinalFields objRef = new FinalFields();
         //! objRef.compileTimeConstant++; // Error remove
         //! objRef.runTimeConstant = objRef.numberGenerator.nextInt();// final modifier
         //! objRef.STATIC_CONSTANT = 100;
         //! objRef.anotherRef = new AClassDefinition(10.9);
         //! objRef.thirdRef = new AClassDefinition(12.5);
         //! objRef.arrayInt = new int[10];
         objRef.aRef = new AClassDefinition(7.9); // ok object not constant
         for(int i = 0; i < arrayInt.length; i++)
             arrayInt[i] = i;// ok contents not final
     }
}

Java also allows you to assign the final keyword to uninitialised fields at the point of definition, leaving you the option of initialising these fields in other ways before they can be used, for instance within a class constructor or by means of an expression. This technique can be useful for creating unique values for final fields within individual objects, while retaining their immutable quality:-

public class BlankFinalFields {
     private final int blankInt;
     private final Integer blankObjRef;
     BlankFinalFields(int initValue) {
         this.blankInt = initValue;
         blankObjRef = new Integer(initValue);
     }
     public static void main(String[] args) {
         BlankFinalFields b1 = new BlankFinalFields(100);
         BlankFinalFields b2 = new BlankFinalFields(4);
     }
}

You can also apply the final keyword to methods of your class to make it impossible for an inheriting class to change its meaning. This is mostly in response to some design consideration. Applying final to a method makes it impossible for any derived classes to override this method or even to include a method with the same name as though it were a different method. By contrast, a method marked private is hidden from inheriting classes but does not prevent you adding a method with the same name in a derived class. It should be understood, this method will have nothing to do with the private method in the base class but would be considered a a brand new method by Java.

class SomeClass {
     final void finalMethod() {
         System.out.println("Inside someClass final");
     }
     private void privateMethod() {
         System.out.println("Inside someClass private");
     }
     protected void protectedMethod() {
         System.out.println("Inside someClass protected");

     }
     public void publicMethod() {
         System.out.println("Inside someClass public");
     }
}
class MyClass extends SomeClass { }
class OtherClass extends SomeClass {
     private void privateMethod() {
         System.out.println("Inside OtherClass private");
     }
     public void publicMethod() {
         System.out.println("Inside OtherClass public");
     }
}
public class FinalMethod extends OtherClass {
     private void privateMethod() {
         System.out.println("Inside FinalMethod private");
     }
     public static void main(String[] args) {
         MyClass myRef = new MyClass();
         OtherClass otherRef = new OtherClass();
         FinalMethod finalRef = new FinalMethod();
         //myRef.privateMethod(); No visibility
         myRef.protectedMethod();//ok inherited
         otherRef.finalMethod();// ok no override
         otherRef.publicMethod();//overridden public()
         finalRef.privateMethod();// new privateMethod()
     }
}
/* Output
Inside someClass protected
Inside someClass final
Inside OtherClass public
Inside FinalMethod private
*//

Extending final to an entire class by preceding its definition with the final keyword allows you to prevent any one from inheriting from this class. You may declare fields in a final class as final or you may choose not to, however all methods in a final class are implicitly final by virtue of the fact that this technique prevents inheritance.

final class Parent {
     String eyeColor = "blue";
     final double height = 5.4;

      void schoolRun() {}
}
//!class Child extends Parent{}// cannot inherit

public class FinalClass {
     public static void main(String[] args) {
         Parent mum = new Parent();
         mum.eyeColor = "brown";
         //!mum.height = 5.7; final field
         mum.schoolRun();
     }
}

Note that adding the final specifier to a method in a class declared as final, makes no difference since you can not in any event, inherit from a final class

Comments and embedded documentation

Java also provides a mechanism for inserting comments in your code. There are two ways to insert comments in your program. The simplest way is a single line comment which starts with a // and continues to the end of the line. For example:-

// This is a single line comment

Another way is to insert your comments across multiple lines using /* until you get to a */:-
   /* This comment can 
    * span multiple
    * lines
    */

As with any system you are likely to develop, good documentation is essential and is in fact taken for granted by your customers who expect to be able to make rapid progress using your code with the aid of proper documentation.
The problem arises when you have to work on your documentation in isolation from your code. By this, I mean how do you make sure your documentation keeps up with the changes to your code? This is certain to be a laborious process if you have ever suffered the misfortune of programming in certain other languages.

Java solves this problem by allowing you to link documentation with your code in a way that makes maintenance easier. With the use of some special syntax, Java allows you to mark text for documentation which can then be extracted and formatted in HTML by means of a special javadoc tool. The javadoc tool comes as a part of your JDK installation and works by looking for and extracting information marked by comment tags that you put in your program. In this way, Java allows you to maintain a single source file for your program and automatically generate useful documentation at the same time.

You can use javadoc either by embedding HTML commands or the special doc tags, either of which must be place within /** comments and ending with the usual */. Standalone doc tags are commands that begin with the @ symbol and can be placed at the beginning of a comment line. Inline doc tags have a similar syntax but can appear anywhere within a javadoc comment and are usually surrounded by curly braces.

We can distinguish between three kinds of comment documentation, each of which correspond to the element they precede; i.e. class comment, field comment and method comment. Javadoc will not ordinarily process comments for private and package access or default members. This is not so incredulous when you consider the logic behind the need to hide implementation from your client programmer who also turns out to be your average consumer.

Embedded HTML

You can use HTML just as you would with any other web document to provide documentation in your code:-

    /**
   * This is a <strong>list<strong> of my favourite
   * technologies
   * <ul>
   * <li> Java
   * <li> Jersey
   * <li> Maven
   * <li> Subversion
   * <li> Jboss
   * <li> Eclipse
   * <ul>
   */

Note that Java discards asterisks and leading spaces at the beginning of each line and reformats everything to conform to the standard documentation appearance.

Doc tags

Below are a couple of example doc tags that you might use for code documentation. For a full list, consult your JDK documentation:-

@author name-text
The -author flag adds an author entry with the specified name-
text to the generated HTML docs. You may specify one name per tag
or multiple names per @author tag.

@param parameter-name description
This tag adds a parameter with the specified name and
description to the "Parameters" section. The parameter-name
element can be the name of a parameter in a method or class
constructor or even the name of a type parameter of a class,
method or constructor.

The description element is a piece of text that can continue on
subsequent lines.

@return description
Adds a "Returns" section with a description text that should
describe the return type and permissible range of values of a
method. This tag is only valid in a doc comment for a method.

Summary

This discussion, I believe, has been a gentle introduction to the Java syntax as well as a basis for understanding the basic form of Java classes. We have also provided only a brief introduction to Java primitive data types which is something you are likely to come across quite often in any Java discourse. The rest of this discussion looks at how to create new data types as well as the elements that comprise a class i.e. fields and methods. We have also considered the use of the static keyword to create per-class data in addition to providing us with a way to call methods without having to objectify a class.
Finally, we have considered the use of special syntax relating to comments and embedded documentation in your program code.

contact me @ kaseosime@btinternet.com