Java syntax
| Company type | Programming language |
|---|---|
| Industry | Software development |
| Founded | 1995 |
| Founder | James Gosling, Sun Microsystems |
| Headquarters | Santa Clara , United States |
Area served | Worldwide |
| Products | Java Platform, Standard Edition (Java SE), Java Development Kit (JDK) |
| Owner | Oracle Corporation |
| Website | www |
The Java syntax establishes the conventions for composing and interpreting Java code.
Primarily influenced by C and C++, Java's syntax prohibits the use of global functions or variables; rather, all executable code is encapsulated within classes, with data members functioning as global variables. Except for primitive data types, which are employed to optimize performance and can be automatically converted to objects, all values are treated as objects. To streamline development and minimize errors, the language excludes features such as operator overloading and unsigned integer types.
Over time, Java's syntax has progressed through successive JDK versions, now incorporating sophisticated features like generic programming and anonymous functions (lambda expressions). Since 2017, biannual JDK releases have steadily introduced incremental improvements to the language.
Identifier
An identifier serves as a name for elements within the source code and adheres to established naming standards. Identifiers are sensitive to case and may contain:
- Unicode letters (including numeric characters such as Roman numerals)
- Currency symbols (for example, ¥)
- Connecting punctuation marks (such as the underscore _)
Limitations include:
- Identifiers cannot begin with a digit
- They must not coincide with reserved keywords, the null literal, or Boolean literals
Keywords
The following reserved terms are prohibited from being used as identifiers:
_abstractassertbooleanbreakbytecasecatchcharclasscontinuedefaultdodoubleelseenumextendsfinalfinallyfloatforifimplementsimportinstanceofintinterfacelongnativenewpackageprivateprotectedpublicreturnshortstaticsuperswitchsynchronizedthisthrowthrows</text/code]- <text/code]transient/text/code]
- text/code]try/text/code]
- text/code]void/text/code]
- text/code]volatile/text/code]
- text/code]while/text/code]
Reserved identifiers
Certain words act as contextual keywords and are restricted only in specific situations:
exports
module
non-sealed</text/code]
- text/code]open/text/code]
- text/code]opens/text/code]
- text/code]permits/text/code]
- text/code]provides/text/code]
- text/code]record/text/code]
- text/code]requires/text/code]
- text/code]sealed/text/code]
- text/code]to/text/code]
- text/code]transitive/text/code]
- text/code]var/text/code]
- text/code]when/text/code]
- text/code]with/text/code]
- text/code]yield/text/code]
Reserved keywords for literal values
These terms denote literal values recognized by the language.
true
false
null
Currently unused
The keywords listed below are reserved but have no active use at present.
const
goto
strictfp
Literal values
Integer formats
binary (added in Java SE 7)
0b11110101 (binary number preceded by the 0b prefix)
octal
0365 (octal number with a leading 0)
hexadecimal
0xF5 (hexadecimal number starting with 0x)
decimal
245 (standard decimal number)
Floating-point literals
float
23.5F, .5f, 1.72E3F (decimal fractions optionally using exponent and ending with F)
0x.5FP0F, 0x.5P-6f (hexadecimal fractions requiring an exponent and F suffix)
double
23.5D, .5, 5., 1.72E3D (decimal fractions with optional exponent and optional D suffix)
0x.5FP0, 0x.5P-6D (hexadecimal fractions with mandatory exponent and optional D)
Character literals
char
'a', 'Z', '\u0231' (single-quoted characters or escape sequences)
Boolean literals
boolean
true, false
Null literal
null reference
null
String literals
String
"Hello, World" (text enclosed in double quotes, including escapes)
Escape sequences within strings
Unicode character
\u3876 (\u followed by a hexadecimal Unicode code point up to U+FFFF)
Octal escape sequence
\352 (octal number not exceeding 377, preceded by a backslash)
Line feed
\n
Carriage return
\r
Form feed
\f
Backslash
\\
Single quote
\'
Integer literals default to type int, unless explicitly declared as long by appending L or l, for example, 367L. Starting with Java SE 7, underscores may be inserted between digits to improve readability; for instance, instead of .
Variables
In programming, variables serve as named references to data values. Declaring a variable involves specifying its data type and identifier, with the option to assign an initial value simultaneously.
<syntaxhighlight lang="java">
int count; // Declares variable 'count' of type int without initialization
count = 35; // Assigns value to 'count'
int count = 35; // Declaration combined with initialization
</syntaxhighlight>
It is possible to declare and initialize multiple variables of the same type within a single statement by separating them with commas.
<syntaxhighlight lang="java">
int a, b; // Declares two integer variables 'a' and 'b'
int a = 2, b = 3; // Declares and initializes both variables in one line
</syntaxhighlight>
Type deduction
Starting with Java 10, the compiler can automatically determine variable types through the use of var.
<syntaxhighlight lang="java">
// 'stream' type deduced as FileOutputStream from the assigned value
var stream = new FileOutputStream("file.txt");
// Equivalent explicit type declaration
FileOutputStream stream = new FileOutputStream("file.txt");
</syntaxhighlight>
Code scopes
Curly braces define blocks of code and establish scope boundaries, such as those enclosing class members or method implementations.
Inside methods, braces introduce nested scopes:
<syntaxhighlight lang="java">
void doSomething() {
int a;
{
int b;
a = 1;
}
a = 2;
b = 3; // Invalid: 'b' is only visible within the inner block
}
</syntaxhighlight>
Comments
Java provides three types of comments: block, inline, and documentation comments.
Block comments begin with /* and conclude with */, allowing multiple lines to be commented out.
<syntaxhighlight lang="java">
/* This is a multi-line comment.
It can span several lines. */
</syntaxhighlight>
Inline comments start with // and continue until the end of the line.
<syntaxhighlight lang="java">
// This is an inline comment
</syntaxhighlight>
Documentation comments commence with /**, adhere to Javadoc standards, and are used to generate API documentation.
<syntaxhighlight lang="java">
/**
* This is a documentation comment.
*
* @author John Doe
*/
</syntaxhighlight>
Core types
All classes within the java.lang package are automatically imported into Java programs unless explicitly excluded. Important classes include:
java.lang.Object
java.lang.Object serves as Java’s universal superclass and top-level type. Every class without an explicit superclass inherits from it implicitly. All data types can be cast to this type, with primitive types undergoing autoboxing when converted.
java.lang.String
java.lang.String represents Java’s fundamental immutable string class. It treats each UTF-16 code unit as an individual "character," and provides methods to convert strings into an int[] array representing UTF-32 code points.
java.lang.Throwable
java.lang.Throwable is the root class for all objects that can be thrown or caught using Java’s throw and catch mechanisms.
Application architecture
Java programs consist of sets of classes organized into packages, which themselves can be nested within other classes.
The main method
Every Java program requires a designated entry point known as the main method. Although multiple classes may define a main, the primary one is specified externally (for instance, via a manifest file). The main method must be declared as public static void, accepting command-line parameters as a string array. Unlike C++ or C#, it does not return any value.
<syntaxhighlight lang=Java>
public static void main(String[] args) {
}
</syntaxhighlight>
Packages
Packages serve to organize classes and differentiate them, while also controlling access through modifiers. For example, in java.io.InputStream, the class name is InputStream, located within the package java.io.
A package declaration appears at the beginning of a source file:
<syntaxhighlight lang=Java>
package myapplication.mylibrary;
public class MyClass {
}
</syntaxhighlight>
Classes declared public must be saved in files named after them, with directory structures reflecting their package hierarchy (e.g., myapplication/mylibrary/MyClass.java).
Type Import Declarations
Type import declarations allow the use of type names without requiring their fully qualified names. Positioned after the package statement, these imports can be either single-type or import-on-demand.
<syntaxhighlight lang="java">
package myPackage;
import java.util.Random; // Importing a single type
public class ImportsTest {
public static void main(String[] args) {
Random random = new Random();
}
}
</syntaxhighlight>
Using import-on-demand imports all types from a specified package, while static imports bring in static members from classes or packages:
<syntaxhighlight lang="java">
import java.util.*; // Imports every class within java.util
import java.*; // Valid syntax but ineffective; no direct classes inside java
</syntaxhighlight>
Static Import Declarations
Introduced in J2SE 5.0, static import declarations permit direct access to static members of classes, interfaces, enums, or annotations without prefixing the class name:
<syntaxhighlight lang="java">
import static java.lang.System.out;
public class HelloWorld {
public static void main(String[] args) {
out.println("Hello World!");
}
}
</syntaxhighlight>
Import-on-demand syntax can also be applied to static members of a class:
<syntaxhighlight lang="java">
import static java.lang.System.*;
</syntaxhighlight>
Static imports extend to enum constants as well. For instance, consider an enum defined in the screen package:
<syntaxhighlight lang="java">
public enum ColorName {
RED, BLUE, GREEN
};
</syntaxhighlight>
By statically importing these constants in another class, referencing them becomes more concise:
<syntaxhighlight lang="java">
import screen.ColorName;
import static screen.ColorName.*;
public class Dots {
ColorName foo = RED;
void shift() {
if (foo == RED) foo = BLUE;
}
}
</syntaxhighlight>
Operators
Java's operators are similar to those found in C++, with notable exceptions: Java does not include the delete operator due to automatic memory management and absence of pointer arithmetic. Additionally, Java introduces a unique unsigned right shift operator (>>). Operator overloading is not supported in Java.
Precedence
Operator
Description
Associativity
1
()
Method call
Left-to-right
[]
Accessing array elements
.
Selecting a member of a class
2
++ --
Postfix increment and decrement operators
3
++ --
Prefix increment and decrement operators
Right-to-left
+ -
Unary plus and minus signs
! ~
Logical NOT and bitwise complement operators
(type) val
Type casting operation
new
Creating new instances or arrays
4
* / %
Multiplication, division, and modulus operations
Left-to-right
5
+ -
Addition and subtraction (including string concatenation)
6
<< >></ code> < code>>>></ code>
Bitwise left shift, signed right shift, and unsigned right shift operators
7
< code><</ code> < code><=</ code>
Less than and less than or equal relational comparisons
< code>></ code> < code>>=</ code>
Greater than and greater than or equal relational comparisons
< code>instanceof</ code>
Type checking operator
8
< code>==</ code> < code>!=</ code>
Equality and inequality comparisons
9
< code>&</ code>
Bitwise AND and logical AND operations
10
< code>^</ code>
Bitwise XOR and logical exclusive OR operations
11
|
Bitwise OR and logical inclusive OR operations
12
< code>&&</ code>
Logical AND conditional operator
13
||
Logical OR conditional operator
14
< code>c ? t : f</ code>
Ternary conditional expression
Right-to-left
15
< code>=</ code>
Simple assignment operator
= </ code>
Compound assignments involving arithmetic, bitwise, and shift operations
if Statement
Java's if statements are syntactically similar to those found in C, following the same structure:
<syntaxhighlight lang="java">
if (i == 3) {
doSomething();
}
</syntaxhighlight>
An optional else clause allows the formation of an if-then-else construct:
<syntaxhighlight lang="java">
if (i == 3) {
doSomething();
} else {
doSomethingElse();
}
</syntaxhighlight>
The else-if pattern is implemented as a chain of if-then-else blocks without any dedicated keyword:
<syntaxhighlight lang="java">
if (i == 3) {
doSomething();
} else if (i == 2) {
doSomethingElse();
} else {
doSomethingDifferent();
}
</syntaxhighlight>
For simpler conditional assignments, the ?: operator provides a compact alternative:
<syntaxhighlight lang="java">
int a = 1;
int b = 2;
int minVal = (a < b) ? a : b;
</syntaxhighlight>
switch Statement
In Java, the switch statement accepts variables of types byte, short, char, int, their corresponding wrapper classes, and since J2SE 5.0, enum types. Starting from Java SE 7, it also supports String objects. Other reference types are not permitted.
Each case label must be a constant value, including enum constants and string literals. Execution begins at the matching case label, with an optional default case to handle unmatched values. Typically, each case concludes with a break statement to avoid fall-through behavior; omitting it may lead to compiler warnings.
<syntaxhighlight lang="java">
switch (ch) {
case 'A':
doSomething();
break;
case 'B':
case 'C':
doSomethingElse();
break;
default:
doSomethingDifferent();
break;
}
</syntaxhighlight>
switch Expressions
Starting with Java 14, switch expressions introduce arrow syntax to enable clearer and expression-oriented control flow:
<syntaxhighlight lang="java">
var result = switch (ch) {
case 'A' -> Result.GREAT;
case 'B', 'C' -> Result.FINE;
default -> throw new ThisIsNoGoodException();
};
</syntaxhighlight>
Alternatively, the yield keyword can be employed within switch expressions; however, arrow syntax is generally favored to prevent unintended fall-through:
<syntaxhighlight lang="java">
var result = switch (ch) {
case 'A':
yield Result.GREAT;
case 'B':
case 'C':
yield Result.FINE;
default:
throw new ThisIsNoGoodException();
};
</syntaxhighlight>
Iteration Statements
Iteration statements enable repetitive execution of code blocks as long as a specified condition evaluates to true. Since J2SE 5.0, Java has supported four distinct looping constructs. The controlling condition must be a boolean or Boolean type; unlike C, non-boolean expressions such as
<syntaxhighlight lang="c">
while (1) {
doSomething();
}
</syntaxhighlight>
will not compile.
while Loop
A while loop evaluates its condition before each iteration:
<syntaxhighlight lang="java">
while (i < 10) {
doSomething();
}
</syntaxhighlight>
do ... while Loop
In a do ... while loop, the condition is evaluated after each cycle, guaranteeing that the loop body executes at least once:
<syntaxhighlight lang="java">
// doSomething() executes at least once
do {
doSomething();
} while (i < 10);
</syntaxhighlight>
for loop
Java's for loops consist of three parts: an initializer, a condition, and an update expression. You can separate multiple initializers or update expressions with commas (but not conditions). Unlike C, commas serve as separators rather than operators:
<syntaxhighlight lang="java">
for (int i = 0; i < 10; i++) {
doSomething();
}
// Loop using two variables
for (int i = 0, j = 9; i < 10; i++, j -= 3) {
doSomething();
}
</syntaxhighlight>
Each of these three components is optional; for instance, omitting all results in an endless loop:
<syntaxhighlight lang="java">
for (;;) {
doSomething();
}
</syntaxhighlight>
Enhanced for loop
Introduced in J2SE 5.0, the enhanced for loop simplifies iterating over arrays and collections by internally using iterators to return elements one by one. Unlike C#, which employs a specific keyword, Java uses a unique syntax:
<syntaxhighlight lang="java">
for (int i : intArray) {
doSomething(i);
}
</syntaxhighlight>
Labels
Labels identify specific locations in code that can be targeted by break and continue. Although Java reserves the goto keyword, it cannot be used.
<syntaxhighlight lang="java">
start:
someMethod();
</syntaxhighlight>
break statement
The break statement terminates the closest enclosing loop or switch, transferring control to the statement following it:
<syntaxhighlight lang="java">
for (int i = 0; i < 10; i++) {
while (true) {
break;
}
// Execution continues here
}
</syntaxhighlight>
By employing labels, it's possible to break out of outer loops:
<syntaxhighlight lang="java">
outer:
for (int i = 0; i < 10; i++) {
while (true) {
break outer;
}
}
// Execution resumes here
</syntaxhighlight>
continue statement
The continue statement skips the current iteration within a loop and advances to the next cycle. In this example, a while loop reads characters via getChar(), bypassing subsequent statements if the character is a space:
<syntaxhighlight lang="java">
int ch;
while ((ch = getChar()) != -1) {
if (ch == ' ') {
continue; // Skips remaining code in this iteration
}
doSomething();
}
</syntaxhighlight>
Labels can also be combined with continue and break to manage outer loops more precisely:
<syntaxhighlight lang="java">
outer:
for (String str : stringsArr) {
char[] strChars = str.toCharArray();
for (char ch : strChars) {
if (ch == ' ') {
continue outer; // Proceeds to next string in stringsArr
}
doSomething(ch);
}
}
</syntaxhighlight>
return statement
The return statement ends method execution and may optionally provide a return value. Methods with a non-void return type must include a return. Examples include:
<syntaxhighlight lang="java">
void doSomething(boolean streamClosed) {
if (streamClosed) {
return;
}
readFromStream();
}
int calculateSum(int a, int b) {
return a + b;
}
</syntaxhighlight>
A return causes immediate termination of method execution unless it occurs inside a try block with an associated finally, in which case control passes to the finally block before exiting:
<syntaxhighlight lang="java">
void doSomething(boolean streamClosed) {
try {
if (streamClosed) {
return;
}
readFromStream();
} finally {
freeResources();
}
}
</syntaxhighlight>
try-catch-finally Blocks
Exception management in Java utilizes try, catch, and optionally finally blocks to handle runtime anomalies:
<syntaxhighlight lang="java">
try {
methodThrowingExceptions();
} catch (Exception ex) {
reportException(ex);
} finally {
freeResources();
}
</syntaxhighlight>
When an exception is thrown inside the try block, control transfers to the first catch clause that matches the exception type. Starting with Java 7, multiple exceptions can be caught in a single catch block using the multi-catch feature:
<syntaxhighlight lang="java">
try {
methodThrowingExceptions();
} catch (IOException | IllegalArgumentException ex) {
reportException(ex);
}
</syntaxhighlight>
If no suitable catch block exists, the exception propagates up the call stack until it is handled or causes program termination. The finally block executes unconditionally after the try/catch, ensuring cleanup actions run regardless of exceptions or early returns.
A try block must be followed by at least one catch or a finally block.
try-with-resources Statements
Introduced in Java SE 7, try-with-resources statements simplify resource management by automatically closing resources once the try block finishes. Resources used must implement the java.lang.AutoCloseable interface. Unlike traditional try-catch-finally, these statements do not mandate explicit catch or finally clauses for resource cleanup.
<syntaxhighlight lang="java">
try (FileOutputStream fos = new FileOutputStream("filename");
XMLEncoder xEnc = new XMLEncoder(fos)) {
xEnc.writeObject(object);
} catch (IOException ex) {
Logger.getLogger(Serializer.class.getName()).log(Level.SEVERE, null, ex);
}
</syntaxhighlight>
From Java 9 onward, previously declared variables can be included directly in the resource specification:
<syntaxhighlight lang="java">
FileOutputStream fos = new FileOutputStream("filename");
XMLEncoder xEnc = new XMLEncoder(fos);
try (fos; xEnc) {
xEnc.writeObject(object);
} catch (IOException ex) {
Logger.getLogger(Serializer.class.getName()).log(Level.SEVERE, null, ex);
}
</syntaxhighlight>
throw Statement
The throw keyword explicitly triggers an exception, terminating the current method or block execution. The exception object follows immediately after the keyword.
<syntaxhighlight lang="java">
void methodThrowingExceptions(Object obj) {
if (obj == null) {
throw new NullPointerException();
}
doSomethingWithObject(obj);
}
</syntaxhighlight>
Thread Synchronization
Java facilitates concurrent programming through built-in synchronization mechanisms. The synchronized construct enforces mutual exclusion by locking on a non-null reference object before executing a critical section. This lock prevents simultaneous access by other threads until it is released.
<syntaxhighlight lang="java">
synchronized (someObject) {
// Synchronized statements
}
</syntaxhighlight>
assert statement
Introduced in J2SE 1.4, the assert statement provides a mechanism for runtime assertions that can be toggled on or off at the class or package level. It triggers an exception if the evaluated condition is false, with the option to include a descriptive message.
<syntaxhighlight lang="java">
// Throws AssertionError if n is zero
assert n != 0;
/* Throws AssertionError with message if n is zero */
assert n != 0 : "n was equal to zero";
</syntaxhighlight>
Primitive types
Java's primitive data types encompass integer values, floating-point numbers, UTF-16 code units, and a Boolean type. Aside from the char type, which represents UTF-16 code units, Java does not provide unsigned types. This is partially mitigated by the presence of the unsigned right shift operator (>>), which is not found in C++. However, this distinction has been criticized for limiting interoperability with C and C++.
Primitive Types
Type
Wrapper class
Category
Range
Size
Default Value
byte
java.lang.Byte
integer
−128 to 127
8-bit (1 byte)
0
short
java.lang.Short
integer
−32,768 to 32,767
16-bit (2 bytes)
0
int
java.lang.Integer
integer
−2,147,483,648 to 2,147,483,647
32-bit (4 bytes)
0
long
java.lang.Long
integer
−9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
64-bit (8 bytes)
0
float
java.lang.Float
floating-point number
±1.401298E−45 to ±3.402823E+38
32-bit (4 bytes)
0.0f
double
java.lang.Double
floating-point number
±4.94065645841246E−324 to ±1.79769313486232E+308
64-bit (8 bytes)
0.0
boolean
java.lang.Boolean
Boolean
true or false
1-bit (1 bit)
false
char
java.lang.Character
UTF-16 code unit (either a BMP character or part of a surrogate pair)
'\u0000' to '\uFFFF'
16-bit (2 bytes)
'\u0000'
The char type can represent either a single character or one half of a surrogate pair used for encoding Unicode code points outside the Basic Multilingual Plane.
Boxing and unboxing
Since J2SE 5.0, boxing refers to the automatic conversion of primitive values into their corresponding wrapper class objects; unboxing denotes the reverse process. These conversions happen implicitly without requiring explicit casting.
Example:
<syntaxhighlight lang="java">
int foo = 42; // primitive int value
Integer bar = foo; // automatic boxing to Integer object
int foo2 = bar; // automatic unboxing back to primitive int
</syntaxhighlight>
Reference types
Reference types consist of classes, interfaces, and arrays. Invoking a constructor results in an object being allocated on the heap with a reference assigned to a variable. Once that variable goes out of scope and no references remain pointing to the object, it becomes eligible for garbage collection.
A reference variable holds the value null when it does not refer to any object instance.
Arrays
In Java, arrays are instantiated during program execution similarly to objects. Once created, their size remains constant.
<syntaxhighlight lang="java">
int[] numbers = new int[5];
numbers[0] = 2;
numbers[1] = 5;
int x = numbers[0];
</syntaxhighlight>
Initializers
<syntaxhighlight lang="java">
// Verbose form
int[] numbers = new int[] {20, 1, 42, 15, 34};
// Concise form
int[] numbers2 = {20, 1, 42, 15, 34};
</syntaxhighlight>
Multi-dimensional arrays
Multi-dimensional arrays consist of arrays containing other arrays, represented as references to subordinate arrays.
<syntaxhighlight lang="java">
int[][] numbers = new int[3][3];
numbers[1][2] = 2;
int[][] numbers2 = {{2, 3, 2}, {1, 2, 6}, {2, 4, 5}};
</syntaxhighlight>
Since sub-arrays may have different lengths, multi-dimensional arrays do not have to be uniform rectangles:
<syntaxhighlight lang=java>
int[][] numbers = new int[2][]; // Only the first dimension is allocated
numbers[0] = new int[3];
numbers[1] = new int[2];
</syntaxhighlight>
Classes
Classes form the foundation of Java’s object-oriented paradigm. They encapsulate members that hold and manage data. Classes are divided into top-level and nested categories. Nested classes are defined within another class and can access its private fields. These include member classes (which can be static or inner), local classes, and anonymous classes.
Declaration
Top-level class
<syntaxhighlight lang="java">
class Foo {
// Class members
}
</syntaxhighlight>
Inner class
<syntaxhighlight lang="java">
class Foo { // Top-level class
class Bar { // Inner class
}
}
</syntaxhighlight>
Nested class
<syntaxhighlight lang="java">
class Foo { // Top-level class
static class Bar { // Nested class
}
}
</syntaxhighlight>
Local class
<syntaxhighlight lang="java">
class Foo {
void bar() {
class Foobar {// Local class inside a method
}
}
}
</syntaxhighlight>
Anonymous class
<syntaxhighlight lang="java">
class Foo {
void bar() {
new Object() {// Anonymous subclass of Object
};
}
}
</syntaxhighlight>
Instantiation
Instance variables and methods tied to specific objects are defined as non-static members. Objects are created by applying the new keyword along with the constructor.
<syntaxhighlight lang="java">
Foo foo = new Foo();
</syntaxhighlight>
Accessing members
Both instance and static members are accessed using the . (dot) operator.
Accessing instance members
Instance fields and methods are referenced through an object variable.
<syntaxhighlight lang="java">
String foo = "Hello";
String bar = foo.toUpperCase();
</syntaxhighlight>
Accessing static members
Static members declared with the static keyword belong to the class itself and are accessed via the class name without needing an object instance.
<syntaxhighlight lang="java">
public class Foo {
public static void doSomething() {
}
}
// Invoking the static method
Foo.doSomething();
</syntaxhighlight>
Modifiers
Modifiers serve to adjust declarations of types and their members, prominently including those that govern access levels.
abstract - Specifies a base class that cannot be directly instantiated.
static - When applied to member classes, indicates the class is not tied to any instance.
final - Prevents further subclassing of a class.
strictfp - Guarantees floating-point calculations adhere strictly to IEEE 754 standards.
Access modifiers
Access modifiers regulate the visibility scope of classes and their members. Members declared as public are accessible from any location, whereas omission of an access modifier results in default (package-private) visibility.
<syntaxhighlight lang="java">
public class Foo {
int go() {
return 0;
}
private class Bar {
}
}
</syntaxhighlight>
The following table outlines accessibility based on modifier and context:
Modifier
Same or nested class
Other class in package
Subclass in another package
Non-subclass in another package
private
yes
no
no
no
default (package private)
yes
yes
no
no
protected
yes
yes
yes
no
public
yes
yes
yes
yes
Constructors and initializers
A constructor is responsible for setting up object fields when a new instance is created. Unlike methods, constructors do not specify a return type and bear the same name as their class:
<syntaxhighlight lang="java">
class Foo {
String str;
Foo() { // No-argument constructor
// Initialization logic here
}
Foo(String str) { // Constructor accepting a parameter
this.str = str;
}
}
</syntaxhighlight>
Initializers execute code blocks during either class loading or object instantiation.
Static initializers, marked with static, are executed once when the class is loaded into memory:
<syntaxhighlight lang="java">
class Foo {
static {
// Static initialization code
}
}
</syntaxhighlight>
Instance initializers run before every constructor invocation and do not accept parameters or throw checked exceptions:
<syntaxhighlight lang="java">
class Foo {
{
// Instance initialization code
}
}
</syntaxhighlight>
Java manages memory through garbage collection rather than destructors. Objects may override the finalize() method, which is invoked prior to garbage collection, to perform necessary cleanup.
Methods
In Java, all executable statements must reside within methods, which are associated with classes and characterized by a return type, method name, and parameters. Methods that do not return values declare the void return type. Unlike C++, Java does not support default argument values in methods; instead, method overloading is commonly used.
<syntaxhighlight lang="java">
class Foo {
int bar(int a, int b) {
return (a*2) + b;
}
/* Overloaded version with different parameter list */
int bar(int a) {
return a*2;
}
}
</syntaxhighlight>
Methods are invoked using the . operator on an object instance or, for static methods, on the class itself.
<syntaxhighlight lang="java">
Foo foo = new Foo();
int result = foo.bar(7, 2); // Invocation of non-static method
int finalResult = Math.abs(result); // Invocation of static method
</syntaxhighlight>
The throws clause declares exceptions that a method might propagate. All checked exceptions must be listed as a comma-separated sequence.
<syntaxhighlight lang="java">
void openStream() throws IOException, myException { }
</syntaxhighlight>
Modifiers
abstract - Specifies methods without implementations within abstract classes; such methods must be implemented in subclasses unless those subclasses are also abstract.
static - Enables method invocation without needing an instance; cannot interact with non-static members.
final - Disallows method overriding in any subclass.
native - Marks methods as implemented outside of Java, typically through JNI, for platform-specific functionality.
strictfp - Guarantees adherence to IEEE 754 standards for floating-point calculations.
synchronized - Requires a thread to obtain a monitor lock before execution; the monitor corresponds to the instance or java.lang.Class for static methods.
- Access modifiers used here correspond to those applied to classes.
Varargs
Introduced in J2SE 5.0, varargs methods accept a flexible number of arguments of a specified type, treated as an array within the method.
<syntaxhighlight lang="java">
void printReport(String header, int... numbers) {
System.out.println(header);
for (int num : numbers) {
System.out.println(num);
}
}
// Example invocation of varargs method
printReport("Report data", 74, 83, 25, 96);
</syntaxhighlight>
Fields
Fields, also known as class variables, are declared inside the class body and hold data values.
<syntaxhighlight lang="java">
class Foo {
double bar;
}
</syntaxhighlight>
It is also possible to assign initial values to fields at the point of declaration.
<syntaxhighlight lang="java">
class Foo {
double bar = 2.3;
}
</syntaxhighlight>
Modifiers
static - Defines the field as belonging to the class rather than instances.
final - Permits assignment only once, either during declaration, within constructors, or initialization blocks.
transient - Prevents the field from being serialized during object serialization.
volatile - Guarantees visibility of changes to the field across multiple threads.
Inheritance
Java supports single inheritance where a class extends only one superclass using extends. Classes declared as final cannot serve as superclasses. The keyword this points to the current object, while super accesses members of the immediate superclass.
<syntaxhighlight lang="java">
class Foo {}
class Foobar extends Foo {}
</syntaxhighlight>
When no superclass is explicitly declared, a class implicitly inherits from java.lang.Object. If the superclass lacks a no-argument constructor, subclasses must explicitly invoke one of its constructors:
<syntaxhighlight lang="java">
class Foo {
public Foo(int n) { }
}
class Foobar extends Foo {
private int number;
public Foobar(int number) {
super(number);
this.number = number;
}
}
</syntaxhighlight>
Overriding methods
In Java, all methods except those marked final are virtual by default and may be overridden in derived classes.
<syntaxhighlight lang="java">
class Operation {
public int doSomething() { return 0; }
}
class NewOperation extends Operation {
@Override
public int doSomething() { return 1; }
}
</syntaxhighlight>
Abstract classes
An Abstract Class is a class that is not fully defined and thus cannot be instantiated on its own.
A class qualifies as abstract if it:
- Contains abstract methods,
- Inherits abstract methods that remain unimplemented,
- Or implements interfaces with abstract methods that have not been overridden.
Instances of subclasses derived from abstract classes can be created, which will invoke the constructors and initialize the fields of the abstract superclass.
<syntaxhighlight lang="java">
package org.dwwwp.test;
public class AbstractClass {
private static final String hello;
static {
System.out.println(AbstractClass.class.getName() + ": static block runtime");
hello = "hello from " + AbstractClass.class.getName();
}
{
System.out.println(AbstractClass.class.getName() + ": instance block runtime");
}
public AbstractClass() {
System.out.println(AbstractClass.class.getName() + ": constructor runtime");
}
public static void hello() {
System.out.println(hello);
}
}
</syntaxhighlight>
<syntaxhighlight lang="java">
package org.dwwwp.test;
public class CustomClass extends AbstractClass {
static {
System.out.println(CustomClass.class.getName() + ": static block runtime");
}
{
System.out.println(CustomClass.class.getName() + ": instance block runtime");
}
public CustomClass() {
System.out.println(CustomClass.class.getName() + ": constructor runtime");
}
public static void main(String[] args) {
CustomClass nc = new CustomClass();
hello();
}
}
</syntaxhighlight>
Output:
<syntaxhighlight lang="text">
org.dwwwp.test.AbstractClass: static block runtime
org.dwwwp.test.CustomClass: static block runtime
org.dwwwp.test.AbstractClass: instance block runtime
org.dwwwp.test.AbstractClass: constructor runtime
org.dwwwp.test.CustomClass: instance block runtime
org.dwwwp.test.CustomClass: constructor runtime
hello from org.dwwwp.test.AbstractClass
</syntaxhighlight>
Enumerations
Enumerations, introduced with J2SE 5.0, are specialized classes that encapsulate a fixed set of enum constants, each representing an instance of the enumeration type. These enum classes cannot be instantiated externally beyond their defined scope.
<syntaxhighlight lang="java">
enum Season {
WINTER, SPRING, SUMMER, AUTUMN
}
</syntaxhighlight>
When the class is loaded, constructors associated with enum constants may be executed:
<syntaxhighlight lang="java">
public enum Season {
WINTER("Cold"), SPRING("Warmer"), SUMMER("Hot"), AUTUMN("Cooler");
private final String description;
Season(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
}
</syntaxhighlight>
Enum classes can also include bodies for each constant, functioning as anonymous subclasses extending the enum:
<syntaxhighlight lang="java">
public enum Season {
WINTER {
String getDescription() { return "cold"; }
},
SPRING {
String getDescription() { return "warmer"; }
},
SUMMER {
String getDescription() { return "hot"; }
},
FALL {
String getDescription() { return "cooler"; }
};
}
</syntaxhighlight>
Interfaces
Interfaces specify method signatures without providing implementations and do not contain any fields. By definition, every interface is abstract. Starting with Java SE 8, interfaces can include static and default method implementations, offering greater adaptability for enterprise software development.
<syntaxhighlight lang="java">
interface ActionListener {
int ACTION_ADD = 0;
int ACTION_REMOVE = 1;
void actionSelected(int action);
}
</syntaxhighlight>
Implementing an interface
To implement an interface, a class uses the implements keyword and may implement several interfaces separated by commas. All interface methods must be provided with concrete implementations unless the class itself is declared abstract.
<syntaxhighlight lang="java">
interface RequestListener {
int requestReceived();
}
class ActionHandler implements ActionListener, RequestListener {
public void actionSelected(int action) { }
public int requestReceived() { return 0; }
}
// Using interface reference
RequestListener listener = new ActionHandler();
listener.requestReceived();
</syntaxhighlight>
Functional interfaces and lambda expressions
With Java SE 8, functional interfaces—those containing exactly one abstract method—were introduced, facilitating succinct coding through lambda expressions. This advancement promotes streamlined development and supports functional programming techniques beneficial in business software solutions. Applying the optional @FunctionalInterface annotation ensures compile-time verification of the interface's functional nature.
<syntaxhighlight lang="java">
// Functional interface
@FunctionalInterface
interface Calculation {
int calculate(int a, int b);
}
// Method receiving functional interface
int runCalculation(Calculation calculation) {
return calculation.calculate(1, 2);
}
// Lambda expression example
runCalculation((a, b) -> a + b);
// Equivalent anonymous class implementation
runCalculation(new Calculation() {
@Override
public int calculate(int a, int b) {
return a + b;
}
});
</syntaxhighlight>
Lambda expressions benefit from type inference and concise syntax; parentheses can be omitted when there is only one parameter.
<syntaxhighlight lang="java">
// Lambda with explicit parameter types and block body
runCalculation((int a, int b) -> { return a + b; });
// Functional interface with single parameter
interface StringExtender {
String extendString(String input);
}
// Lambda assignment
StringExtender extender = input -> input + " Extended";
</syntaxhighlight>
Method references
When an existing method matches the signature of a functional interface's abstract method, it can replace a lambda expression using a method reference. These references fall into several categories:
Type of reference
Example
Corresponding lambda expression
Static method
Integer::sum
(a, b) -> a + b
Bound instance method
"LongString"::substring
i -> "LongString".substring(i)
Unbound instance method
String::isEmpty
s -> s.isEmpty()
Class constructor
ArrayList<String>::new
capacity -> new ArrayList<String>(capacity)
Array constructor
String[]::new
size -> new String[size]
For instance, invoking runCalculation can be simplified as:
<syntaxhighlight lang="java">
runCalculation(Integer::sum);
</syntaxhighlight>
Inheritance
Unlike classes, interfaces are capable of inheriting from multiple other interfaces simultaneously. However, if two or more inherited interfaces declare members with identical names, those members become ambiguous and cannot be accessed.
<syntaxhighlight lang="java">
interface EventListener extends ActionListener, RequestListener {
}
</syntaxhighlight>
Default methods
With the arrival of Java SE 8, default methods were introduced to allow new methods within interfaces without disrupting existing implementations. These methods provide a body and are executed unless explicitly overridden by implementing classes.
<syntaxhighlight lang="java">
interface StringManipulator {
String extendString(String input);
default String shortenString(String input) {
return input.substring(1);
}
}
class PartialStringManipulator implements StringManipulator {
@Override
public String extendString(String input) {
return input + " Extended";
}
}
</syntaxhighlight>
Static methods
Java SE 8 also brought static methods into interfaces, functioning similarly to static methods in classes.
<syntaxhighlight lang="java">
interface StringUtils {
static String shortenByOneSymbol(String input) {
return input.substring(1);
}
}
StringUtils.shortenByOneSymbol("Test");
</syntaxhighlight>
Private methods
Java 9 introduced private methods within interfaces to facilitate code reuse among default methods while keeping these helper methods hidden from the implementing classes.
<syntaxhighlight lang="java">
interface Logger {
default void logError() {
log(Level.ERROR);
}
default void logInfo() {
log(Level.INFO);
}
private void log(Level level) {
SystemLogger.log(level.id);
}
}
</syntaxhighlight>
Annotations
Annotations in Java serve as embedded metadata within source code and were first introduced in J2SE 5.0.
Annotation types
Java provides built-in annotation types and supports defining custom annotations. Declaring an annotation type resembles creating an interface but uses the @ symbol preceding interface. All annotation types implicitly implement java.lang.annotation.Annotation and cannot extend other interfaces or classes.
<syntaxhighlight lang="java">
@interface BlockingOperations {
}
</syntaxhighlight>
Annotations may declare parameterless methods that do not throw exceptions and can include enums or other annotations. Methods may specify default values using the default keyword:
<syntaxhighlight lang="java">
@interface BlockingOperations {
boolean fileSystemOperations();
boolean networkOperations() default false;
}
</syntaxhighlight>
Usage of annotations
Annotations can be applied to various program elements such as packages, classes (including enums), interfaces (including other annotations), fields, methods, parameters, constructors, local variables, and enum constants. They are denoted by @ followed by the annotation name and optional element-value pairs enclosed in parentheses. Elements without predefined defaults must be explicitly assigned values.
<syntaxhighlight lang="java">
@BlockingOperations(/*mandatory*/ fileSystemOperations,
/*optional*/ networkOperations = true)
void openOutputStream() { //Annotated method
}
</syntaxhighlight>
There are two concise annotation forms: marker annotations, which omit element assignments entirely, and single-element annotations, used when only one element lacks a default value. In the latter case, the element name can be omitted:
<syntaxhighlight lang="java">
@Unused // Equivalent to @Unused()
void travelToJupiter() {
}
</syntaxhighlight>
<syntaxhighlight lang="java">
/* Equivalent to @BlockingOperations(fileSystemOperations = true).
networkOperations uses its default */
@BlockingOperations(true)
void openOutputStream() {
}
</syntaxhighlight>
Generics
Generics, introduced with J2SE 5.0, provide parameterized types that allow code to work with different data types safely without requiring explicit type declarations. They enhance type safety by identifying type-related errors during compilation. In contrast to C#, Java implements generics through type erasure, which means that type parameter information is not retained at runtime.
Generic classes
Type parameters can be added to classes by including type variables within angle brackets (< and >) immediately following the class name. These variables can then be utilized throughout the class members in place of specific types. It is possible to declare multiple type parameters separated by commas.
Type parameters may be limited to subclasses of a particular class or interfaces by using the extends keyword. When bounding a type variable by both a class and interfaces, the class must be listed first, followed by interface names separated with &.
<syntaxhighlight lang="java">
/* Class with two type parameters: T must extend
ArrayList and implement Formattable */
public class Mapper<T extends ArrayList & Formattable, V> {
public void add(T array, V item) {
array.add(item);
}
}
</syntaxhighlight>
When specifying or instantiating parameterized types, actual types replace the type variables using the same syntax as in the class declaration.
<syntaxhighlight lang="java">
/* Mapper instantiated with CustomList as T and Integer as V.
CustomList must extend ArrayList and implement Formattable */
Mapper<CustomList, Integer> mapper = new Mapper<CustomList, Integer>();
</syntaxhighlight>
Starting from Java SE 7, the diamond operator (<>) enables type inference, which simplifies object creation:
<syntaxhighlight lang="java">
Mapper<CustomList, Integer> mapper = new Mapper<>();
</syntaxhighlight>
Wildcards (?) may substitute explicit type parameters when declaring generic variables. These wildcards can be bounded by specifying upper bounds with extends or lower bounds with super.
<syntaxhighlight lang="java">
/* Accepts any Mapper instance with CustomList as the first parameter,
regardless of the second parameter's type */
Mapper<CustomList, ?> mapper;
mapper = new Mapper<CustomList, Boolean>();
mapper = new Mapper<CustomList, Integer>();
/* Accepts only Mappers where the second parameter extends Number */
void addMapper(Mapper<?, ? extends Number> mapper) {
}
</syntaxhighlight>
Generic methods and constructors
Generics can also be applied to individual methods or constructors. The type parameters are declared before the method’s return type or before the constructor name.
<syntaxhighlight lang="java">
class Mapper {
// Generic constructor in a non-generic class
<T, V> Mapper(T array, V item) {
}
}
/* Method that accepts arrays containing elements of the same
type as the searched item or its subclasses */
static <T, V extends T> boolean contains(T item, V[] arr) {
for (T currentItem : arr) {
if (item.equals(currentItem)) {
return true;
}
}
return false;
}
</syntaxhighlight>
Generic Interfaces
Just like classes, interfaces can also be defined with type parameters.
<syntaxhighlight lang="java">
interface Expandable<T extends Number> {
void addItem(T item);
}
// Class with type parameter
class Array<T extends Number> implements Expandable<T> {
void addItem(T item) {
}
}
// Class without type parameter, specifying type explicitly
class IntegerArray implements Expandable<Integer> {
void addItem(Integer item) {
}
}
</syntaxhighlight>
See also
References
External links
- The Java Language Specification, Third edition – Definitive guide to the Java language