Java Keywords (Part II): Modifiers

I will continue to show the keyword list on subsequent articles. However, I will gray out the keywords previously discussed. Since the eight primitive data types were also previously discussed, I will gray out those as well. Keywords that were mentioned but not discussed, like package will remain listed until properly explained and illustrated.

Java keyword list

abstract continue for new switch
assert default goto* package synchronized
boolean do if private this
break double implements protected throw
byte else import public throws
case enum instanceof return transient
catch extends int short try
char final interface static void
class finally long strictfp volatile
const* float native super while
Keyword marked with an asterisk (*) are keywords that, although valid, are not used by programmers.
Although the list above is arranged in alphabetical order, I will go through them in a different order.

Modifiers

Aside from the already discussed access modifiers, there are four other modifier keywords in Java. These are: abstract, final, static, and strictfp. I will not discuss the strict floating point behavior keyword (strictfp) at this time.

Method declaration

The keyword abstract is used in both the method declaration and the class declaration.

public class MyClass
{
    public void myMethod()
    {
        // Does something
    }
}
In this example, my class has a single method called "printHelloWorld". As in the case of class declaration, a method declaration starts with an access modifier. If no access modifier is present, the method has "default" access. The line public void printHelloWorld() {...} is the method declaration. The only required elements of a method declaration are the method's return type, name, a pair of parentheses, (), and a body between braces "{}". The image below illustrates this, but also including "public" access modifier.

This is the simplest form of a method declaration. There are other components that could be added to a method declaration that will be discussed in other sections or in other articles. For now, let us discuss the usage of the keyword abstract.

abstract

The word "abstract" is defined as something existing in thought or as an idea but not having a physical or concrete existence. When this keyword is used in a method declaration, it means basically exactly that. An abstract method is a method without body. Using the keyword "abstract" in a method with body is a syntax error. Syntax errors will cause your program not to compile. Compilation is the process of converting your .java files (text) into .class files (byte code). I will discuss this process at a later time.

public abstract class MyClass
{
    public void myConcreteMethod()
    {
        // Does something
    }

    public abstract void myAbstractMethod(); // DO NOT forget semicolon at the end of an abstract method declaration!
}

The opposite of an "abstract" method is a "concrete" method. A concrete method is a method with body (an implemented method). One question remains, what good is a method without body? A method without a body cannot be used because it will not be able to do anything. Therefore, abstract methods must be defined by someone before they can be used. But before we get to that, notice how the class declaration in the example above also contain the keyword abstract. This is a safeguard feature added by the Java language. When a class contains at least one abstract method, the class definition MUST contain the keyword abstract. When a Java class contains the keyword abstract, it is said to be an abstract class. Here are two basic rules for declaring a class "abstract":

  1. A class MUST be abstract if at least one method is abstract
  2. A class CAN be abstract even if no method is abstract

Confusing? Let me show you code examples for these rules...


// Valid: A class MUST be abstract if at least one method is abstract
public abstract class MyClass
{
    public abstract void myAbstractMethod();
}

// Valid: A class CAN be abstract even if no method is abstract
public abstract class MyClass
{
    public void myConcreteMethod()
    {
        // Does something
    } 
}

The benefits of abstract classes will be discussed in a future article. In order to fully grasp this concept, we must discuss the concepts of inheritance and class constructors. Since this article is strictly about modifier keywords, it doesn't make sense to dive in further into these topics. That said, keep in mind that there is a bit more to discuss regarding abstract methods and classes. For now, just now that an abstract class, just like an abstract method, cannot be used (constructed in the case of classes) until that abstraction is realized (made concrete) by someone else. I promise this will become very clear when inheritance is discussed.

final

This keyword has three distinct uses:
  1. When used in a class declaration, it means that the class cannot be extended.
  2. When used in a method declaration, it means that the method cannot be overridden.
  3. When used in a variable declaration, it means that the variable is a constant. (constant variable?)
In order to discuss the first two point, we must discuss inheritance. As I already mentioned, this topic will be introduced in another article. Just remember these three rules for its usage. For now, let us discuss point #3 above. I have always been disturbed by the use of the word "variable" to indicate in general terms a data member of a class. And the reason why this disturbs me is because something that is "variable" can change. On the other hand, something that is "constant" doesn't change. So, how can something be a "constant variable"? I don't have a good explanation for this, other than the term "variable" has been widely accepted in the software industry to refer to data members of a class; regardless of whether their values can change. Even the official Java documentation uses this term. If it helps you understand, let just use the term variable for data members which values can change and term constant for data members which values cannot.

As already mentioned, the keyword final when used in a data member declaration, it means that the data member is a constant. Java is a language that is strongly based in C++. Because of this, the keyword const was reserved, but never implemented. At this point, there is almost a zero chance that it will ever be implemented in the Java language. The concept of const in C++ goes beyond declaring a data member as constant. In Java, to declare a data member as "constant", we simply add the keyword final to the declaration.


public class Student
{
    private int grade; // variable
    private final String studentId; // constant

    // Methods omitted
}

Assuming that a student ID assigned to a student can never be changed, the class "Student" above should make perfect sense: I modeled a person with a variable for "grade" and a constant for "studentId". If you are a high school student, your student ID has not changed ever since you were admitted into your current school district; even from elementary school. If you are a college student, your student ID has been the same since you were admitted into your current university or college. The code example above have two data members declared, but no values have been assigned yet (other than default values which will be discusses later).

Assigning a value to a constant data member, must be done in one of two ways: at development time, or at execution time. Assigning a value at development time means assigning the value right at the point where the constant data member is declared. This is shown in the code below.


public class Student
{
    private int grade; // variable
    private final String studentId = "D12345678"; // constant

    // Methods omitted
}
To explain how to assign a value to a data member (in this case a constant) at execution time (also known as "runtime"), I need to explain briefly how to create objects. For now, let us assume that the only way to create objects is by invoking a special method called "constructor". The constructor of a class is a method that:
  1. Its name is the same as the class that contains it, and...
  2. Has no return type
Let us review a new version of my "Student" class that has a constructor.

public class Student
{
    private int grade; // variable
    private final String studentId; // constant

    // class constructor
    public Student()
    {
        studentId = "D12345678"; // value assigned and cannot be changed
    }

    // Methods omitted
}

There are many rules to class constructors. And those merit a dedicated article. For now know that every class MUST have at least one constructor. Therefore, if you do not define a constructor inside a class, the Java compiler will create one for you that is known as the "default constructor". This "default constructor" is similar to the constructor shown in the code above except the body of the method will be empty. The meaning of this will be explained later when discussing "Constructors".

So far, none of the examples shown are good. The reason being is that if this code was used to create many distinct students, each one will have the same student ID. In order to use constant data members effectively, we must inject the value we want to assign to our student object via the class constructor.


public class Student
{
    private int grade; // variable
    private final String studentId; // constant

    // class constructor
    public Student(String value)
    {
        studentId = value; // value assigned and cannot be changed
    }

    // Methods omitted
}

public class Registrar
{
    public static void main(String[] args)
    {
        Student janeDoe = new Student("D12345678"); // Jane Doe has a permanent student ID
        Student johnDoe = new Student("D98765432"); // John Doe has a permanent student ID
    }
}

The new operator invokes the object constructor. In this example, the "Student" class contains a single String argument. The call to the Student constructor must pass the same number of arguments, in the right order, of the correct type. This example passes a single "String" argument for each Student object created. But this is not the only thing that the "new" operator does. This will be discussed in detailed when discussing the topic of "Constructors".

static

The use of this keyword applies only to data members, methods, and nested classes. Using this keyword in an outer class declaration will result in a syntax error. The meaning of the keyword in all instances mean the same thing. Non-static data member, methods, and nested classes belong to object instances whereas static members belong to the class itself. I know this does not mean much at this time. It might make it simple to see how they are declared and how they are used. Because the rules are the same, I will use static and non-static methods and data members to illustrate this point.

public class MyClass
{
    public int nonStaticVariable = 1;
    public int staticVariable = 4;

    public void myNonStaticMethod()
    {
        // Does something
    }

    public static void myStaticMethod()
    {
        // Does something
    }
}

public class MyClassDemo
{
    public static void main(String[] args)
    {
        MyClass.myStaticMethod(); // calling a static method
        int x = MyClass.staticVariable; // using a static variable
        MyClass object = new MyClass();
        object.myNonStaticMethod(); // calling a non-static method
        int y = object.nonStaticVariable; // using  non-static variable
    }
}

Because static members belong to the class itself, creating an instance of the class to access its accessible members is not necessary. To access static members simply use the class name followed by a period (.), followed by the static member name. To access non-static members, an object of that class must be created first. Then, using the created object, access the members by using object's name followed by a period (.), followed by the non-static member name (see code above).

When should I use static members?
Static members should be used when the member's value (or behavior in the case of method) shall remain the same across all instances AND in the case of variables, a change in value is desired to affect all object instances. Again, this is simple to understand by using a real-world example. I am going to model a "Car" using a class.

public class Car
{
    private static String make = "Ford";
    private static String make = "Fiesta";
    private final String color;

    public Car(String value)
    {
        color = value;
    }

    public void rebrand(String newValue)
    {
        make = newValue;
    }

    public void showBrand()
    {
        System.out.println(brand);
    }
}

public class FordFiestaProductionLine
{
    public static void main(String[] args)
    {
        Car myCar = new Car("white");
        Car yourCar = new Car("red");

        Car.rebrand("Mazda");

        // myCar and yourCar are now "Mazda"
        myCar.showBrand();
        yourCar.showBrand();
    }
}

As you can see, using static data members can be very convenient when a particular behavior is universal across all instances of a class or a particular attribute of a class is shared across all its object instances. In the example above, the production line for Ford Fiesta produces the same make, the same model, but with different colors. If Ford is acquired by Mazda, it will not make sense to rebrand all vehicles individually (even though in the real-world there is no way around it). In programming, it makes more sense to apply this change to the template (class) and all created instances will immediately adopt the change.

Summary

In this post, I discussed the modifier keywords abstract, final, and static. In the process, I had to introduce the concepts of method declaration, as well as declaration and invocation of a class constructor. Lastly, there is yet an additional use of the keyword static that will be cover later. This is because in order to discuss this usage, I need to introduced other keywords. For simplicity, it is better to leave this usage out of this article for later discussion.

Next up, Part III: Returning Values from Methods

Comments

Popular posts from this blog

Implementing Interfaces with Java Records

Customizing Java Records

Exception Handling: File CRUD Operations Example