Architecture & DesignExploring the java.lang Library

Exploring the java.lang Library

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

The java.lang library contains classes and interfaces that are fundamental to every Java program. This package is imported implicitly by the compiler into all programs, so we do not use the import statement to explicitly include it. Due to its tacit nature of inclusion, the significance of its impact often goes unnoticed. The most important class of this library is Object. Every class design in Java is actually a subclass of this class, either directly or indirectly. In other words, Object is the parent of all the classes in Java. Apart from this primordial class, the package contains numerous other classes and interfaces. The article takes on just two of them, the Object class and the collection of wrapper classes of this package, to get a glimpse of the key aspects of this library.

The Object Class

A Java programmer uses inheritance to create classes from existing classes. The primary motivation behind inheritance is to extend the quality of its parent class. That means, if a class has, say, two methods:

class A{
   method1();
   method2();
}

and another class, B, extends class A, and has a single method:

class B extends A{
   // class A methods are inherited automatically
   method3();
}

class B extends the quality of class A by inheriting the methods defined in class A, in addition to the methods defined within itself.

This is what happens with the Object class. The Object class methods are implicitly inherited by all Java classes. But, the difference is that we do not explicitly use the extend keyword; instead, it is implied by default. In this case, the responsibility of applying the inheritance hierarchy to Java classes rests with the compiler. However, there is no harm in doing so explicitly, although it is utterly unnecessary.

public class Employee extends Object{
   // ...
}

In relation to this, note that the Employee class never inherits the constructor of the Object class as per the object-oriented principle of superclass-subclass relationship. The Object constructor is implicitly called by the Employee constructor as soon as its objects are created. It is the first task of any subclass constructor to call its immediate parent class constructor, either explicitly or implicitly, to ensure that the instance variables inherited from the superclass are initialized properly before using them.

public Employee(){
   // ...implicit call to superclass constructor
}

public Employee(){
   // ...explicit call to superclass constructor
   super();
}

The Object class provides a number of methods that are universally applicable to all its subclasses. The most common of this is the toString() method. This method returns the string representation of an object. It is particularly useful for debugging purposes. It is recommended that every class overrides this method to get a customized string representation of the object. By default, the toString() method returns the package name with class name, concatenated by an ‘@’ and the hash code of the class instance. As a result, the following two operation invariably have the same string representation:

employeeObject.toString();
employeeObject.getClass().getName()+"@"+
   Integer.toHexString(emp.hashCode());

The hash code is deemed as a distinct integer that uniquely identifies an object. That means if two objects have the same hash code, they actually are an alias or the same object. A hash code is internally implemented by converting the internal address of the object into an integer.

Employee e1=new Employee();
Employee e2=e1;
e1==e2;        // true
e1.equals(e2); // true

The Object class contains a variation of the wait() method that is used to pause the currently running thread until some other thread invokes the notify() or notifyAll() method for this object.

The protected finalize() method of the class may be overridden to dispose of system resources or to perform other cleanup operations. However, Java does not guarantee that, on invocation of this method, the garbage collector will be promptly put into action nor is it recommended to explicitly use this method.

There is another protected method in the Object class, called clone(). It is mainly overridden to create a copy of the invoked object.

Wrapper Classes

The java.lang library provides wrapper classes for primitive data types such as int, char, float, and so forth. Primitive data types types of Java are not part of any the object hierarchy; they are mainly there for performance reasons. There are certain disadvantages of primitive types in Java, such as while passing a primitive data type as a method argument, they are passed by value. There is no way to pass them by reference directly. As a result, it is not possible to share the same instance of a primitive type value across multiple methods. The wrapper class helps in creating an object representation of these primitive data types, somewhat like wrapping the primitive type within a class. This is the reason these classes are called wrapper classes.

The hierarchy of the wrapper classes are implemented as follows.

Lang
Figure 1: The wrapper classes hierarchy

The Number is an abstract class, is the base class of all wrapper classes that represent numeric types. It contains abstract methods such as byteValue(), doubleValue(), floatValue(), intValue(), longValue(), and shortValue(), which return the object value according to the different number format such as byte, double, float, int, long, and short respectively.

Wrappers for Floating-point Type Numeric Values

The Double and Float classes are wrappers for floating-point types. Apart from providing numerous convenient methods, these classes provide constants, such as shown in the following instances:

For Double:

public class Main {

   public static void main(String[] args) {
      System.out.println("Maximum Exponent :" +
         Double.MAX_EXPONENT);
      System.out.println("Minimum Exponent :" +
         Double.MIN_EXPONENT);
      System.out.println("Maximum positive value :" +
         Double.MAX_VALUE);
      System.out.println("Minimum positive value :" +
         Double.MIN_VALUE);
      System.out.println("Width in bytes :" +
         Double.BYTES);
      System.out.println("Negative Infinity :" +
         Double.NEGATIVE_INFINITY);
      System.out.println("Positive Infinity :" +
         Double.POSITIVE_INFINITY);
      System.out.println("bit width of wrapped value :" +
         Double.SIZE);
   }
}

Output:

Maximum Exponent :1023
Minimum Exponent :-1022
Maximum positive value :1.7976931348623157E308
Minimum positive value :4.9E-324
Width in bytes :8
Negative Infinity :-Infinity
Positive Infinity :Infinity
bit width of wrapped value :64

Similarly, constant values can be obtained for the Float wrapper class. For details on various methods of these classes, refer to the Java API documentation.

Apart from many convenient methods of the Float and Double class, they have two methods, called isInfinite() and isNaN(). These two methods are implemented based on the IEEE floating-point specification of infinity and NaN (Not a Number). The method isInfinity() returns a boolean true value if the value on which it is tested is infinitely large or minutely small in magnitude. And, the method isNaN() returns the true value if the value on which it is tested is not a number.

public class Main {

   public static void main(String[] args) {
      System.out.println(new Double(1/0.).isInfinite());
      System.out.println(new Double(0/0.).isNaN());

   }
}

Output:

true
true

Wrappers for Integer type numeric value

Integer, Long, Short, and Byte are wrappers for integer types. Apart from providing numerous convenient methods, these classes provide constants such as follows:

public class Main {

   public static void main(String[] args) {
      System.out.println("Maximum value :" +
         Integer.MAX_VALUE);
      System.out.println("Minimum value :" +
         Integer.MIN_VALUE);
      System.out.println("Width in bytes :" +
         Integer.BYTES);
      System.out.println("bit width of wrapped value :" +
         Integer.SIZE);

   }
}

Output:

Maximum value :2147483647
Minimum value :-2147483648
Width in bytes :4
bit width of wrapped value :32

Similarly, constant values can be obtained for Byte, Short, and Long. For details on various methods of these classes, refer to the Java API documentation.

A quick example of the utility of Integer wrapper class is as follows.

public class Main {

   public static void main(String[] args) {
      System.out.println(Integer.toBinaryString(20));
      System.out.println(Integer.toHexString(20));
      System.out.println(Integer.toOctalString(20));

   }
}

Output:

10100
14
24

Wrapper for the char Type

The Character wrapper class wraps around the primitive data type char. It provides several static methods to categorize characters and alter their cases, such as isDigit(), isWhiteSpace(), toLowerCase(), and so on.

One of the intriguing aspects of this class is the support for Unicode characters. Prior to JDK version 5, Unicode characters are held to a 16-bit char size. With the expansion of Unicode characters, the default size of the char became insufficient to hold a character value ranging from 0 to 10FFFF. As a result, the technique of dealing with Unicode characters underwent interesting changes.

To understand how Java deals with Unicode, we must understand three basic terms: The Basic Multilingual Plane (BMP) includes characters between 0 to FFFF. Unicode characters that are beyond 0 to FFFF are called supplemental characters. The code point is a character in the range of 0 to 10FFFF.

Java uses two char types to represent a supplemental character. The first, char, is called a high surrogate and the last one called a low surrogate. A new method, codePointAt(), is provided to translate between code points and supplemental characters. Also, Java overloaded several pre-existing methods that take an integer argument, such as;

static boolean isLetter(int codePoint)
static boolean isDigit(int codePoint)
static boolean isDefined(int codePoint)

The overload form takes int rather than char as an argument because an int type as a single value is large enough to hold two chars.

A Quick Example

public class Main {

   public static void main(String[] args) {
      System.out.println(Character.toLowerCase('A'));
      System.out.println(Character.isDigit('7'));
      System.out.println(Character.isLetter('5'));
      for (int i = 0x03B1; i < 0x03D1; i++)
         System.out.println(Character.getName(i) +
            "=" + (char) i);
   }
}

Output:

a
true
false
GREEK SMALL LETTER ALPHA=α
GREEK SMALL LETTER BETA=β
GREEK SMALL LETTER GAMMA=γ
...

Wrapper for Boolean Type

The class Boolean is a wrapper for boolean primitive types. This class provide methods for converting boolean values to a string and vice versa, along with many useful constants. The main use of this class is when we when we want to pass by reference a boolean data type as a method argument.

Conclusion

The java.lang library has many other important classes and interfaces; also, there are many sub packages. But, when one talks about this package, these two categories of classes must be the first point of discussion. Much like the Object class, the package java.lang is also never explicitly imported, unless of course we are using entities defined in many of its sub packages. One should have a very clear idea about what this package offers because it houses many of the basic classes and interfaces in Java.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories