Introduction to the Java Language

Photo by Tianyi Ma on Unsplash

Introduction to the Java Language

Our previous article provided a general overview of how computers can execute programs. The architecture of existing computing devices was considered, and how programs are converted into machine code or bytecode was discussed. The Java Platform and the .NET Framework were also introduced as examples of development and execution environments based on the concepts of intermediate code and application virtual machines.

This article will focus on the Java Platform and show how a Java program is structured, presenting the concepts of data types, variables, and expressions. The presented concepts will be similar in .NET C#; however, any notable differences are highlighted at the end of the article.

Structure of a Java Program

Every Java program is based on the structure listed below

class Program 
{ // beginning of the Program class
    public static void main(String[] args) // program entry-point
    {
    // STATEMENTS 
    } 
} // end of the Program class

Because Java is an object-oriented language, even the simplest program is inherently a class. Therefore, a program begins with the class keyword, followed by the name we wish to assign to the class – Program in the example. Curly braces are utilised to delimit the beginning { and the end } of a class.

Every Java program must include the main entry point, identified by the main keyword. This is the part of the code first executed by the Java runtime—the Java Virtual Machine (JVM)—when the program is started. As well as a class, the beginning and end of the main are indicated respectively by an open and a closed curly brace. One or more instructions that are part of the main are enclosed in between those curly braces. Generally, a group of (one or more) instructions enclosed between curly braces is called a block.

Single-line comments (shown in grey in the example) are text starting with // that will not be processed by the compiler and hence can be used to add human-readable content to a program.

Data Types and Variables

Our previous article discussed how data is stored inside the computer's memory and is processed by the CPU. Low-level CPU instructions were used to access specific areas of the computer memory via the associated address, load their content into the CPU's registries, and perform the desired data computation.

Developers have a more abstract view of a computer system when using a high-level programming language, such as Java (or C#). They are eased from dealing with those low-level memory management issues. Using variables is a way to reserve specific areas of the memory and make them accessible throughout the execution of a program.

In Java, variables must be explicitly declared for use within a program by specifying an identifier—a label associated with the variable—and a type that describes the variable's content. The following listing shows how four different types of variables are declared, namely, an int able to store integer values (positive and negative natural numbers); a double that can store fractional numbers, e.g., an approximated digital representation of real numbers; a char variable containing a single character; and a String able to store sequences of characters.

public static void main(String[] args)
{ 
    // TYPE variable_identifier
    int a;
    double pi;
    char c;

    String aFriend;
}

It should be noted that data types are represented inside the computer memory in different ways, using a specific amount of space (bits). While int and double variables use 32 and 64 bits, respectively, char variables use 16 bits. These types are known as primitive, as they can be used as the building blocks for other types, such as strings, objects and arrays.

Once a variable has been declared, it can store values via the assignment operator =. This is done by specifying the variable identifier, followed by the assignment operator and the value to be assigned. The example below shows how the variable aFriend is assigned the sequence of characters "James" (note the double quotes). After the assignment, every time the variable aFriend is accessed from inside the program, it will contain the value "James", unless its content was updated by assigning a new value. The variable declaration and assignments can also be combined as a single instruction, as shown in the examples below for a double and a char variable.

public static void main(String[] args)
{     
    String aFriend; 
    aFriend = "James"; 

    double pi = 3.14; 
    char c = 'c';
}

Constant expressions like "James", 3.14 and 'c' are called literals because they literally mean what they represent. Therefore, they differ from the variables, whose value cannot be inferred directly from the associated identifier.

Expressions and Operators

In a high-level language, an expression is an instruction built by combining different variables and literals via one or more operators. The assignment instruction is a simple expression that combines a variable identifier, the assignment operator and a literal (or another variable).

More complex expressions can be written by introducing arithmetic operators. Regardless of the number of involved operands, an arithmetic expression, when evaluated during the program execution, eventually produces a single value. An example is shown in the following listing.

public static void main(String[] args)
{     
    int sum;
    int a = 2;
    sum =  a + 10; // sum will contain 12
}

Even more complex expressions can be built by combining many variables and/or literals via different arithmetic operators, as it is shown below.

public static void main(String[] args)
{     
    double x = 3 / 2 + 1; // x = 2
    double y = 3 / (2 + 1); // y = 1
    int z = 10 % 3 * 2 + 1; // z = 1 * 2 + 1 = 3 
}

The third expression makes use of the % operator, which represents the arithmetic modulo operation. It should be noted that the order of evaluation of an expression follows the arithmetic rules, and parenthesis can be used to modify the priority, as shown in the second expression.

When the / division operator is used within an expression, the performed operation will depend on the type of the involved operands, i.e., the integer division is performed when the operands are of type int.

public static void main(String[] args)
{     
    int num = 10; 
    int den = 4; 
    int result = num / den; // result = 2
}

A non-integer value is produced instead as the result when the operands are of type double.

public static void main(String[] args)
{     
    double num = 10; 
    double den = 4; 
    double result = num / den; // result = 2.5
}

Data Conversion

When arithmetic expressions involve mixed types of numeric variables, such as int and double, some conversions may be performed by the C# compiler.

public static void main(String[] args)
{     
    int a = 2; // a contains 2
    double b = 2; // b contains 2.0 (implicit conversion)

    int c = 18.7; // ERROR
    int d = (int) 18.7; // d contains 18 (explicit cast conversion)

    double e = 2/3; // e contains 0.0 (implicit conversion)
}

As shown in the above example, an implicit conversion is performed automatically by the compiler when, e.g., an int value is assigned to the double variable b. However, assigning a double value to an int variable would produce an error unless an explicit conversion is indicated. While the integer value 2 can perfectly be stored inside the double variable b, the fractional value 18.7 cannot be stored inside the int variable c without incurring a loss of information, namely the decimal part .7.

Casting is a type conversion explicitly indicated inside a program instead of being done automatically by the compiler. It is used to perform a conversion that implies the possible loss of information. The casting is indicated by the notation reported in the example above for the variable d, with the (destination) type to be used for the conversion enclosed in parenthesis after the assignment operator, e.g., = (int).

Logical Expressions

A logical expression is a particular type of expression whose result of the evaluation is a value of boolean type. The boolean type is also a primitive data type whose content can be either the value true or false.

public static void main(String[] args)
{     
    boolean b = true;
}

Logical expressions are built by combining operands, such as variables, literals, etc., with relational and logical operators.

Relational Operators

Relational operators are used to test relationships between variables and/or literals and produce a value of boolean type as the result of the evaluation; hence, they can be used to create simple logical expressions.

x > y // x greater than y
x < y // x smaller than y
x >= y // x is greater than or equal to y
x <= y // x is smaller than or equal to y
x == y // x is equal to y
x != y // x is not equal to y

Logical Operators

More complex logical expressions can be defined, e.g., by combining relational expressions, bool variables or literals via logical operators. Specifically, binary logical operators combine two operands, whereas unary logical operators are applied to a single operand. The logical AND operator && returns true if both the involved operands are true, false otherwise.

public static void main(String[] args) 
{ 
    int x = 3;
    int y = 4
    boolean b =  x > 0 && y < 5; // b contains true
}

The above logical expression was built by combining two relational expressions x > 0 and y < 5 via the logical AND operator. Given the values assigned to those variables, both the left and right operands are true, and hence, the evaluation of the logical expression will also give the result true.

The logical OR operator || returns true if at least one of the involved operands is true, false otherwise.

public static void main(String[] args) 
{ 
    int x = 3;
    int y = 6
    boolean b = x > 0 || y < 5; // b contains true
}

In the above example, the first operand of the logical expression evaluates to true, whereas the second one to false. However, as the final logical expression combines those two operands via an OR, the result of its evaluation is true.

Finally, the logical NOT ! is a unary operator that changes the value of its operand: if the operand is true, the result becomes false; if the operand is false, the result becomes true.

static void Main(string[] args) 
{ 
    int x = 3;
    int y = 4
    bool b = x > 0 && !(y < 5); // b contains false
}

In the above listing, because the NOT changes the result of the second relational expression y < 5 from true to false, the whole logical expression becomes false.

Comparison between Java and C#

All the above concepts are also valid in the context of the C# language. The only notable changes are in the structure of a C# program being as follows:

class Program 
{ // beginning of the Program class
    static void Main(string[] args) // program entry-point
    {
    // STATEMENTS 
    } 
} // end of the Program class

It can be noted that the main entry point starts with a capital letter, Main. Moreover, there are minor differences in how strings and boolean variables are declared. In C#, string variables can be defined using string (lowercase) and boolean variables are declared via the keyword bool.

The next article will show how logical expression can be used as part of selection statements to change the execution flow of a program.