Types, Values and Variables

Primitive Types, Literals and Variable Definitions

All Java programs require values. Every value has a type. Some types are primitive, but the ma­jo­ri­ty of types are reference types, i.e. class types. Specialised simple types include enu­me­rat­ed types. We start by looking at some useful types, then we discuss literals of that type, followed by some practical variable definition syntax and use.

Getting Started with Java

Statements and Functions

PREREQUISITES — You should already…

Some Groundwork


We cannot have a consistently meaningful discussion about a typed language like Java, without some understanding of ‹type› as an abstraction, and which types are available in Java. Since types are at the core of every part of the language, you cannot expect to understand everything all at once, so we will gradually add more and more information about types.


Java™ has literals to represent anonymous immediate values, and each literal has a type. Some abstract literals have names, like null, true and false in Java. Every literal is an ‹expr›ession. Character literals and string literals are distinct in Java, while class literals are also possible. With certain limitations, functions can also be represented literally as anonymous functions, called lambdas.

Expression Concepts

An expression is any language construct that results in, or is, a value. Since all values have types, it follows that every expression has a type. An expression can be very simple, or very complex, since operators can appear (only) in expressions.

It is important to understand that every expression:
a) has a ‹type›, and
b) has a ‹value›.
You cannot just think of an expression as a value. To ignore the type, means you ignore the valid­ity of the value in the context where it is used, and the operations which can and cannot be per­for­med on the value.

In formal discussion and syntax, we will indicate expressions with: ‹expr›.


You can use single-line comments, starting with //, anywhere in Java source code. All text start­ing with the two forward slash characters is ignored, up to the end of the line.

Block comments start with the /* token, and end with the first */ token seen. This means block comments can span lines, but cannot nest.

Special comments, which are the only ones processed by the javadoc tool, are called, un­sur­pris­ing­ly, Javadocs. They must appear directly before a class or method, and must start with: /**. We shall discuss these in more detail later.

Inside string literals, comment characters are not tokenised. This means they are not processed as meaningful, and will then not be treated as comment start or termination sequences.


Before long, you will need to create names for classes, methods and variables. These names are formally called ‹ident›ifiers, which we will represent as ‹ident› in syntax specifications to keep it short. Programming languages have rules about the characters that may be used in identifiers.


  • Cannot be a keyword.
  • Are case-sensitive.
  • First character must be: _, az, AZ.
  • Subsequent characters can be: _, az, AZ, 09.
  • Single underscore on its own is not allowed (JDK 9).

Apart from the last point, these are the same rules employed in many C-like languages. To be completely honest, you can also use $, but you should not; and the alphabetic characters can actually be any Unicode alphabetic character, but you should not.

Without going into the detail, you create your own identifiers in the following situations:

SyntaxIdentifier Locations

  • classident{} class name.
  • interfaceident{} interface class name.
  • enumident{} enumerated type name.
  • type› ‹ident(param) {} method name.
  • type› ‹ident; variable name.
  • finaltype› ‹ident=value; symbolic constant name.
  • paramtype1> ‹ident1,type2> ‹ident2,….

The final is often used as static final. In Java, unlike C, C++, C# and some other similar languages, the static modifier is never legal inside a function body; final is allowed however.


To save space, we do not usually include boilerplate code. So all examples where there is no main function, assume you understand that the code will only work within the following structure in some .java file:

Example.javaExample Boilerplate

We often use the format() function (or method) of System.out in examples. You may use printf() instead… it simply calls format. It works just like the format() function of the String class. The basic principle for both methods is that the first ar­gu­ment to pass, is a format string, potentially con­tain­ing format specifiers, all starting with %. These format specifiers act like placeholders where for­mat­ted arguments following the first for­mat string argument, are placed in the output. To format an ac­tu­al per­cen­tage sign, you have to use two: %%.

1) string: ABC, int: 123, float: 456.789000
2) string: ABC, int: 123, float: 456.789000

Notation for Types and Values

To shorten the sentence: “the expression ‹expr› has type ‹type›”, we shall use the notation: ‹expr─t→type›. Similarly, for the value of the ‹expr›ession, we will use: ‹expr─v→value›. For ex­ample, consider a variable V of type int, having the value 123:

In discussions about V, we will represent its ‹type› as : V ─t→ int and its value as : V ─v→ 123. On rare occasions, as part of an expression, we may write Vt, or Vv to represent the ‹type› and ‹value› respectively.


Fundamental Types

Even for simple programs, we have to be aware of types, since every value, every variable, every function return — in other words, every expression — has a type, even if it is an abstract type called void. A type determines the storage space, and the operations that can be performed on the type, including which methods the type may or may not expose.

SyntaxUses for Types

  • type› ‹ident; define member or local variable.
  • type› ‹ident=expr; define member or local variable and initialise.
  • [static] finaltype› ‹ident=expr; define symbolic constant; static only usable at class level.
  • (type)expr› or (type)(expr) type conversion (cast) operator.
  • newtype(arg) allocate and initialise new object (constructor call).
  • type› ‹ident(param) {} define function ‹ident›, returning ‹type›.
  • arg ‹arg›uments passed to function

Even when you define ‹param>eters for a method, types come into play. Types are literally every­where — almost every line in Java either has a type name, or contains an expression, which has a type.

TIPString Representation of a Type

You can get the string representation for the type of any ‹obj›ect or ‹expr›ession with: ‹obj›.getClass().getName(), or (expr).getClass().getName() — as long as it is not a primitive type. IDEs normally do this automatically for you, and represent it in popups. You can also use getSimpleName() instead of getName().

Primitive Types

The primitive types correspond closely to types you will find in C/C++, i.e., native types. They are not objects, and thus have no methods or properties. They are also not dynamically allocated. The primitive types are:

boolean (true/false)
short (16-bit signed integer)
char (16-bit unsigned integer used for 16-bit Unicode characters)
int (32-bit signed integer)
long (64-bit signed integer)
float (32-bit IEEE 754 float)
double (64-bit IEEE 754 float).

Here are a few examples where we use the previous syntax examples, replacing ‹type› with one of these primitive types:

Example.javaWorking Example

The last line cheated a bit, because it used a primitive wrapper, which is not a primitive type. We are representing patterns here, where you can use ‹type›s; it is not (yet) about variables or func­tions. You should practice the patterns we used.

Primitive Wrappers

To make the primitive types act more object-oriented, you can box a primitive value in a cor­re­spon­ding wrapper class. This means a variable with the wrapper type can also be assigned null — we say it is nullable, when this is possible.

The fully-qualified names of all the following wrapper classes start with java.lang, which is al­ways automatically imported when compiling Java programs, which is why we almost never write, for example, java.lang.Integer.

boolean Boolean
char Character
short Short
int Integer
long Long
float Float
double Double

We could (should) have used 123L (a long) instead of 123 (an int), but Java will automatically promote an int value to a long value. We can also see that Java will implicitly convert Long to long, and vice versa. The only real impact is that L = null is legal, but not l = null.


All classes inherit from the java.lang.Object class, or just Object for short. Practically, this means that all objects, of any type, have the same methods you find in Object. The other char­ac­ter­is­tic to be aware of, is that a variable of type Object can store a reference to any type of ob­ject, without an explicit cast. This is an OOP feature that we will consider at a later stage.

Inspecting the list of methods that Object provides, you should notice toString(), which most classes override when they inherit from Object. Again, the detail of inheritance is not par­ti­cu­lar­ly relevant here now, but you must understand the implication: all objects, of all types, have a toString() method. This means that any type will produce some string representation of itself, when you call toString() on it.

1) obj = 123                      4) dbl = 123.456000
2) obj = ABC                      5) dbl = 123.456
3) obj = 123.456

The other important method is equals(). This is the only sure way to compare two objects for value equality. Although much of the following syntax we have not yet discussed, understand the spirit of the code, which shows that the equality operator does not always work the way you ex­pect, but that equals() is always safe:

1) l1 != l2                    3) l1 != l2
2) l1 == l2                    4) l1 == l2

Remember, the main point here is that using ‹obj.equals(expr), which all types inherit from Object, is the safest way to compare the values of objects. The equality operator may or may not compare the values, and is more likely to compare the references to the values. Comparing ref­er­en­ces is useful, but not if you think you are comparing values.

String Type

The String class is not a primitive type, but it is almost impossible to write a Java program with­out using String, since even string literals have type String. Java provides some convenient short­hand syntax, so it acts in many ways, like a primitive type. Always remember that String is an immutable type.

It is possible to “convert” any type to some String representation of its value, with the over­rid­den toString() available on all types, since all types inherit it from Object. This makes String, and strings in general, ubiquitous. All strings have type String, so we will not always remind you of that fact from here on.

Although we discuss string literals later, here are some examples of using the String class, so that you have some initial patterns to experiment with:

1) str  = "ABC"
2) str  = "DEF"
3) str  = "GHI"
4) str  = "GHIJKL"
5) str  = "GHIJKLMNO"
str len = 9

Most types are implicitly convertible to String, which is why we did not have to explicitly cast the result of the str.length() expression, which results in an int. The length() method is one of many instance methods of type String. No instance or static method of String can modify the original value — they all return modified copies of the string, because of String’s immutability.

It is not necessary to use: new String("ABC"), since just using "ABC" will suffice for convenience. Nor is it common to place characters in an array, but it is a way to create a new String.

Strings can be concatenated with the + operator, while the compound assignment: += is more con­ve­nient. Because Strings are immutable, concatenation can be expensive. You should thus rather use the java.lang.StringBuilder class, which is very efficient for concatenation, es­pec­ial­ly if you concatenate long strings, or concatenate multiple times.

String Literals

As you may have seen from the examples, a string literal is an immediate value of type String, and uses a set of paired double quotes to delimit a sequence of 0 or more characters. A literal string with no characters is an empty string: "".

At a more fundamental level, a literal string becomes an array of char values, which is implicitly convertible to type String. Just like character literals, escape sequences may appear inside a string literal.

Escape Sequences

To represent special characters as a literal, they must be escaped. The start of the escape se­quence is the backslash character (\). This is applicbable to string literals, as well as character literals. And so, to represent a single quote as a character literal, you cannot write: ''', but must write: '\'' instead. Similarly, to represent a backslash character as a literal, you must write: '\\'. For string literals, you similarly cannot write """ to represent a double quote character; instead, you must write: "\"".

The following special escape sequences represent unprintable characters:

\b (back space)
\t (tab)
\n (new line)
\r (carriage return)
\f (form feed)

Although it is possible to represent any character code between 0 to 255 with octal (\000\377), e.g.: '\033' for ASCII 27 (1B in hex), you should generally use the Unicode escape sequence. The Unicode escape sequence is a \u token, followed by exactly 4 hexadecimal digits, e.g. \u2500 (Unicode horizontal line-drawing character), or \u001B for ASCII ESC (27).

The escape sequence: '\"' is legal with a character literal, but not necessary; using '"' is also legal. But it is required in string literals to represent a double quote character.

JDK API Documentation

Due to the large amount of information available, you will forever remain dependent on the JDK SE 8 Documentation, whether you peruse it online, download the documentation for offline use, or use API viewers like Dash (MacOS only), or Zeal (Windows and Linux); or even the same doc­um­en­ta­tion in your favourite IDE.

You have to start using the formal documentation as soon as possible, so that you can get used to the terminology and layout. Using the java.lang.String class as an example, there are some important points to note.

Javadoc — java.lang.String API
Javadoc — java.lang.String API
base class

The base class provides members that are also available in a derived class, but not documented in the derived class (although the Javadoc does provide a link). To fully understand what a class is capable of, you have to know what it inherited from its base class, the java.lang.Object class in this case. The other benefit is that you can pass this class to a parameter (or store it in a var­iable) of the base class, without an explicit cast.


Most classes in the JDK SE framework implement a number of interfaces. Practically, for you, this means you can pass String, for example, to any parameter that expects one of the interface types shown, without any explicit type conversion — as if they are base classes. For that to work, String has to implement certain methods and behaviour, and you can see interfaces almost as a category, or set, of methods that are guaranteed to be available.

For example, the java.lang.Comparable<String> interface means that Strings can be compared by value, and passed to any method that expects a Comparable type. It also implies that Strings have a compareTo(String) method.


The constructors initialise new objects. They are methods, which mean they can be overloaded, which is why the documentation will list a number of them, all having different types and num­bers of parameters. Constructors are special methods that can only be called by new (directly or indirectly), and have the same name as the class (String in our example case).

From the String class’ Constructor Summary, we can see that we can initialise a String object in a number of ways, simply by passing the correct number and type of parameters. The first one in the list, for example, indicates that we can even create an empty string by calling: ‘new String()’ with no parameters.


Methods of a class like String determine what you can do with an object of type String. The methods will either be static methods, in which case you can only call them via the class name: String.method(args); or instance methods, in which case you must call them via an object: ‹obj.method(args).

For example, the charAt(int) method is an instance method, which means we first need an object of type String, before we can call it. On the other hand, the format(String, Object…) method is a static method, so we only have to call: String.format(args) to use it.

Numeric Literals

Numbers are obviously ubiquitous in programs. Java allows us to specify integer literals and float­ing point literals. To simplify the language, Java does not provide a syntax for unsigned integer types, although a char is a 16-bit unsigned value supporting all integer operators. There are methods on Long and Integer wrapper classes that can help, if you really need an unsigned re­pre­sen­ta­tion.

IMPORTANTAvoid Magic Numbers

You should understand that randomly sprinkling your code with anonymous numeric literals, called “magic numbers”, should be avoided — rather create a symbolic constant with that value. Then it has meaning and can easily be changed.

Character Literals

This is always a bone of contention, but there is no escaping the fact that char is demonstrably a numeric type; to be more precise, a 16-bit unsigned numeric type. We seldom use it as a number, however; we more commonly use it to represent a single 16-bit Unicode character.

A character literal is a token enclosed in single quotes, e.g.: 'A', having the value 65 in memory.

a.1) c1 =  A, c2 =  A
a.2) c1 = 65, c2 = 65
b.1) c1 =  C, c2 =  C
b.2) c1 =  C, c2 =  C
b.3) c1 = 67, c2 = 67

Arithmetic on char type values always results in int, which is why we had to cast the int results back to char for assignment. Integer literals are implicitly cast to char, although you could cast it: char c2 = (char)65;. Notice that format’s %s formatting specifier will also print a char as a string with one character.

Character literals may contain escape sequences, just like string literals. The only required se­quen­ces are: \' to represent a single quote char, and of course \\ to represent a single back­slash character.

Integer Literals

Unadorned integer literals are presumed to be in decimal notation (base 10), and have type int, a signed 32-bit number, which can range from -2147483648 or -231 to 2147483647 or 231-1. An underscore character can be used to visually separate groups of digits, but cannot be the first character.

An L suffix can be applied to an integer literal, in which case it will have type long, which is a signed value ranging from -263 to 263-1.

As you can see, integer literals of type int can be assigned without a cast to variables of type long. This is called a promotion. The reverse would mean a demotion, and can only be explicitly performed with the cast operator:

If you want to use octal notation (base 8) for your integer literal, simply start it with the digit 0. This is a brain-dead legacy inherited from C. Notation does not affect the type of the literal. The only valid digits in octal are: 0-7.

For hexadecimal notation (base 16), you can prefix 0x or 0X, and the valid digits must be in the range 0-9, a-f (or A-F). Again, the notation does not affect the type of literal.

For binary notation (base 2), simply prefix the literal with 0b or 0B. The range of digits can only be 0 or 1.

To convert an internal number to a string representation using decimal, octal, hexadecimal, and binary notation, you can use the following pattern:

0027	001B	0033	11011

You can experiment, and change the 04 parts to other values. Start by removing the 0 and inspect the output. To print long values, you use exactly the same format specifiers. Experiment by only changing int to long in the snippet above. The rest of the code will still work.

Floating Point Literals

When a decimal literal contains a decimal point, it has type double (64-bit IEEE 754 float), unless suffixed with an f or F, in which case it will have type float (32-bit IEEE 754 float). This re­sem­bles at binary level the formats natively supported by modern CPUs.

Floating point literals can use fixed point notation, or exponential notation (also known as scientific notation).

Never use float — it has much lower precision, and round-off errors accumulate much faster, while its only benefit is that it uses less memory, which is rarely a concern.

You can format floating point output with System.out.format:

Value is: |123.45600000|
Value is: |123.4560|
Value is: |123.46|
Value is: |123|
Value is: |123.45600000|, |123.45600000|
Value is: |    123.4560|, |123.4560    |
Value is: |      123.46|, |123.46      |
Value is: |         123|, |123         |

With a little experimentation, you can see how the ‘.numf’ part influences the output, and sim­il­ar­ly the value of 12 determines the total output width, filling with leading spaces if the con­ver­sion is too short. Also notice the effect of -12, in which case the spaces go after the conversion.

SyntaxFormat Specifiers

  • %[‹arg-index$][‹flags›][‹width›][.precision›]‹specifier
  • arg-index$ offset of argument to format after the format string.
  • flags 0 to zero-fill numbers; + to always include sign; - to left-align.
  • width total output width; fill left with spaces or 0 if output too short.
  • .precision number of decimal digits to format (rounds).
  • specifier one of: d, c, b, s, f, e, x, h or % (to get a % sign).

The output will not trunctate, so if the formatted item is wider than ‹width›, it will simply overflow.

Simple format/printf Specifier Syntax
Simple format/printf Specifier Syntax

The decimals part, if present, must start with a decimal point, and is only allowed when a floating point arguments is passed (and the specifier is an f, of course). At minimum, there must be a percentage signed, followed by one of the specifiers.

If the sequence after the % starts with a +, and the format specifier is for a number, the number will always have a sign (+/-). Alternatively, in this position, you can use 0, which only works for the integer format specifiers, and instead of filling the remaining output field width with spaces, will fill it with 0 digits.

The x specificier can be a capital letter, in which case the alphabetic digits (A-F) will be formatted in upper case, which normally is output as lowercase digits (a-f).

TIPPrimitive Type Names in String Representation

You must get a bit sneaky to print the name (the wrapper class name, to be exact), of a primitive type variable or expression. You first have to box it, by casting it to an Object type, then you can use getClass() and getName() or getSimpleName():

In the last statement, we did not use an intermediate variable, but simply used the result of the cast operator (boxing) directly.


Although we have seen variables, particularly local variables, in several examples above, we now have enough background to describe variables and options more specifically. Variables store dif­fe­rent values at different times, as long as the values are of the correct ‹type›.

Except for primitive types, variables store references to the actual data their type represents. The data is called an object, and is formally “an instance of a class”. We repeat, a variable generally has a name, has a ‹type›, and stores a reference to an object of that ‹type›. Apart from con­ven­ience syntax, objects are dynamically created with new, directly or indirectly (like calling a method that calls new for you).

SyntaxLocal Variable Definitions

  • type› ‹ident; local variable, (or instance variable).
  • type› ‹ident= ‹expr; local variable, (or instance variable), with initialisation.
  • type› ‹ident1,ident2,; multiple local variables, (or instance variables); one, more or all, can be initialised as well.

Primitive Type Variables

The simplest kind of variable is one that stores a value of a primitive type. In this case, the var­iable itself stores the actual value, since values of primitive types (which are not classes) are not objects.


If a variable has been defined, but not initialised, you cannot use it in an expression. You must first assign a value to it. This is accomplished with the assignment operator. Because it is an op­er­ator, and because it associates with its operands from right to left, we can chain assignments.

If we tried to use i before it was assigned a value, the compilation would have failed with a mess­age like:
error: variable i might not have been initialized
Very polite, but still an error.


All the numeric primitive types support the normal arithmetic operators:

+ (addition)
- (subtraction)
* (multiplication)
/ (division)

They have the expected precedence. When you mix the types of their operands, their results will be the type of the operand with the largest type. In the example below, the value of i, which is an int, is first converted to double before the multiplication is performed, so the result is a double:

A fair number of static mathematical functions are available in the system.lang.Math class. Two mathematical constants (final fields) PI and E are also found there.

Be careful with floating point arithmetic, since floating point numbers in a computer are more of­ten than not approximations of a value, which is much coarser for float than double. Either way, every operation on floating point values produces round-off errors as a result, which may ac­cu­mu­late. This is one reason why you should never use double to represent currency — rather use the standard java.util.Currency class, or the java.math.BigDecimal class.

Here are some simple examples of the approximate nature of floating point; keep in mind that in real code, with real numbers, this gets worse fast.

1.1999999999970896       0.9999999999999999       0.8999999999999999

For scientific and engineering applications, the margin of error might be sufficient, if kept under control. All programmers working with serious floating point applications are always encouraged to read “What Every Computer Scientist Should Know About Floating Point”.

Integer Operations

The integer primitive types, i.e.: short, int and long, support a number of additional operations that are exclusive to the integer types.


The modulus or remainder operator: %, is only available for integer types. It produces the re­main­der of an integer division.

Bitwise Operators

The bitwise operators perform low-level logical operations, where each bit in an integer value is treated as “false” (0) or “true” (1). This means each bit in the result is determined from the cor­res­pon­ding bit in the operand(s).

~ (complement - NOT)
| (bitwise OR)
& (bitwise AND)
^ (bitwise XOR)
<< (bitwise left shift)
>> (bitwise right shift)

BitWise.javaBitwise Operator Example
  1111000010100011         1111000010100011        1111000010100011
& 1101010101010101       | 1101010101010101      ^ 1101010101010101
= 1101000000000001       = 1111010111110111      = 0010010111110110

With the bitwise operators, you can set, clear, or check any bit, or pattern of bits, in integer values.


For integer types only, we also have the pre/post-increment and pre/post-decrement operators. It is important to understand that the pre/postfix versions both perform the same task; they only differ in the result they return.

If you have no need for the return value, it does not matter in any way which one you use, but people tend to have a preference.


We can perform comparison operations on all these types, all of which produce boolean results:

== (equal to)
!= (not equal to)
< (less than)
> (greater than)
<= (less than or equal)
>= (greater than or equal)

These operators are all on the same level of precedence, but at a lower level than the logical op­er­at­ors below.

Keep in mind that you should never ever compare floating point values for equality. Rather take the absolute value of their difference, and see if the value is small enough for your app­li­ca­tion.

Logical Operators

On boolean expressions, we have the logical operators, where ! has the highest precedence, fol­low­ed by && and then ||.

! (logical NOT)
|| (logical OR)
&& (logical AND)

The || and && operators perform short-circuit evaluation. This means they do not evaluate their right-hand operand, if the answer can be obtained by evaluation of the left-hand operand. For example, in the expression: ‘false && X’, X is never evaluated, because “false AND ‹anything›” will always be “false”.

LOGICAL AND (&&)             │   LOGICAL OR (||)       
false && false: false        │   false || false: false 
false && true : false        │   false || true : true  
true  && false: false        │   true  || false: true  
true  && true : true         │   true  || true : true  

Exercise: See if you can use the following function to print the above tables in colour.

Conditional Operator

The comparison and logical operators can be combined with the conditional operator, which is an operator that takes three operands. It will only evaluate two, of which the first operand is one. The first operand must be a boolean expression, and depending on its value, either the second, or the third operand will be evaluated, and whichever one is evaluated, becomes the result of the operator.

SyntaxConditional Operator

  • bool-expr?exprT:exprF
  • bool-expr any expression resulting in a boolean value.
  • exprT evaluated only if ‹bool-expr› returned true.
  • exprF evaluated only if ‹bool-expr› returned false.

Since all the operands can be complex expressions, and can even contain other operators, the con­di­tion­al operator should be used with care. Here is an example of how not to use it:

The above code snippet will determine which of the years between 1999 and 2012 inclusive, are leap years or not. It is, at least, an exercise in using a fair number of operators! Just to set the record straight, the following is a better solution. Even though it uses some statements you have not yet learned about, it should still be more readable:

leapyear.javaDetermine Leap Years

You can check the leap year algorithm at Wikipedia, and learn more than you wanted to know about calendars and leap years in the process. Or you can just use the isLeapYear static method from java.util.GregorianCalendar, and save yourself some pain.

Compound Assignment

In programming, we often find the pattern: ‘‹variable=variable› ‹op› ‹expr;’, where ‹op› is a binary operator (taking two operands). For example:

This pattern can be simplified to: ‘‹variable› ‹op=expr;:

Clearly, this is much more succinct, and furthermore, avoids naming redundancy, which is good pro­gram­ming practice in general. Consequently, you are encouraged to use these compound as­sign­ment operators.

Reference Type Variables

The syntax for defining variables that are not primitive types, is exactly the same as for primitive variables. The difference lies in the values used to initialise or assign to reference type variables; in other words, the nature of the ‹expr›essions in the syntax.

Apart from some shorthand, and implicit conversions, you have to call new to:
a) dynamically allocate memory, and
b) initialise the object by choosing an appropriate constructor.

The type of the value stored in q is not a double, even though 23.45 does have type double. Java first converts the double to Double, and then assigns the reference to q.

For fundamental types, this does not seem to be a problem: “it just works”. And because String values are immutable, we cannot even show the one potential problem with reference types (and the reason why String values are immutable). So we will illustrate with another type: the very useful and mu­ta­ble java.lang.StringBuilder class. This class is very efficient when you either concatenate long strings, or concatenate multiple times.

sc == sb

When you run this code, you will see that both sc.toString(), and sb.toString(), return the same value (the toString() is automatically called when we concatenate these variables to a String). The point is that both sb and sc reference the same object in memory. The initialisation: ‘sc = sb’ only copied the reference to the object, from sb to sc. If you want a copy, you have to al­lo­cate more memory (continuing from above code):

sc != sb

Now, you will see that only the object that sc references has changed. The new object stored in sb, created from the original sc, remains untouched. This feature of mutable reference types is not a problem, and in fact is quite efficient, but it is something you must be aware of. If a type is immutable, you are still copying only references, but it is never a problem, because if you want to modify the referenced immutable object, you are forced to create a new object (with potential mo­di­fi­ca­tions).

Not a single one of the instance methods of the String class can modify the object they are call­ed on, even if the method name seems to suggest that it does. All methods that return a String, return a copy. That means that immutable types are safer than mutable types, but some­times a bit of a pain.

Shared Class Variables

Since local variables are only visible inside the method body block, or compound statement block, in which they have been defined, and since they only live as long as the function has not returned, they are somewhat limited if we require a longer lifetime, or a larger scope.

To solve that problem, Java allows you to define, at class level, two kinds of fields:

We will revisit instance fields later, when we cover encapsulation. For now, we will discuss static fields. Any member is visible right through the class, regardless of where in the class you have defined the member. You can thus organise the order of the members as you see fit, but you should group fields together.

Fields that are static have a global lifetime, which means they exist as long as your program is running. They are automatically initialised before your code in main starts running. You can use a special static initialiser construct for non-trivial initialisation of static fields. It, too, is called before main.

SharedFields.javaExample of Shared Fields
In static initialiser
In main() now

If you were to move the static fields to the bottom of the class, it would make no difference. And if we had more functions in the class, they all would have been able to access and modify the same static fields (except for the final one, of course).

Final Variables

We have been using final “variables” in a number of examples now, so this is really just a sum­mary. We use the term “variables”, because that is what they are called, but they do not live up to the term, since their values are fixed after initialisation, and can thus not “vary” (change).

Only class-level final variables can also be static. Inside a method, you can use final, but never static.

The initialisation of final, and static final variables, can be run-time expressions. This means you can even call functions. However, a static initialiser method cannot be used to initialise final variables, even if they are static.

Private Static Variables

Ex-C, or ex-C++, ex-C#, or ex-several-other-languages programmers may be surprised that you can­not create variables in a local scope, with a global lifetime; meaning a variable that can only be used by one function, just like a local variable, but remains in memory when the function returns, unlike local variables. You have to use a pattern for that.

The only way to get what you have been used to (a locally scoped variable with a global life­time), is to wrap it in a class, and mark it private:

PrivGlobal.javaPrivate Global Variables Pattern
i=1, j=2, k=3

It seems a bit of overkill, but we have achieved what we wanted: only the Next() function can access and modify the myvar variable, and it lives for as long as the program runs (global life­time). According to your Java overlords, this is a better abstraction.

Just remember, if you want to make the Counter class public, you have to move it to a separate source file. You may then probably also want to make the Next method public.

Practical Code

Once we have some understanding of literals, variables, and “finals”, we can write some relatively useful programs. We will start with a basic problem statement:

Problem Statement: Write a program that calculates the circumference and area of a circle with a radius of 12.34 units.

Circle formulæ: \(A = \pi r^{2}\); \(C = 2\pi r\); \(r = \sqrt{{{A}\div{\pi}}}\); \(r = C\div 2\pi\).
Where \(r\) is radius, \(A\) is area, and \(C\) is circumference.

Using Literals, Variables and Finals

In the example below, we use increasingly more Java features to provide several solutions to the problem statement. Only the first four, marked: a)d), are important at this stage. The others are to show you that there is more to learn, and to pique your curiosity.

circ00.javaCircle Calculator Version 00
/* Circle Calculator 00.
* Pedagogical example program that shows how increasing knowledge makes
* programs increasingly “better”, where “better” means more readable,
* more maintainable, and more reusable.
import java.lang.*;                  //←default, not really needed.
import static java.lang.System.*;    //←use `out` & `exit` directly.

public class circ00 {

/* Circle Calculator's entry point.
* The code in `main` shows 5 (6th is a bonus) ways to produce an answer
* for the problem: “calculate the circumference and area of a circle given
* a `radius` of `12.34`”. Compound statement blocks are used to separate
* the steps, and limit the scope of variables.
* If you have not learned about functions yet, you can skip the fifth
* set of code (e), and obviously also then the sixth (f).
public static void main (String[] args) {

   out.println("CIRCLE CALCULATOR 00");

   /* a) Absolutely minimal Java knowledge required: only literals,
   *  arithmetic, and `System.out.println`, and that everything is
   *  convertible `toString()`, even when concatenating to a string.
   */ {
      out.println("a) Radius  :" + 12.34);
      out.println("   Circum. :" + (2.0 * 3.14159265359 * 12.34));
      out.println("   Area    :" + (3.14159265359 * 12.34 * 12.34));

   /* b) Knowledge about `format` allows formatting for neater output.
   *  We still have a lot of magic numbers though, and some are even
   *  repeated, neither of which is a good sign in programs.
   */ {
      out.format("b) Radius  : %10.4f\n", 12.34);
      out.format("   Circum. : %10.4f\n", 2.0 * 3.14159265359 * 12.34);
      out.format("   Area    : %10.4f\n", 3.14159265359 * 12.34 * 12.34);

   /* c) Add `final` (symbolic constant, sort of), and it gets better,
   *  except we cannot make it `static` inside a method, so we have to
   *  create the `static` version at class level (see below), although
   *  we do not use it in this block (later we do).
   */ {
      final double pi = 3.14159265359;
      out.format("c) Radius  : %10.4f\n", 12.34);
      out.format("   Circum. : %10.4f\n", 2.0 * pi * 12.34);
      out.format("   Area    : %10.4f\n", pi * 12.34 * 12.34);

   /* d) Add variables and we have more structured and cleaner code. The
   *  variables will only be visible in this compound statement block.
   *  We do not often define multiple variables at the same time, but it
   *  is possible, as you see below.
   */ {
      double                             //←local variable definitions.
         radius = 12.34,                 //←they are also initialised in
         circum = 2.0 * PI * radius,     // sequence of definition, so
         area   = PI * radius * radius;  // this is well-defined code.

      out.format("d) Radius  : %10.4f\n", radius);
      out.format("   Circum. : %10.4f\n", circum);
      out.format("   Area    : %10.4f\n", area  );

   /* e) Add static functions, since the formulae are reusable. See the
   *  function definitions below this function. We can pass the `radius`
   *  to the functions, so it can be used for any radii.
   */ {
      double radius = 12.34;
      out.format("e) Radius  : %10.4f\n", radius         );
      out.format("   Circum. : %10.4f\n", circum(radius) );
      out.format("   Area    : %10.4f\n", area(radius)   );

   /* f) Bonus. You are really not required to understand lambdas at
   *  this point, but we employ the syntax here to abstractly create
   *  “local functions”. It is a little bit clumsy in Java, because we
   *  first have to create an `interface` with one method (see below).
   */ {
      double radius = 12.34;
      Circle circum = r -> 2.0 * PI * r;
      Circle area   = r -> PI * r* r;

      out.format("f) Radius  : %10.4f\n", radius         );
      out.format("   Circum. : %10.4f\n", circum(radius) );
      out.format("   Area    : %10.4f\n", area(radius)   );


// c) Add `final` (symbolic constant), but get from `java.lang.Math`:
static final double PI = java.lang.Math.PI;

// e) Static functions to calculate area and circumference of a circle.
// The parameter `radius` is initialised by the caller passing an argu-
// ment of the right type (`double`). In all other respects, the para-
// meter acts like a local variable to each of these functions, and has
// no scope or syntactical relationship with other `radius` variables.
static double circum (double radius) {   //←simplest and best solution.
   return 2.0 * PI * radius;             //←return result of expression.

static double area (double radius) {     //←more traditional pattern.
   double result;                        //←variable to store result.
   result = PI * radius * radius;        //←operations/calculations.
   return result;                        //←return result.

// f) To use lambdas, you need an `interface` with *one* method. The
// name of the method is useful only from a readability perspective.
// The name of the parameter is required, but again is mostly useful
// as self-documenting code. The return type, and types of any number
// of parameters, are important, since Java uses that to infer the
// types in the lambda expression where used.
interface Circle { double formula (double radius); }


Classic Modularisation

We can improve on the above code slightly, by reorganising our code. This does not really require much new in the line of syntax, simply the observation that if you can write a static member in some class (like above), you can write those same members in another class, and thus use more than one class in the same program — the classes do not even have to be in separate files, as long as only one class is marked with the public access specifier.

circ01.javaCircle Calculator 01
/*  Circle Calculator 01.
* Pedagogical example program that shows how `static` members, like
* “finals” and functions, can be in a separate class, resembling some
* modularity. To avoid having multiple files, the `Circle` class is
* “package-private”. This is the “best” solution for the problem state-
* ment that requires no OO features, except that we *organise* members
* in classes.
* The `Circle` class could have been defined with `public` access, and
* placed in `Circle.java`, and we would not have to change any of the
* code in `main`. This would provide a simple mechanism to group related
* code together in a class, as long as all members to be used from 
* outside the class, are `public` and `static`. This does not involve
* OOP knowledge, and is simply one way to organise code.
import java.lang.*;                  //←default, not really needed.
import static java.lang.System.*;    //←use `out` & `exit` directly.

public class circ01 {

/* Circle Calculator's entry point.
* The code in `main` simply uses `static` members from the `Circle` class
* defined below, in the same file.
public static void main (String[] args) {

   out.println("CIRCLE CALCULATOR 01");

   /* a) Call static functions that reside in another class. We could also
   *  have accessed `Circle.PI`, but there is no need for it here.
   */ {
      double radius = 12.34;
      out.format("a) Radius  : %10.4f\n", radius               );
      out.format("   Circum. : %10.4f\n", Circle.circum(radius));
      out.format("   Area    : %10.4f\n", Circle.area(radius)  );

   /* b) Exactly the same code as a) above, except that some colour was
   *  added, using VT100/ANSI escape sequences. If you cannot deal with
   *  that, remove and ignore this code — only a problem on Windows.
   */ {
      double radius = 12.34;
      final String GRN = "\033[32;1m";
      final String DFT = "\033[0m";
      final String FMT = " : " + GRN + "%10.4f" + DFT + "\n";
      out.format("b) Radius "  + FMT, radius               );
      out.format("   Circum."  + FMT, Circle.circum(radius));
      out.format("   Area   "  + FMT, Circle.area(radius)  );



class Circle { //←“package-private” class, accessible in package only;
               // this is the default access for the members here too.

static final double PI = java.lang.Math.PI;

static double circum (double radius) {   //←simplest and best solution.
   return 2.0 * PI * radius;             //←return result of expression.

static double area (double radius) {     //←more traditional pattern.
   double result;                        //←variable to store result.
   result = PI * radius * radius;        //←operations/calculations.
   return result;                        //←return result.


We can improve on the above example solution in one way only (unless we want to use OOP to model a Circle class using encapsulation). This improvement would involve giving class Circle public access, which in turn means it must be placed in a separate Circle.java file. In line with that, we also should put public in front of all the members, to make it even more widely usable.

The Java compiler will automatically look for classes referenced in all the .java files in the di­rec­to­ry of the class being compiled, which means we do not have to modify any of the code in the main class (circ01), or even change the compiler invocation command.

Some Standard Input

Although the java.lang.System class provides an in (for standard input) field, and we have been using the out (standard output) field incessantly, in does not provide useful buffered (keyboard) input. You have to wrap in with one of several candidate classes; the two most likely are:

Alternatively, you can use java.util.Scanner:

For us, the problem is that to use these classes, we must teach you about exception handling, but this is not a good time for it; though soon… very soon. At this point, we rather show you a more immediately useful class: the java.io.Console class.

SimpleConsole.javaSimple Console Input Example

Apart from the fact the we have to create a java.io.Console type variable, and initialise it, and check if it was successful, it is very easy to use. If you are going to use it a lot, you may want to make it a static field, and initialise it in a static initialiser method:

StaticConsole.javaConsole as Static Member

There you have it: the ability to read strings from standard input. And from the example, you can deduce how to convert an input String to an int, for example. If you want, you can omit the static initialiser method — your program will just fail at a different point if System.console() returned null (could not allocate console).

Getting Started with Java

Statements and Functions

2017-12-24: Created. [brx]