Вы находитесь на странице: 1из 683

ZetCode Java tutorial

This is a Java tutorial. In this tutorial you will learn the Java language. The tutorial is suitable for beginners.

Java
Java is a modern, high-level, general-purpose, object-oriented programming language. The design goals of the language were software robustness, durability and programmer productivity. It can be used to create console applications, GUI applications, web applications, both on PCs or embedded systems.

Java language
In this part of the Java tutorial, we will introduce the Java programming language.

Goal
The goal of this tutorial is to get you started with the Java programming language. The tutorial covers the core of the Java language. This tutorial uses command line compilers to build applications.

Java
Java is a high-level, general-purpose, object-oriented programming language. The main design goals of the language were robustness, portability, high performance and security. Java is a multithreaded and distributed programming language. It can be used to create console applications, GUI applications, web applications, both on PCs or embedded systems. Java is a programming language created by Sun Microsystems in 1991. The first publicly available version of Java was released in 1995. Today, the language is developed by Oracle corporation. Java excels in creating portable mobile applications, programming various appliances and in creating enterprise applications.

Popularity
There are currently several widely used programming languages. Java belongs to the most popular languages today. Several surveys put it into the top three languages in the world.

Java platforms
Java has four programming platforms:

Java Platform, Standard Edition (Java SE) Java Platform, Enterprise Edition (Java EE) 1

Java Platform, Micro Edition (Java ME) JavaFX

All Java platforms consist of a Java Virtual Machine (JVM) and an application programming interface (API). The Java Virtual Machine is a program, for a particular hardware and software platform, that runs Java applications. An API is a collection of software components that we can use to create other software components or applications. Java SE is used for developing desktop applications. Java SE's API provides the core functionality of the Java programming language. It consists of a virtual machine, development tools, deployment technologies, and other class libraries and toolkits used in Java applications. Java EE is built on top of the Java SE platform. The Java EE platform provides an API and runtime environment for developing and running web applications and large-scale, multi-tiered, scalable, reliable, and secure enterprise applications. Java ME is a subset of the Java SE. It provides an API and a small-footprint virtual machine for running Java applications on small devices, like mobile phones. JavaFX is a platform for creating rich internet applications using a lightweight user-interface API. In our tutorial, we use the Java SE platform to create simple console applications.

JDK
Strictly speaking, Java SE is a platform specification. Java Platform, Standard Edition Development Kit (JDK) is an official implementation of the Java SE by Oracle. There are also other implementations. For example free and open source OpenJDK or IBM's J9.
$ ls jdk1.7.0_02/ bin db COPYRIGHT include jre lib LICENSE man README.html release src.zip THIRDPARTYLICENSEREADME.txt

After we dowload and unpack the Oracles's JDK, we can see the contents of the JDK in the our jdk1.7.0_02 directory. The development tools are located in the bin/ subdirectory. The Java javac compiler and the java application launcher are located in this subdirectory. The jre/ subdirectory contains the JVM, class libraries and other files that help execute Java programs. The lib/ subdirectory has some additional class libraries and support files. The db/ subdirectory contains the Java DB, which is the Oracle's distribution of the Apache Derby database. In the include/ subdirectory we can find header files that support native-code programming. The src.zip file contains source files for all classes that make up the Java core API.

JVM
Java virtual machine (JVM) is a program that can execute Java bytecode. The JVM is included in the JDK. Java source code is written in files with the .java extension. The javac Java compiler will compile the Java source code into the Java bytecode; the compiled files have the .class extension. This bytecode is executed by JVM. The java tool is a launcher for Java applications. Oracle's JVM is called HotSpot. HotSpot is a Java virtual machine for desktops and servers. It has advanced techniques such as just-in-time compilation and adaptive optimization designed to improve performance.

Compiling a simple program


In order to develop Java applications, we need to dowloand a JDK. Oracle's official JDK can be downloaded from this dowload page.
$ mkdir -p com/zetcode

Inside the current working directory, we create a com/zetcode/ subdirectory. Java source files are organized in modules called packages. The packages must match the directory structure.
$ touch com/zetcode/SimpleExample.java

A SimpleExample.java source file is created in the com/zetcode/ subdirectory. Java source files have a .java extension.
package com.zetcode; public class SimpleExample { public static void main(String[] args) { } } System.out.println("This is simple Java example.");

This is a source code for a simple Java example. This example prints a message to the console.
package com.zetcode;

The package name must correspond to the directory structure in which the source file is located.
public class SimpleExample {

The public class name is required to match the file name.


$ javac com/zetcode/SimpleExample.java

Using the javac compiler, we compile the source code.


$ ls com/zetcode/ SimpleExample.class SimpleExample.java

The compiler generetes a Java bytecode, which is executed by the Java Virtual Machine. The bytecode has a .class extension.
$ java com.zetcode.SimpleExample This is simple Java example.

With the java application launcher, we execute the program. It starts a Java runtime environment, loading a specified class, and invoking that class's main method. The .class

extension is excluded; it is assumed. The program name is a fully qualified name of the program - com.zetcode.SimpleExample. It includes the name of the program and its package.

Sources
The following sources were used to create this tutorial:

Oracle's Java tutorial Java Platform, Standard Edition 7 API Specification The Java Language Specification

In this part of the Java tutorial, we have introduced the Java language.

Lexical structure
Computer languages, like human languages, have a lexical structure. A source code of a Java program consists of tokens. Tokens are atomic code elements. In Java we have comments, identifiers, literals, operators, separators and keywords. Java programs are composed of characters from the Unicode character set.

Comments
Comments are used by humans to clarify source code. There are three types of comments in Java. Comment type Meaning // comment Single-line comments /* comment */ Multi-line comments /** documentation */ Documentation comments If we want to add some small comment we can use single-line comments. For more complicated explanations, we can use multi-line comments. The documentation comments are used to prepare automatically generated documentation. This is generated with the javadoc tool.
package com.zetcode; /* This is Comments.java Author: Jan Bodnar ZetCode 2013

*/

public class Comments { // Program starts here public static void main(String[] args) { } System.out.println("This is Comments.java");

Comments are ignored by the Java compiler.


/* /* */ This is Comments.java Author: Jan Bodnar */ ZetCode 2013

Comments cannot be nested. The above code does not compile.

White space
White space in Java is used to separate tokens in the source file. It is also used to improve readability of the source code.
int i = 0;

White spaces are required in some places. For example between the int keyword and the variable name. In other places, white spaces are forbidden. They cannot be present in variable identifiers or language keywords.
int a=1; int b = 2; int c = 3;

The amount of space put between tokens is irrelevant for the Java compiler.

Identifiers
Identifiers are names for variables, methods, classes or parameters. Identifiers can have alphanumerical characters, underscores and dollar signs ($). It is an error to begin a variable name with a number. White space in names is not permitted. Identifiers are case sensitive. This means, that Name, name or NAME refer to three different variables. Identifiers also cannot match language keywords. There are also conventions related to naming of identifiers. The names should be descriptive. We should not use cryptic names for our identifiers. If the name consists of multiple words, each subsequent word is capitalized.
String name23; int _col; short car_age;

These are valid Java identifiers.


String 23name; int %col; short car age;

These are invalid Java identifiers. The following program demonstrates that the variable names are case sensitive. Event though the language permits this, it is not a recommended practice to do.
package com.zetcode; public class CaseSensitiveIdentifiers { public static void main(String[] args) { String name = "Robert"; String Name = "Julia"; System.out.println(name); System.out.println(Name);

} }

Name and name are two different identifiers. In Visual Basic, this would not be possible. In this language, variable names are not case sensitive.
$ java com.zetcode.CaseSensitiveIdentifiers Robert Julia

Literals
A literal is a textual representation of a particular value of a type. Literal types include boolean, integer, floating point, string, null, or character. Technically, a literal will be assigned a value at compile time, while a variable will be assigned at runtime.
int age = 29; String nationality = "Hungarian";

Here we assign two literals to variables. Number 29 and string "Hungarian" are literals.
package com.zetcode; public class Literals { public static void main(String[] args) { int age = 23; String name = "James"; boolean sng = true; String job = null; double weight = 68.5; char c = 'J'; System.out.format("His name is %s%n", name); System.out.format("His is %d years old%n", age); if (sng) { System.out.println("He is single"); } else {

System.out.println("He is in a relationship"); } System.out.format("His job is %s%n", job); System.out.format("He weighs %f kilograms%n", weight); System.out.format("His name begins with %c%n", c);

} }

In the above example, we have several literal values. 23 is an integer literal. "James" is a string literal. The true is a boolean literal. The null is a literal that represents a missing value. 68.5 is a floating point literal. 'J' is a character literal.
$ java com.zetcode.Literals His name is James His is 23 years old He is single His job is null He weighs 68.500000 kilograms His name begins with J

This is the output of the program.

Operators
An operator is a symbol used to perform an action on some value. Operators are used in expressions to describe operations involving one or more operands.
+ = == || += != && * -= < >> / *= > % /= &= ^ %= >>= ?: & ^= <<= | ++ >= ! <= -~

<<

This is a partial list of Java operators. We will talk about operators later in the tutorial.

Separators
A separator is a sequence of one or more characters used to specify the boundary between separate, independent regions in plain text or other data stream.
[ ] ( ) { } , ; . String language = "Java";

The double quotes are used to mark the beginning and the end of a string. The semicolon (;) character is used to end each Java statement.
System.out.println("Java language");

Parentheses (round brackets) always follow a method name. Between the parentheses we declare the input parameters. The parentheses are present even if the method does not take any parameters. The System.out.println() method takes one parameter, a string value. The dot character separates the class name (System) from the member (out) and the member from the method name (println()). 7

int[] array = new int[5] { 1, 2, 3, 4, 5 };

The square brackets [] are used to denote an array type. They are also used to access or modify array elements. The curly brackets {} are also used to initiate arrays. The curly brackets are also used enclose the body of a method or a class.
int a, b, c;

The comma character separates variables in a single declaration.

Keywords
A keyword is a reserved word in Java language. Keywords are used to perform a specific task in the computer program. For example, define variables, do repetitive tasks or perform logical operations. Java is rich in keywords. Many of them will be explained in this tutorial.
abstract assert synchronized boolean break byte case catch char class const continue default do double else enum extends final finally float for goto if implements import instanceof int interface long native new package private protected public return short static strictfp super switch this throw throws transient try void volatile while

In the following small program, we use several Java keywords.


package com.zetcode; public class Keywords { public static void main(String[] args) { for (int i = 0; i <= 5; i++) { System.out.println(i); } } }

The package, public, class, static, void, int, for tokens are Java keywords.

Conventions
Conventions are best practices followed by programmers when writing source code. Each language can have its own set of conventions. Conventions are not strict rules; they are merely recommendations for writing good quality code. We mention a few conventions that are recognized by Java programmers. (And often by other programmers too).

Class names begin with an uppercase letter Method names begin with a lowercase letter The public keyword precedes the static keyword when both are used The parameter name of the main() method is called args Constants are written in uppercase Each subsequent word in an identifier name begins with a capital letter

In this part of the Java tutorial, we covered some basic lexis for the Java language.

Basics
In this part of the Java tutorial, we will cover basic programming concepts of the Java language. We begin with some simple programs. We will work with variables, constants and basic data types. We will read and write to the console. We will mention variable interpolation. We start with a very simple code example. The following code is put into Simple.java file. The naming is important here. A public class of a Java program must match the name of the file.
package com.zetcode; public class Simple { public static void main(String[] args) { } } System.out.println("This is Java");

Java code is strictly organized from the very beginning. A file of a Java code may have one or more classes, out of which only one can be declared public.
package com.zetcode;

Packages are used to organize Java classes into groups, which usually share similar functionality. Packages are similar to namespaces and modules in other programming languages. For a simple code example, a package declaration may be omitted. This will create a so called default package. However, in this tutorial we will use a package for all examples. Another important thing is that a directory structure must reflect the package name. In our case the source file Simple.java with a package com.zetcode must be placed into a directory named com/zetcode/. The package statement must be the first line in the source file.
public class Simple { } ...

A class is a basic building block of a Java program. The public keyword gives unrestricted access to this class. The above code is a class definition. The definition has a body that starts

with a left curly brace { and ends with a right curly brace }. Only one class can be declared public in one source file. Also note the name of the class. Its name must match the file name. The source file is called Simple.java and the class Simple. It is a convention that the names of classes start with an uppercase letter.
public static void main(String[] args) { ... }

The main() is a method. A method is a piece of code created to do a specific job. Instead of putting all code into one place, we divide it into pieces called methods. This brings modularity to our application. Each method has a body in which we place statements. The body of a method is enclosed by curly brackets. The specific job for the main() method is to start the application. It is the entry point to each console Java program. The method is declared to be static. This static method can be called without the need to create an instance of the Java class. First we need to start the application and after that, we are able to create instances of classes. The void keyword states that the method does not return a value. Finally, the public keyword makes the main() method available to the outer world without restrictions. These topics will be later explained in more detail.
System.out.println("This is Java");

In the main() method, we put one statement. The statement prints the "This is Java" string to the console. Each statement must be finished with a semicolon (;) character. This statement is a method call. We call the println() method of the System class. The class represents the standard input, output, and error streams for console applications. We specify the fully qualified name of the println() method.
$ pwd /home/janbodnar/programming/java/basics/simple $ javac com/zetcode/Simple.java

Java source code is placed into files with the .java extension. The javac tool is the Java compiler. We compile the Java source into Java classes. Note the directory structure. The structure must match the Java package.
$ ls com/zetcode/ Simple.class Simple.java Simple.java~

After we compile the source, we get a Java class file with the .class extension. It contains a Java bytecode which can be executed on the Java Virtual Machine (JVM). Simple.class is a Java program which can be executed with the java tool.
$ java com.zetcode.Simple This is Java

We execute the program with the java tool. The java tool launches a Java application. It does this by starting a Java runtime environment, loading a specified class, and invoking that class's main method. The parameter to the java tool is the fully qualified name of the Java class. Note that the .class extension is omitted.

10

Reading values
The second example will show, how to read a value from a console.
package com.zetcode; import java.util.Scanner; public class ReadLine { public static void main(String[] args) { System.out.print("Write your name:"); Scanner sc = new Scanner(System.in); String name = sc.nextLine(); System.out.println("Hello " + name); } }

A prompt is shown on the terminal window. The user writes his name on the terminal and the value is read and printed back to the terminal.
import java.util.Scanner;

The Java standard library has a huge collection of classes available for programmers. They are organized inside packages. The Scanner class is one of them. When we import a class with the import keyword, we can refer later to the class without the full package name. Otherwise we must use the fully qualified name. The import allows a shorthand referring for classes. This is different from some other languages. For instance in Python, the import keyword imports objects into the namespace of a script. In Java, the import keyword only saves typing by allowing to refer to types without specifying the full name.
System.out.print("Write your name:");

We print a message to the user. We use the print() method which does not start a new line. The user then types his response next to the message.
Scanner sc = new Scanner(System.in);

A new instance of the Scanner class is created. New objects are created with the new keyword. The constructor of the object follows the new keyword. We put one parameter to the constructor of the Scanner object. It is the standard input stream. This way we are ready to read from the terminal. The Scanner is a simple text scanner which can parse primitive types and strings.
String name = sc.nextLine();

Objects have methods which perform certain tasks. The nextLine() method reads the next line from the terminal. It returns the result in a String data type. The returned value is stored in the name variable which we declare to be of String type.

11

System.out.println("Hello " + name);

We print a message to the terminal. The message consists of two parts. The "Hello " string and the name variable. We concatenate these two values into one string using the + operator. This operator can concatenate two or more strings.
$ java com.zetcode.ReadLine Write your name:Jan Bodnar Hello Jan Bodnar

This is a sample execution of the second program.

Command line arguments


Java programs can receive command line arguments. They follow the name of the program when we run it.
package com.zetcode; public class CommandLineArgs { public static void main(String[] args) { for (String arg : args) { } } } System.out.println(arg);

Command line arguments can be passed to the main() method.


public static void main(String[] args)

The main() method receives a string array of command line arguments. Arrays are collections of data. An array is declared by a type followed by a pair of square brackets []. So the String[] args construct declares an array of strings. The args is an parameter to the main() method. The method then can work with parameters which are passed to it.
for (String arg : args) { System.out.println(arg); }

We go through the array of these arguments with a for loop and print them to the console. The for loop consists of cycles. In this case, the number of cycles equals to the number of parameters in the array. In each cycle, a new element is passed to the arg variable from the args array. The loop ends when all elements of the array were passed. The for statement has a body enclosed by curly brackets {}. In this body, we place statements that we want to be executed in each cycle. In our case, we simply print the value of the arg variable to the terminal. Loops and arrays will be described in more detail later.
$ java com.zetcode.CommandLineArgs 1 2 3 4 5 1

12

2 3 4 5

We provide four numbers as command line arguments and these are printed to the console. When we launch programs from the command line, we specify the arguments right after the name of the program. In Integraged Development Environments (IDE) like Netbeans, we specify these parameters in a dialog. In Netbeans, we rigth click on the project and select Properties. From the Categories list, we select the Run option. In the Arguments edit control, we write our arguments.

Figure: Command line arguments

Variables
A variable is a place to store data. A variable has a name and a data type. A data type determines what values can be assigned to the variable. Integers, strings, boolean values etc. Over the time of the program, variables can obtain various values of the same data type. Variables in Java are always initialized to the default value of their type before any reference to the variable can be made.
package com.zetcode; public class Variables { public static void main(String[] args) { String city = "New York"; String name = "Paul"; int age = 34; String nationality = "American"; System.out.println(city); System.out.println(name); System.out.println(age); System.out.println(nationality); city = "London"; System.out.println(city); } }

In the above example, we work with four variables. Three of the variables are strings. The age variable is an integer. The int keyword is used to declare an integer variable.

13

String city = "New York";

We declare a city variable of the string type and initialize it to the "New York" value.
String name = "Paul"; int age = 34;

We declare and initialize two variables. We can put two statements into one line. Since each statement is finished with a semicolon, the Java compiler knows that there are two statements in one line. But for readability reasons, each statement should be on a separate line.
System.out.println(city); System.out.println(name); System.out.println(age); System.out.println(nationality);

We print the values of the variables to the terminal.


city = "London"; System.out.println(city);

We assign a new value to the city variable and later print it.
$ java com.zetcode.Variables New York Paul 34 American London

This is the output of the example.

Constants
Unlike variables, constants cannot change their initial values. Once initialized, they cannot be modified. Constants are created with the final keyword.
package com.zetcode; public class Constants { public static void main(String[] args) { final int WIDTH = 100; final int HEIGHT= 150; int var = 40; var = 50; } } //WIDTH = 110;

In this example, we declare two constants and one variable.


final int WIDTH = 100;

14

final int HEIGHT= 150;

We use the final keyword to inform the compiler that we declare a constant. It is a convention to write constants in uppercase letters.
int var = 40; var = 50;

We declare and initialize a variable. Later, we assign a new value to the variable. It is legal.
// WIDTH = 110;

Assigning new values to constants is not possible. If we uncomment this line, we will get a compilation error: "Uncompilable source code - cannot assign a value to final variable WIDTH".

String formatting
Building strings from variables is a very common task in programming. Java language has the System.format() method to format strings. Some dynamic languages like Perl, PHP or Ruby support variable interpolation. Variable interpolation is replacing variables with their values inside string literals. Java language does not allow this. It has string formatting insted.
package com.zetcode; public class StringFormatting { public static void main(String[] args) { int age = 34; String name = "William"; String output = String.format("%s is %d years old.", name, age); } } System.out.println(output);

In Java, strings are immutable. We cannot modify an existing string. We must create a new string from existing strings and other types. In the code example, we create a new string. We also use values from two variables.
int age = 34; String name = "William";

Here we have two variables, one integer and one string.


String output = String.format("%s is %d years old.", name, age);

We use the format() method of the built-in String class. The %s and %d are control characters which are later evaluated. The %s accepts string values, the %d integer values. 15

$ java com.zetcode.StringFormatting William is 34 years old.

This is the output of the example. This chapter covered some basics of the Java language.

Data types
In this part of the Java tutorial, we will talk about data types. Computer programs work with data. Spreadsheets, text editors, calculators or chat clients. Tools to work with various data types are essential part of a modern computer language. A data type is a set of values and the allowable operations on those values. Java programming language is a statically typed language. It means that every variable and every expression has a type that is known at compile time. Java language is also a strongly typed language, because types limit the values that a variable can hold or that an expression can produce, limit the operations supported on those values, and determine the meaning of the operations. Strong static typing helps detect errors at compile time. Variables in dynamically typed languages like Ruby or Python can receive different data types over the time. In Java, once a variable is declared to be of a certain data type, it cannot hold values of other data types. There are two fundamental data types in Java: primitive types and reference types. Primitive types are:

boolean char byte short int long float double

There is a specific keyword for each of these types in Java. Primitive types are not objects in Java. Primitive data types cannot be stored in Java collections which work only with objects. They can be placed into arrays instead. The reference types are:

class types interface types array types

There is also a special null type which represents a non-existing value. In Ruby programming language, everything is an object. Even basic data types. 16

#!/usr/bin/ruby 4.times { puts "Ruby" }

This Ruby script prints four times "Ruby" string to the console. We call a times method on the 4 number. This number is an object in Ruby. Java has a different approach. It has primitive data types and wrapper classes. Wrapper classes transform primitive types into objects. Wrapper classes are covered later in this chapter.

Boolean values
There is a duality built in our world. There is a Heaven and Earth, water and fire, jing and jang, man and woman, love and hatred. In Java the boolean data type is a primitive data type having one of two values: true or false. Happy parents are waiting a child to be born. They have chosen a name for both possibilities. If it is going to be a boy, they have chosen Robert. If it is going to be a girl, they have chosen Victoria.
package com.zetcode; import java.util.Random; public class BooleanType { public static void main(String[] args) { String name = ""; Random r = new Random(); boolean male = r.nextBoolean(); if (male == true) { name = "Robert"; } if (male == false) { } name = "Victoria";

System.out.format("We will use name %s%n", name); System.out.println(9 > 8); } }

The program uses a random number generator to simulate our case.


Random r = new Random(); boolean male = r.nextBoolean();

These two lines randomly choose a boolean value.

17

if (male == true) { } name = "Robert";

If the boolean variable male equals to true, we set the name variable to "Robert". The if keyword works with boolean values.
if (male == false) { } name = "Victoria";

If the random generator chooses false than we set the name variable to "Victoria".
System.out.println(9 > 8);

Relational operators result in a boolean value. This line prints true to the console.
$ java com.zetcode.BooleanType We will use name Robert true $ java com.zetcode.BooleanType We will use name Victoria true $ java com.zetcode.BooleanType We will use name Victoria true

Running the program several times.

Integers
Integers are a subset of the real numbers. They are written without a fraction or a decimal component. Integers fall within a set Z = {..., -2, -1, 0, 1, 2, ...} Integers are infinite. In computer languages, integers are (usually) primitive data types. Computers can practically work only with a subset of integer values, because computers have finite capacity. Integers are used to count discrete entities. We can have 3, 4, 6 humans, but we cannot have 3.33 humans. We can have 3.33 kilograms, 4.564 days, or 0.4532 kilomenters. Type Size Range byte 8 bits -128 to 127 short 16 bits -32,768 to 32,767 char 16 bits 0 to 65,535 int 32 bits -2,147,483,648 to 2,147,483,647 long 64 bits -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807 Table: Integer types in Java These integer types may be used according to our needs. We can then use the byte type for a variable that stores the number of children a woman gave birth to. The oldest verified person

18

died at 122, therefore we would probably choose at least the short type for the age variable. This will save us some memory. Integer literals may be expressed in decimal, hexadecimal, octal, or binary notations. If a number has an ASCII letter L or l suffix, it is of type long. Otherwise it is of type int. The capital letter L is preffered for specifying long numbers, since lowercase l can be easily confused with number 1.
int a = 34; byte b = 120; short c = 32000; long d = 45000; long e = 320000L;

We have five assignments. 34, 120, 32000 and 45000 are integer literals of type int. There are no integer literals for byte and short types. If the values fit into the destination type, the compiler does not protest and performs a conversion automatically. For long numbers smaller than Integer.MAX_VALUE, the L suffix is optional.
long x = 2147483648L; long y = 2147483649L;

For long numbers larger than Integer.MAX_VALUE, we must add the L suffix. When we work with integers, we deal with discrete items. For instance, we can use integers to count apples.
package com.zetcode; public class Apples { public static void main(String[] args) { int baskets = 16; int applesInBasket = 24; int total = baskets * applesInBasket; } } System.out.format("There are total of %d apples%n", total);

In our program, we count the total amount of apples. We use the multiplication operation.
int baskets = 16; int applesInBasket = 24;

The number of baskets and the number of apples in each basket are integer values.
int total = baskets * applesInBasket;

Multiplying those values we get an integer too.


$ java com.zetcode.Apples

19

There are total of 384 apples

This is the output of the program. Integers can be specified in four different notations in Java. Decimal, octal, hexadecimal and binary. The binary notation was introduced in Java 7. Decimal numbers are used normally, as we know them. Octal numbers are preceded with a 0 character and followed by octal numbers. Hexadecimal numbers are preceded with 0x characters and followed by hexadecimal numbers. Binary numbers start with 0b and are followed by binary numbers.
package com.zetcode; public class IntegerNotations { public static void main(String[] args) { int int int int n1 n2 n3 n4 = = = = 31; 0x31; 031; 0b1001;

System.out.println(n1); System.out.println(n2); System.out.println(n3); System.out.println(n4); } }

We have four integer variables. Each of the variables is assigned a value with a different integer notation.
int int int int n1 n2 n3 n4 = = = = 31; 0x31; 031; 0b1001;

The first is decimal, the second hexadecimal, the third octal and the fourth binary.
$ java com.zetcode.IntegerNotations 31 49 25 9

We see the output of the com.zetcode.IntegerNotations program. Big numbers are difficult to read. If we have a number like 245342395423452, we find it difficult to read it quickly. Outside computers, big numbers are separated by spaces or commas. Since Java SE 1.7, it is possible to separate integers with an underscore. The underscore cannot be used at the beginning or end of a number, adjacent to a decimal point in a floating point literal, and prior to an F or L suffix.
package com.zetcode;

20

public class UsingUnderscores { public static void main(String[] args) { long a = 23482345629L; long b = 23_482_345_629L; System.out.println(a == b); } }

This code sample demonstrates the usage of underscores in Java.


long a = 23482345629L; long b = 23_482_345_629L;

We have two identical long numbers. In the second one we separate every three digits in a number. Comparing these two numbers we receive a boolean true. The L suffix tells the compiler that we have a long number literal. Java byte, short, int and long types are used do represent fixed precision numbers. Which means, that they can represent a limited amount of integers. The largest integer number that a long type can represent is 9223372036854775807. If we deal with even larger numbers, we have to use the java.math.BigInteger class. It is used to represet immutable arbitrary precision integers. Arbitrary precision integers are only limited by the amount of computer memory available.
package com.zetcode; import java.math.BigInteger; public class VeryLargeIntegers { public static void main(String[] args) { System.out.println(Long.MAX_VALUE); BigInteger b = new BigInteger("92233720368547758071"); BigInteger c = new BigInteger("52498235605326345645"); BigInteger a = b.multiply(c); System.out.println(a); } }

With the help of the java.math.BigInteger class, we multiply two very large numbers.
System.out.println(Long.MAX_VALUE);

We print the largest integer value which can be represented by a long type.
BigInteger b = new BigInteger("92233720368547758071"); BigInteger c = new BigInteger("52498235605326345645");

We define two BigInteger objects. They both hold larger values that a long type can hold. 21

BigInteger a = b.multiply(c);

With the multiply() method, we multiply the two numbers. Note that the BigInteger numbers are immutable. The operation returns a new value which we assign to a new variable.
System.out.println(a);

The computed integer is printed to the console.


$ java com.zetcode.VeryLargeIntegers 9223372036854775807 4842107582663807707870321673775984450795

This is the example output.

Arithmetic overflow
An arithmetic overflow is a condition that occurs when a calculation produces a result that is greater in magnitude than that which a given register or storage location can store or represent.
package com.zetcode; public class Overflow { public static void main(String[] args) { byte a = 126; System.out.println(a); a++; System.out.println(a); a++; System.out.println(a); a++; } } System.out.println(a);

In this example, we try to assign a value beyond the range of a data type. This leads to an arithmetic overflow.
$ java com.zetcode.Overflow 126 127 -128 -127

When an overflow occurs, the variable is reset to negative upper range value. In contrast, Visual Basic programming language would throw an exception.

22

Floating point numbers


Real numbers measure continuous quantities, like weight, height, or speed. Floating point numbers represent an approximation of real numbers in computing. In Java we have two primitive floating point types: float and double. The float is a single precision type which store numbers in 32 bits. The double is a double precision type which store numbers in 64 bits. These two types have fixed precision and cannot represent exactly all real numbers. In situations where we have to work with precise numbers, we can use the BigDecimal class. Floating point numbers with an F/f suffix are of type float, double numbers have D/d suffix. The suffix for double numbers is optional. Let's say a sprinter for 100m ran 9.87s. What is his speed in km/h?
package com.zetcode; public class Sprinter { public static void main(String[] args) { float distance; float time; float speed; distance = 0.1f; time = 9.87f / 3600; speed = distance / time; speed); } } System.out.format("The average speed of a sprinter is %f km/h%n",

In this example, it is necessary to use floating point values. The low precision of the float data type does not pose a problem in this case.
distance = 0.1f;

100m is 0.1km.
time = 9.87f / 3600;

9.87s is 9.87/60*60h.
speed = distance / time;

To get the speed, we divide the distance by the time.


$ java com.zetcode.Sprinter The average speed of a sprinter is 36.474163 km/h

23

This is the output of the com.zetcode.Sprinter program. A small rounding error in the number does not affect our understanding of the sprinter's speed. The float and double types are inexact.
package com.zetcode; public class FloatingInPrecision { public static void main(String[] args) { double a = 0.1 + 0.1 + 0.1; double b = 0.3; System.out.println(a); System.out.println(b); } } System.out.println(a == b);

The code example illustrates the inexact nature of the floating point values.
double a = 0.1 + 0.1 + 0.1; double b = 0.3;

We define two double values. The D/d suffix is optional. At first sight, they should be equal.
System.out.println(a); System.out.println(b);

Printing them will show a very small difference.


System.out.println(a == b);

This line will return false.


$ java com.zetcode.FloatingInPrecision 0.30000000000000004 0.3 false

There is a small margin error. Therefore, the comparison operator returns a boolean false. When we work with money, currency, and generally in business applications, we need to work with precise numbers. The rounding errors of the basic floating point types are not acceptable.
package com.zetcode; public class CountingMoney { public static void main(String[] args) { float c = 1.46f; float sum = 0f;

24

for (int i=0; i<100_000; i++) { sum += c; } } } System.out.println(sum);

The 1.46f represents 1 Euro and 46 Cents. We create a sum from 100000 such amounts.
for (int i=0; i<100_000; i++) { sum += c; }

In this loop, we create a sum from 100000 such amounts of money.


$ java com.zetcode.CountingMoney 146002.55

The calculation leads to an error of 55 Cents. To avoid this margin error, we utilize the BigDecimal class. It is used to hold immutable, arbitrary precision signed decimal numbers.
package com.zetcode; import java.math.BigDecimal; public class CountingMoney2 { public static void main(String[] args) { BigDecimal c = new BigDecimal("1.46"); BigDecimal sum = new BigDecimal("0"); for (int i=0; i<100_000; i++) { } } sum = sum.add(c);

System.out.println(sum); }

We do the same operation with the same amount of money.


BigDecimal c = new BigDecimal("1.46"); BigDecimal sum = new BigDecimal("0");

We define two BigDecimal numbers.


for (int i=0; i<100_000; i++) { sum = sum.add(c);

25

The BigDecimal number is immutable, therefore a new object is always assigned to the sum variable in every loop.
$ java com.zetcode.CountingMoney2 146000.00

In this example, we get the precise value. Java supports the scientific syntax of the floating point values. Also known as exponential notation, it is a way of writing numbers too large or small to be conveniently written in standard decimal notation.
package com.zetcode; import java.math.BigDecimal; import java.text.DecimalFormat; public class ScientificNotation { public static void main(String[] args) { double n = 1.235E10; DecimalFormat dec = new DecimalFormat("#.00"); System.out.println(dec.format(n)); BigDecimal bd = new BigDecimal("1.212e-19"); System.out.println(bd.toEngineeringString()); System.out.println(bd.toPlainString());

} }

We define two floating point values using the scientific notation.


double n = 1.235E10;

This is a floating point value of a double type, written in scientific notation.


DecimalFormat dec = new DecimalFormat("#.00"); System.out.println(dec.format(n));

We use the DecimalFormat class to arrange our double value into standard decimal format.
BigDecimal bd = new BigDecimal("1.212e-19"); System.out.println(bd.toEngineeringString()); System.out.println(bd.toPlainString());

The BigDecimal class takes a floating poing value in a scientific notation as a parameter. We use two methods of the class to print the value in the engineering and plain strings.

26

$ java com.zetcode.ScientificNotation 12350000000.00 121.2E-21 0.0000000000000000001212

This is the example output.

Enumerations
Enumerated type (also called enumeration or enum) is a data type consisting of a set of named values. A variable that has been declared as having an enumerated type can be assigned any of the enumerators as a value. Enumerations make the code more readable. Enumerations are useful when we deal with variables that can only take one out of a small set of possible values.
package com.zetcode; public class Enumerations { enum Days { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY

public static void main(String[] args) { Days day = Days.MONDAY; if (day == Days.MONDAY) { System.out.println("It is Monday"); } System.out.println(day); for (Days d : Days.values()) { } } } System.out.println(d);

In our code example, we create an enumeration for week days.


enum Days { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY,

27

SUNDAY

An enumeration representing the days of a week is created with a enum keyword. Items of an enumeration are constants. By convention, constants are written in uppercase letters.
Days day = Days.MONDAY;

We have a variable called day which is of enumerated type Days. It is initialized to Monday.
if (day == Days.MONDAY) { System.out.println("It is Monday"); }

This code is more readable than if comparing a day variable to some number.
System.out.println(day);

This line prints Monday to the console.


for (Days d : Days.values()) { } System.out.println(d);

This loop prints all days to the console. The static values() method returns an array containing the constants of this enum type, in the order they are declared. This method may be used to iterate over the constants with the enhanced for statement. The enhanced for goes through the array, element by element, and prints them to the terminal.
It is Monday MONDAY MONDAY TUESDAY WEDNESDAY THURSDAY FRIDAY SATURDAY SUNDAY

This is the example output.

Strings and chars


A String is a data type representing textual data in computer programs. A string in Java is a sequence of characters. A char is a single character. Strings are enclosed by double quotes. Since strings are very important in every programming language, we will dedicate a whole chapter to them. Here we only drop a small example.
package com.zetcode; public class StringsChars {

28

public static void main(String[] args) { String word = "ZetCode"; char c = word.charAt(0); char d = word.charAt(3); System.out.println(c); System.out.println(d); } }

The program prints Z character to the terminal.


String word = "ZetCode";

Here we create a string variable and assign it "ZetCode" value.


char c = word.charAt(0);

The charAt() method returns the char value at the specified index. The first char value of the sequence is at index 0, the next at index 1, and so on.
$ java com.zetcode.StringsChars Z C

The program prints the first and the fourth character of the "ZetCode" string to the console.

Arrays
Array is a complex data type which handles a collection of elements. Each of the elements can be accessed by an index. All the elements of an array must be of the same data type. We dedicate a whole chapter to arrays; here we show only a small example.
package com.zetcode; public class ArraysExample { public static void main(String[] args) { int[] numbers = new int[5]; numbers[0] numbers[1] numbers[2] numbers[3] numbers[4] = = = = = 3; 2; 1; 5; 6;

int len = numbers.length; for (int i = 0; i < len; i++) { System.out.println(numbers[i]);

29

} }

In this example, we declare an array, fill it with data and then print the contents of the array to the console.
int[] numbers = new int[5];

We create an integer array which can store up to 5 integers. So we have an array of five elements, with indexes 0..4.
numbers[0] numbers[1] numbers[2] numbers[3] numbers[4] = = = = = 3; 2; 1; 5; 6;

Here we assign values to the created array. We can access the elements of an array by the array access notation. It consists of the array name followed by square brackets. Inside the brackets we specify the index to the element that we want.
int len = numbers.length;

Each array has a length property which returns the number of elements in the array.
for (int i = 0; i < len; i++) { } System.out.println(numbers[i]);

We traverse the array and print the data to the console.


$ java com.zetcode.ArraysExample 3 2 1 5 6

This is the output of the com.zetcode.ArraysExample program. In this part of the Java tutorial, we covered data types in Java.

Data types II
In this part of the Java tutorial, we will continue covering data types of Java.

Wrapper classes
Wrapper classes are object representations of primitive data types. Wrapper classes are used to represent primitive values when an Object is required. For example, Java collections only

30

work with objects. They cannot take primitive types. Wrapper classes also include some useful methods. For example, for doing data type conversions. Placing primitive types into wrapper classes is called boxing. The reverse process is called unboxing. As a general rule, we use wrapper classes when we have some reason for it. Otherwise, we use primitive types. Wrapper classes are immutable. Once they are created, they cannot be changed. Primitive types are faster than boxed types. In scientific computing and other large scale number processing, wrapper classes may cause significant performance hit. Primitive type Wrapper class Constructor arguments byte Byte byte or String short Short short or String int Integer int or String long Long long or String float Float float, double or String double Double double or String char Character char boolean Boolean boolean or String Table: Primitive types and their wrapper class equivalentes The Integer class wraps a value of the primitive type int in an object. It contains constants and methods useful when dealing with an int.
package com.zetcode; public class IntegerWrapper { public static void main(String[] args) { int a = 55; Integer b = new Integer(a); int c = b.intValue(); float d = b.floatValue(); String bin = Integer.toBinaryString(a); String hex = Integer.toHexString(a); String oct = Integer.toOctalString(a); System.out.println(a); System.out.println(b); System.out.println(c); System.out.println(d); System.out.println(bin); System.out.println(hex); System.out.println(oct);

} }

This example works with the Integer wrapper class.


int a = 55;

31

This line creates an integer primitive data type.


Integer b = new Integer(a);

An Integer wrapper class is created from the primitive int type.


int c = b.intValue(); float d = b.floatValue();

The intValue() method converts the Integer to int. Likewise, the floatValue() returns a float data type.
String bin = Integer.toBinaryString(a); String hex = Integer.toHexString(a); String oct = Integer.toOctalString(a);

These three methods return a binary, hexadecimal and octal representations of the integer.
$ java com.zetcode.IntegerWrapper 55 55 55 55.0 110111 37 67

This is the program output. Collections are powerful tools for working with groups of objects. Primitive data types cannot be placed into Java collections. After we box the primitive values, we can put them into collections.
package com.zetcode; import java.util.ArrayList; public class Numbers { public static void main(String[] args) { ArrayList<Number> ls = new ArrayList<>(); ls.add(new Integer(1342341)); ls.add(new Float(34.56)); ls.add(new Double(235.242)); ls.add(new Byte("102")); ls.add(new Short("1245")); for (Number n : ls) { System.out.println(n.getClass()); System.out.println(n);

} } }

32

In the example, we put various numbers into an ArrayList. An ArrayList is a dynamic, resizable array.
ArrayList<Number> ls = new ArrayList<>();

An ArrayList instance is created. In angle brackets we specify the type that the container will hold. The Number is an abstract base class for all five numeric primitive types in Java.
ls.add(new ls.add(new ls.add(new ls.add(new ls.add(new Integer(1342341)); Float(34.56)); Double(235.242)); Byte("102")); Short("1245"));

We add five wrapper classes to the container.


for (Number n : ls) { System.out.println(n.getClass()); System.out.println(n); }

We iterate through the container and print the class name and its value of each of the elements.
$ java com.zetcode.Numbers class java.lang.Integer 1342341 class java.lang.Float 34.56 class java.lang.Double 235.242 class java.lang.Byte 102 class java.lang.Short 1245

The com.zetcode.Numbers program gives this output.

Boxing
Converting from primitive types to object types is called boxing. Unboxing is the opposite operation. It is converting of object types back into primitive types.
package com.zetcode; public class BoxingUnboxing { public static void main(String[] args) { long a = 124235L; Long b = new Long(a); long c = b.longValue(); System.out.println(c);

33

In the code example, we box a long value into a Long object and vice versa.
Long b = new Long(a);

This line performs boxing.


long c = b.longValue();

In this line we do unboxing.

Autoboxing
Java SE 5 introduced autoboxing. Autoboxing is automatic conversion between primitive types and their corresponding object wrapper classes. Autoboxing makes the programming easier. The programmer does not need to do the conversions manually. Automatic boxing and unboxing is performed when one value is primitive type and other is wrapper class in:

assignments passing parameters to methods returning values from methods comparison operations arithmetic operations

Integer i = new Integer(50); if (i < 100) { ... }

Inside the square brackets of the if expression, an Integer is compared with an int. The Integer object is transformed into the primitive int type and compared with the 100 value. Automatic unboxing is done.
package com.zetcode; public class Autoboxing { private static int cube(int x) { } return x * x * x;

public static void main(String[] args) { Integer i = 10; int j = i; System.out.println(i); System.out.println(j);

34

} }

Integer a = cube(i); System.out.println(a);

Automatic boxing and automatic unboxing is demonstrated in this code example.


Integer i = 10;

The Java compiler performs automatic boxing in this code line. An int value is boxed into the Integer type.
int j = i;

Here an automatic unboxing takes place.


Integer a = cube(i);

When we pass an Integer to the cube() method, automatic unboxing is done. When we return the computed value, automatic boxing is perfomed, because an int is transformed back to the Integer. Java language does not support operator overloading. When we apply arithmetic operations on wrapper classes, automatic boxing is done by the compiler.
package com.zetcode; public class Autoboxing2 { public static void main(String[] args) { Integer a = new Integer(5); Integer b = new Integer(7); Integer add = a + b; Integer mul = a * b; System.out.println(add); System.out.println(mul); } }

We have two Integer values. We perform addition and multiplication operations on these two values.
Integer add = a + b; Integer mul = a * b;

Unlike languages like Ruby, C#, Python, D or C++, Java does not have operator overloading implemented. In these two lines, the compiler calls the intValue() methods and converts the wrapper classes to ints and later wraps the outcome back to an Integer by calling the valueOf() method.

35

Autoboxing and object interning


is storing only one copy of each distinct object. The object must be immutable. The distinct objects are stored in an intern pool. In Java, when primitive values are boxed into a wrapper object, certain values (any boolean, any byte, any char from 0 to 127, and any short or int between -128 and 127) are interned, and any two boxing conversions of one of these values are guaranteed to result in the same object. According to the Java language specification, these are minimal ranges. So the behaviour is implementation dependent. Object intering saves time and space. Objects obtained from literals, autoboxing and Integer.valueOf() are interned objects while those constructed with new operator are always distinct objects.
Object intering

The object intering has some important consequences when comparing wrapper classes. The == operator compares reference identity of objects while the equals() method compares values.
package com.zetcode; public class Autoboxing3 { public static void main(String[] args) { Integer a = 5; // new Integer(5); Integer b = 5; // new Integer(5); System.out.println(a == b); System.out.println(a.equals(b)); System.out.println(a.compareTo(b)); Integer c = 155; Integer d = 155; System.out.println(c == d); System.out.println(c.equals(d)); System.out.println(c.compareTo(d));

} }

The example compares some Integer objects.


Integer a = 5; // new Integer(5); Integer b = 5; // new Integer(5);

Two integers are boxed into Integer wrapper classes.


System.out.println(a == b); System.out.println(a.equals(b)); System.out.println(a.compareTo(b));

Three different ways are used to compare the values. The == operator compares the reference identity of two boxed types. Because of the object interning, the operation results in true. If we used the new operator, two distinct objects would be created and the == operator would return false. The equals() method compares the two Integer objects numerically. It returns a boolean true or false. (true in our case.) Finally, the compareTo() method also compares

36

the two objects numerically. It returns the value 0 if this Integer is equal to the argument Integer; a value less than 0 if this Integer is numerically less than the argument Integer; and a value greater than 0 if this Integer is numerically greater than the argument Integer.
Integer c = 155; Integer d = 155;

We have another two boxed types. However, these values are greater than the maximum value interned (127), therefore two distinct objects are created. This time the == operator yields false.
$ java com.zetcode.Autoboxing3 true true 0 false true 0

Output of the com.zetcode.Autoboxing3 program.

The null type


Java has a special null type. The type has no name. As a consequence, it is impossible to declare a variable of the null type or to cast to the null type. The null represents a null reference, one that does not refer to any object. The null is the default value of referencetype variables. Primitive types cannot be assigned a null literal. In different contexts, the null means an absense of an object, an unknow value, or an uninitialized state.
package com.zetcode; import java.util.Random; public class NullType { private static String getName() { Random r = new Random(); boolean n = r.nextBoolean(); if (n == true) { return "John"; } else { return null; } }

public static void main(String[] args) { String name = getName(); System.out.println(name);

37

System.out.println(null == null); if ("John".equals(name)) { System.out.println("His name is John"); } } }

We work with the null value in the program.


private static String getName() { Random r = new Random(); boolean n = r.nextBoolean(); if (n == true) { return "John"; } else { return null; } }

In the getName() method we simulate the situation that a method can sometimes return a null value.
System.out.println(null == null);

We compare a two null values. The expression returns true.


if ("John".equals(name)) { System.out.println("His name is John"); }

We compare the name variable to the "John" string. Notice that we call the equals() method on the "John" string. This is because if the name variable equals to null, calling the method would lead to NullPointerException.
$ java com.zetcode.NullType null true $ java com.zetcode.NullType null true $ java com.zetcode.NullType John true His name is John

We execute the program three times.

38

Default values
Uninitialized fields are given default values by the compiler. Final fields and local variables must be initialized by developers. The following table shows the default values for different types. Data type Default value byte 0 char '\u0000' short 0 int 0 long 0L float 0f double 0d Object null boolean false Table: Default values for uninitialized instance variables The next example will print the default values of the uninitialized instance variables.
package com.zetcode; public class DefaultValues { static static static static static static static static byte b; char c; short s; int i; float f; double d; String str; Object o;

public static void main(String[] args) { System.out.println(b); System.out.println(c); System.out.println(s); System.out.println(i); System.out.println(f); System.out.println(d); System.out.println(str); System.out.println(o);

} }

In the example, we declare eight member fields. They are not initialized. The compiler will set a default value for each of the fields.
static static static static byte b; char c; short s; int i;

39

...

The fields are declared static, because they are accessed from a static main() method.
$ java com.zetcode.DefaultValues 0 0 0 0.0 0.0 null null

This is the output of the com.zetcode.DefaultValues program.

Type conversions
We often work with multiple data types at once. Converting one data type to another one is a common job in programming. The term type conversion refers to changing of an entity of one data type into another. In this section, we will deal with conversions of primitive data types. Reference type conversions will be mentioned in a later chapter. The rules for conversions are complex; they are specified in chapter 5 of the Java language specification. There are two types of conversions: implicit and explicit. Implicit type conversion, also known as coercion, is an automatic type conversion by the compiler. In explicit conversion the programmer directly specifies the converting type inside a pair of round brackets. Explicit conversion is called type casting. Conversions happen in different contexts: assignments, expressions or method invocations.
int x = 456; long y = 34523L; float z = 3.455f; double w = 6354.3425d;

In these four assignments, no conversion takes place. Each of the variables is assigned a literal of the expected type.
int x = 345; long y = x; float m = 22.3354f; double n = m;

In this code two conversions are performed by Java compiler implicitly. Assigning a variable of a smaller type to a variable of a larger type is legal. The conversion is considered safe, as no precision is lost. This kind of conversion is called implicit widening conversion.
long x = 345; int y = (int) x; double m = 22.3354d; float n = (float) m;

40

Assigning variables of larger type to smaller type is not legal in Java. Even if the values themselves fit into the range of the smaller type. In this case it is possible to loose precision. To allow such assignments, we have to use the type casting operation. This way the programmer says that he is doing it on purpose and that he is aware of the fact that there might be some precision lost. This kind of conversion is called explicit narrowing conversion.
byte a = 123; short b = 23532;

In this case, we deal with a specific type of assignment conversion. 123 and 23532 are integer literals, the a, b variables are of byte and short type. It is possible to use the casting operation, but it is not required. The literals can be represented in their variables on the left side of the assignment. We deal with implicit narrowing conversion.
private static byte calc(byte x) { ... } byte b = calc((byte) 5);

The above rule only applies to assignments. When we pass an integer literal to a method that expects a byte, we have to perform the casting operation.

Numeric promotions
Numeric promotion is a specific type of an implicit type conversion. It takes place in arithmetic expressions. Numeric promotions are used to convert the operands of a numeric operator to a common type so that an operation can be performed.
int x = 3; double y = 2.5; double z = x + y;

In the third line we have an addition expression. The x operand is int, the y operand is double. The compiler converts the integer to double value and adds the two numbers. The result is a double. It is a case of implicit widening primitive conversion.
byte a = 120; a = a + 1; // compilation error

This code leads to a compile time error. In the right side of the second line, we have a byte variable a and an integer literal 1. The variable is converted to integer and the values are added. The result is an integer. Later, the compiler tries to assign the value to the a variable. Assigning larger types to smaller types is not possible without an explicit cast operator. Therefore we receive a compile time error.
byte a = 120; a = (byte) (a + 1);

This code does compile. Note the usage of round brackets for the a + 1 expression. The (byte) casting operator has a higher precedence than the addition operator. If we want to apply the casting on the whole expression, we have to use round brackets. 41

byte a = 120; a += 5;

Compound operators perform implicit conversions automatically.


short r = 21; short s = (short) -r;

Applying the +/- unary operator on a variable a unary numberic promotion is performed. The short type is promoted to int type. Therefore we must use the casting operator for the assignment to pass.
byte u = 100; byte v = u++;

In case of the unary increment ++, decrement -- operators, no conversion is done. The casting is not necessary.

Boxing, unboxing conversions


Boxing conversion converts expressions of primitive type to corresponding expressions of wrapper type. Unboxing conversion converts expressions of wrapper type to corresponding expressions of primitive type. Conversions from boolean to Boolean or from byte to Byte are examples of boxing conversions. The reverse conversions, e.g. from Boolean to boolean or from Byte to byte are examples of unboxing conversions.
Byte b = 124; byte c = b;

In the first code line, automatic boxing conversion is performed by the Java compiler. In the second line, an unboxing conversion is done.
private static String checkAge(Short age) { ... } String r = checkAge((short) 5);

Here we have boxing conversion in the context of a method invocation. We pass a short type to the method which expects a Short wrapper type. The value is boxed.
Boolean gameOver = new Boolean("true"); if (gameOver) { System.out.println("The game is over"); }

This is an example of an unboxing conversion. Inside the if expression, the booleanValue() method is called. The method returns the value of a Boolean object as a boolean primitive.

String conversions
Performing string conversions between numbers and strings is very common in programming. The casting operation is not allowed, because the strings and primitive types

42

are fundamentally different types. There are several methods for doing string conversion. There is also an automatic string conversion for the + operator. More about string conversions will be covered in the Strings chapter of this tutorial.
String s = (String) 15; // compilation error int i = (int) "25"; // compilation error

It is not possible to cast between numbers and strings. Instead, we have various methods for doing conversion between numbers and strings.
short age = Short.parseShort("35"); int salary = Integer.parseInt("2400"); float height = Float.parseFloat("172.34"); double weight = Double.parseDouble("55.6");

The parse methods of the wrapper classes convert strings to primitive types.
Short age = Short.valueOf("35"); Integer salary = Integer.valueOf("2400"); Float height = Float.valueOf("172.34"); Double weight = Double.valueOf("55.6");

The valueOf() method returns the wrapper classes from primitive types.
int age = 17; double weight = 55.3; String v1 = String.valueOf(age); String v2 = String.valueOf(weight);

The String class has a valueOf() method for converting various types to strings. Automatic string conversions take place when using the + operator and one operator is a string, the other operator is not a string. The non-string operand to the + is converted to a string.
package com.zetcode; public class AutomaticStringConversion { public static void main(String[] args) { String name = "Jane"; short age = 17; System.out.println(name + " is " + } } age + " years old.\n");

In the example, we have a String data type and a short data type. The two types are concatenated using the + operator into a sentence.
System.out.println(name + " is " + age + " years old.");

In the expression, the age variable is converted to a String type. 43

$ java com.zetcode.AutomaticStringConversion Jane is 17 years old.

This is the example output. In this part of the Java tutorial, we covered data types and their conversions.

Strings
In this part of the Java tutorial, we will work with string data in more detail. Strings are very important data types in computer languages. That is why we dedicate a whole chapter to working with strings in Java. In Java, a string is a sequence of unicode characters. Strings are objects. There are two basic classes for working with strings:
String StringBuilder

The String is an immutable sequence of characters. The StringBuilder is a mutable sequence of characters. (There is also a StringBuffer class which can be used by multiple threads. If we are not dealing with threads, we use the StringBuilder.) A string literal a series of characters in the source code that is enclosed in double quotes. For example, "Java" is a string literal. Whenever Java compiler encounters a string literal in the code, it creates a String object with its value.
String lang = "Java"; // same as String lang = new String("Java");

String literals are used by many programming languages. It is an established convention and it also saves typing.

Initializing strings
There are multiple ways of creating strings, both immutable and mutable. We will show a few of them.
package com.zetcode; public class StringInit { public static void main(String[] args) { char[] cdb = {'M', 'y', 'S', 'q', 'l'}; String lang = "Java"; String ide = new String("NetBeans"); String db = new String(cdb); System.out.println(lang); System.out.println(ide);

44

System.out.println(db); StringBuilder sb1 = new StringBuilder(lang); StringBuilder sb2 = new StringBuilder(); sb2.append("Fields"); sb2.append(" of "); sb2.append("glory"); System.out.println(sb1); System.out.println(sb2); } }

The example shows a few ways of creating String and StringBuilder objects.
String lang = "Java";

The most common way is to create a string object from a string literal.
String ide = new String("NetBeans");

In this line, we create a string using usual way of building objects with the new keyword.
String db = new String(cdb);

Here we create a string object from an array of characters.


StringBuilder sb1 = new StringBuilder(lang);

A StringBuilder object is created from a String.


StringBuilder sb2 = new StringBuilder(); sb2.append("Fields"); sb2.append(" of "); sb2.append("glory");

We create an empty StringBuilder object. We append three strings into the object.
$ java com.zetcode.StringInit Java NetBeans MySql Java Fields of glory

Running the example gives this result.

Strings are objects


Strings are objects; they are not primitive data types. Strings are instances of the String or StringBuilder class. Since they are objects, they have multiple methods available for doing various work.
package com.zetcode;

45

public class StringObjects { public static void main(String[] args) { String lang = "Java"; String bclass = lang.getClass().toString(); System.out.println(bclass); String sup = lang.getClass().getSuperclass().toString(); System.out.println(sup); if (lang.isEmpty()) { System.out.println("The string is empty"); } else { } System.out.println("The string is not empty");

int l = lang.length(); System.out.println("The string has " + l + " characters"); } }

In this program, we demonstrate that strings are objects. Objects must have a class name, a parent class and they must also have some methods that we can call.
String lang = "Java";

An object of String type is created.


String bclass = lang.getClass().toString();

We determine the class name of the object to which the lang variable refers.
String sup = lang.getClass().getSuperclass().toString();

A parent class of our object is received. All objects have at least one parent the Object.
if (lang.isEmpty()) { System.out.println("The string is empty"); } else { } System.out.println("The string is not empty");

Objects have various methods. One of the useful string methods is the isEmpty() method, which determines whether the string is empty.
int l = lang.length();

The length() method returns the size of the string.

46

$ java com.zetcode.StringObjects class java.lang.String class java.lang.Object The string is not empty The string has 4 characters

Our string object is an instance of the String class. It has the Object parent class. The object is not empty and it contains four characters.

Mutable & immutable strings


The String is a sequence of immutable characters, while the StringBuilder is a sequence of mutable characters. The next example will show the difference.
package com.zetcode; public class MutableImmutable { public static void main(String[] args) { String name = "Jane"; String name2 = name.replace('J', 'K'); String name3 = name2.replace('n', 't'); System.out.println(name); System.out.println(name3); StringBuilder sb = new StringBuilder("Jane"); System.out.println(sb); sb.setCharAt(0, 'K'); sb.setCharAt(2, 't'); } } System.out.println(sb);

Both objects have methods for replacing characters in a string.


String name = "Jane"; String name2 = name.replace('J', 'K'); String name3 = name2.replace('n', 't');

Calling a replace() method on a String results in returning a new modified string. The original string is not changed.
sb.setCharAt(0, 'K'); sb.setCharAt(2, 't');

The setCharAt() method of a StringBuilder will replace a character at the given index with a new character. The original string is modified.
$ java com.zetcode.MutableImmutable Jane Kate Jane

47

Kate

This is the output of the com.zetcode.MutableImmutable example.

Concatenating strings
Immutable strings can be added using the + operator or the concat() method. They will form a new string which is a chain of all concatenated strings. Mutable strings have the append() method which builds a string from any number of other strings.
package com.zetcode; public class ConcatenateStrings { public static void main(String[] args) { System.out.println("Return" + " of " + "the king."); System.out.println("Return".concat(" of ").concat("the king.")); StringBuilder sb = new StringBuilder(); sb.append("Return"); sb.append(" of "); sb.append("the king."); System.out.println(sb); } }

The example creates three sentences by adding strings.


System.out.println("Return" + " of " + "the king.");

A new string is formed by using the + operator.


System.out.println("Return".concat(" of ").concat("the king."));

The concat() method returns a string that represents the concatenation of this object's characters followed by the string argument's characters.
StringBuilder sb = new StringBuilder(); sb.append("Return"); sb.append(" of "); sb.append("the king.");

A mutable object of the StringBuilder type is created by calling the append() method three times.
$ java Return Return Return com.zetcode.ConcatenateStrings of the king. of the king. of the king.

This is the example output.

48

Using quotes
What if we wanted to display quotes, for example in a direct speech? In such a case, the inner quotes must be escaped.
package com.zetcode; public class Quotes { public static void main(String[] args) { System.out.println("There are may stars"); System.out.println("He said: \"Which one are you looking at?\""); } }

We use the (\) character to escape additional quotes.


$ java com.zetcode.Quotes There are may stars He said: "Which one are you looking at?"

Here we see the output of the com.zetcode.Quotes program.

Multiline strings
It is not possible to create a multiline string in Java. In order to span a string on multiple lines, we need to do a concatenation operation.
package com.zetcode; public class MultilineString { static String lyrics = "I cheated myself\n" + "like I knew I would\n" + "I told ya, I was trouble\n" + "you know that I'm no good"; public static void main(String[] args) { System.out.println(lyrics); } }

One strophe spans four lines. The four strings are concatenated with the + operator.
$ java com.zetcode.MultilineString I cheated myself like I knew I would I told ya, I was trouble you know that I'm no good

We see the output of the com.zetcode.MultilineString example.

49

String elements
A string is a sequence of characters. A character is a basic element of a string. The following two examples will show some methods that work with characters of a string.
package com.zetcode; public class StringElements { public static void main(String[] args) { char[] crs = {'Z', 'e', 't', 'C', 'o', 'd', 'e' }; String s = new String(crs); char c1 = s.charAt(0); char c2 = s.charAt(s.length()-1); System.out.println(c1); System.out.println(c2); int i1 = s.indexOf('e'); int i2 = s.lastIndexOf('e'); System.out.println("The first index of character e is " + i1); System.out.println("The last index of character e is " + i2); System.out.println(s.contains("t")); System.out.println(s.contains("f")); char[] elements = s.toCharArray(); for (char el : elements) { } } } System.out.println(el);

In the first example, we will work with an immutable string.


char[] crs = {'Z', 'e', 't', 'C', 'o', 'd', 'e' }; String s = new String(crs);

A new immutable string is formed from an array of characters.


char c1 = s.charAt(0); char c2 = s.charAt(s.length()-1);

With the charAt() method, we get the first and the last char value of the string.
int i1 = s.indexOf('e'); int i2 = s.lastIndexOf('e');

With the above methods, we get the first and the last occurence of the character 'e'.
System.out.println(s.contains("t"));

50

With the contains() method, we check if the string contains the 't' character. The method returns a boolean value.
char[] elements = s.toCharArray(); for (char el : elements) { } System.out.println(el);

The toCharArray() method creates a character array from the string. We go through the array and print each of the characters.
$ java com.zetcode.StringElements Z e The first index of character e is 1 The last index of character e is 6 true false Z e t C o d e

This is the example output. In the second example, we will work with the elements of a StringBuilder class.
package com.zetcode; public class StringBuilderElements { public static void main(String[] args) { StringBuilder sb = new StringBuilder("Misty mountains"); System.out.println(sb); sb.deleteCharAt(sb.length()-1); System.out.println(sb); sb.append('s'); System.out.println(sb); sb.insert(0, 'T'); sb.insert(1, 'h'); sb.insert(2, 'e'); sb.insert(3, ' '); System.out.println(sb); sb.setCharAt(4, 'm'); System.out.println(sb); } }

51

A mutable string is formed. We modify the contents of the string by deleting, appending, inserting and replacing characters.
sb.deleteCharAt(sb.length()-1);

This line deletes the last character.


sb.append('s');

The deleted character is appended back to the string.


sb.insert(0, sb.insert(1, sb.insert(2, sb.insert(3, 'T'); 'h'); 'e'); ' ');

We insert four characters at the beginning of the string.


sb.setCharAt(4, 'm');

Finally, we replace a character at index 4.


$ java com.zetcode.StringBuilderElements Misty mountains Misty mountain Misty mountains The Misty mountains The misty mountains

From the output we can see how the mutable string is changing.

Comparing strings
There are two basic methods for comparing strings. The equals() method compares the contents of two strings and returns a boolean value indicating, whether the strings are equal or not. The equalsIgnoreCase() does the same thing, except that it ignores the case.
package com.zetcode; public class ComparingStrings { public static void main(String[] args) { String a = "book"; String b = "Book"; System.out.println(a.equals(b)); System.out.println(a.equalsIgnoreCase(b)); } }

We compare two strings using the aforementioned methods.


String a = "book"; String b = "Book";

52

We define two strings that we will compare.


System.out.println(a.equals(b));

The equals() method returns false. The two strings differ in the first character.
System.out.println(a.equalsIgnoreCase(b));

When we ignore the case, the strings are equal. The equalsIgnoreCase() method returns true.
$ java com.zetcode.ComparingStrings false true

This is the output of the com.zetcode.ComparingStrings program. If we are comparing a variable to a string, it is important to remember that the string is on the left side of the comparing method. Otherwise we might get the NullPointerException.
import java.util.Random; public class ComparingStrings2 { public static String readString() { Random r = new Random(); boolean b = r.nextBoolean(); if (b == true) { return "ZetCode"; } else { } } public static void main(String[] args) { String d = readString(); if ("ZetCode".equals(d)) { System.out.println("Strings are equal"); } else { System.out.println("Strings are not equal"); } } } return null;

In the code example, we compare the strings properly, avoiding possible NullPointerException.
public static String readString() {

53

Random r = new Random(); boolean b = r.nextBoolean(); if (b == true) { return "ZetCode"; } else { return null; } }

The readString() method simulates the case where a method invocation can result in a null value. This could happen, for instance, if we try to read a value from a database.
String d = readString();

The d variable can contain the null value.


if ("ZetCode".equals(d)) {

The above line is the correct way of comparing two strings where one string is a known literal. If we placed the d variable on the left side, this would lead to NullPointerException if the d variable would contain the null value. The equals() method compares the characters of two strings. The == operator tests for reference equality. All string literals are interned automatically in Java. They are placed inside a string pool. This happens at compile time. If two variables contain two equal string literals, they in fact refer to the same string object inside a string pool.
package com.zetcode; public class ComparingStrings3 { public static void main(String[] args) { boolean boolean boolean boolean boolean a b c d e = = = = = "ZetCode" "ZetCode" "ZetCode" "ZetCode" "ZetCode" == == == == == "ZetCode"; new String("ZetCode"); "Zet" + "Code"; new String("ZetCode").intern(); " ZetCode ".trim();

} }

System.out.println(a); System.out.println(b); System.out.println(c); System.out.println(d); System.out.println(e);

In this code example, we compare string objects with the == operator.


boolean a = "ZetCode" == "ZetCode";

These strings literals are interned. Therefore, the identity comparison operator returns true. 54

boolean b = "ZetCode" == new String("ZetCode");

Strings created with the new operator are not interned. The comparison operator results in a false value.
boolean c = "ZetCode" == "Zet" + "Code";

Strings are concatenated at compile time. The string literals result in the same object. The result is a true.
boolean d = "ZetCode" == new String("ZetCode").intern();

The intern() object puts the string object on the right side into the pool. Therefore, the d variable holds a boolean true.
boolean e = "ZetCode" == " ZetCode ".trim();

The trim() method is called at runtime, generating a distinct object. The e variable holds a boolean false.
$ java com.zetcode.ComparingStrings3 true false true true false

This is the output of the example.

Formatting strings
We can use both System.out.printf() and System.out.format() methods to format strings in Java. They work the same. These two methods write a formatted string to the output stream using the specified format string and arguments. If there are more arguments than format specifiers, the extra arguments are ignored.
%[argument_index$][flags][width][.precision]conversion

The format specifiers for general, character, and numeric types have this syntax.
%[argument_index$][flags][width]conversion

This is the syntax for types which are used to represents dates and times. The format specifiers begin with the % character and end with a 1 or 2 character conversion that specifies the kind of formatted output being generated. The optional items are placed between the square brackets. The argument_index is a decimal integer indicating the position of the argument in the argument list. The flags is a set of characters that modify the output format. The set of valid flags depends on the conversion. The width is a non-negative decimal integer indicating the minimum number of characters to be written to the output. The precision is a non-negative 55

decimal integer usually used to restrict the number of characters. The specific behavior depends on the conversion. The required conversion is a character indicating how the argument should be formatted.
package com.zetcode; public class Conversions { public static void main(String[] args) { System.out.format("There are %d %s.%n", 5, "pencils"); System.out.printf("The rock weighs %f kilograms.%n", 5.345); } }

In this program, we format two simple sentences.


System.out.format("There are %d %s.%n", 5, "pencils");

In this code line, we have three format specifiers. Each specifier starts with the % character. The d specifier formats integer values. The s specifier expects string values. The %n outputs a platform-specific line terminator; it does not require an argument.
System.out.printf("The rock weighs %f kilograms.%n", 5.345);

The f formats a floating point value as a decimal value. The System.out.printf() works the same as the System.out.format().
$ java com.zetcode.Conversions There are 5 pencils. The rock weighs 5.345000 kilograms.

This is the output of the com.zetcode.Conversions program.


package com.zetcode; import java.util.Calendar; public class IndexPosition { public static void main(String[] args) { int x = 12; int y = 32; int z = 43; Calendar c = Calendar.getInstance(); System.out.format("There are %d apples, %d oranges and " + "%d pears%n", x, y, z); System.out.format("There are %2$d apples, %3$d oranges and " + "%1$d pears%n", x, y, z); System.out.format("Year: %tY, Month: %<tm, Day: %<td%n", c); } }

56

The example uses argument index to refer to variables included the list of arguments.
System.out.format("There are %d apples, %d oranges and " + "%d pears%n", x, y, z);

If we do not specify the index, the variables automatically match the specifiers. The d specifier formats an integer value as a decimal value.
System.out.format("There are %2$d apples, %3$d oranges and " + "%1$d pears%n", x, y, z);

The 1$ referes to the x variable, the 2$ referes to the y variable and the 3$ refers to the z variable.
System.out.format("Year: %tY, Month: %<tm, Day: %<td%n", c);

The '<' flag causes the argument for the previous format specifier to be reused. All three specifiers refer to the c variable.
$ java com.zetcode.IndexPosition There are 12 apples, 32 oranges and 43 pears There are 32 apples, 43 oranges and 12 pears Year: 2013, Month: 07, Day: 17

This is the output of the com.zetcode.IndexPosition program. The flag modifies the format in a specific way. There are several flags available. For instance, the + flag requires the output to include a positive sign for all positive numbers.
package com.zetcode; public class Flags { public static void main(String[] args) { System.out.format("%+d%n", 553); System.out.format("%010d%n", 553); System.out.format("%10d%n", 553); System.out.format("%-10d%n", 553); System.out.format("%d%n", -553); System.out.format("%(d%n", -553);

} }

The example presents a few flags of the string format specifier.


System.out.format("%010d%n", 553);

The 0 flag will cause the output to be padded with leading zeros to the minimum field width. Our number has three digits. The minimum width is 10. Therefore, we have 7 leading zeros in the output.
System.out.format("%10d%n", 553);

57

Without the 0 flag, the number is right aligned.


System.out.format("%-10d%n", 553);

The - flag will cause the number to be left aligned.


System.out.format("%d%n", -553); System.out.format("%(d%n", -553);

By default, negative numbers have a minus sign. If we use the ( flag, the negative values will be put inside round brackets.
$ java com.zetcode.Flags +553 0000000553 553 553 -553 (553)

Here we see the output of the com.zetcode.Flags example. The width field is the minimum number of characters to be written to the output. It cannot be used together with the line separator.
package com.zetcode; public class WidthSpecifier { public static void main(String[] args) { System.out.println(1); System.out.println(16); System.out.println(1655); System.out.println(16567); System.out.println(166701); System.out.format("%10d%n", System.out.format("%10d%n", System.out.format("%10d%n", System.out.format("%10d%n", System.out.format("%10d%n", 1); 16); 1655); 16567); 166701);

} }

First, we print five numbers without specifying the field width. The width of the output is equal to the number of the characters being displayed. In the second case, we have a field width of 10. Each of the 5 outputs has a minimum length of 10 characters. The numbers are right aligned.
System.out.format("%10d%n", 1);

Number 10 states that the string output must have at least ten characters.
$ java com.zetcode.WidthSpecifier 1

58

16 1655 16567 166701 1 16 1655 16567 166701

We can see that in the second case the numbers are right aligned. The precision field has different meaning for different conversions. For general argument types, the precision is the maximum number of characters to be written to the output.
package com.zetcode; public class PrecisionSpecifier { public static void main(String[] args) { System.out.format("%.3g%n", 0.0000006); System.out.format("%.3f%n", 54.34263); System.out.format("%.3s%n", "ZetCode"); } }

The precision specifier is demonstrated on three outputs.


System.out.format("%.3g%n", 0.0000006);

If the g conversion is used, then the precision is the total number of digits in the resulting magnitude after rounding.
System.out.format("%.3f%n", 54.34263);

For floating point values, the precision is the number of digits after the decimal separator.
System.out.format("%.3s%n", "ZetCode");

For strings, it is the maximum number of printed characters. Only three characters out of seven are printed to the console.
$ java com.zetcode.PrecisionSpecifier 6.00e-07 54.343 Zet

This is the example output. The next example will format numeric data.
package com.zetcode; public class FormatNumbers {

59

public static void main(String[] args) { System.out.format("%d%n", 12263); System.out.format("%o%n", 12263); System.out.format("%x%n", 12263); System.out.format("%e%n", 0.03452342263); System.out.format("%d%%%n", 45); } }

The example demonstrates the standard formatting specifiers for numbers.


System.out.format("%d%n", 12263);

The d conversion specifier will turn an integer value into a decimal value.
System.out.format("%o%n", 12263);

The o conversion specifier will format the number into the octal base.
System.out.format("%x%n", 12263);

With the x specifier, the result is formatted as a hexadecimal integer.


System.out.format("%e%n", 0.03452342263);

Using the 'e' specifier, the number is printed in a scientific notation.


System.out.format("%d%%%n", 45);

The %% characters are used to print a percent sign.


$ java com.zetcode.FormatNumbers 12263 27747 2fe7 3.452342e-02 45%

The program prints numbers in different formats. Finally, we will format date and time data.
package com.zetcode; import java.util.Calendar; public class FormatDateTime { public static void main(String[] args) { Calendar c = Calendar.getInstance(); System.out.format("%tF%n", c); System.out.format("%tD%n", c);

60

System.out.format("%tT%n", c); System.out.format("%1$tA,%1$tb %1$tY%n", c); System.out.format("%1$td.%1$tm.%1$tY%n", c); System.out.format("%ts%n", c); } }

The preceding example demonstrates the standard formatting specifiers for dates. The conversion part of the date and time format string starts with the 't' character.
System.out.format("%tF%n", c);

This line prints a date in a complete ISO 8601 format, as a result of the tF conversion.
System.out.format("%1$td.%1$tm.%1$tY%n", c);

Using these format specifiers, we print a date in the form that is used in Slovakia. The parts are separated by the dot character and the day precedes the month and the month precedes the year. All three format specifiers refer to the c variable.
System.out.format("%ts%n", c);

The 's' conversion character creates a Unix time. It is a number of seconds since the beginning of the epoch starting at 1 January 1970 00:00:00 UTC.
$ java com.zetcode.FormatDateTime 2013-07-17 07/17/13 18:29:48 Wednesday,Jul 2013 17.07.2013 1374078588

This is the output of the com.zetcode.FormatDateTime program. This part of the Java tutorial covered strings in more detail.

Arrays
In this part of the Java tutorial, we will cover arrays. An array is a container object that holds a fixed number of values of a single type. The length of an array is established when the array is created. After creation, its length is fixed. A scalar variable can hold only one item at a time. Arrays can hold multiple items. These items are called elements of the array. Arrays store data of the same data type. Each element can be referred to by an index. Arrays are zero based. The index of the first element is zero. Arrays are used to store data of our applications. We declare arrays to be of a certain data type. We specify their length. And we initialize arrays with data. We have several methods

61

for working with arrays. We can modify the elements, sort them, copy them or search for them.
int[] ages; String[] names; float[] weights;

We have three array declarations. The declaration consists of two parts. The type of the array and the array name. The type of an array has a data type that determines the types of the elements within an array (int, String, float in our case) and a pair of square brackets []. The brackets indicate that we have an array. Collections serve a similar purpose like arrays. They are more powerful than arrays. They will be described later in a separate chapter.

Initializing arrays
There are several ways how we can initialize an array in Java. In the first example, an array is created and initialized in two steps.
package com.zetcode; import java.util.Arrays; public class InitArray { public static void main(String[] args) { int[] a = new int[5]; a[0] a[1] a[2] a[3] a[4] } = = = = = 1; 2; 3; 4; 5;

System.out.println(Arrays.toString(a)); }

We create and initialize a numerical array. The contents of the array are printed to the console.
int[] a = new int[5];

Here we create an array which can contain five elements. The statement allocates memory for five integers. The square brackets are used for declaring an array, the type (int in our case) tells us what type of values the array will hold. An array is an object and therefore it is created with the new keyword.
a[0] a[1] a[2] a[3] a[4] = = = = = 1; 2; 3; 4; 5;

62

We initialize the array with some data. This is assignment initialization. The indexes are in the square brackets. Number 1 is going to be the first element of the array. Number 2 is the second etc.
System.out.println(Arrays.toString(a));

The Arrays class is a helper class which contains various methods for manipulating arrays. The toString() method returns a string representation of the contents of the specified array. This method is helpful in debugging.
$ java com.zetcode.InitArray [1, 2, 3, 4, 5]

Output of the com.zetcode.InitArray example. We can declare and initialize an array in one statement.
package com.zetcode; import java.util.Arrays; public class InitArray2 { public static void main(String[] args) { int[] a = new int[] { 2, 4, 5, 6, 7, 3, 2 }; System.out.println(Arrays.toString(a)); } }

This is a modified version of the previous program.


int[] array = new int[] { 2, 4, 5, 6, 7, 3, 2 };

An array is created and initialized in one step. The elements are specified in curly brackets. We did not specify the length of the array. The compiler will do it for us. The one step creation and initialization can be further simplified by only specifying the numbers between the curly brackets.
package com.zetcode; import java.util.Arrays; public class InitArray3 { public static void main(String[] args) { int[] a = { 2, 4, 5, 6, 7, 3, 2 }; System.out.println(Arrays.toString(a)); } }

63

An array of integers is created using the most simple way of array creation.
int[] a = { 2, 4, 5, 6, 7, 3, 2 };

The new int[] construct can be omitted. The right side of the statement is an array literal notation. It resembles the C/C++ style of array initialization. Even if we drop the new keyword, the array is created the same way as in previous two examples. This is just a convenient shorthand notation.

Accessing elements
After the array is created, its elements can be accessed by their index. The index is a number placed inside square brackets which follow the array name.
package com.zetcode; public class AccessingElements { public static void main(String[] args) { String[] names = {"Jane", "Thomas", "Lucy", "David"}; System.out.println(names[0]); System.out.println(names[1]); System.out.println(names[2]); System.out.println(names[3]);

} }

In the example, we create an array of string names. We access each of the elements by its index and print them to the terminal.
String[] names = {"Jane", "Thomas", "Lucy", "David"};

An array of strings is created.


System.out.println(names[0]); System.out.println(names[1]); System.out.println(names[2]); System.out.println(names[3]);

Each of the elements of the array is printed to the console. With the names[0] construct, we refer to the first element of the names array.
$ java com.zetcode.AccessingElements Jane Thomas Lucy David

Running the example we get the above output. It is possible to change the elements of an array. The elements are not immutable.

64

package com.zetcode; import java.util.Arrays; public class AccessingElements2 { public static void main(String[] args) { int[] vals = { 1, 2, 3 }; vals[0] *= 2; vals[1] *= 2; vals[2] *= 2; } } System.out.println(Arrays.toString(vals));

We have an array of three integers. Each of the values will be multiplied by two.
int[] vals = { 1, 2, 3 };

An array of three integers is created.


vals[0] *= 2; vals[1] *= 2; vals[2] *= 2;

Using the element access, we multiply each value in the array by two.
$ java com.zetcode.AccessingElements2 [2, 4, 6]

All three integers have been multiplied by number 2.

Traversing arrays
We often need to go through all elements of an array. We show two common methods for traversing an array.
package com.zetcode; public class TraversingArrays { public static void main(String[] args) { String[] planets = { "Mercury", "Venus", "Mars", "Earth", "Jupiter", "Saturn", "Uranus", "Neptune", "Pluto" }; for (int i=0; i < planets.length; i++) { System.out.println(planets[i]); } for (String planet : planets) {

65

} } }

System.out.println(planet);

An array of planet names is created. We use the for loop to print all the values.
for (int i=0; i < planets.length; i++) { } System.out.println(planets[i]);

In this loop, we utilize the fact that we can get the number of elements from the array object. The number of elements is stored in the length constant.
for (String planet : planets) { } System.out.println(planet);

An enhanced for keyword can be used to make the code more compact when traversing arrays or other collections. In each cycle, the planet variable is passed the next value from the planets array.

Multidimensional arrays
So far we have been working with one-dimensional arrays. In Java, we can create multidimensional arrays. A multidimensional array is an array of arrays. In such an array, the elements are themselves arrays. In multidimensional arrays, we use two or more sets of brackets.
package com.zetcode; public class TwoDimensions { public static void main(String[] args) { int[][] twodim = new int[][] { {1, 2, 3}, {1, 2, 3} }; int d1 = twodim.length; int d2 = twodim[1].length; for (int i = 0; i < d1; i++) { for (int j = 0; j < d2; j++) { System.out.println(twodim[i][j]); } } } }

In this example, we create a two-dimensional array of integers.


int[][] twodim = new int[][] { {1, 2, 3}, {1, 2, 3} };

66

Two pairs of square brackets are used to declare a two-dimensional array. Inside the curly brackets, we have additional two pairs of curly brackets. They represent two inner arrays.
int d1 = twodim.length; int d2 = twodim[1].length;

We determine the length of the outer array that holds other two arrays and the second inner array.
for (int i = 0; i < d1; i++) { for (int j = 0; j < d2; j++) { } } System.out.println(twodim[i][j]);

Two for loops are used to print all the six values from the two-dimensional array. The first index of the twodim[i][j] array refers to one of the inner arrays. The second index refers to the element of the chosen inner array.
$ java com.zetcode.TwoDimensions 1 2 3 1 2 3

This is the output of the com.zetcode.TwoDimensions program. In a similar fashion, we create a three-dimensional array of integers.
package com.zetcode; public class ThreeDimensions { public static void main(String[] args) { int[][][] n3 {{12, 2, {{14, 5, {{3, 26, {{4, 11, }; = { 8}, 2}, 9}, 2}, {0, {0, {8, {0, 2, 5, 7, 9, 1}}, 4}}, 1}}, 6}}

int d1 = n3.length; int d2 = n3[0].length; int d3 = n3[0][0].length; for (int i = 0; i < d1; i++) { for (int j = 0; j < d2; j++) { for (int k = 0; k < d3; k++) { System.out.print(n3[i][j][k] + " ");

67

} } } }

System.out.print('\n');

A variable that holds a tree-dimensional array is declared with three pairs of square brackets. The values are place inside three pairs of curly brackets.
int[][][] n3 {{12, 2, {{14, 5, {{3, 26, {{4, 11, }; = { 8}, 2}, 9}, 2}, {0, {0, {8, {0, 2, 5, 7, 9, 1}}, 4}}, 1}}, 6}}

Three-dimensional array n3 is created. It is an array that has elements which are themselves arrays of arrays.
int d1 = n3.length; int d2 = n3[0].length; int d3 = n3[0][0].length;

We get the length of all three dimensions.


for (int i = 0; i < d1; i++) { for (int j = 0; j < d2; j++) { for (int k = 0; k < d3; k++) { } } } System.out.print(n3[i][j][k] + " ");

We need three for loops to traverse a three dimensional array.


$ java com.zetcode.ThreeDimensions 12 2 8 0 2 1 14 5 2 0 5 4 3 26 9 8 7 1 4 11 2 0 9 6

We print the contents of the three-dimensional array to the console.

Irregular arrays
Arrays that have elements of the same size are called rectangular arrays. It is possible to create irregular arrays where the arrays have a different size. In C# such arrays are called jagged arrays.
package com.zetcode; public class IrregularArrays { public static void main(String[] args) {

68

int[][] {1, {1, {1, };

ir = new int[][] { 2}, 2, 3}, 2, 3, 4}

for (int[] a : ir) { for (int e : a) { System.out.print(e + " "); } } System.out.print('\n'); } }

This is an example of an irregular array.


int[][] {1, {1, {1, }; ir = new int[][] { 2}, 2, 3}, 2, 3, 4}

This is a declaration and initialization of an irregular array. The three inner arrays have 2, 3 and 4 elements.
for (int[] a : ir) { for (int e : a) { System.out.print(e + " "); } }

The enhanced for loop is used to go through all the elements of the array.
$ java com.zetcode.IrregularArrays 1 2 1 2 3 1 2 3 4

This is the output of the example.

Array methods
The Arrays class, available in the java.util package, is a helper class that contains methods for working with arrays. These methods can be used for modifying, sorting, copying, or searching data. These methods that we use are static methods of the Array class.
package com.zetcode; import java.util.Arrays; public class ArrayMethods { public static void main(String[] args) {

69

int[] a = {5, 2, 4, 3, 1}; Arrays.sort(a); System.out.println(Arrays.toString(a)); Arrays.fill(a, 8); System.out.println(Arrays.toString(a)); int[] b = Arrays.copyOf(a, 5); if (Arrays.equals(a, b)) { System.out.println("Arrays a, b are equal"); } else { } } } System.out.println("Arrays a, b are not equal");

In the code example, we will present five methods of the Arrays class.
import java.util.Arrays;

We will use the shorthand notation for the Arrays class.


int[] a = {5, 2, 4, 3, 1};

We have an array of five integers.


Arrays.sort(a);

The sort() method sorts the integers in an ascending order.


System.out.println(Arrays.toString(a));

The toString() method returns a string representation of the contents of the specified array.
Arrays.fill(a, 8);

The fill() method assigns the specified integer value to each element of the array.
int[] b = Arrays.copyOf(a, 5);

The copyOf() method copies the specified number of elements to a new array.
if (Arrays.equals(a, b)) { System.out.println("Arrays a, b are equal"); } else { System.out.println("Arrays a, b are not equal"); }

70

The equals() method compares the two arrays. Two arrays are equal if they contain the same elements in the same order.
$ java [1, 2, [8, 8, Arrays com.zetcode.ArrayMethods 3, 4, 5] 8, 8, 8] a, b are equal

Running the com.zetcode.ArrayMethods application, we get the above result.

Comparing arrays
There are two methods for comparing arrays. The equals() method and the deepEquals() method. The deepEquals() method also compares references to arrays inside arrays.
package com.zetcode; import java.util.Arrays; public class ComparingArrays { public static void main(String[] args) { int[] a = {1, 1, 2, 1, 1}; int[] b = {0, 0, 3, 0, 0}; int[][] c = { {1, 1, 2, 1, 1}, {0, 0, 3, 0, 0} }; int[][] d = { a, b }; System.out.print("equals() method: "); if (Arrays.equals(c, d)) { System.out.println("Arrays c, d are equal"); } else { System.out.println("Arrays c, d are not equal"); } System.out.print("deepEquals() method: "); if (Arrays.deepEquals(c, d)) { System.out.println("Arrays c, d are equal"); } else { System.out.println("Arrays c, d are not equal"); } } }

71

The example explains the difference between the two methods.


int[] a = {1, 1, 2, 1, 1}; int[] b = {0, 0, 3, 0, 0};

We have two arrays of integers.


int[][] c = { {1, 1, 2, 1, 1}, {0, 0, 3, 0, 0} };

The c array has two inner arrays. The elements of the inner arrays are equal to the a, b arrays.
int[][] d = { a, b };

The d array contains references to a, b arrays.


System.out.print("equals() method: "); if (Arrays.equals(c, d)) { System.out.println("Arrays c, d are equal"); } else { System.out.println("Arrays c, d are not equal"); } System.out.print("deepEquals() method: "); if (Arrays.deepEquals(c, d)) { System.out.println("Arrays c, d are equal"); } else { System.out.println("Arrays c, d are not equal"); }

Now the c, d arrays are compared using both methods. For the equals() method, the arrays are not equal. The deepEquals() method goes deeper in the referenced arrays and retrieves their elements for comparison. For this method, the c, d arrays are equal.
$ java com.zetcode.ComparingArrays equals() method: Arrays c, d are not equal deepEquals() method: Arrays c, d are equal

This is the example output.

72

Searching arrays
The Arrays class has a simple method for searching elements in an array. It is called the binarySearch(). The method searches for elements using a binary search algorithm. The binarySearch() method only works on sorted arrays.
package com.zetcode; import java.util.Arrays; public class Searching { public static void main(String[] args) { String[] planets = { "Mercury", "Venus", "Mars", "Earth", "Jupiter", "Saturn", "Uranus", "Neptune", "Pluto" }; Arrays.sort(planets); String p = "Earth"; int r = Arrays.binarySearch(planets, p); String msg; if (r >= 0) { msg = String.format("%s was found at position %d of the " + "sorted array", p, r); } else { msg = p + " was not found"; } System.out.println(msg); } }

In the example, we search for the "Earth" string in an array of planets.


Arrays.sort(planets);

Since the algorithm only works on sorted arrays, we must sort the array first.
String p = "Earth";

We will be searching for the "Earth" element.


int r = Arrays.binarySearch(planets, p);

The binarySearch() method is called. The first parameter is the array name, the second the element we are looking for. If the element is found, the return value is greater or equal to zero. In such a case, it is the index of the element in the sorted array.
if (r >= 0) { msg = String.format("%s was found at position %d of the " + "sorted array", p, r);

73

} else { msg = p + " was not found"; }

Depending on the returned value, we create a message.


$ java com.zetcode.Searching Earth was found at position 0 of the sorted array

This is the example output. In this part of the Java tutorial, we worked with arrays.

Expressions
In this part of the Java tutorial, we will talk about expressions. Expressions are constructed from operands and operators. The operators of an expression indicate which operations to apply to the operands. The order of evaluation of operators in an expression is determined by the precedence and associativity of the operators. An operator is a special symbol which indicates a certain process is carried out. Operators in programming languages are taken from mathematics. Programmers work with data. The operators are used to process data. An operand is one of the inputs (arguments) of an operator. An operator usually has one or two operands. Those operators that work with only one operand are called unary operators. Those who work with two operands are called binary operators. There is also one ternary operator (?:) which works with three operands. Certain operators may be used in different contexts. For example the + operator. It can be used in different cases. It adds numbers, concatenates strings or indicates the sign of a number. We say that the operator is overloaded.

Sign operators
There are two sign operators. + and -. They are used to indicate or change the sign of a value.
package com.zetcode; public class SignOperators { public static void main(String[] args) { System.out.println(2); System.out.println(+2); System.out.println(-2); } }

The + and - signs indicate the sign of a value. The plus sign can be used to signal that we have a positive number. It can be omitted and it is mostly done so. 74

package com.zetcode; public class MinusSign { public static void main(String[] args) { int a = 1; System.out.println(-a); System.out.println(-(-a)); } }

The minus sign changes the sign of a value.

The assignment operator


The assignment operator = assigns a value to a variable. A variable is a placeholder for a value. In mathematics, the = operator has a different meaning. In an equation, the = operator is an equality operator. The left side of the equation is equal to the right one.
int x = 1;

Here we assign a number to the x variable.


x = x + 1;

This expression does not make sense in mathematics. But it is legal in programming. The expression adds 1 to the x variable. The right side is equal to 2 and 2 is assigned to x.
3 = x;

This code line results in syntax error. We cannot assign a value to a literal.

Concatenating strings
In Java the + operator is also used to concatenate strings.
package com.zetcode; public class ConcatenateStrings { public static void main(String[] args) { System.out.println("Return " + "of " + "the king."); System.out.println("Return".concat(" of").concat(" the king."));

} }

We join three strings together.


System.out.println("Return " + "of " + "the king.");

Strings are joined with the + operator.

75

System.out.println("Return".concat(" of").concat(" the king."));

An alternative method for concatenating strings is the concat method.


$ java com.zetcode.ConcatenateStrings Return of the king. Return of the king.

This is the output of the com.zetcode.ConcatenateStrings program.

Increment, decrement operators


Incrementing or decrementing a value by one is a common task in programming. Java has two convenient operators for this: ++ and --.
x++; x = x + 1; ... y--; y = y - 1;

The above two pairs of expressions do the same.


package com.zetcode; public class IncDec { public static void main(String[] args) { int x = 6; x++; x++; System.out.println(x); x--; System.out.println(x); } }

In the above example, we demonstrate the usage of both operators.


int x = 6; x++; x++;

We initiate the x variable to 6. Then we increment the x two times. Now the variable equals to 8.
x--;

We use the decrement operator. Now the variable equals to 7.

76

$ java com.zetcode.IncDec 8 7

And here is the output of the example.

Arithmetic operators
The following is a table of arithmetic operators in Java. Symbol Name + Addition Subtraction * Multiplication / Division % Remainder The following example shows arithmetic operations.
package com.zetcode; public class Arithmetic { public static void main(String[] args) { int a = 10; int b = 11; int c = 12; int int int int int add = a + b + c; sb = c - a; mult = a * b; div = c / 3; rem = c % a;

System.out.println(add); System.out.println(sb); System.out.println(mult); System.out.println(div); System.out.println(rem); } }

In the preceding example, we use addition, subtraction, multiplication, division and remainder operations. This is all familiar from the mathematics.
int rem = c % a;

The % operator is called the remainder or the modulo operator. It finds the remainder of division of one number by another. For example, 9 % 4, 9 modulo 4 is 1, because 4 goes into 9 twice with a remainder of 1.
$ java com.zetcode.Arithmetic 33

77

2 110 4 2

This is the output of the example. Next we will show the distinction between integer and floating point division.
package com.zetcode; public class Division { public static void main(String[] args) { int c = 5 / 2; System.out.println(c); double d = 5 / 2.0; System.out.println(d);

} }

In the preceding example, we divide two numbers.


int c = 5 / 2;

In this code, we have done integer division. The returned value of the division operation is an integer. When we divide two integers the result is an integer.
double d = 5 / 2.0;

If one of the values is a double or a float, we perform a floating point division. In our case, the second operand is a double so the result is a double.
$ java com.zetcode.Division 2 2.5

We see the result of the com.zetcode.Division program.

Boolean operators
In Java we have three logical operators. The boolean keyword is used to declare a Boolean value. Symbol Name && logical and || logical or ! negation Boolean operators are also called logical.

78

package com.zetcode; public class BooleanOperators { public static void main(String[] args) { int x = 3; int y = 8; System.out.println(x == y); System.out.println(y > x); if (y > x) { } } } System.out.println("y is greater than x");

Many expressions result in a boolean value. Boolean values are used in conditional statements.
System.out.println(x == y); System.out.println(y > x);

Relational operators always result in a boolean value. These two lines print false and true.
if (y > x) { } System.out.println("y is greater than x");

The body of the if statement is executed only if the condition inside the parentheses is met. The y > x returns true, so the message "y is greater than x" is printed to the terminal. The true and false keywords represent boolean literals in Java.
package com.zetcode; public class AndOperator { public static void main(String[] args) { boolean boolean boolean boolean a b c d = = = = true && true; true && false; false && true; false && false;

} }

System.out.println(a); System.out.println(b); System.out.println(c); System.out.println(d);

The code example shows the logical and operator. It evaluates to true only if both operands are true.

79

$ java com.zetcode.AndOperator true false false false

Only one expression results in true. The logical or || operator evaluates to true, if either of the operands is true.
package com.zetcode; public class OrOperator { public static void main(String[] args) { boolean boolean boolean boolean a b c d = = = = true || true; true || false; false || true; false || false;

System.out.println(a); System.out.println(b); System.out.println(c); System.out.println(d); } }

If one of the sides of the operator is true, the outcome of the operation is true.
$ java com.zetcode.OrOperator true true true false

Three of four expressions result in true. The negation operator ! makes true false and false true.
package com.zetcode; public class Negation { public static void main(String[] args) { System.out.println(! true); System.out.println(! false); System.out.println(! (4 < 3)); } }

The example shows the negation operator in action.


$ java com.zetcode.Negation false true

80

true

This is the output of the com.zetcode.Negation program. The ||, and && operators are short circuit evaluated. Short circuit evaluation means that the second argument is only evaluated if the first argument does not suffice to determine the value of the expression: when the first argument of the logical and evaluates to false, the overall value must be false; and when the first argument of logical or evaluates to true, the overall value must be true. Short circuit evaluation is used mainly to improve performance. An example may clarify this a bit more.
package com.zetcode; public class ShortCircuit { public static boolean One() { System.out.println("Inside one"); return false;

public static boolean Two() { System.out.println("Inside two"); return true;

public static void main(String[] args) { System.out.println("Short circuit"); if (One() && Two()) { System.out.println("Pass"); } System.out.println("#############"); if (Two() || One()) { } } } System.out.println("Pass");

We have two methods in the example. They are used as operands in boolean expressions. We will see if they are called or not.
if (One() && Two()) { } System.out.println("Pass");

The One() method returns false. The short circuit && does not evaluate the second method. It is not necessary. Once an operand is false, the result of the logical conclusion is always false. Only "Inside one" is only printed to the console. 81

if (Two() || One()) { } System.out.println("Pass");

In the second case, we use the || operator and use the Two() method as the first operand. In this case, "Inside two" and "Pass" strings are printed to the terminal. It is again not necessary to evaluate the second operand, since once the first operand evaluates to true, the logical or is always true.
$ java com.zetcode.ShortCircuit Short circuit Inside one ############# Inside two Pass

We see the result of the com.zetcode.ShortCircuit program.

Relational Operators
Relational operators are used to compare values. These operators always result in a boolean value. Symbol Meaning < less than <= less than or equal to > greater than >= greater than or equal to == equal to != not equal to Relational operators are also called comparison operators.
package com.zetcode; public class Relational { public static void main(String[] args) { System.out.println(3 System.out.println(3 System.out.println(4 System.out.println(4 } } < 4); == 4); >= 3); != 3);

In the code example, we have four expressions. These expressions compare integer values. The result of each of the expressions is either true or false. In Java we use the == to compare numbers. Some languages like Ada, Visual Basic, or Pascal use = for comparing numbers.

82

Bitwise operators
Decimal numbers are natural to humans. Binary numbers are native to computers. Binary, octal, decimal or hexadecimal symbols are only notations of the same number. Bitwise operators work with bits of a binary number. Bitwise operators are seldom used in higher level languages like Java. Symbol Meaning ~ bitwise negation ^ bitwise exclusive or & bitwise and | bitwise or The bitwise negation operator changes each 1 to 0 and 0 to 1.
System.out.println(~7); // prints -8 System.out.println(~ -8); // prints 7

The operator reverts all bits of a number 7. One of the bits also determines, whether the number is negative or not. If we negate all the bits one more time, we get number 7 again. The bitwise and operator performs bit-by-bit comparison between two numbers. The result for a bit position is 1 only if both corresponding bits in the operands are 1.
& = 00110 00011 00010

The first number is a binary notation of 6. The second is 3. The result is 2.


System.out.println(6 & 3); // prints 2 System.out.println(3 & 6); // prints 2

The bitwise or operator performs bit-by-bit comparison between two numbers. The result for a bit position is 1 if either of the corresponding bits in the operands is 1.
00110 | 00011 = 00111

The result is 00110 or decimal 7.


System.out.println(6 | 3); // prints 7 System.out.println(3 | 6); // prints 7

The bitwise exclusive or operator performs bit-by-bit comparison between two numbers. The result for a bit position is 1 if one or the other (but not both) of the corresponding bits in the operands is 1.
^ = 00110 00011 00101

83

The result is 00101 or decimal 5.


System.out.println(6 ^ 3); // prints 5 System.out.println(3 ^ 6); // prints 5

Compound assignment operators


The compound assignment operators consist of two operators. They are shorthand operators.
a = a + 3; a += 3;

The += compound operator is one of these shorthand operators. The above two expressions are equal. Value 3 is added to the a variable. Other compound operators are:
-= *= /= %= &= package com.zetcode; |= <<= >>=

public class CompoundOperators { public static void main(String[] args) { int a = 1; a = a + 1; System.out.println(a); a += 5; System.out.println(a); a *= 3; System.out.println(a);

} }

In the example, we use two compound operators.


int a = 1; a = a + 1;

The a variable is initiated to one. 1 is added to the variable using the non-shorthand notation.
a += 5;

Using a += compound operator, we add 5 to the a variable. The statement is equal to a = a + 5;.
a *= 3;

Using the *= operator, the a is multiplied by 3. The statement is equal to a = a * 3;.


$ java com.zetcode.CompoundOperators 2

84

7 21

This is the example output.

The instanceof operator


The instanceof operator compares an object to a specified type.
package com.zetcode; class Base {} class Derived extends Base {} public class InstanceofOperator { public static void main(String[] args) { Base b = new Base(); Derived d = new Derived(); System.out.println(d instanceof Base); System.out.println(b instanceof Derived); System.out.println(d instanceof Object);

} }

In the example, we have two classes. One base and one derived from the base.
System.out.println(d instanceof Base);

This line checks if the variable d points to the class that is an instance of the Base class. Since the Derived class inherits from the Base class, it is also an instance of the Base class too. The line prints true.
System.out.println(b instanceof Derived);

The b object is not an instance of the Derived class. This line prints false.
System.out.println(d instanceof Object);

Every class has Object as a superclass. Therefore, the d object is also an instance of the Object class.
$ java com.zetcode.InstanceofOperator true false true

This is the output of the com.zetcode.InstanceofOperator program.

85

Operator precedence
The operator precedence tells us which operators are evaluated first. The precedence level is necessary to avoid ambiguity in expressions. What is the outcome of the following expression? 28 or 40?
3 + 5 * 5

Like in mathematics, the multiplication operator has a higher precedence than addition operator. So the outcome is 28.
(3 + 5) * 5

To change the order of evaluation, we can use parentheses. Expressions inside parentheses are always evaluated first. The result of the above expression is 40. The following table shows common Java operators ordered by precedence (highest precedence first): Meaning Associativity [] () . array access, method invoke, object member access Left-to-right ++ -- + increment, decrement, unary plus and minus Right-to-left ! ~ (type) new negation, bitwise NOT, type cast, object creation Right-to-left * / % multiplication, division, modulo Left-to-right + addition, subtraction Left-to-right + string concatenation Left-to-right << >> >>> shift Left-to-right < <= > >= relational Left-to-right instanceof type comparison Left-to-right == != equality Left-to-right & bitwise AND Left-to-right ^ bitwise XOR Left-to-right | bitwise OR Left-to-right && logical AND Left-to-right || logical OR Left-to-right ? : ternary Right-to-left = simple assignment Right-to-left += -= *= /= %= &= compound assignment Right-to-left ^= |= <<= >>= >>>= compound assignment Right-to-left Table: Operator precedence and associativity Operators on the same row of the table have the same precedence. If we use operators with the same precedence, then the associativity rule is applied.
package com.zetcode; public class Precedence {

Operator

86

public static void main(String[] args) { System.out.println(3 + 5 * 5); System.out.println((3 + 5) * 5); System.out.println(! true | true); System.out.println(! (true | true));

} }

In this code example, we show a few expressions. The outcome of each expression is dependent on the precedence level.
System.out.println(3 + 5 * 5);

This line prints 28. The multiplication operator has a higher precedence than addition. First, the product of 5*5 is calculated, then 3 is added.
System.out.println(! true | true);

In this case, the negation operator has a higher precedence than the bitwise OR. First, the initial true value is negated to false, then the | operator combines false and true, which gives true in the end.
$ java com.zetcode.Precedence 28 40 true false

This is the example output.

Associativity
Sometimes the precedence is not satisfactory to determine the outcome of an expression. There is another rule called associativity. The associativity of operators determines the order of evaluation of operators with the same precedence level.
9 / 3 * 3

What is the outcome of this expression? 9 or 1? The multiplication, deletion and the modulo operator are left to right associated. So the expression is evaluated this way: (9 / 3) * 3 and the result is 9. Arithmetic, boolean, relational and bitwise operators are all left to right associated. The assignment operators, ternary operator, increment, decrement, unary plus and minus, negation, bitwise NOT, type cast, object creation operators are right associated.
package com.zetcode; public class Associativity { public static void main(String[] args) {

87

int a, b, c, d; a = b = c = d = 0; String str = String.format("%d %d %d %d", a, b, c, d); System.out.println(str); int j = 0; j *= 3 + 1; System.out.println(j);

} }

In the example, we have two cases where the associativity rule determines the expression.
int a, b, c, d; a = b = c = d = 0;

The assignment operator is right to left associated. If the associativity was left to right, the previous expression would not be possible.
int j = 0; j *= 3 + 1;

The compound assignment operators are right to left associated. We might expect the result to be 1. But the actual result is 0. Because of the associativity. The expression on the right is evaluated first and than the compound assignment operator is applied.
$ java com.zetcode.Associativity 0 0 0 0 0

Output of the com.zetcode.Associativity program.

The ternary operator


The ternary operator ? : is a conditional operator. It is a convenient operator for cases where we want to pick up one of two values, depending on the conditional expression.
cond-exp ? exp1 : exp2

If cond-exp is true, exp1 is evaluated and the result is returned. If the cond-exp is false, exp2 is evaluated and its result is returned.
package com.zetcode; public class TernaryOperator { public static void main(String[] args) { int age = 31; boolean adult = age >= 18 ? true : false; System.out.println(String.format("Adult: %s", adult)); }

88

In most countries the adulthood is based on your age. You are adult if you are older than a certain age. This is a situation for a ternary operator.
bool adult = age >= 18 ? true : false;

First the expression on the right side of the assignment operator is evaluated. The first phase of the ternary operator is the condition expression evaluation. So if the age is greater or equal to 18, the value following the ? character is returned. If not, the value following the : character is returned. The returned value is then assigned to the adult variable.
$ java com.zetcode.TernaryOperator Adult: true

A 31 years old person is adult.

Calculating prime numbers


In the following example, we are going to calculate prime numbers.
package com.zetcode; public class PrimeNumbers { public static void main(String[] args) { int[] nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24}; System.out.print("Prime numbers: "); for (int num : nums) { if (num == 1 || num == 2 || num == 3) { System.out.print(num + " "); continue; } int i = (int) Math.sqrt(num); boolean isPrime = true; while (i > 1) { if (num % i == 0) { } i--; } if (isPrime) { isPrime = false;

89

} } } }

System.out.print(num + " ");

System.out.print('\n');

In the above example, we deal with several operators. A prime number (or a prime) is a natural number that has exactly two distinct natural number divisors: 1 and itself. We pick up a number and divide it by numbers, from 1 up to the picked up number. Actually, we do not have to try all smaller numbers; we can divide by numbers up to the square root of the chosen number. The formula will work. We use the remainder division operator.
int[] nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24};

We will calculate primes from these numbers.


if (num == 1 || num == 2 || num == 3) { System.out.print(num + " "); continue; }

We skip the calculations for the 1, 2, 3 numbers. They are primes. Note the usage of the equality and conditional or operators. The == has a higher precedence than the || operator. So we do not need to use parentheses.
int i = (int) Math.sqrt(num);

We are OK if we only try numbers smaller than the square root of a number in question.
while (i > 1) { ... i--; }

This is a while loop. The i is the calculated square root of the number. We use the decrement operator to decrease the i by one each loop cycle. When the i is smaller than 1, we terminate the loop. For example, we have number 9. The square root of 9 is 3. We will divide the 9 number by 3 and 2. This is sufficient for our calculation.
if (num % i == 0) { } isPrime = false;

If the remainder division operator returns 0 for any of the i values, then the number in question is not a prime. In this part of the Java tutorial, we covered Java expressions.

90

Flow control
In this part of the Java tutorial, we will talk about the flow control. We will define several keywords that enable us to control the flow of a Java program. In Java language there are several keywords that are used to alter the flow of the program. When the program is run, the statements are executed from the top of the source file to the bottom. One by one. This flow can be altered by specific keywords. Statements can be executed multiple times. Some statements are called conditional statements. They are executed only if a specific condition is met.

The if statement
The if statement has the following general form:
if (expression) { } statement;

The if keyword is used to check if an expression is true. If it is true, a statement is then executed. The statement can be a single statement or a compound statement. A compound statement consists of multiple statements enclosed by a block. A block is code enclosed by curly brackets. The brackets are optional if we have only one statement in the body.
package com.zetcode; import java.util.Random; public class IfStatement { public static void main(String[] args) { Random r = new Random(); int num = r.nextInt(); if (num > 0) { System.out.println("The number is positive"); } } }

A random number is generated. If the number is greater than zero, we print a message to the terminal.
Random r = new Random(); int num = r.nextInt();

These two lines generate a random integer. The number can be both positive and negative.
if (num > 0) {

91

System.out.println("The number is positive");

Using the if keyword, we check if the generated number is greater than zero. The if keyword is followed by a pair of round brackets. Inside the brackets, we place an expression. The expression results in a boolean value. If the boolean value is true, then the block enclosed by two curly brackets is executed. In our case, the string "The number is positive" is printed to the terminal. If the random value is negative, nothing is done. The curly brackets are optional if we have only one expression. We can use the else keyword to create a simple branch. If the expression inside the square brackets following the if keyword evaluates to false, the statement following the else keyword is automatically executed.
package com.zetcode; import java.util.Random; public class Branch { public static void main(String[] args) { Random r = new Random(); int num = r.nextInt(); if (num > 0) { System.out.println("The number is positive"); } else { System.out.println("The number is negative"); } } }

Either the block following the if keyword is executed, or the block following the else keyword.
if (num > 0) { System.out.println("The number is positive"); } else { System.out.println("The number is negative"); }

The else keyword follows the right curly bracket of the if block. It has its own block enclosed by a pair of curly brackets.
$ java com.zetcode.Branch The number is positive $ java com.zetcode.Branch The number is negative $ java com.zetcode.Branch

92

The number is negative

We run the example three times. This is a sample output. We can create multiple branches using the else if keyword. The else if keyword tests for another condition, if and only if the previous condition was not met. Note that we can use multiple else if keywords in our tests. The previous program had a slight issue. Zero was given to negative values. The following program will fix this.
package com.zetcode; import java.util.Scanner; public class MultipleBranches { public static void main(String[] args) { System.out.print("Enter an integer:"); Scanner sc = new Scanner(System.in); int num = sc.nextInt(); if (num < 0) { System.out.println("The integer is negative"); } else if (num == 0) { System.out.println("The integer equals to zero"); } else { } } } System.out.println("The integer is positive");

We receive a value from the user and test it if it is a negative number or positive, or if it equals to zero.
System.out.print("Enter an integer:");

A prompt to enter an integer is written to the standard output.


Scanner sc = new Scanner(System.in); int num = sc.nextInt();

Using the Scanner class of the java.util package, we read an integer value from the standard input.
if (num < 0) { System.out.println("The integer is negative"); } else if (num == 0) { System.out.println("The integer equals to zero");

93

} else { } System.out.println("The integer is positive");

If the first condition evaluates to true, e.g. the entered value is less than zero, the first block is executed and the remaining two blocks are skipped. If the first condition is not met, then the second condition following the if else keywords is checked. If the second condition evaluates to true, the second block is executed. If not, the third block following the else keyword is executed. The else block is always executed if the previous conditions were not met.
$ java com.zetcode.MultipleBranches Enter an integer:4 The integer is positive $ java com.zetcode.MultipleBranches Enter an integer:0 The integer equals to zero $ java com.zetcode.MultipleBranches Enter an integer:-3 The integer is negative

We run the example three times, so that all conditions are tested. The zero is correctly handled.

The switch statement


The switch statement is a selection control flow statement. It allows the value of a variable or expression to control the flow of a program execution via a multi-way branch. It creates multiple branches in a simpler way than using the combination of if, else if statements. Each branch is ended with the break keyword. We have a variable or an expression. The switch keyword is used to test a value from the variable or the expression against a list of values. The list of values is presented with the case keyword. If the values match, the statement following the case is executed. There is an optional default statement. It is executed if no other match is found.
package com.zetcode; import java.util.Scanner; public class SwitchStatement { public static void main(String[] args) { System.out.print("Enter a domain:"); Scanner sc = new Scanner(System.in); String domain = sc.nextLine(); domain = domain.trim().toLowerCase(); switch (domain) { case "us":

94

System.out.println("United States"); break; case "de": System.out.println("Germany"); break; case "sk": System.out.println("Slovakia"); break; case "hu": System.out.println("Hungary"); break; default: System.out.println("Unknown"); break; } } }

The user is requested to enter a domain name. The domain name is read and stored in a variable. The variable is tested with the switch keyword against a list of options. In our program, we have a domain variable. We read a value for the variable from the command line. We use the case statement to test for the value of the variable. There are several options. If the value equals for example to "us" the "United States" string is printed to the console.
Scanner sc = new Scanner(System.in); String domain = sc.nextLine();

Input from the user is read from the console.


domain = domain.trim().toLowerCase();

The trim() method strips the variable from potential leading and trailing white spaces. The toLowerCase() converts the characters to lowercase. Now the "us", "US", "us " are viable options for the us domain name.
switch (domain) { ... }

In the round brackets, the switch keyword takes an input which is going to be tested. The input can be of byte, short, char, int, enum or String data type. The body of the switch keyword is placed inside a pair or curly brackets. In the body, we can place multiple case options. Each option is ended with the break keyword.
case "us": System.out.println("United States"); break;

In this case option, we test if the domain variable is equal to "us" string. If true, we print a message to the console. The option is ended with the break keyword. If one of the options is successfully evaluated, the break keyword terminates the switch block. 95

default: System.out.println("Unknown"); break;

The default keyword is optional. If none of the case options is evaluated, then the default section is executed.
$ java com.zetcode.SwitchStatement Enter a domain:us United States

This is a sample output.

The while statement


The while statement is a control flow statement that allows code to be executed repeatedly based on a given boolean condition. This is the general form of the while loop:
while (expression) { } statement;

The while keyword executes the statements inside the block enclosed by the curly brackets. The statements are executed each time the expression is evaluated to true.
package com.zetcode; public class WhileStatement { public static void main(String[] args) { int i = 0; int sum = 0; while (i < 10) { i++; sum += i;

} }

System.out.println(sum); }

In the code example, we calculate the sum of values from a range of numbers. The while loop has three parts. Initialization, testing and updating. Each execution of the statement is called a cycle.
int i = 0;

We initiate the i variable. It is used as a counter. 96

while (i < 10) { ... }

The expression inside the round brackets following the while keyword is the second phase, the testing. The statements in the body are executed until the expression is evaluated to false.
i++;

The last phase of the while loop is the updating. We increment the counter. Note that improper handling of the while loops may lead to endless cycles.
$ java com.zetcode.WhileStatement 55

The program calculated the sum of 0, 1, ..., 9 values. There is a modified version of the while statement. It is the do while statement. It is guaranteed that the statements inside the block are run at least once. Even if the condition is not met.
package com.zetcode; public class DoWhile { public static void main(String[] args) { int count = 0; do { } } System.out.println(count); } while (count != 0);

First the block is executed and then the truth expression is evaluated. In our case, the condition is not met and the do while statement terminates.

The for statement


When the number of cycles is know before the loop is initiated, we can use the for statement. In this construct we declare a counter variable, which is automatically increased or decreased in value during each repetition of the loop.
package com.zetcode; public class ForStatement { public static void main(String[] args) { for (int i = 0; i < 10; i++) { System.out.println(i); } }

97

In this example, we print numbers 0..9 to the console.


for (int i = 0; i < 10; i++) { System.out.println(i); }

There are three phases. First, we initiate the counter i to zero. This phase is done only once. Next comes the condition. If the condition is met, the statement inside the for block is executed. Then comes the third phase; the couter is increased. Now we repeat the 2, 3 phases until the condition is not met and the for loop is left. In our case, when the counter i is equal to 10, the for loop stops executing. A for loop can be used for easy traversal of an array. From the length property of the array, we know the size of the array.
package com.zetcode; public class ForStatement2 { public static void main(String[] args) { String[] planets = {"Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Pluto"}; for (int i = 0; i < planets.length; i++) { } System.out.println(planets[i]);

System.out.println("In reverse:"); for (int i = planets.length - 1; i >= 0; i--) { System.out.println(planets[i]); } } }

We have an array holding the names of planets in our Solar System. Using two for loops, we print the values in ascending and descending orders.
for (int i = 0; i < planets.length; i++) { System.out.println(planets[i]); }

The arrays are accessed by zero-based indexing. The first item has index 0. Therefore, the i variable is initialized to zero. The condition checks if the i variable is less than the lenght of the array. In the final phase, the i variable is incremented.
for (int i = planets.length - 1; i >= 0; i--) { System.out.println(planets[i]);

98

This for loop prints the elements of the array in reverse order. The i counter is initialized to array size. Since the indexing is zero based, the last element has index array size-1. The condition ensures that the counter is greater or equal to zero. (Array indexes cannot be negative). In the third step, the i counter is decremented by one. More expressions can be placed in the initialization and iteration phase of the for loop.
package com.zetcode; import java.util.Arrays; import java.util.Random; public class ForStatement3 { public static void main(String[] args) { Random r = new Random(); int[] values = new int[10]; int num; int sum=0; for (int i = 0; i < 10; i++, sum += num) { num = r.nextInt(10); values[i] = num; } System.out.println(Arrays.toString(values)); System.out.println("The sum of the values is " + sum); } }

In our example, we create an array of ten random numbers. A sum of the numbers is calculated.
for (int i = 0; i < 10; i++, sum += num) { num = r.nextInt(10); values[i] = num; }

In the third part of the for loop, we have two expressions separated by a comma character. The i counter is incremented and the current number is added to the sum variable.
$ java com.zetcode.ForStatement3 [1, 9, 2, 9, 0, 9, 8, 5, 5, 3] The sum of the values is 51

This is sample execution of the program.

99

The enhanced for statement


The enhanced for statement simplifies traversing over collections of data. It has no explicit counter. The statement goes through an array or a collection one by one and the current value is copied to a variable defined in the construct.
package com.zetcode; public class EnhancedFor { public static void main(String[] args) { String[] planets = { "Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Pluto" }; for (String planet : planets) { System.out.println(planet); } } }

In this example, we use the enhanced for statement to go through an array of planets.
for (String planet : planets) { System.out.println(planet); }

The usage of the for statement is straightforward. The planets is the array that we iterate through. The planet is the temporary variable that has the current value from the array. The for statement goes through all the planets and prints them to the console.
$ java com.zetcode.EnhancedFor Mercury Venus Earth Mars Jupiter Saturn Uranus Pluto

Running the above Java program gives this output.

The break statement


The break statement can be used to terminate a block defined by while, for or switch statements.
package com.zetcode; import java.util.Random;

100

public class BreakStatement { public static void main(String[] args) { Random random = new Random(); while (true) { int num = random.nextInt(30); System.out.print(num + " "); if (num == 22) { } } } } System.out.print('\n'); break;

We define an endless while loop. We use the break statement to get out of this loop. We choose a random value from 1 to 30. We print the value. If the value equals to 22, we finish the endless while loop.
while (true) { ... }

Placing true in the brackets of the while statement creates an endless loop. We must terminate the loop ourselves. Note that such code is error-prone. We should avoid using such loops.
if (num == 22) { } break;

When the randomly chosen value is equal to 22, the break statement is executed. The while loop is terminated.
$ java com.zetcode.BreakStatement 23 12 0 4 13 16 6 12 11 9 24 23 23 19 15 26 3 3 27 28 25 3 3 25 6 22 $ java com.zetcode.BreakStatement 23 19 29 27 3 28 2 2 26 0 0 24 17 4 7 12 8 20 22 $ java com.zetcode.BreakStatement 15 20 10 25 2 19 26 4 13 21 15 21 21 24 3 22

Here we see three sample executions of the program.

The continue statement


The continue statement is used to skip a part of the loop and continue with the next iteration of the loop. It can be used in combination with for and while statements.

101

In the following example, we will print a list of numbers that cannot be divided by 2 without a remainder.
package com.zetcode; public class ContinueStatement { public static void main(String[] args) { int num = 0; while (num < 100) { num++; if ((num % 2) == 0) { continue; } } } System.out.print(num + " ");

System.out.print('\n'); }

We iterate through numbers 1..99 with the while loop.


if ((num % 2) == 0) { continue; }

If the expression num % 2 returns 0, the number in question can be divided by 2. The continue statement is executed and the rest of the cycle is skipped. In our case, the last statement of the loop is skipped and the number is not printed to the console. The next iteration is started. In this part of the Java tutorial, we were talking about control flow structures.

Object-oriented programming
In this part of the Java tutorial, we will talk about object-oriented programming in Java. There are three widely used programming paradigms there. Procedural programming, functional programming and object-oriented programming. Java supports both procedural and object-oriented programming. Object-oriented programming (OOP) is a programming paradigm that uses objects and their interactions to design applications and computer programs. There are some basic programming concepts in OOP:

Abstraction

102

Polymorphism Encapsulation Inheritance

The abstraction is simplifying complex reality by modeling classes appropriate to the problem. The polymorphism is the process of using an operator or function in different ways for different data input. The encapsulation hides the implementation details of a class from other objects. The inheritance is a way to form new classes using classes that have already been defined.

Objects
Objects are basic building blocks of a Java OOP program. An object is a combination of data and methods. In an OOP program, we create objects. These objects communicate together through methods. Each object can receive messages, send messages and process data. There are two steps in creating an object. First, we define a class. A class is a template for an object. It is a blueprint which describes the state and behavior that the objects of the class all share. A class can be used to create many objects. Objects created at runtime from a class are called instances of that particular class.
package com.zetcode; class Being {} public class SimpleObject { public static void main(String[] args) { Being b = new Being(); System.out.println(b);

} }

In our first example, we create a simple object.


class Being {}

This is a simple class definition. The body of the template is empty. It does not have any data or methods.
Being b = new Being();

We create a new instance of the Being class. For this we have the new keyword. The b variable is the handle to the created object.
System.out.println(b);

We print the object to the console to get some basic description of the object. What does it mean, to print an object? When we print an object, we in fact call its toString() method. But we have not defined any method yet. It is because every object created inherits from the

103

base Object. It has some elementary functionality which is shared among all objects created. One of this is the toString() method.
$ javac com/zetcode/SimpleObject.java $ ls com/zetcode/ Being.class SimpleObject.class SimpleObject.java

The compiler creates two class files. The SimpleObject.class is the application class and the Being.class is the custom class that we work with in the application.
$ java com.zetcode.SimpleObject com.zetcode.Being@125ee71

We get a the name of the class of which the object is an instance, the @ characer, and the unsigned hexadecimal representation of the hash code of the object.

Object attributes
Object attributes is the data bundled in an instance of a class. The object attributes are called instance variables or member fields. An instance variable is a variable defined in a class, for which each object in the class has a separate copy.
package com.zetcode; class Person { public String name; } public class ObjectAttributes { public static void main(String[] args) { Person p1 = new Person(); p1.name = "Jane"; Person p2 = new Person(); p2.name = "Beky"; System.out.println(p1.name); System.out.println(p2.name); } }

In the above Java code, we have a Person class with one member field.
class Person { } public String name;

We declare a name member field. The public keyword specifies that the member field will be accessible outside the class block.
Person p1 = new Person();

104

p1.name = "Jane";

We create an instance of the Person class and set the name variable to "Jane". We use the dot operator to access the attributes of objects.
Person p2 = new Person(); p2.name = "Beky";

We create another instance of the Person class. Here we set the variable to "Beky".
Console.WriteLine(p1.name); Console.WriteLine(p2.name);

We print the contents of the variables to the console.


$ java com.zetcode.ObjectAttributes Jane Beky

We see the output of the program. Each instance of the Person class has a separate copy of the name member field.

Methods
Methods are functions defined inside the body of a class. They are used to perform operations with the attributes of our objects. Methods bring modularity to our programs. Methods are essential in the encapsulation concept of the OOP paradigm. For example, we might have a connect() method in our AccessDatabase class. We need not to be informed how exactly the method connect() connects to the database. We only have to know that it is used to connect to a database. This is essential in dividing responsibilities in programming. Especially in large applications. Objects group state and behavior. Methods represent the behavioral part of the objects.
package com.zetcode; class Circle { private int radius; public void setRadius(int radius) { } this.radius = radius;

public double area() { return this.radius * this.radius * Math.PI; } }

public class Methods { public static void main(String[] args) {

105

Circle c = new Circle(); c.setRadius(5); } } System.out.println(c.area());

In the code example, we have a Circle class. In the class, we define two methods. The setRadius() method assigns a value to the radius member and the area() method computes an area of the circle from the class member and a constant.
private int radius;

We have one member field in the class. It is the radius of the circle. The private keyword is an access specifier. It tells that the variable is restricted to the outside world. If we want to modify this variable from the outside, we must use the publicly available setRadius() method. This way we protect our data.
public void setRadius(int radius) { } this.radius = radius;

This is the setRadius() method. The this variable is a special variable which we use to access the member fields from methods. The this.radius is an instance variable, while the radius is a local variable, valid only inside the setRadius() method.
Circle c = new Circle(); c.setRadius(5);

We create an instance of the Circle class and set its radius by calling the setRadius() method on the object of the circle. We use the dot operator to call the method.
public double area() { } return this.radius * this.radius * Math.PI;

The area() method returns the area of a circle. The Math.PI is a built-in constant.
$ java com.zetcode.Methods 78.53981633974483

Running the example we get the above output.

Access modifiers
Access modifiers set the visibility of methods and member fields. Java has three access modifiers: public, protected and private. The public members can be accessed from anywhere. The protected members can be accessed only within the class itself, by inherited classes and other classes from the same package. Finally, the private members are limited to the containing type, e.g. only within its class or interface. If we do not specify an access 106

modifier, we have a package-private visibility. In such a case, members and methods are accessible within the same package. Access modifiers protect data against accidental modifications. They make the programs more robust.
package com.zetcode; class Person { public String name; private int age; public int getAge() { return this.age; } public void setAge(int age) { } } public class AccessModifiers { public static void main(String[] args) { Person p = new Person(); p.name = "Jane"; p.setAge(17); System.out.println(String.format("%s is %d years old", p.name, p.getAge())); this.age = age;

} }

In the above program, we have two member fields. One is declared public, the other private.
public int getAge() { return this.age; }

If a member field is private, the only way to access it is via methods. If we want to modify an attribute outside the class, the method must be declared public. This is an important aspect of data protection.
public void setAge(int age) { this.age = age; }

The setAge() method enables us to change the private age variable from outside of the class definition.

107

Person p = new Person(); p.name = "Jane";

We create a new instance of the Person class. Because the name attribute is public, we can access it directly. However, this is not recommended.
p.setAge(17);

The setAge() method modifies the age member field. The age cannot be accessed or modified directly, because it is declared private.
System.out.println(String.format("%s is %d years old", p.name, p.getAge()));

Finally, we access both members to build a string, which is printed to the console.
$ java com.zetcode.AccessModifiers Jane is 17 years old

Running the example we have this output. The following program will show how access modifiers influence the way members are inherited by subclasses.
package com.zetcode; class Base { public String name = "Base"; protected int id = 5323; private boolean isDefined = true; } class Derived extends Base { public void info() { System.out.println("This is Derived class"); System.out.println("Members inherited:"); System.out.println(this.name); System.out.println(this.id); // System.out.println(this.isDefined);

} }

public class ProtectedMember { public static void main(String[] args) { Derived drv = new Derived(); drv.info(); } }

108

In this program, we have a Derived class which inherits from the Base class. The Base class has three member fields. All with different access modifiers. The isDefined member is not inherited. The private modifier prevents this.
class Derived extends Base {

The Derived class inherits from the Base class. To inherit from another class, we use the extends keyword.
System.out.println(this.name); System.out.println(this.id); // System.out.println(this.isDefined);

The public and the protected members are inherited by the Derived class. They can be accessed. The private member is not inherited. The line accessing the member field is commented. If we uncommented the line, the code would not compile.
$ java com.zetcode.ProtectedMember This is Derived class Members inherited: Base 5323

Running the program, we receive this output.

The constructor
A constructor is a special kind of a method. It is automatically called when the object is created. Constructors do not return values and also do not use the void keyword. The purpose of the constructor is to initiate the state of an object. Constructors have the same name as the class. The constructors are methods, so they can be overloaded too. Constructors cannot be directly invoked. The new keyword invokes them. Constructors cannot be declared synchronized, final, abstract, native, or static. Constructors cannot be inherited. They are called in the order of inheritance. If we do not write any constructor for a class, Java provides an implicit default constructor. If we provide any kind of a constructor then the default is not supplied.
package com.zetcode; class Being { public Being() { System.out.println("Being is created"); } public Being(String being) { } } public class Constructor { System.out.println(String.format("Being %s is created", being));

109

@SuppressWarnings("ResultOfObjectAllocationIgnored") public static void main(String[] args) { new Being(); new Being("Tom"); } }

We have a Being class. This class has two constructors. The first one does not take parameters, the second one takes one parameter.
public Being() { } System.out.println("Being is created");

This constructor does not take any parameters.


public Being(String being) { } System.out.println(String.format("Being %s is created", being));

This constructor takes one string parameter.


@SuppressWarnings("ResultOfObjectAllocationIgnored")

This annotation will suppress a warning that we do not assing our created objects to any variables. Normally this would be a suspicious activity.
new Being();

An instance of the Being class is created. The no-argument constructor is called upon object creation.
new Being("Tom");

Another instance of the Being class is created. This time the constructor with a parameter is called upon object creation.
$ java com.zetcode.Constructor Being is created Being Tom is created

This is the output of the program. In the next example, we initiate data members of the class. Initiation of variables is a typical job for constructors.
package com.zetcode; import java.util.Calendar; import java.util.GregorianCalendar;

110

class MyFriend { private GregorianCalendar born; private String name; public MyFriend(String name, GregorianCalendar born) { this.name = name; this.born = born;

public void info() { System.out.format("%s was born on %s/%s/%s\n", this.name, this.born.get(Calendar.DATE), this.born.get(Calendar.MONTH), this.born.get(Calendar.YEAR));

} }

public class MemberInit { public static void main(String[] args) { String name = "Lenka"; GregorianCalendar born = new GregorianCalendar(1990, 3, 5); MyFriend fr = new MyFriend(name, born); fr.info();

} }

We have a MyFriend class with data members and methods.


private GregorianCalendar born; private String name;

We have two private variables in the class definition.


public MyFriend(String name, GregorianCalendar born) { this.name = name; this.born = born;

In the constructor, we initiate the two data members. The this variable is a handler used to reference the object variables. When the names of constructor parameters and the names of members are equal, using this keyword is required. Otherwise the usage is optional.
MyFriend fr = new MyFriend(name, born); fr.info();

We create a MyFriend object with two arguments. Then we call the info() method of the object.
$ java com.zetcode.MemberInit Lenka was born on 5/3/1990

111

This is the output of the com.zetcode.MemberInit program.

Constructor chaining
Constructor chaining is the ability to call another constructor from a constructor. To call another constructor from the same class, we use the this keyword. To call another constructor from a parent class, we use the super keyword.
package com.zetcode; class Shape { private int x; private int y; public Shape(int x, int y) { this.x = x; this.y = y;

protected int getX() { return this.x; } protected int getY() { } } class Circle extends Shape { private int r; public Circle(int r, int x, int y) { super(x, y); this.r = r; } public Circle() { } this(1, 1, 1); return this.y;

@Override public String toString() { getY()); } } return String.format("Circle: r:%d, x:%d, y:%d", r, getX(),

public class ConstructorChaining { public static void main(String[] args) {

112

Circle c1 = new Circle(5, 10, 10); Circle c2 = new Circle(); System.out.println(c1); System.out.println(c2);

} }

We have a Circle class. The class has two constructors. One that takes one parameter and one that does not take any parameters.
class Shape { private int x; private int y;

... }

The Shape class is responsible for dealing with the x, y coordinates of various shapes.
public Shape(int x, int y) { this.x = x; this.y = y;

The constructor of the Shape class initiates the x, y coordinates with the given parameters.
protected int getX() { } return this.x;

protected int getY() { return this.y; }

We have defined two methods to retrieve the values of the coordinates. The members are private, so the only access possible is through methods.
class Circle extends Shape { private int r; ... }

The Circle class inherits from the Shape class. It defines the radius member which is specific to this shape.
public Circle(int r, int x, int y) { super(x, y); this.r = r; }

113

The first constructor of the Circle class takes three parameters. The radius and the x, y coordinates. With the super keyword, we call the parent's constructor passing the coordinates. Note that the super keyword must be the first statement in the constructor. The second statement initiates the radius member of the circle class.
public Circle() { } this(1, 1, 1);

The second constructor takes no parameters. In such a case, we provide some default values. The this keyword is used to call the three-parameter constructor of the same class, passing three default values.
@Override public String toString() { return String.format("Circle: r:%d, x:%d, y:%d", r, getX(), getY()); }

Inside the toString() method, we provide a string representation of the Circle class. To determine the x, y coordinates, we use the inherited getX() and getY() methods.
$ java com.zetcode.ConstructorChaining Circle: r:5, x:10, y:10 Circle: r:1, x:1, y:1

This is the output of the example.

Class constants
It is possible to create class constants. These constants do not belong to a concrete object. They belong to the class. By convention, constants are written in uppercase letters.
package com.zetcode; class Math { } public static final double PI = 3.14159265359;

public class ClassConstant { public static void main(String[] args) { System.out.println(Math.PI); } }

We have a Math class with a PI constant.


public static final double PI = 3.14159265359;

114

The final keyword is used to define a constant. The static keyword enables to reference the member without creating an instance of the class. The public keyword makes it accessible outside the body of the class.
$ java com.zetcode.ClassConstant 3.14159265359

Running the example we get the above output.

The toString() method


Each object has a toString() method. It returns a human-readable representation of the object. The default implementation returns the fully qualified name of the type of the Object. When we call the System.out.println() method with an object as a parameter, the toString() is being called.
package com.zetcode; class Being { @Override public String toString() { return "This is Being class"; } }

public class ThetoStringMethod { public static void main(String[] args) { Being b = new Being(); Object o = new Object(); System.out.println(o.toString()); System.out.println(b.toString()); System.out.println(b);

} }

We have a Being class in which we override the default implementation of the toString() method.
@Override public String toString() { } return "This is Being class";

Each class created inherits from the base Object. The toString() method belongs to this object class. The @Override annotation informs the compiler that the element is meant to override an element declared in a superclass. The compiler will then check that we did not create any error.
Being b = new Being();

115

Object o = new Object();

We create two objects. One custom defined and one built-in.


System.out.println(o.toString()); System.out.println(b.toString());

We call the toString() method explicitly on these two objects.


Console.WriteLine(b);

As we have specified earlier, placing an object as a parameter to the System.out.println() will call its toString() method. This time, we have called the method implicitly.
$ java com.zetcode.ThetoStringMethod java.lang.Object@125ee71 This is Being class This is Being class

This is what we get when we run the example.

Inheritance
The inheritance is a way to form new classes using classes that have already been defined. The newly formed classes are called derived classes, the classes that we derive from are called base classes. Important benefits of inheritance are code reuse and reduction of complexity of a program. The derived classes (descendants) override or extend the functionality of base classes (ancestors).
package com.zetcode; class Being { public Being() { } } class Human extends Being { public Human() { } } public class Inheritance { @SuppressWarnings("ResultOfObjectAllocationIgnored") public static void main(String[] args) { new Human(); } } System.out.println("Human is created"); System.out.println("Being is created");

116

In this program, we have two classes. A base Being class and a derived Human class. The derived class inherits from the base class.
class Human extends Being {

In Java, we use the extends keyword to create inheritance relations.


new Human();

We instantiate the derived Human class.


$ java com.zetcode.Inheritance Being is created Human is created

We can see that both constructors were called. First, the constructor of the base class is called, then the constructor of the derived class. A more complex example follows.
package com.zetcode; class Being { static int count = 0; public Being() { count++; System.out.println("Being is created");

public void getCount() { System.out.format("There are %d Beings%n", count); } }

class Human extends Being { public Human() { System.out.println("Human is created"); } }

class Animal extends Being { public Animal() { System.out.println("Animal is created"); } }

class Dog extends Animal { public Dog() {

117

} }

System.out.println("Dog is created");

public class Inheritance2 { @SuppressWarnings("ResultOfObjectAllocationIgnored") public static void main(String[] args) { new Human(); Dog dog = new Dog(); dog.getCount(); } }

We have four classes. The inheritance hierarchy is more complicated. The Human and the Animal classes inherit from the Being class. And the Dog class inherits directly from the Animal class and indirectly from the Being class.
static int count = 0;

We define a static variable. Static members are shared by all instances of a class.
public Being() { count++; System.out.println("Being is created");

Each time the Being class is instantiated, we increase the count variable by one. This way we keep track of the number of instances created.
class Animal extends Being { ... class Dog extends Animal { ...

The Animal inherits from the Being and the Dog inherits from the Animal. Indirectly, the Dog inherits from the Being as well.
new Human(); Dog dog = new Dog(); dog.getCount();

We create instances from the Human and from the Dog classes. We call the getCount() method of the Dog object.
$ java com.zetcode.Inheritance2 Being is created Human is created Being is created Animal is created Dog is created There are 2 Beings

118

The Human object calls two constructors. The Dog object calls three constructors. There are two Beings instantiated. We use the super() keyword to call the parent's constructor explicitly.
package com.zetcode; class Shape { protected int x; protected int y; public Shape() { System.out.println("Shape is created"); } public Shape(int x, int y) { this.x = x; this.y = y; } }

class Circle extends Shape { private int r; public Circle(int r, int x, int y) { super(x, y); this.r = r;

@Override public String toString() { } } public class Shapes { public static void main(String[] args) { Circle c = new Circle(2, 5, 6); System.out.println(c); } } return String.format("Circle, r:%d, x:%d, y:%d", r, x, y);

We have two classes. The Shape class and the Circle class. The Shape class is a base class for geometrical shapes. We can put into this class some commonalities of the common shapes, like the x, y coordinates.
public Shape() { } System.out.println("Shape is created");

119

public Shape(int x, int y) { this.x = x; this.y = y; }

The Shape class has two constructors. The first one is a no-argument constructor. The second one takes two parameters: the x, y coordinates.
public Circle(int r, int x, int y) { super(x, y); this.r = r;

This is the constructor for the Circle class. This constructor initiates the r member and calls the parent's second constructor, to which it passes the x, y coordinates. As a consequence, the coordinates are initiated in the Shape class. Have we not called the constructor explicitly with the super keyword, the default constructor of the Shape class would be called.
$ java com.zetcode.Shapes Circle, r:2, x:5, y:6

We can see the output of the com.zetcode.Shapes program.

Final class, private constructor


A class with a final modifier cannot be subclassed. A class with a constructor that has a private modifier cannot be instantiated.
package com.zetcode; final class MyMath { public static final double PI = 3.14159265358979323846; } // other static members and methods

public class FinalClass { public static void main(String[] args) { } } System.out.println(MyMath.PI);

We have a MyMath class. This class has some static members and methods. We do not want anyone to inherit from our class. Therefore, we declare it to be final. Furthermore, we also do not want to allow creation of instances from our class. We decide it to be used only from a static context. Declaring a private constructor, the class cannot be instantiated.

120

package com.zetcode; final class MyMath { private MyMath() {} public static final double PI = 3.14159265358979323846; } // other static members and methods

public class PrivateConstructor { public static void main(String[] args) { } } System.out.println(MyMath.PI);

Our MyMath class cannot be instantiated and cannot be subclassed. This is how java.lang.Math is designed in Java language. This was the first part of the description of OOP in Java.

Methods
In this part of the tutorial, you will learn about Java methods. In object oriented programming, we work with objects. Objects are the basic building blocks of a program. Objects consists of data and methods. Methods change the state of the objects created. They are the dynamic part of the objects; the data is the static part. A method is a code block containing a series of statements. Methods must be declared within a class. It is a good programming practice that methods do only one specific task. Methods bring modularity to programs. Proper use of methods bring the following advantages:

Reducing duplication of code Decomposing complex problems into simpler pieces Improving clarity of the code Reuse of code Information hiding

Basic characteristics of methods are:


Access level Return value type Method name Method parameters Parentheses Block of statements

121

Access level of methods is controlled with access modifiers. They set the visibility of methods. They determine who can call the method. Methods may return a value to the caller. In case our method returns a value, we declare its data type. If not, we use the void keyword to indicate that our method does not return any value. Method parameters are surrounded by parentheses and separated by commas. Empty parentheses indicate that the method requires no parameters. The method block is surrounded with { } characters. The block contains one or more statements, that are executed, when the method is invoked. It is legal to have an empty method block. A method signature is a unique identification of a method for the Java compiler. The signature consists of a method name, and the type and kind (value, reference, or output) of each of its formal parameters. Method signature does not include the return type. Any legal character can be used in the name of a method. By convention, method names begin with a lowercase letter. The method names are verbs or verbs followed by adjectives or nouns. Each subsequent word starts with an uppercase character. The following are typical names of methods in Java:

execute findId setName getName checkIfValid testValidity

Simple example
We start with a simple example.
package com.zetcode; class Base { public void showInfo() { } } public class ShowInfoMethod { public static void main(String[] args) { Base bs = new Base(); bs.showInfo(); } } System.out.println("This is Base class");

We have a showInfo() method that prints the name of its class.


class Base { public void showInfo() {

122

} }

System.out.println("This is Base class");

Each method must be defined inside a class. It must have a name. In our case the name is showInfo. The keywords that precede the name of the method are access specifier and the return type. Parentheses follow the name of the method. They may contain parameters of the method. Our method does not take any parameters.
public static void main(String[] args) { ... }

This is the main() method. It is the entry point to each console or GUI Java application. The method takes a string array of arguments.
Base bs = new Base(); bs.showInfo();

We create an instance of the Base class. We call the showInfo() method upon the object. We say that the method is an instance method. Because it needs an instance to be called. The method is called by specifying the object instance, followed by the member access operator the dot, followed by the method name.

Method parameters
A parameter is a value passed to the method. Methods can take one or more parameters. If methods work with data, we must pass the data to the methods. We do it by specifying them inside the parentheses. In the method definition, we must provide a name and type for each parameter.
package com.zetcode; class AddValues { public int addTwoValues(int x, int y) { } return x + y;

public int addThreeValues(int x, int y, int z) { return x + y + z; } }

public class Addition { public static void main(String[] args) { AddValues a = new AddValues(); int x = a.addTwoValues(12, 13); int y = a.addThreeValues(12, 13, 14); System.out.println(x); System.out.println(y);

123

In the above example, we have an AddValues class which has two methods. One of them takes two parameters, the other one takes three parameters.
public int addTwoValues(int x, int y) { } return x + y;

The addTwoValues() method takes two parameters. These parameters have int type. The method also returns an integer to the caller. We use the return keyword to return a value from the method.
public int addThreeValues(int x, int y, int z) { } return x + y + z;

The addThreeValues() is similar to the previous method. It takes three parameters.


int x = a.addTwoValues(12, 13);

We call the addTwoValues() method of the AddValues object. It takes two values. These values are passed to the method. The method returns a value which is assigned to the x variable.

Variable number of arguments


Since Java 5, a method can take variable number of arguments. For this we use the ellipsis.
package com.zetcode; public class SumOfValues { public static int sum(int...vals) { int sum = 0; for (int val : vals) { sum += val; } return sum; } public static void main(String[] args) { int s1 = sum(1, 2, 3); int s2 = sum(1, 2, 3, 4, 5); int s3 = sum(1, 2, 3, 4, 5, 6, 7); System.out.println(s1); System.out.println(s2); System.out.println(s3);

124

We create a sum() method which can take variable number of arguments. The method will calculate the sum of integers passed to the method.
int s1 = sum(1, 2, 3); int s2 = sum(1, 2, 3, 4, 5); int s3 = sum(1, 2, 3, 4, 5, 6, 7);

We call the sum() method three time. In each case, we pass different number of parameters to the method.
public static int sum(int...vals) { ... }

The sum() method can take variable number of integer values. All values are added to an array.
int sum = 0; for (int val : vals) { sum += val; } return sum;

We compute the sum of the values and return the calculated sum.
$ java com.zetcode.SumOfValues 6 15 28

This is the output of the com.zetcode.SumOfValues example.

Passing arguments by value


In Java, arguments are always passed by value to methods. When we pass primitive types, the copies of the values are sent to the methods. In case of objects, copies of the references are handed to the methods. Java does not support passing arguments by reference, like C# or C++ languages.
package com.zetcode; class Cat {} class Dog {} public class PassByValue { private static void tryChangeInteger(int x) { x = 15;

125

} private static void tryChangeObject(Object o) { Dog d = new Dog(); o = d; } public static void main(String[] args) { int n = 10; tryChangeInteger(n); System.out.println(n); Cat c = new Cat(); tryChangeObject(c); System.out.println(c.getClass());

} }

The example shows that it is not possible to change the values of primitive types and the references to objects inside methods.
private static void tryChangeInteger(int x) { x = 15; }

The value of the passed variable is copied to the local variable x. Assigning a new value to the x variable does not affect the outside variable.
private static void tryChangeObject(Object o) { Dog d = new Dog(); o = d;

The same applies for objects. We are passing copies of references to methods. The o is a local variable that refers to the Dog object. The object defined outside the tryChangeObject() is not affected.
int n = 10; tryChangeInteger(n); System.out.println(n);

We define the n variable and pass it to the tryChangeInteger() method. Later we print it to check if it was modified.
Cat c = new Cat(); tryChangeObject(c); System.out.println(c.getClass());

We define a Cat object and pass it to the tryChangeObject() method.


$ java com.zetcode.PassByValue 10

126

class com.zetcode.Cat

From the output we can see that neither the primitive value nor the object were modified.

Method overloading
Method overloading allows the creation of several methods with the same name which differ in the type of the input. What is method overloading good for? The Qt4 library gives a nice example for the usage. The QPainter class has three methods to draw a rectangle. Their name is drawRect() and their parameters differ. One takes a reference to a floating point rectangle object, another takes a reference to an integer rectangle object, and the last one takes four parameters: x, y, width, and height. If the C++ language, which is the language in which Qt is developed, didn't have method overloading, the creators of the library would have to name the methods like drawRectRectF(), drawRectRect(), drawRectXYWH(). The solution with method overloading is more elegant.
package com.zetcode; class Sum { public int getSum() { return 0; } public int getSum(int x) { } return x;

public int getSum(int x, int y) { return x + y; } }

public class Overloading { public static void main(String[] args) { Sum s = new Sum(); System.out.println(s.getSum()); System.out.println(s.getSum(5)); System.out.println(s.getSum(5, 10));

} }

We have three methods called setSum(). They differ in input parameters.


public int getSum(int x) { return x; }

127

This one takes one parameter.


System.out.println(s.getSum()); System.out.println(s.getSum(5)); System.out.println(s.getSum(5, 10));

We call all three methods. All methods have the same name. The compiler knows which method to call on the grounds of the method input.
$ java com.zetcode.Overloading 0 5 15

And this is what we get when we run the example.

Recursion
Recursion, in mathematics and computer science, is a way of defining methods in which the method being defined is applied within its own definition. In other words, a recursive method calls itself to do its job. Recursion is a widely used approach to solve many programming tasks. Every problem solved using a recursion can be worked out with the iteration too. A typical example is the calculation of a factorial.
package com.zetcode; public class Recursion { static int factorial(int n) { if (n == 0) { return 1; } else { } } public static void main(String[] args) { System.out.println(factorial(6)); System.out.println(factorial(15)); } } return n * factorial(n - 1);

In this code example, we calculate the factorial of two numbers.


return n * factorial(n - 1);

Inside the body of the factorial method, we call the factorial method with a modified argument. The function calls itself. This is the essence of the recursion algorithm.
$ java com.zetcode.Recursion

128

720 2004310016

These are the results.

Method scope
A variable declared inside a method has a method scope. The scope of a name is the region of program text within which it is possible to refer to the entity declared by the name without the qualification of the name. A variable which is declared inside a method has a method scope. It is also called a local scope. The variable is valid only in this particular method.
package com.zetcode; class Test { int x = 1; public void exec1() { System.out.println(this.x); System.out.println(x); } public void exec2() { int z = 5; System.out.println(x); System.out.println(z); } }

public class MethodScope { public static void main(String[] args) { Test ts = new Test(); ts.exec1(); ts.exec2(); } }

In the preceding example, we have the x variable defined outside the instance methods. The variable has a class scope. It is valid everywhere inside the definition of the Test class, e.g. between its curly brackets.
public void exec1() { System.out.println(this.x); System.out.println(x); }

The x variable, also called the x field, is an instance variable. And so it is accessible through the this keyword. It is also valid inside the exec1() method and can be referred by its bare name. Both statements refer to the same variable.

129

public void exec2() { int z = 5; System.out.println(x); System.out.println(z); }

The x variable can be accessed also in the exec2() method. The z variable is defined in the exec2() method. It has a method scope. It is valid only in this method.
$ java com.zetcode.MethodScope 1 1 1 5

This is the output of the com.zetcode.MethodScope program. A variable defined inside a method has a local/method scope. If a local variable has the same name as an instance variable, it shadows the instance variable. The instance variable is still accessible inside the method by using the this keyword.
package com.zetcode; class Test { int x = 1; public void exec() { int x = 3; System.out.println(this.x); System.out.println(x); } }

public class Shadowing { public static void main(String[] args) { Test t = new Test(); t.exec();

} }

We declare an instance variable x. We declare another x variable inside the exec() method. Both variables have the same name, but they are not in conflict because they reside in different scopes.
System.out.println(this.x); System.out.println(x);

130

The variables are accessed differently. The x variable defined inside the method, also called the local variable, is simply accessed by its name. The instance variable can be referred by using the this keyword.
$ java com.zetcode.Shadowing 1 3

This is the output of the com.zetcode.Shadowing example.

Static methods
Static methods are called without an instance of the object. To call a static method, we use the name of the class and the dot operator. Static methods can only work with static variables. Static methods are often used to represent data or calculations that do not change in response to object state. An example is a math library which contains static methods for various calculations. We use the static keyword to declare a static method or a static variable. When no static modifier is present, the method is said to be an instance method. We cannot use the this keyword in static methods. It can be used in instance methods only.
package com.zetcode; class Basic { static int id = 2321; public static void showInfo() { System.out.println("This is Basic class"); System.out.format("The Id is: %d%n", id);

} }

public class StaticMethod { public static void main(String[] args) { } } Basic.showInfo();

In our code example, we define a static ShowInfo() method.


static int id = 2321;

A static method can only work with static variables. Static variables are not available to instance methods.
public static void showInfo() { System.out.println("This is Basic class"); System.out.format("The Id is: %d%n", id); }

This is our static ShowInfo() method. It works with a static Id member. 131

Basic.showInfo();

To invoke a static method, we do not need an object instance. We call the method by using the name of the class and the dot operator.
$ java com.zetcode.StaticMethod This is Basic class The Id is: 2321

This is the output of the example.

Hiding methods
In case of static methods, a method in the derived class with the same signature as in the base class hides the one in the base class. The method to be called is determined at compile time. This process is referred to as early or static binding.
package com.zetcode; class Base { public static void showInfo() { } } class Derived extends Base { public static void showInfo() { } } public class Hiding { public static void main(String[] args) { Base.showInfo(); Derived.showInfo(); } } System.out.println("This is Derived class"); System.out.println("This is Base class");

We have two classes. The Derived and the Base class. The Derived class inherits from the Base class. Both have a method called showInfo().
class Derived extends Base { public static void showInfo() { } } System.out.println("This is Derived class");

132

The static class method showInfo() of the Derived class hides the showInfo() method of the Base class.
Base.showInfo(); Derived.showInfo();

We call the showInfo() method for both classes. Each class calls its own method.
$ java com.zetcode.Hiding This is Base class This is Derived class

This is the output of the com.zetcode.Hiding example.

Overriding methods
Overriding happens when we create an instance method of a derived class with the same signature and return type as an instance method in the base class. The method to be executed is determined at runtime. The determining of the method to be executed at runtime is called late or dynamic binding. We might want to use the @Override annotation that instructs the compiler that we intend to override a method in the superclass. It helps prevent some programming errors.
package com.zetcode; class Base { public void showInfo() { System.out.println("This is Base class"); } }

class Derived extends Base { @Override public void showInfo() { } } public class Overriding { public static void main(String[] args) { Base[] objs = { new Base(), new Derived(), new Base(), new Base(), new Base(), new Derived() }; for (Base obj : objs) { obj.showInfo(); } } } System.out.println("This is Derived class");

133

We create an array of the Base and Derived objects. We go through the array and invoke the showInfo() method upon all of them.
@Override public void showInfo() { System.out.println("This is Derived class"); }

Here we are overriding the showInfo() method of the Base class.


Base[] objs = { new Base(), new Derived(), new Base(), new Base(), new Base(), new Derived() };

Here we create an array of Base and Derived objects. Note that we used the Base type in our array declaration. The Derived class can be converted to the Base class, because it inherits from it. The opposite is not true. The only way to have defferent objects in one array is to use a type which is shared by all objects.
for (Base obj : objs) { obj.showInfo(); }

We traverse the array and call showInfo() on all objects in the array. The method to be called is determined at runtime.
$ java com.zetcode.Overriding This is Base class This is Derived class This is Base class This is Base class This is Base class This is Derived class

This is the output.

Final methods
A final method cannot be overriden or hidden by derived classes. This is used to prevent unexpected behavior from a subclass altering a method that may be crucial to the function or consistency of the class.
package com.zetcode; class Base { public void f1() { } System.out.println("f1 of the Base");

public final void f2() { System.out.println("f2 of the Base");

134

class Derived extends Base { @Override public void f1() { } // // // // // } System.out.println("f1 of the Derived"); @Override public void f2() { } System.out.println("f2 of the Derived");

public class FinalMethods { public static void main(String[] args) { Base b = new Base(); b.f1(); b.f2(); Derived d = new Derived(); d.f1(); d.f2(); } }

In this example we have a final method f2() in the Base class. This method cannot be overriden.
public final void f2() { } System.out.println("f2 of the Base");

The f2() method is declared to be final. No overloading is possible.


@Override public void f1() { System.out.println("f1 of the Derived"); }

In the Derived class, we can override the f1() method of the Base class. We also use the @Override annotation to inform the compiler that we are overriding a method.
// // // // // @Override public void f2() { } System.out.println("f2 of the Derived");

135

These lines are commented, because otherwise the code example would not compile. The compiler would give the following error: Exception in thread "main" java.lang.VerifyError: class com.zetcode.Derived overrides final method f2.
d.f2();

Since it is not possible to override a final method, the above line will call the f2() method of the Base class.
$ java com.zetcode.FinalMethods f1 of the Base f2 of the Base f1 of the Derived f2 of the Base

This is the output. In this part of the Java tutorial, we have covered methods.

Object-oriented programming II
In this chapter of the Java tutorial, we continue the description of the OOP.

Abstract classes and methods


Designing an application, we often find that our classes have a lot of common functionality. These commononalities can be extracted and put into parent classes. This way we reduce the size of our code and make our application more compact. We might find that the parent class is intangible, unreal entity an idea. On a desk we have a pen, a book, a pencil or a cup of tea. An Item might be considered as a parent class for all these things. The class will contain some common qualities of these items. For example an id, a weight, or a color. We can implement a getId() method , but we cannot implenment a getWeight() or getColor() methods in this class. An item has no weight or color. These methods can be implemented only in the subclasses of the Item class. For these situations, we have abstract methods and classes. An Item class is a candidate for an abstract class a class that cannot be created and some or all of its methods cannot be implemented. An abstract class or method is created using the abstract keyword. Abstract classes cannot be instantiated but they can be subclassed. If a class contains at least one abstract method, it must be declared abstract too. Abstract methods cannot be implemented; they merely declare the methods' signatures. When we inherit from an abstract class, all abstract methods must be implemented by the derived class or the class must itself be abstract. A single abstract class is subclassed by similar classes that have a lot in common (the implemented parts of the abstract class), but also have some differences (the abstract methods). Abstract classes may have methods with full implementation and may also have defined member fields. So abstract classes may provide a partial implementation. Programmers often put some common functionality into abstract classes. And these abstract classes are later 136

subclassed to provide more specific implementation. The common features are implemented in the abstract class, the differences are hinted by abstract methods. For example, the Qt graphics library has a QAbstractButton which is the abstract base class of button widgets, providing functionality common to buttons. Buttons Q3Button, QCheckBox, QPushButton, QRadioButton, and QToolButton inherit from this base abstract class and provide their specific functionality. The static, private, and final methods cannot be abstract, because these types of methods cannot be overridden by a subclass. Likewise, a final class cannot have any abstract methods. Formally put, abstract classes are used to enforce a protocol. A protocol is a set of operations which all implementing objects must support.
package com.zetcode; abstract class Drawing { protected int x = 0; protected int y = 0; public abstract double area(); public String getCoordinates() { return String.format("x: %d, y: %d", this.x, this.y); } }

class Circle extends Drawing { private int r; public Circle(int x, int y, int r) { this.x = x; this.y = y; this.r = r; } @Override public double area() { return this.r * this.r * Math.PI; } @Override public String toString() { return String.format("Circle at x: %d, y: %d, radius: %d", this.x, this.y, this.r);

} }

public class AbstractClass { public static void main(String[] args) {

137

Circle c = new Circle(12, 45, 22); System.out.println(c); System.out.format("Area of circle: %f%n", c.area()); System.out.println(c.getCoordinates());

} }

We have an abstract base Drawing class. The class defines two member fields, defines one method and declares one method. One of the methods is abstract, the other one is fully implemented. The Drawing class is abstract, because we cannot draw it. We can draw a circle, a dot or a square. The Drawing class has some common functionality to the objects that we can draw.
abstract class Drawing {

We use the abstract keyword to define an abstract class.


public abstract double area();

An abstract method is also preceded with a abstract keyword. A Drawing class is an idea. It is unreal and we cannot implement the area() method for it. This is the kind of situation where we use abstract methods. The method will be implemented in a more concrete entity like a circle.
class Circle extends Drawing {

A Circle is a subclass of the Drawing class. Therefore it must implement the abstract area() method.
@Override public double area() { return this.r * this.r * Math.PI; }

Here we are implementing the area() method.


$ java com.zetcode.AbstractClass Circle at x: 12, y: 45, radius: 22 Area of circle: 1520.530844 x: 12, y: 45

We create a Circle object and print its area and coordinates.

Interfaces
A remote control is an interface between the viewer and the TV. It is an interface to this electronic device. Diplomatic protocol guides all activities in the diplomatic field. Rules of the road are rules that motorists, cyclists and pedestrians must follow. Interfaces in programming are analogous to the previous examples. Interfaces are: 138

APIs Contracts

Objects interact with the outside world with the methods they expose. The actual implementation is not important to the programmer, or it also might be secret. A company might sell a library and it does not want to disclose the actual implementation. A programmer might call a maximize() method on a window of a GUI toolkit, but knows nothing about how this method is implemented. From this point of view, interfaces are ways through which objects interact with the outside world, without exposing too much about their inner workings. From the second point of view, interfaces are contracts. If agreed upon, they must be followed. They are used to design an architecture of an application. They help organize the code. Interfaces are fully abstract types. They are declared using the interface keyword. In Java, an interface is a reference type, similar to a class that can contain only constants, method signatures, and nested types. There are no method bodies. Interfaces cannot be instantiated they can only be implemented by classes or extended by other interfaces. All interface members implicitly have public access. Interfaces cannot have fully implemented methods. A Java class may implement any number of interfaces. An interface can also extend any number of interfaces. A class that implements an interface must implement all method signatures of an interface. Interfaces are used to simulate multiple inheritance. A Java class can inherit only from one class. A Java class can implement multiple interfaces. Multiple inheritance using the interfaces is not about inheriting methods and variables. It is about inheriting ideas or contracts which are described by the interfaces. The body of the interface contains abstract methods, but since all methods in an interface are, by definition, abstract, the abstract keyword is not required. Since the interface specifies a set of exposed behaviors, all methods are implicitly public. An interface can contain constant member declarations in addition to method declarations. All constant values defined in an interface are implicitly public, static, and final. These modifiers can be omitted. There is one important distinction between interfaces and abstract classes. Abstract classes provide partial implementation for classes that are related in the inheritance hierarchy. Interfaces on the other hand can be implemented by classes that are not related to each other. For example, we have two buttons. A classic button and a round button. Both inherit from an abstract button class that provides some common functionality to all buttons. Implementing classes are related, since all are buttons. Whereas classes Database and SignIn are not related to each other. We can apply an ILoggable interface that would force them to create a method to do logging.
package com.zetcode; interface IInfo { } void doInform();

139

class Some implements IInfo { @Override public void doInform() { System.out.println("This is Some Class"); } }

public class Interface1 { public static void main(String[] args) { Some sm = new Some(); sm.doInform();

} }

This is a simple Java program demonstrating an interface.


interface IInfo { void doInform(); }

This is an interface IInfo. It has the doInform() method signature.


class Some implements IInfo {

We implement the IInfo interface. To implement a specific interface, we use the implements keyword.
@Override public void doInform() { System.out.println("This is Some Class"); }

The class provides an implementation for the doInform() method. The @Override annotation tells the compiler that we are overriding a method. Java does not allow to inherit from more than one class directly. It allows to implement multiple interfaces. The next example shows how a class can implement multiple interfaces.
package com.zetcode; interface Device { void switchOn(); void switchOff(); } interface Volume { void volumeUp();

140

void volumeDown();

interface Pluggable { void plugIn(); void plugOff(); } class CellPhone implements Device, Volume, Pluggable { @Override public void switchOn() { System.out.println("Switching on"); } @Override public void switchOff() { System.out.println("Switching on"); } @Override public void volumeUp() { System.out.println("Volume up"); } @Override public void volumeDown() { System.out.println("Volume down"); } @Override public void plugIn() { System.out.println("Plugging in"); } @Override public void plugOff() { System.out.println("Plugging off"); } }

public class MultipleInterfaces { public static void main(String[] args) { CellPhone cp = new CellPhone(); cp.switchOn(); cp.volumeUp(); cp.plugIn();

} }

We have a CellPhone class that inherits from three interfaces. 141

class CellPhone implements Device, Volume, Pluggable {

The class implements all three interfaces which are divided by a comma. The CellPhone class must implement all method signatures from all three interfaces.
$ java com.zetcode.MultipleInterfaces Switching on Volume up Plugging in

Running the program we get this output. The next example shows how interfaces can form a hierarchy. Interfaces can inherit from other interfaces using the extends keyword.
package com.zetcode; interface IInfo { void doInform(); } interface IVersion { } void getVersion();

interface ILog extends IInfo, IVersion { void doLog(); } class DBConnect implements ILog { @Override public void doInform() { System.out.println("This is DBConnect class"); } @Override public void getVersion() { System.out.println("Version 1.02"); } @Override public void doLog() { System.out.println("Logging"); } public void connect() { } } public class InterfaceHierarchy { System.out.println("Connecting to the database");

142

public static void main(String[] args) { DBConnect db = new DBConnect(); db.doInform(); db.getVersion(); db.doLog(); db.connect(); } }

We define three interfaces. The interfaces are organized in a hierarchy.


interface ILog extends IInfo, IVersion {

The ILog interface inherits from two interfaces.


class DBConnect implements ILog {

The DBConnect class implements the ILog interface. Therefore it must implement the methods of all three interfaces.
@Override public void doInform() { System.out.println("This is DBConnect class"); }

The DBConnect class implements the doInform() method. This method was inherited by the ILog interface which the class implements.
$ java com.zetcode.InterfaceHierarchy This is DBConnect class Version 1.02 Logging Connecting to the database

This is the example output.

Polymorphism
The polymorphism is the process of using an operator or function in different ways for different data input. In practical terms, polymorphism means that if class B inherits from class A, it doesn't have to inherit everything about class A; it can do some of the things that class A does differently. (Wikipedia) In general, polymorphism is the ability to appear in different forms. Technically, it is the ability to redefine methods for derived classes. Polymorphism is concerned with the application of specific implementations to an interface or a more generic base class. Polymorphism is the ability to redefine methods for derived classes.
package com.zetcode;

143

abstract class Shape { protected int x; protected int y; public abstract int area(); } class Rectangle extends Shape { public Rectangle(int x, int y) { this.x = x; this.y = y; } @Override public int area() { return this.x * this.y; } }

class Square extends Shape { public Square(int x) { this.x = x; } @Override public int area() { return this.x * this.x; } }

public class Polymorphism { public static void main(String[] args) { Shape[] shapes = {new Square(5), new Rectangle(9, 4), new Square(12)}; for (Shape shape : shapes) { } } } System.out.println(shape.area());

In the above program, we have an abstract Shape class. This class morphs into two descendant classes, Rectangle and Square. Both provide their own implementation of the area() method. Polymorphism brings flexibility and scalability to the OOP systems.
@Override public int area() { return this.x * this.y;

144

} ... @Override public int area() { return this.x * this.x; }

The Rectangle and the Square classes have their own implementations of the area() method.
Shape[] shapes = {new Square(5), new Rectangle(9, 4), new Square(12)};

We create an array of three Shapes.


for (Shape shape : shapes) { System.out.println(shape.area()); }

We go through each shape and call the area() method on it. The compiler calls the correct method for each shape. This is the essence of polymorphism.

Nested classes
It is possible to define a class within another class. Such class is called a nested class in Java terminology. A class that is not a nested class is called a top-level class. Java has four types of nested classes:

Static nested classes Inner classes Local classes Anonymous classes

Using nested classes may increase the readability of the code and improve the organization of the code. Inner classes are often used as callbacks in GUI. For example in Java Swing toolkit.

Static nested classes


A static nested class is a nested class that can be created without the instance of the enclosing class. It has access to the static variables and methods of the enclosing class.
package com.zetcode; public class SNCTest { private static int x = 5; static class Nested { @Override public String toString() {

145

} }

return "This is a static nested class; x:" + x;

public static void main(String[] args) { SNCTest.Nested sn = new SNCTest.Nested(); System.out.println(sn); } }

The example presents a static nested class.


private static int x = 5;

This is a private static variable of the SNCTest class. It can be accessed by a static nested class.
static class Nested { @Override public String toString() { return "This is a static nested class; x:" + x; }

A static nested class is defined. It has one method which prints a message and refers to the static x variable.
$ java com.zetcode.SNCTest This is a static nested class; x:5

This is the output of the com.zetcode.SNCTest program.

Inner classes
An instance of a normal or top-level class can exist on its own. By contrast, an instance of an inner class cannot be instantiated without being bound to a top-level class. Inner classes are also called member classes. They belong to the instance of the enclosing class. Inner classes have acces to the members of the enclosing class.
package com.zetcode; public class InnerClassTest { private int x = 5; class Inner { @Override public String toString() { return "This is Inner class; x:" + x; }

146

public static void main(String[] args) { InnerClassTest nc = new InnerClassTest(); InnerClassTest.Inner inner = nc.new Inner(); System.out.println(inner); } }

A nested class is defined in the InnerClassTest class. It has access to the member x variable.
class Inner { @Override public String toString() { return "This is Inner class; x:" + x; } }

An Inner class is defined in the body of the InnerClassTest class.


InnerClassTest nc = new InnerClassTest();

First, we need to create an instance of the top-level class. Inner classes cannot exist without an instance of the enclosing class.
InnerClassTest.Inner inner = nc.new Inner();

Once we have the top-level class instantiated, we can create the instance of the inner class.
$ java com.zetcode.InnerClassTest This is Inner class; x:5

This is the uput of the com.zetcode.InnerClassTest program.

Shadowing
If a variable in the inner scope has the same name as the variable in the outer scope then it is shadowing it. It is still possible to refer to the variable in the outer scope.
package com.zetcode; public class Shadowing { private int x = 0; class Inner { private int x = 5; void method1(int x) { System.out.println(x); System.out.println(this.x); System.out.println(Shadowing.this.x);

147

public static void main(String[] args) { Shadowing sh = new Shadowing(); Shadowing.Inner si = sh.new Inner(); } } si.method1(10);

We define an x variable in the top-level class, in the inner class and inside a method.
System.out.println(x);

This line refers to the x variable defined in the local scope of the method.
System.out.println(this.x);

Using the this keyword, we refer to the x variable defined in the Inner class.
System.out.println(Shadowing.this.x);

Here we refer to the x variable of the Shadowing top-level class.


$ java com.zetcode.Shadowing 10 5 0

Example output.

Local classes
A local class is a special case of an inner class. Local classes are classes that are defined in a block. (A block is a group of zero or more statements between braces.) A local class has access to the members of its enclosing class. In addition, a local class has access to local variables if they are declared final. The reason for this is technical. The lifetime of an instance of a local class can be much longer than the execution of the method in which the class is defined. To solve this, the local variables are copied into the local class. To ensure that they are later not changed, they have to be declared final. Local classes cannot be public, private, protected or static. They are not allowed for local variable declarations or local class declarations. Except for constants that are declared static and final, local classes cannot contain static fields, methods, or classes.
package com.zetcode; public class LocalClassTest { public static void main(String[] args) { final int x = 5;

148

class Local { @Override public String toString() { return "This is Local class; x:" + x; }

} }

Local loc = new Local(); System.out.println(loc);

A local class is defined in the body of the main() method.


@Override public String toString() { return "This is Local class; x:" + x; }

A local class can access local variables if they are declared final.

Anonymous classes
Anonymous classes are local classes that do not have a name. They enable us to declare and instantiate a class at the same time. We can use anonymous classes if we want to use the class only once. An anonymous class is defined and instantiated in a single expression. Anonymous inner classes are also used where the event handling code is only used by one component and therefore does not need a named reference. An anonymous class must implement an interface or inherit from a class. But the implements and extends keywords are not used. If the name following the new keyword is the name of a class, the anonymous class is a subclass of the named class. If the name following new specifies an interface, the anonymous class implements that interface and extends the Object. Since an anonymous class has no name, it is not possible to define a constructor for an anonymous class. Inside the body of an anonymous class, we cannot define any statements; only methods or members.
package com.zetcode; public class AnonymousClass { interface Message { public void send(); } public void createMessage() { Message msg = new Message() { @Override public void send() {

149

} }; }

System.out.println("This is a message");

msg.send();

public static void main(String[] args) { AnonymousClass ac = new AnonymousClass(); ac.createMessage();

} }

In this code example, we create an anonymous class.


interface Message { public void send(); }

An anonymous class must be either a subclass or must implement an interface. Our anonymous class will implement a Message interface. Otherwise, the type would not be recognized by the compiler.
public void createMessage() { Message msg = new Message() { @Override public void send() { System.out.println("This is a message"); } }; } msg.send();

An anonymous class is a local class, hence it is defined in the body of a method. An anonymous class is defined in an expression, therefore the enclosing right bracket is followed by a semicolon. In this part of the Java tutorial, we continued covering object-oriented programming in Java.

Packages
In this part of the Java tutorial, we will talk about Java packages. A package is a grouping of related types providing access protection and name space management. Packages in Java is a similar concept to namespaces in C#. A package is declared with the package keyword. This statement must be placed at the top of every source file. There can be only one such statement in each source file. The Java source files must be placed in directories that match the package name.

150

package com.zetcode;

All types defined in the file with the above package are part of the com.zetcode package. A class Being has a fully qualified name com.zetcode.Being. There are millions of Java programmers worldwide. To avoid potential name conflicts, there is a naming convention in Java. The package names reversed Internet domain names. The letters are written in lowercase. There can be only one zetcode.com domain name so using a reversed name com.zetcode for the packages will make them unique. A Java source file with a com.zetcode package must be located in the com/zetcode/ subdirectory. The import keyword is used at the beginning of a source file to specify types (classes, interfaces, enumerations, annotations) or entire Java packages to be referred to later without including their package names in the reference. Since Java SE 5.0, import statements can import static members of a class (methods and variables).
import java.awt.*;

Using the * wildcard character, we can import the whole package at a time. After this import, we can refer to all types of the java.awt package without their fully qualified names.
import java.awt.event.*;

The java.awt.event subpackage is not imported with the java.awt.* import. Subpackages must be imported independently.
package com.zetcode; import java.util.Random; public class Packages { public static void main(String[] args) { Random r = new Random(); int x = r.nextInt(); System.out.println(x); java.util.Calendar c = java.util.Calendar.getInstance(); System.out.println(c.getTime()); } }

The example uses two types. The Random class and the Calendar class. The first calss is imported and the second is reffered by its fully qualified name.
package com.zetcode;

We declare a package with the package keyword. The Packages.java file must be located in the com/zetcode/ subdirectory.
import java.util.Random;

This code line enables us to use the Random class without the package name. 151

Random r = new Random();

Here we use the Random without using it's full name.


java.util.Calendar c = java.util.Calendar.getInstance();

If we did not use the import keyword on a type, we can refer to it only by its full name java.util.Calendar in our case. The import keyword saves a lot of typing.
$ ls com/zetcode/ Packages.java

The Packages.java source file is placed in the com/zetcode/ subdirectory. The package name must reflect the directory structure.
$ javac com/zetcode/Packages.java

We compile the source file with the javac tool. The tool is called from the parent directory of the com/zetcode/ directory.
$ java com.zetcode.Packages 928256084 Sun Jul 21 15:01:14 CEST 2013

This is the output of the com.zetcode.Packages program.

Package-private visibility
If we do not specify any access modifier (e.g. private, protected or public), we have a package-private visibility. In such a case, variables and methods are accessible within the same package. Classes in other packages cannot access classes and members declared with package-private access.

Default package
If no package is declared, all types defined in that file are part of a default unnamed package. It is recommended to always place your types in a package. Even for small programs.
public class DefaultPackage { public static void main(String[] args) { } } System.out.println("A class in a default package");

The DefaultPackage class is part of the default package.


$ ls DefaultPackage.java

If we do not specify a package, we do not place the source file in a specific subdirectory.

152

$ javac DefaultPackage.java $ java DefaultPackage A class in a default package

We compile the code and run the application. The source file and the bytecode is located in the current working directory.

Automatic imports
Java compiler automatically imports two packages. The java.lang package and the current package. Constants.java
package com.zetcode; public class Constants { public static final String version = "1.0"; }

The Constants class is located in the same package as the AutomaticImports which is referring to its version member. AutomaticImports.java
package com.zetcode; public class AutomaticImports { public static void main(String[] args) { String os = System.getProperty("os.name"); System.out.println(os); System.out.println(Constants.version);

} }

In this example, we refer to some classes that are automatically imported by Java compiler.
String os = System.getProperty("os.name");

The String and System classes are part of the java.lang package.
System.out.println(Constants.version);

The Constants class is located in the same package as the AutomaticImports class. Therefore, we can access the class and its member without using the fully qualified name or utilizing the import keyword.
$ ls com/zetcode/ AutomaticImports.java Constants.java

Both AutomaticImports.java and Constants.java files are located in the same subdirectory.

153

$ javac com/zetcode/AutomaticImports.java com/zetcode/Constants.java

Both files are compiled.


$ java com.zetcode.AutomaticImports Linux 1.0

This is a sample output of the com.zetcode.AutomaticImports program.

Static imports
If we often use some static members, we can use import static statement to refer to them later without a full class name. Static imports should be used with caution.
package com.zetcode; import static java.lang.Math.E; import static java.lang.Math.PI; import static java.lang.Math.abs; public class StaticImport { public static void main(String[] args) { System.out.println(E); System.out.println(PI); System.out.println(abs(-5)); } }

In this example, we refer to two constants and one static method.


import static java.lang.Math.E; import static java.lang.Math.PI; import static java.lang.Math.abs;

We use the import static statement to enable referring to them without their full names.
System.out.println(E); System.out.println(PI); System.out.println(abs(-5));

We refer to these three members without their class name.


$ java com.zetcode.StaticImport 2.718281828459045 3.141592653589793 5

This is the output of the com.zetcode.StaticImport program. This chapter covered packages in Java.

154

Exceptions
In this chapter of the Java tutorial, we work with exceptions. Java uses exceptions to handle errors. During the execution of our application many things might go wrong. A disk might get full and we cannot save our data. An Internet connection might go down while our application tries to connect to a site. A user fills invalid data to a form. These errors can crash the application, make it unresponsive, and in some cases even compromise the security of a system. It is a responsibility of a programmer to handle errors that can be anticipated. In Java we recognize three kinds of exceptions: checked exceptions, unchecked exceptions and errors. Checked exceptions are error conditions that can be anticipated and recovered from (invalid user input, database problems, network outages, absent files). All subclasses of Exception except for RuntimeException and its subclasses are checked exceptions. IOException, SQLException, or PrinterException are examples of checked exceptions. Checked exceptions are forced by Java compiler to be either catched or declared in the method signature (using the throws keyword). Unchecked exceptions are error conditions that cannot be anticipated and recovered from. They are usually programming errors and cannot be handled at runtime. Unchecked Exceptions are subclasses of java.lang.RuntimeException. ArithmeticException, NullPointerException, or BufferOverflowException belong to this group of exceptions. Unchecked exceptions are not enforced by the Java compiler. Errors are serious problems that programmers cannot solve. For example hardware or system malfunctions cannot be handled by applications. Errors are instances of the java.lang.Error class. Examples of errors include InternalError, OutOfMemoryError, StackOverflowError or AssertionError. Errors and runtime exceptions are often referred to as unchecked exceptions. The try, catch and finally keywords are used to handle exceptions. The throws keyword is used in method declarations to specify which exceptions are not handled within the method but rather passed to the next higher level of the program. The throw keyword causes the declared exception instance to be thrown. After the exception is thrown, the runtime system attempts to find an appropriate exception handler. The call stack is a hierarchy of methods that are searched for the handler.
package com.zetcode; public class UncheckedException { public static void main(String[] args) { int[] n = { 5, 2, 4, 5, 6, 7, 2 }; System.out.format("The last element in the array is %d%n", n[n.length]);

155

In the above program, there is a bug. We try to access an element that does not exist. This is a programming error. There is no reason to handle this error; the code must be fixed.
System.out.format("The last element in the array is %d%n", n[n.length]);

The array indexes start from zero. Therefore, the last index is n.length - 1.
$ java com.zetcode.UncheckedException Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 7 at com.zetcode.UncheckedException.main(UncheckedException.java:9)

A java.lang.ArrayIndexOutOfBoundsException is thrown by the runtime system. This is an example of an unchecked exception. The Scanner class throws an InputMismatchException to indicate that the token retrieved does not match the pattern for the expected type. This exception is an example of an unchecked exception. We are not forced to handle this exception by the compiler.
package com.zetcode; import import import import java.util.InputMismatchException; java.util.Scanner; java.util.logging.Level; java.util.logging.Logger;

public class UncheckedException2 { public static void main(String[] args) { System.out.println("Enter an integer: "); try { Scanner sc = new Scanner(System.in); int x = sc.nextInt(); System.out.println(x); } catch (InputMismatchException e) { Logger.getLogger(UncheckedException2.class.getName()).log(Level.SEVERE, null, e); } } }

The error prone code is placed in the try block. If an exception is thrown, the code jumps to the catch block. The exception class that is thrown must match the exception following the catch keyword.
try {

156

Scanner sc = new Scanner(System.in); int x = sc.nextInt(); System.out.println(x); }

The try keyword defines a block of statements which can throw an exception.
} catch (InputMismatchException e) { Logger.getLogger(UncheckedException2.class.getName()).log(Level.SEVERE, null, e);

The exception is handled in the catch block. We use the Logger class to log the error. The following code example connects to a MySQL database and finds out the version of the database system. Connecting to databases is error prone.
package zetcode; import import import import import import import java.sql.Connection; java.sql.DriverManager; java.sql.ResultSet; java.sql.SQLException; java.sql.Statement; java.util.logging.Level; java.util.logging.Logger;

public class Version { public static void main(String[] args) { Connection con = null; Statement st = null; ResultSet rs = null; String url = "jdbc:mysql://localhost:3306/testdb"; String user = "testuser"; String password = "test623"; try { con = DriverManager.getConnection(url, user, password); st = con.createStatement(); rs = st.executeQuery("SELECT VERSION()"); if (rs.next()) { } System.out.println(rs.getString(1));

} catch (SQLException ex) { Logger lgr = Logger.getLogger(Version.class.getName()); lgr.log(Level.SEVERE, ex.getMessage(), ex); } finally {

157

try { if (rs != null) { rs.close(); } if (st != null) { st.close(); } if (con != null) { con.close(); } } catch (SQLException ex) { Logger lgr = Logger.getLogger(Version.class.getName()); lgr.log(Level.WARNING, ex.getMessage(), ex); } } } }

An SQLException is an example of a checked exceptions. We are forced to handle it.


try { con = DriverManager.getConnection(url, user, password); st = con.createStatement(); rs = st.executeQuery("SELECT VERSION()"); if (rs.next()) { } } System.out.println(rs.getString(1));

The code that might lead to an error is placed in the try block.
} catch (SQLException ex) { Logger lgr = Logger.getLogger(Version.class.getName()); lgr.log(Level.SEVERE, ex.getMessage(), ex); }

When an exception occurs, we jump to the catch block. We handle the exception by logging what happened.
} finally { try { if (rs != null) { rs.close(); } if (st != null) { st.close();

158

} if (con != null) { con.close(); } } catch (SQLException ex) { Logger lgr = Logger.getLogger(Version.class.getName()); lgr.log(Level.WARNING, ex.getMessage(), ex); } }

The finally block is executed whether we received and exception or not. We are trying to close the resources. Even in this process, there might be an exception. Therefore, we have another catch block.

Throwing exceptions
The Throwable class is the superclass of all errors and exceptions in the Java language. Only objects that are instances of this class (or one of its subclasses) are thrown by the Java Virtual Machine or can be thrown by the Java throw statement. Similarly, only this class or one of its subclasses can be the argument type in a catch clause. Programmers can throw exceptions using the throw keyword. Exceptions are often handled in a different place from where they are thrown. Methods can throw off their responsibility to handle exception by using the throws keyword at the end of the method definition. The keyword is followed by comma-separated list of all exceptions thrown by that method. Thrown exceptions travel through a call stack and look for the closest match.
package com.zetcode; import import import import java.util.InputMismatchException; java.util.Scanner; java.util.logging.Level; java.util.logging.Logger;

public class ThrowingExceptions { public static void main(String[] args) { System.out.println("Enter your age: "); try { Scanner sc = new Scanner(System.in); short age = sc.nextShort(); if (age <= 0 || age > 130) { } throw new IllegalArgumentException("Incorrect age");

System.out.format("Your age is: %d %n", age); } catch (IllegalArgumentException | InputMismatchException e) {

159

Logger.getLogger(ThrowingExceptions.class.getName()).log(Level.SEVERE, null, e); } } }

In the example, we ask the user to enter his age. We read the value and throw an exception if the value is outside the range of the expected human age.
if (age <= 0 || age > 130) { } throw new IllegalArgumentException("Incorrect age");

An age cannot be a negative value and there is no record of a person older than 130 years. If the value is outside of this range we throw a built-in IllegalArgumentException. This exception is thrown to indicate that a method has been passed an illegal or inappropriate argument.
} catch (IllegalArgumentException | InputMismatchException e) { Logger.getLogger(ThrowingExceptions.class.getName()).log(Level.SEVERE, null, e); }

Since Java 7, it is possible to catch multiple exceptions in one catch clause. However, these exceptions cannot be subclasses of each other. For example, IOException and FileNotFoundException cannot be used in one catch statement. The following example will show how to pass the responsibility for handling exceptions to other methods.
package com.zetcode; import import import import import import java.io.BufferedReader; java.io.FileNotFoundException; java.io.FileReader; java.io.IOException; java.util.logging.Level; java.util.logging.Logger;

public class ThrowingExceptions { public static void readFileContents(String fname) throws FileNotFoundException, IOException { BufferedReader br = new BufferedReader(new FileReader(fname)); String line; while((line = br.readLine()) != null) { System.out.println(line); } br.close();

160

} public static void main(String[] args) { try { readFileContents("quotes.txt"); } catch (FileNotFoundException ex) {

Logger.getLogger(ThrowingExceptions.class.getName()).log(Level.SEVERE, null, ex); } catch (IOException ex) { Logger.getLogger(ThrowingExceptions.class.getName()).log(Level.SEVERE, null, ex); } } }

This example will read the contents of a text file. The responsibility to handle exceptions is delegated from the readFileContents() method to the main() method.
public static void readFileContents(String fname) throws FileNotFoundException, IOException {

When we read from a file, we deal with two checked exceptions. The readFileContents() method throws both exceptions. The task to handle these exceptions is delegated to the caller.
try { readFileContents("quotes.txt"); } catch (FileNotFoundException ex) {

Logger.getLogger(ThrowingExceptions.class.getName()).log(Level.SEVERE, null, ex); } catch (IOException ex) { Logger.getLogger(ThrowingExceptions.class.getName()).log(Level.SEVERE, null, ex); }

The main() method calls the readFileContents() method. The readFileContents() method throws two checked exceptions, thereofore, the main() method must handle them.

Try with resources


The try-with-resources statement is a special kind of a try statement. It was introduced in Java 7. In parentheses we put one or more resources. These resources will be automatically closed at the end of the statement. We do not have to manually close the resources.
package com.zetcode;

161

import import import import import import

java.io.BufferedReader; java.io.FileNotFoundException; java.io.FileReader; java.io.IOException; java.util.logging.Level; java.util.logging.Logger;

public class TryWithResources { public static void main(String[] args) { try (BufferedReader br = new BufferedReader(new FileReader("quotes"))) { String line; while ((line = br.readLine()) != null) { } System.out.println(line);

} catch (FileNotFoundException ex) { Logger.getLogger(TryWithResources.class.getName()).log(Level.SEVERE, null, ex); } catch (IOException ex) { Logger.getLogger(TryWithResources.class.getName()).log(Level.SEVERE, null, ex); } } }

In the example, we read the contents of a file and use the try-with-resources statement.
try (BufferedReader br = new BufferedReader(new FileReader("quotes"))) { String line; while ((line = br.readLine()) != null) { } } System.out.println(line);

An opened file is a resource that must be closed. The input stream will be closed regardless of whether the try statement completes normally or abruptly.

Custom exception
Custom exceptions are user defined exception classes that extend either the Exception class or the RuntimeException class. The custom exception is cast off with the throw keyword.
package com.zetcode;

162

class BigValueException extends Exception { public BigValueException(String message) { } } public class BigValueExceptions { public static void main(String[] args) { int x = 340004; final int LIMIT = 333; try { if (x > LIMIT) { throw new BigValueException("Exceeded the maximum value"); } } catch (BigValueException e) { } } } System.out.println(e.getMessage()); super(message);

We assume that we have a situation in which we cannot deal with big numbers.
class BigValueException extends Exception { public BigValueException(String message) { } } super(message);

We have a BigValueException class. This class derives from the built-in Exception class. It passes the error message to the parent class using the super keyword.
final int LIMIT = 333;

Numbers bigger than this constant are considered to be "big" by our program.
if (x > LIMIT) { } throw new BigValueException("Exceeded the maximum value");

If the value is bigger than the limit, we throw our custom exception. We give the exception a message "Exceeded the maximum value".
} catch (BigValueException e) { } System.out.println(e.getMessage());

163

We catch the exception and print its message to the console. In this part of the Java tutorial, we have talked about exceptions in Java.

Collections
In this chapter we will deal with collections. Java provides specialized classes for data storage and retrieval. In one of the previous chapters, we have described arrays. Collections are enhancement to the arrays. Java 5 introduced generic collections. The generic collections are more flexible and they are the preferred way to work with data. Generic collections enhance code reuse, type safety, and performance. There are many classes in the collection framework. Some of them, like ArrayBlockingQueue or IdentityHashMap, are specialized containers used in specific situations. We will mention a few generic purpose containers.

ArrayList
An ArrayList is a dynamic, resizable array. It provides random access to its elements. Random access means that we can grab any element in constant time. An ArrayList automatically expands as data is added. Unlike arrays, an ArrayList can hold data of multiple data types. Elements in the ArrayList are accessed via an integer index. Indexes are zero based. Indexing of elements and insertion and deletion at the end of the ArrayList takes constant time. Inserting or deleting an element in the middle of the dynamic array is more costly. It requires shifting all the latter elements over. The process takes linear time.
package com.zetcode; import java.util.ArrayList; class Base { } public class ArrayListExample { public static void main(String[] args) { ArrayList da = new ArrayList(); da.add("Java"); da.add(3.5); da.add(55); da.add(new Base()); for (Object el : da) { System.out.println(el); } } }

164

In the above example, we have created an ArrayList collection. We have added some elements to it. They are of various data type.
import java.util.ArrayList;

From the java.util package, we import the ArrayList class.


ArrayList da = new ArrayList();

An ArrayList collection is created.


da.add("Java"); da.add(3.5); da.add(55); da.add(new Base());

We add four elements to the array with the add() method.


for (Object el : da) { } System.out.println(el);

We iterate through the array list and print its elements to the console.
$ java com.zetcode.ArrayListExample Java 3.5 55 com.zetcode.Base@1535ac

Here we can see the output of the com.zetcode.ArrayListExample. The second example will use generic collections.
package com.zetcode; import java.util.ArrayList; import java.util.Iterator; public class ArrayListExample2 { public static void main(String[] args) { ArrayList<String> names = new ArrayList<String>(); names.add("Jane"); names.add("Thomas"); names.add("Robin"); names.add("David"); names.add("Becky"); System.out.format("There are %d elements in the collection%n", names.size()); names.remove(1); System.out.format("There are %d elements in the collection%n",

165

names.size()); names.set(1, "Tom"); System.out.println(names); System.out.println(names.get(3)); System.out.println("************"); Iterator<String> it = names.iterator(); while (it.hasNext()) { System.out.println(it.next()); } } }

In the example we present some useful methods of the ArrayList container.


ArrayList<String> names = new ArrayList<String>();

A generic ArrayList is created. We restrict the data type of elements to String data type. This is done by writing the data type between the <> characters.
names.add("Jane"); names.add("Thomas"); names.add("Robin"); names.add("David"); names.add("Becky");

We add five string elements to the array list.


System.out.format("There are %d elements in the collection%n", names.size());

The size of the ArrayList is determined by the size() method.


names.remove(1);

We remove the second element from the collection. The parameter is the index to the collection.
names.set(1, "Tom");

The set() method replaces the element at the specified index with the given element. "Thomas" is replaced with "Tom".
System.out.println(names);

Putting the container as a parameter to the println() method will call the container's toString() method. It transforms the collection into a string.
System.out.println(names.get(3));

166

The get() method retrieves the fourth element of the container.


Iterator<String> it = names.iterator(); while (it.hasNext()) { } System.out.println(it.next());

We go through the container using the Iterator object. The hasNext() method checks if there are some elements left and the next() method retrieves the next element in the iteration.
$ java com.zetcode.ArrayListExample2 There are 5 elements in the collection There are 4 elements in the collection [Jane, Tom, David, Becky] Becky ************ Jane Tom David Becky

This is a sample output of the com.zetcode.ArrayListExample2 example. In the next example, we continue working with ArrayList.
package com.zetcode; import java.util.ArrayList; public class ArrayListExample3 { public static void main(String[] args) { ArrayList<String> names = new ArrayList<>(); names.add("Jane"); names.add(0, "Thomas"); names.add(1, "Robin"); names.add("David"); names.add("Becky"); System.out.println(names); System.out.println(names.isEmpty()); System.out.println(names.contains("Jane")); System.out.println(names.contains("Robert")); System.out.println(names.indexOf("Jane")); System.out.println(names.subList(1, 4)); names.clear(); System.out.println(names.isEmpty()); System.out.println(names); } }

167

We show another five methods that can be used to work with ArrayLists.
ArrayList<String> names = new ArrayList<>();

Since Java 7 it is possible to omit the explicit type arguments in constructor calls to generic classes. The compiler infers the parameter types for constructors of generic classes.
names.add("Jane"); names.add(0, "Thomas");

The add() method adds a new item to the container. The overloaded second option specifies the index where the item will be placed. In the end, the "Thomas" string is located before the "Jane" string.
System.out.println(names.isEmpty());

The empty() method checks if the container is empty. The line returns false. At this moment, we have five strings in the container.
System.out.println(names.contains("Jane"));

The contains method determines if the specified element is present in the container.
System.out.println(names.indexOf("Jane"));

The indexOf() method returns the index of the first occurrence of the specified element, or -1 if the list does not contain the element.
System.out.println(names.subList(1, 4));

The subList() method returns a slice of the list between the specified indexes. The element at the first index is included in the slice, the element at the second index is not.
names.clear();

The clear() method removes all elements from the container.


$ java com.zetcode.ArrayListExample3 [Thomas, Robin, Jane, David, Becky] false true false 2 [Robin, Jane, David] true []

This is the output of the com.zetcode.ArrayListExample3.

168

LinkedList
A LinkedList is a doubly linked list in Java. Insertions and removals of elements take constant time. Linked lists provide sequential access to their elements, which means that grabbing elements takes linear time. Because linked lists need extra storage for references, they are impractical for lists of small data items such as characters.
package com.zetcode; import java.util.LinkedList; public class LinkedListExample { public static void main(String[] args) { LinkedList<Integer> nums = new LinkedList<>(); nums.add(5); nums.add(10); nums.add(13); nums.add(12); nums.add(15); nums.add(23); System.out.println(nums); nums.removeFirst(); nums.removeLast(); nums.addFirst(17); nums.addLast(77); } } System.out.println(nums);

This is a LinkedList example with some of its methods.


LinkedList<Integer> nums = new LinkedList<>();

This LinkedList holds integer numbers.


nums.add(5); nums.add(10);

We add numbers to the list. Autoboxing wraps primitive int types to the Integer objects.
nums.removeFirst(); nums.removeLast();

These two methods remove the first and the last element from the container.
nums.addFirst(17); nums.addLast(77);

We add an element at the beginning and at the end of the list.

169

$ java com.zetcode.LinkedListExample [5, 10, 13, 12, 15, 23] [17, 10, 13, 12, 15, 77]

The elements contained by the linked list are printed twice to the console.

HashMap
A HashMap is a container that that stores key/value pairs. Each key is associated with one value. Keys must be unique. This container type is called an associative array or a dictionary in other programming languages. HashMaps take more memory, because for each value there is also a key. Deletion and insertion operations take constant time. HashMaps can store null values.
package com.zetcode; import java.util.HashMap; import java.util.Set; public class HashMapExample { public static void main(String[] args) { HashMap<String, String> domains = new HashMap<>(); domains.put("de", domains.put("sk", domains.put("us", domains.put("ru", domains.put("hu", domains.put("pl", "Germany"); "Slovakia"); "United States"); "Russia"); "Hungary"); "Poland");

System.out.println(domains.get("pl")); for (String item : domains.values()) { System.out.println(item); } Set keys = domains.keySet(); } } System.out.println(keys);

We have a HashMap where we map domain names to their country names.


HashMap<String, String> domains = new HashMap<>();

We create a HashMap with string keys and values.


domains.put("de", "Germany"); domains.put("sk", "Slovakia"); domains.put("us", "United States"); ...

170

We put some data to the HashMap. The first string is the key. The second is the value.
System.out.println(domains.get("pl"));

We retrieve a specific value by its key. For the retrieval operation, we use the get method.
for (String item : domains.values()) { System.out.println(item); }

The values() method returns a collection of values contained in the domains HashMap. We go through the values with the for loop and print them to the console.
Set keys = domains.keySet();

The keySet() method returns the keys of the HashMap in a Set collection. A Set is a collection of unique elements.
System.out.println(keys);

The elements of the set are printed to the console.


$ java com.zetcode.HashMapExample Poland Germany Slovakia Hungary Poland United States Russia [de, sk, hu, pl, us, ru]

This is the output of the example.

TreeMap
A TreeMap is a map that is sorted according to the natural ordering of its keys. While HashMap is more time-efficient, a TreeMap is more space-efficient.
package com.zetcode; import java.util.TreeMap; public class TreeMapExample { public static void main(String[] args) { TreeMap<String, String> domains = new TreeMap<>(); domains.put("de", domains.put("sk", domains.put("us", domains.put("ru", domains.put("hu", "Germany"); "Slovakia"); "United States"); "Russia"); "Hungary");

171

domains.put("pl", "Poland"); System.out.println(domains); System.out.println(domains.descendingMap()); } }

In the example, we create a TreeMap and put domains with their country names into it.
TreeMap<String, String> domains = new TreeMap<>();

A TreeMap is created.
System.out.println(domains);

This will print the keys/values in their natural sort order in ascending order.
System.out.println(domains.descendingMap());

THe descendingMap() method returns a reverse order view of the mappings contained in this map.
$ java com.zetcode.TreeMapExample {de=Germany, hu=Hungary, pl=Poland, ru=Russia, sk=Slovakia, us=United States} {us=United States, sk=Slovakia, ru=Russia, pl=Poland, hu=Hungary, de=Germany}

The com.zetcode.TreeMapExample program printed keys with their values in ascending and descending sort order.

HashSet
A HashSet is a collection that contains no duplicate elements. This class offers constant time performance for the basic operations (add, remove, contains and size). A HashSet does not provide ordering of elements.
package com.zetcode; import java.util.HashSet; public class HashSetExample { public static void main(String[] args) { HashSet<String> brands = new HashSet<>(); brands.add("Pepsi"); brands.add("Amazon"); brands.add("Volvo"); brands.add("IBM"); brands.add("IBM"); System.out.println(brands); System.out.println(brands.isEmpty());

172

System.out.println(brands.contains("Volvo")); brands.remove("Volvo"); System.out.println(brands.contains("Volvo")); brands.clear(); System.out.println(brands); } }

There can be only one brand registered under a name. So the brand names is a good example for a HashSet.
HashSet<String> brands = new HashSet<>(); brands.add("Pepsi"); brands.add("Amazon"); brands.add("Volvo"); brands.add("IBM"); brands.add("IBM");

We create a HashSet and add new elements. The IBM brand is added twice. However, the IBM is present in the container only once.
System.out.println(brands);

We print all the elements in one shot.


System.out.println(brands.isEmpty());

The isEmpty() method checks if the container is empty.


System.out.println(brands.contains("Volvo"));

With the contains() method we check if the Volvo brand is present in the brands container. The line prints true.
brands.remove("Volvo"); System.out.println(brands.contains("Volvo"));

We remove the Volvo brand from the brands container. The second line prints false.
brands.clear();

The clear() method removes all of the elements from the set.
$ java com.zetcode.HashSetExample [IBM, Pepsi, Volvo, Amazon] false true false []

This is the output of the com.zetcode.HashSetExample program.

173

TreeSet
A TreeSet is a set which has elements ordered using their natural ordering. A TreeSet is slower than a HashSet. A HashSet can contain null values, while a TreeSet cannot.
package com.zetcode; import java.util.ArrayList; import java.util.TreeSet; public class TreeSetExample { public static void main(String[] args) { ArrayList<String> brands = new ArrayList<>(); brands.add("Pepsi"); brands.add("Amazon"); brands.add("Volvo"); brands.add("IBM"); brands.add("HP"); brands.add("Apple"); brands.add("Starbucks"); TreeSet<String> brands2 = new TreeSet<>(); brands2.addAll(brands); System.out.println(brands2); System.out.println(brands2.descendingSet()); System.out.println(brands2.first()); System.out.println(brands2.last()); System.out.println(brands2.headSet("IBM", true)); System.out.println(brands2.tailSet("IBM", false)); System.out.println(brands2.subSet("Apple", true, "Starbucks", true)); } }

In this example, we work with a TreeSet.


ArrayList<String> brands = new ArrayList<>(); brands.add("Pepsi"); brands.add("Amazon"); brands.add("Volvo"); brands.add("IBM"); brands.add("HP"); brands.add("Apple"); brands.add("Starbucks");

An ArrayList of various brands is created.


TreeSet<String> brands2 = new TreeSet<>(); brands2.addAll(brands);

With the help of the addAll() method, a new TreeSet is created from the ArrayList container.

174

System.out.println(brands2); System.out.println(brands2.descendingSet());

The elements of the container are printed to the console in ascending and descending orders.
System.out.println(brands2.first()); System.out.println(brands2.last());

We print the first and the last element of the container.


System.out.println(brands2.headSet("IBM", true));

The headSet() method returns a slice of the set whose elements are less than the specified element. The second parameter controls whether the specified element is included.
System.out.println(brands2.tailSet("IBM", false));

The tailSet() method returns a slice of the set whose elements are greater than the specified element.
System.out.println(brands2.subSet("Apple", true, "Starbucks", true));

The subSet() method returns a portion of the container whose elements range from the first specified element to the second one.
$ java com.zetcode.TreeSetExample [Amazon, Apple, HP, IBM, Pepsi, Starbucks, Volvo] [Volvo, Starbucks, Pepsi, IBM, HP, Apple, Amazon] Amazon Volvo [Amazon, Apple, HP, IBM] [Pepsi, Starbucks, Volvo] [Apple, HP, IBM, Pepsi, Starbucks]

This is the output of the com.zetcode.TreeSetExample example.

Collections class
The Collections is a utility class that provides many useful methods for working with containers. It consists exclusively of static methods. Some of the methods are not applicable to all collection types. For example, it is not possible to use the sort() method on a HashSet, because this container does not support ordered elements.
package com.zetcode; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; public class CollectionsExample { public static void main(String[] args) {

175

Integer[] nums = { 4, 3, 2, 4, 5, 6, 4, 2, 7, 8, 9, 0, 1 }; ArrayList<Integer> ns = new ArrayList<>(Arrays.asList(nums)); System.out.println("Default order:"); System.out.println(ns); System.out.println("Ascending order:"); Collections.sort(ns); System.out.println(ns); System.out.println("Descending order:"); Collections.reverse(ns); System.out.println(ns); System.out.println("Swapping the first and the last elements:"); Collections.swap(ns, 0, ns.size()-1); System.out.println(ns); System.out.println("Replacing all 4s with 0s:"); Collections.replaceAll(ns, 4, 0); System.out.println(ns); System.out.println("Random order:"); Collections.shuffle(ns); System.out.println(ns); System.out.println(Collections.max(ns)); System.out.println(Collections.min(ns)); } }

In the example, we use several methods of the Collections class.


Integer[] nums = { 4, 3, 2, 4, 5, 6, 4, 2, 7, 8, 9, 0, 1 }; ArrayList<Integer> ns = new ArrayList<>(Arrays.asList(nums));

An ArrayList is created from an array of Integers. The asList() method of the Arrays class is used to transform an array into a list which is then passed to the constructor.
Collections.sort(ns);

The sort() method sorts the elements in ascending order.


Collections.reverse(ns);

The reverse() method reverses the order of elements in the list.


Collections.swap(ns, 0, ns.size()-1);

The swap() method exchanges two elements. The first element with the last element in our case.
Collections.replaceAll(ns, 4, 0);

This line replaces all occurrences of number 4 with 0.

176

Collections.shuffle(ns);

The shuffle() method randomly reorders the elements in the container.


System.out.println(Collections.max(ns)); System.out.println(Collections.min(ns));

Here we print the maximum and the minimum values of the list.
$ java com.zetcode.CollectionsExample Default order: [4, 3, 2, 4, 5, 6, 4, 2, 7, 8, 9, 0, 1] Ascending order: [0, 1, 2, 2, 3, 4, 4, 4, 5, 6, 7, 8, 9] Descending order: [9, 8, 7, 6, 5, 4, 4, 4, 3, 2, 2, 1, 0] Swapping the first and the last elements: [0, 8, 7, 6, 5, 4, 4, 4, 3, 2, 2, 1, 9] Replacing all 4s with 0s: [0, 8, 7, 6, 5, 0, 0, 0, 3, 2, 2, 1, 9] Random order: [1, 6, 2, 8, 0, 2, 0, 9, 5, 0, 7, 3, 0] 9 0

This is a sample output of the com.zetcode.CollectionsExample program. This part of the Java tutorial was dedicated to collections in Java.

177

PostgreSQL Java tutorial


This is a Java tutorial for the PostgreSQL database. It covers the basics of PostgreSQL programming with Java. In this tutorial, we use the PostgreSQL JDBC Driver driver. It is the official JDBC driver for PostgreSQL. The examples were created and tested on Ubuntu Linux. There is a similar PostgreSQL Python tutorial, MySQL Java tutorial or Apache Derby tutorial on ZetCode.

JDBC
JDBC is an API for the Java programming language that defines how a client may access a database. It provides methods for querying and updating data in a database. JDBC is oriented towards relational databases. From a technical point of view, the API is as a set of classes in the java.sql package. To use JDBC with a particular database, we need a JDBC driver for that database.

About PostgreSQL database


PostgreSQL is a powerful, open source object-relational database system. It is a multi-user database management system. It runs on multiple platforms including Linux, FreeBSD, Solaris, Microsoft Windows and Mac OS X. PostgreSQL is developed by the PostgreSQL Global Development Group.

Before we start
We need to install postgresql package. We need to install the JDK, Java Development Kit, for compiling and running Java programs. Finally, we need the PostgreSQL Java driver. If we are using Netbeans IDE, than we have already the driver at hand. Inside the Projects tab, right click on the Libraries node and select Add Library option. From the list of options, select PostgreSQL JDBC Driver. Note that the driver may be older. Older driver may cause problems on newer PostgreSQL versions. In such a case, we can add the newest driver from by choosing add JAR/Folder and selecting a driver from the filesystem.

Figure: Netbeans project libs If we want to compile the examples from the command line, go to the site http://jdbc.postgresql.org/download.html and download the latest PostgreSQL Java Driver.

178

$ javac zetcode/Version.java $ java -cp .:lib/postgresql.jdbc4.jar zetcode/Version PostgreSQL 9.1.2 on i686-pc-linux-gnu, compiled by gcc-4.6.real (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1, 32-bit

Assuming, that we have put the driver jar file into the lib directory and using package zetcode, we compile and run the first example this way.

If we don't already have PostgreSQL installed, we must install it.


$ sudo apt-get install postgresql

This command installs the PostgreSQL server and various other packages.
$ sudo update-rc.d -f postgresql remove Removing any system startup links for /etc/init.d/postgresql ... /etc/rc0.d/K21postgresql /etc/rc1.d/K21postgresql /etc/rc2.d/S19postgresql /etc/rc3.d/S19postgresql /etc/rc4.d/S19postgresql /etc/rc5.d/S19postgresql /etc/rc6.d/K21postgresql

If we install the PostgreSQL database from packages, it is automatically added to the start up scripts of the operating system. If we are only learning to work with the database, it is unnecessary to start the database each time we boot the system. The above command removes any system startup links for the PostgreSQL database.
$ /etc/init.d/postgresql status Running clusters: 9.1/main $ service postgresql status Running clusters: 9.1/main

We check if the PostgreSQL server is running. If not, we need to start the server.
$ sudo service postgresql start * Starting PostgreSQL 9.1 database server [ OK ]

On Ubuntu Linux we can start the server with the service postgresql start command.
$ sudo service postgresql stop [sudo] password for janbodnar: * Stopping PostgreSQL 9.1 database server

[ OK ]

We use the service postgresql stop command to stop the PostgreSQL server.

Next, we are going to create a new database user and a new database. We will use the createuser and createdb commands. These are installed with the PostgreSQL database.
$ sudo -u postgres createuser -W user12

179

Shall the new role be a superuser? (y/n) n Shall the new role be allowed to create databases? (y/n) y Shall the new role be allowed to create more new roles? (y/n) n Password:

We create a new database user. The user is called user12 and it is created with a password (W option).
$ sudo -u postgres createdb testdb -O user12

We create a new testdb database. We will use this database throughout the tutorial. We set the new user to be the owner of the database (the -O option).

PostgreSQL version
If the following program runs OK, then we have everything installed OK. We check the version of the PostgreSQL server.
package zetcode; import import import import import import import java.sql.Connection; java.sql.DriverManager; java.sql.ResultSet; java.sql.SQLException; java.sql.Statement; java.util.logging.Level; java.util.logging.Logger;

public class Version { public static void main(String[] args) { Connection con = null; Statement st = null; ResultSet rs = null; String url = "jdbc:postgresql://localhost/testdb"; String user = "user12"; String password = "34klq*"; try { con = DriverManager.getConnection(url, user, password); st = con.createStatement(); rs = st.executeQuery("SELECT VERSION()"); if (rs.next()) { System.out.println(rs.getString(1)); } } catch (SQLException ex) { Logger lgr = Logger.getLogger(Version.class.getName()); lgr.log(Level.SEVERE, ex.getMessage(), ex); } finally { try { if (rs != null) { rs.close();

180

} if (st != null) { st.close(); } if (con != null) { con.close(); } } catch (SQLException ex) { Logger lgr = Logger.getLogger(Version.class.getName()); lgr.log(Level.WARNING, ex.getMessage(), ex); } } } }

We connect to the database and get some info about the PostgreSQL server.
String url = "jdbc:postgresql://localhost/testdb";

This is the connection url for the PostgreSQL database. Each driver has a different syntax for the url. In our case, we provide a host, a port and a database name.
con = DriverManager.getConnection(url, user, password);

We establish a connection to the database, using the connection url, user name and password.
st = con.createStatement();

The createStatement() method of the connection object creates a Statement object for sending SQL statements to the database.
rs = st.executeQuery("SELECT VERSION()");

The executeQuery() method of the connection object executes the given SQL statement, which returns a single ResultSet object. The ResultSet is a table of data returned by a specific SQL statement.
if (rs.next()) { System.out.println(rs.getString(1)); }

A ResultSet object maintains a cursor pointing to its current row of data. Initially the cursor is positioned before the first row. The next() method moves the cursor to the next row. If there are no rows left, the method returns false. The getString() method retrieves the value of a specified column. The first column has index 1.
} catch (SQLException ex) { Logger lgr = Logger.getLogger(Version.class.getName()); lgr.log(Level.SEVERE, ex.getMessage(), ex); }

In case of an exception, we log the error message. For this console example, the message is displayed in the terminal. 181

try { if (rs != null) { rs.close(); } if (st != null) { st.close(); } if (con != null) { con.close(); } ...

Inside the finally block, we close the database resources. We also check if the objects are not equal to null. This is to prevent null pointer exceptions. Otherwise we might get a NullPointerException, which would terminate the application and might leave the resources not cleaned up.
} catch (SQLException ex) { Logger lgr = Logger.getLogger(Version.class.getName()); lgr.log(Level.WARNING, ex.getMessage(), ex); }

We log an error message, when the resources could not be closed.


$ javac zetcode/Version.java $ java -cp .:lib/postgresql.jdbc4.jar zetcode.Version PostgreSQL 9.1.2 on i686-pc-linux-gnu, compiled by gcc-4.6.real (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1, 32-bit

This is a sample output of the program.

Creating and populating tables


Next we are going to create database tables and fill them with data. These tables will be used throughout this tutorial.
DROP TABLE IF EXISTS books, authors, testing, images; CREATE TABLE IF NOT EXISTS authors ( id serial PRIMARY KEY, name VARCHAR(25) ); CREATE TABLE IF NOT EXISTS books ( id serial PRIMARY KEY, author_id INT references authors(id), title VARCHAR(100) ); CREATE TABLE IF NOT EXISTS testing(id INT); CREATE TABLE IF NOT EXISTS images(id serial, data bytea); INSERT INSERT INSERT INSERT INSERT INTO INTO INTO INTO INTO authors(id, authors(id, authors(id, authors(id, authors(id, name) name) name) name) name) VALUES(1, VALUES(2, VALUES(3, VALUES(4, VALUES(5, 'Jack London'); 'Honore de Balzac'); 'Lion Feuchtwanger'); 'Emile Zola'); 'Truman Capote');

182

INSERT INTO INSERT INTO INSERT INTO INSERT INTO INSERT INTO INSERT INTO INSERT INTO INSERT INTO INSERT INTO Tiffany');

books(id, books(id, books(id, books(id, books(id, books(id, books(id, books(id, books(id,

author_id, author_id, author_id, author_id, author_id, author_id, author_id, author_id, author_id,

title) title) title) title) title) title) title) title) title)

VALUES(1, VALUES(2, VALUES(3, VALUES(4, VALUES(5, VALUES(6, VALUES(7, VALUES(8, VALUES(9,

1, 1, 2, 2, 3, 4, 4, 5, 5,

'Call of the Wild'); 'Martin Eden'); 'Old Goriot'); 'Cousin Bette'); 'Jew Suess'); 'Nana'); 'The Belly of Paris'); 'In Cold blood'); 'Breakfast at

We have a tables.sql file. It creates four database tables, authors, books, testing and images. We place a foreign key constraint on the author_id column of the books table. We fill the authors and books tables with initial data.
$ psql testdb psql (9.1.2) Type "help" for help. testdb=# testdb=# \i tables.sql DROP TABLE psql:tables.sql:6: NOTICE: CREATE TABLE will create implicit sequence "authors_id_seq" for serial column "authors.id" psql:tables.sql:6: NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "authors_pkey" for table "authors" CREATE TABLE ...

The psql is a terminal-based front-end to PostgreSQL. We can use it to perfom interactive queries, issue them to PostgreSQL, and see the query results. Inside the psql tool, we import and execute the tables.sql file.
testdb=# \dt List of relations Schema | Name | Type | Owner --------+---------+-------+----------public | authors | table | janbodnar public | books | table | janbodnar public | images | table | janbodnar public | testing | table | janbodnar (4 rows)

We check the created tables.

Prepared statements
Now we will concern ourselves with prepared statements. When we write prepared statements, we use placeholders instead of directly writing the values into the statements. Prepared statements increase security and performance. In Java a PreparedStatement is an object which represents a precompiled SQL statement.
package zetcode;

183

import import import import import import

java.sql.Connection; java.sql.DriverManager; java.sql.PreparedStatement; java.sql.SQLException; java.util.logging.Level; java.util.logging.Logger;

public class Prepared { public static void main(String[] args) { Connection con = null; PreparedStatement pst = null; String url = "jdbc:postgresql://localhost/testdb"; String user = "user12"; String password = "34klq*"; try { int id = 6; String author = "Trygve Gulbranssen"; con = DriverManager.getConnection(url, user, password); String stm = "INSERT INTO authors(id, name) VALUES(?, ?)"; pst = con.prepareStatement(stm); pst.setInt(1, id); pst.setString(2, author); pst.executeUpdate(); } catch (SQLException ex) { Logger lgr = Logger.getLogger(Prepared.class.getName()); lgr.log(Level.SEVERE, ex.getMessage(), ex); } finally { try { if (pst != null) { pst.close(); } if (con != null) { con.close(); } } catch (SQLException ex) { Logger lgr = Logger.getLogger(Prepared.class.getName()); lgr.log(Level.SEVERE, ex.getMessage(), ex); } } } }

We add a new author to the authors table.


String stm = "INSERT INTO authors(id, name) VALUES(?, ?)"; pst = con.prepareStatement(stm);

Here we create a prepared statement. When we write prepared statements, we use placeholders instead of directly writing the values into the statements. Prepared statements

184

are faster and guard against SQL injection attacks. The ? is a placeholder, which is going to be filled later. In our case we will fill two values. An integer id and a string name.
pst.setInt(1, id);

A value is bound to the placeholder.


pst.setString(2, author);

Another value is bound to the placeholder.


pst.executeUpdate();

The prepared statement is executed. We use the executeUpdate() method of the statement object when we don't expect any data to be returned. This is when we create databases or execute INSERT, UPDATE, DELETE statements.
$ javac zetcode/Prepared.java $ java -cp .:lib/postgresql.jdbc4.jar zetcode.Prepared testdb=# SELECT * FROM authors; id | name ----+-------------------1 | Jack London 2 | Honore de Balzac 3 | Lion Feuchtwanger 4 | Emile Zola 5 | Truman Capote 6 | Trygve Gulbranssen (6 rows)

We have a new author inserted into the table.

For the following two examples, we will use the Testing table. We will execute a normal statement and a prepared statement 1000 times. We check, if there is some difference in execution time.
package zetcode; import import import import import import java.sql.Connection; java.sql.DriverManager; java.sql.SQLException; java.sql.Statement; java.util.logging.Level; java.util.logging.Logger;

public class NotPrepared { public static void main(String[] args) { Connection con = null; Statement st = null;

185

String url = "jdbc:postgresql://localhost/testdb"; String user = "user12"; String password = "34klq*"; try { con = DriverManager.getConnection(url, user, password); st = con.createStatement(); for (int i=1; i<=1000; i++) { String query = "INSERT INTO Testing(Id) VALUES(" + 2*i + ")"; } } catch (SQLException ex) { Logger lgr = Logger.getLogger(NotPrepared.class.getName()); lgr.log(Level.SEVERE, ex.getMessage(), ex); } finally { try { if (st != null) { st.close(); } if (con != null) { con.close(); } } catch (SQLException ex) { Logger lgr = Logger.getLogger(NotPrepared.class.getName()); lgr.log(Level.SEVERE, ex.getMessage(), ex); } } } } st.executeUpdate(query);

The first example uses the normal Statement object.


for (int i=1; i<=1000; i++) { String query = "INSERT INTO Testing(Id) VALUES(" + 2*i + ")"; st.executeUpdate(query); }

We build the query and execute it 1000 times.


$ javac zetcode/NotPrepared.java $ /usr/bin/time java -cp .:lib/postgresql.jdbc4.jar zetcode.NotPrepared 0.81user 0.14system 0:32.27elapsed 2%CPU (0avgtext+0avgdata 89824maxresident)k 960inputs+88outputs (1major+6008minor)pagefaults 0swaps

We use the time command to measure the time, that the program ran. Note that we use a standard Linux command, not the built-in bash time command. It took 32s to insert 1000 rows into the table using the Statement object.
package zetcode;

186

import import import import import import

java.sql.Connection; java.sql.DriverManager; java.sql.PreparedStatement; java.sql.SQLException; java.util.logging.Level; java.util.logging.Logger;

public class Prepared2 { public static void main(String[] args) { Connection con = null; PreparedStatement pst = null; String url = "jdbc:postgresql://localhost/testdb"; String user = "user12"; String password = "34klq*"; try { con = DriverManager.getConnection(url, user, password); pst = con.prepareStatement("INSERT INTO Testing(Id) VALUES(?)"); for (int i = 1; i <= 1000; i++) { pst.setInt(1, i * 2); pst.executeUpdate(); } } catch (SQLException ex) { Logger lgr = Logger.getLogger(Prepared2.class.getName()); lgr.log(Level.SEVERE, ex.getMessage(), ex); } finally { try { if (pst != null) { pst.close(); } if (con != null) { con.close(); } } catch (SQLException ex) { Logger lgr = Logger.getLogger(Prepared2.class.getName()); lgr.log(Level.SEVERE, ex.getMessage(), ex); } } } }

Now we use the PreparedStatement to do the same task.


pst = con.prepareStatement("INSERT INTO Testing(Id) VALUES(?)");

We create the prepared statement using the prepareStatement() method.


for (int i = 1; i <= 1000; i++) { pst.setInt(1, i * 2);

187

pst.executeUpdate();

We bind a value to the prepared statement, execute it in a loop thousand times.


$ javac zetcode/Prepared2.java $ /usr/bin/time java -cp .:lib/postgresql.jdbc4.jar zetcode.Prepared2 0.59user 0.11system 0:15.08elapsed 4%CPU (0avgtext+0avgdata 76912maxresident)k 0inputs+64outputs (0major+4941minor)pagefaults 0swaps

This time it took 15s to insert 1000 rows. We have saved 17s using prepared statements.

Retrieving data
Next we will show, how to retrieve data from a database table. We get all data from the authors table.
package zetcode; import import import import import import import java.sql.PreparedStatement; java.sql.Connection; java.sql.DriverManager; java.sql.ResultSet; java.sql.SQLException; java.util.logging.Level; java.util.logging.Logger;

public class Retrieve { public static void main(String[] args) { Connection con = null; PreparedStatement pst = null; ResultSet rs = null; String url = "jdbc:postgresql://localhost/testdb"; String user = "user12"; String password = "34klq*"; try { con = DriverManager.getConnection(url, user, password); pst = con.prepareStatement("SELECT * FROM authors"); rs = pst.executeQuery(); while (rs.next()) { System.out.print(rs.getInt(1)); System.out.print(": "); System.out.println(rs.getString(2)); } } catch (SQLException ex) { Logger lgr = Logger.getLogger(Retrieve.class.getName()); lgr.log(Level.SEVERE, ex.getMessage(), ex); } finally {

188

try { if (rs != null) { rs.close(); } if (pst != null) { pst.close(); } if (con != null) { con.close(); } } catch (SQLException ex) { Logger lgr = Logger.getLogger(Retrieve.class.getName()); lgr.log(Level.WARNING, ex.getMessage(), ex); }

} } }

We get all authors from the authors table and print them to the console.
pst = con.prepareStatement("SELECT * FROM authors"); rs = pst.executeQuery();

We execute a query that selects all columns from the authors table. We use the executeQuery() method. The method executes the given SQL statement, which returns a single ResultSet object. The ResultSet is the data table returned by the SQL query.
while (rs.next()) { System.out.print(rs.getInt(1)); System.out.print(": "); System.out.println(rs.getString(2)); }

The next() method advances the cursor to the next record. It returns false, when there are no more rows in the result set. The getInt() and getString() methods retrieve the value of the designated column in the current row of this ResultSet object as an int/String in the Java programming language.
$ javac zetcode/Retrieve.java $ java -cp .:lib/postgresql.jdbc4.jar zetcode/Retrieve 1: Jack London 2: Honore de Balzac 3: Lion Feuchtwanger 4: Emile Zola 5: Truman Capote

We have ids and names of authors printed to the console.

Properties
It is a common practice to put the configuration data outside the program in a separate file. This way the programmers are more flexible. We can change the user, the password or the connection url without needing to recompile the program. It is especially useful in a dynamic environment, where is a need for a lot of testing, debugging, securing data etc.

189

In Java, the Properties is a class used often for this. The class is used for easy reading and saving of key/value properties.
db.url=jdbc:postgresql://localhost/testdb db.user=user12 db.passwd=34klq*

We have a database.properties file, in which we have three key/value pairs. These are dynamically loaded during execution of the program.
package zetcode; import import import import import import import import import import import java.io.FileInputStream; java.io.FileNotFoundException; java.io.IOException; java.sql.Connection; java.sql.DriverManager; java.sql.ResultSet; java.sql.SQLException; java.sql.PreparedStatement; java.util.Properties; java.util.logging.Level; java.util.logging.Logger;

public class Retrieve2 { public static void main(String[] args) { Connection con = null; PreparedStatement pst = null; ResultSet rs = null; Properties props = new Properties(); FileInputStream in = null; try { in = new FileInputStream("database.properties"); props.load(in); } catch (IOException ex) { Logger lgr = Logger.getLogger(Retrieve2.class.getName()); lgr.log(Level.SEVERE, ex.getMessage(), ex); } finally { try { if (in != null) { in.close(); } } catch (IOException ex) { Logger lgr = Logger.getLogger(Retrieve2.class.getName()); lgr.log(Level.SEVERE, ex.getMessage(), ex); } } String url = props.getProperty("db.url"); String user = props.getProperty("db.user");

190

String passwd = props.getProperty("db.passwd"); try { con = DriverManager.getConnection(url, user, passwd); pst = con.prepareStatement("SELECT * FROM Authors"); rs = pst.executeQuery(); while (rs.next()) { System.out.print(rs.getInt(1)); System.out.print(": "); System.out.println(rs.getString(2)); } } catch (Exception ex) { Logger lgr = Logger.getLogger(Retrieve2.class.getName()); lgr.log(Level.SEVERE, ex.getMessage(), ex); } finally { try { if (rs != null) { rs.close(); } if (pst != null) { pst.close(); } if (con != null) { con.close(); } } catch (SQLException ex) { Logger lgr = Logger.getLogger(Retrieve2.class.getName()); lgr.log(Level.WARNING, ex.getMessage(), ex); } } } }

We connect to the testdb database and print the contents of the authors table to the console. This time, we load the connection properties from a file. They are not hard coded in the proram.
Properties props = new Properties(); FileInputStream in = null; try { in = new FileInputStream("database.properties"); props.load(in); ...

The Properties class is created. The data is loaded from the file called database.properties, where we have our configuration data.
String url = props.getProperty("db.url"); String user = props.getProperty("db.user"); String passwd = props.getProperty("db.passwd");

191

The values are retrieved with the getProperty() method.

Multiple statements
It is possible to execute multiple SQL statements in one query.
package zetcode; import import import import import import import java.sql.Connection; java.sql.DriverManager; java.sql.PreparedStatement; java.sql.ResultSet; java.sql.SQLException; java.util.logging.Level; java.util.logging.Logger;

public class Multiple { public static void main(String[] args) { Connection con = null; PreparedStatement pst = null; ResultSet rs = null; String url = "jdbc:postgresql://localhost/testdb"; String user = "user12"; String password = "34klq*"; try { con = DriverManager.getConnection(url, user, password); String query = "SELECT id, name FROM authors WHERE Id=1;" + "SELECT id, name FROM authors WHERE Id=2;" + "SELECT id, name FROM authors WHERE Id=3"; pst = con.prepareStatement(query); boolean isResult = pst.execute(); do { rs = pst.getResultSet(); while (rs.next()) { System.out.print(rs.getInt(1)); System.out.print(": "); System.out.println(rs.getString(2)); } isResult = pst.getMoreResults(); } while (isResult); } catch (SQLException ex) { Logger lgr = Logger.getLogger(Multiple.class.getName()); lgr.log(Level.SEVERE, ex.getMessage(), ex); } finally {

192

try { if (rs != null) { rs.close(); } if (pst != null) { pst.close(); } if (con != null) { con.close(); } } catch (SQLException ex) { Logger lgr = Logger.getLogger(Multiple.class.getName()); lgr.log(Level.WARNING, ex.getMessage(), ex);

} } } }

In the code example, we retrieve three rows from the authors table. We use three SELECT statements to get three rows.
String query = "SELECT id, name FROM authors WHERE Id=1;" + "SELECT id, name FROM authors WHERE Id=2;" + "SELECT id, name FROM authors WHERE Id=3";

Here we have a query with multiple statements. The statements are separated by a semicolon.
boolean isResult = pst.execute();

We call the execute() method of the prepared statement object. The method returns a boolean value indicating if the first result is a ResultSet object. Subsequent results are called using the getMoreResults() method.
do { rs = pst.getResultSet(); while (rs.next()) { System.out.print(rs.getInt(1)); System.out.print(": "); System.out.println(rs.getString(2)); } isResult = pst.getMoreResults(); } while (isResult);

The processing of the results is done inside the do/while loop. The ResultSet is retrieved with the getResultSet() method call. To find out, if there are other results, we call the getMoreResults() method.
$ javac zetcode/Multiple.java $ java -cp .:lib/postgresql.jdbc4.jar zetcode.Multiple 1: Jack London 2: Honore de Balzac 3: Lion Feuchtwanger

193

The output of the example. The first three rows were retrieved from the authors table.

Metadata
Metadata is information about the data in the database. Metadata in a PostgreSQL database contains information about the tables and columns, in which we store data. Number of rows affected by an SQL statement is a metadata. Number of rows and columns returned in a result set belong to metadata as well. Metadata in PostgreSQL can be obtained by calling the getMetaData() method of the result set object or from the information_schema table. Next we will show, how to print column headers with the data from the database table.
package zetcode; import import import import import import import import import java.sql.Connection; java.sql.DriverManager; java.sql.PreparedStatement; java.sql.ResultSet; java.sql.ResultSetMetaData; java.sql.SQLException; java.util.Formatter; java.util.logging.Level; java.util.logging.Logger;

public class ColumnHeaders { public static void main(String[] args) { Connection con = null; PreparedStatement pst = null; ResultSet rs = null; String url = "jdbc:postgresql://localhost/testdb"; String user = "user12"; String password = "34klq*"; try { con = DriverManager.getConnection(url, user, password); String query = "SELECT name, title From authors, " + "books WHERE authors.id=books.author_id"; pst = con.prepareStatement(query); rs = pst.executeQuery(); ResultSetMetaData meta = rs.getMetaData(); String colname1 = meta.getColumnName(1); String colname2 = meta.getColumnName(2); Formatter fmt1 = new Formatter(); fmt1.format("%-21s%s", colname1, colname2); System.out.println(fmt1);

194

while (rs.next()) { Formatter fmt2 = new Formatter(); fmt2.format("%-21s", rs.getString(1)); System.out.print(fmt2); System.out.println(rs.getString(2)); } } catch (SQLException ex) { Logger lgr = Logger.getLogger(ColumnHeaders.class.getName()); lgr.log(Level.SEVERE, ex.getMessage(), ex); } finally { try { if (rs != null) { rs.close(); } if (pst != null) { pst.close(); } if (con != null) { con.close(); } } catch (SQLException ex) { Logger lgr = Logger.getLogger(ColumnHeaders.class.getName()); lgr.log(Level.WARNING, ex.getMessage(), ex); } } } }

In this program, we select authors from the authors table and their books from the books table. We print the names of the columns returned in the result set. We format the output.
String query = "SELECT name, title From authors, " + "books WHERE authors.id=books.author_id";

This is the SQL statement which joins authors with their books.
ResultSetMetaData meta = rs.getMetaData();

To get the column names we need to get the ResultSetMetaData. It is an object that can be used to get information about the types and properties of the columns in a ResultSet object.
String colname1 = meta.getColumnName(1); String colname2 = meta.getColumnName(2);

From the obtained metadata, we get the column names.


Formatter fmt1 = new Formatter(); fmt1.format("%-21s%s", colname1, colname2); System.out.println(fmt1)

We print the column names to the console. We use the Formatter object to format the data.

195

while (rs.next()) { Formatter fmt2 = new Formatter(); fmt2.format("%-21s", rs.getString(1)); System.out.print(fmt2); System.out.println(rs.getString(2)); }

We print the data to the console. We again use the Formatter object to format the data. The first column is 21 characters wide and is aligned to the left.
$ javac zetcode/ColumnHeaders.java $ java -cp .:lib/postgresql.jdbc4.jar zetcode/ColumnHeaders name title Jack London Call of the Wild Jack London Martin Eden Honore de Balzac Old Goriot Honore de Balzac Cousin Bette Lion Feuchtwanger Jew Suess Emile Zola Nana Emile Zola The Belly of Paris Truman Capote In Cold blood Truman Capote Breakfast at Tiffany

Output of the program.

In the following example we will list all tables in the testdb database.
package zetcode; import import import import import import import java.sql.Connection; java.sql.DriverManager; java.sql.PreparedStatement; java.sql.ResultSet; java.sql.SQLException; java.util.logging.Level; java.util.logging.Logger;

public class ListTables { public static void main(String[] args) { Connection con = null; PreparedStatement pst = null; ResultSet rs = null; String url = "jdbc:postgresql://localhost/testdb"; String user = "user12"; String password = "34klq*"; try { con = DriverManager.getConnection(url, user, password); String query = "SELECT table_name FROM information_schema.tables " + "WHERE table_schema = 'public'"; pst = con.prepareStatement(query);

196

rs = pst.executeQuery(); while (rs.next()) { System.out.println(rs.getString(1)); } } catch (SQLException ex) { Logger lgr = Logger.getLogger(ListTables.class.getName()); lgr.log(Level.SEVERE, ex.getMessage(), ex); } finally { try { if (rs != null) { rs.close(); } if (pst != null) { pst.close(); } if (con != null) { con.close(); } } catch (SQLException ex) { Logger lgr = Logger.getLogger(ListTables.class.getName()); lgr.log(Level.WARNING, ex.getMessage(), ex); } } } }

The code example prints all available tables in the current database to the terminal.
String query = "SELECT table_name FROM information_schema.tables " + "WHERE table_schema = 'public'";

The table names are stored inside the system information_schema table.
$ javac zetcode/ListTables.java $ java -cp .:lib/postgresql.jdbc4.jar zetcode.ListTables authors books testing images

Listing available tables in the testdb database.

Writing images
Some people prefer to put their images into the database, some prefer to keep them on the file system for their applications. Technical difficulties arise when we work with millions of images. Images are binary data. PostgreSQL database has a special data type to store binary data called bytea. This is a non-standard data type. The standard data type in databases is BLOB.

197

For this example, we use the images table.


package zetcode; import import import import import import import import import import java.io.File; java.io.FileInputStream; java.io.FileNotFoundException; java.io.IOException; java.sql.Connection; java.sql.DriverManager; java.sql.PreparedStatement; java.sql.SQLException; java.util.logging.Level; java.util.logging.Logger;

public class WriteImage { public static void main(String[] args) { Connection con = null; PreparedStatement pst = null; FileInputStream fin = null; String url = "jdbc:postgresql://localhost/testdb"; String user = "user12"; String password = "34klq*"; try { File img = new File("woman.jpg"); fin = new FileInputStream(img); con = DriverManager.getConnection(url, user, password); VALUES(?)"); pst = con.prepareStatement("INSERT INTO images(data) pst.setBinaryStream(1, fin, (int) img.length()); pst.executeUpdate(); } catch (FileNotFoundException | SQLException ex) { Logger lgr = Logger.getLogger(WriteImage.class.getName()); lgr.log(Level.SEVERE, ex.getMessage(), ex); } finally { try { if (pst != null) { pst.close(); } if (con != null) { con.close(); } if (fin != null) { fin.close(); } } catch (IOException | SQLException ex) { Logger lgr = Logger.getLogger(WriteImage.class.getName()); lgr.log(Level.WARNING, ex.getMessage(), ex); }

198

} }

In the preceding example, we read a jpg image from the current working directory and insert in into the images table.
pst = con.prepareStatement("INSERT INTO images(data) VALUES(?)");

This is the SQL to insert an image.


File img = new File("woman.jpg"); fin = new FileInputStream(img);

We create a File object for the image file. To read bytes from this file, we create a FileInputStream object.
pst.setBinaryStream(1, fin, (int) img.length());

The binary stream is set to the prepared statement. The parameters of the setBinaryStream() method are the parameter index to bind, the input stream and the number of bytes in the stream.
pst.executeUpdate();

We execute the statement.

Reading images
In the previous example, we have inserted an image into the database table. Now we are going to read the image back from the table. Warning: if we are using PostgreSQL 9 and later, we must also use the latest JDBC driver. PostgreSQL has changed the way bytea data is stored. Therefore we run into troubles when using older driver with the PostgreSQL 9.x. Notice that Netbeans or other applications might use older drivers.
package zetcode; import import import import import import import import import import import java.io.FileOutputStream; java.io.IOException; java.io.InputStream; java.io.OutputStream; java.sql.Connection; java.sql.DriverManager; java.sql.PreparedStatement; java.sql.ResultSet; java.sql.SQLException; java.util.logging.Level; java.util.logging.Logger;

public class ReadImage { public static void main(String[] args) {

199

Connection con = null; PreparedStatement pst = null; FileOutputStream fos = null; String url = "jdbc:postgresql://localhost/testdb"; String user = "user12"; String password = "34klq*"; try { con = DriverManager.getConnection(url, user, password); String query = "SELECT data, LENGTH(data) FROM images WHERE id = 1"; pst = con.prepareStatement(query); ResultSet result = pst.executeQuery(); result.next(); fos = new FileOutputStream("woman2.jpg"); int len = result.getInt(2); byte[] buf = result.getBytes("data"); fos.write(buf, 0, len); } catch (IOException | SQLException ex) { Logger lgr = Logger.getLogger(ReadImage.class.getName()); lgr.log(Level.SEVERE, ex.getMessage(), ex); } finally { try { if (pst != null) { pst.close(); } if (con != null) { con.close(); } if (fos != null) { fos.close(); } } catch (IOException | SQLException ex) { Logger lgr = Logger.getLogger(ReadImage.class.getName()); lgr.log(Level.WARNING, ex.getMessage(), ex); } } } }

We read one image from the images table.


String query = "SELECT data, LENGTH(data) FROM images WHERE id = 1";

We select the data and the size of the image from the database table.
fos = new FileOutputStream("woman2.jpg");

200

The FileOutputStream object is created to write to a file. It is meant for writing streams of raw bytes such as image data.
int len = result.getInt(2);

We get the length of the image data. In other words, we get the number of bytes.
byte[] buf = result.getBytes("data");

The getBytes() method retrieves all bytes from the result set, as an array of bytes.
fos.write(buf, 0, len);

The bytes are written to the output stream. The image is created on the filesystem.

Transaction support
A transaction is an atomic unit of database operations against the data in one or more databases. The effects of all the SQL statements in a transaction can be either all committed to the database or all rolled back. When a connection is created, it is in autocommit mode. This means that each individual SQL statement is treated as a transaction and is automatically committed right after it is executed. This is true for all JDBC drivers, including the PostgreSQL's one. To start a new transaction, we turn the autocommit off. In direct SQL, a transaction is started with BEGIN TRANSACTION statement and ended with END TRANSACTION/COMMIT statement. In PostgreSQL these statements are BEGIN and COMMIT. However, when working with drivers these statements are omitted. They are handled by the driver. Exact details are specific to the driver. For example psycopg2 Python driver starts a transaction after the first SQL statement. The autocommit mode must be set by setting the autocommit property to True. In constrast, JDBC driver is by default in the autocommit mode. And to start a new transaction, the autocommit must be turned off.
package zetcode; import import import import import import java.sql.Connection; java.sql.DriverManager; java.sql.SQLException; java.sql.Statement; java.util.logging.Level; java.util.logging.Logger;

public class Transaction { public static void main(String[] args) { Connection con = null; Statement st = null; String url = "jdbc:postgresql://localhost/testdb"; String user = "user12"; String password = "34klq*";

201

try { con = DriverManager.getConnection(url, user, password); st = con.createStatement(); con.setAutoCommit(false); st.executeUpdate("UPDATE authors SET name = 'Leo Tolstoy' " + "WHERE Id = 1"); st.executeUpdate("UPDATE books SET title = 'War and Peace' " + "WHERE Id = 1"); st.executeUpdate("UPDATE books SET titl = 'Anna Karenina' " + "WHERE Id = 2"); con.commit(); } catch (SQLException ex) { if (con != null) { try { con.rollback(); } catch (SQLException ex1) { Logger lgr = Logger.getLogger(Transaction.class.getName()); lgr.log(Level.WARNING, ex1.getMessage(), ex1); } } Logger lgr = Logger.getLogger(Transaction.class.getName()); lgr.log(Level.SEVERE, ex.getMessage(), ex); } finally { try { if (st != null) { st.close(); } if (con != null) { con.close(); } } catch (SQLException ex) { Logger lgr = Logger.getLogger(Transaction.class.getName()); lgr.log(Level.WARNING, ex.getMessage(), ex);

} } } }

In this program, we want to change the name of the author in the first row of the authors table. We must also change the books associated with this author. A good example where a transaction is necessary. If we change the author and do not change the author's books, the data is corrupted.
con.setAutoCommit(false);

To work with transactions, we must set the autocommit to false. By default, a database connection is in autocommit mode. In this mode each statement is committed to the database, 202

as soon as it is executed. A statement cannot be undone. When the autocommit is turned off, we commit the changes by calling the commit() or roll it back by calling the rollback() method.
st.executeUpdate("UPDATE books SET titl = 'Anna Karenina' " + "WHERE Id = 2");

The third SQL statement has an error. There is no titl column in the table.
con.commit();

If there is no exception, the transaction is committed. If the autocommit is turned off, we must explicitly call the commit() method.
if (con != null) { try { con.rollback(); } catch (SQLException ex1) { Logger lgr = Logger.getLogger(Transaction.class.getName()); lgr.log(Level.WARNING, ex1.getMessage(), ex1); } }

In case of an exception, the transaction is rolled back. No changes are committed to the database.
$ javac zetcode/Transaction.java $ java -cp .:lib/postgresql.jdbc4.jar zetcode.Transaction Feb 03, 2012 1:48:51 PM zetcode.Transaction main SEVERE: ERROR: column "titl" of relation "books" does not exist Position: 18 org.postgresql.util.PSQLException: ERROR: column "titl" of relation "books" does not exist ... testdb=# SELECT name, title FROM authors, books WHERE authors.id=books.author_id; name | title -------------------+---------------------Jack London | Call of the Wild Jack London | Martin Eden Honore de Balzac | Old Goriot Honore de Balzac | Cousin Bette Lion Feuchtwanger | Jew Suess Emile Zola | Nana Emile Zola | The Belly of Paris Truman Capote | In Cold blood Truman Capote | Breakfast at Tiffany (9 rows)

An exception was thrown. The transaction was rolled back and no changes took place.

However, without a transaction, the data is not safe.


package zetcode;

203

import import import import import import

java.sql.Connection; java.sql.DriverManager; java.sql.SQLException; java.sql.Statement; java.util.logging.Level; java.util.logging.Logger;

public class NonTransaction { public static void main(String[] args) { Connection con = null; Statement st = null; String url = "jdbc:postgresql://localhost/testdb"; String user = "user12"; String password = "34klq*"; try { con = DriverManager.getConnection(url, user, password); st = con.createStatement(); st.executeUpdate("UPDATE authors SET name = 'Leo Tolstoy' " + "WHERE Id = 1"); st.executeUpdate("UPDATE books SET title = 'War and Peace' " + "WHERE Id = 1"); st.executeUpdate("UPDATE books SET titl = 'Anna Karenina' " + "WHERE Id = 2"); con.close(); } catch (SQLException ex) { Logger lgr = Logger.getLogger(NonTransaction.class.getName()); lgr.log(Level.SEVERE, ex.getMessage(), ex); } finally { try { if (st != null) { st.close(); } if (con != null) { con.close(); } } catch (SQLException ex) { Logger lgr = Logger.getLogger(NonTransaction.class.getName()); lgr.log(Level.WARNING, ex.getMessage(), ex); } } } }

We have the same example. This time, without the transaction support.

204

$ javac zetcode/NonTransaction.java $ java -cp .:lib/postgresql.jdbc4.jar zetcode.NonTransaction Feb 03, 2012 1:59:04 PM zetcode.NonTransaction main SEVERE: ERROR: column "titl" of relation "books" does not exist Position: 18 ... testdb=# SELECT name, title FROM authors, books WHERE authors.id=books.author_id; name | title -------------------+---------------------Leo Tolstoy | Martin Eden Honore de Balzac | Old Goriot Honore de Balzac | Cousin Bette Lion Feuchtwanger | Jew Suess Emile Zola | Nana Emile Zola | The Belly of Paris Truman Capote | In Cold blood Truman Capote | Breakfast at Tiffany Leo Tolstoy | War and Peace (9 rows)

An exception is thrown again. Leo Tolstoy did not write Martin Eden. The data is corrupted.

Batch updates
When we need to update data with multiple statements, we can use batch updates. Batch updates are available for INSERT, UPDATE, DELETE statements as well as for CREATE TABLE and DROP TABLE statements.
package zetcode; import import import import import import java.sql.Connection; java.sql.DriverManager; java.sql.SQLException; java.sql.Statement; java.util.logging.Level; java.util.logging.Logger;

public class BatchUpdate { public static void main(String[] args) { Connection con = null; Statement st = null; String url = "jdbc:postgresql://localhost/testdb"; String user = "user12"; String password = "34klq*"; try { con = DriverManager.getConnection(url, user, password); st = con.createStatement(); con.setAutoCommit(false);

205

st.addBatch("DROP TABLE IF EXISTS friends"); st.addBatch("CREATE TABLE friends(id serial, name VARCHAR(10))"); st.addBatch("INSERT INTO friends(name) VALUES ('Jane')"); st.addBatch("INSERT INTO friends(name) VALUES ('Tom')"); st.addBatch("INSERT INTO friends(name) VALUES ('Rebecca')"); st.addBatch("INSERT INTO friends(name) VALUES ('Jim')"); st.addBatch("INSERT INTO friends(name) VALUES ('Robert')"); int counts[] = st.executeBatch(); con.commit(); System.out.println("Committed " + counts.length + " updates"); } catch (SQLException ex) { System.out.println(ex.getNextException()); if (con != null) { try { con.rollback(); } catch (SQLException ex1) { Logger lgr = Logger.getLogger(BatchUpdate.class.getName()); lgr.log(Level.WARNING, ex1.getMessage(), ex1); } } Logger lgr = Logger.getLogger(BatchUpdate.class.getName()); lgr.log(Level.SEVERE, ex.getMessage(), ex); } finally { try { if (st != null) { st.close(); } if (con != null) { con.close(); } } catch (SQLException ex) { Logger lgr = Logger.getLogger(BatchUpdate.class.getName()); lgr.log(Level.WARNING, ex.getMessage(), ex); } } } }

This is an example program for a batch update. We create a new table called friends and insert five rows into it.
con.setAutoCommit(false);

Autocommit should always be turned off when doing batch updates.


st.addBatch("DROP TABLE IF EXISTS friends"); st.addBatch("CREATE TABLE friends(id serial, name VARCHAR(10))");

206

st.addBatch("INSERT INTO friends(name) VALUES ('Jane')"); st.addBatch("INSERT INTO friends(name) VALUES ('Tom')"); ...

We use teh addBatch() method to add a new command to the statement.


int counts[] = st.executeBatch();

After adding all commands, we call the executeBatch() to perform a batch update. The method returns an array of committed changes.
con.commit();

Batch updates are committed in a transaction.


$ javac zetcode/BatchUpdate.java $ java -cp .:lib/postgresql.jdbc4.jar zetcode.BatchUpdate Committed 7 updates testdb=# SELECT * FROM friends; id | name ----+--------1 | Jane 2 | Tom 3 | Rebecca 4 | Jim 5 | Robert (5 rows)

We execute the BatchUpdate program. We have created a new friends table and successfully inserted 5 rows.

Export and import of data


PostgreSQL has a COPY statement which can be used to copy data between a table and a file. From the JDBC point of view, it is an extension to the standard.
package zetcode; import import import import import import import import import import import java.io.FileWriter; java.io.IOException; java.sql.PreparedStatement; java.sql.Connection; java.sql.DriverManager; java.sql.ResultSet; java.sql.SQLException; java.util.logging.Level; java.util.logging.Logger; org.postgresql.copy.CopyManager; org.postgresql.core.BaseConnection;

public class CopyTo { public static void main(String[] args) { Connection con = null;

207

PreparedStatement pst = null; ResultSet rs = null; FileWriter fw = null; String url = "jdbc:postgresql://localhost/testdb"; String user = "user12"; String password = "34klq*"; try { con = DriverManager.getConnection(url, user, password); CopyManager cm = new CopyManager((BaseConnection) con); fw = new FileWriter("friends.txt"); cm.copyOut("COPY friends TO STDOUT WITH DELIMITER AS '|'", fw); } catch (SQLException | IOException ex) { Logger lgr = Logger.getLogger(CopyTo.class.getName()); lgr.log(Level.SEVERE, ex.getMessage(), ex); } finally { try { if (rs != null) { rs.close(); } if (pst != null) { pst.close(); } if (con != null) { con.close(); } if (fw != null) { fw.close(); } } catch (SQLException | IOException ex) { Logger lgr = Logger.getLogger(CopyTo.class.getName()); lgr.log(Level.WARNING, ex.getMessage(), ex); }

} } }

In the previous example a simple friends table was created. In the above code, we will copy the friends table to a file.
CopyManager cm = new CopyManager((BaseConnection) con);

Here we create an instance of the CopyManager. The CopyManager is the API for PostgreSQL COPY bulk data transfer.
fw = new FileWriter("friends.txt");

We will write the data to the friends.txt file.


cm.copyOut("COPY friends TO STDOUT WITH DELIMITER AS '|'", fw);

208

We pass the results of a COPY TO STDOUT query from database into a Writer using the copyOut() method. The columns will be delimited with the | character.
$ javac -cp .:lib/postgresql.jdbc4.jar zetcode/CopyTo.java $ java -cp .:lib/postgresql.jdbc4.jar zetcode.CopyTo $ cat friends.txt 1|Jane 2|Tom 3|Rebecca 4|Jim 5|Robert

This is the file created.

In the second example, we do the reverse operation. We copy the data from the file into the database table.
package zetcode; import import import import import import import import import import import java.io.FileReader; java.io.IOException; java.sql.PreparedStatement; java.sql.Connection; java.sql.DriverManager; java.sql.ResultSet; java.sql.SQLException; java.util.logging.Level; java.util.logging.Logger; org.postgresql.copy.CopyManager; org.postgresql.core.BaseConnection;

public class CopyFrom { public static void main(String[] args) { Connection con = null; PreparedStatement pst = null; ResultSet rs = null; FileReader fr = null; String url = "jdbc:postgresql://localhost/testdb"; String user = "user12"; String password = "34klq*"; try { con = DriverManager.getConnection(url, user, password); CopyManager cm = new CopyManager((BaseConnection) con); fr = new FileReader("friends.txt"); cm.copyIn("COPY friends FROM STDIN WITH DELIMITER '|'", fr); } catch (SQLException | IOException ex) { Logger lgr = Logger.getLogger(CopyFrom.class.getName()); lgr.log(Level.SEVERE, ex.getMessage(), ex); } finally {

209

try { if (rs != null) { rs.close(); } if (pst != null) { pst.close(); } if (con != null) { con.close(); } if (fr != null) { fr.close(); } } catch (SQLException | IOException ex) { Logger lgr = Logger.getLogger(CopyFrom.class.getName()); lgr.log(Level.WARNING, ex.getMessage(), ex); }

} } }

The example uses the FileReader class to read the contents of the friends.txt table and the COPY statement to transfer the data to the friends class.
fr = new FileReader("friends.txt");

We will read from the friends.txt file.


cm.copyIn("COPY friends FROM STDIN WITH DELIMITER '|'", fr);

We copy the data from the file using the COPY statement.
testdb=# DELETE FROM friends; DELETE 5 $ javac -cp .:lib/postgresql.jdbc4.jar zetcode/CopyFrom.java $ java -cp .:lib/postgresql.jdbc4.jar zetcode.CopyFrom testdb=# SELECT * FROM friends; id | name ----+--------1 | Jane 2 | Tom 3 | Rebecca 4 | Jim 5 | Robert (5 rows)

First we delete the rows from the friends table. Then we compile and run the CopyFrom application. Finally, we check the contents of the friends table. This was the PostgreSQL Java tutorial.

210

Java Swing tutorial


This is a Java Swing tutorial. The Java Swing tutorial is suited for beginners and intermediate Swing developers. After reading this tutorial, you will be able to develop non-trivial Java Swing applications.

Swing
Swing is a principal GUI toolkit for the Java programming language. It is a part of the JFC (Java Foundation Classes), which is an API for providing a graphical user interface for Java programs. It is completely written in Java.

Introduction Java Swing


This is an introductory Swing tutorial. The purpose of this tutorial is to get you started with the Java Swing toolkit. The tutorial has been created and tested on Linux.

About Swing
Swing library is an official Java GUI toolkit released by Sun Microsystems. It is used to create Graphical user interfaces with Java. The main characteristics of the Swing toolkit

platform independent customizable extensible configurable lightweight

The Swing API has 18 public packages:


javax.accessibility javax.swing javax.swing.border javax.swing.colorchooser javax.swing.event javax.swing.filechooser javax.swing.plaf javax.swing.plaf.basic javax.swing.plaf.metal javax.swing.plaf.multi javax.swing.plaf.synth javax.swing.table javax.swing.text javax.swing.text.html

211

javax.swing.text.html.parser javax.swing.text.rtf javax.swing.tree javax.swing.undo

Swing is an advanced GUI toolkit. It has a rich set of widgets. From basic widgets like buttons, labels, scrollbars to advanced widgets like trees and tables. Swing itself is written in Java. Swing is a part of JFC, Java Foundation Classes. It is a collection of packages for creating full featured desktop applications. JFC consists of AWT, Swing, Accessibility, Java 2D, and Drag and Drop. Swing was released in 1997 with JDK 1.2. It is a mature toolkit. The Java platform has Java2D library, which enables developers to create advanced 2D graphics and imaging. There are basically two types of widget toolkits.

Lightweight Heavyweight

A heavyweight toolkit uses OS's API to draw the widgets. For example Borland's VCL is a heavyweight toolkit. It depends on WIN32 API, the built in Windows application programming interface. On Unix systems, we have GTK+ toolkit, which is built on top of X11 library. Swing is a lightweight toolkit. It paints its own widgets. Similarly does the Qt4 toolkit.

SWT library
There is also another GUI library for the Java programming language. It is called SWT. The Standard widget toolkit. The SWT library was initially developed by the IBM corporation. Now it is an open source project maintained by the Eclipse community. The SWT is an example of a heavyweight toolkit. It lets the underlying OS to create GUI. SWT uses the java native interface to do the job. There is a tutorial dedicated to SWT on ZetCode. This was an introduction to Java Swing.

Java Swing first programs


In this chapter, we will program our first programs in Swing toolkit. The examples are going to be very simple. We will cover some basic functionality.

Our first example


In our first example, we will show a basic window on the screen.
package com.zetcode; import javax.swing.JFrame;

212

import javax.swing.SwingUtilities; public class SimpleExample extends JFrame { public SimpleExample() { setTitle("Simple example"); setSize(300, 200); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { SimpleExample ex = new SimpleExample(); ex.setVisible(true); } }); } }

While this code is very short, the application window can do quite a lot. It can be resized, maximized, minimized. All the complexity that comes with it has been hidden from the application programmer.
import javax.swing.JFrame; import javax.swing.SwingUtilities;

Here we import Swing classes that will be used in the code example.
public class Example extends JFrame {

The Example class inherits from the JFrame widget. JFrame is a toplevel container. In the container, we put other widgets.
setTitle("Simple example");

Here we set the title of the window using the setTitle() method.
setSize(300, 200);

This code will resize the window to be 300px wide and 200px tall.
setLocationRelativeTo(null);

This line will center the window on the screen.


setDefaultCloseOperation(EXIT_ON_CLOSE);

This method will close the window, if we click on the close button of the titlebar. By default nothing happens.

213

SwingUtilities.invokeLater(new Runnable() { @Override public void run() { SimpleExample ex = new SimpleExample(); ex.setVisible(true); } });

We create an instance of our code example and make it visible on the screen. The invokeLater() method places the application on the Swing Event Queue. It is used to ensure that all UI updates are concurrency-safe. In other words, it is to prevent GUI from hanging in certain situations. This topic is an advanced concept and we should not worry right now about it.

Figure: Simple example

Quit button
In our next example, we will have a button. When we click on the button, the application terminates.
package com.zetcode; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import import import import javax.swing.JButton; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities;

public class QuitButtonExample extends JFrame { public QuitButtonExample() { } initUI();

private void initUI() { JPanel panel = new JPanel(); getContentPane().add(panel); panel.setLayout(null);

214

JButton quitButton = new JButton("Quit"); quitButton.setBounds(50, 60, 80, 30); quitButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent event) { System.exit(0); } }); panel.add(quitButton); setTitle("Quit button"); setSize(300, 200); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE);

public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { QuitButtonExample ex = new QuitButtonExample(); ex.setVisible(true); } }); } }

We position a JButton on the window. We will add an action listener to this button.
public QuitButtonExample() { } initUI();

It is a good programming practice to put the code that creates the GUI inside a specific method.
JPanel panel = new JPanel(); getContentPane().add(panel);

We create a JPanel component. It is a generic lightweight container. We add the JPanel to the JFrame.
panel.setLayout(null);

By default, the JPanel has a FlowLayout manager. The layout manager is used to place widgets onto the containers. If we call setLayout(null) we can position our components absolutely. For this, we use the setBounds() method.
JButton quitButton = new JButton("Quit"); quitButton.setBounds(50, 60, 80, 30);

Here we create a button. We position it by calling the setBounds() method. 215

quitButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent event) { System.exit(0); } });

We add an action listener. The action listener will be called, when we perform an action on the button. In our case, if we click on the button. The click will terminate the application.
panel.add(quitButton);

In order to show the quit button, we must add it to the panel.

Figure: Quit button

A tooltip
Tooltips are part of the internal application's help system. The Swing shows a small rectangular window, if we hover a mouse pointer over an object.
package com.zetcode; import import import import javax.swing.JButton; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities;

public class TooltipExample extends JFrame { public TooltipExample() { initUI(); } private void initUI() { JPanel panel = new JPanel(); getContentPane().add(panel); panel.setLayout(null); panel.setToolTipText("A Panel container"); JButton btn = new JButton("Button");

216

btn.setBounds(100, 60, 100, 30); btn.setToolTipText("A Button component"); panel.add(btn); setTitle("Tooltip"); setSize(300, 200); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE);

public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { TooltipExample ex = new TooltipExample(); ex.setVisible(true); } }); } }

In the example, we set the tooltip for the frame and the button.
panel.setToolTipText("A Panel container");

To enable a tooltip, we call the setTooltipText() method.

Figure: Tooltip In this chapter, we have created some simple Java Swing programs.

Menus and toolbars in Java Swing


A menubar is one of the most visible parts of the GUI application. It is a group of commands located in various menus. While in console applications you had to remember all those arcane commands, here we have most of the commands grouped into logical parts. There are accepted standards that further reduce the amount of time spending to learn a new application.

217

In Java Swing, to implement a menubar, we use three objects. A JMenuBar, a JMenu and a JMenuItem.

Simple menu
We begin with a simple menubar example.
package com.zetcode; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import import import import import import javax.swing.ImageIcon; javax.swing.JFrame; javax.swing.JMenu; javax.swing.JMenuBar; javax.swing.JMenuItem; javax.swing.SwingUtilities;

public class Example extends JFrame { public Example() { initUI(); } public final void initUI() { JMenuBar menubar = new JMenuBar(); ImageIcon icon = new ImageIcon(getClass().getResource("exit.png")); JMenu file = new JMenu("File"); file.setMnemonic(KeyEvent.VK_F); JMenuItem eMenuItem = new JMenuItem("Exit", icon); eMenuItem.setMnemonic(KeyEvent.VK_E); eMenuItem.setToolTipText("Exit application"); eMenuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { System.exit(0); } }); file.add(eMenuItem); menubar.add(file); setJMenuBar(menubar); setTitle("Simple menu"); setSize(300, 200); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() {

218

Example ex = new Example(); ex.setVisible(true); }); } } }

Our example will show a menu with one item. Selecting the exit menu item we close the application.
JMenuBar menubar = new JMenuBar();

Here we create a menubar.


ImageIcon icon = new ImageIcon(getClass().getResource("exit.png"));

We will display an icon in the menu.


JMenu file = new JMenu("File"); file.setMnemonic(KeyEvent.VK_F);

We create a menu object. The menus can be accessed via the keybord as well. To bind a menu to a particular key, we use the setMnemonic() method. In our case, the menu can be opened with the ALT + F shortcut.
eMenuItem.setToolTipText("Exit application");

This code line creates a tooltip for a menu item.

Figure: Simple menu

Submenu
Each menu can also have a submenu. This way we can group similar commnads into groups. For example we can place commands that hide/show various toolbars like personal bar, address bar, status bar or navigation bar into a submenu called toolbars. Within a menu, we can seperate commands with a separator. It is a simple line. It is common practice to separate commands like new, open, save from commands like print, print preview with a single separator. Menus commands can be launched via keyboard shortcuts. For this, we define menu item accelerators.

219

package com.zetcode; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import import import import import import import javax.swing.ImageIcon; javax.swing.JFrame; javax.swing.JMenu; javax.swing.JMenuBar; javax.swing.JMenuItem; javax.swing.KeyStroke; javax.swing.SwingUtilities;

public class Example extends JFrame { public Example() { initUI(); } public final void initUI() { JMenuBar menubar = new JMenuBar(); ImageIcon iconNew = new ImageIcon(getClass().getResource("new.png")); ImageIcon iconOpen = new ImageIcon(getClass().getResource("open.png")); ImageIcon iconSave = new ImageIcon(getClass().getResource("save.png")); ImageIcon iconExit = new ImageIcon(getClass().getResource("exit.png")); JMenu file = new JMenu("File"); file.setMnemonic(KeyEvent.VK_F); JMenu imp = new JMenu("Import"); imp.setMnemonic(KeyEvent.VK_M); JMenuItem newsf = new JMenuItem("Import newsfeed list..."); JMenuItem bookm = new JMenuItem("Import bookmarks..."); JMenuItem mail = new JMenuItem("Import mail..."); imp.add(newsf); imp.add(bookm); imp.add(mail); JMenuItem fileNew = new JMenuItem("New", iconNew); fileNew.setMnemonic(KeyEvent.VK_N); JMenuItem fileOpen = new JMenuItem("Open", iconOpen); fileNew.setMnemonic(KeyEvent.VK_O); JMenuItem fileSave = new JMenuItem("Save", iconSave); fileSave.setMnemonic(KeyEvent.VK_S); JMenuItem fileExit = new JMenuItem("Exit", iconExit); fileExit.setMnemonic(KeyEvent.VK_C); fileExit.setToolTipText("Exit application"); fileExit.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_W, ActionEvent.CTRL_MASK));

220

fileExit.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { System.exit(0); } }); file.add(fileNew); file.add(fileOpen); file.add(fileSave); file.addSeparator(); file.add(imp); file.addSeparator(); file.add(fileExit); menubar.add(file); setJMenuBar(menubar); setTitle("Submenu"); setSize(360, 250); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { Example ex = new Example(); ex.setVisible(true); } }); } }

In this example, we create a submenu, a menu separator and an accelerator key.


JMenu imp = new JMenu("Import"); ... file.add(imp);

A submenu is just like any other normal menu. It is created the same way. We simply add a menu to existing menu.
fileExit.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_W, ActionEvent.CTRL_MASK));

An accelerator is a key shortcut that launches a menu item. In our case, by pressing Ctrl + W we close the application.
file.addSeparator();

A separator is a horizontal line that visually separates the menu items. This way we can group items into some logical places.

221

Figure: Submenu

JCheckBoxMenuItem
A menu item that can be selected or deselected. If selected, the menu item typically appears with a checkmark next to it. If unselected or deselected, the menu item appears without a checkmark. Like a regular menu item, a check box menu item can have either text or a graphic icon associated with it, or both.
package com.zetcode; import import import import import import import import import import import import java.awt.BorderLayout; java.awt.event.ActionEvent; java.awt.event.ActionListener; java.awt.event.KeyEvent; javax.swing.BorderFactory; javax.swing.JCheckBoxMenuItem; javax.swing.JFrame; javax.swing.JLabel; javax.swing.JMenu; javax.swing.JMenuBar; javax.swing.SwingUtilities; javax.swing.border.EtchedBorder;

public class Example extends JFrame { private JLabel statusbar; public Example() { initUI(); } public final void initUI() { JMenuBar menubar = new JMenuBar(); JMenu file = new JMenu("File"); file.setMnemonic(KeyEvent.VK_F); JMenu view = new JMenu("View"); view.setMnemonic(KeyEvent.VK_V);

222

JCheckBoxMenuItem sbar = new JCheckBoxMenuItem("Show StatuBar"); sbar.setState(true); sbar.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { if (statusbar.isVisible()) { statusbar.setVisible(false); } else { statusbar.setVisible(true); } } }); view.add(sbar); menubar.add(file); menubar.add(view); setJMenuBar(menubar); statusbar = new JLabel(" Statusbar"); statusbar.setBorder(BorderFactory.createEtchedBorder( EtchedBorder.RAISED)); add(statusbar, BorderLayout.SOUTH); setTitle("JCheckBoxMenuItem"); setSize(360, 250); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE);

public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { Example ex = new Example(); ex.setVisible(true); } }); }

The example shows a JCheckBoxMenuItem. By selecting the menu item, we toggle the visibility of the statusbar.
JCheckBoxMenuItem sbar = new JCheckBoxMenuItem("Show StatuBar"); sbar.setState(true);

We create the JCheckBoxMenuItem and check it by default. The statusbar is initially visible.
if (statusbar.isVisible()) { statusbar.setVisible(false); } else { statusbar.setVisible(true); }

Here we toggle the visibility of the statusbar.

223

statusbar = new JLabel(" Statusbar"); statusbar.setBorder(BorderFactory.createEtchedBorder( EtchedBorder.RAISED));

The statusbar is a simple JLabel component. We put a raised EtchedBorder around the label, so that it is visible.

Figure: JCheckBoxMenuItem

A popup menu
Another type of a menu is a popup menu. It is sometimes called a context menu. It is usually shown, when we right click on a component. The idea is to provide only the commands, that are relevant to the current context. Say we have an image. By right clicking on the image, we get a window with commands to save, rescale, move etc the image.
package com.zetcode; import import import import import import import import import java.awt.Toolkit; java.awt.event.ActionEvent; java.awt.event.ActionListener; java.awt.event.MouseAdapter; java.awt.event.MouseEvent; javax.swing.JFrame; javax.swing.JMenuItem; javax.swing.JPopupMenu; javax.swing.SwingUtilities;

public class PopupMenu extends JFrame { private JPopupMenu menu; private Toolkit toolkit; public PopupMenu(String title) { super(title); this.initUI(); } private void initUI() { toolkit = this.getToolkit();

224

menu = new JPopupMenu(); JMenuItem menuItemBeep = new JMenuItem("Beep"); menuItemBeep.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { toolkit.beep(); } }); menu.add(menuItemBeep); JMenuItem menuItemClose = new JMenuItem("Close"); menuItemClose.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { System.exit(0); } }); menu.add(menuItemClose); this.addMouseListener(new MouseAdapter() { @Override public void mouseReleased(MouseEvent e) { if (e.getButton() == MouseEvent.BUTTON3) { menu.show(e.getComponent(), e.getX(), e.getY()); } } }); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setSize(250, 200); this.setLocationRelativeTo(null); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { PopupMenu pm = new PopupMenu("JPopupMenu"); pm.setVisible(true); } }); } }

Our example shows a demonstrational popup menu with two commands. The first option of the popup menu will beep a sound, the second one will close the window. In our example, we create a submenu, menu separators and create an accelerator key.
menu = new JPopupMenu();

To create a popup menu, we have a class called JPopupMenu.


JMenuItem menuItemBeep = new JMenuItem("Beep");

The menu item is the same, as with the standard JMenu


this.addMouseListener(new MouseAdapter() {

225

@Override public void mouseReleased(MouseEvent e) { if (e.getButton() == MouseEvent.BUTTON3) { menu.show(e.getComponent(), e.getX(), e.getY()); } } });

The popup menu is shown, where we clicked with the mouse button. The MouseEvent.BUTTON3 constant is here to enable the popup menu only for the mouse right click.

Figure: Popup menu

JToolbar
Menus group commands that we can use in an application. Toolbars provide a quick access to the most frequently used commands. In Java Swing, the JToolBar class creates a toolbar in an application.
package com.zetcode; import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import import import import import import import javax.swing.ImageIcon; javax.swing.JButton; javax.swing.JFrame; javax.swing.JMenu; javax.swing.JMenuBar; javax.swing.JToolBar; javax.swing.SwingUtilities;

public class Example extends JFrame { public Example() { initUI(); } public final void initUI() { JMenuBar menubar = new JMenuBar(); JMenu file = new JMenu("File");

226

menubar.add(file); setJMenuBar(menubar); JToolBar toolbar = new JToolBar(); ImageIcon icon = new ImageIcon(getClass().getResource("exit.png")); JButton exitButton = new JButton(icon); toolbar.add(exitButton); exitButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { System.exit(0); } }); add(toolbar, BorderLayout.NORTH); setTitle("Simple toolbar"); setSize(300, 200); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { Example ex = new Example(); ex.setVisible(true); } }); } }

The example creates a toolbar with one exit button.


JToolBar toolbar = new JToolBar();

This is the JToolBar constructor.


JButton exitButton = new JButton(icon); toolbar.add(exitButton);

We create a button and add it to the toolbar.

227

Figure: Simple toolbar

Toolbars
Say, we wanted to create two toolbars. The next example shows, how we could do it.
package com.zetcode; import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import import import import import import import javax.swing.BoxLayout; javax.swing.ImageIcon; javax.swing.JButton; javax.swing.JFrame; javax.swing.JPanel; javax.swing.JToolBar; javax.swing.SwingUtilities;

public class Example extends JFrame { public Example() { initUI(); } public final void initUI() { JToolBar toolbar1 = new JToolBar(); JToolBar toolbar2 = new JToolBar(); JPanel panel = new JPanel(); panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); ImageIcon newi = new ImageIcon( getClass().getResource("new.png")); ImageIcon open = new ImageIcon( getClass().getResource("open.png")); ImageIcon save = new ImageIcon( getClass().getResource("save.png")); ImageIcon exit = new ImageIcon( getClass().getResource("exit.png")); JButton newb = new JButton(newi); JButton openb = new JButton(open); JButton saveb = new JButton(save); toolbar1.add(newb); toolbar1.add(openb); toolbar1.add(saveb); toolbar1.setAlignmentX(0); JButton exitb = new JButton(exit); toolbar2.add(exitb); toolbar2.setAlignmentX(0); exitb.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { System.exit(0);

228

} }); panel.add(toolbar1); panel.add(toolbar2); add(panel, BorderLayout.NORTH); setTitle("Toolbars"); setSize(360, 250); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE);

public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { Example ex = new Example(); ex.setVisible(true); } }); }

We show only one way, how we could create toolbars. Of course, there are several possibilities. We put a JPanel to the north of the BorderLayout manager. The panel has a vertical BoxLayout. We add the two toolbars into this panel.
JToolBar toolbar1 = new JToolBar(); JToolBar toolbar2 = new JToolBar();

Creation of two toolbars.


JPanel panel = new JPanel(); panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));

The panel has a vertical BoxLayout.


toolbar1.setAlignmentX(0);

The toolbar is left aligned.


panel.add(toolbar1); panel.add(toolbar2); add(panel, BorderLayout.NORTH);

We add the toolbars to the panel. Finally, the panel is located into the north part of the frame.

229

Figure: Toolbars In this part of the Java Swing tutorial, we have mentioned menus and toolbars.

The Swing layout management


The Java Swing toolkit has two kind of components. Containers and children. The containers group children into suitable layouts. To create layouts, we use layout managers. Layout managers are one of the most difficult parts of modern GUI programming. Many beginning programmers have too much respect for layout managers. Mainly because they are usually poorly documented. I believe, that GUI builder's like Matisse cannot replace the proper understanding of layout managers. ZetCode offers a dedicated 196 pages e-book for the Swing layout management process: Java Swing layout management tutorial

No manager
We can use no layout manager, if we want. There might be situations, where we might not need a layout manager. For example, in my code examples, I often go without a manager. It is because I did not want to make the examples too complex. But to create truly portable, complex applications, we need layout managers. Without layout manager, we position components using absolute values.
package zetcode; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.SwingUtilities; public class AbsoluteExample extends JFrame { public AbsoluteExample() { initUI();

230

} public final void initUI() { setLayout(null); JButton ok = new JButton("OK"); ok.setBounds(50, 50, 80, 25); JButton close = new JButton("Close"); close.setBounds(150, 50, 80, 25); add(ok); add(close); setTitle("Absolute positioning"); setSize(300, 250); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null);

public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { AbsoluteExample ex = new AbsoluteExample(); ex.setVisible(true); }

}); } }

This simple example shows two buttons.


setLayout(null);

We use absolute positioning by providing null to the setLayout() method.


ok.setBounds(50, 50, 80, 25);

The setBounds() method positions the ok button. The parameters are the x, y location values and the width and height of the component.

FlowLayout manager
This is the simplest layout manager in the Java Swing toolkit. It is mainly used in combination with other layout managers. When calculating its children size, a flow layout lets each component assume its natural (preferred) size. The manager puts components into a row. In the order, they were added. If they do not fit into one row, they go into the next one. The components can be added from the right to the left or vice versa. The manager allows to align the components. Implicitly, the components are centered and there is 5px space among components and components and the edges of the container.

231

FlowLayout() FlowLayout(int align) FlowLayout(int align, int hgap, int vgap)

There are three constructors available for the FlowLayout manager. The first one creates a manager with implicit values. Centered with 5px horizontal and vertical spaces. The others allow to specify those parametes.
package zetcode; import java.awt.Dimension; import import import import import import javax.swing.JButton; javax.swing.JFrame; javax.swing.JPanel; javax.swing.JTextArea; javax.swing.JTree; javax.swing.SwingUtilities;

public class FlowLayoutExample extends JFrame { public FlowLayoutExample() { } initUI();

public final void initUI() { JPanel panel = new JPanel(); JTextArea area = new JTextArea("text area"); area.setPreferredSize(new Dimension(100, 100)); JButton button = new JButton("button"); panel.add(button); JTree tree = new JTree(); panel.add(tree); panel.add(area); add(panel); pack(); setTitle("FlowLayout Example"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null);

public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { FlowLayoutExample ex = new FlowLayoutExample(); ex.setVisible(true);

232

}); } }

The example shows a button, a tree and a text area component in the window. Interesingly, if we create an empty tree component, there are some default values inside the component.
JPanel panel = new JPanel();

The implicit layout manager of the JPanel component is a flow layout manager. We do not have to set it manually.
JTextArea area = new JTextArea("text area"); area.setPreferredSize(new Dimension(100, 100));

The flow layout manager sets a preferred size for it's components. This means, that in our case, the area component will have 100x100 px. If we didn't set the preferred size, the component would have a size of its text. No more, no less. Without the text, the component would not be visible at all. Try to write or delete some text in the area component. The component will grow and shrink accordingly.
panel.add(area);

To put a component inside a container, we simply call the add() method.

Figure: FlowLaout manager

GridLayout
The GridLayout layout manager lays out components in a rectangular grid. The container is divided into equally sized rectangles. One component is placed in each rectangle.
package zetcode; import java.awt.GridLayout; import import import import import import javax.swing.BorderFactory; javax.swing.JButton; javax.swing.JFrame; javax.swing.JLabel; javax.swing.JPanel; javax.swing.SwingUtilities;

public class GridLayoutExample extends JFrame {

233

public GridLayoutExample() { initUI(); } public final void initUI() { JPanel panel = new JPanel(); panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); panel.setLayout(new GridLayout(5, 4, 5, 5)); String[] buttons = { "Cls", "Bck", "", "Close", "7", "8", "9", "/", "4", "5", "6", "*", "1", "2", "3", "-", "0", ".", "=", "+" }; for (int i = 0; i < buttons.length; i++) { if (i == 2) panel.add(new JLabel(buttons[i])); else panel.add(new JButton(buttons[i]));

add(panel); setTitle("GridLayout"); setSize(350, 300); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null);

public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { GridLayoutExample ex = new GridLayoutExample(); ex.setVisible(true);

} } } });

The example shows a skeleton of a simple calculator tool. It is an ideal example for this layout manager. We put 19 buttons and one label into the manager. Notice, that each button is of the same size.
panel.setLayout(new GridLayout(5, 4, 5, 5));

Here we set the grid layout manager for the panel component. The layout manager takes four parameters. The number of rows, the number of columns and the horizontal and vertical gaps between components.

234

Figure: GridLayout manager

BorderLayout
A BorderLayout manager is a very handy layout manager. I haven't seen it elsewhere. It divides the space into five regions. North, West, South, East and Centre. Each region can have only one component. If we need to put more components into a region, we can simply put a panel there with a manager of our choice. The components in N, W, S, E regions get their preferred size. The component in the centre takes up the whole space left. It does not look good, if child components are too close to each other. We must put some space among them. Each component in Swing toolkit can have borders around it's edges. To create a border, we either create a new instance of an EmptyBorder class or we use a BorderFactory. Except for EmptyBorder, there are other types of borders as well. But for layout management we will use only this one.
package zetcode; import import import import import import import import java.awt.BorderLayout; java.awt.Color; java.awt.Dimension; java.awt.Insets; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities; javax.swing.border.EmptyBorder;

public class BorderExample extends JFrame { public BorderExample() { } initUI();

public final void initUI() {

235

JPanel panel = new JPanel(new BorderLayout()); JPanel top = new JPanel(); top.setBackground(Color.gray); top.setPreferredSize(new Dimension(250, 150)); panel.add(top); panel.setBorder(new EmptyBorder(new Insets(20, 20, 20, 20))); add(panel); pack(); setTitle("Border Example"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null);

public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { BorderExample ex = new BorderExample(); ex.setVisible(true);

} } } });

The example will display a gray panel and border around it.
JPanel panel = new JPanel(new BorderLayout()); JPanel top = new JPanel();

We place a panel into a panel. We used a BorderLayout manager for the first panel, because this manager will resize its children.
panel.add(top);

Here we placed a top panel into the panel component. More precisely, we placed into the center area of the BorderLayout manager.
panel.setBorder(new EmptyBorder(new Insets(20, 20, 20, 20)));

Here we created a 20px border around the panel. The border values are as follows: top, left, bottom and right. They go counterclockwise.

236

Figure: Border example The next example will show a typical usage of a border layout manager.
package zetcode; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.Insets; import import import import import import import import import import import javax.swing.ImageIcon; javax.swing.JButton; javax.swing.JFrame; javax.swing.JLabel; javax.swing.JMenu; javax.swing.JMenuBar; javax.swing.JTextArea; javax.swing.JToolBar; javax.swing.SwingUtilities; javax.swing.border.EmptyBorder; javax.swing.border.LineBorder;

public class BorderLayoutExample extends JFrame { public BorderLayoutExample() { initUI(); } public final void initUI() { JMenuBar menubar = new JMenuBar(); JMenu file = new JMenu("File"); menubar.add(file); setJMenuBar(menubar); JToolBar toolbar = new JToolBar(); toolbar.setFloatable(false); ImageIcon exit = new ImageIcon("exit.png"); JButton bexit = new JButton(exit); bexit.setBorder(new EmptyBorder(0 ,0, 0, 0)); toolbar.add(bexit);

237

add(toolbar, BorderLayout.NORTH); JToolBar vertical = new JToolBar(JToolBar.VERTICAL); vertical.setFloatable(false); vertical.setMargin(new Insets(10, 5, 5, 5)); ImageIcon select = new ImageIcon("drive.png"); ImageIcon freehand = new ImageIcon("computer.png"); ImageIcon shapeed = new ImageIcon("printer.png"); JButton selectb = new JButton(select); selectb.setBorder(new EmptyBorder(3, 0, 3, 0)); JButton freehandb = new JButton(freehand); freehandb.setBorder(new EmptyBorder(3, 0, 3, 0)); JButton shapeedb = new JButton(shapeed); shapeedb.setBorder(new EmptyBorder(3, 0, 3, 0)); vertical.add(selectb); vertical.add(freehandb); vertical.add(shapeedb); add(vertical, BorderLayout.WEST); add(new JTextArea(), BorderLayout.CENTER); JLabel statusbar = new JLabel(" Statusbar"); statusbar.setPreferredSize(new Dimension(-1, 22)); statusbar.setBorder(LineBorder.createGrayLineBorder()); add(statusbar, BorderLayout.SOUTH); setSize(350, 300); setTitle("BorderLayout"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { BorderLayoutExample ex = new BorderLayoutExample(); ex.setVisible(true); } } } });

The example shows a typical application skeleton. We show a vertical and horizontal toolbars, a statusbar and a central component. (a text area) A default layout manager for a JFrame component is BorderLayout manager. So we don't have to set it.
add(toolbar, BorderLayout.NORTH);

Simply put the toolbar to the north of the layout. 238

add(vertical, BorderLayout.WEST);

Put the vertical toobar to the west.


add(new JTextArea(), BorderLayout.CENTER);

Put the text area to the center.


add(statusbar, BorderLayout.SOUTH);

Put the statusbar to the south.

Figure: BorderLayout manager

BoxLayout
BoxLayout is a powerful manager, that can be used to create sofisticated layouts. This layout manager puts components into a row or into a column. It enables nesting, a powerful feature, that makes this manager very flexible. It means, that we can put a box layout into another box layout. The box layout manager is often used with the Box class. This class creates several invisible components, which affect the final layout.

glue strut rigid area

Let's say, we want to put two buttons into the right bottom corner of the window. We will use the boxlayut managers to accomplish this.
package zetcode; import java.awt.Dimension;

239

import import import import import import

javax.swing.Box; javax.swing.BoxLayout; javax.swing.JButton; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities;

public class TwoButtonsExample extends JFrame { public TwoButtonsExample() { } initUI();

public final void initUI() { JPanel basic = new JPanel(); basic.setLayout(new BoxLayout(basic, BoxLayout.Y_AXIS)); add(basic); basic.add(Box.createVerticalGlue()); JPanel bottom = new JPanel(); bottom.setAlignmentX(1f); bottom.setLayout(new BoxLayout(bottom, BoxLayout.X_AXIS)); JButton ok = new JButton("OK"); JButton close = new JButton("Close"); bottom.add(ok); bottom.add(Box.createRigidArea(new Dimension(5, 0))); bottom.add(close); bottom.add(Box.createRigidArea(new Dimension(15, 0))); basic.add(bottom); basic.add(Box.createRigidArea(new Dimension(0, 15))); setTitle("Two Buttons"); setSize(300, 150); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { TwoButtonsExample ex = new TwoButtonsExample(); ex.setVisible(true); } } } });

The following drawing illustrates the example.

240

Figure: Two buttons We will create two panels. The basic panel has a vertical box layout. The bottom panel has a horizontal one. We will put a bottom panel into the basic panel. We will right align the bottom panel. The space between the top of the window and the bottom panel is expandable. It is done by the vertical glue.
basic.setLayout(new BoxLayout(basic, BoxLayout.Y_AXIS));

Here we create a basic panel with the vertical BoxLayout.


JPanel bottom = new JPanel(); bottom.setAlignmentX(1f); bottom.setLayout(new BoxLayout(bottom, BoxLayout.X_AXIS));

The bottom panel is right aligned. This is done by the setAlignmentX() method. The panel has a horizontal layout.
bottom.add(Box.createRigidArea(new Dimension(5, 0)));

We put some rigid space between the buttons.


basic.add(bottom);

Here we put the bottom panel with a horizontal box layout to the vertical basic panel.
basic.add(Box.createRigidArea(new Dimension(0, 15)));

We also put some space between the bottom panel and the border of the window.

Figure: Two buttons

241

When we use a BoxLayout manager, we can set a rigid area among our components.
package zetcode; import java.awt.Dimension; import java.awt.Insets; import import import import import import import javax.swing.Box; javax.swing.BoxLayout; javax.swing.JButton; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities; javax.swing.border.EmptyBorder;

public class RigidAreaExample extends JFrame { public RigidAreaExample() { initUI(); } public final void initUI() { JPanel panel = new JPanel(); panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); panel.setBorder(new EmptyBorder(new Insets(40, 60, 40, 60))); panel.add(new JButton("Button")); panel.add(Box.createRigidArea(new Dimension(0, 5))); panel.add(new JButton("Button")); panel.add(Box.createRigidArea(new Dimension(0, 5))); panel.add(new JButton("Button")); panel.add(Box.createRigidArea(new Dimension(0, 5))); panel.add(new JButton("Button")); add(panel); pack(); setTitle("RigidArea"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { RigidAreaExample ex = new RigidAreaExample(); ex.setVisible(true); } } } });

242

In this example, we display four buttons. By default, there is no space among the buttons. To put some space among them, we add some rigid area.
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));

We use a vertical BoxLayout manager for our panel.


panel.add(new JButton("Button")); panel.add(Box.createRigidArea(new Dimension(0, 5))); panel.add(new JButton("Button"));

We add buttons and create a rigid area in between them.

Figure: Rigid area

Tip of the Day


JDeveloper has a dialog window called Tip of the Day.

243

Figure: Tip of the Day We will create a similar dialog. We will use a combination of various layout managers. Namely a border layout, flow layout and box layout manager.
package zetcode; import import import import import import import import import import import import import import import import java.awt.BorderLayout; java.awt.Color; java.awt.Dimension; java.awt.FlowLayout; java.awt.event.KeyEvent; javax.swing.BorderFactory; javax.swing.BoxLayout; javax.swing.ImageIcon; javax.swing.JButton; javax.swing.JCheckBox; javax.swing.JDialog; javax.swing.JLabel; javax.swing.JPanel; javax.swing.JSeparator; javax.swing.JTextPane; javax.swing.SwingUtilities;

public class TipOfDayExample extends JDialog { public TipOfDayExample() { initUI(); } public final void initUI() {

244

JPanel basic = new JPanel(); basic.setLayout(new BoxLayout(basic, BoxLayout.Y_AXIS)); add(basic); JPanel topPanel = new JPanel(new BorderLayout(0, 0)); topPanel.setMaximumSize(new Dimension(450, 0)); JLabel hint = new JLabel("JDeveloper Productivity Hints"); hint.setBorder(BorderFactory.createEmptyBorder(0, 25, 0, 0)); topPanel.add(hint); ImageIcon icon = new ImageIcon("jdev.png"); JLabel label = new JLabel(icon); label.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); topPanel.add(label, BorderLayout.EAST); JSeparator separator = new JSeparator(); separator.setForeground(Color.gray); topPanel.add(separator, BorderLayout.SOUTH); basic.add(topPanel); JPanel textPanel = new JPanel(new BorderLayout()); textPanel.setBorder(BorderFactory.createEmptyBorder(15, 25, 15, JTextPane pane = new JTextPane(); pane.setContentType("text/html"); String text = "<p><b>Closing windows using the mouse wheel</b></p>" + window. " + "<p>Clicking with the mouse wheel on an editor tab closes the

25));

"This method works also with dockable windows or Log window tabs.</p>"; pane.setText(text); pane.setEditable(false); textPanel.add(pane); basic.add(textPanel); JPanel boxPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 20, 0)); JCheckBox box = new JCheckBox("Show Tips at startup"); box.setMnemonic(KeyEvent.VK_S); boxPanel.add(box); basic.add(boxPanel); JPanel bottom = new JPanel(new FlowLayout(FlowLayout.RIGHT)); JButton ntip = new JButton("Next Tip"); ntip.setMnemonic(KeyEvent.VK_N); JButton close = new JButton("Close"); close.setMnemonic(KeyEvent.VK_C); bottom.add(ntip); bottom.add(close); basic.add(bottom);

245

bottom.setMaximumSize(new Dimension(450, 0)); setTitle("Tip of the Day"); setSize(new Dimension(450, 350)); setResizable(false); setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); setLocationRelativeTo(null);

public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { TipOfDayExample ex = new TipOfDayExample(); ex.setVisible(true); } } } });

The example uses a mix of layout managers. Simply we put four panels into the vertically organized basic panel.
JPanel basic = new JPanel(); basic.setLayout(new BoxLayout(basic, BoxLayout.Y_AXIS)); add(basic);

This is the very bottom panel. It has a vertical box layout manager. The basic panel is added to the default JDialog component. This component has a border layout manager by default.
JPanel topPanel = new JPanel(new BorderLayout(0, 0));

The topPanel panel has a border layout manager. We will put three components into it. Two labels and a separator.
topPanel.setMaximumSize(new Dimension(450, 0));

If we want to have a panel, that is not greater than it's components, we must set it's maximum size. The zero value is ignored. The manager calculates the necessary heights.
JPanel textPanel = new JPanel(new BorderLayout()); ... textPanel.add(pane);

The text pane component is added to the center area of the border layout manager. It takes all space left. Exactly, as we wanted.
JPanel boxPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 20, 0));

The check box is shown in the boxPanel panel. It is left aligned. The flow layout manager has a 20px horizontal gap. Other components have 25px. Why is that? It is because the flow layout manager puts some space to between the component and the edge as well.

246

JPanel bottom = new JPanel(new FlowLayout(FlowLayout.RIGHT)); ... bottom.setMaximumSize(new Dimension(450, 0));

The bottom panel displays two buttons. It has a right aligned flow layout manager. In order to show the buttons on the right edge of the dialog, the panel must stretch horizontally from the beginning to the end.

Figure: Tip of the Day In this chapter, we have mentioned layout management in Swing.

Java Swing Events


Events are an important part in any GUI program. All GUI applications are event-driven. An application reacts to different event types which are generated during its life. Events are generated mainly by the user of an application. But they can be generated by other means as well. e.g. internet connection, window manager, timer. In the event model, there are three participants:

event source event object event listener

The Event source is the object whose state changes. It generates Events. The Event object (Event) encapsulates the state changes in the event source. The Event listener is the object that wants to be notified. Event source object delegates the task of handling an event to the event listener.

247

Event handling in Java Swing toolkit is very powerful and flexible. Java uses Event Delegation Model. We specify the objects that are to be notified when a specific event occurs.

An event object
When something happens in the application, an event object is created. For example, when we click on the button or select an item from a list. There are several types of events. An ActionEvent, TextEvent, FocusEvent, ComponentEvent etc. Each of them is created under specific conditions. Event object has information about an event, that has happened. In the next example, we will analyze an ActionEvent in more detail.
package zetcode; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import import import import import import import import import java.text.DateFormat; java.util.Date; java.util.Locale; javax.swing.DefaultListModel; javax.swing.JButton; javax.swing.JFrame; javax.swing.JList; javax.swing.JPanel; javax.swing.SwingUtilities;

public class Example extends JFrame implements ActionListener { private JList list; private DefaultListModel model; public Example() { initUI(); } public final void initUI() { JPanel panel = new JPanel(); panel.setLayout(null); model = new DefaultListModel(); list = new JList(model); list.setBounds(150, 30, 220, 150); JButton okButton = new JButton("Ok"); okButton.setBounds(30, 35, 80, 25); okButton.addActionListener(this); panel.add(okButton); panel.add(list); add(panel); setTitle("Event object"); setSize(420, 250);

248

setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE); } public void actionPerformed(ActionEvent e) { Locale locale = Locale.getDefault(); Date date = new Date(e.getWhen()); String s = DateFormat.getTimeInstance(DateFormat.SHORT, locale).format(date); if (!model.isEmpty()) { model.clear(); } if (e.getID() == ActionEvent.ACTION_PERFORMED) { model.addElement(" Event Id: ACTION_PERFORMED"); } model.addElement(" Time: " + s); String source = e.getSource().getClass().getName(); model.addElement(" Source: " + source); int mod = e.getModifiers(); StringBuffer buffer = new StringBuffer(" Modifiers: "); if ((mod & ActionEvent.ALT_MASK) > 0) { buffer.append("Alt "); } if ((mod & ActionEvent.SHIFT_MASK) > 0) { buffer.append("Shift "); } if ((mod & ActionEvent.META_MASK) > 0) { buffer.append("Meta "); } if ((mod & ActionEvent.CTRL_MASK) > 0) { buffer.append("Ctrl "); } } model.addElement(buffer);

public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { Example ex = new Example(); ex.setVisible(true); } } } });

The code example shows a button and a list. If we click on the button, information about the event is displayed in the list. In our case, we are talking about an ActionEvent class. The

249

data will be the time, when the event occurred, the id of the event, the event source and the modifier keys.
public void actionPerformed(ActionEvent event) {

The ActionEvent is an instance of the event, that has occurred.


Locale locale = Locale.getDefault(); Date date = new Date(e.getWhen()); String s = DateFormat.getTimeInstance(DateFormat.SHORT, locale).format(date);

Here we get the time, when the event occurred. The getWhen() method returns time value in milliseconds. So we must format it appropriately.
String source = event.getSource().getClass().getName(); model.addElement(" Source: " + source);

Here we add the name of the source of the event to the list. In our case the source is a JButton.
int mod = event.getModifiers();

We get the modifier keys. It is a bitwise-or of the modifier constants.


if ((mod & ActionEvent.SHIFT_MASK) > 0) buffer.append("Shift ");

Here we determine, whether we have pressed a Shift key.

Figure: Event Object

Implementation
There are several ways, how we can implement event handling in Java Swing toolkit.

Anonymous inner class Inner class 250

Derived class

Anonymous inner class


We will illustrate these concepts on a simple event example.
package zetcode; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import import import import javax.swing.JButton; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities;

public class AnonymousInnerClassExample extends JFrame { public AnonymousInnerClassExample() { initUI(); } public final void initUI() { JPanel panel = new JPanel(); panel.setLayout(null); JButton closeButton = new JButton("Close"); closeButton.setBounds(40, 50, 80, 25); closeButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { System.exit(0); } }); panel.add(closeButton); add(panel); setTitle("Anonymous inner class"); setSize(300, 200); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE);

public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { AnonymousInnerClassExample ex = new AnonymousInnerClassExample(); ex.setVisible(true); } } });

251

In this example, we have a button that closes the window upon clicking.
JButton closeButton = new JButton("Close");

The button is the event source. It will generate events.


closeButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { System.exit(0); } });

Here we register an action listener with the button. This way, the events are sent to the event target. The event target in our case is ActionListener class. In this code, we use an anonymous inner class.

Inner class
Here we implement the example using an inner ActionListener class.
package zetcode; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import import import import javax.swing.JButton; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities;

public class InnerClassExample extends JFrame { public InnerClassExample() { initUI(); } public final void initUI() { JPanel panel = new JPanel(); panel.setLayout(null); JButton closeButton = new JButton("Close"); closeButton.setBounds(40, 50, 80, 25); ButtonCloseListener listener = new ButtonCloseListener(); closeButton.addActionListener(listener); panel.add(closeButton); add(panel); setTitle("Inner class example"); setSize(300, 200); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE);

252

} class ButtonCloseListener implements ActionListener { public void actionPerformed(ActionEvent e) { System.exit(0); }

public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { InnerClassExample ex = new InnerClassExample(); ex.setVisible(true); }

}); } }

We have on close button on the panel. The listener is defined inside an inner class, which has a name.
ButtonCloseListener listener = new ButtonCloseListener(); closeButton.addActionListener(listener);

Here we have a non anonymous inner class.


class ButtonCloseListener implements ActionListener { public void actionPerformed(ActionEvent e) { System.exit(0); }

The button listener is defined here.

A derived class implementing the listener


The following example will derive a class from a component and implement an action listener inside the class.
package zetcode; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import import import import javax.swing.JButton; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities;

public class DerivedClassExample extends JFrame { public DerivedClassExample() {

253

initUI();

public final void initUI() { JPanel panel = new JPanel(); panel.setLayout(null); MyButton closeButton = new MyButton("Close"); closeButton.setBounds(40, 50, 80, 25); panel.add(closeButton); add(panel); setTitle("Derived class"); setSize(300, 200); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE); } class MyButton extends JButton implements ActionListener { public MyButton(String text) { super.setText(text); addActionListener(this); } public void actionPerformed(ActionEvent e) { System.exit(0); } } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { DerivedClassExample ex = new DerivedClassExample(); ex.setVisible(true); } } } });

In this example, we create a MyButton class, which will implement the action listener.
MyButton closeButton = new MyButton("Close");

Here we create the MyButton custom class.


class MyButton extends JButton implements ActionListener {

The MyButton class is extended from the JButton class. It implements the ActionListener interface. This way, the event handling is managed within the MyButton class.
addActionListener(this);

Here we add the action listener to the MyButton class.

254

Multiple sources
A listener can be plugged into several sources. This will be explained in the next example.
package zetcode; import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import import import import import import import javax.swing.BorderFactory; javax.swing.JButton; javax.swing.JFrame; javax.swing.JLabel; javax.swing.JPanel; javax.swing.SwingUtilities; javax.swing.border.EtchedBorder;

public class MultipleSources extends JFrame { JLabel statusbar; public MultipleSources() { } initUI();

public final void initUI() { JPanel panel = new JPanel(); statusbar = new JLabel(" ZetCode"); statusbar.setBorder(BorderFactory.createEtchedBorder( EtchedBorder.RAISED)); panel.setLayout(null); JButton close = new JButton("Close"); close.setBounds(40, 30, 80, 25); close.addActionListener(new ButtonListener()); JButton open = new JButton("Open"); open.setBounds(40, 80, 80, 25); open.addActionListener(new ButtonListener()); JButton find = new JButton("Find"); find.setBounds(40, 130, 80, 25); find.addActionListener(new ButtonListener()); JButton save = new JButton("Save"); save.setBounds(40, 180, 80, 25); save.addActionListener(new ButtonListener()); panel.add(close); panel.add(open); panel.add(find); panel.add(save); add(panel); add(statusbar, BorderLayout.SOUTH);

255

setTitle("Multiple Sources"); setSize(400, 300); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE);

class ButtonListener implements ActionListener { public void actionPerformed(ActionEvent e) { JButton o = (JButton) e.getSource(); String label = o.getText(); statusbar.setText(" " + label + " button clicked"); } } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { MultipleSources ms = new MultipleSources(); ms.setVisible(true); } } } });

We create four buttons and a statusbar. The statusbar will display an informative message upon clicking on the button.
close.addActionListener(new ButtonListener()); ... open.addActionListener(new ButtonListener()); ...

Each button will be registered against a ButtonListener class.


JButton o = (JButton) e.getSource(); String label = o.getText();

Here we determine, which button was pressed.


statusbar.setText(" " + label + " button clicked")

We update the statusbar.

256

Figure: Multiple Sources

Multiple listeners
We can register several listeners for one event.
package zetcode; import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Calendar; import import import import import import import import import import javax.swing.BorderFactory; javax.swing.JButton; javax.swing.JFrame; javax.swing.JLabel; javax.swing.JPanel; javax.swing.JSpinner; javax.swing.SpinnerModel; javax.swing.SpinnerNumberModel; javax.swing.SwingUtilities; javax.swing.border.EtchedBorder;

public class MultipleListeners extends JFrame { private JLabel statusbar; private JSpinner spinner; private static int count = 0; public MultipleListeners() { initUI(); } public final void initUI() { JPanel panel = new JPanel();

257

statusbar = new JLabel("0"); statusbar.setBorder(BorderFactory.createEtchedBorder( EtchedBorder.RAISED)); panel.setLayout(null); JButton add = new JButton("+"); add.setBounds(40, 30, 80, 25); add.addActionListener(new ButtonListener1()); add.addActionListener(new ButtonListener2()); Calendar calendar = Calendar.getInstance(); int currentYear = calendar.get(Calendar.YEAR); SpinnerModel yearModel = new SpinnerNumberModel(currentYear, currentYear - 100, currentYear + 100, 1); spinner = new JSpinner(yearModel); spinner.setEditor(new JSpinner.NumberEditor(spinner, "#")); spinner.setBounds(190, 30, 80, 25); panel.add(add); panel.add(spinner); add(panel); add(statusbar, BorderLayout.SOUTH); setTitle("Multiple Listeners"); setSize(300, 200); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE);

class ButtonListener1 implements ActionListener { public void actionPerformed(ActionEvent e) { Integer val = (Integer) spinner.getValue(); spinner.setValue(++val); }

class ButtonListener2 implements ActionListener { public void actionPerformed(ActionEvent e) { statusbar.setText(Integer.toString(++count)); } } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { MultipleListeners ml = new MultipleListeners(); ml.setVisible(true); } });

258

In this example, we have a button, spinner and a statusbar. We use two button listeners for one event. One click of a button will add one year to the spinner component and update the statusbar. The statusbar will show, how many times we have clicked on the button.
add.addActionListener(new ButtonListener1()); add.addActionListener(new ButtonListener2());

We register two button listeners.


SpinnerModel yearModel = new SpinnerNumberModel(currentYear, currentYear - 100, currentYear + 100, 1); spinner = new JSpinner(yearModel);

Here we create the spinner component. We use a year model for the spinner. The SpinnerNumberModel arguments are initial value, min, max values and the step.
spinner.setEditor(new JSpinner.NumberEditor(spinner, "#"));

We remove the thousands separator.


Integer val = (Integer) spinner.getValue(); spinner.setValue(++val);

Here we increase the year number.

Figure: Multiple Listeners

Removing listeners
The Java Swing toolkit enables us to remove the registered listeners.
package zetcode; import import import import java.awt.event.ActionEvent; java.awt.event.ActionListener; java.awt.event.ItemEvent; java.awt.event.ItemListener;

259

import import import import import import

javax.swing.JButton; javax.swing.JCheckBox; javax.swing.JFrame; javax.swing.JLabel; javax.swing.JPanel; javax.swing.SwingUtilities;

public class RemoveListenerExample extends JFrame { private private private private private JLabel text; JButton add; JCheckBox active; ButtonListener buttonlistener; static int count = 0;

public RemoveListenerExample() { JPanel panel = new JPanel(); panel.setLayout(null); add = new JButton("+"); add.setBounds(40, 30, 80, 25); buttonlistener = new ButtonListener(); active = new JCheckBox("Active listener"); active.setBounds(160, 30, 140, 25); active.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent event) { if (active.isSelected()) { add.addActionListener(buttonlistener); } else { add.removeActionListener(buttonlistener); } }

});

text = new JLabel("0"); text.setBounds(40, 80, 80, 25); panel.add(add); panel.add(active); panel.add(text); add(panel); setTitle("Remove listener"); setSize(310, 200); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE); } class ButtonListener implements ActionListener { public void actionPerformed(ActionEvent e) { text.setText(Integer.toString(++count)); }

260

} public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { RemoveListenerExample ex = new RemoveListenerExample(); ex.setVisible(true); } } } });

We have three components on the panel. A button, check box and a label. By toggling the check box, we add or remove the listener for a button.
buttonlistener = new ButtonListener();

We have to create a non anonymous listener, if we want to later remove it. We need a reference to it.
if (active.isSelected()) { add.addActionListener(buttonlistener);} else { add.removeActionListener(buttonlistener); }

We determine, whether the check box is selected. Then we add or remove the listener.

Figure: Remove listener

Moving a window
The following example will look for a position of a window on the screen.
package zetcode; import java.awt.Font; import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; import javax.swing.JFrame; import javax.swing.JLabel;

261

import javax.swing.JPanel; import javax.swing.SwingUtilities; public class MovingWindowExample extends JFrame implements ComponentListener { private JLabel labelx; private JLabel labely; public MovingWindowExample() { } initUI();

public final void initUI() { JPanel panel = new JPanel(); panel.setLayout(null); labelx = new JLabel("x: "); labelx.setFont(new Font("Serif", Font.BOLD, 14)); labelx.setBounds(20, 20, 60, 25); labely = new JLabel("y: "); labely.setFont(new Font("Serif", Font.BOLD, 14)); labely.setBounds(20, 45, 60, 25); panel.add(labelx); panel.add(labely); add(panel); addComponentListener(this); setTitle("Moving window"); setSize(310, 200); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE);

public void componentResized(ComponentEvent e) { } public void componentMoved(ComponentEvent e) { int x = e.getComponent().getX(); int y = e.getComponent().getY(); labelx.setText("x: " + x); labely.setText("y: " + y); } public void componentShown(ComponentEvent e) { } public void componentHidden(ComponentEvent e) { } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() {

262

}); } }

public void run() { MovingWindowExample ex = new MovingWindowExample(); ex.setVisible(true); }

The example shows the current window coordinates on the panel. To get the window position, we use the ComponentListener.
labelx.setFont(new Font("Serif", Font.BOLD, 14));

We make the font bigger, the default one is a bit small.


int x = e.getComponent().getX(); int y = e.getComponent().getY();

Here we get the x and the y positions. Notice, that we have to implement all four methods, that are available in the ComponentListener. Even, if we do not use them.

Figure: Moving a window

Adapters
Adapters are convenient classes. In the previous code example, we had to implement all four methods of a ComponentListener class. Even if we did not use them. To avoid unnecessary coding, we can use adapters. Adapter is a class that implements all necessary methods. They are empty. We then use only those methods, that we actually need. There is no adapter for a button click event. Because there we have only one method to implement. The actionPerformed() method. We can use adapters in situations, where we have more than one method to implement. The following example is a rewrite of the previous one, using a ComponentAdapter.
package zetcode; import java.awt.Font; import java.awt.event.ComponentAdapter;

263

import java.awt.event.ComponentEvent; import import import import javax.swing.JFrame; javax.swing.JLabel; javax.swing.JPanel; javax.swing.SwingUtilities;

public class AdapterExample extends JFrame { private JLabel labelx; private JLabel labely; public AdapterExample() { } initUI();

public final void initUI() { JPanel panel = new JPanel(); panel.setLayout(null); labelx = new JLabel("x: "); labelx.setFont(new Font("Serif", Font.BOLD, 14)); labelx.setBounds(20, 20, 60, 25); labely = new JLabel("y: "); labely.setFont(new Font("Serif", Font.BOLD, 14)); labely.setBounds(20, 45, 60, 25); panel.add(labelx); panel.add(labely); add(panel); addComponentListener(new MoveAdapter()); setTitle("Adapter example"); setSize(310, 200); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE);

class MoveAdapter extends ComponentAdapter { @Override public void componentMoved(ComponentEvent e) { int x = e.getComponent().getX(); int y = e.getComponent().getY(); labelx.setText("x: " + x); labely.setText("y: " + y); } } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { AdapterExample ex = new AdapterExample(); ex.setVisible(true);

264

}); } }

This example is a rewrite of the previous one. Here we use the ComponentAdapter.
addComponentListener(new MoveAdapter());

Here we register the component listener.


class MoveAdapter extends ComponentAdapter { @Override public void componentMoved(ComponentEvent e) { int x = e.getComponent().getX(); int y = e.getComponent().getY(); labelx.setText("x: " + x); labely.setText("y: " + y); } }

Inside the MoveAdapter inner class, we define the componentMoved() method. All the other methods are left empty. This part of the Java Swing tutorial was dedicated to Swing events.

Java Swing dialogs


Dialog windows or dialogs are an indispensable part of most modern GUI applications. A dialog is defined as a conversation between two or more persons. In a computer application a dialog is a window which is used to "talk" to the application. A dialog is used to input data, modify data, change the application settings etc. Dialogs are important means of communication between a user and a computer program. In Java Swing toolkit, we can create two kinds of dialogs. Custom dialogs and standard dialogs. Custom dialogs are dialogs, created by the programmer. They are based on the JDialog class. Standard dialogs preedefined dialogs available in the Swing toolkit. For example JColorChooser or JFileChooser. These are dialogs for common programming tasks like showing text, receiving input , loading and saving files etc. They save programmer's time and enhance using some standard behaviour. There are two basic types of dialogs. Modal and modeless. Modal dialogs block input to other top level windows. Modeless dialogs allow input to other windows. What type of dialog to use, depends on the circumstances. An open file dialog is a good example of a modal dialog. While choosing a file to open, no other operation should be permitted. A typical modeless dialog is a find text dialog. It is handy to have the ability to move the cursor in the text control and define, where to start the finding of the particular text.

265

A simple custom dialog


In the following example we create a simple custom dialog. It is a sample about dialog, found in most GUI applications, usually located in the help menu.
package zetcode; import import import import import import import import import import import import import import import import java.awt.Dimension; java.awt.Font; java.awt.event.ActionEvent; java.awt.event.ActionListener; java.awt.event.KeyEvent; javax.swing.Box; javax.swing.BoxLayout; javax.swing.ImageIcon; javax.swing.JButton; javax.swing.JDialog; javax.swing.JFrame; javax.swing.JLabel; javax.swing.JMenu; javax.swing.JMenuBar; javax.swing.JMenuItem; javax.swing.SwingUtilities;

class AboutDialog extends JDialog { public AboutDialog() { } initUI();

public final void initUI() { setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS)); add(Box.createRigidArea(new Dimension(0, 10))); ImageIcon icon = new ImageIcon("notes.png"); JLabel label = new JLabel(icon); label.setAlignmentX(0.5f); add(label); add(Box.createRigidArea(new Dimension(0, 10))); JLabel name = new JLabel("Notes, 1.23"); name.setFont(new Font("Serif", Font.BOLD, 13)); name.setAlignmentX(0.5f); add(name); add(Box.createRigidArea(new Dimension(0, 50))); JButton close = new JButton("Close"); close.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { dispose(); }

});

266

close.setAlignmentX(0.5f); add(close); setModalityType(ModalityType.APPLICATION_MODAL); setTitle("About Notes"); setDefaultCloseOperation(DISPOSE_ON_CLOSE); setLocationRelativeTo(null); setSize(300, 200);

} }

public class SimpleDialog extends JFrame { public SimpleDialog() { } initUI();

public final void initUI() { JMenuBar menubar = new JMenuBar(); JMenu file = new JMenu("File"); file.setMnemonic(KeyEvent.VK_F); JMenu help = new JMenu("Help"); help.setMnemonic(KeyEvent.VK_H); JMenuItem about = new JMenuItem("About"); help.add(about); about.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { AboutDialog ad = new AboutDialog(); ad.setVisible(true); } }); menubar.add(file); menubar.add(help); setJMenuBar(menubar); setTitle("Simple Dialog"); setSize(300, 200); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { SimpleDialog sd = new SimpleDialog(); sd.setVisible(true); } } } });

267

The sample code will popup a small dialog box. The dialog will display an icon a text and one close button.
class AboutDialog extends JDialog {

The custom dialog is based on the JDialog class.


setModalityType(ModalityType.APPLICATION_MODAL);

Here we make the dialog modal.


setDefaultCloseOperation(DISPOSE_ON_CLOSE);

Here we set the defaul close operation.


AboutDialog ad = new AboutDialog(); ad.setVisible(true);

Here we display the about dialog, from the menu of the main frame.

Figure: Simple custom dialog

Message boxes
Message boxes provide information to the user.
package zetcode; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import import import import import javax.swing.JButton; javax.swing.JFrame; javax.swing.JOptionPane; javax.swing.JPanel; javax.swing.SwingUtilities;

public class MessageBoxes extends JFrame { private JPanel panel; public MessageBoxes() {

268

initUI(); } public final void initUI() { panel = new JPanel(); panel.setLayout(new GridLayout(2, 2)); JButton JButton JButton JButton error = new JButton("Error"); warning = new JButton("Warning"); question = new JButton("Question"); information = new JButton("Information");

error.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { JOptionPane.showMessageDialog(panel, "Could not open file", "Error", JOptionPane.ERROR_MESSAGE); } }); warning.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { JOptionPane.showMessageDialog(panel, "A deprecated call", "Warning", JOptionPane.WARNING_MESSAGE); } }); question.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { JOptionPane.showMessageDialog(panel, "Are you sure to quit?", }); } "Question", JOptionPane.QUESTION_MESSAGE);

information.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { JOptionPane.showMessageDialog(panel, "Download completed", "Question", JOptionPane.INFORMATION_MESSAGE); }

});

panel.add(error); panel.add(warning); panel.add(question); panel.add(information); add(panel); setTitle("Message Boxes"); setSize(300, 200); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE); } public static void main(String[] args) {

269

SwingUtilities.invokeLater(new Runnable() { public void run() { MessageBoxes mb = new MessageBoxes(); mb.setVisible(true); }

}); } }

The example shows an error, question, warning and information message boxes.
panel.setLayout(new GridLayout(2, 2));

We use a GridLayout layout manager to organize buttons, that will popup message boxes.
JButton JButton JButton JButton error = new JButton("Error"); warning = new JButton("Warning"); question = new JButton("Question"); information = new JButton("Information");

Here are the four buttons, that we will use.


JOptionPane.showMessageDialog(panel, "Could not open file", "Error", JOptionPane.ERROR_MESSAGE);

To create a message box, we call the showMessageDialog static method of the JOptionPane class. We provide the component name, message text, title and a message type. The message type is determined by the constant we choose. Available constants are:

ERROR_MESSAGE WARNING_MESSAGE QUESTION_MESSAGE INFORMATION_MESSAGE

Figure: Question message box

JFileChooser
JFileChooser

is a standard dialog for selecting a file from the file system.

package zetcode; import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener;

270

import import import import import import import import import import import import import import import import

java.io.BufferedReader; java.io.File; java.io.FileReader; java.io.IOException; javax.swing.BorderFactory; javax.swing.ImageIcon; javax.swing.JButton; javax.swing.JFileChooser; javax.swing.JFrame; javax.swing.JPanel; javax.swing.JScrollPane; javax.swing.JTextArea; javax.swing.JToolBar; javax.swing.SwingUtilities; javax.swing.filechooser.FileFilter; javax.swing.filechooser.FileNameExtensionFilter;

public class FileChooserDialog extends JFrame { private JPanel panel; private JTextArea area; public FileChooserDialog() { } initUI();

public final void initUI() { panel = new JPanel(); panel.setLayout(new BorderLayout()); ImageIcon open = new ImageIcon("open.png"); JToolBar toolbar = new JToolBar(); JButton openb = new JButton(open); openb.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { JFileChooser fileopen = new JFileChooser(); FileFilter filter = new FileNameExtensionFilter("c files", fileopen.addChoosableFileFilter(filter); int ret = fileopen.showDialog(panel, "Open file"); if (ret == JFileChooser.APPROVE_OPTION) { File file = fileopen.getSelectedFile(); String text = readFile(file); area.setText(text); } }); }

"c");

toolbar.add(openb); area = new JTextArea(); area.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));

271

JScrollPane pane = new JScrollPane(); pane.getViewport().add(area); panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); panel.add(pane); add(panel); add(toolbar, BorderLayout.NORTH); setTitle("FileChooserDialog"); setSize(400, 300); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE); } public String readFile(File file) { StringBuffer fileBuffer = null; String fileString = null; String line = null; try { FileReader in = new FileReader(file); BufferedReader brd = new BufferedReader(in); fileBuffer = new StringBuffer(); while ((line = brd.readLine()) != null) { fileBuffer.append(line).append( System.getProperty("line.separator")); } in.close(); fileString = fileBuffer.toString(); } catch (IOException e) { return null; } return fileString; } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { FileChooserDialog fcd = new FileChooserDialog(); fcd.setVisible(true); } } } });

The code example will demonstrate how to use a file chooser dialog in order to load file contents into the text area component.
JFileChooser fileopen = new JFileChooser();

This is the constructor of the file chooser dialog.

272

FileFilter filter = new FileNameExtensionFilter("c files", "c"); fileopen.addChoosableFileFilter(filter);

Here we define the file filter. In our case, we will have c files with extension .c. We have also the default All files option.
int ret = fileopen.showDialog(panel, "Open file");

Here we show the file chooser dialog. Upon clicking on the open file button, the return value is equal to JFileChooser.APPROVE_OPTION.
if (ret == JFileChooser.APPROVE_OPTION) { File file = fileopen.getSelectedFile(); String text = readFile(file); area.setText(text); }

Here we get the name of the selected file. We read the contents of the file and set the text into the textarea.

Figure: JFileChooser dialog

JColorChooser
JColorChooser

is a standard dialog for selecting a color.

package zetcode; import import import import java.awt.BorderLayout; java.awt.Color; java.awt.event.ActionEvent; java.awt.event.ActionListener;

273

import import import import import import import import

javax.swing.BorderFactory; javax.swing.ImageIcon; javax.swing.JButton; javax.swing.JColorChooser; javax.swing.JFrame; javax.swing.JPanel; javax.swing.JToolBar; javax.swing.SwingUtilities;

public class ColorChooserDialog extends JFrame { private JPanel panel; private JPanel display; public ColorChooserDialog() { initUI(); } public final void initUI() { panel = new JPanel(); panel.setLayout(new BorderLayout()); ImageIcon open = new ImageIcon("color.png"); JToolBar toolbar = new JToolBar(); JButton openb = new JButton(open); openb.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { JColorChooser clr = new JColorChooser(); Color color = clr.showDialog(panel, "Choose Color", Color.white); display.setBackground(color); } }); toolbar.add(openb); display = new JPanel(); display.setBackground(Color.WHITE); panel.setBorder(BorderFactory.createEmptyBorder(30, 50, 30, 50)); panel.add(display); add(panel); add(toolbar, BorderLayout.NORTH); setTitle("ColorChooserDialog"); setSize(400, 300); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE);

public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() {

274

ColorChooserDialog ccd = new ColorChooserDialog(); ccd.setVisible(true); }); } } }

In the example, we have a white panel. We will change the background color of the panel by selecting a color from the color chooser dialog.
JColorChooser clr = new JColorChooser(); Color color = clr.showDialog(panel, "Choose Color", Color.white); display.setBackground(color);

This code shows a color chooser dialog. The showDialog() method returns the selected color value. We change the display panel background to the newly selected color.

Figure: JColorChooser dialog In this part of the Java Swing tutorial, we have covered dialogs.

Basic Swing components


Swing components are basic building blocks of an application. Swing toolkit has a wide range of various widgets. Buttons, check boxes, sliders, list boxes etc. Everything a programmer needs for his job. In this section of the tutorial, we will describe several useful components.

275

JLabel Component
JLabel

is a simple component for displaying text, images or both. It does not react to input

events.
package com.zetcode; import import import import import import import import import import java.awt.BorderLayout; java.awt.Color; java.awt.Dimension; java.awt.Font; java.awt.Toolkit; javax.swing.BorderFactory; javax.swing.JFrame; javax.swing.JLabel; javax.swing.JPanel; javax.swing.SwingUtilities;

public class LabelExample extends JFrame { private Toolkit toolkit; public LabelExample() { initUI(); } private void initUI() { setTitle("No Sleep"); String lyrics = "<html>It's way too late to "Someone I would call now<br>" + "And neon signs got tired<br>" + "Red eye flights help the stars out<br>" + "I'm safe in a corner<br>" + "Just hours before me<br>" + "<br>" + "I'm waking with the roaches<br>" + "The world has surrendered<br>" + "I'm dating ancient ghosts<br>" + "The ones I made friends with<br>" + "The comfort of fireflies<br>" + "Long gone before daylight<br>" + "<br>" + "And if I had one wishful field tonight<br>" "I'd ask for the sun to never rise<br>" + "If God leant his voice for me to speak<br>" "I'd say go to bed, world<br>" + "<br>" + "I've always been too late<br>" + "To see what's before me<br>" + "And I know nothing sweeter than<br>" + "Champaign from last New Years<br>" + "Sweet music in my ears<br>" + "And a night full of no fears<br>" + "<br>" + "But if I had one wishful field tonight<br>" think of<br>" +

+ +

276

"I'd ask for the sun to never rise<br>" + "If God passed a mic to me to speak<br>" + "I'd say stay in bed, world<br>" + "Sleep in peace</html>"; JPanel panel = new JPanel(); panel.setLayout(new BorderLayout(10, 10)); JLabel label = new JLabel(lyrics); label.setFont(new Font("Georgia", Font.PLAIN, 14)); label.setForeground(new Color(50, 50, 25)); panel.add(label, BorderLayout.CENTER); panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); add(panel); pack(); toolkit = getToolkit(); Dimension screensize = toolkit.getScreenSize(); setLocation((screensize.width - getWidth())/2, (screensize.height - getHeight())/2); setDefaultCloseOperation(EXIT_ON_CLOSE);

public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { LabelExample ex = new LabelExample(); ex.setVisible(true); } }); } }

In our example, we show lyrics of no sleep song from cardigans. We can use html tags in JLabel component. We use the <br> tag to separate lines.
JPanel panel = new JPanel(); panel.setLayout(new BorderLayout(10, 10));

We create a panel and set a BorderLayout manager.


JLabel label = new JLabel(lyrics); label.setFont(new Font("Georgia", Font.PLAIN, 14)); label.setForeground(new Color(50, 50, 25));

Here we create the label component. We set it's font to plain georgia, 14 px tall. We also change the foreground color.
panel.add(label, BorderLayout.CENTER); panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));

We put the label into the center of the panel. We put 10px around the label.

277

add(panel); pack();

The panel is added to the frame component. We call the pack() method, which will resize the window, so that all components are visible.

Figure: JLabel

JCheckBox
JCheckBox is a component that has two states. On and Off. It is a box with a label. If the check box is checked, it is represented by a tick in a box. A check box can be used to show/hide splashscreen at startup, toggle visibility of a toolbar etc.
package com.zetcode; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener;

278

import import import import import

javax.swing.Box; javax.swing.BoxLayout; javax.swing.JCheckBox; javax.swing.JFrame; javax.swing.SwingUtilities;

public class CheckBoxExample extends JFrame implements ActionListener { public CheckBoxExample() { initUI(); } public final void initUI() { setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS)); add(Box.createRigidArea(new Dimension(15, 20))); JCheckBox checkbox = new JCheckBox("Show Title", true); checkbox.setFocusable(false); checkbox.addActionListener(this); add(checkbox); setSize(280, 200); setTitle("JCheckBox example"); setLocationRelativeTo(null); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } @Override public void actionPerformed(ActionEvent e) { JCheckBox source = (JCheckBox) e.getSource(); boolean state = source.isSelected(); if (state) { this.setTitle("JCheckbox example"); } else { this.setTitle(""); } } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { CheckBoxExample ex = new CheckBoxExample(); ex.setVisible(true); } }); } }

Our code example shows or hides the title of the window depending on its state.
setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));

279

add(Box.createRigidArea(new Dimension(15, 20)));

In this example, we use a BoxLayout layout manager. We put some space there, so that the checkbox is not too close to the corner.
JCheckBox checkbox = new JCheckBox("Show Title", true);

Here we have a constructor for the checkbox. We provide text and state.
checkbox.setFocusable(false);

We have disabled the focus for the checkbox. A JCheckBox that has a focus may be selected or unselected with a spacebar.
JCheckBox source = (JCheckBox) e.getSource(); boolean state = source.isSelected(); if (state) { this.setTitle("JCheckbox example"); } else { this.setTitle(""); }

From the event object, we get the source component. In our case is the a check box. We find out the selection state of the check box. Depending on the state of the check box, we show or hide the title of the window.

Figure: JCheckBox

JSlider
JSlider is a component that lets the user graphically select a value by sliding a knob within a bounded interval. Our example will show a volume control.
package com.zetcode; import java.awt.BorderLayout; import java.awt.Dimension; import import import import javax.swing.BorderFactory; javax.swing.Box; javax.swing.BoxLayout; javax.swing.ImageIcon;

280

import import import import import import import

javax.swing.JFrame; javax.swing.JLabel; javax.swing.JPanel; javax.swing.JSlider; javax.swing.SwingUtilities; javax.swing.event.ChangeEvent; javax.swing.event.ChangeListener;

public class SliderExample extends JFrame { private JSlider slider; private JLabel label; private private private private ImageIcon ImageIcon ImageIcon ImageIcon mute; min; med; max;

public SliderExample() { loadImages(); initUI();

private void loadImages() { mute = new ImageIcon(getClass().getResource("mute.png")); min = new ImageIcon(getClass().getResource("min.png")); med = new ImageIcon(getClass().getResource("med.png")); max = new ImageIcon(getClass().getResource("max.png")); mute = new ImageIcon(getClass().getResource("mute.png")); } public final void initUI() { JPanel panel = new JPanel(); panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS)); panel.setBorder(BorderFactory.createEmptyBorder(40, 40, 40, 40)); setLayout(new BorderLayout()); panel.add(Box.createHorizontalGlue()); slider = new JSlider(0, 150, 0); slider.setPreferredSize(new Dimension(150, 30)); slider.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent event) { int value = slider.getValue(); if (value == 0) { label.setIcon(mute); } else if (value > 0 && value <= 30) { label.setIcon(min); } else if (value > 30 && value < 80) { label.setIcon(med); } else { label.setIcon(max); } } });

281

panel.add(slider); panel.add(Box.createRigidArea(new Dimension(5, 0))); label = new JLabel(mute, JLabel.CENTER); panel.add(label); panel.add(Box.createHorizontalGlue()); add(panel, BorderLayout.CENTER); pack(); setTitle("JSlider"); setDefaultCloseOperation(EXIT_ON_CLOSE); setLocationRelativeTo(null); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { SliderExample ex = new SliderExample(); ex.setVisible(true); } });

} }

In the code example, we show a JSlider and a JLabel. By dragging the slider, we change the icon on the label component.
private void loadImages() { mute = new ImageIcon(getClass().getResource("mute.png")); min = new ImageIcon(getClass().getResource("min.png")); med = new ImageIcon(getClass().getResource("med.png")); max = new ImageIcon(getClass().getResource("max.png")); mute = new ImageIcon(getClass().getResource("mute.png")); }

In the loadImages() method, we load the image files from the disk.
panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));

Panel component has a horizontal BoxLayout.


panel.setBorder(BorderFactory.createEmptyBorder(40, 40, 40, 40));

We creare a 40px border around the panel.


panel.add(Box.createHorizontalGlue());

We put resizable space to bo both sides, left and right. It is to prevent JSlider from growing to unnatural sizes.
slider = new JSlider(0, 150, 0);

282

This is a JSlider constructor. The parameters are minimum value, maximum value and current value.
slider.addChangeListener(new ChangeListener() { ... });

We add a ChangeListener to the slider. Inside the listener, we determine the current slider value and update the label accordingly.
panel.add(Box.createRigidArea(new Dimension(5, 0)));

We place a 5px rigid space between the two components. They are too close to each other, when the slider is at the end position.

Figure: JSlider

JComboBox
Combobox is a component that combines a button or editable field and a drop-down list. The user can select a value from the drop-down list, which appears at the user's request. If you make the combo box editable, then the combo box includes an editable field into which the user can type a value.
import import import import import import import import import import import import import import java.awt.Component; java.awt.Dimension; java.awt.event.ActionEvent; java.awt.event.ActionListener; java.awt.event.ItemEvent; java.awt.event.ItemListener; javax.swing.Box; javax.swing.BoxLayout; javax.swing.ImageIcon; javax.swing.JButton; javax.swing.JComboBox; javax.swing.JDialog; javax.swing.JLabel; javax.swing.border.LineBorder;

public class ComboBox extends JDialog implements ActionListener, ItemListener { final String[] authors = { "Leo Tolstoy", "John Galsworthy", "Honore de Balzac", "Stefan Zweig",

283

};

"Boris Pasternak", "Tom Wolfe"

final String[] images = { "tolstoy.jpg", "galsworthy.jpg", "balzac.jpg", "zweig.jpg", "pasternak.jpg", "wolfe.jpg" }; private JLabel display = null; private JComboBox combobox = null; private JButton button = null; ImageIcon icon = new ImageIcon( ClassLoader.getSystemResource("balzac.jpg")); public ComboBox() { setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS)); add(Box.createRigidArea(new Dimension(0, 35))); display = new JLabel(); display.setPreferredSize(new Dimension(100, 127)); display.setMaximumSize(new Dimension(100, 127)); display.setAlignmentX(Component.CENTER_ALIGNMENT); display.setBorder(LineBorder.createGrayLineBorder()); add(display); add(Box.createRigidArea(new Dimension(0, 15))); combobox = new JComboBox(authors); combobox.setSelectedIndex(-1); combobox.setPreferredSize(new Dimension(140, 22)); combobox.setMaximumSize(new Dimension(140, 22)); combobox.addItemListener(this); add(combobox); add(Box.createRigidArea(new Dimension(0, 15))); button = new JButton("Close"); button.setAlignmentX(Component.CENTER_ALIGNMENT); button.addActionListener(this); add(button); setTitle("JComboBox"); setSize(300, 300); setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); setLocationRelativeTo(null); setVisible(true);

public static void main(String[] args) { new ComboBox(); } public void actionPerformed(ActionEvent e) { System.exit(0); } public void itemStateChanged(ItemEvent e) {

284

if (e.getStateChange() == ItemEvent.SELECTED) { JComboBox combo = (JComboBox) e.getSource(); int index = combo.getSelectedIndex(); display.setIcon(new ImageIcon( ClassLoader.getSystemResource(images[index]))); } } }

In our example, we have three components. A label, a combobox and a button. The button closes the window. We have six names of famous novelists in our combobox. If we select a name, an image is displayed in the label.
public class ComboBox extends JDialog implements ActionListener, ItemListener {

This is a dialog based application example.


display = new JLabel();

The display area is a simple JLabel.


combobox = new JComboBox(authors); combobox.setSelectedIndex(-1);

The constructor of the JComboBox takes a string array of novelists. If we provide -1 as an argument in the setSelectedIndex() method, no item to be selected.
combobox.addItemListener(this);

We add an ItemListener to our combobox. In the event handler, we get the selected index of the combobox and set an appropriate icon for the label. The selected item is an index to the array of images.

285

Figure: JComboBox

JProgressBar
A progress bar is a widget that is used, when we process lengthy tasks. It is animated so that the user knows, that our task is progressing. The JProgressBar widget provides a horizontal or vertical progress bar. The initial and minimum values are 0, and the maximum is 100.
package com.zetcode; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import import import import import import import import import javax.swing.BorderFactory; javax.swing.Box; javax.swing.BoxLayout; javax.swing.JButton; javax.swing.JFrame; javax.swing.JPanel; javax.swing.JProgressBar; javax.swing.SwingUtilities; javax.swing.Timer;

public class ProgressBarExample extends JFrame { private private private private ActionListener updateProBar; Timer timer; JProgressBar progressBar; JButton button;

public ProgressBarExample() { } initUI();

286

private void initUI() { setTitle("JProgressBar"); JPanel panel = new JPanel(); panel.setBorder(BorderFactory.createEmptyBorder(40, 40, 40, 40)); panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); progressBar = new JProgressBar(); progressBar.setMaximumSize(new Dimension(150, 20)); progressBar.setMinimumSize(new Dimension(150, 20)); progressBar.setPreferredSize(new Dimension(150, 20)); progressBar.setAlignmentX(0f); panel.add(progressBar); panel.add(Box.createRigidArea(new Dimension(0, 20))); button = new JButton("Start"); button.setFocusable(false); button.setMaximumSize(button.getPreferredSize()); updateProBar = new ActionListener() { @Override public void actionPerformed(ActionEvent actionEvent) { int val = progressBar.getValue(); if (val >= 100) { timer.stop(); button.setText("End"); return; } progressBar.setValue(++val); }; }

timer = new Timer(50, updateProBar); button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if (timer.isRunning()) { timer.stop(); button.setText("Start"); } else if (!"End".equals(button.getText())) { timer.start(); button.setText("Stop"); } } }); panel.add(button); add(panel); pack(); setDefaultCloseOperation(EXIT_ON_CLOSE); setResizable(false);

287

setLocationRelativeTo(null); setVisible(true); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { ProgressBarExample ex = new ProgressBarExample(); ex.setVisible(true); } }); } }

The example displays a progress bar and a button. The button starts and stops the progress.
progressBar = new JProgressBar();

Here we create the JProgressBar. The minimum value is 0, maximum 100 and the initial value is 0. These are the default values.
progressBar.setMaximumSize(new Dimension(150, 20)); progressBar.setMinimumSize(new Dimension(150, 20)); progressBar.setPreferredSize(new Dimension(150, 20));

These lines are for design purposes only. I want my examples to look nice. The default height on my box was only 14px which looked bad.
progressBar.setAlignmentX(0f);

This line aligns both progress bar with the button. To the left.
panel.add(Box.createRigidArea(new Dimension(0, 20)));

Here we put some rigid space between the two components.


button.setFocusable(false);

Focus looks bad, better disable it.


timer = new Timer(50, updateProBar);

The timer object launches updateProBar listener every 50ms. Inside that listener, we check, if the progress bar reached the value 100 and stop the timer, or update the progress bar.
if (timer.isRunning()) { timer.stop(); button.setText("Start"); } else if (!"End".equals(button.getText())) { timer.start(); button.setText("Stop"); }

288

Clicking on the button starts or stops the progress. The text of the button is updated dynamically. It can have Start, Stop or End String values.

Figure: JProgressBar

JToggleButton
is a button that has two states. Pressed and not pressed. You toggle between these two states by clicking on it. There are situations where this functionality fits well.
JToggleButton package com.zetcode; import import import import import import import import import import import import java.awt.Color; java.awt.Dimension; java.awt.event.ActionEvent; java.awt.event.ActionListener; javax.swing.BorderFactory; javax.swing.Box; javax.swing.BoxLayout; javax.swing.JDialog; javax.swing.JPanel; javax.swing.JToggleButton; javax.swing.SwingUtilities; javax.swing.border.LineBorder;

public class ToggleButtonExample extends JDialog implements ActionListener { private private private private JToggleButton redButton; JToggleButton greenButton; JToggleButton blueButton; JPanel display;

public ToggleButtonExample() { } initUI();

private void initUI() { JPanel bottom = new JPanel(); bottom.setLayout(new BoxLayout(bottom, BoxLayout.X_AXIS)); bottom.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); JPanel leftPanel = new JPanel();

289

leftPanel.setLayout(new BoxLayout(leftPanel, BoxLayout.Y_AXIS)); redButton = new JToggleButton("red"); redButton.addActionListener(this); greenButton = new JToggleButton("green"); greenButton.addActionListener(this); blueButton = new JToggleButton("blue"); blueButton.addActionListener(this); blueButton.setMaximumSize(greenButton.getMaximumSize()); redButton.setMaximumSize(greenButton.getMaximumSize()); leftPanel.add(redButton); leftPanel.add(Box.createRigidArea(new Dimension(25, 7))); leftPanel.add(greenButton ); leftPanel.add(Box.createRigidArea(new Dimension(25, 7))); leftPanel.add(blueButton); bottom.add(leftPanel); bottom.add(Box.createRigidArea(new Dimension(20, 0))); display = new JPanel(); display.setPreferredSize(new Dimension(110, 110)); display.setBorder(LineBorder.createGrayLineBorder()); display.setBackground(Color.black); bottom.add(display); add(bottom); pack(); setTitle("JToggleButton"); setResizable(false); setLocationRelativeTo(null); setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);

@Override public void actionPerformed(ActionEvent e) { Color color = display.getBackground(); int red = color.getRed(); int green = color.getGreen(); int blue = color.getBlue(); if (e.getActionCommand().equals("red")) { if (red == 0) { red = 255; } else { red = 0; } } if (e.getActionCommand().equals("green")) { if (green == 0) { green = 255; } else {

290

} }

green = 0;

if (e.getActionCommand().equals("blue")) { if (blue == 0) { blue = 255; } else { blue = 0; } } Color setCol = new Color(red, green, blue); display.setBackground(setCol); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { ToggleButtonExample ex = new ToggleButtonExample(); ex.setVisible(true); } }); } }

The example has three panels and three toggle buttons. Panels are bottom panel, left panel and display panel. The bottom panel is use to organize the left and display panels. For this, we use horizontal BoxLayout manager. The left panel will holt three toggle buttons. This time we use vertical BoxLayout manager. We set the background color of the display panel to black. The toggle buttons will toggle the red, green and blue parts of the color value. The background color will depend on which togglebuttons we have pressed.
redButton = new JToggleButton("red"); redButton.addActionListener(this);

Here we create a toggle button and set an action listener to it.


blueButton.setMaximumSize(greenButton.getMaximumSize()); redButton.setMaximumSize(greenButton.getMaximumSize());

We make all three buttons of equal size.


Color color = display.getBackground(); int red = color.getRed(); int green = color.getGreen(); int blue = color.getBlue();

In the actionPerformed method, we determine the current red, green, blue parts of the display background color.
if (e.getActionCommand().equals("red")) { if (red == 0) { red = 255;

291

} else { red = 0; }

We determine, which button was toggled, and update the color part of the RGB value accordingly.
Color setCol = new Color(red, green, blue); display.setBackground(setCol);

Here a new color is created and the display panel is updated to a new color.

Figure: JToggleButton In this part of the Java Swing tutorial, we have covered basic Swing components.

Basic Swing components II


Swing components are basic building blocks of an application. Swing toolkit has a wide range of various components. Buttons, check boxes, sliders, list boxes etc. Everything a programmer needs for his job. In this section of the tutorial, we will describe several useful components.

JList Component
JList

is a component that displays a list of objects. It allows the user to select one or more

items.
package com.zetcode; import import import import import import import import import import import java.awt.BorderLayout; java.awt.Dimension; java.awt.Font; java.awt.GraphicsEnvironment; javax.swing.BorderFactory; javax.swing.JFrame; javax.swing.JLabel; javax.swing.JList; javax.swing.JPanel; javax.swing.JScrollPane; javax.swing.SwingUtilities;

292

import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; public class ListExample extends JFrame { private JLabel label; private JList list; public ListExample() { } initUI();

private void initUI() { JPanel panel = new JPanel(); panel.setLayout(new BorderLayout()); panel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); String[] fonts = ge.getAvailableFontFamilyNames(); list = new JList(fonts); list.addListSelectionListener(new ListSelectionListener() { @Override public void valueChanged(ListSelectionEvent e) { if (!e.getValueIsAdjusting()) { String name = (String) list.getSelectedValue(); Font font = new Font(name, Font.PLAIN, 12); label.setFont(font); } } }); JScrollPane pane = new JScrollPane(); pane.getViewport().add(list); pane.setPreferredSize(new Dimension(250, 200)); panel.add(pane); label = new JLabel("Aguirre, der Zorn Gottes"); label.setFont(new Font("Serif", Font.PLAIN, 12)); add(label, BorderLayout.SOUTH); add(panel); pack(); setTitle("JList"); setDefaultCloseOperation(EXIT_ON_CLOSE); setLocationRelativeTo(null);

public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { ListExample ex = new ListExample();

293

} } } });

ex.setVisible(true);

In our example, we will display a JList and JLabel components. The list component contains a list of all available font family names on our system. If we select an item from the list, the label will be displayed in a font, we have chosen.
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); String[] fonts = ge.getAvailableFontFamilyNames();

Here we obtain all possible font family names on our system.


list = new JList(fonts);

We create a JList component.


public void valueChanged(ListSelectionEvent e) { if (!e.getValueIsAdjusting()) {

Events in list selection are grouped. We receive events for both selecting and deselecting. To filter only the selecting events, we use the getValueIsAdjusting() method.
String name = (String) list.getSelectedValue(); Font font = new Font(name, Font.PLAIN, 12); label.setFont(font);

We get the selected item and set a new font for the label.
JScrollPane pane = new JScrollPane(); pane.getViewport().add(list);

component is not scrollable by default. We put the list into the JScrollPane to make it scrollable.
JLabel

294

Figure: JList

JTextArea component
A JTextArea is a multi-line text area that displays plain text. It is lightweight component for working with text. The component does not handle scrolling. For this task, we use JScrollPane component.
package com.zetcode; import java.awt.BorderLayout; import java.awt.Dimension; import import import import import import javax.swing.BorderFactory; javax.swing.JFrame; javax.swing.JPanel; javax.swing.JScrollPane; javax.swing.JTextArea; javax.swing.SwingUtilities;

public class TextAreaExample extends JFrame { public TextAreaExample() { } initUI();

private void initUI() { JPanel panel = new JPanel(); panel.setLayout(new BorderLayout()); panel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); JScrollPane pane = new JScrollPane(); JTextArea area = new JTextArea(); area.setLineWrap(true); area.setWrapStyleWord(true); area.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8)); pane.getViewport().add(area);

295

panel.add(pane); add(panel); setTitle("JTextArea"); setSize(new Dimension(350, 300)); setDefaultCloseOperation(EXIT_ON_CLOSE); setLocationRelativeTo(null); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { TextAreaExample ex = new TextAreaExample(); ex.setVisible(true); } });

} }

The example shows a simple JTextArea component.


JTextArea area = new JTextArea();

This is the constructor of the JTextArea component.


area.setLineWrap(true);

Make the lines wrapped, if they are too long to fit the width.
area.setWrapStyleWord(true);

Here we specify, how is line going to be wrapped. In our case, lines will be wrapped at word boundaries, whitespaces.
area.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));

We put some border around the text in the component.


pane.getViewport().add(area);

To make the text scrollable, we put the JTextArea component into the JScrollPane component.

296

Figure: JTextAra

JTextPane component
component is a more advanced component for working with text. The component can do some complex formatting operations over the text. It can display also HTML documents.
JTextPane package com.zetcode; import java.awt.BorderLayout; import java.io.IOException; import java.util.logging.Level; import java.util.logging.Logger; import import import import import import javax.swing.BorderFactory; javax.swing.JFrame; javax.swing.JPanel; javax.swing.JScrollPane; javax.swing.JTextPane; javax.swing.SwingUtilities;

public class TextPaneExample extends JFrame { JTextPane textPane; public TextPaneExample() { } initUI();

private void initUI() { JPanel panel = new JPanel(); panel.setLayout(new BorderLayout()); panel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); JScrollPane pane = new JScrollPane();

297

textPane = new JTextPane(); textPane.setContentType("text/html"); textPane.setEditable(false); textPane.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8)); loadFile(); pane.getViewport().add(textPane); panel.add(pane); add(panel); pack(); setTitle("JTextPane"); setDefaultCloseOperation(EXIT_ON_CLOSE); setLocationRelativeTo(null); } private void loadFile() { try { String cd = System.getProperty("user.dir") + "/"; textPane.setPage("File:///" + cd + "test.html"); } catch (IOException ex) { Logger.getLogger(TextPaneExample.class.getName()).log(Level.SEVERE, null, ex); } } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { TextPaneExample ex = new TextPaneExample(); ex.setVisible(true); } });

} }

This is the HTML code, that we are loading into the JTextPane component. The component does not handle scrolling.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>A simple html document</title> </head> <body> <h2>A simple html document</h2> <p> <b>JTextPane</b> can display html documents. </p>

298

<br> <pre> JScrollPane pane = new JScrollPane(); JTextPane textpane = new JTextPane(); textpane.setContentType("text/html"); textpane.setEditable(false); </pre> <br> <small>The Java Swing tutorial, 2013</small> </body> </html>

In our example we show a JTextPane component and load a HTML document. Example shows formatting capabilities of the component.
JTextPane textpane = new JTextPane(); textpane.setContentType("text/html"); textpane.setEditable(false);

We create a JTextPane component, set the content of the component to be a HTML document and disable editing.
private void loadFile() { try { String cd = System.getProperty("user.dir") + "/"; textPane.setPage("File:///" + cd + "test.html"); } catch (IOException ex) { Logger.getLogger(TextPaneExample.class.getName()).log(Level.SEVERE, null, ex); } }

Here we determine the current working directory of the user. We load a HTML document into the pane.

299

Figure: JTextPane In this chapter, we have continued covering basic Swing components.

Java Swing model architecture


Swing engineers created the Swing toolkit implementing a modified Model View Controller design pattern. This enables efficient handling of data and using pluggable look and feel at runtime. The traditional MVC pattern divides an application into three parts. A model, a view and a cotroller. The model represents the data in the application. The view is the visual representation of the data. And finally the controller processes and responds to events, typically user actions, and may invoke changes on the model. The idea is to separate the data access and business logic from data presentation and user interaction, by introducing an intermediate component: the controller. The Swing toolkit uses a modified MVC design pattern. The Swing has single UI object for both the view and the controller. This modified MVC is sometimes called a separable model architecture. In the Swing toolkit, every component has it's model. Even the basic ones like buttons. There are two kinds of models in Swing toolkit.

state models data models

The state models handle the state of the component. For example the model keeps track whether the component is selected or pressed. The data models handle data, they work with. A list component keeps a list of items, it is displaying.

300

For Swing developer it means, that we often need to get a model instance in order to manipulate the data in the component. But there are exceptions. For convenience, there are some methods that return data without the model.
public int getValue() { return getModel().getValue(); }

For example the getValue() method of the JSlider component. The developer does not need to work with the model directly. Instead, the access to the model is done behind the scenes. It would be an overkill to work with models directly in such simple situations. Because of this, the Swing tookit provides some convenience methods like the previous one. To query the state of the model, we have two kinds of notifications.

lightweight notification stateful notification

The lightweight notification uses a ChangeListener class. We have only one single event (ChangeEvent) for all notifications coming from the component. For more complicated components, the stateful notification is used. For such notifications, we have different kinds of events. For example the JList component has ListDataEvent and ListSelectionEvent. If we do not set a model for a component, a default one is created. For example the button component has DefaultButtonModel model
public JButton(String text, Icon icon) { // Create the model setModel(new DefaultButtonModel()); // initialize init(text, icon);

If we look at the JButton.java source file, we find out, that the default model is created at the construction of the component.

ButtonModel
The model is used for various kinds of buttons like push buttons, check boxes, radio boxes and for menu items. The following example illustrates the model for a JButton. We can manage only the state of the button, because no data can be associated with a push button.
import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import import import import import import javax.swing.DefaultButtonModel; javax.swing.JButton; javax.swing.JCheckBox; javax.swing.JFrame; javax.swing.JLabel; javax.swing.JPanel;

301

import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; public class ButtonModel extends JFrame { private private private private JButton ok; JLabel enabled; JLabel pressed; JLabel armed;

public ButtonModel() { setTitle("ButtonModel"); JPanel panel = new JPanel(); panel.setLayout(null); ok = new JButton("ok"); JCheckBox cb = new JCheckBox("Enabled", true); ok.setBounds(40, 30, 80, 25); ok.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { DefaultButtonModel model = (DefaultButtonModel) ok.getModel(); if (model.isEnabled()) enabled.setText("Enabled: true"); else enabled.setText("Enabled: false"); if (model.isArmed()) armed.setText("Armed: true"); else armed.setText("Armed: false"); if (model.isPressed()) pressed.setText("Pressed: true"); else pressed.setText("Pressed: false"); } }); cb.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (ok.isEnabled()) ok.setEnabled(false); else ok.setEnabled(true); } }); cb.setBounds(180, 30, 100, 25); enabled = new JLabel("Enabled: true"); enabled.setBounds(40, 90, 90, 25); pressed = new JLabel("Pressed: false"); pressed.setBounds(40, 120, 90, 25); armed = new JLabel("Armed: false");

302

armed.setBounds(40, 150, 90, 25); panel.add(ok); panel.add(cb); panel.add(enabled); panel.add(pressed); panel.add(armed); add(panel); setSize(350, 250); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE); setVisible(true); } public static void main(String[] args) { new ButtonModel(); }

In our example, we have a button, check box and three labels. The labels represent three properties of the button. Whether it is pressed, disabled or armed.
ok.addChangeListener(new ChangeListener() {

We use a lightweight ChangeListener to listen for button state changes.


DefaultButtonModel model = (DefaultButtonModel) ok.getModel();

Here we get the default button model.


if (model.isEnabled()) enabled.setText("Enabled: true"); else enabled.setText("Enabled: false");

We query the model, whether the button is enabled or not. We update the label accordingly.
if (ok.isEnabled()) ok.setEnabled(false); else ok.setEnabled(true);

The check box enables or disables the button. To enable the ok button, we call the setEnable() method. So we change the state of the button. Where is the model? The answer lies in the AbstractButton.java file.
public void setEnabled(boolean b) { if (!b && model.isRollover()) { model.setRollover(false); } super.setEnabled(b); model.setEnabled(b); }

303

The answer is, that internally, we the Swing toolkit works with a model. The setEnable() is another convenience method for programmers.

Figure: ButtonModel

Custom ButtonModel
In the previous example, we used a default button model. In the following code example we will use our own button model.
package zetcode; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.*; public class ButtonModel2 extends JFrame { private private private private JButton ok; JLabel enabled; JLabel pressed; JLabel armed;

public ButtonModel2(String title) { super(title); this.initUI(); } private void initUI() { setTitle("ButtonModel"); JPanel panel = new JPanel(); panel.setLayout(null); ok = new JButton("ok"); JCheckBox cb = new JCheckBox("Enabled", true); ok.setBounds(40, 30, 80, 25); cb.addActionListener(new ActionListener() {

304

@Override public void actionPerformed(ActionEvent e) { if (ok.isEnabled()) ok.setEnabled(false); else ok.setEnabled(true); } }); cb.setBounds(180, 30, 100, 25); enabled = new JLabel("Enabled: true"); enabled.setBounds(40, 90, 120, 25); pressed = new JLabel("Pressed: false"); pressed.setBounds(40, 120, 120, 25); armed = new JLabel("Armed: false"); armed.setBounds(40, 150, 120, 25); ButtonModel model = new DefaultButtonModel() { @Override public void setEnabled(boolean b) { if (b) enabled.setText("Enabled: true"); else enabled.setText("Enabled: false"); } super.setEnabled(b);

@Override public void setArmed(boolean b) { if (b) armed.setText("Armed: true"); else armed.setText("Armed: false"); } super.setArmed(b);

@Override public void setPressed(boolean b) { if (b) pressed.setText("Pressed: true"); else pressed.setText("Pressed: false"); } }; ok.setModel(model); panel.add(ok); panel.add(cb); panel.add(enabled); panel.add(pressed); panel.add(armed); super.setPressed(b);

305

add(panel); setSize(350, 250); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE); setVisible(true); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { ButtonModel2 ex = new ButtonModel2("ButtonModel"); ex.setVisible(true); } }); }

This example does the same thing as the previous one. The difference is that we do not use a change listener and we use a custom button model.
ButtonModel model = new DefaultButtonModel() {

We create a button model and overwrite the necessary methods.


@Override public void setEnabled(boolean b) { if (b) enabled.setText("Enabled: true"); else enabled.setText("Enabled: false"); } super.setEnabled(b);

We overwrite the setEnabled() method and add some functionality there. We must not forget to call the parent method as well to procede with the processing.
ok.setModel(model);

We set the custom model for the button.

JList models
Several components have two models. The JList component has the following models: ListModel and ListSelectionModel. The ListModel handles data. And the ListSelectionModel works with the GUI. The following example shows both models.
import import import import import import java.awt.BorderLayout; java.awt.Dimension; java.awt.event.ActionEvent; java.awt.event.ActionListener; java.awt.event.MouseAdapter; java.awt.event.MouseEvent;

306

import import import import import import import import import import import

javax.swing.BorderFactory; javax.swing.Box; javax.swing.BoxLayout; javax.swing.DefaultListModel; javax.swing.JButton; javax.swing.JFrame; javax.swing.JList; javax.swing.JOptionPane; javax.swing.JPanel; javax.swing.JScrollPane; javax.swing.ListSelectionModel;

public class List extends JFrame { private DefaultListModel model; private JList list; public List() { setTitle("JList models"); model = new DefaultListModel(); model.addElement("Amelie"); model.addElement("Aguirre, der Zorn Gottes"); model.addElement("Fargo"); model.addElement("Exorcist"); model.addElement("Schindler list"); JPanel panel = new JPanel(); panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS)); JPanel leftPanel = new JPanel(); JPanel rightPanel = new JPanel(); leftPanel.setLayout(new BorderLayout()); rightPanel.setLayout(new BoxLayout(rightPanel, BoxLayout.Y_AXIS)); list = new JList(model); list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); list.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2)); list.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e) { if(e.getClickCount() == 2){ int index = list.locationToIndex(e.getPoint()); Object item = model.getElementAt(index); String text = JOptionPane.showInputDialog("Rename item", String newitem = null; if (text != null) newitem = text.trim(); else return; if (!newitem.isEmpty()) { model.remove(index); model.add(index, newitem); ListSelectionModel selmodel = list.getSelectionModel(); selmodel.setLeadSelectionIndex(index);

item);

307

} } });

20));

JScrollPane pane = new JScrollPane(); pane.getViewport().add(list); leftPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, leftPanel.add(pane); JButton removeall = new JButton("Remove All"); JButton add = new JButton("Add"); add.setMaximumSize(removeall.getMaximumSize()); JButton rename = new JButton("Rename"); rename.setMaximumSize(removeall.getMaximumSize()); JButton delete = new JButton("Delete"); delete.setMaximumSize(removeall.getMaximumSize()); add.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { String text = JOptionPane.showInputDialog("Add a new

item");

String item = null; if (text != null) item = text.trim(); else return; if (!item.isEmpty()) model.addElement(item); }); }

delete.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { ListSelectionModel selmodel = list.getSelectionModel(); int index = selmodel.getMinSelectionIndex(); if (index >= 0) model.remove(index); } }); rename.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { ListSelectionModel selmodel = list.getSelectionModel(); int index = selmodel.getMinSelectionIndex(); if (index == -1) return; Object item = model.getElementAt(index); String text = JOptionPane.showInputDialog("Rename item", String newitem = null; if (text != null) { newitem = text.trim(); } else

item);

308

return; if (!newitem.isEmpty()) { model.remove(index); model.add(index, newitem); } }); }

removeall.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { model.clear(); } }); rightPanel.add(add); rightPanel.add(Box.createRigidArea(new Dimension(0,4))); rightPanel.add(rename); rightPanel.add(Box.createRigidArea(new Dimension(0,4))); rightPanel.add(delete); rightPanel.add(Box.createRigidArea(new Dimension(0,4))); rightPanel.add(removeall); rightPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 20)); panel.add(leftPanel); panel.add(rightPanel); add(panel); setSize(350, 250); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE); setVisible(true); } public static void main(String[] args) { new List(); }

The example shows a list component and four buttons. The buttons control the data in the list component. The example is a bit larger, because we did some additional checks there. We do not allow to input empty spaces into the list component.
model = new DefaultListModel(); model.addElement("Amelie"); model.addElement("Aguirre, der Zorn Gottes"); ...

We create a list model and add elements into it.


list = new JList(model); list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); list.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));

We create a list component. The parameter of the constructor is the model, we have created. We put the list into the single selection mode. We also put some space around the list.

309

if (text != null) item = text.trim(); else return; if (!item.isEmpty()) model.addElement(item);

We add only items that are not equal to null and are not empty. e.g. that contain at least one character other than white space. It makes no sense to add white spaces or null values into the list.
ListSelectionModel selmodel = list.getSelectionModel(); int index = selmodel.getMinSelectionIndex(); if (index >= 0) model.remove(index);

This is the code, that runs when we press the delete button. In order to delete an item from the list, it must be selected. So we must figure out the currently selected item. For this, we call the getSelectionModel() method. This is a GUI work, so we use a ListSelectionModel. Removing an item is working with data. For that we use the list data model. So, in our example we used both list models. We called add(), remove() and clear() methods of the list data model to work with our data. And we used a list selection model in order to find out the selected item, which is a GUI job.

Figure: List Models

A document model
This is an excellent example of a separation of a data from the visual representation. In a JTextPane component, we have a StyledDocument for setting the style of the text data.
import import import import java.awt.BorderLayout; java.awt.Dimension; java.awt.event.ActionEvent; java.awt.event.ActionListener;

import javax.swing.BorderFactory;

310

import import import import import import import import import import

javax.swing.ImageIcon; javax.swing.JButton; javax.swing.JFrame; javax.swing.JPanel; javax.swing.JScrollPane; javax.swing.JTextPane; javax.swing.JToolBar; javax.swing.text.Style; javax.swing.text.StyleConstants; javax.swing.text.StyledDocument;

public class DocumentModel extends JFrame { private StyledDocument doc; private JTextPane textpane; public DocumentModel() { setTitle("Document Model"); JToolBar toolbar = new JToolBar(); ImageIcon ImageIcon ImageIcon ImageIcon JButton JButton JButton JButton bold = new ImageIcon("bold.png"); italic = new ImageIcon("italic.png"); strike = new ImageIcon("strike.png"); underline = new ImageIcon("underline.png"); = = = = new new new new JButton(bold); JButton(italic); JButton(strike); JButton(underline);

boldb italb strib undeb

toolbar.add(boldb); toolbar.add(italb); toolbar.add(strib); toolbar.add(undeb); add(toolbar, BorderLayout.NORTH); JPanel panel = new JPanel(); panel.setLayout(new BorderLayout()); panel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); JScrollPane pane = new JScrollPane(); textpane = new JTextPane(); textpane.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8)); doc = textpane.getStyledDocument(); Style style = textpane.addStyle("Bold", null); StyleConstants.setBold(style, true); style = textpane.addStyle("Italic", null); StyleConstants.setItalic(style, true); style = textpane.addStyle("Underline", null); StyleConstants.setUnderline(style, true); style = textpane.addStyle("Strike", null); StyleConstants.setStrikeThrough(style, true);

311

boldb.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { doc.setCharacterAttributes(textpane.getSelectionStart(), textpane.getSelectionEnd() textpane.getSelectionStart(), textpane.getStyle("Bold"), false); } }); italb.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { doc.setCharacterAttributes(textpane.getSelectionStart(), textpane.getSelectionEnd() textpane.getSelectionStart(), textpane.getStyle("Italic"), false); } }); strib.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { doc.setCharacterAttributes(textpane.getSelectionStart(), textpane.getSelectionEnd() textpane.getSelectionStart(), textpane.getStyle("Strike"), false); } }); undeb.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { doc.setCharacterAttributes(textpane.getSelectionStart(), textpane.getSelectionEnd() textpane.getSelectionStart(), textpane.getStyle("Underline"), false); } }); pane.getViewport().add(textpane); panel.add(pane); add(panel); setSize(new Dimension(380, 320)); setLocationRelativeTo(null); setDefaultCloseOperation(EXIT_ON_CLOSE); setVisible(true);

public static void main(String[] args) { new DocumentModel(); } }

312

The example has a text pane and a toolbar. In the toolbar, we have four buttons, that change attributes of the text.
doc = textpane.getStyledDocument();

Here we get the styled document, which is a model for the text pane component.
Style style = textpane.addStyle("Bold", null); StyleConstants.setBold(style, true);

A style is a set of text attributes, such as color, size. Here we register a bold style for the text pane component. The registered styles can be retrieved at any time.
doc.setCharacterAttributes(textpane.getSelectionStart(), textpane.getSelectionEnd() - textpane.getSelectionStart(), textpane.getStyle("Bold"), false);

Here we change the attributes of the text. The parameters are the offset, length of the selection, the style and the boolean value replace. The offset is the beginning of the text, where we apply the bold text. We get the length value by substracting the selection end and selection start values. Boolean value false means, we are not replacing an old style with a new one, but we merge them. This means, if the text is underlined and we make it bold, the result is an underlined bold text.

Figure: Document model In this chapter, we have mentioned Swing models.

Drag and Drop in Swing


In computer graphical user interfaces, drag-and-drop is the action of (or support for the action of) clicking on a virtual object and dragging it to a different location or onto another virtual

313

object. In general, it can be used to invoke many kinds of actions, or create various types of associations between two abstract objects. (Wikipedia) Drag and drop functionality is one of the most visible aspects of the graphical user interface. Drag and drop operation enables users to do complex things intuitively. Usually, we can drag and drop two things. Data or some graphical objects. If we drag an image from one application to another, we drag and drop binary data. If we drag a tab in Firefox and move it to another place, we drag and drop a graphical component. The sheer amount of various classes involved with drag and drop operations in Java Swing toolkit might be overwhelming. The best way how to cope with this complexity is to create a small example for all situations. And slowly make it to more complex examples.

The component, where the drag operation begins must have a DragSource object registered. A DropTarget is an object responsible for accepting drops in an drag and drop operation. A Transferable encapsulates data being transferred. The transferred data can be of various type. A DataFlavor object provides information about the data being transferred. Several Swing components have already a built-in support for drag and drop operations. In such cases, a Swing programmer uses a TransferHandler to manage the drag and drop functionality. In situations, where there is no built-in support, the programmer has to create everything from scratch.

A simple drag and drop example


We will demonstrate a simple drag and drop example. We will work with built-in drag and drop support. We will utilize a TransferHandler class.
import import import import javax.swing.JButton; javax.swing.JFrame; javax.swing.JTextField; javax.swing.TransferHandler;

314

public class SimpleDnD extends JFrame { JTextField field; JButton button; public SimpleDnD() { setTitle("Simple Drag & Drop"); setLayout(null); button = new JButton("Button"); button.setBounds(200, 50, 90, 25); field = new JTextField(); field.setBounds(30, 50, 150, 25); add(button); add(field); field.setDragEnabled(true); button.setTransferHandler(new TransferHandler("text")); setSize(330, 150); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null); setVisible(true);

public static void main(String[] args) { new SimpleDnD(); } }

In our example we have a text field and a button. We can drag a text from the field and drop it onto the button.
field.setDragEnabled(true);

The text field has a built in support for dragging. We must enable it.
button.setTransferHandler(new TransferHandler("text"));

The TransferHandler is a class responsible for transfering data between components. The constructor takes a property name as a parameter.

Figure: Simple drag & drop example

315

Icon drag & drop


Some of the Java Swing components do not have built in drag support. JLabel component is such a component. We have to code the drag functionality ourselves. We will drag and drop icons. In the previous example, we used a text property. This time we will use an icon property.
import import import import import import import import import import import java.awt.FlowLayout; java.awt.event.MouseAdapter; java.awt.event.MouseEvent; java.awt.event.MouseListener; javax.swing.ImageIcon; javax.swing.JButton; javax.swing.JComponent; javax.swing.JFrame; javax.swing.JLabel; javax.swing.JPanel; javax.swing.TransferHandler;

public class IconDnD extends JFrame { public IconDnD() { setTitle("Icon Drag & Drop"); JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT, 50, 15)); ImageIcon icon1 = new ImageIcon("sad.png"); ImageIcon icon2 = new ImageIcon("plain.png"); ImageIcon icon3 = new ImageIcon("crying.png"); JButton button = new JButton(icon2); button.setFocusable(false); JLabel label1 JLabel label2 = new JLabel(icon1, JLabel.CENTER); = new JLabel(icon3, JLabel.CENTER);

MouseListener listener = new DragMouseAdapter(); label1.addMouseListener(listener); label2.addMouseListener(listener); label1.setTransferHandler(new TransferHandler("icon")); button.setTransferHandler(new TransferHandler("icon")); label2.setTransferHandler(new TransferHandler("icon")); panel.add(label1); panel.add(button); panel.add(label2); add(panel); pack(); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null); setVisible(true); }

316

class DragMouseAdapter extends MouseAdapter { public void mousePressed(MouseEvent e) { JComponent c = (JComponent) e.getSource(); TransferHandler handler = c.getTransferHandler(); handler.exportAsDrag(c, e, TransferHandler.COPY); } } public static void main(String[] args) { new IconDnD(); } }

In the code example, we have two labels and a button. Each component displays an icon. The two labels enable drag gestures, the button accepts a drop gesture.
MouseListener listener = new DragMouseAdapter(); label1.addMouseListener(listener); label2.addMouseListener(listener);

The drag support is not enabled by default for the label. We register a custom mouse adapter for both labels.
label1.setTransferHandler(new TransferHandler("icon")); button.setTransferHandler(new TransferHandler("icon")); label2.setTransferHandler(new TransferHandler("icon"));

Each of the three components has a TransferHandler class for an icon property. The TransferHandler is needed for both drag sources and drag targets as well.
JComponent c = (JComponent) e.getSource(); TransferHandler handler = c.getTransferHandler(); handler.exportAsDrag(c, e, TransferHandler.COPY);

These code lines initiate the drag support. We get the drag source. In our case it is a label instance. We get it's transfer handler object. And finally initiate the drag support with the exportAsDrag() method call.

Figure: Icon drag & drop example

Custom JList drop example


Some components do not have a default drop support. One of them is a JList component. There is a good reason for this. We don't know, if the data will be inserted into one row, or two or more rows. So we must implement manually the drop support for the list component. The comma separated text will be inserted into two or more rows. Text without a comma will go into one row. 317

import import import import import import import import import import import import import

java.awt.Dimension; java.awt.FlowLayout; java.awt.datatransfer.DataFlavor; java.awt.datatransfer.Transferable; javax.swing.DefaultListModel; javax.swing.DropMode; javax.swing.JFrame; javax.swing.JList; javax.swing.JPanel; javax.swing.JScrollPane; javax.swing.JTextField; javax.swing.ListSelectionModel; javax.swing.TransferHandler;

public class ListDrop extends JFrame { JTextField field; DefaultListModel model; public ListDrop() { setTitle("ListDrop"); JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT, 15, 15)); JScrollPane pane = new JScrollPane(); pane.setPreferredSize(new Dimension(180, 150)); model = new DefaultListModel(); JList list = new JList(model); list.setDropMode(DropMode.INSERT); list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); list.setTransferHandler(new ListHandler()); field = new JTextField(""); field.setPreferredSize(new Dimension(150, 25)); field.setDragEnabled(true); panel.add(field); pane.getViewport().add(list); panel.add(pane); add(panel); pack(); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null); setVisible(true); } private class ListHandler extends TransferHandler { public boolean canImport(TransferSupport support) { if (!support.isDrop()) { return false; }

318

return support.isDataFlavorSupported(DataFlavor.stringFlavor);

public boolean importData(TransferSupport support) { if (!canImport(support)) { return false; } Transferable transferable = support.getTransferable(); String line; try { line = (String) transferable.getTransferData(DataFlavor.stringFlavor); } catch (Exception e) { return false; } JList.DropLocation dl = (JList.DropLocation) support.getDropLocation(); int index = dl.getIndex(); String[] data = line.split(","); for (String item: data) { if (!item.isEmpty()) model.add(index++, item.trim()); } return true;

} }

public static void main(String[] args) { new ListDrop(); }

In the above example, we have a text field and a list component. The text in the text field can be dragged and dropped into the list. If the text is comma separated, the words will be split into rows. If not, the text will be inserted into one row.
list.setDropMode(DropMode.INSERT);

Here we specify a drop mode. The DropMode.INSERT specifies, that we are going to insert new items into the list component. If we chose DropMode.INSERT, we would drop new items onto the existing ones.
list.setTransferHandler(new ListHandler());

We set a custom transfer handler class.


field.setDragEnabled(true);

We enable the drag support for the text field component.


public boolean canImport(TransferSupport support) { if (!support.isDrop()) { return false; }

319

return support.isDataFlavorSupported(DataFlavor.stringFlavor);

This method tests suitability of a drop operation. Here we filter out the clipboard paste operations and allow only String drop operations. If the method returns false, the drop operation is cancelled.
public boolean importData(TransferSupport support) { ... }

The importData() method transfers the data from the clipboard or from the drag and drop operation to the drop location.
Transferable transferable = support.getTransferable();

The Transferable is the class, where the data is bundled.


line = (String) transferable.getTransferData(DataFlavor.stringFlavor);

We retrieve our data.


JList.DropLocation dl = (JList.DropLocation) support.getDropLocation(); int index = dl.getIndex();

We get a drop location for the list. We retrieve the index, where the data will be inserted.
String[] data = line.split(","); for (String item: data) { if (!item.isEmpty()) model.add(index++, item.trim()); }

Here we split the text into parts and insert it into one or more rows.

Figure: JList drop example The previous examples used components with built-in drag and drop support. Next we are going to create a drag and drop functionality from scratch.

320

Drag Gesture
In the following example we will inspect a simple drag gesture. We will work with several classes needed to create a drag gesture. A DragSource, DragGestureEvent, DragGestureListener, Transferable.
import import import import import import import import import import java.awt.Color; java.awt.Cursor; java.awt.Dimension; java.awt.FlowLayout; java.awt.datatransfer.DataFlavor; java.awt.datatransfer.Transferable; java.awt.dnd.DnDConstants; java.awt.dnd.DragGestureEvent; java.awt.dnd.DragGestureListener; java.awt.dnd.DragSource;

import javax.swing.JFrame; import javax.swing.JPanel; public class DragGesture extends JFrame implements DragGestureListener, Transferable { public DragGesture() { setTitle("Drag Gesture"); JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT, 50, 15)); JPanel left = new JPanel(); left.setBackground(Color.red); left.setPreferredSize(new Dimension(120, 120)); DragSource ds = new DragSource(); ds.createDefaultDragGestureRecognizer(left, DnDConstants.ACTION_COPY, this); panel.add(left); add(panel); pack(); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null); setVisible(true);

public void dragGestureRecognized(DragGestureEvent event) { System.out.println("grag gesture"); Cursor cursor = null; if (event.getDragAction() == DnDConstants.ACTION_COPY) { cursor = DragSource.DefaultCopyDrop; } event.startDrag(cursor, this); } public static void main(String[] args) { new DragGesture(); }

321

public Object getTransferData(DataFlavor flavor) { return null; } public DataFlavor[] getTransferDataFlavors() { return new DataFlavor[0]; } public boolean isDataFlavorSupported(DataFlavor flavor) { return false; } }

This simple example demostrates a drag gesture. The drag gesture is created, when we click on a component and move a mouse pointer, while the button is pressed. The example will show, how we can create a DragSource for a component.
public class DragGesture extends JFrame implements DragGestureListener, Transferable {

The DragGesture implements two interfaces. The DragGestureListener will listen for drag gestures. The Transferable handles data for a transfer operation. In the example, we will not transfer any data. We will only demonstrate a drag gesture. So the three necessary methods of the Transferable interface are left unimplemented.
DragSource ds = new DragSource(); ds.createDefaultDragGestureRecognizer(left, DnDConstants.ACTION_COPY, this);

Here we create a DragSource object and register it for the left panel. The DragSource is the entity responsible for the initiation of the Drag and Drop operation. The createDefaultDragGestureRecognizer() associates a drag source and DragGestureListener with a particular component.
public void dragGestureRecognized(DragGestureEvent event) { }

The dragGestureRecognized() method responds to a drag gesture.


Cursor cursor = null; if (event.getDragAction() == DnDConstants.ACTION_COPY) { cursor = DragSource.DefaultCopyDrop; } event.startDrag(cursor, this);

The startDrag() method of the DragGestureEvent finally starts the drag operation. We will specify two parameters. The cursor type and the Transferable object.
public Object getTransferData(DataFlavor flavor) { return null; } public DataFlavor[] getTransferDataFlavors() {

322

return new DataFlavor[0]; } public boolean isDataFlavorSupported(DataFlavor flavor) { return false; }

The object that implements the Transferable interface must implement these three methods. As I have already mentioned, we left these methods unimplemented for now.

A complex drag and drop example


In the following example, we create a complex drag and drop example. We create a drag source a drop target and a transferable object.
import import import import import import import import import import import import import import import import import import import import java.awt.Color; java.awt.Cursor; java.awt.Dimension; java.awt.FlowLayout; java.awt.datatransfer.DataFlavor; java.awt.datatransfer.Transferable; java.awt.datatransfer.UnsupportedFlavorException; java.awt.dnd.DnDConstants; java.awt.dnd.DragGestureEvent; java.awt.dnd.DragGestureListener; java.awt.dnd.DragSource; java.awt.dnd.DropTarget; java.awt.dnd.DropTargetAdapter; java.awt.dnd.DropTargetDropEvent; java.awt.event.ActionEvent; java.awt.event.ActionListener; javax.swing.JButton; javax.swing.JColorChooser; javax.swing.JFrame; javax.swing.JPanel;

public class ComplexExample extends JFrame implements DragGestureListener { JPanel panel; JPanel left; public ComplexExample() { setTitle("Complex Example"); panel = new JPanel(new FlowLayout(FlowLayout.LEFT, 50, 15));

JButton openb = new JButton("Choose Color"); openb.setFocusable(false); left = new JPanel(); left.setBackground(Color.red); left.setPreferredSize(new Dimension(100, 100)); openb.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) {

323

JColorChooser clr = new JColorChooser(); Color color = clr.showDialog(panel, "Choose Color", Color.white); }); } left.setBackground(color);

JPanel right = new JPanel(); right.setBackground(Color.white); right.setPreferredSize(new Dimension(100, 100)); new MyDropTargetListener(right); DragSource ds = new DragSource(); ds.createDefaultDragGestureRecognizer(left, DnDConstants.ACTION_COPY, this); panel.add(openb); panel.add(left); panel.add(right); add(panel); pack(); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null); setVisible(true); } public void dragGestureRecognized(DragGestureEvent event) { Cursor cursor = null; JPanel panel = (JPanel) event.getComponent(); Color color = panel.getBackground(); if (event.getDragAction() == DnDConstants.ACTION_COPY) { cursor = DragSource.DefaultCopyDrop; } } event.startDrag(cursor, new TransferableColor(color));

class MyDropTargetListener extends DropTargetAdapter { private DropTarget dropTarget; private JPanel panel; public MyDropTargetListener(JPanel panel) { this.panel = panel; dropTarget = new DropTarget(panel, DnDConstants.ACTION_COPY, this, true, null);

public void drop(DropTargetDropEvent event) { try { Transferable tr = event.getTransferable(); Color color = (Color) tr.getTransferData(TransferableColor.colorFlavor);

324

if (event.isDataFlavorSupported(TransferableColor.colorFlavor)) event.acceptDrop(DnDConstants.ACTION_COPY); this.panel.setBackground(color); event.dropComplete(true); return;

} }

} event.rejectDrop(); } catch (Exception e) { e.printStackTrace(); event.rejectDrop(); }

public static void main(String[] args) { new ComplexExample(); }

class TransferableColor implements Transferable { protected static DataFlavor colorFlavor = new DataFlavor(Color.class, "A Color Object"); protected static DataFlavor[] supportedFlavors = { colorFlavor, DataFlavor.stringFlavor, }; Color color; public TransferableColor(Color color) { this.color = color; } } public DataFlavor[] getTransferDataFlavors() { return supportedFlavors; public boolean isDataFlavorSupported(DataFlavor flavor) { if (flavor.equals(colorFlavor) || flavor.equals(DataFlavor.stringFlavor)) return true; return false;

public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException { if (flavor.equals(colorFlavor)) return color; else if (flavor.equals(DataFlavor.stringFlavor)) return color.toString(); else throw new UnsupportedFlavorException(flavor); } }

The code example shows a button and two panels. The button displays a color chooser dialog and sets a color for the first panel. The color can be dragged into the second panel.

325

This example will enhance the previous one. We will add a drop target and a custom transferable object.
new MyDropTargetListener(right);

We register a drop target listener with the right panel.


event.startDrag(cursor, new TransferableColor(color));

The startDrag() method has two parameters. A cursor and a Transferable object.
public MyDropTargetListener(JPanel panel) { this.panel = panel; dropTarget = new DropTarget(panel, DnDConstants.ACTION_COPY, this, true, null);

In the MyDropTargetListener we create a drop target object.


Transferable tr = event.getTransferable(); Color color = (Color) tr.getTransferData(TransferableColor.colorFlavor); if (event.isDataFlavorSupported(TransferableColor.colorFlavor)) { event.acceptDrop(DnDConstants.ACTION_COPY); this.panel.setBackground(color); event.dropComplete(true); return;

We get the data being transferred. In our case it is a color object. Here we set the color of the right panel.
event.rejectDrop();

If the conditions for a drag and drop operation are not fulfilled, we reject it.
protected static DataFlavor colorFlavor = new DataFlavor(Color.class, "A Color Object");

In the TransferableColor, we create a new DataFlavor object.


protected static DataFlavor[] supportedFlavors = { colorFlavor, DataFlavor.stringFlavor, };

Here we specify, what data flavors we support. In our case it is a custom defined color flavor and a pre-defined DataFlavor.stringFlavor.
public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException { if (flavor.equals(colorFlavor))

326

return color; else if (flavor.equals(DataFlavor.stringFlavor)) return color.toString(); else throw new UnsupportedFlavorException(flavor);

Return an object for a specific data flavor.

Figure: A complex example This part of the Java Swing tutorial was dedicated to Swing drap and drop operations.

Painting in Swing
Painting is used, when we want to change or enhance an existing widget. Or if we are creating a custom widget from scratch. To do the painting, we use the painting API provided by the Swing toolkit. The painting is done within the paintComponent() method. In the painting process, we use the Graphics2D object.

2D Vector Graphics
There are two different computer graphics. Vector and raster graphics. Raster graphics represents images as a collection of pixels. Vector graphics is the use of geometrical primitives such as points, lines, curves or polygons to represent images. These primitives are created using mathematical equations. Both types of computer graphics have advantages and disadvantages. The advantages of vector graphics over raster are:

smaller size ability to zoom indefinitely moving, scaling, filling or rotating does not degrade the quality of an image

Types of primitives

points lines polylines polygons 327

circles ellipses Splines

Points
The most simple graphics primitive is point. It is a single dot on the window. There is no method to draw a point in Swing. To draw a point, we use the drawLine() method. We use one point twice.
package com.zetcode; import import import import import import import import import java.awt.Color; java.awt.Dimension; java.awt.Graphics; java.awt.Graphics2D; java.awt.Insets; java.util.Random; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities;

class DrawPanel extends JPanel { private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; g2d.setColor(Color.blue); for (int i = 0; i <= 1000; i++) { Dimension size = getSize(); Insets insets = getInsets(); int w = size.width - insets.left - insets.right; int h = size.height - insets.top - insets.bottom; Random r = new Random(); int x = Math.abs(r.nextInt()) % w; int y = Math.abs(r.nextInt()) % h; g2d.drawLine(x, y, x, y);

} }

@Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g);

} }

public class PointsExample extends JFrame { public PointsExample() { initUI(); }

328

public final void initUI() { DrawPanel dpnl = new DrawPanel(); add(dpnl); setSize(250, 200); setTitle("Points"); setLocationRelativeTo(null); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { PointsExample ex = new PointsExample(); ex.setVisible(true); } });

} }

One point is difficult to observe. Therefore, we will randomly draw 1000 points on the panel surface.
class DrawPanel extends JPanel {

We are drawing on a custom drawing panel, which is a JPanel component. The drawing panel will later be added to a JFrame component.
@Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g);

Custom painting is performed inside the paintComponent() method, which we override. The super.paintComponent() method calls the method of the parent class. It does some necessary work to prepare component for drawing. Actual drawing is delegated to the doDrawing() method.
Graphics2D g2d = (Graphics2D) g;

Painting in Swing is done on the Graphics2D object.


g2d.setColor(Color.blue);

We will paint our points in blue color.


Dimension size = getSize(); Insets insets = getInsets();

The size of the window includes borders and titlebar. We don't paint there. 329

int w = int h =

size.width - insets.left - insets.right; size.height - insets.top - insets.bottom;

Here we calculate the area, where we will effectively paint our points.
Random r = new Random(); int x = Math.abs(r.nextInt()) % w; int y = Math.abs(r.nextInt()) % h;

We get a random number in range of the size of area, that we computed above.
g2d.drawLine(x, y, x, y);

Here we draw the point. As we already said, we use a drawLine() method. We specify the same point twice.

Figure: Points

Lines
A line is a simple graphics primitive. It is drawn using two points.
package com.zetcode; import import import import import import java.awt.BasicStroke; java.awt.Graphics; java.awt.Graphics2D; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities;

class DrawPanel extends JPanel { private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; float[] float[] float[] float[] dash1 dash2 dash3 dash4 = = = = {2f, {1f, {4f, {4f, 0f, 1f, 0f, 4f, 2f}; 1f}; 2f}; 1f};

g2d.drawLine(20, 40, 250, 40);

330

BasicStroke bs1 = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND, 1.0f, dash1, 2f); BasicStroke bs2 = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND, 1.0f, dash2, 2f); BasicStroke bs3 = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND, 1.0f, dash3, 2f); BasicStroke bs4 = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND, 1.0f, dash4, 2f); g2d.setStroke(bs1); g2d.drawLine(20, 80, 250, 80); g2d.setStroke(bs2); g2d.drawLine(20, 120, 250, 120); g2d.setStroke(bs3); g2d.drawLine(20, 160, 250, 160); g2d.setStroke(bs4); g2d.drawLine(20, 200, 250, 200);

@Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g); } }

public class LinesExample extends JFrame { public LinesExample() { initUI(); } public final void initUI() { DrawPanel dpnl = new DrawPanel(); add(dpnl); setSize(280, 270); setTitle("Lines"); setLocationRelativeTo(null); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { LinesExample ex = new LinesExample(); ex.setVisible(true); } });

} }

331

In the example, we draw five lines. The first line is drawn using the default values. Other will have a different stroke. The stroke is created using the BasicStroke class. It defines a basic set of rendering attributes for the outlines of graphics primitives.
float[] dash1 = { 2f, 0f, 2f };

Here we create a dash, that we use in the stroke object.


BasicStroke bs1 = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND, 1.0f, dash1, 2f )

This code creates a stroke. The stroke defines the line width, end caps, line joins, miter limit, dash and the dash phase.

Figure: Lines

Rectangles
To draw rectangles, we use the drawRect() method. To fill rectangles with the current color, we use the fillRect() method.
package com.zetcode; import import import import import import java.awt.Color; java.awt.Graphics; java.awt.Graphics2D; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities;

class DrawPanel extends JPanel { private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; g2d.setColor(new Color(212, 212, 212)); g2d.drawRect(10, 15, 90, 60); g2d.drawRect(130, 15, 90, 60);

332

g2d.drawRect(250, 15, 90, 60); g2d.drawRect(10, 105, 90, 60); g2d.drawRect(130, 105, 90, 60); g2d.drawRect(250, 105, 90, 60); g2d.drawRect(10, 195, 90, 60); g2d.drawRect(130, 195, 90, 60); g2d.drawRect(250, 195, 90, 60); g2d.setColor(new Color(125, 167, 116)); g2d.fillRect(10, 15, 90, 60); g2d.setColor(new Color(42, 179, 231)); g2d.fillRect(130, 15, 90, 60); g2d.setColor(new Color(70, 67, 123)); g2d.fillRect(250, 15, 90, 60); g2d.setColor(new Color(130, 100, 84)); g2d.fillRect(10, 105, 90, 60); g2d.setColor(new Color(252, 211, 61)); g2d.fillRect(130, 105, 90, 60); g2d.setColor(new Color(241, 98, 69)); g2d.fillRect(250, 105, 90, 60); g2d.setColor(new Color(217, 146, 54)); g2d.fillRect(10, 195, 90, 60); g2d.setColor(new Color(63, 121, 186)); g2d.fillRect(130, 195, 90, 60); g2d.setColor(new Color(31, 21, 1)); g2d.fillRect(250, 195, 90, 60); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g);

} }

public class RectanglesExample extends JFrame { public RectanglesExample() { initUI(); } public final void initUI() { DrawPanel dpnl = new DrawPanel(); add(dpnl); setSize(360, 300); setTitle("Rectangles"); setLocationRelativeTo(null); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

333

public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { RectanglesExample ex = new RectanglesExample(); ex.setVisible(true); } }); } }

In the example we draw nine colored rectangles.


g2d.setColor(new Color(212, 212, 212)); g2d.drawRect(10, 15, 90, 60); ...

We set the color of the outline of the rectangle to a soft gray color, so that it does not interfere with the fill color. To draw the outline of the rectangle, we use the drawRect() method. The first two parameters are the x and y values. The third and fourth are width and height.
g2d.fillRect(10, 15, 90, 60);

To fill the rectangle with a color, we use the fillRect() method.

Figure: Rectangles

Textures
A texture is a bitmap image applied to a shape. To work with textures in Java 2D, we use the TexturePaint class.
package com.zetcode; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Rectangle;

334

import java.awt.TexturePaint; import java.awt.image.BufferedImage; import javax.swing.JFrame; import javax.swing.JPanel; import import import import import java.io.IOException; java.util.logging.Logger; java.util.logging.Level; javax.imageio.ImageIO; javax.swing.SwingUtilities;

class DrawPanel extends JPanel { BufferedImage slate; BufferedImage java; BufferedImage pane; TexturePaint slatetp; TexturePaint javatp; TexturePaint panetp; public DrawPanel() { loadImages(); } private void loadImages() { try { slate = ImageIO.read(this.getClass().getResource("slate.png")); java = ImageIO.read(this.getClass().getResource("java.png")); pane = ImageIO.read(this.getClass().getResource("pane.png")); } catch (IOException ex) { Logger.getLogger(Textures.class.getName()).log(Level.SEVERE, null, ex);

} }

@Override public void paintComponent(Graphics g) { super.paintComponent(g); } doDrawing(g);

private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; slatetp = new TexturePaint(slate, new Rectangle(0, 0, 90, 60)); javatp = new TexturePaint(java, new Rectangle(0, 0, 90, 60)); panetp = new TexturePaint(pane, new Rectangle(0, 0, 90, 60)); g2d.setPaint(slatetp); g2d.fillRect(10, 15, 90, 60); g2d.setPaint(javatp); g2d.fillRect(130, 15, 90, 60); g2d.setPaint(panetp);

335

} }

g2d.fillRect(250, 15, 90, 60);

public class Textures extends JFrame { public Textures() { initUI(); } public final void initUI() { DrawPanel dpnl = new DrawPanel(); add(dpnl); setSize(360, 120); setTitle("Textures"); setLocationRelativeTo(null); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { Textures ex = new Textures(); ex.setVisible(true); } }); } }

In the code example, we fill three rectangles with three different textures.
slate = ImageIO.read(this.getClass().getResource("slate.png"));

Here we read the image into the buffered image.


slatetp = new TexturePaint(slate, new Rectangle(0, 0, 90, 60));

We create a TexturePaint class out of the buffered image.


g2d.setPaint(slatetp); g2d.fillRect(10, 15, 90, 60);

We fill a rectangle with a texture.

Figure: Textures

336

Gradients
In computer graphics, gradient is a smooth blending of shades from light to dark or from one color to another. In 2D drawing programs and paint programs, gradients are used to create colorful backgrounds and special effects as well as to simulate lights and shadows. (answers.com)
package com.zetcode; import import import import import import import java.awt.Color; java.awt.GradientPaint; java.awt.Graphics; java.awt.Graphics2D; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities;

class DrawPanel extends JPanel { private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; GradientPaint gp1 = new GradientPaint(5, 5, Color.red, 20, 20, Color.black, true); g2d.setPaint(gp1); g2d.fillRect(20, 20, 300, 40); GradientPaint gp2 = new GradientPaint(5, 25, Color.yellow, 20, 2, Color.black, true); g2d.setPaint(gp2); g2d.fillRect(20, 80, 300, 40); GradientPaint gp3 = new GradientPaint(5, 25, Color.green, 2, 2, Color.black, true); g2d.setPaint(gp3); g2d.fillRect(20, 140, 300, 40); GradientPaint gp4 = new GradientPaint(25, 25, Color.blue, 15, 25, Color.black, true); g2d.setPaint(gp4); g2d.fillRect(20, 200, 300, 40); GradientPaint gp5 = new GradientPaint(0, 0, Color.orange, 0, 20, Color.black, true); g2d.setPaint(gp5); g2d.fillRect(20, 260, 300, 40);

@Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g);

337

public class GradientsExample extends JFrame { public GradientsExample() { initUI(); } public final void initUI() { DrawPanel dpnl = new DrawPanel(); add(dpnl); setSize(350, 350); setTitle("Gradients"); setLocationRelativeTo(null); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { GradientsExample ex = new GradientsExample(); ex.setVisible(true); } });

} }

Our code example presents five rectangles with gradients.


GradientPaint gp4 = new GradientPaint(25, 25, Color.blue, 15, 25, Color.black, true);

To work with gradients, we use Java Swing's GradientPaint class. By manipulating the color values and the starting end ending points, we can get different results.
g2d.setPaint(gp5);

The gradient is activated calling the setPaint() method.

338

Figure: Gradients

Drawing text
Drawing is done with the drawString() method. We specify the string we want to draw and the position of the text on the window area.
package com.zetcode; import import import import import import import java.awt.Font; java.awt.Graphics; java.awt.Graphics2D; java.awt.RenderingHints; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities;

class DrawPanel extends JPanel { private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; RenderingHints rh = new RenderingHints( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); rh.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); g2d.setRenderingHints(rh); Font font = new Font("URW Chancery L", Font.BOLD, 21); g2d.setFont(font); g2d.drawString("Not marble, nor the gilded monuments", 20, 30);

339

20, 60);

g2d.drawString("Of princes, shall outlive this powerful rhyme;", g2d.drawString("But you shall shine more bright in these contents", 20, 90); g2d.drawString("Than unswept stone, besmear'd with sluttish time.", 20, 120); g2d.drawString("When wasteful war shall statues overturn,", 20, g2d.drawString("And broils root out the work of masonry,", 20, g2d.drawString("Nor Mars his sword, nor war's quick " + "fire shall burn", 20, 210); g2d.drawString("The living record of your memory.", 20, 240); g2d.drawString("'Gainst death, and all oblivious enmity", 20, 270); g2d.drawString("Shall you pace forth; your praise shall still " + "find room", 20, 300); g2d.drawString("Even in the eyes of all posterity", 20, 330); g2d.drawString("That wear this world out to the ending doom.", 20,

150); 180);

360); 390); 420); }

g2d.drawString("So, till the judgment that yourself arise,", 20, g2d.drawString("You live in this, and dwell in lovers' eyes.", 20,

@Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g); } }

public class DrawingText extends JFrame { public DrawingText() { initUI(); } public final void initUI() { DrawPanel dpnl = new DrawPanel(); add(dpnl); setSize(500, 470); setTitle("Sonnet55"); setLocationRelativeTo(null); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { DrawingText ex = new DrawingText(); ex.setVisible(true); } });

} }

340

In our example, we draw a sonnet on the panel component.


RenderingHints rh = new RenderingHints( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); rh.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); g2d.setRenderingHints(rh);

This code is to make our text look better. We apply a technique called antialiasing.
Font font = new Font("URW Chancery L", Font.BOLD, 21); g2d.setFont(font);

We choose a specific font for our text.


g2d.drawString("Not marble, nor the gilded monuments", 20, 30);

This is the code, that draws the text.

Images
On of the most important capabililies of a toolkit is the ability to display images. An image is an array of pixels. Each pixel represents a color at a given position. We can use components like JLabel to display an image, or we can draw it using the Java 2D API.
package com.zetcode; import import import import import import import import java.awt.Dimension; java.awt.Graphics; java.awt.Graphics2D; java.awt.Image; javax.swing.ImageIcon; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities;

class DrawPanel extends JPanel { Image img; public DrawPanel() { loadImage(); Dimension dm = new Dimension(img.getWidth(null), img.getHeight(null)); setPreferredSize(dm); } private void loadImage() { img = new ImageIcon("slanec.png").getImage(); } private void doDrawing(Graphics g) {

341

Graphics2D g2d = (Graphics2D) g; } g2d.drawImage(img, 0, 0, null);

@Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g); } }

public class ImageExample extends JFrame { public ImageExample() { initUI(); } public final void initUI() { DrawPanel dpnl = new DrawPanel(); add(dpnl); setTitle("Image"); pack(); setLocationRelativeTo(null); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { ImageExample ex = new ImageExample(); ex.setVisible(true); } });

} }

This example will draw an image on the panel. The image will fit the JFrame window.
public DrawPanel() { loadImage(); Dimension dm = new Dimension(img.getWidth(null), img.getHeight(null)); setPreferredSize(dm); }

In the constructor of the DrawPanel class, we call the loadImage() method. We determine the image dimensions and set the preffered size of the panel component. This will together with the pack() method display the image to fit exactly the window.
private void loadImage() { img = new ImageIcon("slanec.png").getImage(); }

342

The method loads an image from the disk. We use the ImageIcon class. This class simplyfies the work with the images in Java Swing.
g2d.drawImage(this.img, 0, 0, null);

The image is drawn using the drawImage() method. In this chapter, we did some painting.

Resizable components in Java Swing


In this part of the Java Swing tutorial, we will create a resizable component.

Resizable component
Resizable components are most often used when creating charts, diagrams and similar. The most common resizable component is a chart in a spreadsheet application. For example, when we create a chart in a OpenOffice application. The chart can be moved over the grid widget of the application and resized. In order to create a component that can be freely dragged over a panel, we need a panel with absolute positioning enabled. We must not use a layout manager. In our example, we will create a component (a JPanel) that we can freely move over a parent window and resize. In order to distinguish which component has a focus, we draw 8 small rectangles on the border of our resizable component. This way we know, that the component has focus. The rectangles serve as a dragging points, where we can draw the component and start resizing. I have learnt to use resizable components from this blog.
package resizablecomponent; import import import import java.awt.Color; java.awt.Dimension; java.awt.event.MouseAdapter; java.awt.event.MouseEvent;

import javax.swing.JFrame; import javax.swing.JPanel; /* ResizableComponent.java */ public class ResizableComponent extends JFrame { private JPanel panel = new JPanel(null); private Resizable resizer; public ResizableComponent() { add(panel); JPanel area = new JPanel();

343

area.setBackground(Color.white); resizer = new Resizable(area); resizer.setBounds(50, 50, 200, 150); panel.add(resizer); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize(new Dimension(350, 300)); setTitle("Resizable Component"); setLocationRelativeTo(null); addMouseListener(new MouseAdapter() { public void mousePressed(MouseEvent me) { requestFocus(); resizer.repaint(); } }); } public static void main(String[] args) { ResizableComponent rc = new ResizableComponent(); rc.setVisible(true); } }

The ResizableComponent sets up the panel and the component.


private JPanel panel = new JPanel(null);

We have already mentioned, that we cannot use any layout manager. We must use absolute positioning for resizable component. By providing null to the constructor, we create a panel with absolute positioning.
addMouseListener(new MouseAdapter() { public void mousePressed(MouseEvent me) { requestFocus(); resizer.repaint();

} });

If we press on the parent panel, e.g outside the resizable component, we grab focus and repaint the component. The rectangles over he border will disappear.
package resizablecomponent; import import import import import import import java.awt.Color; java.awt.Component; java.awt.Cursor; java.awt.Graphics; java.awt.Insets; java.awt.Rectangle; java.awt.event.MouseEvent;

import javax.swing.SwingConstants; import javax.swing.border.Border;

344

// ResizableBorder.java public class ResizableBorder implements Border { private int dist = 8; int locations[] = { SwingConstants.NORTH, SwingConstants.SOUTH, SwingConstants.WEST, SwingConstants.EAST, SwingConstants.NORTH_WEST, SwingConstants.NORTH_EAST, SwingConstants.SOUTH_WEST, SwingConstants.SOUTH_EAST }; int cursors[] = { Cursor.N_RESIZE_CURSOR, Cursor.S_RESIZE_CURSOR, Cursor.W_RESIZE_CURSOR, Cursor.E_RESIZE_CURSOR, Cursor.NW_RESIZE_CURSOR, Cursor.NE_RESIZE_CURSOR, Cursor.SW_RESIZE_CURSOR, Cursor.SE_RESIZE_CURSOR }; public ResizableBorder(int dist) { this.dist = dist; } public Insets getBorderInsets(Component component) { return new Insets(dist, dist, dist, dist); } public boolean isBorderOpaque() { return false; } public void paintBorder(Component component, Graphics g, int x, int y, int w, int h) { g.setColor(Color.black); g.drawRect(x + dist / 2, y + dist / 2, w - dist, h - dist); if (component.hasFocus()) { for (int i = 0; i < locations.length; i++) { Rectangle rect = getRectangle(x, y, w, h, locations[i]); g.setColor(Color.WHITE); g.fillRect(rect.x, rect.y, rect.width - 1, rect.height - 1); g.setColor(Color.BLACK); g.drawRect(rect.x, rect.y, rect.width - 1, rect.height - 1); } } { }

private Rectangle getRectangle(int x, int y, int w, int h, int location) switch (location) { case SwingConstants.NORTH: return new Rectangle(x + w / 2 - dist / 2, y, dist, dist); case SwingConstants.SOUTH: return new Rectangle(x + w / 2 - dist / 2, y + h - dist, dist, dist); case SwingConstants.WEST: return new Rectangle(x, y + h / 2 - dist / 2, dist, dist);

345

case SwingConstants.EAST: return new Rectangle(x + w - dist, y + h / 2 - dist / 2, dist, dist); case SwingConstants.NORTH_WEST: return new Rectangle(x, y, dist, dist); case SwingConstants.NORTH_EAST: return new Rectangle(x + w - dist, y, dist, dist); case SwingConstants.SOUTH_WEST: return new Rectangle(x, y + h - dist, dist, dist); case SwingConstants.SOUTH_EAST: return new Rectangle(x + w - dist, y + h - dist, dist, dist); } return null;

public int getCursor(MouseEvent me) { Component c = me.getComponent(); int w = c.getWidth(); int h = c.getHeight(); for (int i = 0; i < locations.length; i++) { Rectangle rect = getRectangle(0, 0, w, h, locations[i]); if (rect.contains(me.getPoint())) return cursors[i]; } } } return Cursor.MOVE_CURSOR;

The ResizableBorder is responsible for drawing the border of the component and determining the type of the cursor to use.
int locations[] = { SwingConstants.NORTH, SwingConstants.SOUTH, SwingConstants.WEST, SwingConstants.EAST, SwingConstants.NORTH_WEST, SwingConstants.NORTH_EAST, SwingConstants.SOUTH_WEST, SwingConstants.SOUTH_EAST };

These are locations, where we will draw rectangles. These locations are grabbing points, where we can grab the component and resize it.
g.setColor(Color.black); g.drawRect(x + dist / 2, y + dist / 2, w - dist, h - dist);

In the paintBorder() method, we draw the border of the resizable component. The upper code draws the outer border of the component.
if (component.hasFocus()) { for (int i = 0; i < locations.length; i++) { Rectangle rect = getRectangle(x, y, w, h, locations[i]); g.setColor(Color.WHITE); g.fillRect(rect.x, rect.y, rect.width - 1, rect.height - 1); g.setColor(Color.BLACK); g.drawRect(rect.x, rect.y, rect.width - 1, rect.height - 1);

346

The eight rectangles are drawn only in case that the resizable component has currently focus. Finally, the getRectangle() method gets the coordinates of the rectangles and the getCursor() methods gets the cursor type for the grab point in question.
package resizablecomponent; import import import import import import java.awt.BorderLayout; java.awt.Component; java.awt.Cursor; java.awt.Point; java.awt.Rectangle; java.awt.event.MouseEvent;

import javax.swing.JComponent; import javax.swing.event.MouseInputAdapter; import javax.swing.event.MouseInputListener; // Resizable.java public class Resizable extends JComponent { public Resizable(Component comp) { this(comp, new ResizableBorder(8)); } public Resizable(Component comp, ResizableBorder border) { setLayout(new BorderLayout()); add(comp); addMouseListener(resizeListener); addMouseMotionListener(resizeListener); setBorder(border); } private void resize() { if (getParent() != null) { ((JComponent)getParent()).revalidate(); } } MouseInputListener resizeListener = new MouseInputAdapter() { public void mouseMoved(MouseEvent me) { if (hasFocus()) { ResizableBorder border = (ResizableBorder)getBorder(); setCursor(Cursor.getPredefinedCursor(border.getCursor(me))); } } public void mouseExited(MouseEvent mouseEvent) { setCursor(Cursor.getDefaultCursor()); } private int cursor; private Point startPos = null; public void mousePressed(MouseEvent me) { ResizableBorder border = (ResizableBorder)getBorder();

347

cursor = border.getCursor(me); startPos = me.getPoint(); requestFocus(); repaint(); } public void mouseDragged(MouseEvent me) { if (startPos != null) { int int int int x y w h = = = = getX(); getY(); getWidth(); getHeight();

int dx = me.getX() - startPos.x; int dy = me.getY() - startPos.y; switch (cursor) { case Cursor.N_RESIZE_CURSOR: if (!(h - dy < 50)) { setBounds(x, y + dy, w, h - dy); resize(); } break; case Cursor.S_RESIZE_CURSOR: if (!(h + dy < 50)) { setBounds(x, y, w, h + dy); startPos = me.getPoint(); resize(); } break; case Cursor.W_RESIZE_CURSOR: if (!(w - dx < 50)) { setBounds(x + dx, y, w - dx, h); resize(); } break; case Cursor.E_RESIZE_CURSOR: if (!(w + dx < 50)) { setBounds(x, y, w + dx, h); startPos = me.getPoint(); resize(); } break; case Cursor.NW_RESIZE_CURSOR: if (!(w - dx < 50) && !(h - dy < 50)) { setBounds(x + dx, y + dy, w - dx, h - dy); resize(); } break; case Cursor.NE_RESIZE_CURSOR: if (!(w + dx < 50) && !(h - dy < 50)) { setBounds(x, y + dy, w + dx, h - dy); startPos = new Point(me.getX(), startPos.y); resize();

348

} break; case Cursor.SW_RESIZE_CURSOR: if (!(w - dx < 50) && !(h + dy < 50)) { setBounds(x + dx, y, w - dx, h + dy); startPos = new Point(startPos.x, me.getY()); resize(); } break; case Cursor.SE_RESIZE_CURSOR: if (!(w + dx < 50) && !(h + dy < 50)) { setBounds(x, y, w + dx, h + dy); startPos = me.getPoint(); resize(); } break; case Cursor.MOVE_CURSOR: Rectangle bounds = getBounds(); bounds.translate(dx, dy); setBounds(bounds); resize(); } setCursor(Cursor.getPredefinedCursor(cursor)); } }

public void mouseReleased(MouseEvent mouseEvent) { startPos = null; } };

The Resizable class represents the component, that is being resized and moved on the window.
private void resize() { if (getParent() != null) { ((JComponent)getParent()).revalidate(); } }

The resize() method is called, after we have resized the component. The revalidate() method will cause the component to be redrawn.
MouseInputListener resizeListener = new MouseInputAdapter() { public void mouseMoved(MouseEvent me) { if (hasFocus()) { ResizableBorder border = (ResizableBorder)getBorder(); setCursor(Cursor.getPredefinedCursor(border.getCursor(me))); } }

349

We change the cursor type, when we hover the cursor over the grip points. The cursor type changes only if the component has focus.
public void mousePressed(MouseEvent me) { ResizableBorder border = (ResizableBorder)getBorder(); cursor = border.getCursor(me); startPos = me.getPoint(); requestFocus(); repaint(); }

If we click on the resizable component, we change the cursor, get the starting point of dragging, give focus to the component and redraw it.
int int int int x y w h = = = = getX(); getY(); getWidth(); getHeight();

int dx = me.getX() - startPos.x; int dy = me.getY() - startPos.y;

In the mouseDragged() method, we determine the x, y coordinates of the cursor, width and height of the component. We calculate the distances, that we make during the mouse drag event.
case Cursor.N_RESIZE_CURSOR: if (!(h - dy < 50)) { setBounds(x, y + dy, w, h - dy); resize(); } break;

For all resizing we ensure, that the component is not smaller than 50 px. Otherwise, we could make it so small, that we would eventually hide the component. The setBounds() method relocates and resizes the component.

350

Figure: Resizable component In this part of the Java Swing tutorial, we have created a resizable component.

The Puzzle in Java Swing


In this chapter, we will create a simple puzzle game in Java Swing toolkit.

Puzzle
We have an image of a Sid character from the Ice Age movie. It is cut into 12 pieces. The goal is to form the picture.
package zetcode; import import import import import import import import import import import import import import import java.awt.BorderLayout; java.awt.Dimension; java.awt.GridLayout; java.awt.Image; java.awt.event.ActionEvent; java.awt.event.ActionListener; java.awt.image.CropImageFilter; java.awt.image.FilteredImageSource; javax.swing.Box; javax.swing.ImageIcon; javax.swing.JButton; javax.swing.JFrame; javax.swing.JLabel; javax.swing.JPanel; javax.swing.SwingUtilities;

public class Puzzle extends JFrame implements ActionListener { private JPanel centerPanel; private JButton button; private JLabel label; private Image source; private Image image; int[][] pos; int width, height; public Puzzle() { initUI(); } public final void initUI() { pos = new int[][]{ {0, 1, 2}, {3, 4, 5}, {6, 7, 8}, {9, 10, 11} };

351

centerPanel = new JPanel(); centerPanel.setLayout(new GridLayout(4, 4, 0, 0)); ImageIcon sid = new ImageIcon(Puzzle.class.getResource("icesid.jpg")); source = sid.getImage(); width = sid.getIconWidth(); height = sid.getIconHeight(); add(Box.createRigidArea(new Dimension(0, 5)), BorderLayout.NORTH); add(centerPanel, BorderLayout.CENTER); for (int i = 0; i < 4; i++) { for (int j = 0; j < 3; j++) { if (j == 2 && i == 3) { label = new JLabel(""); centerPanel.add(label); } else { button = new JButton(); button.addActionListener(this); centerPanel.add(button); image = createImage(new FilteredImageSource(source.getSource(), new CropImageFilter(j * width / 3, i * height / 4, (width / 3) + 1, height / 4))); button.setIcon(new ImageIcon(image)); } } } setSize(325, 275); setTitle("Puzzle"); setResizable(false); setLocationRelativeTo(null); setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

public void actionPerformed(ActionEvent e) { JButton button = (JButton) e.getSource(); Dimension size = button.getSize(); int int int int int int int labelX = label.getX(); labelY = label.getY(); buttonX = button.getX(); buttonY = button.getY(); buttonPosX = buttonX / size.width; buttonPosY = buttonY / size.height; buttonIndex = pos[buttonPosY][buttonPosX];

if (labelX == buttonX && (labelY - buttonY) == size.height) { int labelIndex = buttonIndex + 3; centerPanel.remove(buttonIndex);

352

centerPanel.add(label, buttonIndex); centerPanel.add(button, labelIndex); centerPanel.validate();

if (labelX == buttonX && (labelY - buttonY) == -size.height) { int labelIndex = buttonIndex - 3; centerPanel.remove(labelIndex); centerPanel.add(button, labelIndex); centerPanel.add(label, buttonIndex); centerPanel.validate(); } if (labelY == buttonY && (labelX - buttonX) == size.width) { int labelIndex = buttonIndex + 1; centerPanel.remove(buttonIndex); centerPanel.add(label, buttonIndex); centerPanel.add(button, labelIndex); centerPanel.validate(); } if (labelY == buttonY && (labelX - buttonX) == -size.width) { int labelIndex = buttonIndex - 1; centerPanel.remove(buttonIndex); centerPanel.add(label, labelIndex); centerPanel.add(button, labelIndex); centerPanel.validate(); } }

public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { Puzzle puzzle = new Puzzle(); puzzle.setVisible(true); } } } });

The goal of this little game is to form the original picture. We move the buttons by clicking on them. Only buttons adjacent to the label can be moved.
pos = new {0, {3, {6, {9, }; int[][] { 1, 2}, 4, 5}, 7, 8}, 10, 11}

These are the positions of the image parts.

353

ImageIcon sid = new ImageIcon(Puzzle.class.getResource("icesid.jpg")); source = sid.getImage();

We use the ImageIcon class to load the image.


for (int i = 0; i < 4; i++) { for (int j = 0; j < 3; j++) { if (j == 2 && i == 3) { label = new JLabel(""); centerPanel.add(label); } else { button = new JButton(); button.addActionListener(this); centerPanel.add(button); image = createImage(new FilteredImageSource(source.getSource(), new CropImageFilter(j * width / 3, i * height / 4, (width / 3) + 1, height / 4))); button.setIcon(new ImageIcon(image)); } } }

The code creates 11 buttons and one label. We crop the image into pieces and place them on the buttons.
int int int int labelX = label.getX(); labelY = label.getY(); buttonX = button.getX(); buttonY = button.getY();

We get the x, y coordinates of the button that we hit and an empty label. The x, y coordinates are important in the logic of the program.
int buttonPosX = buttonX / size.width; int buttonPosY = buttonY / size.height; int buttonIndex = pos[buttonPosY][buttonPosX];

Here we get the index of the button in the two dimensional array of the button positions.
if (labelX == buttonX && (labelY - buttonY) == size.height ) { int labelIndex = buttonIndex + 3; centerPanel.remove(buttonIndex); centerPanel.add(label, buttonIndex); centerPanel.add(button,labelIndex); centerPanel.validate();

In this case, we check if we clicked on the button, that is right above the empty label. If it is above the label, they share the x coordinate. If the button is right above the label, the equation (labelY - buttonY) == size.height is true.

354

Figure: Puzzle This was a puzzle game.

Tetris
In this chapter, we will create a Tetris game clone in Java Swing.

Tetris
The Tetris game is one of the most popular computer games ever created. The original game was designed and programmed by a Russian programmer Alexey Pajitnov in 1985. Since then, Tetris is available on almost every computer platform in lots of variations. Tetris is called a falling block puzzle game. In this game, we have seven different shapes called tetrominoes. S-shape, Z-shape, T-shape, L-shape, Line-shape, MirroredL-shape and a Square-shape. Each of these shapes is formed with four squares. The shapes are falling down the board. The object of the Tetris game is to move and rotate the shapes, so that they fit as much as possible. If we manage to form a row, the row is destroyed and we score. We play the Tetris game until we top out.

Figure: Tetrominoes

The development
We do not have images for our Tetris game, we draw the tetrominoes using Swing drawing API. Behind every computer game, there is a mathematical model. So it is in Tetris. 355

Some ideas behind the game.


We use a Timer class to create a game cycle The tetrominoes are drawn The shapes move on a square by square basis (not pixel by pixel) Mathematically a board is a simple list of numbers

I have simplified the game a bit, so that it is easier to understand. The game starts immediately, after it is launched. We can pause the game by pressing the p key. The space key will drop the Tetris piece immediately to the bottom. The d key will drop the piece one line down. (It can be used to speed up the falling a bit.) The game goes at constant speed, no acceleration is implemented. The score is the number of lines, that we have removed. Tetris.java
package com.zetcode; import java.awt.BorderLayout; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.SwingUtilities; public class Tetris extends JFrame { private JLabel statusbar; public Tetris() { initUI(); } private void initUI() { statusbar = new JLabel(" 0"); add(statusbar, BorderLayout.SOUTH); Board board = new Board(this); add(board); board.start(); setSize(200, 400); setTitle("Tetris"); setDefaultCloseOperation(EXIT_ON_CLOSE); setLocationRelativeTo(null); } public JLabel getStatusBar() { } return statusbar; public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() {

356

Tetris game = new Tetris(); game.setVisible(true); }); } } }

In the Tetris.java file, we set up the game. We create a board on which we play the game. We create a statusbar.
board.start();

The start() method starts the Tetris game. Immediately, after the window appears on the screen. Shape.java
package com.zetcode; import java.util.Random; public class Shape { protected enum Tetrominoes { NoShape, ZShape, SShape, LineShape, TShape, SquareShape, LShape, MirroredLShape }; private Tetrominoes pieceShape; private int coords[][]; private int[][][] coordsTable; public Shape() { coords = new int[4][2]; setShape(Tetrominoes.NoShape);

public void setShape(Tetrominoes shape) { coordsTable = new int[][][] { { { 0, 0 }, { 0, 0 }, { 0, 0 }, { { 0, -1 }, { 0, 0 }, { -1, 0 }, { { 0, -1 }, { 0, 0 }, { 1, 0 }, { { 0, -1 }, { 0, 0 }, { 0, 1 }, { { -1, 0 }, { 0, 0 }, { 1, 0 }, { { 0, 0 }, { 1, 0 }, { 0, 1 }, { { -1, -1 }, { 0, -1 }, { 0, 0 }, { { 1, -1 }, { 0, -1 }, { 0, 0 }, }; for (int i = 0; i < 4 ; i++) { for (int j = 0; j < 2; ++j) { } } } pieceShape = shape; coords[i][j] = coordsTable[shape.ordinal()][i][j]; { { { { { { { { 0, 0 } }, -1, 1 } }, 1, 1 } }, 0, 2 } }, 0, 1 } }, 1, 1 } }, 0, 1 } }, 0, 1 } }

357

private void setX(int index, int private void setY(int index, int public int x(int index) { return public int y(int index) { return public Tetrominoes getShape() { public void setRandomShape() {

x) { coords[index][0] = x; } y) { coords[index][1] = y; } coords[index][0]; } coords[index][1]; } return pieceShape; }

Random r = new Random(); int x = Math.abs(r.nextInt()) % 7 + 1; Tetrominoes[] values = Tetrominoes.values(); setShape(values[x]);

public int minX() { int m = coords[0][0]; for (int i=0; i < 4; i++) { m = Math.min(m, coords[i][0]); } } return m;

public int minY() { int m = coords[0][1]; for (int i=0; i < 4; i++) { } } public Shape rotateLeft() { if (pieceShape == Tetrominoes.SquareShape) return this; Shape result = new Shape(); result.pieceShape = pieceShape; for (int i = 0; i < 4; ++i) { result.setX(i, y(i)); result.setY(i, -x(i)); } } return result; m = Math.min(m, coords[i][1]);

return m;

public Shape rotateRight() { if (pieceShape == Tetrominoes.SquareShape) return this;

358

Shape result = new Shape(); result.pieceShape = pieceShape; for (int i = 0; i < 4; ++i) { result.setX(i, -y(i)); result.setY(i, x(i)); } } } return result;

The Shape class provides information about a Tetris piece.


protected enum Tetrominoes { NoShape, ZShape, SShape, LineShape, TShape, SquareShape, LShape, MirroredLShape };

The Tetrominoes enum holds all seven Tetris shapes. Plus the empty shape called here NoShape.
public Shape() { coords = new int[4][2]; setShape(Tetrominoes.NoShape);

This is the constructor of the Shape class. The coords array holds the actual coordinates of a Tetris piece.
coordsTable = new int[][][] { { { 0, 0 }, { 0, 0 }, { 0, 0 }, { { 0, -1 }, { 0, 0 }, { -1, 0 }, { { 0, -1 }, { 0, 0 }, { 1, 0 }, { { 0, -1 }, { 0, 0 }, { 0, 1 }, { { -1, 0 }, { 0, 0 }, { 1, 0 }, { { 0, 0 }, { 1, 0 }, { 0, 1 }, { { -1, -1 }, { 0, -1 }, { 0, 0 }, { { 1, -1 }, { 0, -1 }, { 0, 0 }, }; { { { { { { { { 0, 0 } }, -1, 1 } }, 1, 1 } }, 0, 2 } }, 0, 1 } }, 1, 1 } }, 0, 1 } }, 0, 1 } }

The coordsTable array holds all possible coordinate values of our Tetris pieces. This is a template from which all pieces take their coordinate values.
for (int i = 0; i < 4 ; i++) { for (int j = 0; j < 2; ++j) { } } coords[i][j] = coordsTable[shape.ordinal()][i][j];

Here we put one row of the coordinate values from the coordsTable to a coords array of a Tetris piece. Note the use of the ordinal() method. In C++, an enum type is esencially an

359

integer. Unlike in C++, Java enums are full classes. And the ordinal() method returns the current position of the enum type in the enum object. The following image will help understand the coordinate values a bit more. The coords array saves the coordinates of the Tetris piece. For example, numbers { 0, -1 }, { 0, 0 }, { -1, 0 }, { -1, -1 } , represent a rotated S-shape. The following diagram illustrates the shape.

Figure: Coordinates
public Shape rotateLeft() { if (pieceShape == Tetrominoes.SquareShape) return this; Shape result = new Shape(); result.pieceShape = pieceShape; for (int i = 0; i < 4; ++i) { result.setX(i, y(i)); result.setY(i, -x(i)); } } return result;

This code rotates the piece to the left. The square does not have to be rotated. That's why we simply return the reference to the current object. Looking at the previous image will help to understand the rotation. Board.java
package com.zetcode; import import import import import import import import import import java.awt.Color; java.awt.Dimension; java.awt.Graphics; java.awt.event.ActionEvent; java.awt.event.ActionListener; java.awt.event.KeyAdapter; java.awt.event.KeyEvent; javax.swing.JLabel; javax.swing.JPanel; javax.swing.Timer;

360

import com.zetcode.Shape.Tetrominoes; public class Board extends JPanel implements ActionListener { private final int BoardWidth = 10; private final int BoardHeight = 22; private private private private private private private private private private Timer timer; boolean isFallingFinished = false; boolean isStarted = false; boolean isPaused = false; int numLinesRemoved = 0; int curX = 0; int curY = 0; JLabel statusbar; Shape curPiece; Tetrominoes[] board;

public Board(Tetris parent) { initBoard(parent); } private void initBoard(Tetris parent) { setFocusable(true); curPiece = new Shape(); timer = new Timer(400, this); timer.start(); statusbar = parent.getStatusBar(); board = new Tetrominoes[BoardWidth * BoardHeight]; addKeyListener(new TAdapter()); clearBoard();

@Override public void actionPerformed(ActionEvent e) { if (isFallingFinished) { isFallingFinished = false; newPiece(); } else { } } private int squareWidth() { return (int) getSize().getWidth() / BoardWidth; } private int squareHeight() { return (int) getSize().getHeight() / BoardHeight; } private Tetrominoes shapeAt(int x, int y) { return board[(y * BoardWidth) + x]; } public void start() if (isPaused) { oneLineDown();

361

return; isStarted = true; isFallingFinished = false; numLinesRemoved = 0; clearBoard(); newPiece(); timer.start(); {

private void pause() if (!isStarted) return;

isPaused = !isPaused; if (isPaused) { timer.stop(); statusbar.setText("paused"); } else { timer.start(); statusbar.setText(String.valueOf(numLinesRemoved)); } } repaint();

private void doDrawing(Graphics g) { Dimension size = getSize(); int boardTop = (int) size.getHeight() - BoardHeight * squareHeight(); for (int i = 0; i < BoardHeight; ++i) { for (int j = 0; j < BoardWidth; ++j) { Tetrominoes shape = shapeAt(j, BoardHeight - i - 1); if (shape != Tetrominoes.NoShape) drawSquare(g, 0 + j * squareWidth(), boardTop + i * squareHeight(), shape); } }

if (curPiece.getShape() != Tetrominoes.NoShape) { for (int i = 0; i < 4; ++i) { int x = curX + curPiece.x(i); int y = curY - curPiece.y(i); drawSquare(g, 0 + x * squareWidth(), boardTop + (BoardHeight - y - 1) * curPiece.getShape());

squareHeight(), } }

362

} @Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g);

private void dropDown() { int newY = curY; while (newY > 0) { if (!tryMove(curPiece, curX, newY - 1)) break; --newY; } } pieceDropped(); {

private void oneLineDown()

if (!tryMove(curPiece, curX, curY - 1)) pieceDropped();

private void clearBoard() { for (int i = 0; i < BoardHeight * BoardWidth; ++i) board[i] = Tetrominoes.NoShape; } private void pieceDropped() { for (int i = 0; i < 4; ++i) { int x = curX + curPiece.x(i); int y = curY - curPiece.y(i); board[(y * BoardWidth) + x] = curPiece.getShape();

removeFullLines(); if (!isFallingFinished) newPiece(); {

private void newPiece()

curPiece.setRandomShape(); curX = BoardWidth / 2 + 1; curY = BoardHeight - 1 + curPiece.minY(); if (!tryMove(curPiece, curX, curY)) { curPiece.setShape(Tetrominoes.NoShape); timer.stop();

363

isStarted = false; statusbar.setText("game over"); } }

private boolean tryMove(Shape newPiece, int newX, int newY) { for (int i = 0; i < 4; ++i) { int x = newX + newPiece.x(i); int y = newY - newPiece.y(i); if (x < 0 || x >= BoardWidth || y < 0 || y >= BoardHeight) return false; if (shapeAt(x, y) != Tetrominoes.NoShape) return false;

curPiece = newPiece; curX = newX; curY = newY; repaint(); return true; } private void removeFullLines() { int numFullLines = 0; for (int i = BoardHeight - 1; i >= 0; --i) { boolean lineIsFull = true; for (int j = 0; j < BoardWidth; ++j) { if (shapeAt(j, i) == Tetrominoes.NoShape) { lineIsFull = false; break; } } if (lineIsFull) { ++numFullLines; for (int k = i; k < BoardHeight - 1; ++k) { for (int j = 0; j < BoardWidth; ++j) board[(k * BoardWidth) + j] = shapeAt(j, k + 1); } }

if (numFullLines > 0) { numLinesRemoved += numFullLines; statusbar.setText(String.valueOf(numLinesRemoved)); isFallingFinished = true; curPiece.setShape(Tetrominoes.NoShape); repaint(); } }

364

private void drawSquare(Graphics g, int x, int y, Tetrominoes shape) Color colors[] = { new Color(102, new Color(204, new Color(102, }; new Color(0, 0, 0), new Color(204, 102, 102), 204, 102), new Color(102, 102, 204), 204, 102), new Color(204, 102, 204), 204, 204), new Color(218, 170, 0)

Color color = colors[shape.ordinal()]; g.setColor(color); g.fillRect(x + 1, y + 1, squareWidth() - 2, squareHeight() - 2); g.setColor(color.brighter()); g.drawLine(x, y + squareHeight() - 1, x, y); g.drawLine(x, y, x + squareWidth() - 1, y); g.setColor(color.darker()); g.drawLine(x + 1, y + squareHeight() - 1, x + squareWidth() - 1, y + squareHeight() - 1); g.drawLine(x + squareWidth() - 1, y + squareHeight() - 1, x + squareWidth() - 1, y + 1); } class TAdapter extends KeyAdapter { @Override public void keyPressed(KeyEvent e) { { if (!isStarted || curPiece.getShape() == Tetrominoes.NoShape) } return;

int keycode = e.getKeyCode(); if (keycode == 'p' || keycode == 'P') { pause(); return; } if (isPaused) return; switch (keycode) { case KeyEvent.VK_LEFT: tryMove(curPiece, curX - 1, curY); break; case KeyEvent.VK_RIGHT: tryMove(curPiece, curX + 1, curY); break; case KeyEvent.VK_DOWN: tryMove(curPiece.rotateRight(), curX, curY); break; case KeyEvent.VK_UP: tryMove(curPiece.rotateLeft(), curX, curY);

365

break; case KeyEvent.VK_SPACE: dropDown(); break; case 'd': oneLineDown(); break; case 'D': oneLineDown(); break; } } } }

Finally, we have the Board.java file. This is where the game logic is located.
... private private private private private private ... boolean isFallingFinished = false; boolean isStarted = false; boolean isPaused = false; int numLinesRemoved = 0; int curX = 0; int curY = 0;

We initialize some important variables. The isFallingFinished variable determines, if the Tetris shape has finished falling and we then need to create a new shape. The numLinesRemoved counts the number of lines, we have removed so far. The curX and curY variables determine the actual position of the falling Tetris shape.
setFocusable(true);

We must explicitly call the setFocusable() method. From now, the board has the keyboard input.
timer = new Timer(400, this); timer.start();

object fires one or more action events after a specified delay. In our case, the timer calls the actionPerformed() method each 400ms.
Timer @Override public void actionPerformed(ActionEvent e) { if (isFallingFinished) { isFallingFinished = false; newPiece(); } else { oneLineDown(); } }

366

The actionPerformed() method checks if the falling has finished. If so, a new piece is created. If not, the falling Tetris piece goes one line down. Inside the doDrawing() method, we draw all objects on the board. The painting has two steps.
for (int i = 0; i < BoardHeight; ++i) { for (int j = 0; j < BoardWidth; ++j) { Tetrominoes shape = shapeAt(j, BoardHeight - i - 1); if (shape != Tetrominoes.NoShape) drawSquare(g, 0 + j * squareWidth(), boardTop + i * squareHeight(), shape);

} }

In the first step we paint all the shapes, or remains of the shapes, that have been dropped to the bottom of the board. All the squares are remembered in the board array. We access it using the shapeAt() method.
if (curPiece.getShape() != Tetrominoes.NoShape) { for (int i = 0; i < 4; ++i) { int x = curX + curPiece.x(i); int y = curY - curPiece.y(i); drawSquare(g, 0 + x * squareWidth(), boardTop + (BoardHeight - y - 1) * squareHeight(), curPiece.getShape()); } }

In the second step, we paint the actual falling piece.


private void dropDown() { int newY = curY; while (newY > 0) { if (!tryMove(curPiece, curX, newY - 1)) break; --newY;

} }

pieceDropped();

If we press the space key, the piece is dropped to the bottom. We simply try to drop the piece one line down until it reaches the bottom or the top of another fallen Tetris piece.
private void clearBoard() { for (int i = 0; i < BoardHeight * BoardWidth; ++i) board[i] = Tetrominoes.NoShape;

367

The clearBoard() method fills the board with empty NoShapes. This is later used at collision detection.
private void pieceDropped() { for (int i = 0; i < 4; ++i) { int x = curX + curPiece.x(i); int y = curY - curPiece.y(i); board[(y * BoardWidth) + x] = curPiece.getShape();

removeFullLines(); if (!isFallingFinished) newPiece();

The pieceDropped() method puts the falling piece into the board array. Once again, the board holds all the squares of the pieces and remains of the pieces that has finished falling. When the piece has finished falling, it is time to check, if we can remove some lines off the board. This is the job of the removeFullLines() method. Then we create a new piece. More precisely, we try to create a new piece.
private void newPiece() {

curPiece.setRandomShape(); curX = BoardWidth / 2 + 1; curY = BoardHeight - 1 + curPiece.minY(); if (!tryMove(curPiece, curX, curY)) { curPiece.setShape(Tetrominoes.NoShape); timer.stop(); isStarted = false; statusbar.setText("game over"); } }

The newPiece() method creates a new Tetris piece. The piece gets a new random shape. Then we compute the initial curX and curY values. If we cannot move to the initial positions, the game is over. We top out. The timer is stopped. We put game over string on the statusbar.
private boolean tryMove(Shape newPiece, int newX, int newY) { for (int i = 0; i < 4; ++i) { int x = newX + newPiece.x(i); int y = newY - newPiece.y(i); if (x < 0 || x >= BoardWidth || y < 0 || y >= BoardHeight) return false; if (shapeAt(x, y) != Tetrominoes.NoShape) return false; }

368

curPiece = newPiece; curX = newX; curY = newY; repaint(); } return true;

The tryMove() method tries to move the Tetris piece. The method returns false, if it has reached the board boundaries or it is adjacent to the already fallen Tetris pieces.
int numFullLines = 0; for (int i = BoardHeight - 1; i >= 0; --i) { boolean lineIsFull = true; for (int j = 0; j < BoardWidth; ++j) { if (shapeAt(j, i) == Tetrominoes.NoShape) { lineIsFull = false; break; } } if (lineIsFull) { ++numFullLines; for (int k = i; k < BoardHeight - 1; ++k) { for (int j = 0; j < BoardWidth; ++j) board[(k * BoardWidth) + j] = shapeAt(j, k + 1); } }

Inside the removeFullLines() method, we check if there is any full row among all rows in the board. If there is at least one full line, it is removed. After finding a full line we increase the counter. We move all the lines above the full row one line down. This way we destroy the full line. Notice, that in our Tetris game, we use so called naive gravity. This means, that the squares may be left floating above empty gaps. Every Tetris piece has four squares. Each of the squares is drawn with the drawSquare() method. Tetris pieces have different colors.
g.setColor(color.brighter()); g.drawLine(x, y + squareHeight() - 1, x, y); g.drawLine(x, y, x + squareWidth() - 1, y);

The left and top sides of a square are drawn with a brighter color. Similarly, the bottom and right sides are drawn with darker colors. This is to simulate a 3D edge. We control the game with a keyboard. The control mechanism is implemented with a KeyAdapter. This is an inner class that overrides the keyPressed() method.
case KeyEvent.VK_LEFT: tryMove(curPiece, curX - 1, curY); break;

369

If we press the left arrow key, we try to move the falling piece one square to the left.

Figure: Tetris This was the Tetris game.

370

Java SWT tutorial


This is Java SWT tutorial. In this tutorial, we will learn the basics of GUI programming with Java SWT library. The tutorial is suitable for beginners and intermediate programmers.

SWT
The Standard Widget Toolkit (SWT) is a graphical widget toolkit for the Java programming language. It was originally developed by IBM. It is an alternative to the Java Swing GUI toolkit provided by Sun Microsystems, which is a part of the Java Platform, Standard Edition. SWT is written in Java. It uses the native GUI APIs like Winapi, GTK+ to create its widgets via the JNI.

Introduction to SWT
In this part of the Java SWT programming tutorial, we will introduce the Java SWT library and create our first programs. The purpose of this tutorial is to get you started with the Java SWT toolkit. Images used in this tutorial can be downloaded here. I used some icons from the tango icons pack of the Gnome project.

About
The Standard Widget Toolkit (SWT) is a graphical widget toolkit for the Java programming language. It was originally developed by IBM. It is an alternative to the Java Swing GUI toolkit provided by Sun Microsystems, which is a part of the Java Platform, Standard Edition. SWT is written in Java. It uses the native GUI APIs like Winapi, GTK+ to create its widgets via the JNI.

Centering a window
In the first example, we create a simple window. The window is centered on the screen.
package com.zetcode; import import import import org.eclipse.swt.graphics.Point; org.eclipse.swt.graphics.Rectangle; org.eclipse.swt.widgets.Display; org.eclipse.swt.widgets.Shell;

/** * ZetCode Java SWT tutorial * * In this program, we show a window in * the center of the screen * * @author jan bodnar * website zetcode.com

371

* last modified June 2009 */ public class SWTApp { public SWTApp(Display display) { Shell shell = new Shell(display); shell.setText("Center"); shell.setSize(250, 200); center(shell); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } }

public void center(Shell shell) { Rectangle bds = shell.getDisplay().getBounds(); Point p = shell.getSize(); int nLeft = (bds.width - p.x) / 2; int nTop = (bds.height - p.y) / 2; shell.setBounds(nLeft, nTop, p.x, p.y); } public static void main(String[] args) { Display display = new Display(); new SWTApp(display); display.dispose(); } }

This example shows a 250x200px window in the centre of the screen. In each SWT application, there are two important classes. The Display and the Shell. The Display is the connection between the SWT and the underlying OS. It implements the event loop and provides information about the OS. The Shell represents a window. There are top level shells. These take a Display as a parent. Other shells are called secondary shells.
Shell shell = new Shell(display);

Top level window is created.


shell.setText("Center");

We set a title for the window.

372

shell.setSize(250, 200);

Here we set a size for the shell/window.


shell.open();

The window is shown on the screen.


while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } }

These lines start the event mainloop.


Rectangle bds = shell.getDisplay().getBounds();

We get the resolution of the screen. If you work with more than one display, you might need to call the getMonitor() method instead of getDisplay().
int nLeft = (bds.width - p.x) / 2; int nTop = (bds.height - p.y) / 2;

We calculate the left and top coordinates of the window.


shell.setBounds(nLeft, nTop, p.x, p.y);

We set the shell's bounds.


Display display = new Display(); Display

is created.

new SWTApp(display);

We instantiate our example program.


display.dispose();

After the application terminates, we release the OS resources.

Creating a Tooltip
The second example will show a tooltip. A tooltip is a small rectangular window, which gives a brief information about an object. It is usually a GUI component. It is part of the help system of the application.
package com.zetcode; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell;

373

/** * ZetCode Java SWT tutorial * * In this program, we show a tooltip * * @author jan bodnar * website zetcode.com * last modified June 2009 */ public class SWTApp { public SWTApp(Display display) { Shell shell = new Shell(display); shell.setText("Tooltip"); shell.setLocation(300, 300); shell.setToolTipText("This is a window"); shell.setSize(250, 200); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } } } public static void main(String[] args) { Display display = new Display(); new SWTApp(display); display.dispose(); }

The example creates a window. If we hover a mouse pointer over the area of the window, a tooltip pops up.
shell.setToolTipText("This is a window");

This line creates a tooltip for the window.

374

Figure: Tooltip

Quit button
In the last example of this section, we will create a quit button. When we press this button, the application terminates.
package com.zetcode; import import import import import import org.eclipse.swt.SWT; org.eclipse.swt.events.SelectionAdapter; org.eclipse.swt.events.SelectionEvent; org.eclipse.swt.widgets.Button; org.eclipse.swt.widgets.Display; org.eclipse.swt.widgets.Shell;

/** * ZetCode Java SWT tutorial * * This example shows a button on a window. * Clicking on the button, we terminate the * application. * * @author jan bodnar * website zetcode.com * last modified June 2009 */ public class SWTApp { private Shell shell; public SWTApp(Display display) { shell = new Shell(display); shell.setText("Button"); initUI(); shell.setSize(250, 200); shell.setLocation(300, 300); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } }

public void initUI() { Button quit = new Button(shell, SWT.PUSH); quit.setText("Quit"); quit.setBounds(50, 50, 80, 30);

375

quit.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { shell.getDisplay().dispose(); System.exit(0); } });

public static void main(String[] args) { Display display = new Display(); new SWTApp(display); display.dispose(); }

We use a Button widget. This is a very common widget. It shows a text label, image or both.
initUI();

We delegate the creation of the user interface to the initUI() method.


Button quit = new Button(shell, SWT.PUSH); Button

widget is created. The parent is the shell. The SWT.PUSH specifies the type of the

button.
quit.setText("Quit"); quit.setBounds(50, 50, 80, 30);

We set a label and the size for the button.


quit.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { shell.getDisplay().dispose(); System.exit(0); } });

We add a selection listener for the button. When we click on the button, the widgetSelected() method is called. Inside this method, we release the OS resources and exit the application.

376

Figure: Quit button This section was an introduction to the Java SWT library.

Layout management
In this chapter we will show how to lay out our widgets in windows or dialogs. When we design the GUI of our application, we decide what widgets we will use and how we will organize those widgets in the application. To organize our widgets, we use specialized non visible widgets called layout containers. In this chapter, we will mention absolute positioning, FillLayout, RowLayout, GridLayout and FormLayout.

Absolute positioning
In most cases, programmers should use layout managers. There are a few situations, where we can use absolute positioning. In absolute positioning, the programmer specifies the position and the size of each widget in pixels. The size and the position of a widget do not change, if you resize a window. Applications look different on various platforms, and what looks OK on Linux, might not look OK on Mac. Changing fonts in your application might spoil the layout. If you translate your application into another language, you must redo your layout. For all these issues, use the absolute positioning only when you have a reason to do so.
package com.zetcode; import import import import org.eclipse.swt.SWT; org.eclipse.swt.widgets.Button; org.eclipse.swt.widgets.Display; org.eclipse.swt.widgets.Shell;

/** * ZetCode Java SWT tutorial * * In this program, we position two * buttons using absolute coordinates * * @author jan bodnar * website zetcode.com * last modified June 2009 */

377

public class SWTApp { private Shell shell; public SWTApp(Display display) { shell = new Shell(display); shell.setText("Absolute"); initUI(); shell.setSize(250, 200); shell.setLocation(300, 300); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } }

public void initUI() { Button button1 = new Button(shell, SWT.PUSH); button1.setText("Button"); button1.setBounds(20, 50, 80, 30); Button button2 = new Button(shell, SWT.PUSH); button2.setText("Button"); button2.setSize(80, 30); button2.setLocation(50, 100);

public static void main(String[] args) { Display display = new Display(); new SWTApp(display); display.dispose(); } }

In our example, we place two buttons on the window using absolute positioning.
button1.setBounds(20, 50, 80, 30);

The setBounds() method does two things. It positions the button at x=20, y=50 coordinates. It also sizes the button, width=80, height=30.
button2.setSize(80, 30); button2.setLocation(50, 100);

Here we do the same in two steps. First we size the button using the setSize() method. Then we locate it on the window using the setLocation() method.

378

Figure: Absolute

FillLayout manager
The FillLayout manager lays out widgets in a single row or column. It makes them to be the same size.
package com.zetcode; import import import import import import import import org.eclipse.swt.SWT; org.eclipse.swt.graphics.Device; org.eclipse.swt.graphics.Image; org.eclipse.swt.graphics.Rectangle; org.eclipse.swt.layout.FillLayout; org.eclipse.swt.widgets.Display; org.eclipse.swt.widgets.Label; org.eclipse.swt.widgets.Shell;

/** * ZetCode Java SWT tutorial * * This program demonstrates the FillLayout * manager * * @author jan bodnar * website zetcode.com * last modified June 2009 */ public class SWTApp { private Shell shell; private Image castle; public SWTApp(Display display) { shell = new Shell(display); shell.setLayout(new FillLayout()); shell.setText("FillLayout"); Device dev = shell.getDisplay(); try { castle = new Image(dev, "redrock.png");

379

} catch(Exception e) { System.out.println("Cannot load image"); System.out.println(e.getMessage()); System.exit(1); } initUI(); Rectangle rect = castle.getBounds(); shell.setSize(rect.width, rect.height); shell.setLocation(300, 300); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } } } public void initUI() { Label label = new Label(shell, SWT.IMAGE_PNG); label.setImage(castle); label.pack();

@Override public void finalize() { System.out.println("disposing"); castle.dispose(); } public static void main(String[] args) { Display display = new Display(); SWTApp app = new SWTApp(display); app.finalize(); display.dispose(); } }

In our example, we use this manager to display an image.


shell.setLayout(new FillLayout());

We set the FillLayout to be the layout class for the shell.


try { castle = new Image(dev, "redrock.png"); } catch(Exception e) { System.out.println("Cannot load image"); System.out.println(e.getMessage()); System.exit(1); }

We load an image.

380

Rectangle rect = castle.getBounds(); shell.setSize(rect.width, rect.height);

We find out the size of the picture to resize the shell to exactly fit the image size.
Label label = new Label(shell, SWT.IMAGE_PNG); label.setImage(castle); label.pack();

We set the image to the label widget.

Figure: FillLayout

RowLayout
The RowLayout manager places all widgets either in one row or in one column.
package com.zetcode; import import import import import import org.eclipse.swt.SWT; org.eclipse.swt.layout.RowData; org.eclipse.swt.layout.RowLayout; org.eclipse.swt.widgets.Button; org.eclipse.swt.widgets.Display; org.eclipse.swt.widgets.Shell;

/** * ZetCode Java SWT tutorial * * This program demonstrates the RowLayout * manager * * @author jan bodnar * website zetcode.com * last modified June 2009 */ public class SWTApp { private Shell shell;

381

public SWTApp(Display display) { shell = new Shell(display); shell.setText("RowLayout"); initUI(); shell.pack(); shell.setLocation(300, 300); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } }

public void initUI() { RowLayout rowLayout = new RowLayout(SWT.HORIZONTAL); rowLayout.marginTop = 10; rowLayout.marginBottom = 10; rowLayout.marginLeft = 5; rowLayout.marginRight = 5; rowLayout.spacing = 10; shell.setLayout(rowLayout); Button button1 = new Button(shell, SWT.PUSH); button1.setText("Button"); button1.setLayoutData(new RowData(80, 30)); Button button2 = new Button(shell, SWT.PUSH); button2.setText("Button"); button2.setLayoutData(new RowData(80, 30)); Button button3 = new Button(shell, SWT.PUSH); button3.setText("Button"); button3.setLayoutData(new RowData(80, 30)); } public static void main(String[] args) { Display display = new Display(); new SWTApp(display); display.dispose(); } }

In our example, we create a row of three buttons.


RowLayout rowLayout = new RowLayout(SWT.HORIZONTAL);

We create a row.
rowLayout.marginTop = 10; rowLayout.marginBottom = 10;

382

rowLayout.marginLeft = 5; rowLayout.marginRight = 5; rowLayout.spacing = 10;

We change the margins and the spacing for the row.


shell.setLayout(rowLayout);

We specify the row layout to be the layout for the shell.

Figure: RowLayout manager

Calculator
In the following example, we use the GridLayout manager to create a skeleton of a calculator. The GridLayout manager puts its child widgets into a grid.
package com.zetcode; import import import import import import import import org.eclipse.swt.SWT; org.eclipse.swt.layout.GridData; org.eclipse.swt.layout.GridLayout; org.eclipse.swt.widgets.Button; org.eclipse.swt.widgets.Display; org.eclipse.swt.widgets.Label; org.eclipse.swt.widgets.Shell; org.eclipse.swt.widgets.Text;

/** * ZetCode Java SWT tutorial * * In this program, we use the GridLayout to * create a calculator skeleton * * @author jan bodnar * website zetcode.com * last modified June 2009 */ public class SWTApp { private Shell shell; public SWTApp(Display display) { shell = new Shell(display); shell.setText("Calculator"); initUI(); shell.pack(); shell.setLocation(300, 300);

383

shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } } } public void initUI() { GridLayout gl = new GridLayout(4, true); gl.horizontalSpacing = 4; gl.verticalSpacing = 4; gl.marginBottom = 5; gl.marginTop = 5; shell.setLayout(gl); String[] buttons = { "Cls", "Bck", "", "Close", "7", "8", "9", "/", "4", "5", "6", "*", "1", "2", "3", "-", "0", ".", "=", "+" }; Text display = new Text(shell, SWT.SINGLE); GridData gridData = new GridData(); gridData.horizontalSpan = 4; gridData.horizontalAlignment = GridData.FILL; display.setLayoutData(gridData); for (int i = 0; i < buttons.length; i++) { if (i == 2) { Label lbl = new Label(shell, SWT.CENTER); GridData gd = new GridData(SWT.FILL, SWT.FILL, false, lbl.setLayoutData(gd); } else { Button btn = new Button(shell, SWT.PUSH); btn.setText(buttons[i]); GridData gd = new GridData(SWT.FILL, SWT.FILL, false, gd.widthHint = 50; gd.heightHint = 30; btn.setLayoutData(gd);

false);

false);

} } }

public static void main(String[] args) { Display display = new Display(); new SWTApp(display); display.dispose(); }

We create a skeleton of a calculator with the GridLayout manager. We use three types of widgets. A text widget, a label widget and several buttons.

384

GridLayout gl = new GridLayout(4, true); gl.horizontalSpacing = 4; gl.verticalSpacing = 4; gl.marginBottom = 5; gl.marginTop = 5; shell.setLayout(gl);

We create a GridLayout with 4 columns. We provide some spacing and margins.


Text display = new Text(shell, SWT.SINGLE); GridData gridData = new GridData(); gridData.horizontalSpan = 4; gridData.horizontalAlignment = GridData.FILL; display.setLayoutData(gridData);

The GridData is the layout data object associated with GridLayout. We make the text widget span all four columns.
Button btn = new Button(shell, SWT.PUSH); btn.setText(buttons[i]); GridData gd = new GridData(SWT.FILL, SWT.FILL, false, false); gd.widthHint = 50; gd.heightHint = 30; btn.setLayoutData(gd);

Inside the for loop, we create buttons and put them into the grid.

Figure: Calculator skeleton

Buttons
In the last example, we will create an example with the FormLayout manager. This manager controls the position and size of the children using two objects. FormData and FormAttachment.
package com.zetcode; import import import import org.eclipse.swt.SWT; org.eclipse.swt.layout.FormAttachment; org.eclipse.swt.layout.FormData; org.eclipse.swt.layout.FormLayout;

385

import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; /** * ZetCode Java SWT tutorial * * In this program, we position two buttons * in the bottom right corner of the window * * @author jan bodnar * website zetcode.com * last modified June 2009 */ public class SWTApp { private Shell shell; public SWTApp(Display display) { shell = new Shell(display); shell.setText("Buttons"); initUI(); shell.setSize(350, 200); shell.setLocation(300, 300); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } }

public void initUI() { FormLayout layout = new FormLayout(); shell.setLayout(layout); Button okButton = new Button(shell, SWT.PUSH); okButton.setText("OK"); Button cancelButton = new Button(shell, SWT.PUSH); cancelButton.setText("Cancel"); FormData cancelData = new FormData(80, 30); cancelData.right = new FormAttachment(98); cancelData.bottom = new FormAttachment(95); cancelButton.setLayoutData(cancelData); FormData okData = new FormData(80, 30); okData.right = new FormAttachment(cancelButton, -5, SWT.LEFT); okData.bottom = new FormAttachment(cancelButton, 0, SWT.BOTTOM); okButton.setLayoutData(okData);

386

public static void main(String[] args) { Display display = new Display(); new SWTApp(display); display.dispose(); } }

In this code example, we position two buttons in the bottom right corner of the window.
FormLayout layout = new FormLayout(); shell.setLayout(layout); FormLayout

manager is created.

Button okButton = new Button(shell, SWT.PUSH); okButton.setText("OK"); Button cancelButton = new Button(shell, SWT.PUSH); cancelButton.setText("Cancel");

Two buttons are created.


FormData cancelData = new FormData(80, 30);

The cancel button's size is 80x30.


cancelData.right = new FormAttachment(98); cancelData.bottom = new FormAttachment(95);

The right side of the button is attached at 98% of the width of the window. The bottom side of the button is attached at 95% of the height of the window.
okData.right = new FormAttachment(cancelButton, -5, SWT.LEFT); okData.bottom = new FormAttachment(cancelButton, 0, SWT.BOTTOM);

The right side of the ok button goes 5px to the left of the cancel button. The bottom side of the ok button is aligned with the bottom of the cancel button.

Figure: Buttons In this part of the Java SWT tutorial, we talked about layout management of widgets.

387

Widgets in SWT
In this part of the Java SWT programming tutorial, we will introduce some SWT widgets. Widgets are basic building blocks of a GUI application. Think of widgets as parts of a lego. Over the years, several widgets became a standard in all toolkits on all OS platforms. For example a button, a check box or a scroll bar.

Label
A Label widget shows text.
package com.zetcode; import import import import import org.eclipse.swt.SWT; org.eclipse.swt.graphics.Point; org.eclipse.swt.widgets.Display; org.eclipse.swt.widgets.Label; org.eclipse.swt.widgets.Shell;

/** * ZetCode Java SWT tutorial * * This program uses the Label widget to * show lyrics of a song * * @author jan bodnar * website zetcode.com * last modified June 2009 */ public class SWTApp { Shell shell; String lyrics = "And I know that he knows I'm unfaithful\n"+ "And it kills him inside\n"+ "To know that I am happy with some other guy\n"+ "I can see him dyin'\n"+ "\n"+ "I don't wanna do this anymore\n"+ "I don't wanna be the reason why\n"+ "Every time I walk out the door\n"+ "I see him die a little more inside\n"+ "I don't wanna hurt him anymore\n"+ "I don't wanna take away his life\n"+ "I don't wanna be...A murderer";

public SWTApp(Display display) { shell = new Shell(display);

388

shell.setText("Unfaithful"); initUI(); shell.pack(); shell.setLocation(300, 300); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } }

public void initUI() { Label label = new Label(shell, SWT.LEFT); label.setText(lyrics); Point p = label.computeSize(SWT.DEFAULT, SWT.DEFAULT); label.setBounds(5, 5, p.x+5, p.y+5);

public static void main(String[] args) { Display display = new Display(); new SWTApp(display); display.dispose(); }

The code example shows some lyrics on the window.


String lyrics = "And I know that he knows I'm unfaithful\n"+ "And it kills him inside\n"+ ...

We build a multiline text.


Label label = new Label(shell, SWT.LEFT); label.setText(lyrics);

The Label widget is created. Text is left aligned.


Point p = label.computeSize(SWT.DEFAULT, SWT.DEFAULT); label.setBounds(5, 5, p.x+5, p.y+5);

We compute the size of the text in order put some space round the text.

389

Figure: Label Widget

CheckButton
In SWT, check button is a special case of a Button. It is a widget, that has two states. On and Off. The On state is visualised by a check mark. It is used to denote some boolean property.
package com.zetcode; import import import import import import org.eclipse.swt.SWT; org.eclipse.swt.events.SelectionAdapter; org.eclipse.swt.events.SelectionEvent; org.eclipse.swt.widgets.Button; org.eclipse.swt.widgets.Display; org.eclipse.swt.widgets.Shell;

/** * ZetCode Java SWT tutorial * * This program uses a check button * widget to show/hide the title * of the window * * @author jan bodnar * website zetcode.com * last modified June 2009 */ public class SWTApp { private Shell shell; private Button cb; public SWTApp(Display display) { shell = new Shell(display); shell.setText("Check button");

390

initUI(); shell.setSize(250, 200); shell.setLocation(300, 300); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } } } public void initUI() { cb = new Button(shell, SWT.CHECK); cb.setText("Show title"); cb.setSelection(true); cb.setLocation(50, 50); cb.pack(); cb.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { if (cb.getSelection()) { shell.setText("Check button"); } else { shell.setText(""); } } }); } public static void main(String[] args) { Display display = new Display(); new SWTApp(display); display.dispose(); } }

We will display a title in the titlebar of the window, depending on the state of the check button.
cb = new Button(shell, SWT.CHECK); cb.setText("Show title"); CheckButton

widget is created.

cb.setSelection(true);

The title is visible by default, so we check the check button by default.


if (cb.getSelection()) { shell.setText("Check button"); } else {

391

shell.setText("");

Depending on the state of the CheckButton, we show or hide the title of the window.

Figure: CheckButton

List widget
The next example introduces the List widget. This widget enables a user to select an option from a list of items.
package com.zetcode; import import import import import import import import import import org.eclipse.swt.SWT; org.eclipse.swt.layout.FormAttachment; org.eclipse.swt.layout.FormData; org.eclipse.swt.layout.FormLayout; org.eclipse.swt.widgets.Display; org.eclipse.swt.widgets.Event; org.eclipse.swt.widgets.Label; org.eclipse.swt.widgets.List; org.eclipse.swt.widgets.Listener; org.eclipse.swt.widgets.Shell;

/** * ZetCode Java SWT tutorial * * This program shows the List * widget * * @author jan bodnar * website zetcode.com * last modified June 2009 */ public class SWTApp { Shell shell; public SWTApp(Display display) { shell = new Shell(display);

392

shell.setText("List"); initUI(); shell.setSize(300, 250); shell.setLocation(300, 300); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } } } public void initUI() { final Label status = new Label(shell, SWT.BORDER); status.setText("Ready"); FormLayout layout = new FormLayout(); shell.setLayout(layout); FormData labelData = new FormData(); labelData.left = new FormAttachment(0); labelData.right = new FormAttachment(100); labelData.bottom = new FormAttachment(100); status.setLayoutData(labelData); final List list = new List(shell, SWT.BORDER); list.add("Aliens"); list.add("Capote"); list.add("Neverending story"); list.add("Starship troopers"); list.add("Exorcist"); list.add("Omen"); list.addListener(SWT.Selection, new Listener () { public void handleEvent (Event e) { String[] items = list.getSelection(); status.setText(items[0]); } }); FormData listData = new FormData(); listData.left = new FormAttachment(shell, 30, SWT.LEFT); listData.top = new FormAttachment(shell, 30, SWT.TOP); list.setLayoutData(listData);

public static void main(String[] args) { Display display = new Display(); new SWTApp(display); display.dispose(); }

393

In this example, the selected item from the list widget is shown in the statusbar.
final Label status = new Label(shell, SWT.BORDER); status.setText("Ready");

SWT library has no statusbar widget. We use a simple label for this. The label has a border.
FormLayout layout = new FormLayout(); shell.setLayout(layout);

We use the FormLayout widget to arrange our widgets on the window.


FormData labelData = new FormData(); labelData.left = new FormAttachment(0); labelData.right = new FormAttachment(100); labelData.bottom = new FormAttachment(100); status.setLayoutData(labelData);

This code will attach the status label to the bottom of the window. Where we usually see the statusbar.
final List list = new List(shell, SWT.BORDER);

The List widget is created.


list.add("Aliens"); list.add("Capote"); list.add("Neverending story"); list.add("Starship troopers"); list.add("Exorcist"); list.add("Omen");

It is filled with data.


list.addListener(SWT.Selection, new Listener () { public void handleEvent (Event e) { String[] items = list.getSelection(); status.setText(items[0]); } });

We add a listener to the List widget. When we select an option, the handleEvet() method is executed. In this method, we set the selected text to the status label.
FormData listData = new FormData(); listData.left = new FormAttachment(shell, 30, SWT.LEFT); listData.top = new FormAttachment(shell, 30, SWT.TOP); list.setLayoutData(listData);

This code puts the List widget at x = 30px, y = 30px.

394

Figure: List widget

Slider
The Slider is a widget, that lets the user graphically select a value by sliding a knob within a bounded interval. Our example will show a volume control.
package com.zetcode; import import import import import import import import import org.eclipse.swt.SWT; org.eclipse.swt.graphics.Device; org.eclipse.swt.graphics.Image; org.eclipse.swt.widgets.Display; org.eclipse.swt.widgets.Event; org.eclipse.swt.widgets.Label; org.eclipse.swt.widgets.Listener; org.eclipse.swt.widgets.Shell; org.eclipse.swt.widgets.Slider;

/** * ZetCode Java SWT tutorial * * In this program, we use the slider * widget to create a volume control * * @author jan bodnar * website zetcode.com * last modified June 2009 */ public class SWTApp { private Shell shell; private private private private Image Image Image Image mute; min; med; max;

public SWTApp(Display display) {

395

shell = new Shell(display); Device dev = shell.getDisplay(); try { mute = new Image(dev, "mute.png"); min = new Image(dev, "min.png"); med = new Image(dev, "med.png"); max = new Image(dev, "max.png"); } catch(Exception e) { System.out.println("Cannot load images"); System.out.println(e.getMessage()); System.exit(1); } shell.setText("Slider"); initUI(); shell.setSize(350, 200); shell.setLocation(300, 300); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } } } public void initUI() { final Label label = new Label(shell, SWT.IMAGE_PNG); label.setImage(mute); label.pack(); label.setLocation(240, 30); final Slider slider = new Slider(shell, SWT.HORIZONTAL); slider.setMaximum(100); slider.setBounds(30, 30, 180, 30); slider.addListener (SWT.Selection, new Listener () { public void handleEvent (Event e) { int value = slider.getSelection(); if (value == 0) { label.setImage(mute); label.pack(); } else if (value > 0 && value <= 30) { label.setImage(min); } else if (value > 30 && value < 80) { label.setImage(med); } else { label.setImage(max); } }); } }

396

@Override public void finalize() { System.out.println("disposing"); mute.dispose(); med.dispose(); min.dispose(); max.dispose(); } public static void main(String[] args) { Display display = new Display(); SWTApp app = new SWTApp(display); app.finalize(); display.dispose(); } }

In the example above, we have Slider and Image widgets. By dragging the knob of the slider we change the picture on the Label widget.
final Slider slider = new Slider(shell, SWT.HORIZONTAL); slider.setMaximum(100);

Slider widget is created. Its maximum value is 100.


int value = slider.getSelection();

Inside the listener object, we obtain the value of the slider widget.
if (value == 0) { label.setImage(mute); label.pack(); } else if (value > 0 && value <= 30) { label.setImage(min); } else if (value > 30 && value < 80) { label.setImage(med); } else { label.setImage(max); }

Depending on the obtained value, we change the picture in the label widget.
@Override public void finalize() { System.out.println("disposing"); mute.dispose(); med.dispose(); min.dispose(); max.dispose(); }

We release the resources.

397

Figure: Slider widget

Combo widget
Combo is a widget that allows the user to choose from a drop down list of options.
package com.zetcode; import import import import import import import org.eclipse.swt.SWT; org.eclipse.swt.events.SelectionAdapter; org.eclipse.swt.events.SelectionEvent; org.eclipse.swt.widgets.Combo; org.eclipse.swt.widgets.Display; org.eclipse.swt.widgets.Label; org.eclipse.swt.widgets.Shell;

/** * ZetCode Java SWT tutorial * * In this program, we use the Combo * widget to select an option. * The selected option is shown in the * Label widget. * * @author jan bodnar * website zetcode.com * last modified June 2009 */ public class SWTApp { Shell shell; public SWTApp(Display display) { shell = new Shell(display); shell.setText("Combo"); initUI(); shell.setSize(300, 250); shell.setLocation(300, 300); shell.open();

398

while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } } } public void initUI() { final Label label = new Label(shell, SWT.LEFT); label.setText("..."); label.setLocation(50, 100); label.pack(); final Combo combo = new Combo(shell, SWT.DROP_DOWN); combo.add("Ubuntu"); combo.add("Fedora"); combo.add("Mandriva"); combo.add("Red Hat"); combo.add("Mint"); combo.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { label.setText(combo.getText()); label.pack(); };

});

combo.setLocation(50, 30); combo.pack(); } public static void main(String[] args) { Display display = new Display(); new SWTApp(display); display.dispose(); }

The example shows a combo box and a label. The combo box has a list of six options. These are the names of Linux Distros. The label widget shows the selected option from the combo box.
final Combo combo = new Combo(shell, SWT.DROP_DOWN); Combo

widget is created.

combo.add("Ubuntu"); combo.add("Fedora"); combo.add("Mandriva"); combo.add("Red Hat");

399

combo.add("Mint");

It is filled with data.


@Override public void widgetSelected(SelectionEvent e) { label.setText(combo.getText()); label.pack(); };

We set the selected text to the label widget.

Figure: Combo widget In this part of the Java SWT tutorial, we described some widgets of the SWT library.

Menus & toolbars


In this part of the Java SWT tutorial, we will work with menus & toolbars. A menubar is one of the most common parts of the GUI application. It is a group of commands located in various menus. While in console applications you have to remember all those arcane commands, here we have most of the commands grouped into logical parts. These are accepted standards that further reduce the amount of time spending to learn a new application.

Simple menu
In our first example, we will create a menubar with one file menu. The menu will have only one menu item. By selecting the item the application quits.
package com.zetcode; import import import import import org.eclipse.swt.SWT; org.eclipse.swt.events.SelectionAdapter; org.eclipse.swt.events.SelectionEvent; org.eclipse.swt.widgets.Display; org.eclipse.swt.widgets.Menu;

400

import org.eclipse.swt.widgets.MenuItem; import org.eclipse.swt.widgets.Shell; /** * ZetCode Java SWT tutorial * * This program creates a simple menu * * @author jan bodnar * website zetcode.com * last modified June 2009 */ public class SWTApp { private Shell shell; public SWTApp(Display display) { shell = new Shell(display); shell.setText("Simple menu"); initUI(); shell.setSize(250, 200); shell.setLocation(300, 300); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } }

public void initUI() { Menu menuBar = new Menu(shell, SWT.BAR); MenuItem cascadeFileMenu = new MenuItem(menuBar, SWT.CASCADE); cascadeFileMenu.setText("&File"); Menu fileMenu = new Menu(shell, SWT.DROP_DOWN); cascadeFileMenu.setMenu(fileMenu); MenuItem exitItem = new MenuItem(fileMenu, SWT.PUSH); exitItem.setText("&Exit"); shell.setMenuBar(menuBar); exitItem.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { shell.getDisplay().dispose(); System.exit(0); } }); }

401

public static void main(String[] args) { Display display = new Display(); new SWTApp(display); display.dispose(); } }

This is a small example with minimal menubar functionality.


Menu menuBar = new Menu(shell, SWT.BAR);

Here we create a menu bar.


MenuItem cascadeFileMenu = new MenuItem(menuBar, SWT.CASCADE); cascadeFileMenu.setText("&File");

Toplevel menu items are cascade menu items.


Menu fileMenu = new Menu(shell, SWT.DROP_DOWN); cascadeFileMenu.setMenu(fileMenu);

We create a drop down menu.


MenuItem exitItem = new MenuItem(fileMenu, SWT.PUSH); exitItem.setText("&Exit");

A push menu item is plugged into a drop down menu.


shell.setMenuBar(menuBar);

We set a menu bar for our shell.


exitItem.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { shell.getDisplay().dispose(); System.exit(0); } });

When we select the exit push menu item, we terminate the application.

402

Figure: Simple menu

Submenu
The next example demonstrates how to create a submenu in Java SWT.
package com.zetcode; import import import import import import import org.eclipse.swt.SWT; org.eclipse.swt.events.SelectionAdapter; org.eclipse.swt.events.SelectionEvent; org.eclipse.swt.widgets.Display; org.eclipse.swt.widgets.Menu; org.eclipse.swt.widgets.MenuItem; org.eclipse.swt.widgets.Shell;

/** * ZetCode Java SWT tutorial * * This program creates a submenu * * @author jan bodnar * website zetcode.com * last modified June 2009 */ public class SWTApp { private Shell shell; public SWTApp(Display display) { shell = new Shell(display); shell.setText("Submenu"); initUI(); shell.setSize(250, 200); shell.setLocation(300, 300); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } } } public void initUI() { Menu menuBar = new Menu(shell, SWT.BAR); MenuItem cascadeFileMenu = new MenuItem(menuBar, SWT.CASCADE); cascadeFileMenu.setText("&File"); Menu fileMenu = new Menu(shell, SWT.DROP_DOWN); cascadeFileMenu.setMenu(fileMenu); MenuItem cascadeMenu = new MenuItem(menuBar, SWT.CASCADE);

403

cascadeMenu.setText("&File"); MenuItem subMenuItem = new MenuItem(fileMenu, SWT.CASCADE); subMenuItem.setText("Import"); Menu submenu = new Menu(shell, SWT.DROP_DOWN); subMenuItem.setMenu(submenu); MenuItem feedItem = new MenuItem(submenu, SWT.PUSH); feedItem.setText("&Import news feed..."); MenuItem bmarks = new MenuItem(submenu, SWT.PUSH); bmarks.setText("&Import bookmarks..."); MenuItem mailItem = new MenuItem(submenu, SWT.PUSH); mailItem.setText("&Import mail..."); MenuItem exitItem = new MenuItem(fileMenu, SWT.PUSH); exitItem.setText("&Exit"); shell.setMenuBar(menuBar); exitItem.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { shell.getDisplay().dispose(); System.exit(0); }

}); }

public static void main(String[] args) { Display display = new Display(); new SWTApp(display); display.dispose(); } }

Submenu creation.
MenuItem subMenuItem = new MenuItem(fileMenu, SWT.CASCADE); subMenuItem.setText("Import");

A submenu creation is similar to creating a normal menu. First, we create a cascade menu item. The only difference is the parent widget. This time the parent is the menu object, to which the submenu belongs.
MenuItem feedItem = new MenuItem(submenu, SWT.PUSH); feedItem.setText("&Import news feed...");

We create a push menu item. The parent widget is the submenu object.

404

Figure: Submenu

CheckMenuItem
A CheckMenuItem is a menu item with a check box. It can be used to work with boolean properties.
package com.zetcode; import import import import import import import import import import import import import org.eclipse.swt.SWT; org.eclipse.swt.events.SelectionAdapter; org.eclipse.swt.events.SelectionEvent; org.eclipse.swt.layout.FormAttachment; org.eclipse.swt.layout.FormData; org.eclipse.swt.layout.FormLayout; org.eclipse.swt.widgets.Display; org.eclipse.swt.widgets.Event; org.eclipse.swt.widgets.Label; org.eclipse.swt.widgets.Listener; org.eclipse.swt.widgets.Menu; org.eclipse.swt.widgets.MenuItem; org.eclipse.swt.widgets.Shell;

/** * ZetCode Java SWT tutorial * * This program creates a check menu item. * It will show or hide a statusbar. * * @author jan bodnar * website zetcode.com * last modified June 2009 */ public class SWTApp { private Shell shell; private Label status; private MenuItem statItem; public SWTApp(Display display) { shell = new Shell(display); shell.setText("Check menu item");

405

initUI(); shell.setSize(300, 250); shell.setLocation(300, 300); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } } } Listener statListener = new Listener() { public void handleEvent(Event event) { if (statItem.getSelection()) { status.setVisible(true); } else { status.setVisible(false); } } }; public void initUI() { Menu menuBar = new Menu(shell, SWT.BAR); shell.setMenuBar(menuBar); MenuItem cascadeFileMenu = new MenuItem(menuBar, SWT.CASCADE); cascadeFileMenu.setText("&File"); Menu fileMenu = new Menu(shell, SWT.DROP_DOWN); cascadeFileMenu.setMenu(fileMenu); MenuItem exitItem = new MenuItem(fileMenu, SWT.PUSH); exitItem.setText("&Exit"); MenuItem cascadeViewMenu = new MenuItem(menuBar, SWT.CASCADE); cascadeViewMenu.setText("&View"); Menu viewMenu = new Menu(shell, SWT.DROP_DOWN); cascadeViewMenu.setMenu(viewMenu); statItem = new MenuItem(viewMenu, SWT.CHECK); statItem.setSelection(true); statItem.setText("&View Statusbar"); statItem.addListener(SWT.Selection, statListener); exitItem.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { shell.getDisplay().dispose(); System.exit(0); }

});

406

status = new Label(shell, SWT.BORDER); status.setText("Ready"); FormLayout layout = new FormLayout(); shell.setLayout(layout); FormData labelData = new FormData(); labelData.left = new FormAttachment(0); labelData.right = new FormAttachment(100); labelData.bottom = new FormAttachment(100); status.setLayoutData(labelData);

public static void main(String[] args) { Display display = new Display(); new SWTApp(display); display.dispose(); } }

In our code example we show a check menu item. If the check box is activated, the statusbar widget is shown. If not, the statusbar is hidden.
statItem = new MenuItem(viewMenu, SWT.CHECK);

The SWT.CHECK flag will create a check menu item.


statItem.setSelection(true);

The setSelection() method checks/unchecks the check menu item.


if (statItem.getSelection()) { status.setVisible(true); } else { status.setVisible(false); }

Depending on the state of the check menu item, we show or hide the statusbar widget.

Figure: CheckMenuItem

407

Popup menu
In the next example, we will create a popup menu. It is also called a context menu. This type of menu is shown, when we right click on an object.
package com.zetcode; import import import import import import import org.eclipse.swt.SWT; org.eclipse.swt.events.SelectionAdapter; org.eclipse.swt.events.SelectionEvent; org.eclipse.swt.widgets.Display; org.eclipse.swt.widgets.Menu; org.eclipse.swt.widgets.MenuItem; org.eclipse.swt.widgets.Shell;

/** * ZetCode Java SWT tutorial * * This program creates a popup * menu * * @author jan bodnar * website zetcode.com * last modified June 2009 */ public class SWTApp { private Shell shell; public SWTApp(Display display) { shell = new Shell(display); shell.setText("Popup menu"); initUI(); shell.setSize(300, 250); shell.setLocation(300, 300); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } }

public void initUI() { Menu menu = new Menu(shell, SWT.POP_UP); MenuItem minItem = new MenuItem(menu, SWT.PUSH); minItem.setText("Minimize"); minItem.addSelectionListener(new SelectionAdapter() { @Override

408

});

public void widgetSelected(SelectionEvent e) { shell.setMinimized(true); }

MenuItem exitItem = new MenuItem(menu, SWT.PUSH); exitItem.setText("Exit"); exitItem.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { shell.getDisplay().dispose(); System.exit(0); } }); shell.setMenu(menu); } public static void main(String[] args) { Display display = new Display(); new SWTApp(display); display.dispose(); }

In our code example, we create a popup menu with two menu items. The first will minimize the window and the second will terminate the application.
Menu menu = new Menu(shell, SWT.POP_UP);

Popup menu is created with the SWT.POP_UP flag.


MenuItem minItem = new MenuItem(menu, SWT.PUSH); minItem.setText("Minimize");

Menu items inside a popup menu are normal push menu items.
public void widgetSelected(SelectionEvent e) { shell.setMinimized(true); }

This code will minimize the window.


shell.setMenu(menu);

We set a popup menu for the shell.

409

Figure: Popup menu

Simple toolbar
Menus group commands that we can use in application. Toolbars provide a quick access to the most frequently used commands. In the following example, we will create a simple toolbar.
package com.zetcode; import import import import import import import import import org.eclipse.swt.SWT; org.eclipse.swt.events.SelectionAdapter; org.eclipse.swt.events.SelectionEvent; org.eclipse.swt.graphics.Device; org.eclipse.swt.graphics.Image; org.eclipse.swt.widgets.Display; org.eclipse.swt.widgets.Shell; org.eclipse.swt.widgets.ToolBar; org.eclipse.swt.widgets.ToolItem;

/** * ZetCode Java SWT tutorial * * This program creates a simple toolbar * * @author jan bodnar * website zetcode.com * last modified June 2009 */ public class SWTApp { private private private private Shell Image Image Image shell; newi; opei; quii;

public SWTApp(Display display) { shell = new Shell(display); shell.setText("Simple toolbar");

410

initUI(); shell.setSize(300, 250); shell.setLocation(300, 300); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } }

public void initUI() { Device dev = shell.getDisplay(); try { newi = new Image(dev, "new.png"); opei = new Image(dev, "open.png"); quii = new Image(dev, "quit.png"); } catch (Exception e) { System.out.println("Cannot load images"); System.out.println(e.getMessage()); System.exit(1); } ToolBar toolBar = new ToolBar(shell, SWT.BORDER); ToolItem item1 = new ToolItem(toolBar, SWT.PUSH); item1.setImage(newi); ToolItem item2 = new ToolItem(toolBar, SWT.PUSH); item2.setImage(opei); new ToolItem(toolBar, SWT.SEPARATOR); ToolItem item3 = new ToolItem(toolBar, SWT.PUSH); item3.setImage(quii); toolBar.pack(); item3.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { shell.getDisplay().dispose(); System.exit(0); } } });

public static void main(String[] args) { Display display = new Display(); new SWTApp(display); display.dispose(); } }

411

The example shows a toolbar and three tool items.


ToolBar toolBar = new ToolBar(shell, SWT.BORDER);

Toolbar widget is created.


ToolItem item1 = new ToolItem(toolBar, SWT.PUSH); item1.setImage(newi);

We create a tool item with an image.


new ToolItem(toolBar, SWT.SEPARATOR);

Here we create a vertical separator.

Figure: Toolbar In this chapter of the Java SWT tutorial, we showed, how to work with menus & toolbars.

Dialogs
In this part of the Java SWT tutorial, we will introduce dialogs. Dialog windows or dialogs are an indispensable part of most modern GUI applications. A dialog is defined as a conversation between two or more persons. In a computer application a dialog is a window which is used to "talk" to the application. A dialog is used to input data, modify data, change the application settings etc. Dialogs are important means of communication between a user and a computer program.

Directory dialog
The directory dialog is a dialog, which is used to select a path to a certain directory.
package com.zetcode; import org.eclipse.swt.SWT; import org.eclipse.swt.events.MouseAdapter;

412

import import import import import import import import

org.eclipse.swt.events.MouseEvent; org.eclipse.swt.layout.FormAttachment; org.eclipse.swt.layout.FormData; org.eclipse.swt.layout.FormLayout; org.eclipse.swt.widgets.DirectoryDialog; org.eclipse.swt.widgets.Display; org.eclipse.swt.widgets.Label; org.eclipse.swt.widgets.Shell;

/** * ZetCode Java SWT tutorial * * This example shows a directory dialog * * @author jan bodnar * website zetcode.com * last modified June 2009 */ public class SWTApp { private Shell shell; public SWTApp(Display display) { shell = new Shell(display); shell.setText("DirectoryDialog"); initUI(); shell.setSize(350, 250); shell.setLocation(300, 300); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } }

public void initUI() { final Label status = new Label(shell, SWT.BORDER); status.setText("Ready"); FormLayout layout = new FormLayout(); shell.setLayout(layout); FormData labelData = new FormData(); labelData.left = new FormAttachment(0); labelData.right = new FormAttachment(100); labelData.bottom = new FormAttachment(100); status.setLayoutData(labelData); shell.addMouseListener(new MouseAdapter() { @Override

413

public void mouseDown(MouseEvent event) { DirectoryDialog dialog = new DirectoryDialog(shell); String path = dialog.open(); if (path != null) status.setText(path); } }); } public static void main(String[] args) { Display display = new Display(); new SWTApp(display); display.dispose(); } }

In our example, we use the directory dialog to select a directory. The path to the directory is shown in the status label. The dialog is shown by clicking on the area of the window.
DirectoryDialog dialog = new DirectoryDialog(shell); DirectoryDialog

is created.

String path = dialog.open();

We get the path to the selected directory.


if (path != null) status.setText(path);

If the path is not null, we show the path in the status label.

414

Figure: Directory dialog

FontDialog
The FontDialog is a dialog for selecting fonts. It is typically used in applications, that do some text editing or formatting.
package com.zetcode; import import import import import import import import import org.eclipse.swt.SWT; org.eclipse.swt.events.MouseAdapter; org.eclipse.swt.events.MouseEvent; org.eclipse.swt.graphics.Font; org.eclipse.swt.graphics.FontData; org.eclipse.swt.widgets.Display; org.eclipse.swt.widgets.FontDialog; org.eclipse.swt.widgets.Label; org.eclipse.swt.widgets.Shell;

/** * ZetCode Java SWT tutorial * * This example shows a font dialog * * @author jan bodnar * website zetcode.com

415

* last modified June 2009 */ public class SWTApp { private Shell shell; public SWTApp(Display display) { shell = new Shell(display); shell.setText("FontDialog"); initUI(); shell.setSize(350, 250); shell.setLocation(300, 300); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } }

public void initUI() { final Label label = new Label(shell, SWT.NONE); label.setText("ZetCode Java SWT tutorial"); label.setLocation(50, 50); label.pack(); shell.addMouseListener(new MouseAdapter() { @Override public void mouseDown(MouseEvent event) { FontDialog dialog = new FontDialog(shell); FontData fdata = dialog.open(); if (fdata != null) { Font font = new Font(shell.getDisplay(), fdata); label.setFont(font); label.pack(); font.dispose();

} }); } }

public static void main(String[] args) { Display display = new Display(); new SWTApp(display); display.dispose(); }

416

In the code example, we use a FontDialog to change the font of a label.


FontDialog dialog = new FontDialog(shell);

We create the FontDialog.


Font font = new Font(shell.getDisplay(), fdata); Font

object is created from the font data, returned by the font dialog.

Figure: FontDialog

ColorDialog
ColorDialog

is a dialog for selecting a color.

package com.zetcode; import import import import import import import import import org.eclipse.swt.SWT; org.eclipse.swt.events.MouseAdapter; org.eclipse.swt.events.MouseEvent; org.eclipse.swt.graphics.Color; org.eclipse.swt.graphics.RGB; org.eclipse.swt.widgets.ColorDialog; org.eclipse.swt.widgets.Display; org.eclipse.swt.widgets.Label; org.eclipse.swt.widgets.Shell;

/** * ZetCode Java SWT tutorial *

417

* This example shows a color dialog * * @author jan bodnar * website zetcode.com * last modified June 2009 */ public class SWTApp { private Shell shell; public SWTApp(Display display) { shell = new Shell(display); shell.setText("ColorDialog"); initUI(); shell.setSize(350, 250); shell.setLocation(300, 300); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } }

public void initUI() { final Label label = new Label(shell, SWT.NONE); label.setText("ZetCode Java SWT tutorial"); label.setLocation(50, 50); label.pack(); shell.addMouseListener(new MouseAdapter() { @Override public void mouseDown(MouseEvent event) { ColorDialog dialog = new ColorDialog(shell); RGB rgb = dialog.open(); if (rgb != null) { Color col = new Color(shell.getDisplay(), rgb); label.setForeground(col); col.dispose(); } } }); } public static void main(String[] args) { Display display = new Display();

418

new SWTApp(display); display.dispose(); } }

The example is very similar to the previous one. This time we change the color of the label.
ColorDialog dialog = new ColorDialog(shell);

We create the ColorDialog.


RGB rgb = dialog.open();

We get the RGB value.


Color col = new Color(shell.getDisplay(), rgb); label.setForeground(col);

We get the color value and modify the color of the label.

Figure: ColorDialog

FileDialog
The FileDialog is used to select a name of a file.
package com.zetcode; import import import import import import import org.eclipse.swt.SWT; org.eclipse.swt.events.MouseAdapter; org.eclipse.swt.events.MouseEvent; org.eclipse.swt.widgets.Display; org.eclipse.swt.widgets.FileDialog; org.eclipse.swt.widgets.Label; org.eclipse.swt.widgets.Shell;

419

/** * ZetCode Java SWT tutorial * * This example shows a file dialog * * @author jan bodnar * website zetcode.com * last modified June 2009 */ public class SWTApp { private Shell shell; public SWTApp(Display display) { shell = new Shell(display); shell.setText("FileDialog"); initUI(); shell.setSize(350, 250); shell.setLocation(300, 300); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } } } public void initUI() { final Label label = new Label(shell, SWT.NONE); label.setText("ZetCode Java SWT tutorial"); label.setLocation(50, 50); label.pack(); shell.addMouseListener(new MouseAdapter() { @Override public void mouseDown(MouseEvent event) { FileDialog dialog = new FileDialog(shell, SWT.OPEN); String[] filterNames = new String[] {"Java sources", "All Files (*)"}; String[] filterExtensions = new String[] {"*.java", "*"}; dialog.setFilterNames(filterNames); dialog.setFilterExtensions(filterExtensions); String path = dialog.open();

420

if (path != null) { label.setText(path); label.pack(); } }); } public static void main(String[] args) { Display display = new Display(); new SWTApp(display); display.dispose(); } } }

The code example uses a FileDialog to select a file. The dialog uses a filter to show only the java sources. The name of the file is shown in the label.
FileDialog dialog = new FileDialog(shell, SWT.OPEN);

We create an FileDialog with a SWT.OPEN flag. The dialog can be used to open or save files.
String[] filterNames = new String[] {"Java sources", "All Files (*)"}; String[] filterExtensions = new String[] {"*.java", "*"}; dialog.setFilterNames(filterNames); dialog.setFilterExtensions(filterExtensions);

We use a filter to narrow the files to Java sources. This part of the Java SWT tutorial was about dialogs in SWT.

Painting in Java SWT


In this part of the Java SWT tutorial, we will do some painting. When do we need to paint? There are situations, when we need to create a widget from scratch. In such a case, we need to do painting. Or we want to create charts, special ornaments, effects or widget enhancements.

Colors
In the first example, we will work with colors. A color is an object representing a combination of Red, Green, and Blue (RGB) intensity values. In Java SWT, valid RGB values are in the range 0 to 255.
package com.zetcode; import org.eclipse.swt.events.PaintEvent; import org.eclipse.swt.events.PaintListener;

421

import org.eclipse.swt.graphics.Color; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; /** * ZetCode Java SWT tutorial * * This program draws three rectangles. * The interiors are filled with * different colors. * * @author jan bodnar * website zetcode.com * last modified June 2009 */ public class SWTApp { private Shell shell; public SWTApp(Display display) { shell = new Shell(display); shell.addPaintListener(new ColorsPaintListener()); shell.setText("Colors"); shell.setSize(360, 120); shell.setLocation(300, 300); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } }

private class ColorsPaintListener implements PaintListener { public void paintControl(PaintEvent e) { drawRectangles(e); e.gc.dispose(); } }

private void drawRectangles(PaintEvent e) { Color c1 = new Color(e.display, 50, 50, 200); e.gc.setBackground(c1); e.gc.fillRectangle(10, 15, 90, 60); Color c2 = new Color(e.display, 105, 90, 60); e.gc.setBackground(c2); e.gc.fillRectangle(130, 15, 90, 60); Color c3 = new Color(e.display, 33, 200, 100); e.gc.setBackground(c3); e.gc.fillRectangle(250, 15, 90, 60); c1.dispose();

422

c2.dispose(); c3.dispose(); } public static void main(String[] args) { Display display = new Display(); new SWTApp(display); display.dispose(); }

In our example, we will draw three rectangles and fill them with three different colors.
shell.addPaintListener(new ColorsPaintListener());

We create a listener for paint events.


private class ColorsPaintListener implements PaintListener { public void paintControl(PaintEvent e) { drawRectangles(e); e.gc.dispose(); } }

When a paint event is received, the paintControl() is executed. The actual drawing is delegated to the drawRectangles() method.
Color c1 = new Color(e.display, 50, 50, 200); e.gc.setBackground(c1); e.gc.fillRectangle(10, 15, 90, 60);

We create a color object and fill a rectangle with it.


c1.dispose(); c2.dispose(); c3.dispose(); e.gc.dispose();

Resources are released.

Figure: Colors

Basic shapes
The next example draws some basic shapes onto the window.

423

package com.zetcode; import import import import import import org.eclipse.swt.SWT; org.eclipse.swt.events.PaintEvent; org.eclipse.swt.events.PaintListener; org.eclipse.swt.graphics.Color; org.eclipse.swt.widgets.Display; org.eclipse.swt.widgets.Shell;

/** * ZetCode Java SWT tutorial * * In this program, we draw some * basic shapes of the Java SWT library * * @author jan bodnar * website zetcode.com * last modified June 2009 */ public class SWTApp { private Shell shell; public SWTApp(Display display) { shell = new Shell(display); shell.addPaintListener(new ArcExamplePaintListener()); shell.setText("Basic shapes"); shell.setSize(430, 300); shell.setLocation(300, 300); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } } } private class ArcExamplePaintListener implements PaintListener { public void paintControl(PaintEvent e) { drawShapes(e); e.gc.dispose(); } }

private void drawShapes(PaintEvent e) { e.gc.setAntialias(SWT.ON); e.gc.setBackground(new Color(e.display, 150, 150, 150)); e.gc.fillRectangle(20, 20, 120, 80); e.gc.fillRectangle(180, 20, 80, 80); e.gc.fillOval(290, 20, 120, 70);

424

e.gc.fillOval(20, 150, 80, 80); e.gc.fillRoundRectangle(150, 150, 100, 80, 25, 25); e.gc.fillArc(280, 150, 100, 100, 0, 115);

public static void main(String[] args) { Display display = new Display(); new SWTApp(display); display.dispose(); } }

In this example, we will create a rectangle, a square, an ellipse, a circle, a rounded rectangle, and an arc.
e.gc.fillRectangle(20, 20, 120, 80); e.gc.fillRectangle(180, 20, 80, 80); e.gc.fillOval(290, 20, 120, 70);

These lines draw a rectangle a square and an ellipse.


e.gc.fillOval(20, 150, 80, 80);

Here the fillOval() method draws a circle.


e.gc.fillRoundRectangle(150, 150, 100, 80, 25, 25); e.gc.fillArc(280, 150, 100, 100, 0, 115);

These two lines draw a rounded rectangle and an arc.

Figure: Basic shapes

Transparent rectangles
Transparency is the quality of being able to see through a material. The easiest way to understand transparency is to imagine a piece of glass or water. Technically, the rays of light can go through the glass and this way we can see objects behind the glass. 425

In computer graphics, we can achieve transparency effects using alpha compositing. Alpha compositing is the process of combining an image with a background to create the appearance of partial transparency. The composition process uses an alpha channel. (wikipedia.org, answers.com)
package com.zetcode; import import import import import org.eclipse.swt.events.PaintEvent; org.eclipse.swt.events.PaintListener; org.eclipse.swt.graphics.Color; org.eclipse.swt.widgets.Display; org.eclipse.swt.widgets.Shell;

/** * ZetCode Java SWT tutorial * * This program draws ten * rectangles with different * levels of transparency * * @author jan bodnar * website zetcode.com * last modified June 2009 */ public class SWTApp { private Shell shell; public SWTApp(Display display) { shell = new Shell(display); shell.addPaintListener(new ArcExamplePaintListener()); shell.setText("Transparent rectangles"); shell.setSize(590, 120); shell.setLocation(300, 300); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } } } private class ArcExamplePaintListener implements PaintListener { public void paintControl(PaintEvent e) { drawRectangles(e); } } private void drawRectangles(PaintEvent e) { Color blue = new Color(e.display, 0, 0, 255); e.gc.dispose();

426

e.gc.setBackground(blue); for (int i = 1; i < 11; i++) { e.gc.setAlpha(i * 25); e.gc.fillRectangle(50 * i, 20, 40, 40); } blue.dispose(); } public static void main(String[] args) { Display display = new Display(); new SWTApp(display); display.dispose(); }

In the example we will draw ten rectangles with different levels of transparency.
e.gc.setAlpha(i * 25);

Here we set the aplha transparency value for the painting process.

Figure: Transparent rectangles

Donut
In the following example we create an complex shape by rotating a bunch of ellipses.
package com.zetcode; import import import import import import org.eclipse.swt.SWT; org.eclipse.swt.events.PaintEvent; org.eclipse.swt.events.PaintListener; org.eclipse.swt.graphics.Transform; org.eclipse.swt.widgets.Display; org.eclipse.swt.widgets.Shell;

/** * ZetCode Java SWT tutorial * * This program creates a donut shape * * @author jan bodnar * website zetcode.com * last modified June 2009 */ public class SWTApp { private Shell shell;

427

public SWTApp(Display display) { shell = new Shell(display); shell.addPaintListener(new DonutPaintListener()); shell.setText("Donut"); shell.setSize(430, 300); shell.setLocation(300, 300); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } }

private class DonutPaintListener implements PaintListener { public void paintControl(PaintEvent e) { drawDonut(e); e.gc.dispose(); } }

private void drawDonut(PaintEvent e) { int w = e.width; int h = e.height; e.gc.setAntialias(SWT.ON); Transform tr = new Transform(e.display); tr.translate(w / 2, h / 2); e.gc.setTransform(tr); for (int rot = 0; rot < 36; rot++) { tr.rotate(5f); e.gc.drawOval(-125, -40, 250, 80); e.gc.setTransform(tr); } } public static void main(String[] args) { Display display = new Display(); new SWTApp(display); display.dispose(); }

In this example, we create a donut. The shapes resembles a cookie, hence the name donut.
Transform tr = new Transform(e.display); tr.translate(w / 2, h / 2); e.gc.setTransform(tr);

We move the middle of the axis to the center of the window. 428

for (int rot = 0; rot < 36; rot++) { tr.rotate(5f); e.gc.drawOval(-125, -40, 250, 80); e.gc.setTransform(tr); }

We do several rotations and draw the ellipses.

Figure: Donut

Drawing text
In the next example, we draw some text on the window.
package com.zetcode; import import import import import import import org.eclipse.swt.SWT; org.eclipse.swt.events.PaintEvent; org.eclipse.swt.events.PaintListener; org.eclipse.swt.graphics.Color; org.eclipse.swt.graphics.Font; org.eclipse.swt.widgets.Display; org.eclipse.swt.widgets.Shell;

/** * ZetCode Java SWT tutorial * * This program draws text * on the window * * @author jan bodnar * website zetcode.com * last modified June 2009 */ public class SWTApp { Shell shell;

429

public SWTApp(Display display) { shell = new Shell(display); shell.addPaintListener(new LyricsExamplePaintListener()); shell.setText("Soulmate"); shell.setSize(380, 300); shell.setLocation(300, 300); shell.open(); while (!shell.isDisposed()) { if (!display.readAndDispatch()) { display.sleep(); } } } private class LyricsExamplePaintListener implements PaintListener { public void paintControl(PaintEvent e) { drawLyrics(e); e.gc.dispose(); } }

private void drawLyrics(PaintEvent e) { e.gc.setAntialias(SWT.ON); Font font = new Font(e.display, "Purisa", 10, SWT.NORMAL); Color color = new Color(e.display, 25, 25, 25); e.gc.setForeground(color); e.gc.setFont(font); e.gc.drawText("Most relationships seem so transitory", 20, 30); e.gc.drawText("They're good but not the permanent one", 20, 60); e.gc.drawText("Who doesn't long for someone to hold", 20, 120); e.gc.drawText("Who knows how to love without being told", 20, 150); e.gc.drawText("Somebody tell me why I'm on my own", 20, 180); e.gc.drawText("If there's a soulmate for everyone", 20, 210); font.dispose();

public static void main(String[] args) { Display display = new Display(); new SWTApp(display); display.dispose(); } }

We display part of the lyrics from the Natasha Bedingfields Soulmate song.
Font font = new Font(e.display, "Purisa", 10, SWT.NORMAL);

Here we specify the font, that we use. Purisa, 10px, normal.

430

e.gc.drawText("Most relationships seem so transitory", 20, 30);

The drawText() method draws text onto the window.

Figure: Soulmate In this chapter of the Java SWT tutorial, we did some painting.

Nibbles
In this part of the Java SWT tutorial, we will create a Nibbles game clone. Nibbles is an older classic video game. It was first created in late 70s. Later it was brought to PCs. In this game the player controls a snake. The objective is to eat as many apples as possible. Each time the snake eats an apple, its body grows. The snake must avoid the walls and its own body.

Development
The size of each of the joints of a snake is 10px. The snake is controlled with the cursor keys. Initially, the snake has three joints. The game starts immediately. When the game is finished, we display "Game Over" message in the center of the window.
package com.zetcode; import import import import import import import import import org.eclipse.swt.SWT; org.eclipse.swt.events.KeyAdapter; org.eclipse.swt.events.PaintEvent; org.eclipse.swt.events.PaintListener; org.eclipse.swt.graphics.Color; org.eclipse.swt.graphics.Font; org.eclipse.swt.graphics.Image; org.eclipse.swt.graphics.ImageData; org.eclipse.swt.graphics.Point;

431

import org.eclipse.swt.widgets.Canvas; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; public class Board extends Canvas { private private private private private private final final final final final final int int int int int int WIDTH = 300; HEIGHT = 300; DOT_SIZE = 10; ALL_DOTS = 900; RAND_POS = 29; DELAY = 140;

private int x[] = new int[ALL_DOTS]; private int y[] = new int[ALL_DOTS]; private int dots; private int apple_x; private int apple_y; private private private private private boolean boolean boolean boolean boolean left = false; right = true; up = false; down = false; inGame = true;

private Image ball; private Image apple; private Image head; private Display display; private Shell shell; private Runnable runnable;

public Board(Shell shell) { super(shell, SWT.NULL); this.shell = shell; display = shell.getDisplay(); setSize(WIDTH, HEIGHT); this.addPaintListener(new BoardPaintListener()); this.addKeyListener(new BoardKeyListener()); Color col = new Color(shell.getDisplay(), 0, 0, 0); this.setBackground(col); col.dispose(); ImageData iib = new ImageData("dot.png"); ball = new Image(display, iib); ImageData iia = new ImageData("apple.png"); apple = new Image(display, iia);

432

ImageData iih = new ImageData("head.png"); head = new Image(display, iih); initGame(shell.getDisplay()); } public void initGame(final Display display) { dots = 3; for (int z = 0; z < dots; z++) { x[z] = 50 - z*10; y[z] = 50; } locateApple(); runnable = new Runnable() { public void run() { if (inGame) { checkApple(); checkCollision(); move(); } display.timerExec(DELAY, this); redraw(); }; }; private class BoardPaintListener implements PaintListener { public void paintControl(PaintEvent e) { Color col = new Color(shell.getDisplay(), 0, 0, 0); e.gc.setBackground(col); col.dispose(); e.gc.setAntialias(SWT.ON); if (inGame) { drawObjects(e); } else { gameOver(e); } e.gc.dispose(); } } public void drawObjects(PaintEvent e) { e.gc.drawImage(apple, apple_x, apple_y); for (int z = 0; z < dots; z++) { }

display.timerExec(DELAY, runnable);

433

} }

if (z == 0) { e.gc.drawImage(head, x[z], y[z]); } else { e.gc.drawImage(ball, x[z], y[z]); }

public void gameOver(PaintEvent e) { String msg = "Game Over"; Font font = new Font(e.display, "Helvetica", 12, SWT.NORMAL); Color white = new Color(shell.getDisplay(), 255, 255, 255); Point size = e.gc.textExtent (msg); e.gc.setForeground(white); e.gc.setFont(font); e.gc.drawText(msg, (WIDTH - size.x) / 2, (HEIGHT - size.y) / 2); font.dispose(); white.dispose(); e.gc.dispose(); display.timerExec(-1, runnable); } public void checkApple() { if ((x[0] == apple_x) && (y[0] == apple_y)) { dots++; locateApple(); } } public void move() { for (int z = dots; z > 0; z--) { x[z] = x[(z - 1)]; y[z] = y[(z - 1)]; } if (left) { x[0] -= DOT_SIZE; } if (right) { x[0] += DOT_SIZE; } if (up) { y[0] -= DOT_SIZE; } if (down) { y[0] += DOT_SIZE; }

434

} public void checkCollision() { for (int z = dots; z > 0; z--) { if ((z > 4) && (x[0] == x[z]) && (y[0] == y[z])) { inGame = false; } } if (y[0] > HEIGHT - DOT_SIZE) { inGame = false; } if (y[0] < 0) { inGame = false; } if (x[0] > WIDTH - DOT_SIZE) { inGame = false; } if (x[0] < 0) { inGame = false; }

public void locateApple() { int r = (int) (Math.random() * RAND_POS); apple_x = ((r * DOT_SIZE)); r = (int) (Math.random() * RAND_POS); apple_y = ((r * DOT_SIZE)); } private class BoardKeyListener extends KeyAdapter { @Override public void keyPressed(org.eclipse.swt.events.KeyEvent e) { int key = e.keyCode; if ((key left up = down } == SWT.ARROW_LEFT) && (!right)) { = true; false; = false;

if ((key == SWT.ARROW_RIGHT) && (!left)) { right = true; up = false; down = false; } if ((key == SWT.ARROW_UP) && (!down)) { up = true; right = false; left = false; }

435

} } }

if ((key == SWT.ARROW_DOWN) && (!up)) { down = true; right = false; left = false; }

First we will define some globals used in our game. The WIDTH and HEIGHT constants determine the size of the Board. The DOT_SIZE is the size of the apple and the dot of the snake. The ALL_DOTS constant defines the maximum number of possible dots on the Board. The RAND_POS constant is used to calculate a random position of an apple. The DELAY constant determines the speed of the game.
private int x[] = new int[ALL_DOTS]; private int y[] = new int[ALL_DOTS];

These two arrays store x, y coordinates of all possible joints of a snake. The initGame() method initializes variables, loads images and starts a timeout function.
runnable = new Runnable() { public void run() { if (inGame) { checkApple(); checkCollision(); move(); } display.timerExec(DELAY, this); redraw(); } };

Every DELAY ms, the run() method is called. If we are in the game, we call three methods, that build the logic of the game.
if (inGame) { drawObjects(e); } else { gameOver(e); }

Inside the paintControl() method, we check the inGame variable. If it is true, we draw our objects. The apple and the snake joints. Otherwise we display "Game over" text.
public void drawObjects(PaintEvent e) { e.gc.drawImage(apple, apple_x, apple_y); for (int z = 0; z < dots; z++) { if (z == 0) { e.gc.drawImage(head, x[z], y[z]); } else { e.gc.drawImage(ball, x[z], y[z]); }

436

The drawObjects() method draws the apple and the joints of the snake. The first joint of a snake is its head, which is represented by a red circle.
public void checkApple() { if ((x[0] == apple_x) && (y[0] == apple_y)) { dots++; locateApple(); } }

The checkApple() method checks, if the snake has hit the apple object. If so, we add another snake joint and call the locateApple() method, which randomly places a new apple object. In the move() method we have the key algorithm of the game. To understand it, look at how the snake is moving. You control the head of the snake. You can change its direction with the cursor keys. The rest of the joints move one position up the chain. The second joint moves where the first was, the third joint where the second was etc.
for (int z = dots; z > 0; z--) { x[z] = x[(z - 1)]; y[z] = y[(z - 1)]; }

This code moves the joints up the chain.


if (left) { x[0] -= DOT_SIZE; }

Move the head to the left. In the checkCollision() method, we determine if the snake has hit itself or one of the walls.
for (int z = dots; z > 0; z--) { if ((z > 4) && (x[0] == x[z]) && (y[0] == y[z])) { inGame = false; }

Finish the game, if the snake hits one of its joints with the head.
if (y[0] > HEIGHT - DOT_SIZE) { inGame = false; }

Finish the game, if the snake hits the bottom of the Board. The locateApple() method locates an apple randomly on the board.

437

int r = (int) (Math.random() * RAND_POS);

We get a random number from 0 to RAND_POS - 1.


apple_x = ((r * DOT_SIZE)); ... apple_y = ((r * DOT_SIZE));

These line set the x, y coordinates of the apple object. In the keyPressed() method of the BoardKeyListener class, we determine the keys that were pressed.
if ((key left up = down } == SWT.ARROW_LEFT) && (!right)) { = true; false; = false;

If we hit the left cursor key, we set left variable to true. This variable is used in the move() method to change coordinates of the snake object. Notice also, that when the snake is heading to the right, we cannot turn immediately to the left.
package com.zetcode; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; /** * ZetCode Java SWT tutorial * * In this code example, we create * a Nibbles game clone * * @author jan bodnar * website zetcode.com * last modified June 2009 */ public class SWTApp { private Shell shell; public SWTApp(Display display) { shell = new Shell(display); shell.setText("Nibbles"); initUI(); shell.setSize(305, 330); shell.setLocation(300, 300); shell.open(); while (!shell.isDisposed()) {

438

} }

if (!display.readAndDispatch()) { display.sleep(); }

public void initUI() { FillLayout layout = new FillLayout(); shell.setLayout(layout); } new Board(shell);

public static void main(String[] args) { Display display = new Display(); new SWTApp(display); display.dispose(); }

In this class, we set up the Nibbles game.


shell.setSize(305, 330);

In SWT library, there is no platform independent way of calculating the size of the decorations of the window. Borders and title bar. Or I didn't find it. These two numbers fit my case. You may need to update the width and height of the window to fit your case.

Figure: Nibbles This was the Nibbles computer game programmed with the SWT library and the Java programming language.

439

Java 2D tutorial
This is Java 2D tutorial. In this tutorial, we will learn the basics of programming in Java 2D. The Java 2D tutorial is suitable for beginners and intermediate Java programmers.

Java 2D
Java 2D is an API for drawing two-dimensional graphics using the Java programming language. Java 2D is a powerful technology. It can be used to create rich user interfaces, games, animations, multimedia applications or various special effects.

Introduction
In this part of the Java 2D tutorial, we will introduce the Java 2D technology.

About
This is Java 2D tutorial. It is aimed at beginners. This tutorial will teach you basics of programming in Java 2D. The images used in this tutorial can be downloaded here.

Revisions

September 2008 - the tutorial was created. June 2013 - the tutorial was updated and cleaned.

Vector graphics
There are two different computer graphics. Vector and raster graphics. Raster (bitmap) graphics represent images as a collection of pixels. Vector graphics is the use of geometrical primitives such as points, lines, curves or polygons to represent images. These primitives are created using mathematical equations. Both types of computer graphics have advantages and disadvantages. The advantages of vector graphics are:

smaller size ability to zoom indefinitely moving, scaling, filling or rotating does not degrade the quality of an image

The Java 2D API provides tools to work with both vector and raster graphics.

Java 2D API
Java 2D is an API for drawing two-dimensional graphics using the Java programming language. The Java 2D API provides the following capabilities:

A uniform rendering model for display devices and printers 440

A wide range of geometric primitives Hit detection on shapes, text, and images A compositing model Enhanced color support Printing documents Control of the quality of the rendering

The Java 2D API enhances the graphics, text, and imaging capabilities of the Abstract Windowing Toolkit (AWT). AWT was the original toolkit for creating user interfaces and graphics in Java. For compatibility purposes, Java 2D is technically a superset of the AWT toolkit. Java 2D is a powerful technology. It can be used to create rich user interfaces, games, animations, multimedia applications or various special effects.

The paint mechanism


The custom painting code should be placed in the paintComponent() method. This method is invoked when it is time to paint. The paint subsystem first calls the paint() method. This method invokes the following three methods:

paintComponent() paintBorder() paintChildren()

In specific cases, we might want to override the paintBorder() or the paintChildren() methods. In most cases, we override the paintComponent() method.

Simple Java 2D example


We will create a simple example of a Java 2D application. Skeleton.java
import import import import import package com.zetcode; java.awt.Graphics; java.awt.Graphics2D; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities;

class Surface extends JPanel { private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; g2d.drawString("Java 2D", 50, 50);

@Override public void paintComponent(Graphics g) { super.paintComponent(g);

441

} }

doDrawing(g);

public class Skeleton extends JFrame { public Skeleton() { } initUI();

private void initUI() { setTitle("Simple Java 2D example"); add(new Surface()); setSize(300, 200); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { Skeleton sk = new Skeleton(); sk.setVisible(true);

} } } });

We draw a text on a JPanel component. Much of the code repeats throughout the Java 2D tutorial.
class Surface extends JPanel { ... }

We create a Surface class. This class will be our drawing panel. It inherits from the JPanel component.
Graphics2D g2d = (Graphics2D) g;

The Graphics2D class is a fundamental class for rendering graphics in Java 2D. It represents number of devices in a generic way. It extends the old Graphics object. This casting is necessary to get access to all advanced operations.
g2d.drawString("Java 2D", 50, 50);

Here we draw a string on the panel with the drawString() method.


@Override

442

public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g); }

Custom painting is performed inside the paintComponent() method, which we override. The super.paintComponent() method calls the method of the parent class. It does some necessary work to prepare component for drawing. We delegate the drawing to the doDrawing() method.
private void initUI() { ... }

The initUI() method initiates the user interface of the application.


add(new Surface());

The surface is added to the JFrame container.


SwingUtilities.invokeLater(new Runnable() { @Override public void run() { Skeleton sk = new Skeleton(); sk.setVisible(true); }); }

We create an instance of our code example and make it visible on the screen. The invokeLater() method places the application on the Swing Event Queue. It is used to ensure that all UI updates are concurrency-safe.

Figure: Simple Java 2D example

Reference
The following resources were used to create this tutorial:

Java Platform, Standard Edition 6 API Specification

443

JH Labs Performing Convolution Operations Java 2D demo code examples

This part of the Java 2D tutorial was an introduction to the Java 2D library.

Basic drawing
In this part of the Java 2D tutorial, we will do some basic drawing.

Points
The most simple graphics primitive is a point. It is a single dot on the window. There is a Point class for representing a point in a coordinate space, but there is no method to to draw a point. We used the drawLine() method, where we supplied one point twice. Points.java
import import import import import import import import import package com.zetcode; java.awt.Color; java.awt.Dimension; java.awt.Graphics; java.awt.Graphics2D; java.awt.Insets; java.util.Random; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities;

class Surface extends JPanel { private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; g2d.setColor(Color.blue); Dimension size = getSize(); Insets insets = getInsets(); int w = size.width - insets.left - insets.right; int h = size.height - insets.top - insets.bottom; Random r = new Random(); for (int i = 0; i < 1000; i++) { int x = Math.abs(r.nextInt()) % w; int y = Math.abs(r.nextInt()) % h; g2d.drawLine(x, y, x, y); } }

@Override public void paintComponent(Graphics g) {

444

super.paintComponent(g); doDrawing(g); } }

public class Points extends JFrame { public Points() { initUI(); } private void initUI() { setTitle("Points"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); add(new Surface()); setSize(350, 250); setLocationRelativeTo(null);

public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { Points ps = new Points(); ps.setVisible(true);

} } } });

One point is difficult to observe. Therefore, we will randomly draw 1000 points on the panel surface.
g2d.setColor(Color.blue);

We will paint our points in blue color.


Dimension size = getSize(); Insets insets = getInsets();

The size of the window includes borders and a titlebar. We don't paint there.
int w = int h = size.width - insets.left - insets.right; size.height - insets.top - insets.bottom;

Here we calculate the area where we will effectively paint our points.
Random r = new Random(); int x = Math.abs(r.nextInt()) % w; int y = Math.abs(r.nextInt()) % h;

We get a random number in range of the size of area, that we computed above. 445

g2d.drawLine(x, y, x, y);

Here we draw the point. As we already mentioned, we use the drawLine() method. We specify the same point twice.

Figure: Points

Lines
A line is a simple graphics primitive. A line is an object which connects two points. Lines are drawn with the drawLine() method. Lines2.java
import import import import import package com.zetcode; java.awt.Graphics; java.awt.Graphics2D; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities;

class Surface extends JPanel { private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; g2d.drawLine(30, 30, 200, 30); g2d.drawLine(200, 30, 30, 200); g2d.drawLine(30, 200, 200, 200); g2d.drawLine(200, 200, 30, 30); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g); } }

446

public class Lines2 extends JFrame { public Lines2() { initUI(); } private void initUI() { setTitle("Lines"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); add(new Surface()); setSize(350, 250); setLocationRelativeTo(null);

public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { Lines2 lines = new Lines2(); lines.setVisible(true); }); } } }

We draw a simple object with four lines.


g2d.drawLine(30, 30, 200, 30);

A straight line is drawn. The parameters of the method are the x, y coordinates of the two points.

Figure: Lines

447

BasicStroke
The BasicStroke class defines a basic set of rendering attributes for the outlines of graphics primitives. These rendering attributes include width, end caps, line joins, miter limit and dash attributes. BasicStrokes.java
package com.zetcode; import import import import import import java.awt.BasicStroke; java.awt.Graphics; java.awt.Graphics2D; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities;

class Surface extends JPanel { private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; float[] float[] float[] float[] dash1 dash2 dash3 dash4 = = = = {2f, {1f, {4f, {4f, 0f, 1f, 0f, 4f, 2f}; 1f}; 2f}; 1f};

g2d.drawLine(20, 40, 250, 40); BasicStroke bs1 = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND, 1.0f, dash1, 2f); BasicStroke bs2 = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND, 1.0f, dash2, 2f); BasicStroke bs3 = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND, 1.0f, dash3, 2f); BasicStroke bs4 = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND, 1.0f, dash4, 2f); g2d.setStroke(bs1); g2d.drawLine(20, 80, 250, 80); g2d.setStroke(bs2); g2d.drawLine(20, 120, 250, 120); g2d.setStroke(bs3); g2d.drawLine(20, 160, 250, 160); g2d.setStroke(bs4); g2d.drawLine(20, 200, 250, 200);

@Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g);

448

public class BasicStrokes extends JFrame { public BasicStrokes() { initUI(); } private void initUI() { setTitle("Basic strokes"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); add(new Surface()); setSize(280, 270); setLocationRelativeTo(null);

public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { BasicStrokes bs = new BasicStrokes(); bs.setVisible(true); }); } } }

In this example, we show various types of dashes. A dash attribute is a pattern, which is created by mixing opaque and transparent sections.
float[] float[] float[] float[] dash1 dash2 dash3 dash4 = = = = { { { { 2f, 1f, 4f, 4f, 0f, 1f, 0f, 4f, 2f 1f 2f 1f }; }; }; };

Here we define four different dash patterns.


BasicStroke bs1 = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND, 1.0f, dash1, 2f );

This line constructs a BasicStroke object.


g2d.setStroke(bs1);

We use the setStroke() method to apply the BasicStroke to the current graphics context.
g2d.drawLine(20, 80, 250, 80);

Finally, we draw the line.

449

Figure: Basic strokes

Caps
Caps are decorations applied to the ends of unclosed subpaths and dash segments. There are three different end caps in Java 2D. CAP_BUTT, CAP_ROUND and CAP_SQUARE. Caps.java
import import import import import import import package com.zetcode; java.awt.BasicStroke; java.awt.Graphics; java.awt.Graphics2D; java.awt.RenderingHints; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities;

class Surface extends JPanel { private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; RenderingHints rh = new RenderingHints( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); rh.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); g2d.setRenderingHints(rh); BasicStroke bs1 = new BasicStroke(8, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL); g2d.setStroke(bs1); g2d.drawLine(20, 30, 250, 30); BasicStroke bs2 = new BasicStroke(8, BasicStroke.CAP_ROUND, BasicStroke.JOIN_BEVEL); g2d.setStroke(bs2);

450

g2d.drawLine(20, 80, 250, 80); BasicStroke bs3 = new BasicStroke(8, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_BEVEL); g2d.setStroke(bs3); g2d.drawLine(20, 130, 250, 130); BasicStroke bs4 = new BasicStroke(); g2d.setStroke(bs4); g2d.drawLine(20, 20, 20, 140); g2d.drawLine(250, 20, 250, 140); g2d.drawLine(254, 20, 254, 140);

@Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g); } }

public class Caps extends JFrame { public Caps() { initUI(); } private void initUI() { setTitle("Caps"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); add(new Surface()); setSize(280, 270); setLocationRelativeTo(null);

public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { Caps caps = new Caps(); caps.setVisible(true); }); } } }

In our example, we show all three types of end caps.


BasicStroke bs1 = new BasicStroke(8, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL); g2d.setStroke(bs1);

451

A basic stroke with a butt cap is created and applied.


g2d.drawLine(20, 20, 20, 140); g2d.drawLine(250, 20, 250, 140); g2d.drawLine(254, 20, 254, 140);

We draw three vertical lines to explain the differences among the end caps. Lines with CAP_ROUND and CAP_SQUARE are bigger than the line with CAP_BUTT. Exactly how much bigger depends on the line size. In our case a line is 8px thick. Lines are bigger by 8px, 4px on the left and 4px on the right. It is clear from the picture.

Figure: Caps

Joins
Line joins are decorations applied at the intersection of two path segments and at the intersection of the endpoints of a subpath. There are three decorations are. JOIN_BEVEL, JOIN_MITER and JOIN_ROUND. Joins.java
package com.zetcode; import import import import import import java.awt.BasicStroke; java.awt.Graphics; java.awt.Graphics2D; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities;

class Surface extends JPanel { private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; BasicStroke bs1 = new BasicStroke(8, BasicStroke.CAP_ROUND, BasicStroke.JOIN_BEVEL); g2d.setStroke(bs1); g2d.drawRect(15, 15, 80, 50);

452

BasicStroke bs2 = new BasicStroke(8, BasicStroke.CAP_ROUND, BasicStroke.JOIN_MITER); g2d.setStroke(bs2); g2d.drawRect(125, 15, 80, 50); BasicStroke bs3 = new BasicStroke(8, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND); g2d.setStroke(bs3); g2d.drawRect(235, 15, 80, 50); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g);

} }

public class Joins extends JFrame { public Joins() { } initUI();

private void initUI() { setTitle("Joins"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); add(new Surface()); setSize(340, 110); setLocationRelativeTo(null); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { Joins js = new Joins(); js.setVisible(true);

} } } });

This code example show three different line joins in action.


BasicStroke bs1 = new BasicStroke(8, BasicStroke.CAP_ROUND, BasicStroke.JOIN_BEVEL); g2d.setStroke(bs1); g2d.drawRect(15, 15, 80, 50);

Here we create a rectangle with a JOIN_BEVEL join. 453

Figure: Joins In this part of the Java 2D tutorial, we did some basic drawing.

Shapes and fills


In this part of the Java 2D tutorial, we will create some basic and more advanced shapes. Then we will fill shapes with solid colors, gradients and textures.

Basic shapes
First we draw some basic Java 2D shapes. BasicShapes.java
package com.zetcode; import import import import import import import import java.awt.Color; java.awt.Graphics; java.awt.Graphics2D; java.awt.RenderingHints; java.awt.geom.Ellipse2D; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities;

class Surface extends JPanel { private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; g2d.setColor(new Color(150, 150, 150)); RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); rh.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); g2d.setRenderingHints(rh); g2d.fillRect(30, 20, 50, 50); g2d.fillRect(120, 20, 90, 60); g2d.fillRoundRect(250, 20, 70, 60, 25, 25); g2d.fill(new Ellipse2D.Double(10, 100, 80, 100)); g2d.fillArc(120, 130, 110, 100, 5, 150);

454

g2d.fillOval(270, 130, 50, 50); @Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g);

public class BasicShapes extends JFrame { public BasicShapes() { initUI(); } private void initUI() { setTitle("Basic Shapes"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); add(new Surface()); setSize(350, 250); setLocationRelativeTo(null);

public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { BasicShapes bs = new BasicShapes(); bs.setVisible(true); }); } } }

In this example, we draw six basic shapes on the panel. A square, a rectangle, a rounded rectangle, an ellipse, an arc and a circle.
g2d.setColor(new Color(150, 150, 150));

The shapes will be drawn in a gray background.


g2d.fillRect(20, 20, 50, 50); g2d.fillRect(120, 20, 90, 60);

The fillRect() method is used to draw both a rectangle and a square. The first two parameters are x, y coordinates of a shape to be drawn. The last two parameters are the width and the height of the shape.
g2d.fillRoundRect(250, 20, 70, 60, 25, 25);

455

Here we create a rounded rectangle. The last two parameters are the horizontal and vertical diameters of the arc at the four corners.

Figure: Basic shapes

General path
More complex shapes can be constructed with a GeneralPath class. It represents a geometric path constructed from straight lines, and quadratic and cubic Bzier curves. In the next example, we will create a star with this class. Star.java
package com.zetcode; import import import import import import import java.awt.Graphics; java.awt.Graphics2D; java.awt.RenderingHints; java.awt.geom.GeneralPath; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities;

class Surface extends JPanel { private double points[][] = { { 0, 85 }, { 75, 75 }, { 100, 10 }, { 125, 75 }, { 200, 85 }, { 150, 125 }, { 160, 190 }, { 100, 150 }, { 40, 190 }, { 50, 125 }, { 0, 85 } }; private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D)g; g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); g2d.translate(25, 5);

456

GeneralPath star = new GeneralPath(); star.moveTo(points[0][0], points[0][1]); for (int k = 1; k < points.length; k++) star.lineTo(points[k][0], points[k][1]); star.closePath(); g2d.fill(star); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g);

} }

public class Star extends JFrame { public Star() { } initUI();

private void initUI() { setTitle("Star"); add(new Surface()); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize(350, 250); setLocationRelativeTo(null); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { Star sr = new Star(); sr.setVisible(true);

} } } });

We will create a star from a series of points.


private double points[][] = { { 0, 85 }, { 75, 75 }, { 100, 10 }, { 125, 75 }, { 200, 85 }, { 150, 125 }, { 160, 190 }, { 100, 150 }, { 40, 190 }, { 50, 125 }, { 0, 85 } };

457

These are the coordinates of the star.


GeneralPath star = new GeneralPath();

Here we instantiate the GeneralPath class.


star.moveTo(points[0][0], points[0][1]);

We move to the initial coordinate of the GeneralPath.


for (int k = 1; k < points.length; k++) star.lineTo(points[k][0], points[k][1]);

Here we connect all the coordinates of the star.


star.closePath(); g2d.fill(star);

We close the path and fill the interior of the star.

Figure: Star

Colors
The Color class is used to work with colors in Java 2D. To fill rectangles with the current color, we use the fillRect() method. Colors.java
package com.zetcode; import import import import import import java.awt.Color; java.awt.Graphics; java.awt.Graphics2D; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities;

class Surface extends JPanel {

458

public void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; g2d.setColor(new Color(125, 167, 116)); g2d.fillRect(10, 10, 90, 60); g2d.setColor(new Color(42, 179, 231)); g2d.fillRect(130, 10, 90, 60); g2d.setColor(new Color(70, 67, 123)); g2d.fillRect(250, 10, 90, 60); g2d.setColor(new Color(130, 100, 84)); g2d.fillRect(10, 100, 90, 60); g2d.setColor(new Color(252, 211, 61)); g2d.fillRect(130, 100, 90, 60); g2d.setColor(new Color(241, 98, 69)); g2d.fillRect(250, 100, 90, 60); g2d.setColor(new Color(217, 146, 54)); g2d.fillRect(10, 190, 90, 60); g2d.setColor(new Color(63, 121, 186)); g2d.fillRect(130, 190, 90, 60); g2d.setColor(new Color(31, 21, 1)); g2d.fillRect(250, 190, 90, 60); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g);

} }

public class Colors extends JFrame { public Colors() { } initUI();

private void initUI() { setTitle("Colors"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); add(new Surface()); setSize(360, 300); setLocationRelativeTo(null); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() {

459

@Override public void run() { Colors col = new Colors(); col.setVisible(true); } } } });

In the example we draw nine colored rectangles.


g2d.setColor(new Color(125, 167, 116));

A new color is created with the Color class. The parameters of the constructor are the red, green and blue parts of the new color. The setColor() method sets the graphics context's current color to the specified color. All subsequent graphics operations using this graphics context use this specified color.
g2d.fillRect(10, 15, 90, 60);

To fill the rectangle with a color, we use the fillRect() method.

Figure: Colors

Gradients
In computer graphics, gradient is a smooth blending of shades from light to dark or from one color to another. In 2D drawing programs and paint programs, gradients are used to create colorful backgrounds and special effects as well as to simulate lights and shadows. (answers.com) Gradients.java
package com.zetcode; import java.awt.Color;

460

import import import import import import

java.awt.GradientPaint; java.awt.Graphics; java.awt.Graphics2D; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities;

class Surface extends JPanel { private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; GradientPaint gp1 = new GradientPaint(5, 5, Color.red, 20, 20, Color.black, true); g2d.setPaint(gp1); g2d.fillRect(20, 20, 300, 40); GradientPaint gp2 = new GradientPaint(5, 25, Color.yellow, 20, 2, Color.black, true); g2d.setPaint(gp2); g2d.fillRect(20, 80, 300, 40); GradientPaint gp3 = new GradientPaint(5, 25, Color.green, 2, 2, Color.black, true); g2d.setPaint(gp3); g2d.fillRect(20, 140, 300, 40); GradientPaint gp4 = new GradientPaint(25, 25, Color.blue, 15, 25, Color.black, true); g2d.setPaint(gp4); g2d.fillRect(20, 200, 300, 40); GradientPaint gp5 = new GradientPaint(0, 0, Color.orange, 0, 20, Color.black, true); g2d.setPaint(gp5); g2d.fillRect(20, 260, 300, 40); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g);

} }

public class Gradients extends JFrame { public Gradients() { } initUI();

private void initUI() {

461

setTitle("Gradients"); add(new Surface()); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize(350, 350); setLocationRelativeTo(null);

public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { Gradients gr = new Gradients(); gr.setVisible(true); }); } } }

Our code example presents five rectangles with gradients.


GradientPaint gp4 = new GradientPaint(25, 25, Color.blue, 15, 25, Color.black, true);

To work with gradients, we use the GradientPaint class. By manipulating the color values and the starting and ending points, we can get different results.
g2d.setPaint(gp5);

The gradient is activated calling the setPaint() method.

462

Figure: Gradients

Textures
A texture is a bitmap image applied to a shape. To work with textures in Java 2D, we use the TexturePaint class. Textures.java
package com.zetcode; import import import import import import import import import import import import import java.awt.Graphics; java.awt.Graphics2D; java.awt.Rectangle; java.awt.TexturePaint; java.awt.image.BufferedImage; java.io.File; java.io.IOException; java.util.logging.Level; java.util.logging.Logger; javax.imageio.ImageIO; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities;

class Surface extends JPanel { private private private private private private BufferedImage slate; BufferedImage java; BufferedImage pane; TexturePaint slatetp; TexturePaint javatp; TexturePaint panetp;

public Surface() { loadImages(); } private void loadImages() { try { slate = ImageIO.read(new File("slate.png")); java = ImageIO.read(new File("java.png")); pane = ImageIO.read(new File("pane.png")); } catch (IOException ex) { null, ex); } } Logger.getLogger(Surface.class.getName()).log(Level.SEVERE,

private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; slatetp = new TexturePaint(slate, new Rectangle(0, 0, 90, 60)); javatp = new TexturePaint(java, new Rectangle(0, 0, 90, 60));

463

panetp = new TexturePaint(pane, new Rectangle(0, 0, 90, 60)); g2d.setPaint(slatetp); g2d.fillRect(10, 15, 90, 60); g2d.setPaint(javatp); g2d.fillRect(130, 15, 90, 60); g2d.setPaint(panetp); g2d.fillRect(250, 15, 90, 60); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g);

} }

public class Textures extends JFrame { public Textures() { } initUI();

private void initUI() { setTitle("Textures"); add(new Surface()); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize(360, 120); setLocationRelativeTo(null); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { Textures tx = new Textures(); tx.setVisible(true);

} } } });

In the code example, we fill three rectangles with three different textures.
slate = ImageIO.read(new File("slate.png"));

Using the ImageIO class, we read the image into the buffered image.
slatetp = new TexturePaint(slate, new Rectangle(0, 0, 90, 60));

464

We create a TexturePaint class out of the buffered image.


g2d.setPaint(slatetp); g2d.fillRect(10, 15, 90, 60);

We fill a rectangle with a texture.

Figure: Textures In this part of the Java 2D tutorial, we have covered some basic and more advanced shapes of the Java 2D library.

Transparency
In this part of the Java 2D , we will talk about transparency. We will provide some basic definitions and several interesting transparency effects.

Transparency explained
Transparency is the quality of being able to see through a material. The easiest way to understand transparency is to imagine a piece of glass or water. Technically, the rays of light can go through the glass and this way we can see objects behind the glass. In computer graphics, we can achieve transparency effects using alpha compositing. Alpha compositing is the process of combining an image with a background to create the appearance of partial transparency. The composition process uses an alpha channel. Alpha channel is an 8-bit layer in a graphics file format that is used for expressing translucency (transparency). The extra eight bits per pixel serves as a mask and represents 256 levels of translucency. (answers.com, wikipedia.org) The AlphaComposite class is used to work with transparency in Java 2D. It implements the basic alpha compositing rules for combining source and destination pixels to achieve blending and transparency effects with graphics and images. To create an AlphaComposite, you provide two values. The rule designator and the alpha value. The rule specifies how we combine source and destination pixels. Most often it is AlphaComposite.SRC_OVER. The alpha value can range from 0.0f (completely transparent) to 1.0f (completely opaque).

Transparent rectangles
The first example will draw ten rectangles with different levels of transparency.

465

TransparentRectangles.java
package com.zetcode; import import import import import import import java.awt.AlphaComposite; java.awt.Color; java.awt.Graphics; java.awt.Graphics2D; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities;

class Surface extends JPanel { private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; g2d.setColor(Color.BLUE); for (int i = 1; i <= 10; i++) { g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, i * 0.1f)); g2d.fillRect(50 * i, 20, 40, 40); } } @Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g); } }

public class TransparentRectangles extends JFrame { public TransparentRectangles() { initUI(); } private void initUI() { setTitle("Transparent rectangles"); add(new Surface()); setSize(590, 120); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null);

public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { TransparentRectangles tr = new TransparentRectangles();

466

} } } });

tr.setVisible(true);

In our example we draw 10 blue rectangles with various levels of transparency applied.
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, i * 0.1f));

This is the key line of the example. We use the forementioned AlphaComposite.SRC_OVER rule. The alpha value dynamically changes in the for loop.

Figure: Transparent rectangles

Fade out demo


In the next example, we will fade out an image. The image will gradually get more transparent until it is completely invisible. FadeOut.java
import import import import import import import import import import import import package com.zetcode; java.awt.AlphaComposite; java.awt.Dimension; java.awt.Graphics; java.awt.Graphics2D; java.awt.Image; java.awt.event.ActionEvent; java.awt.event.ActionListener; javax.swing.ImageIcon; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities; javax.swing.Timer;

class Surface extends JPanel implements ActionListener { private Image img; private Timer timer; private float alpha = 1f; public Surface() { loadImage(); setSurfaceSize(); initTimer();

467

} private void loadImage() { } img = new ImageIcon("mushrooms.jpg").getImage();

private void setSurfaceSize() { int h = img.getHeight(this); int w = img.getWidth(this); setPreferredSize(new Dimension(w, h)); } private void initTimer() { timer = new Timer(20, this); timer.start(); } private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha)); g2d.drawImage(img, 0, 0, null); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g);

@Override public void actionPerformed(ActionEvent e) { alpha += -0.01f; if (alpha <= 0) { alpha = 0; timer.stop(); } } } public class FadeOut extends JFrame { public FadeOut() { } initUI(); repaint();

private void initUI() {

468

setTitle("Fade out"); add(new Surface()); pack(); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null);

public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { FadeOut fo = new FadeOut(); fo.setVisible(true); }); } } }

With the AlphaComposite we gradually fade out the image on the panel.
private void setSurfaceSize() { int h = img.getHeight(this); int w = img.getWidth(this); setPreferredSize(new Dimension(w, h));

The setSurfaceSize() method finds out the size of the image and sets a preferred size for the panel. The preferred size with the combination of the pack() method will display the whole image on the window.
private void initTimer() { timer = new Timer(20, this); timer.start(); }

The initTimer() method starts the timer. The timer fires action events after a specified delay. In reaction to an action event, we will change the alpha value and repaint the panel.
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha)); g2d.drawImage(castle, 0, 0, null);

These two methods draw an image with increasing levels of transparency on the panel.
alpha += -0.01f;

In the actionPerformed() method we gradually decrease the alpha value. Note that the alpha value must not be negative.
repaint();

469

The method repaints the component. It calls the paint() method of this component (JPanel) as soon as possible.

Waiting
In this example, we use transparency effect to create a waiting demo. We will draw 8 lines that will gradually fade out creating an illusion, that a line is moving. Such effects are often used to inform users, that a lengthy task is going on behind the scenes. An example is streaming video over the Internet. Waiting.java
package com.zetcode; import import import import import import import import import import import java.awt.AlphaComposite; java.awt.BasicStroke; java.awt.Graphics; java.awt.Graphics2D; java.awt.RenderingHints; java.awt.event.ActionEvent; java.awt.event.ActionListener; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities; javax.swing.Timer;

class Surface extends JPanel implements ActionListener { private Timer timer; private int count; private final double[][] trs = { {0.0, 0.15, 0.30, 0.5, 0.65, 0.80, 0.9, 1.0}, {1.0, 0.0, 0.15, 0.30, 0.5, 0.65, 0.8, 0.9}, {0.9, 1.0, 0.0, 0.15, 0.3, 0.5, 0.65, 0.8}, {0.8, 0.9, 1.0, 0.0, 0.15, 0.3, 0.5, 0.65}, {0.65, 0.8, 0.9, 1.0, 0.0, 0.15, 0.3, 0.5}, {0.5, 0.65, 0.8, 0.9, 1.0, 0.0, 0.15, 0.3}, {0.3, 0.5, 0.65, 0.8, 0.9, 1.0, 0.0, 0.15}, {0.15, 0.3, 0.5, 0.65, 0.8, 0.9, 1.0, 0.0,} }; public Surface() { initTimer(); } private void initTimer() { timer = new Timer(80, this); timer.setInitialDelay(190); timer.start();

private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g;

470

g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); int width = getWidth(); int height = getHeight(); g2d.setStroke(new BasicStroke(3, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); g2d.translate(width / 2, height / 2); for (int i = 0; i < 8; i++) { g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, (float) trs[count % 8][i])); g2d.rotate(Math.PI / 4f); g2d.drawLine(0, -10, 0, -40); } }

@Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g); } @Override public void actionPerformed(ActionEvent e) { repaint(); count++;

} }

public class Waiting extends JFrame { public Waiting() { } initUI();

private void initUI() { setTitle("Waiting"); add(new Surface()); setSize(300, 200); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override

471

public void run() { Waiting wt = new Waiting(); wt.setVisible(true); }); } } }

We draw eight lines with eight different alpha values.


private final double[][] trs = { ... };

This is a two dimensional array of transparency values used in this demo. There are 8 rows, each for one state. Each of the 8 lines will continuously use these values.
g2d.setStroke(new BasicStroke(3, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));

We make the lines a bit thicker, so that they are better visible. We draw the lines with rounded caps.
g2d.rotate(Math.PI/4f); g2d.drawLine(0, -10, 0, -40);

This code will draw each of the eight lines. The rotate() method is used to rotate the lines alongside a circle.

Figure: Waiting In this part of the Java 2D tutorial, we have talked about transparency.

Composition
In this part of the Java 2D programming tutorial, we will define compositing operations. Compositing is the combining of visual elements from separate sources into single images. They are used to create the illusion that all those elements are parts of the same scene.

472

Compositing is widely used in film industry to create crowds, entire new worlds which would be expensive or impossible to create otherwise. (wikipedia.org)

Operations
There are several compositing operations. We will show some of them in the next code example. The AlphaComposite class implements basic alpha compositing rules for combining source and destination colors to achieve blending and transparency effects with graphics and images. Say we want to draw two objects on a panel. The first object drawn is called a destination. The second one a source. The AlphaComposite class determines how these two are going to be blended together. If we have a AlphaComposite.SRC_OVER rule, the pixels of the source object will be drawn, where the two objects overlap. Compositing.java
package com.zetcode; import import import import import import import import java.awt.AlphaComposite; java.awt.Color; java.awt.Graphics; java.awt.Graphics2D; java.awt.image.BufferedImage; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities;

class Surface extends JPanel { private int rules[] = { AlphaComposite.DST, AlphaComposite.DST_ATOP, AlphaComposite.DST_OUT, AlphaComposite.SRC, AlphaComposite.SRC_ATOP, AlphaComposite.SRC_OUT }; private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; for (int x = 20, y = 20, i = 0; i < rules.length; x += 60, i++) { AlphaComposite ac = AlphaComposite.getInstance(rules[i], 0.8f); BufferedImage buffImg = new BufferedImage(60, 60, BufferedImage.TYPE_INT_ARGB); Graphics2D gbi = buffImg.createGraphics(); gbi.setPaint(Color.blue); gbi.fillRect(0, 0, 40, 40); gbi.setComposite(ac); gbi.setPaint(Color.green); gbi.fillRect(5, 5, 40, 40);

473

} }

g2d.drawImage(buffImg, x, y, null);

@Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g);

} }

public class Composition extends JFrame { public Composition() { setTitle("Composition"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); add(new Surface()); setSize(400, 120); setLocationRelativeTo(null);

public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { Composition cm = new Composition(); cm.setVisible(true); }); } } }

We draw two rectangles and combine them with six different compositing operations.
private int rules[] = { AlphaComposite.DST, AlphaComposite.DST_ATOP, AlphaComposite.DST_OUT, AlphaComposite.SRC, AlphaComposite.SRC_ATOP, AlphaComposite.SRC_OUT };

Here we have six different compositing rules. Only two of these have practical significance. If not sure what compositing rule to use, pick up the SRC_OVER rule.
AlphaComposite ac = AlphaComposite.getInstance(rules[i]);

Here we get the AlphaComposite class.

474

BufferedImage buffImg = new BufferedImage(60, 60, BufferedImage.TYPE_INT_ARGB); Graphics2D gbi = buffImg.createGraphics();

We use a buffer image to perform the compositing operations.


gbi.setComposite(ac);

Sets the Composite for the Graphics2D context.


g2d.drawImage(buffImg, x, y, null);

We draw the image on the panel.

Figure: Composition

Sun and Cloud


In the next example we show the Sun coming from behind a cloud. We will use composition technique in this animation. SunAndCloud.java
import import import import import import import import import import import import package com.zetcode; java.awt.AlphaComposite; java.awt.Graphics; java.awt.Graphics2D; java.awt.Image; java.awt.event.ActionEvent; java.awt.event.ActionListener; java.awt.image.BufferedImage; javax.swing.ImageIcon; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities; javax.swing.Timer;

class Surface extends JPanel implements ActionListener { private private private private Image Image Timer float sun; cloud; timer; alpha;

public Surface() { loadImages(); initTimer();

475

} private void loadImages() { sun = new ImageIcon("sun.png").getImage(); cloud = new ImageIcon("cloud.png").getImage(); } private void initTimer() { timer = new Timer(800, this); timer.start(); alpha = 1f;

private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; BufferedImage buffImg = new BufferedImage(220, 140, BufferedImage.TYPE_INT_ARGB); Graphics2D gbi = buffImg.createGraphics(); AlphaComposite ac = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha); gbi.drawImage(sun, 40, 30, null); gbi.setComposite(ac); gbi.drawImage(cloud, 0, 0, null); g2d.drawImage(buffImg, 20, 20, null); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g);

@Override public void actionPerformed(ActionEvent e) { alpha -= 0.1; if (alpha <= 0) { alpha = 0; timer.stop(); System.out.println("Timer stopped.");

} }

repaint(); }

public class SunAndCloud extends JFrame { public SunAndCloud() {

476

initUI();

private void initUI() { setTitle("Sun and Cloud"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); add(new Surface()); setSize(300, 210); setLocationRelativeTo(null); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { SunAndCloud sac = new SunAndCloud(); sac.setVisible(true);

} } } });

The Sun comes from behind the cloud. The cloud finally disappears.
private void loadImages() { sun = new ImageIcon("sun.png").getImage(); cloud = new ImageIcon("cloud.png").getImage();

We load two images from the disk.


private void initTimer() { timer = new Timer(800, this); timer.start(); alpha = 1f;

Inside the initTimer() method the timer is activated.


AlphaComposite ac = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha);

We use the AlphaComposite.SRC_OVER rule. Here the source blends with destination and overwrites empty pixels.
gbi.drawImage(sun, 40, 30, null); gbi.setComposite(ac); gbi.drawImage(cloud, 0, 0, null); g2d.drawImage(buffImg, 20, 20, null);

477

The images are rendered into a BufferedImage and are later copied to the screen. The setComposite() specifies how new pixels are to be combined with the existing pixels on the graphics device during the rendering process.

Figure: Sun & Cloud In this part of the Java 2D tutorial, we have talked about image composition.

Clipping
In this part of the Java 2D tutorial, we will talk about clipping.

Clipping
Clipping is restricting of drawing to a certain area. This is done for efficiency reasons and to create various effects. In the following example we will be clipping an image. Clipping.java
package com.zetcode; import import import import import import import import import import import import import java.awt.Dimension; java.awt.Graphics; java.awt.Graphics2D; java.awt.Image; java.awt.RenderingHints; java.awt.event.ActionEvent; java.awt.event.ActionListener; java.awt.geom.Ellipse2D; javax.swing.ImageIcon; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities; javax.swing.Timer;

class Surface extends JPanel implements ActionListener { private int pos_x = 8;

478

private int pos_y = 8; private int radius = 90; private Timer timer; private Image image; private double delta[] = { 3, 3 }; public Surface() { loadImage(); determineAndSetImageSize(); initTimer();

private void loadImage() { image = new ImageIcon("mushrooms.jpg").getImage(); } private void determineAndSetImageSize() { int h = image.getHeight(this); int w = image.getWidth(this); setPreferredSize(new Dimension(w, h));

private void initTimer() { timer = new Timer(35, this); timer.start();

private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); rh.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); g2d.setClip(new Ellipse2D.Double(pos_x, pos_y, radius, radius)); } g2d.drawImage(image, 5, 5, null);

@Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g); } @Override public void actionPerformed(ActionEvent e) { int w = getWidth(); int h = getHeight();

479

if (pos_x < 0) { delta[0] = Math.random() % 4 + 5; } else if (pos_x > w - radius) { } delta[0] = -(Math.random() % 4 + 5);

if (pos_y < 0 ) { delta[1] = Math.random() % 4 + 5; } else if (pos_y > h - radius) { } delta[1] = -(Math.random() % 4 + 5);

pos_x += delta[0]; pos_y += delta[1]; } } public class Clipping extends JFrame { public Clipping() { } initUI(); repaint();

private void initUI() { setTitle("Clipping"); add(new Surface()); pack(); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { Clipping cl = new Clipping(); cl.setVisible(true);

} } } });

In this example, we will clip an image. A circle is moving on the screen and showing a part of the underlying image. This is as if we looked through a hole.
g2d.setClip(new Ellipse2D.Double(pos_x, pos_y, radius, radius));

480

This is the key part of the code. Here we restrict drawing to a specific shape. In our case it is a circle.
if (pos_x < 0) { delta[0] = Math.random() % 4 + 5; } else if (pos_x > w - radius) { delta[0] = -(Math.random() % 4 + 5); }

If the circle hits the left or the right side of the window, the direction of the circle movement changes randomly. Same for the top and bottom sides.

Clipping shapes
In the following example, we will be clipping two shapes. A rectangle and a circle. ClippingShapes.java
import import import import import import import import import import import import import import package com.zetcode; java.awt.Color; java.awt.Graphics; java.awt.Graphics2D; java.awt.Rectangle; java.awt.RenderingHints; java.awt.event.ActionEvent; java.awt.event.ActionListener; java.awt.geom.AffineTransform; java.awt.geom.Ellipse2D; java.awt.geom.GeneralPath; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities; javax.swing.Timer;

class Surface extends JPanel implements ActionListener { private private private private private private Timer timer; double rotate = 1; int pos_x = 8; int pos_y = 8; int radius = 60; double delta[] = {1, 1};

public Surface() { initTimer(); } private void initTimer() { timer = new Timer(10, this); timer.start(); }

481

private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); int w = getWidth(); int h = getHeight(); Rectangle rect1 = new Rectangle(0, 0, 200, 80); AffineTransform tx = new AffineTransform(); tx.rotate(Math.toRadians(rotate), w / 2, h / 2); tx.translate(w / 2 - 100, h / 2 - 40); Ellipse2D circle = new Ellipse2D.Double(pos_x, pos_y, radius, radius); step(); GeneralPath path = new GeneralPath(); path.append(tx.createTransformedShape(rect1), false); g2d.setColor(new Color(110, 110, 110)); g2d.clip(circle); g2d.clip(path); g2d.fill(circle); g2d.setClip(new Rectangle(0, 0, w, h)); g2d.draw(circle); g2d.draw(path);

@Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g); } public void step() { int w = getWidth(); int h = getHeight(); if (pos_x < 0) { delta[0] = 1; } else if (pos_x > w - radius) { } delta[0] = -1;

if (pos_y < 0) {

482

delta[1] = 1; } else if (pos_y > h - radius) { delta[1] = -1; } pos_x += delta[0]; pos_y += delta[1]; } @Override public void actionPerformed(ActionEvent e) { rotate += 1; repaint();

} }

public class ClippingShapes extends JFrame { public ClippingShapes() { } initUI();

private void initUI() { setTitle("Clipping shapes"); add(new Surface()); setSize(350, 300); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { ClippingShapes cs = new ClippingShapes(); cs.setVisible(true);

} } } });

In our example, we have a bouncing circle and a rotating rectangle. When these shapes overlap, the resulting area is filled with color.
Rectangle rect1 = new Rectangle(0, 0, 200, 80); AffineTransform tx = new AffineTransform(); tx.rotate(Math.toRadians(rotate), w / 2, h / 2); tx.translate(w / 2 - 100, h / 2 - 40);

The rectangle is being rotated. It is always positioned in the middle of the panel. 483

GeneralPath path = new GeneralPath(); path.append(tx.createTransformedShape(rect1), false);

Here we get the shape of the rotated rectangle.


g2d.clip(circle); g2d.clip(path); g2d.fill(circle)

Here we restrict drawing to these two shapes. If they overlap, the interior of the resulting shape is filled with color.
g2d.setClip(new Rectangle(0, 0, w, h));

We reset the clip areas, before we draw the shapes.

Figure: Clipping Shapes In this part of the Java 2D tutorial, we have talked about clipping.

Transformations
In this part of the Java 2D programming tutorial, we will talk about transformations. An affine transform is composed of zero or more linear transformations (rotation, scaling or shear) and translation (shift). Several linear transformations can be combined into a single matrix. A rotation is a transformation that moves a rigid body around a fixed point. A scaling is a transformation that enlarges or diminishes objects. The scale factor is the same in all directions. A translation is a transformation that moves every point a constant distance in a specified direction. A shear is a transformation that moves an object perpendicular to a given axis, with greater value on one side of the axis than the other. sources: (wikipedia.org, freedictionary.com)

484

The AffineTransform is the class in Java 2D to perform affine transformations.

Translation
The following example describes a simple translation. Translation.java
package com.zetcode; import import import import import import java.awt.Color; java.awt.Graphics; java.awt.Graphics2D; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities;

class Surface extends JPanel { private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; g2d.setColor(new Color(150, 150, 150)); g2d.fillRect(20, 20, 80, 50); g2d.translate(150, 50); g2d.fillRect(20, 20, 80, 50);

@Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g); } }

public class Translation extends JFrame { public Translation() { initUI(); } private void initUI() { setTitle("Translation"); add(new Surface()); setSize(300, 200); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null);

public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override

485

public void run() { Translation trl = new Translation(); trl.setVisible(true); }); } } }

The example draws a rectangle. Then we do a translation and draw the same rectangle again.
g2d.translate(150, 50);

This line moves the origin of the Graphics2D context to a new point.

Figure: Translation

Rotation
The next example demonstrates a rotation. Rotation.java
import import import import import import package com.zetcode; java.awt.Color; java.awt.Graphics; java.awt.Graphics2D; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities;

class Surface extends JPanel { private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; g2d.setColor(new Color(150, 150, 150)); g2d.fillRect(20, 20, 80, 50); g2d.translate(180, -50); g2d.rotate(Math.PI/4); g2d.fillRect(80, 80, 80, 50);

@Override

486

public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g); } }

public class Rotation extends JFrame { public Rotation() { initUI(); } private void initUI() { setTitle("Rotation"); add(new Surface()); setSize(300, 200); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null);

public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { Rotation rt = new Rotation(); rt.setVisible(true); }); } } }

The example draws a rectangle, performs a translation and a rotation and draws the same rectangle again.
g2d.rotate(Math.PI/4);

The rotate() method performs rotation. Note that the rotation parameter is in radians.

487

Figure: Rotation

Scaling
The next example demonstrates scaling of an object. Scaling is done with the scale() method. In this method, we provide two parameters. They are the x scale and y scale factor, by which coordinates are scaled along the x or y axis respectively. Scaling.java
package com.zetcode; import import import import import import import java.awt.Color; java.awt.Graphics; java.awt.Graphics2D; java.awt.geom.AffineTransform; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities;

class Surface extends JPanel { private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; g2d.setColor(new Color(150, 150, 150)); g2d.fillRect(20, 20, 80, 50); AffineTransform tx1 = new AffineTransform(); tx1.translate(110, 22); tx1.scale(0.5, 0.5); g2d.setTransform(tx1); g2d.fillRect(0, 0, 80, 50); AffineTransform tx2 = new AffineTransform(); tx2.translate(170, 20); tx2.scale(1.5, 1.5); g2d.setTransform(tx2); g2d.fillRect(0, 0, 80, 50); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g);

} }

public class Scaling extends JFrame { public Scaling() { } initUI();

private void initUI() {

488

setTitle("Scaling"); add(new Surface()); setSize(330, 160); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { Scaling sc = new Scaling(); sc.setVisible(true);

} } } });

We have a rectangle. First we scale it down and then we scale it up a bit.


AffineTransform tx2 = new AffineTransform(); tx2.translate(170, 20); tx2.scale(1.5, 1.5);

Another scaling would be added to the first one. So we need to create and apply a new affine transform.

Figure: Scaling

Shearing
In the following example we perform shearing. We use the share() method. Scale.java
package com.zetcode; import import import import import java.awt.Color; java.awt.Graphics; java.awt.Graphics2D; java.awt.Rectangle; java.awt.geom.AffineTransform;

489

import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.SwingUtilities; class Surface extends JPanel { private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; AffineTransform tx1 = new AffineTransform(); tx1.translate(50, 90); g2d.setTransform(tx1); g2d.setColor(Color.green); g2d.drawRect(0, 0, 160, 50); AffineTransform tx2 = new AffineTransform(); tx2.translate(50, 90); tx2.shear(0, 1); g2d.setTransform(tx2); g2d.setColor(Color.blue); g2d.draw(new Rectangle(0, 0, 80, 50)); AffineTransform tx3 = new AffineTransform(); tx3.translate(130, 10); tx3.shear(0, 1); g2d.setTransform(tx3); g2d.setColor(Color.red); g2d.drawRect(0, 0, 80, 50);

@Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g); } }

public class Shearing extends JFrame { public Shearing() { initUI(); } private void initUI() { setTitle("Shearing"); add(new Surface()); setSize(330, 270); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null);

490

public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { Shearing sh = new Shearing(); sh.setVisible(true);

} } } });

In this example, we draw three rectangles in three different colors. They form a structure. Two of them are sheared.
tx2.shear(0, 1);

The two parameters are multipliers by which coordinates are shifted in the direction of the x and y axis.

Figure: Shearing

Donut
In the following example we create an complex shape by rotating an ellipse. Donut.java
import import import import import import import import import package com.zetcode; java.awt.BasicStroke; java.awt.Color; java.awt.Dimension; java.awt.Graphics; java.awt.Graphics2D; java.awt.RenderingHints; java.awt.geom.AffineTransform; java.awt.geom.Ellipse2D; javax.swing.JFrame;

491

import javax.swing.JPanel; import javax.swing.SwingUtilities; class Surface extends JPanel { private void doDrawing(Graphics g) { Graphics2D g2 = (Graphics2D) g; RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); rh.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); g2.setRenderingHints(rh); Dimension size = getSize(); double w = size.getWidth(); double h = size.getHeight(); Ellipse2D e = new Ellipse2D.Double(0, 0, 80, 130); g2.setStroke(new BasicStroke(1)); g2.setColor(Color.gray); for (double deg = 0; deg < 360; deg += 5) { AffineTransform at = AffineTransform.getTranslateInstance(w / 2, h / 2); at.rotate(Math.toRadians(deg)); g2.draw(at.createTransformedShape(e)); }

@Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g); } }

public class Donut extends JFrame { public Donut() { initUI(); } private void initUI() { setTitle("Donut"); add(new Surface()); setSize(370, 320); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null);

492

public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { Donut dn = new Donut(); dn.setVisible(true);

} } } });

In this example, we create a donut shape.


Ellipse2D e = new Ellipse2D.Double(0, 0, 80, 130); g2.setStroke(new BasicStroke(1)); g2.setColor(Color.gray);

In the beginning there was an ellipse.


for (double deg = 0; deg < 360; deg += 5) { AffineTransform at = AffineTransform.getTranslateInstance(w / 2, h / 2); at.rotate(Math.toRadians(deg)); g2.draw(at.createTransformedShape(e)); }

After several rotations, there is a donut. In this part of the Java 2D tutorial, we have talked about transformations.

Effects
In this part of the Java 2D programming tutorial, we will show some effects.

Bubbles
In the first example, we will see growing colored bubbles, that randomly appear and disappear on the screen. The example comes from the Java 2D demo. Bubbles.java
package com.zetcode; import import import import import import import import import import java.awt.BasicStroke; java.awt.Color; java.awt.Dimension; java.awt.Graphics; java.awt.Graphics2D; java.awt.RenderingHints; java.awt.event.ActionEvent; java.awt.event.ActionListener; java.awt.geom.Ellipse2D; javax.swing.JFrame;

493

import javax.swing.JPanel; import javax.swing.SwingUtilities; import javax.swing.Timer; class Surface extends JPanel implements ActionListener { private Color colors[] = { Color.blue, Color.cyan, Color.green, Color.magenta, Color.orange, Color.pink, Color.red, Color.yellow, Color.lightGray, Color.white }; private private private private private Ellipse2D.Float[] ellipses; double esize[]; float estroke[]; double maxSize = 0; Timer timer;

public Surface() { initSurface(); initEllipses(); initTimer(); } private void initSurface() { setBackground(Color.black); ellipses = new Ellipse2D.Float[25]; esize = new double[ellipses.length]; estroke = new float[ellipses.length]; } private void initEllipses() { int w = 350; int h = 250; maxSize = w / 10; for (int i = 0; i < ellipses.length; i++) { ellipses[i] = new Ellipse2D.Float(); posRandEllipses(i, maxSize * Math.random(), w, h);

} }

private void initTimer() { timer = new Timer(30, this); timer.setInitialDelay(190); timer.start();

private void posRandEllipses(int i, double size, int w, int h) { esize[i] = estroke[i] double x = double y = size; = 1.0f; Math.random() * (w - (maxSize / 2)); Math.random() * (h - (maxSize / 2));

494

ellipses[i].setFrame(x, y, size, size);

private void doStep(int w, int h) { for (int i = 0; i < ellipses.length; i++) { estroke[i] += 0.025f; esize[i]++; if (esize[i] > maxSize) { posRandEllipses(i, 1, w, h); } else { ellipses[i].setFrame(ellipses[i].getX(), ellipses[i].getY(), esize[i], esize[i]); } } } private void drawEllipses(Graphics2D g2) { for (int i = 0; i < ellipses.length; i++) { g2.setColor(colors[i % colors.length]); g2.setStroke(new BasicStroke(estroke[i])); g2.draw(ellipses[i]);

} }

private void doDrawing(Graphics g) { Graphics2D g2 = (Graphics2D) g; RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); rh.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); g2.setRenderingHints(rh); Dimension size = getSize(); doStep(size.width, size.height); drawEllipses(g2); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g);

@Override public void actionPerformed(ActionEvent e) { repaint();

495

public class Bubbles extends JFrame { public Bubbles() { initUI(); } private void initUI() { setTitle("Bubbles"); add(new Surface()); setSize(350, 250); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null);

public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { Bubbles bb = new Bubbles(); bb.setVisible(true); }); } } }

This is bubbles example.


private Color colors[] = { Color.blue, Color.cyan, Color.green, Color.magenta, Color.orange, Color.pink, Color.red, Color.yellow, Color.lightGray, Color.white };

These colors will be used to paint the bubbles.


private void initSurface() { setBackground(Color.black); ellipses = new Ellipse2D.Float[25]; esize = new double[ellipses.length]; estroke = new float[ellipses.length];

The initSurface() method sets a black background for the panel. We create three arrays. An array for the ellipses (a circle is a special case of an ellipse), an array for the size of each of the ellipses and an array for ellipses' strokes. Both size and stroke of the bubble grow during the animation.

496

private void initEllipses() { int w = 350; int h = 250; maxSize = w / 10; for (int i = 0; i < ellipses.length; i++) { ellipses[i] = new Ellipse2D.Float(); posRandEllipses(i, maxSize * Math.random(), w, h);

} }

The ellipses array is filled with ellipse objects. The posRandEllipses() method positions randomly the ellipse objects on the window. The initial sizes of the ellipses are chosen also randomly.
private void initTimer() { timer = new Timer(30, this); timer.setInitialDelay(190); timer.start(); }

A timer object is created ans started. It is used for the animation of the example.
private void posRandEllipses(int i, double size, int w, int h) { esize[i] = size; estroke[i] = 1.0f; double x = Math.random() * (w - (maxSize / 2)); double y = Math.random() * (h - (maxSize / 2)); ellipses[i].setFrame(x, y, size, size); }

The posRandEllipses() method positions randomly the ellipses on the window. The esize and estroke arrays are filled with values. The setFrame() method sets the location and size of the framing rectangle of an ellipse.
private void doStep(int w, int h) { for (int i = 0; i < ellipses.length; i++) { estroke[i] += 0.025f; esize[i]++; if (esize[i] > maxSize) { posRandEllipses(i, 1, w, h); } else { ellipses[i].setFrame(ellipses[i].getX(), ellipses[i].getY(), esize[i], esize[i]);

} } }

497

The animation consists of steps. In each step, we increase the stroke and size values of each ellipse. After the bubble reaches its maximum size, it is reset to the minimum size and randomly repositioned on the panel. Else it is displayed with the increased values.
private void drawEllipses(Graphics2D g2) { for (int i = 0; i < ellipses.length; i++) { g2.setColor(colors[i % colors.length]); g2.setStroke(new BasicStroke(estroke[i])); g2.draw(ellipses[i]);

} }

The drawEllipses() method draws all the ellipses from the array on the panel.
Dimension size = getSize(); doStep(size.width, size.height);

In the doDrawing() method, we compute the size of the panel. If the window is resized, the bubbles are distrubuted randomly over the whole area of the window.
@Override public void actionPerformed(ActionEvent e) { } repaint();

The timer object triggers action events at specified intervals. The repaint() method repaints the panel component.

Figure: Bubbles

Star
The next example shows a rotating and scaling star. Star.java
package com.zetcode;

498

import import import import import import import import import import

java.awt.Graphics; java.awt.Graphics2D; java.awt.RenderingHints; java.awt.event.ActionEvent; java.awt.event.ActionListener; java.awt.geom.GeneralPath; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities; javax.swing.Timer;

class Surface extends JPanel implements ActionListener { private double points[][] = { {0, 85}, {75, 75}, {100, 10}, {125, 75}, {200, 85}, {150, 125}, {160, 190}, {100, 150}, {40, 190}, {50, 125}, {0, 85} }; private private private private Timer timer; double angle = 0; double scale = 1; double delta = 0.01;

public Surface() { } initTimer();

private void initTimer() { timer = new Timer(10, this); timer.start();

private void doDrawing(Graphics g) { int h = getHeight(); int w = getWidth(); Graphics2D g2d = (Graphics2D) g; g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); g2d.translate(w / 2, h / 2); GeneralPath star = new GeneralPath(); star.moveTo(points[0][0], points[0][1]); for (int k = 1; k < points.length; k++) { } star.lineTo(points[k][0], points[k][1]);

star.closePath(); g2d.rotate(angle);

499

g2d.scale(scale, scale); } g2d.fill(star);

@Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g); } @Override public void actionPerformed(ActionEvent e) { if (scale < 0.01) { delta = -delta; } else if (scale > 0.99) { } delta = -delta;

scale += delta; angle += 0.01; } } public class Star extends JFrame { public Star() { } initUI(); repaint();

private void initUI() { setTitle("Star"); add(new Surface()); setSize(420, 250); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { Star st = new Star(); st.setVisible(true);

} } } });

500

In this demo, we have a star. The star rotates and grows and then shrinks.
private double points[][] = { {0, 85}, {75, 75}, {100, 10}, {125, 75}, {200, 85}, {150, 125}, {160, 190}, {100, 150}, {40, 190}, {50, 125}, {0, 85} };

These points are used to draw the star object.


private double angle = 0; private double scale = 1; private double delta = 0.01;

The angle is used when we rotate the star. The scale factor determines the size of the star. Finally, the delta factor is the amount of change of the scale.
if (scale < 0.01) { delta = -delta; } else if (scale > 0.99) { delta = -delta; }

This code controls the shrinking and growing of the star.

Puff
Next we show a puff effect. This effect is common in flash animations or film intros. Text grows gradually on the screen and after some time it slowly disappears. Puff.java
import import import import import import import import import import import import import package com.zetcode; java.awt.AlphaComposite; java.awt.Dimension; java.awt.Font; java.awt.FontMetrics; java.awt.Graphics; java.awt.Graphics2D; java.awt.RenderingHints; java.awt.event.ActionEvent; java.awt.event.ActionListener; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities; javax.swing.Timer;

class Surface extends JPanel implements ActionListener { private Timer timer; private int x = 1; private float alpha = 1;

501

public Surface() { } initTimer();

private void initTimer() { timer = new Timer(8, this); timer.setInitialDelay(190); timer.start(); } private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); rh.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); g2d.setRenderingHints(rh); Font font = new Font("Dialog", Font.PLAIN, x); g2d.setFont(font); FontMetrics fm = g2d.getFontMetrics(); String s = "ZetCode"; Dimension size = getSize(); int w = (int) size.getWidth(); int h = (int) size.getHeight(); int stringWidth = fm.stringWidth(s); g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha)); g2d.drawString(s, (w - stringWidth) / 2, h / 2); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g);

@Override public void actionPerformed(ActionEvent e) { x += 1; if (x > 40) alpha -= 0.01; if (alpha <= 0.01) timer.stop();

502

repaint(); } }

public class Puff extends JFrame { public Puff() { initUI(); } private void initUI() { setTitle("Puff"); add(new Surface()); setSize(400, 300); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { Puff pf = new Puff(); pf.setVisible(true);

} } } });

The example draws a growing text on the window and from some point, the text becomes more and more transparent. Until it is invisible.
Font font = new Font("Dialog", Font.PLAIN, x); g2d.setFont(font);

This is the font that we use for our text.


FontMetrics fm = g2d.getFontMetrics();

Here we get the FontMetrics class. The class stores information about the rendering of a particular font on a particular screen.
int stringWidth = fm.stringWidth(s);

We use the stringWidth() method of the FontMetrics object to get the width of our screen. We need it in order to place the text in the middle of the screen.
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha));

503

Here we set the transparency of the text being drawn.


g2d.drawString(s, (w - stringWidth) / 2, h / 2);

This code line draws the string in the (horizontal) middle of the screen.
if (x > 40) alpha -= 0.01;

After the string is 40 pixels high, it begins fading. In this part of the Java 2D tutorial, we did some visual effects.

Images
In this part of the Java 2D tutorial, we will work with images. Images are really a complex topic. Here we only provide some basics. plays a crucial role when we work with images in Java 2D. It is used to manipulate with images. It is created in memory for efficiency. The process is as follows: we copy the image pixels into the BufferedImage, manipulate the pixels and draw the result on the panel.
BufferedImage

Displaying an Image
In the first example, we display an image on the panel. DisplayImage.java
import import import import import import import import package com.zetcode; java.awt.Dimension; java.awt.Graphics; java.awt.Graphics2D; java.awt.Image; javax.swing.ImageIcon; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities;

class Surface extends JPanel { private Image mshi; public Surface() { loadImage(); setSurfaceSize(); } private void loadImage() { } mshi = new ImageIcon("mushrooms.jpg").getImage();

504

private void setSurfaceSize() { Dimension d = new Dimension(); d.width = mshi.getWidth(null); d.height = mshi.getHeight(null); setPreferredSize(d); } private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; g2d.drawImage(mshi, 1, 1, null); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g);

} }

public class DisplayImage extends JFrame { public DisplayImage() { } initUI();

private void initUI() { setTitle("Mushrooms"); add(new Surface()); pack(); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { DisplayImage ex = new DisplayImage(); ex.setVisible(true); } });

} }

In the example, we display an image on the panel. The window is resized to fit the size of the image.
private void loadImage() { mshi = new ImageIcon("mushrooms.jpg").getImage(); }

505

We use the ImageIcon class to load the image. The image is located in the current working directory.
private void setSurfaceSize() { Dimension d = new Dimension(); d.width = mshi.getWidth(null); d.height = mshi.getHeight(null); setPreferredSize(d); }

We determine the size of the loaded image. With the setPreferredSize() method, we set the preferred size of the Surface panel. The pack() method of the JFrame container will cause the frame to fit the size of its children. In our case the Surface panel. As a consequence, the window will be sized to exactly display the loaded image.
private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; g2d.drawImage(mshi, 1, 1, null);

The image is drawn on the panel using the drawImage() method. The last parameter is the ImageObserver class. It is sometimes used for asynchronous loading. When we do not need asynchronous loading of our images, we can just put null there.
private void initUI() { ... pack(); ... }

The pack() method sizes the contaniner to fit the size of the child panel.

Grayscale image
In computing, a grayscale digital image is an image in which the value of each pixel is a single sample, that is, it carries the full (and only) information about its intensity. Images of this sort are composed exclusively of shades of neutral gray, varying from black at the weakest intensity to white at the strongest. (Wikipedia) In the next example, we will create a grayscale image with Java 2D. GrayScaleImage.java
import import import import import import import import import package com.zetcode; java.awt.Dimension; java.awt.Graphics; java.awt.Graphics2D; java.awt.Image; java.awt.image.BufferedImage; javax.swing.ImageIcon; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities;

506

class Surface extends JPanel { private Image mshi; private BufferedImage bufimg; private Dimension d; public Surface() { loadImage(); determineAndSetSize(); createGrayImage(); } private void determineAndSetSize() { d = new Dimension(); d.width = mshi.getWidth(null); d.height = mshi.getHeight(null); setPreferredSize(d); } private void createGrayImage() { bufimg = new BufferedImage(d.width, d.height, BufferedImage.TYPE_BYTE_GRAY); Graphics2D g2d = bufimg.createGraphics(); g2d.drawImage(mshi, 1, 1, null); g2d.dispose(); } private void loadImage() { } mshi = new ImageIcon("mushrooms.jpg").getImage();

private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; g2d.drawImage(bufimg, null, 2, 2);

@Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g); } }

public class GrayScaleImage extends JFrame { public GrayScaleImage() { initUI(); } public final void initUI() { Surface dpnl = new Surface();

507

add(dpnl); pack(); setTitle("GrayScale Image"); setLocationRelativeTo(null); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { GrayScaleImage ex = new GrayScaleImage(); ex.setVisible(true); } }); } }

Creating a grayscale image is very easy in Java 2D.


bufimg = new BufferedImage(d.width, d.height, BufferedImage.TYPE_BYTE_GRAY);

We create a BufferedImage class of type BufferedImage.TYPE_BYTE_GRAY.


Graphics2D g2d = bufimg.createGraphics(); g2d.drawImage(mshi, 1, 1, null);

Here we draw the mushrooms image into the buffered image.


g2d.dispose();

Graphics objects are disposed by JVM automatically. In some cases, calling the dispose() method will create a more efficient code. According to the documentation, the method should be called only when the graphics objec was created directly from a component or another Graphics object. Graphics objects which are provided as arguments to the paint() and update() methods of components are automatically released by the system when those methods return.
private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; g2d.drawImage(bufimg, null, 2, 2);

The buffered image is drawn on the panel.

Flipped image
Next we are going to flip an image. We are going to filter the image. There is a filter() method, which is transforming images. FlippedImage.java
package com.zetcode;

508

import import import import import import import import import import import

java.awt.Dimension; java.awt.Graphics; java.awt.Graphics2D; java.awt.Image; java.awt.geom.AffineTransform; java.awt.image.AffineTransformOp; java.awt.image.BufferedImage; javax.swing.ImageIcon; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities;

class Surface extends JPanel { private Image mshi; private BufferedImage bufimg; private final int SPACE = 10; public Surface() { loadImage(); createFlippedImage(); setSurfaceSize(); } private void loadImage() { } mshi = new ImageIcon("mushrooms.jpg").getImage();

private void createFlippedImage() { bufimg = new BufferedImage(mshi.getWidth(null), mshi.getHeight(null), BufferedImage.TYPE_INT_RGB); Graphics gb = bufimg.getGraphics(); gb.drawImage(mshi, 0, 0, null); gb.dispose(); AffineTransform tx = AffineTransform.getScaleInstance(-1, 1); tx.translate(-mshi.getWidth(null), 0); AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_NEAREST_NEIGHBOR); bufimg = op.filter(bufimg, null);

private void setSurfaceSize() { int w = bufimg.getWidth(); int h = bufimg.getHeight(); Dimension d = new Dimension(3*SPACE+2*w, h+2*SPACE); } setPreferredSize(d);

private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g;

509

g2d.drawImage(mshi, SPACE, SPACE, null); g2d.drawImage(bufimg, null, 2*SPACE+bufimg.getWidth(), SPACE); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g);

} }

public class FlippedImage extends JFrame { public FlippedImage() { } initUI();

private void initUI() { setTitle("Flipped image"); add(new Surface()); pack(); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { FlippedImage fi = new FlippedImage(); fi.setVisible(true); } });

} }

In our code example, we horizontally flip an image.


AffineTransform tx = AffineTransform.getScaleInstance(-1, 1); tx.translate(-castle.getWidth(null), 0);

Flipping an image means scaling it and translating it. So we do an AffineTransform operation.


AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_NEAREST_NEIGHBOR); bufferedImage = op.filter(bufferedImage, null)

This is one of the filtering operations available. This could be also done by pixel manipulation. But Java 2D provides high level classes that make it easier to manipulate images. In our case, the AffineTransformOp class performs scaling and translation on the image pixels.

510

private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; g2d.drawImage(mshi, SPACE, SPACE, null); g2d.drawImage(bufimg, null, 2*SPACE+bufimg.getWidth(), SPACE); }

Both images are painted on the panel.


private void setSurfaceSize() { int w = bufimg.getWidth(); int h = bufimg.getHeight(); Dimension d = new Dimension(3*SPACE+2*w, h+2*SPACE); } setPreferredSize(d);

We set the preferred size for the panel. We calculate the size so that we can place two images on the panel and put some space between them and the images and the window borders.

Blurred image
The next code example will blur an image. A blur means an unfocused image. To blur an image, we use a convolution operation. It is a mathematical operation which is also used in edge detection or noise elimination. Blur operations can be used in various graphical effects. For example creating speed illusion, showing an unfocused vision of a human being etc. The blur filter operation replaces each pixel in image with an average of the pixel and its neighbours. Convolutions are per-pixel operations. The same arithmetic is repeated for every pixel in the image. A kernel can be thought of as a two-dimensional grid of numbers that passes over each pixel of an image in sequence, performing calculations along the way. Since images can also be thought of as two-dimensional grids of numbers, applying a kernel to an image can be visualized as a small grid (the kernel) moving across a substantially larger grid (the image). (developer.apple.com) BlurredImage.java
package com.zetcode; import import import import import import import import import import import import import import import java.awt.Dimension; java.awt.Graphics; java.awt.Graphics2D; java.awt.image.BufferedImage; java.awt.image.BufferedImageOp; java.awt.image.ConvolveOp; java.awt.image.Kernel; java.io.File; java.io.IOException; java.util.logging.Level; java.util.logging.Logger; javax.imageio.ImageIO; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities;

511

class Surface extends JPanel { private BufferedImage mshi; private BufferedImage databuf; public Surface() { loadImage(); createBlurredImage(); setSurfaceSize();

private void loadImage() { try { mshi = ImageIO.read(new File("mushrooms.jpg")); } catch (IOException ex) { null, ex); } } Logger.getLogger(Surface.class.getName()).log(Level.SEVERE,

private void createBlurredImage() { databuf = new BufferedImage(mshi.getWidth(null), mshi.getHeight(null), BufferedImage.TYPE_INT_BGR); Graphics g = databuf.getGraphics(); g.drawImage(mshi, 455, 255, null); float[] 1 / 1 / 1 / }; blurKernel = { 9f, 1 / 9f, 1 / 9f, 9f, 1 / 9f, 1 / 9f, 9f, 1 / 9f, 1 / 9f

BufferedImageOp blur = new ConvolveOp(new Kernel(3, 3, blurKernel)); mshi = blur.filter(mshi, new BufferedImage(mshi.getWidth(), mshi.getHeight(), mshi.getType())); g.dispose(); } private void setSurfaceSize() { Dimension d = new Dimension(); d.width = mshi.getWidth(null); d.height = mshi.getHeight(null); setPreferredSize(d);

private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; g2d.drawImage(mshi, null, 3, 3);

512

@Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g);

} }

public class BlurredImage extends JFrame { public BlurredImage() { setTitle("Blurred image"); add(new Surface()); pack(); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null);

public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { BlurredImage bi = new BlurredImage(); bi.setVisible(true); }); } } }

In the code example, we load an image from the disk, perform a blur operation on the image and display the result on the window.
private void loadImage() { try { mshi = ImageIO.read(new File("mushrooms.jpg")); } catch (IOException ex) { Logger.getLogger(Surface.class.getName()).log(Level.SEVERE, null, ex); } }

Here we use an alternative method of loading an image in Java 2D. We use the read() method of the ImageIO class. This way we directly have a BufferedImage class.
float[] 1 / 1 / 1 / }; blurKernel = { 9f, 1 / 9f, 1 / 9f, 9f, 1 / 9f, 1 / 9f, 9f, 1 / 9f, 1 / 9f

513

This matrix is called a kernel. The values are weights that are applied to the neighbouring values of the pixel being changed.
BufferedImageOp blur = new ConvolveOp(new Kernel(3, 3, blurKernel)); mshi = blur.filter(mshi, new BufferedImage(mshi.getWidth(), mshi.getHeight(), mshi.getType()));

Here we apply the blur filter to the image.

Reflection
In the next example we show a reflected image. This effect makes an illusion as if the image was reflected in water. The following code example was inspired by the code from jhlabs.com. Reflection.java
import import import import import import import import import import import import import import package com.zetcode; java.awt.AlphaComposite; java.awt.Color; java.awt.Dimension; java.awt.GradientPaint; java.awt.Graphics; java.awt.Graphics2D; java.awt.image.BufferedImage; java.io.File; java.util.logging.Level; java.util.logging.Logger; javax.imageio.ImageIO; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities;

class Surface extends JPanel { private private private private private BufferedImage image; BufferedImage refImage; int img_w; int img_h; final int SPACE = 30;

public Surface() { loadImage(); getImageSize(); createReflectedImage();

private void loadImage() { try { image = ImageIO.read(new File("rotunda.jpg")); } catch (Exception ex) { null, ex); } Logger.getLogger(Surface.class.getName()).log(Level.SEVERE,

514

} private void getImageSize() { img_w = image.getWidth(); img_h = image.getHeight(); } private void createReflectedImage() { float opacity = 0.4f; float fadeHeight = 0.3f; refImage = new BufferedImage(img_w, img_h, BufferedImage.TYPE_INT_ARGB); Graphics2D rg = refImage.createGraphics(); rg.drawImage(image, 0, 0, null); rg.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_IN)); rg.setPaint(new GradientPaint(0, img_h * fadeHeight, new Color(0.0f, 0.0f, 0.0f, 0.0f), 0, img_h, new Color(0.0f, 0.0f, 0.0f, opacity))); rg.fillRect(0, 0, img_w, img_h); rg.dispose(); } private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; int win_w = getWidth(); int win_h = getHeight(); int gap = 20; g2d.setPaint(new GradientPaint(0, 0, Color.black, 0, win_h, Color.darkGray)); g2d.fillRect(0, 0, win_w, win_h); g2d.translate((win_w - img_w) / 2, win_h / 2 - img_h); g2d.drawImage(image, 0, 0, null); g2d.translate(0, 2 * img_h + gap); g2d.scale(1, -1); g2d.drawImage(refImage, 0, 0, null); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g);

@Override public Dimension getPreferredSize() { } } public class Reflection extends JFrame { return new Dimension(img_w + 2 * SPACE, 2 * img_h + 3 * SPACE);

515

public Reflection() { initUI(); } private void initUI() { setTitle("Reflection"); add(new Surface()); pack(); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null);

public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { Reflection bi = new Reflection(); bi.setVisible(true); } }); } }

In the example, we create an illusion of a reflected image.


refImage = new BufferedImage(img_w, img_h, BufferedImage.TYPE_INT_ARGB); Graphics2D rg = refImage.createGraphics(); rg.drawImage(image, 0, 0, null);

A copy of a loaded image is created.


rg.setComposite(AlphaComposite.getInstance(AlphaComposite.DST_IN)); rg.setPaint(new GradientPaint(0, img_h * fadeHeight, new Color(0.0f, 0.0f, 0.0f, 0.0f), 0, img_h, new Color(0.0f, 0.0f, 0.0f, opacity))); rg.fillRect(0, 0, img_w, img_h);

This is the most important part of the code. We make the second image transparent. But the transparency is not constant. The image gradually fades out. This is achieved with the GradientPaint.
g2d.setPaint(new GradientPaint(0, 0, Color.black, 0, win_h, Color.darkGray)); g2d.fillRect(0, 0, win_w, win_h);

The background of the window is filled with a gradient paint. The paint is a smooth blending from black to dark gray.
g2d.translate((win_w - img_w) / 2, win_h / 2 - img_h); g2d.drawImage(image, 0, 0, null);

516

The normal image is moved to the center of the window and drawn.
g2d.translate(0, 2 * imageHeight + gap); g2d.scale(1, -1);

This code flips the image and translates it below the original image. The translation operation is necessary, because the scaling operation makes the image upside down and translates the image up. To understand what happens, simply take a photograph and place it on the table and flip it.
g2d.drawImage(refImage, 0, 0, null);

The reflected image is drawn.


@Override public Dimension getPreferredSize() { } return new Dimension(img_w + 2 * SPACE, 2 * img_h + 3 * SPACE);

Another way to set a preferred size for a component is to override the getPreferredSize() method.

Figure: Reflection In this part of the Java2D tutorial we have worked with images.

Text and Fonts


In this part of the Java 2D tutorial, we will work with texts and fonts.

517

Text and fonts


Rendering text is another complicated topic. It would easily fill a specialized book. Here we only provide some basic examples. A character is a symbol that represents an item such as a letter, a digit or a punctuation. A glyph is a shape used to render a character or a sequence of characters. In the Latin alphabet a glyph typically represents one character. In other writing systems, a character may be composed of several glyphs. Like , , , . These are latin characters with accents. There are basically two types of fonts. Physical and logical. Physical fonts are the actual font libraries. Logical fonts are the five font families defined by the Java platform. Serif, SansSerif, Monospaced, Dialog, and DialogInput. Logical fonts are not actual font libraries. Logical font names are mapped to physical fonts by the Java runtime environment. Text can be drawn on the window using various fonts. A font is a set of type characters of a particular typeface design and size. Various typefaces include Helvetica, Georgia, Times or Verdana. A collection of glyphs with a particular style form a font face. A collection of font faces forms a font family. (docs.oracle.com, answers.com)

System fonts
This console example will print all available fonts on your platform. AllFonts.java
package com.zetcode; import java.awt.Font; import java.awt.GraphicsEnvironment; public class AllFonts { public static void main(String[] args) { GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); Font[] fonts = ge.getAllFonts(); for (int i = 0; i < fonts.length; i++) { System.out.print(fonts[i].getFontName() + " : "); System.out.println(fonts[i].getFamily());

} } }

We print the name and family of each of the installed fonts.


GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();

There are objects that are typical for a particular platform. Fonts are among these objects. The collection of fonts on a Unix, OS X and Windows platform differ. The

518

class describes the collection of GraphicsDevice objects and Font objects available on a particular platform.
GraphicsEnvironment Font[] fonts = ge.getAllFonts();

The getAllFonts() returns all fonts available in the GraphicsEnvironment.


System.out.print(fonts[i].getFontName() + " : "); System.out.println(fonts[i].getFamily());

The font name and the font family is printed to the terminal.
... URW Bookman L Demi Bold : URW Bookman L URW Bookman L Demi Bold Italic : URW Bookman L URW Bookman L Light : URW Bookman L URW Bookman L Light Italic : URW Bookman L URW Chancery L Medium Italic : URW Chancery L URW Gothic L Book : URW Gothic L URW Gothic L Book Oblique : URW Gothic L URW Gothic L Demi : URW Gothic L URW Gothic L Demi Oblique : URW Gothic L URW Palladio L Bold : URW Palladio L URW Palladio L Bold Italic : URW Palladio L URW Palladio L Italic : URW Palladio L URW Palladio L Roman : URW Palladio L Ubuntu : Ubuntu Ubuntu Bold : Ubuntu Ubuntu Bold Italic : Ubuntu Ubuntu Condensed : Ubuntu Condensed ...

This is an excerpt of all fonts on Ubuntu Linux.

Soulmate
In the next example, we will display some lyrics on the panel. Soulmate.java
package com.zetcode; import import import import import import import java.awt.Font; java.awt.Graphics; java.awt.Graphics2D; java.awt.RenderingHints; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities;

class Surface extends JPanel { private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; RenderingHints rh =

519

new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); rh.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); g2d.setRenderingHints(rh); g2d.setFont(new Font("Purisa", Font.PLAIN, 13)); g2d.drawString("Most relationships seem so transitory", 20, 30); g2d.drawString("They're all good but not the permanent one", 20, 60); 120); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g); g2d.drawString("Who doesn't long for someone to hold", 20, 90); g2d.drawString("Who knows how to love you without being told", 20, g2d.drawString("Somebody tell me why I'm on my own", 20, 150); g2d.drawString("If there's a soulmate for everyone", 20, 180);

} }

public class Soulmate extends JFrame { public Soulmate() { } initUI();

private void initUI() { setTitle("Soulmate"); add(new Surface()); setSize(420, 250); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { Soulmate so = new Soulmate(); so.setVisible(true);

} } } });

In this example, we draw text on the panel. We choose a specific font type. 520

g2d.setFont(new Font("Purisa", Font.PLAIN, 13));

Here we set a Purisa font type.


g2d.drawString("Most relationships seem so transitory", 20, 30);

The drawString() method renders the text using the current text attribute state in the Graphics2D context.

Figure: Soulmate

Unicode
The next example demonstrates, how to display unicode text. Note that in real world applications, the text would be placed outside of the code in a separate resource.
$ cat fyodor 30 (11 ) 1821 . 7 . , , . ...

We have a file called fyodor where we have the text in azbuka.


$ native2ascii fyodor unicode

We use the tool called native2ascii, which can be found in the bin directory of the jdk. It converts a file with native-encoded characters to one with Unicode-encoded characters. The first parameter is the input file. The second parameter is the output file.
$ cat unicode \u0424\u0451\u0434\u043e\u0440 \u041c\u0438\u0445\u0430\u0439\u043b\u043e\u0432\u0438\u0447 ...

The same text in unicode encoding.

521

Unicode.java
package com.zetcode; import import import import import import import java.awt.Font; java.awt.Graphics; java.awt.Graphics2D; java.awt.RenderingHints; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities;

class Surface extends JPanel { String sent1 = "\u0424\u0451\u0434\u043e\u0440 \u041c\u0438\u0445" + "\u0430\u0439\u043b\u043e\u0432\u0438\u0447 \u0414\u043e\u0441\u0442" + "\u043e\u0435\u0432\u0441\u043a\u0438\u0439 \u0440\u043e\u0434\u0438" + "\u043b\u0441\u044f 30 \u043e\u043a\u0442\u044f\u0431\u0440\u044f " + "(11 \u043d\u043e\u044f\u0431\u0440\u044f) 1821 \u0433\u043e\u0434" + "\u0430 \u0432 \u041c\u043e\u0441\u043a\u0432\u0435. "; String sent2 = "\u0411\u044b\u043b \u0432\u0442\u043e\u0440\u044b\u043c " + "\u0438\u0437 7 \u0434\u0435\u0442\u0435\u0439. \u041e\u0442\u0435\u0446, " + "\u041c\u0438\u0445\u0430\u0438\u043b \u0410\u043d\u0434\u0440\u0435\u0435" + "\u0432\u0438\u0447, \u0440\u0430\u0431\u043e\u0442\u0430\u043b \u0432 " + "\u0433\u043e\u0441\u043f\u0438\u0442\u0430\u043b\u0435 \u0434\u043b\u044f " + "\u0431\u0435\u0434\u043d\u044b\u0445."; String sent3 = "\u041c\u0430\u0442\u044c, \u041c\u0430\u0440\u0438\u044f " + "\u0424\u0451\u0434\u043e\u0440\u043e\u0432\u043d\u0430 " + "(\u0432 \u0434\u0435\u0432\u0438\u0447\u0435\u0441\u0442\u0432\u0435 " + "\u041d\u0435\u0447\u0430\u0435\u0432\u0430), \u043f\u0440\u043e\u0438\u0441" + "\u0445\u043e\u0434\u0438\u043b\u0430 \u0438\u0437 \u043a\u0443\u043f\u0435" + "\u0447\u0435\u0441\u043a\u043e\u0433\u043e \u0440\u043e\u0434\u0430."; String sent4 = "\u041a\u043e\u0433\u0434\u0430 \u0414\u043e\u0441\u0442" + "\u043e\u0435\u0432\u0441\u043a\u043e\u043c\u0443 \u0431\u044b\u043b\u043e 15 " + "\u043b\u0435\u0442, \u0435\u0433\u043e \u043c\u0430\u0442\u044c " + "\u0443\u043c\u0435\u0440\u043b\u0430 \u043e\u0442 \u0447\u0430\u0445\u043e" + "\u0442\u043a\u0438, \u0438 \u043e\u0442\u0435\u0446 \u043e\u0442\u043f\u0440" + "\u0430\u0432\u0438\u043b"; String sent5 = "\u0441\u0442\u0430\u0440\u0448\u0438\u0445 \u0441\u044b" + "\u043d\u043e\u0432\u0435\u0439, \u0424\u0451\u0434\u043e\u0440\u0430 \u0438 " + "\u041c\u0438\u0445\u0430\u0438\u043b\u0430 (\u0432\u043f\u043e\u0441\u043b" + "\u0435\u0434\u0441\u0442\u0432\u0438\u0438 \u0442\u0430\u043a\u0436\u0435 " +

522

"\u0441\u0442\u0430\u0432\u0448\u0435\u0433\u043e \u043f\u0438\u0441\u0430" + "\u0442\u0435\u043b\u0435\u043c),"; String sent6 = "\u0432 \u043f\u0430\u043d\u0441\u0438\u043e\u043d \u041a. " + "\u0424. \u041a\u043e\u0441\u0442\u043e\u043c\u0430\u0440\u043e\u0432\u0430 " + "\u0432 \u041f\u0435\u0442\u0435\u0440\u0431\u0443\u0440\u0433\u0435."; private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); rh.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); g2d.setRenderingHints(rh); g2d.setFont(new Font("Franklin Gothic Medium", Font.PLAIN, 11)); g2d.drawString(sent1, g2d.drawString(sent2, g2d.drawString(sent3, g2d.drawString(sent4, g2d.drawString(sent5, g2d.drawString(sent6, 20, 20, 20, 20, 20, 20, 30); 55); 80); 120); 145); 170);

@Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g); } }

public class Unicode extends JFrame { public Unicode() { initUI(); } private void initUI() { setTitle("Unicode"); add(new Surface()); setSize(550, 230); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null);

public static void main(String[] args) {

523

SwingUtilities.invokeLater(new Runnable() { @Override public void run() { Unicode uni = new Unicode(); uni.setVisible(true);

} } } });

Note, that the text would go outside source code in real world programs. Here the text is kept inside the source for simplicity reasons.
String sent1 = "\u0424\u0451\u0434\u043e\u0440 \u041c\u0438\u0445" + ...

This is the first unicode line.


g2d.drawString(sent1, 20, 30);

And the sentence is drawn.

Figure: Unicode

Shadowed text
In the next example, we will create a shadowed text. The effect is created by drawing the same text two times. One text serves as the main text, the other one as a shadow. The shadowed text is moved a bit, colored in light gray and blurred. ShadowedText.java
package com.zetcode; import import import import import import import java.awt.Color; java.awt.Font; java.awt.Graphics2D; java.awt.RenderingHints; java.awt.font.TextLayout; java.awt.image.BufferedImage; java.awt.image.ConvolveOp;

524

import import import import import

java.awt.image.Kernel; javax.swing.ImageIcon; javax.swing.JFrame; javax.swing.JLabel; javax.swing.SwingUtilities;

public class ShadowedText extends JFrame { private int width = 490; private int height = 150; private String text = "Disciplin ist macht"; private TextLayout textLayout; public ShadowedText() { initUI(); BufferedImage image = createImage(); add(new JLabel(new ImageIcon(image)));

private void initUI() { setTitle("Shadowed Text"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize(width, height); setLocationRelativeTo(null);

private void setRenderingHints(Graphics2D g) { g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);

private BufferedImage createImage() int x = 10; int y = 100;

Font font = new Font("Georgia", Font.ITALIC, 50); BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Graphics2D g1 = image.createGraphics(); setRenderingHints(g1); textLayout = new TextLayout(text, font, g1.getFontRenderContext()); g1.setPaint(Color.WHITE); g1.fillRect(0, 0, width, height); g1.setPaint(new Color(150, 150, 150)); textLayout.draw(g1, x+3, y+3); g1.dispose(); float[] kernel = { 1f / 9f, 1f / 9f, 1f / 9f,

525

1f / 9f, 1f / 9f, 1f / 9f, 1f / 9f, 1f / 9f, 1f / 9f }; ConvolveOp op = new ConvolveOp(new Kernel(3, 3, kernel), ConvolveOp.EDGE_NO_OP, null); BufferedImage image2 = op.filter(image, null); Graphics2D g2 = image2.createGraphics(); setRenderingHints(g2); g2.setPaint(Color.BLACK); textLayout.draw(g2, x, y); return image2; } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { ShadowedText st = new ShadowedText(); st.setVisible(true);

} } } });

This time, we don't draw in the paintComponent() method. We create a image that we put inside a JLabel.
Font font = new Font("Georgia", Font.ITALIC, 50);

Our font is Georgia, italic, of 50 points size.


BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);

We create the first buffered image.


Graphics2D g1 = image.createGraphics();

From the buffered image a Graphics2D object is created. It will be used to draw into the buffered image.
textLayout = new TextLayout(text, font, g1.getFontRenderContext());

We create a TextLayout class. TextLayout is an immutable graphical representation of styled character data. It is used for advanced manipulation with the text and font.
textLayout.draw(g1, x+3, y+3);

The code renders this TextLayout at the specified location in the specified Graphics2D context. The origin of the layout is placed at x, y. 526

float[] kernel = { 1f / 9f, 1f / 9f, 1f / 9f, 1f / 9f, 1f / 9f, 1f / 9f, 1f / 9f, 1f / 9f, 1f / 9f }; ConvolveOp op = new ConvolveOp(new Kernel(3, 3, kernel), ConvolveOp.EDGE_NO_OP, null);

This creates the blur effect.


BufferedImage image2 = op.filter(image, null);

We apply the blur effect on the first image and copy the outcome to the second buffered image.
textLayout.draw(g2, x, y);

At this point, we have both the original text and the blurred text in the TaxLayout object.

Figure: Shadowed text

Text attributes
When we draw text, we can control its various attributes. We can modify text rendering with Font class, TextAttributes, AttributeString classes. The Font class represents fonts, which are used to render text. The TextAttribute class defines attribute keys and attribute values used for text rendering. Finally, the AttributedString class holds text and related attribute information. TextAttributes.java
package com.zetcode; import import import import import import import import import import java.awt.Color; java.awt.Font; java.awt.Graphics; java.awt.Graphics2D; java.awt.RenderingHints; java.awt.font.TextAttribute; java.text.AttributedString; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities;

class Surface extends JPanel {

527

private String words = "Valour fate kinship darkness"; private String java = "Java TM"; private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); Font font = new Font("Serif", Font.PLAIN, 40); AttributedString as1 = new AttributedString(words); as1.addAttribute(TextAttribute.FONT, font); as1.addAttribute(TextAttribute.FOREGROUND, Color.red, 0, 6); as1.addAttribute(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON, 7, 11); as1.addAttribute(TextAttribute.BACKGROUND, Color.LIGHT_GRAY, 12, 19); as1.addAttribute(TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON, 20, 28); g2d.drawString(as1.getIterator(), 15, 60); AttributedString as2 = new AttributedString(java); as2.addAttribute(TextAttribute.SIZE, 40); as2.addAttribute(TextAttribute.SUPERSCRIPT, TextAttribute.SUPERSCRIPT_SUPER, 5, 7); g2d.drawString(as2.getIterator(), 130, 125); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g);

} }

public class TextAttributes extends JFrame { public TextAttributes() { } initUI();

private void initUI() { setTitle("TextAttributes"); add(new Surface()); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize(620, 190); setLocationRelativeTo(null); } public static void main(String[] args) {

528

SwingUtilities.invokeLater(new Runnable() { @Override public void run() { TextAttributes ta = new TextAttributes(); ta.setVisible(true); }); } } }

In our example, we demonstrate various text rendering possibilities.


AttributedString as1 = new AttributedString(words);

We create an AttributeString out of the words string.


as1.addAttribute(TextAttribute.FOREGROUND, Color.red, 0, 6);

Here we add a new attribute to the AttributeString class. This attribute specifies that the first seven characters will be rendered in red color.
g2d.drawString(as1.getIterator(), 15, 60);

The first text is drawn on the panel. Because at the moment we work with a AttributeString class and not directly with the string, we use an overloaded drawString() method, which takes a AttributedCharacterIterator instance as its first parameter.

Figure: Text Attributes

Rotated Text
In the last example, we will show a rotated text on the panel. To rotate a text, we will do rotation and translation operations. As we already stated, a glyph is a shape used to render a character. So in our code example, we need to get all glyphs of our text, get their measurements and manipulate them one by one. We will work with several important classes. The FontRenderContext class is a container for the information needed to correctly measure text. The GlyphVector object is a collection

529

of glyphs containing geometric information for the placement of each glyph in a transformed coordinate space. RotatedText.java
package com.zetcode; import import import import import import import import import import import import java.awt.Font; java.awt.Graphics; java.awt.Graphics2D; java.awt.RenderingHints; java.awt.Shape; java.awt.font.FontRenderContext; java.awt.font.GlyphVector; java.awt.geom.AffineTransform; java.awt.geom.Point2D; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities;

class Surface extends JPanel { private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); String s = "ZetCode, tutorials for programmers"; Font font = new Font("Courier", Font.PLAIN, 12); g2d.translate(20, 20); FontRenderContext frc = g2d.getFontRenderContext(); GlyphVector gv = font.createGlyphVector(frc, s); int length = gv.getNumGlyphs(); for (int i = 0; i < length; i++) { Point2D p = gv.getGlyphPosition(i); double theta = (double) i / (double) (length - 1) * Math.PI / 3; AffineTransform at = AffineTransform.getTranslateInstance(p.getX(), p.getY()); at.rotate(theta); Shape glyph = gv.getGlyphOutline(i); Shape transformedGlyph = at.createTransformedShape(glyph); g2d.fill(transformedGlyph);

} }

@Override public void paintComponent(Graphics g) { super.paintComponent(g);

530

} }

doDrawing(g);

public class RotatedText extends JFrame { public RotatedText() { } initUI();

private void initUI() { setTitle("TextAttributes"); add(new Surface()); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize(400, 300); setLocationRelativeTo(null); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { RotatedText ta = new RotatedText(); ta.setVisible(true);

} } } });

This is a rotated text example.


String s = "ZetCode, tutorials for programmers";

This is our text. Because the text is in Latin1 encoding, glyphs correspond to characters in a one-to-one manner.
GlyphVector gv = font.createGlyphVector(frc, s);

Here we create a GlyphVector object. A GlyphVector is a collection of glyphs and their positions.
int length = gv.getNumGlyphs();

Here we get the number of glyphs of our text. If we print the number to the console, we get 34. So in our case, each character is one glyph.
Point2D p = gv.getGlyphPosition(i);

We iterate through the vector of glyphs. For each glyph we calculate its position.

531

double theta = (double) i / (double) (length - 1) * Math.PI / 3;

We calculate the degree, by which the glyph is going to be rotated.


AffineTransform at = AffineTransform.getTranslateInstance(p.getX(), p.getY()); at.rotate(theta);

We do an affine rotate transformation.


Shape glyph = gv.getGlyphOutline(i); Shape transformedGlyph = at.createTransformedShape(glyph);

The getGlyphOutline() method returns a Shape of the specified glyph. The createTransformedShape() method returns a new Shape object modified by our affine transform operation.
g2d.fill(transformedGlyph);

Finally, we paint the glyph.

Figure: Rotated text In this part of the Java 2D tutorial, we covered Text and Fonts.

Hit testing, moving objects


In this part of the Java 2D programming tutorial, we will first talk about hit testing. We will show, how to determine if we have clicked inside a shape on a panel. In the second example, we will create two shapes that we can move with a mouse on the panel and resize them with a mouse wheel. In the last example, we will be resizing a rectangle with two controlling points.

532

Hit testing
Hit testing is determining if we have clicked inside a Shape with a mouse pointer. Each Shape has a contains() method. The method tests if a specified Point2D is inside the boundary of a Shape. HitTesting.java
package com.zetcode; import import import import import import import import import import import import import import java.awt.AlphaComposite; java.awt.Color; java.awt.Graphics; java.awt.Graphics2D; java.awt.RenderingHints; java.awt.event.MouseAdapter; java.awt.event.MouseEvent; java.awt.geom.Ellipse2D; java.awt.geom.Rectangle2D; java.util.logging.Level; java.util.logging.Logger; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities;

class Surface extends JPanel { private private private private Rectangle2D rect; Ellipse2D ellipse; float alpha_rectangle; float alpha_ellipse;

public Surface() { } initSurface();

private void initSurface() { this.addMouseListener(new HitTestAdapter()); rect = new Rectangle2D.Float(20f, 20f, 80f, 50f); ellipse = new Ellipse2D.Float(120f, 30f, 60f, 60f); alpha_rectangle = 1f; alpha_ellipse = 1f; } private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; g2d.setColor(new Color(50, 50, 50)); RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); rh.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);

533

g2d.setRenderingHints(rh); g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha_rectangle)); g2d.fill(rect); g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha_ellipse)); g2d.fill(ellipse); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g);

class RectRunnable implements Runnable { private Thread runner; public RectRunnable() { initThread(); } private void initThread() { runner = new Thread(this); runner.start(); } @Override public void run() { while (alpha_rectangle >= 0) { repaint(); alpha_rectangle += -0.01f; if (alpha_rectangle < 0) { alpha_rectangle = 0; } try { Thread.sleep(50); } catch (InterruptedException ex) { Logger.getLogger(Surface.class.getName()).log(Level.SEVERE, null, ex); } } } }

534

class HitTestAdapter extends MouseAdapter implements Runnable { private RectRunnable rectAnimator; private Thread ellipseAnimator; @Override public void mousePressed(MouseEvent e) { int x = e.getX(); int y = e.getY(); if (rect.contains(x, y)) { } rectAnimator = new RectRunnable();

if (ellipse.contains(x, y)) { ellipseAnimator = new Thread(this); ellipseAnimator.start();

} }

@Override public void run() { while (alpha_ellipse >= 0) { repaint(); alpha_ellipse += -0.01f; if (alpha_ellipse < 0) { } alpha_ellipse = 0;

try { Thread.sleep(50); } catch (InterruptedException ex) { Logger.getLogger(Surface.class.getName()).log(Level.SEVERE, null, ex); } } } } } public class HitTesting extends JFrame { public HitTesting() { setTitle("Hit testing"); add(new Surface()); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize(250, 150);

535

setLocationRelativeTo(null);

public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { HitTesting ht = new HitTesting(); ht.setVisible(true); }); } } }

In our example, we have two Shapes. A rectangle and a circle. By clicking on them they gradually begin to fade away. In this example, we work with Threads.
private Rectangle2D rect; private Ellipse2D ellipse;

We work with a rectangle and an ellipse.


private float alpha_rectangle; private float alpha_ellipse;

These two variables control the transparency of the two geometrical objects.
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha_rectangle)); g2d.fill(rect);

Inside the doDrawing() method, we set the transparency of the rectangle. The alpha_rectangle is computed inside a dedicated Thread. The HitTestAdapter class is responsible for handling of mouse events. It does implement the Runnable interface, which means that it also creates the first thread.
if (ellipse.contains(x, y)) { ellipseAnimator = new Thread(this); ellipseAnimator.start();

If we press inside the ellipse a new Thread is created. The thread calls the run() method. In our case, it is the run() method of the class itself (HitTestAdapter).
if (rect.contains(x, y)) { } rectAnimator = new RectRunnable();

536

For the rectangle, we have a separate inner class. The RectRunnable class. This class creates its own thread in the constructor.
public void run() { while (alpha_ellipse >= 0) { repaint(); alpha_ellipse += -0.01f; ...

Note that the run() method is only called once. To actually do something, we have to implement a while loop. The while loop repaints the panel and decrements the alpha_ellipse variable.

Figure: Hit testing

Moving and Scaling


In the next section we will learn how to move and scale graphical objects with a mouse on the panel. It can be used to move and scale charts, diagrams or other various objects in our application. MovingScaling.java
package com.zetcode; import import import import import import import import import import import import import import java.awt.Color; java.awt.Font; java.awt.Graphics; java.awt.Graphics2D; java.awt.RenderingHints; java.awt.event.MouseAdapter; java.awt.event.MouseEvent; java.awt.event.MouseWheelEvent; java.awt.event.MouseWheelListener; java.awt.geom.Ellipse2D; java.awt.geom.Rectangle2D; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities;

class Surface extends JPanel { private ZRectangle zrect; private ZEllipse zell;

537

public Surface() { initUI(); } private void initUI() { MovingAdapter ma = new MovingAdapter(); addMouseMotionListener(ma); addMouseListener(ma); addMouseWheelListener(new ScaleHandler()); zrect = new ZRectangle(50, 50, 50, 50); zell = new ZEllipse(150, 70, 80, 80); setDoubleBuffered(true); } private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g; Font font = new Font("Serif", Font.BOLD, 40); g2d.setFont(font); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); g2d.setColor(new Color(0, 0, 200)); g2d.fill(zrect); g2d.setColor(new Color(0, 200, 0)); g2d.fill(zell); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g);

class ZEllipse extends Ellipse2D.Float { public ZEllipse(float x, float y, float width, float height) { setFrame(x, y, width, height); } public boolean isHit(float x, float y) { if (getBounds2D().contains(x, y)) { return true; } else { return false; }

538

} public void addX(float x) { } this.x += x;

public void addY(float y) { this.y += y; } public void addWidth(float w) { } this.width += w;

public void addHeight(float h) { this.height += h; } }

class ZRectangle extends Rectangle2D.Float { public ZRectangle(float x, float y, float width, float height) { setRect(x, y, width, height); } public boolean isHit(float x, float y) { if (getBounds2D().contains(x, y)) { return true; } else { return false; } }

public void addX(float x) { this.x += x; } public void addY(float y) { } this.y += y;

public void addWidth(float w) { this.width += w; } public void addHeight(float h) { } } this.height += h;

539

class MovingAdapter extends MouseAdapter { private int x; private int y; @Override public void mousePressed(MouseEvent e) { x = e.getX(); y = e.getY();

@Override public void mouseDragged(MouseEvent e) { int dx = e.getX() - x; int dy = e.getY() - y; if (zrect.isHit(x, y)) { zrect.addX(dx); zrect.addY(dy); repaint(); } if (zell.isHit(x, y)) { zell.addX(dx); zell.addY(dy); repaint();

} }

x += dx; y += dy;

class ScaleHandler implements MouseWheelListener { @Override public void mouseWheelMoved(MouseWheelEvent e) { int x = e.getX(); int y = e.getY(); if (e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL) { if (zrect.isHit(x, y)) { float amount = e.getWheelRotation() * 5f; zrect.addWidth(amount); zrect.addHeight(amount); repaint(); } if (zell.isHit(x, y)) { float amount = e.getWheelRotation() * 5f; zell.addWidth(amount); zell.addHeight(amount);

540

} } } } }

repaint();

public class MovingScaling extends JFrame { public MovingScaling() { initUI(); } private void initUI() { setTitle("Moving and scaling"); add(new Surface()); setSize(300, 300); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null);

public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { MovingScaling ms = new MovingScaling(); ms.setVisible(true); } }); } }

In our code example, we have two graphical objects. A rectangle and a circle. We can move both by clicking on them and dragging them. We can also scale them up or down by positioning the mouse cursor over the objects and moving the mouse wheel.
private ZRectangle zrect; private ZEllipse zell;

As we have already mentioned, we have a rectangle and an ellipse on our panel. Both classes extend the functionality of a built-in classes from Java AWT package.
addMouseMotionListener(ma); addMouseListener(ma); addMouseWheelListener(new ScaleHandler());

We register three listeners. These listeners will capture mouse press, mouse drag and mouse wheel events.
class ZEllipse extends Ellipse2D.Float { public ZEllipse(float x, float y, float width, float height) {

541

setFrame(x, y, width, height);

public boolean isHit(float x, float y) { if (getBounds2D().contains(x, y)) { return true; } else { } } ... } return false;

This code excerpt shows a ZEllipse class. It extends the built-in Ellipse2D.Float class. It adds functionality for scaling and moving an ellipse. For example, the isHit() method determines if the mouse pointer is inside the area of an ellipse. The MovingAdapter class handles the mouse press and mouse drag events.
@Override public void mousePressed(MouseEvent e) { x = e.getX(); y = e.getY(); }

In the mousePressed() method, we remember the initial x, y coordinates of the object.


int dx = e.getX() - x; int dy = e.getY() - y;

Inside the mouseDragged() method, we calculate the distance by which we have dragged the object.
if (zrect.isHit(x, y)) { zrect.addX(dx); zrect.addY(dy); repaint(); }

Here if we are inside the area of the rectangle, we update the x, y coordinates of the rectangle and repaint the panel.
x += dx; y += dy;

The initial coordinates are updated. The ScaleHandler class handles the scaling of the objects.

542

if (e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL) { if (zrect.isHit(x, y)) { float amount = e.getWheelRotation() * 5f; zrect.addWidth(amount); zrect.addHeight(amount); repaint(); } ... }

If we move a mouse wheel and our cursor is inside the area of a rectangle, the rectangle is resized and the panel repainted. The amount of the scaling is computed from the getWheelRotation() method, which returns the amount of the wheel rotation.

Resize Rectangle
In the next example, we will show how to resize a shape. Our shape will be a rectangle. On the rectangle, we will draw two small black rectangles. By clicking on these tiny rectangles and dragging them, we can resize our main rectangle. ResizeRectangle.java
import import import import import import import import import import package com.zetcode; java.awt.Graphics; java.awt.Graphics2D; java.awt.Point; java.awt.event.MouseAdapter; java.awt.event.MouseEvent; java.awt.geom.Point2D; java.awt.geom.Rectangle2D; javax.swing.JFrame; javax.swing.JPanel; javax.swing.SwingUtilities;

class Surface extends JPanel { private Point2D[] points; private int SIZE = 8; private int pos; public Surface() { } initUI();

private void initUI() { addMouseListener(new ShapeTestAdapter()); addMouseMotionListener(new ShapeTestAdapter()); pos = -1; points = new Point2D[2]; points[0] = new Point2D.Double(50, 50); points[1] = new Point2D.Double(150, 100);

543

} private void doDrawing(Graphics g) { Graphics2D g2 = (Graphics2D) g; for (int i = 0; i < points.length; i++) { double x = points[i].getX() - SIZE / 2; double y = points[i].getY() - SIZE / 2; g2.fill(new Rectangle2D.Double(x, y, SIZE, SIZE));

Rectangle2D s = new Rectangle2D.Double(); s.setFrameFromDiagonal(points[0], points[1]); } g2.draw(s);

@Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g); } private class ShapeTestAdapter extends MouseAdapter { @Override public void mousePressed(MouseEvent event) { Point p = event.getPoint(); for (int i = 0; i < points.length; i++) { double x = points[i].getX() - SIZE / 2; double y = points[i].getY() - SIZE / 2; Rectangle2D r = new Rectangle2D.Double(x, y, SIZE, SIZE); if (r.contains(p)) { pos = i; return; } } @Override public void mouseReleased(MouseEvent event) { pos = -1; } @Override public void mouseDragged(MouseEvent event) { if (pos == -1) { return; } }

544

points[pos] = event.getPoint(); repaint(); } } public class ResizeRectangle extends JFrame { public ResizeRectangle() } initUI(); { }

private void initUI() { setTitle("Resize rectangle"); add(new Surface()); setSize(300, 300); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { ResizeRectangle rr = new ResizeRectangle(); rr.setVisible(true);

} } } });

There are two ways to create a rectangle. By providing x, y coordinates plus the width and height of the rectangle. Another way is to provide the top-left and bottom-right points. In our code example, we will use both methods.
private Point2D[] points;

In this array, we will store points, that will make our rectangle.
private int SIZE = 8;

This is the size of the small black rectangles.


points = new Point2D[2]; points[0] = new Point2D.Double(50, 50); points[1] = new Point2D.Double(150, 100);

These are the initial coordinates for a rectangle.


Rectangle2D s = new Rectangle2D.Double(); s.setFrameFromDiagonal(points[0], points[1]);

545

g2.draw(s);

Here we draw a rectangle from the points.


for (int i = 0; i < points.length; i++) { double x = points[i].getX() - SIZE / 2; double y = points[i].getY() - SIZE / 2; g2.fill(new Rectangle2D.Double(x, y, SIZE, SIZE));

This code draws the two small controlling rectangles.


@Override public void mousePressed(MouseEvent event) { Point p = event.getPoint(); for (int i = 0; i < points.length; i++) { double x = points[i].getX() - SIZE / 2; double y = points[i].getY() - SIZE / 2; Rectangle2D r = new Rectangle2D.Double(x, y, SIZE, SIZE); if (r.contains(p)) { pos = i; return; } } }

In the mousePressed() method, we determine, if we have clicked inside one of the two controlling points. If we hit one of them, the pos variable stores which of them it was.
@Override public void mouseDragged(MouseEvent event) { if (pos == -1) { return; } points[pos] = event.getPoint(); repaint(); }

Here the rectangle is dynamically resized. During the mouseDragged() event, we get the current point, update our array of points and repaint the panel.

546

Figure: Resizing a rectangle In this part of the Java 2D tutorial, we covered hit testing and moving objects.

Tetris
In this chapter, we will create a Tetris game clone in Java Swing.

Tetris
The Tetris game is one of the most popular computer games ever created. The original game was designed and programmed by a Russian programmer Alexey Pajitnov in 1985. Since then, Tetris is available on almost every computer platform in lots of variations. Even my mobile phone has a modified version of the Tetris game. Tetris is called a falling block puzzle game. In this game, we have seven different shapes called tetrominoes. S-shape, Z-shape, T-shape, L-shape, Line-shape, MirroredL-shape and a Square-shape. Each of these shapes is formed with four squares. The shapes are falling down the board. The object of the Tetris game is to move and rotate the shapes, so that they fit as much as possible. If we manage to form a row, the row is destroyed and we score. We play the Tetris game until we top out.

Figure: Tetrominoes

547

The development
We do not have images for our Tetris game, we draw the tetrominoes using Swing drawing API. Behind every computer game, there is a mathematical model. So it is in Tetris. Some ideas behind the game.

We use a Timer class to create a game cycle The tetrominoes are drawn The shapes move on a square by square basis (not pixel by pixel) Mathematically a board is a simple list of numbers

I have simplified the game a bit, so that it is easier to understand. The game starts immediately, after it is launched. We can pause the game by pressing the p key. The space key will drop the Tetris piece immediately to the bottom. The d key will drop the piece one line down. (It can be used to speed up the falling a bit.) The game goes at constant speed, no acceleration is implemented. The score is the number of lines, that we have removed. Tetris.java
package com.zetcode; import java.awt.BorderLayout; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.SwingUtilities; public class Tetris extends JFrame { private JLabel statusbar; public Tetris() { initUI(); } private void initUI() { statusbar = new JLabel(" 0"); add(statusbar, BorderLayout.SOUTH); Board board = new Board(this); add(board); board.start(); setSize(200, 400); setTitle("Tetris"); setDefaultCloseOperation(EXIT_ON_CLOSE); setLocationRelativeTo(null); } public JLabel getStatusBar() { } return statusbar;

548

public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { Tetris game = new Tetris(); game.setVisible(true);

} } } });

In the Tetris.java file, we set up the game. We create a board on which we play the game. We create a statusbar.
board.start();

The start() method starts the Tetris game. Immediately after the window appears on the screen. Shape.java
package com.zetcode; import java.util.Random; public class Shape { protected enum Tetrominoes { NoShape, ZShape, SShape, LineShape, TShape, SquareShape, LShape, MirroredLShape }; private Tetrominoes pieceShape; private int coords[][]; private int[][][] coordsTable; public Shape() { coords = new int[4][2]; setShape(Tetrominoes.NoShape); } public void setShape(Tetrominoes shape) { coordsTable = new int[][][] { { { 0, 0 }, { 0, 0 }, { 0, 0 }, { { 0, -1 }, { 0, 0 }, { -1, 0 }, { { 0, -1 }, { 0, 0 }, { 1, 0 }, { { 0, -1 }, { 0, 0 }, { 0, 1 }, { { -1, 0 }, { 0, 0 }, { 1, 0 }, { { 0, 0 }, { 1, 0 }, { 0, 1 }, { { -1, -1 }, { 0, -1 }, { 0, 0 }, { { 1, -1 }, { 0, -1 }, { 0, 0 }, }; for (int i = 0; i < 4 ; i++) { for (int j = 0; j < 2; ++j) { { { { { { { { { 0, 0 } }, -1, 1 } }, 1, 1 } }, 0, 2 } }, 0, 1 } }, 1, 1 } }, 0, 1 } }, 0, 1 } }

549

coords[i][j] = coordsTable[shape.ordinal()][i][j]; } } private void setX(int index, int private void setY(int index, int public int x(int index) { return public int y(int index) { return public Tetrominoes getShape() { public void setRandomShape() { Random r = new Random(); int x = Math.abs(r.nextInt()) % 7 + 1; Tetrominoes[] values = Tetrominoes.values(); setShape(values[x]); } public int minX() { int m = coords[0][0]; for (int i=0; i < 4; i++) { } } public int minY() { int m = coords[0][1]; for (int i=0; i < 4; i++) { m = Math.min(m, coords[i][1]); } } return m; m = Math.min(m, coords[i][0]); x) { coords[index][0] = x; } y) { coords[index][1] = y; } coords[index][0]; } coords[index][1]; } return pieceShape; } }

pieceShape = shape;

return m;

public Shape rotateLeft() { if (pieceShape == Tetrominoes.SquareShape) return this; Shape result = new Shape(); result.pieceShape = pieceShape; for (int i = 0; i < 4; ++i) { result.setX(i, y(i)); result.setY(i, -x(i));

550

return result;

public Shape rotateRight() { if (pieceShape == Tetrominoes.SquareShape) return this; Shape result = new Shape(); result.pieceShape = pieceShape; for (int i = 0; i < 4; ++i) { result.setX(i, -y(i)); result.setY(i, x(i));

} }

return result; }

The Shape class provides information about a Tetris piece.


protected enum Tetrominoes { NoShape, ZShape, SShape, LineShape, TShape, SquareShape, LShape, MirroredLShape };

The Tetrominoes enum holds all seven Tetris shapes. Plus the empty shape called here NoShape.
public Shape() { coords = new int[4][2]; setShape(Tetrominoes.NoShape); }

This is the constructor of the Shape class. The coords array holds the actual coordinates of a Tetris piece.
coordsTable = new int[][][] { { { 0, 0 }, { 0, 0 }, { 0, 0 }, { { 0, -1 }, { 0, 0 }, { -1, 0 }, { { 0, -1 }, { 0, 0 }, { 1, 0 }, { { 0, -1 }, { 0, 0 }, { 0, 1 }, { { -1, 0 }, { 0, 0 }, { 1, 0 }, { { 0, 0 }, { 1, 0 }, { 0, 1 }, { { -1, -1 }, { 0, -1 }, { 0, 0 }, { { 1, -1 }, { 0, -1 }, { 0, 0 }, }; { { { { { { { { 0, 0 } }, -1, 1 } }, 1, 1 } }, 0, 2 } }, 0, 1 } }, 1, 1 } }, 0, 1 } }, 0, 1 } }

The coordsTable array holds all possible coordinate values of our Tetris pieces. This is a template from which all pieces take their coordinate values.
for (int i = 0; i < 4 ; i++) { for (int j = 0; j < 2; ++j) { coords[i][j] = coordsTable[shape.ordinal()][i][j];

551

Here we put one row of the coordinate values from the coordsTable to a coords array of a Tetris piece. Note the use of the ordinal() method. In C++, an enum type is esencially an integer. Unlike in C++, Java enums are full classes. And the ordinal() method returns the current position of the enum type in the enum object. The following image will help understand the coordinate values a bit more. The coords array saves the coordinates of the Tetris piece. For example, numbers { 0, -1 }, { 0, 0 }, { -1, 0 }, { -1, -1 } , represent a rotated S-shape. The following diagram illustrates the shape.

Figure: Coordinates
public Shape rotateLeft() { if (pieceShape == Tetrominoes.SquareShape) return this; Shape result = new Shape(); result.pieceShape = pieceShape; for (int i = 0; i < 4; ++i) { result.setX(i, y(i)); result.setY(i, -x(i)); } } return result;

This code rotates the piece to the left. The square does not have to be rotated. That's why we simply return the reference to the current object. Looking at the previous image will help to understand the rotation. Board.java
package com.zetcode; import import import import import java.awt.Color; java.awt.Dimension; java.awt.Graphics; java.awt.event.ActionEvent; java.awt.event.ActionListener;

552

import import import import import

java.awt.event.KeyAdapter; java.awt.event.KeyEvent; javax.swing.JLabel; javax.swing.JPanel; javax.swing.Timer;

import com.zetcode.Shape.Tetrominoes; public class Board extends JPanel implements ActionListener { private final int BoardWidth = 10; private final int BoardHeight = 22; private private private private private private private private private private Timer timer; boolean isFallingFinished = false; boolean isStarted = false; boolean isPaused = false; int numLinesRemoved = 0; int curX = 0; int curY = 0; JLabel statusbar; Shape curPiece; Tetrominoes[] board;

public Board(Tetris parent) { initBoard(parent); } private void initBoard(Tetris parent) { setFocusable(true); curPiece = new Shape(); timer = new Timer(400, this); timer.start(); statusbar = parent.getStatusBar(); board = new Tetrominoes[BoardWidth * BoardHeight]; addKeyListener(new TAdapter()); clearBoard();

@Override public void actionPerformed(ActionEvent e) { if (isFallingFinished) { isFallingFinished = false; newPiece(); } else { } } private int squareWidth() { return (int) getSize().getWidth() / BoardWidth; } private int squareHeight() { return (int) getSize().getHeight() / BoardHeight; } oneLineDown();

553

private Tetrominoes shapeAt(int x, int y) { return board[(y * BoardWidth) + x]; } public void start() if (isPaused) return; isStarted = true; isFallingFinished = false; numLinesRemoved = 0; clearBoard(); newPiece(); timer.start(); } private void pause() if (!isStarted) return; isPaused = !isPaused; if (isPaused) { timer.stop(); statusbar.setText("paused"); } else { timer.start(); statusbar.setText(String.valueOf(numLinesRemoved)); { {

} }

repaint(); private void doDrawing(Graphics g) { Dimension size = getSize(); int boardTop = (int) size.getHeight() - BoardHeight * squareHeight(); for (int i = 0; i < BoardHeight; ++i) { for (int j = 0; j < BoardWidth; ++j) { Tetrominoes shape = shapeAt(j, BoardHeight - i - 1); if (shape != Tetrominoes.NoShape) drawSquare(g, 0 + j * squareWidth(), boardTop + i * squareHeight(), shape);

} }

if (curPiece.getShape() != Tetrominoes.NoShape) { for (int i = 0; i < 4; ++i) { int x = curX + curPiece.x(i);

554

squareHeight(), } } }

int y = curY - curPiece.y(i); drawSquare(g, 0 + x * squareWidth(), boardTop + (BoardHeight - y - 1) * curPiece.getShape());

@Override public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g); } private void dropDown() { int newY = curY; while (newY > 0) { if (!tryMove(curPiece, curX, newY - 1)) break; --newY;

} }

pieceDropped(); private void oneLineDown() {

if (!tryMove(curPiece, curX, curY - 1)) pieceDropped(); } private void clearBoard() { for (int i = 0; i < BoardHeight * BoardWidth; ++i) board[i] = Tetrominoes.NoShape;

private void pieceDropped() { for (int i = 0; i < 4; ++i) { int x = curX + curPiece.x(i); int y = curY - curPiece.y(i); board[(y * BoardWidth) + x] = curPiece.getShape(); } removeFullLines(); if (!isFallingFinished) newPiece(); } private void newPiece() {

curPiece.setRandomShape();

555

curX = BoardWidth / 2 + 1; curY = BoardHeight - 1 + curPiece.minY(); if (!tryMove(curPiece, curX, curY)) { curPiece.setShape(Tetrominoes.NoShape); timer.stop(); isStarted = false; statusbar.setText("game over");

} }

private boolean tryMove(Shape newPiece, int newX, int newY) { for (int i = 0; i < 4; ++i) { int x = newX + newPiece.x(i); int y = newY - newPiece.y(i); if (x < 0 || x >= BoardWidth || y < 0 || y >= BoardHeight) return false; if (shapeAt(x, y) != Tetrominoes.NoShape) return false; } curPiece = newPiece; curX = newX; curY = newY; repaint(); } return true;

private void removeFullLines() { int numFullLines = 0; for (int i = BoardHeight - 1; i >= 0; --i) { boolean lineIsFull = true; for (int j = 0; j < BoardWidth; ++j) { if (shapeAt(j, i) == Tetrominoes.NoShape) { lineIsFull = false; break; } } if (lineIsFull) { ++numFullLines; for (int k = i; k < BoardHeight - 1; ++k) { for (int j = 0; j < BoardWidth; ++j) board[(k * BoardWidth) + j] = shapeAt(j, k + 1); } } } if (numFullLines > 0) { numLinesRemoved += numFullLines;

556

statusbar.setText(String.valueOf(numLinesRemoved)); isFallingFinished = true; curPiece.setShape(Tetrominoes.NoShape); repaint(); } } {

private void drawSquare(Graphics g, int x, int y, Tetrominoes shape) Color colors[] = { new Color(102, new Color(204, new Color(102, }; new Color(0, 0, 0), new Color(204, 102, 102), 204, 102), new Color(102, 102, 204), 204, 102), new Color(204, 102, 204), 204, 204), new Color(218, 170, 0)

Color color = colors[shape.ordinal()]; g.setColor(color); g.fillRect(x + 1, y + 1, squareWidth() - 2, squareHeight() - 2); g.setColor(color.brighter()); g.drawLine(x, y + squareHeight() - 1, x, y); g.drawLine(x, y, x + squareWidth() - 1, y); g.setColor(color.darker()); g.drawLine(x + 1, y + squareHeight() - 1, x + squareWidth() - 1, y + squareHeight() - 1); g.drawLine(x + squareWidth() - 1, y + squareHeight() - 1, x + squareWidth() - 1, y + 1); } class TAdapter extends KeyAdapter { @Override public void keyPressed(KeyEvent e) { if (!isStarted || curPiece.getShape() == Tetrominoes.NoShape) { } int keycode = e.getKeyCode(); if (keycode == 'p' || keycode == 'P') { pause(); return; } if (isPaused) return; switch (keycode) { case KeyEvent.VK_LEFT: tryMove(curPiece, curX - 1, curY); break; case KeyEvent.VK_RIGHT: tryMove(curPiece, curX + 1, curY); break; return;

557

case KeyEvent.VK_DOWN: tryMove(curPiece.rotateRight(), curX, curY); break; case KeyEvent.VK_UP: tryMove(curPiece.rotateLeft(), curX, curY); break; case KeyEvent.VK_SPACE: dropDown(); break; case 'd': oneLineDown(); break; case 'D': oneLineDown(); break; }

} } }

Finally, we have the Board.java file. This is where the game logic is located.
... private private private private private private ... boolean isFallingFinished = false; boolean isStarted = false; boolean isPaused = false; int numLinesRemoved = 0; int curX = 0; int curY = 0;

We initialize some important variables. The isFallingFinished variable determines, if the Tetris shape has finished falling and we then need to create a new shape. The numLinesRemoved counts the number of lines, we have removed so far. The curX and curY variables determine the actual position of the falling Tetris shape.
setFocusable(true);

We must explicitly call the setFocusable() method. From now, the board has the keyboard input.
timer = new Timer(400, this); timer.start();

object fires one or more action events after a specified delay. In our case, the timer calls the actionPerformed() method each 400ms.
Timer @Override public void actionPerformed(ActionEvent e) { if (isFallingFinished) {

558

isFallingFinished = false; newPiece(); } else { } } oneLineDown();

The actionPerformed() method checks if the falling has finished. If so, a new piece is created. If not, the falling Tetris piece goes one line down. Inside the doDrawing() method, we draw all objects on the board. The painting has two steps.
for (int i = 0; i < BoardHeight; ++i) { for (int j = 0; j < BoardWidth; ++j) { Tetrominoes shape = shapeAt(j, BoardHeight - i - 1); if (shape != Tetrominoes.NoShape) drawSquare(g, 0 + j * squareWidth(), boardTop + i * squareHeight(), shape); } }

In the first step we paint all the shapes, or remains of the shapes, that have been dropped to the bottom of the board. All the squares are remembered in the board array. We access it using the shapeAt() method.
if (curPiece.getShape() != Tetrominoes.NoShape) { for (int i = 0; i < 4; ++i) { int x = curX + curPiece.x(i); int y = curY - curPiece.y(i); drawSquare(g, 0 + x * squareWidth(), boardTop + (BoardHeight - y - 1) * squareHeight(), curPiece.getShape());

} }

In the second step, we paint the actual falling piece.


private void dropDown() { int newY = curY; while (newY > 0) { if (!tryMove(curPiece, curX, newY - 1)) break; --newY; } } pieceDropped();

559

If we press the space key, the piece is dropped to the bottom. We simply try to drop the piece one line down until it reaches the bottom or the top of another fallen Tetris piece.
private void clearBoard() { for (int i = 0; i < BoardHeight * BoardWidth; ++i) board[i] = Tetrominoes.NoShape; }

The clearBoard() method fills the board with empty NoShapes. This is later used at collision detection.
private void pieceDropped() { for (int i = 0; i < 4; ++i) { int x = curX + curPiece.x(i); int y = curY - curPiece.y(i); board[(y * BoardWidth) + x] = curPiece.getShape(); } removeFullLines(); if (!isFallingFinished) newPiece(); }

The pieceDropped() method puts the falling piece into the board array. Once again, the board holds all the squares of the pieces and remains of the pieces that has finished falling. When the piece has finished falling, it is time to check, if we can remove some lines off the board. This is the job of the removeFullLines() method. Then we create a new piece. More precisely, we try to create a new piece.
private void newPiece() {

curPiece.setRandomShape(); curX = BoardWidth / 2 + 1; curY = BoardHeight - 1 + curPiece.minY(); if (!tryMove(curPiece, curX, curY)) { curPiece.setShape(Tetrominoes.NoShape); timer.stop(); isStarted = false; statusbar.setText("game over");

} }

The newPiece() method creates a new Tetris piece. The piece gets a new random shape. Then we compute the initial curX and curY values. If we cannot move to the initial positions, the game is over. We top out. The timer is stopped. We put game over string on the statusbar.
private boolean tryMove(Shape newPiece, int newX, int newY) { for (int i = 0; i < 4; ++i) { int x = newX + newPiece.x(i);

560

int y = newY - newPiece.y(i); if (x < 0 || x >= BoardWidth || y < 0 || y >= BoardHeight) return false; if (shapeAt(x, y) != Tetrominoes.NoShape) return false;

curPiece = newPiece; curX = newX; curY = newY; repaint(); return true; }

The tryMove() method tries to move the Tetris piece. The method returns false, if it has reached the board boundaries or it is adjacent to the already fallen Tetris pieces.
int numFullLines = 0; for (int i = BoardHeight - 1; i >= 0; --i) { boolean lineIsFull = true; for (int j = 0; j < BoardWidth; ++j) { if (shapeAt(j, i) == Tetrominoes.NoShape) { lineIsFull = false; break; } } if (lineIsFull) { ++numFullLines; for (int k = i; k < BoardHeight - 1; ++k) { for (int j = 0; j < BoardWidth; ++j) board[(k * BoardWidth) + j] = shapeAt(j, k + 1); } } }

Inside the removeFullLines() method, we check if there is any full row among all rows in the board. If there is at least one full line, it is removed. After finding a full line we increase the counter. We move all the lines above the full row one line down. This way we destroy the full line. Notice, that in our Tetris game, we use the so called naive gravity. This means, that the squares may be left floating above empty gaps. Every Tetris piece has four squares. Each of the squares is drawn with the drawSquare() method. Tetris pieces have different colors.
g.setColor(color.brighter()); g.drawLine(x, y + squareHeight() - 1, x, y); g.drawLine(x, y, x + squareWidth() - 1, y);

The left and top sides of a square are drawn with a brighter color. Similarly, the bottom and right sides are drawn with darker colors. This is to simulate a 3D edge. 561

We control the game with a keyboard. The control mechanism is implemented with a KeyAdapter. This is an inner class that overrides the keyPressed() method.
case KeyEvent.VK_LEFT: tryMove(curPiece, curX - 1, curY); break;

If we press the left arrow key, we try to move the falling piece one square to the left.

Figure: Tetris This was the Tetris game.

562

Java 2D games tutorial


This is Java 2D games tutorial. In this tutorial, you will learn the basics of 2D game programming in Java. The Java 2D games tutorial is suitable for beginners and intermediate programmers.

Basics
In this part of the Java 2D games tutorial, we will write about some basics needed to create games. We will create a skeleton of a game. Paint a donut and display a picture.

About
This is Java 2D games tutorial. It is aimed at beginners. This tutorial will teach you basics of programming 2D games in Java programming language and Swing GUI toolkit. All images used in this tutorial can be downloaded here.

Skeleton
We will show the skeleton of each of our Java 2D games. Board.java
package skeleton; import javax.swing.JPanel; public class Board extends JPanel { public Board() { } }

The Board is a panel, where the game takes place. Skeleton.java


package skeleton; import javax.swing.JFrame; public class Skeleton extends JFrame { public Skeleton() { add(new Board()); setTitle("Skeleton"); setDefaultCloseOperation(EXIT_ON_CLOSE); setSize(300, 280); setLocationRelativeTo(null); setVisible(true); setResizable(false); } public static void main(String[] args) { new Skeleton();

563

This is the entry point of the game. Here we have the main method.
add(new Board());

Here we put the Board to the center of the JFrame component.


setDefaultCloseOperation(EXIT_ON_CLOSE);

This will close the application when we click on the close button. It is not the default behaviour.
setSize(300, 280);

This line sets the size for our window.


setLocationRelativeTo(null);

We center the window.


setVisible(true);

Show the window on the screen.


setResizable(false);

Make the window unresizable.

Figure: Skeleton

Donut
The objects on the Board are either images or are drawn with the painting tools provided by the Java 2D API. In the next example, we draw a Donut. We use the painting API. 564

Board.java
package donut; import import import import import import import import java.awt.BasicStroke; java.awt.Color; java.awt.Dimension; java.awt.Graphics; java.awt.Graphics2D; java.awt.RenderingHints; java.awt.geom.AffineTransform; java.awt.geom.Ellipse2D;

import javax.swing.JPanel; public class Board extends JPanel{ public void paint(Graphics g) { super.paint(g); Graphics2D g2 = (Graphics2D) g; RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); rh.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); g2.setRenderingHints(rh); Dimension size = getSize(); double w = size.getWidth(); double h = size.getHeight(); Ellipse2D e = new Ellipse2D.Double(0, 0, 80, 130); g2.setStroke(new BasicStroke(1)); g2.setColor(Color.gray); for (double deg = 0; deg < 360; deg += 5) { AffineTransform at = AffineTransform.getTranslateInstance(w / 2, h / 2); at.rotate(Math.toRadians(deg)); g2.draw(at.createTransformedShape(e)); } } }

The painting is done inside the paint() method.


Graphics2D g2 = (Graphics2D) g;

The Graphics2D object provides a sophisticated control over painting.


RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

565

rh.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); g2.setRenderingHints(rh);

The rendering hints are used to make the drawing smooth.


Dimension size = getSize(); double w = size.getWidth(); double h = size.getHeight();

We get the height and the width of the window.


Ellipse2D e = new Ellipse2D.Double(0, 0, 80, 130); g2.setStroke(new BasicStroke(1)); g2.setColor(Color.gray);

Here we create the ellipse.


for (double deg = 0; deg < 360; deg += 5) { AffineTransform at = AffineTransform.getTranslateInstance(w / 2, h / 2); at.rotate(Math.toRadians(deg)); g2.draw(at.createTransformedShape(e)); }

Here the ellispse is rotated 72 times to create a "donut". Donut.java


package donut; import javax.swing.JFrame; public class Donut extends JFrame { public Donut() { add(new Board()); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize(360, 310); setLocationRelativeTo(null); setTitle("Donut"); setVisible(true); } public static void main(String[] args) { new Donut(); }

This is the main class.

566

Figure: Donut

Image
When we create computer games, we often work with images. In the next example we load an image and display it on the Board. Board.java
package bardejov; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import javax.swing.ImageIcon; import javax.swing.JPanel; public class Board extends JPanel { Image bardejov; public Board() { ImageIcon ii = new ImageIcon(this.getClass().getResource("bardejov.jpg")); bardejov = ii.getImage(); } public void paint(Graphics g) { Graphics2D g2d = (Graphics2D) g; g2d.drawImage(bardejov, 10, 10, null); } }

We display an image of a town on the Board. The image is drawn inside the paint() method.

567

ImageIcon ii = new ImageIcon(this.getClass().getResource("bardejov.jpg"));

We create an ImageIcon.
bardejov = ii.getImage();

We get an Image out of the ImageIcon.


g2d.drawImage(bardejov, 10, 10, null);

We draw the image on the window. Image.java


package bardejov; import javax.swing.JFrame; public class Image extends JFrame { public Image() { add(new Board()); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize(280, 240); setLocationRelativeTo(null); setTitle("Bardejov"); setVisible(true);

public static void main(String[] args) { new Image(); } }

This is the main class of the example.

Figure: Image In this chapter, we have covered some basics of Java game programming.

568

Animation
In this part of the Java 2D games tutorial, we will work with animation. Animation is a rapid display of sequence of images which creates an illusion of movement. We will animate a star on our Board. We will implement the movement in three basic ways. We will use a Swing timer, a standard utility timer and a thread.

Swing timer
In the first example we will use a Swing timer to create animation. This is the easiest but also the least effective way of animating objects in Java games. Star.java
package star; import javax.swing.JFrame; public class Star extends JFrame { public Star() { add(new Board()); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize(280, 240); setLocationRelativeTo(null); setTitle("Star"); setResizable(false); setVisible(true); } public static void main(String[] args) { new Star(); } }

This is the main class for the code example. Board.java


import import import import package star; java.awt.Color; java.awt.Graphics; java.awt.Graphics2D; java.awt.Image;

import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.ImageIcon; import javax.swing.JPanel; import javax.swing.Timer;

569

public class Board extends JPanel implements ActionListener { Image star; Timer timer; int x, y; public Board() { setBackground(Color.BLACK); ImageIcon ii = new ImageIcon(this.getClass().getResource("star.png")); star = ii.getImage(); setDoubleBuffered(true); x = y = 10; timer = new Timer(25, this); timer.start();

public void paint(Graphics g) { super.paint(g); Graphics2D g2d = (Graphics2D)g; g2d.drawImage(star, x, y, this); Toolkit.getDefaultToolkit().sync(); g.dispose();

public void actionPerformed(ActionEvent e) { x += 1; y += 1; if (y > 240) { y = -45; x = -45; } repaint(); } } setDoubleBuffered(true);

Our JPanel component will use a buffer to paint. This means that all drawing will be done in memory first. Later the off-screen buffer will be copied to the screen. In this example, I didn't notice any differences.
timer = new Timer(25, this); timer.start();

Here we create a Swing Timer class. We start the timer. Every 25 ms the timer will call the actionPerformed() method. In order to use the actionPerformed() method, we must implement the ActionListener interface.
g2d.drawImage(star, x, y, this);

570

In the paint() method, we draw the star.


Toolkit.getDefaultToolkit().sync();

We must synchronize the painting on Linux systems. Otherwise, the animation would not be smooth.
public void actionPerformed(ActionEvent e) { x += 1; y += 1; if (y > 240) { y = -45; x = -45; } repaint(); }

In the actionPerformed() method we increase the x, y values. Then we call the repaint() method. This way we regularly repaint the Board thus making the animation.

Figure: Star

Utility timer
This is very similar to the previous way. We use the java.util.Timer instead of the javax.Swing.Timer. For Java Swing games this way should be more accurate. Star.java
package star2; import javax.swing.JFrame; public class Star extends JFrame { public Star() { add(new Board()); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize(280, 240);

571

setLocationRelativeTo(null); setTitle("Star"); setResizable(false); setVisible(true); } public static void main(String[] args) { new Star(); } }

The main class. Board.java


import import import import import package star2; java.awt.Color; java.awt.Graphics; java.awt.Graphics2D; java.awt.Image; java.awt.Toolkit;

import java.util.Timer; import java.util.TimerTask; import javax.swing.ImageIcon; import javax.swing.JPanel; public class Board extends JPanel Image star; Timer timer; int x, y; public Board() { setBackground(Color.BLACK); ImageIcon ii = new ImageIcon(this.getClass().getResource("star.png")); star = ii.getImage(); setDoubleBuffered(true); x = y = 10; timer = new Timer(); timer.scheduleAtFixedRate(new ScheduleTask(), 100, 10); } public void paint(Graphics g) { super.paint(g); Graphics2D g2d = (Graphics2D)g; g2d.drawImage(star, x, y, this); Toolkit.getDefaultToolkit().sync(); g.dispose(); } {

572

class ScheduleTask extends TimerTask { public void run() { x += 1; y += 1; if (y > 240) { y = -45; x = -45; } repaint(); } } }

In this example, the timer will regularly call the run() method of the ScheduleTask class.
timer = new Timer(); timer.scheduleAtFixedRate(new ScheduleTask(), 100, 10);

Here we create a timer. And schedule a task at 10 ms interval. There is 100 ms initial delay.
public void run() { ... }

Each 10 ms the timer will call this run() method.

Thread
Animating objects using a thread is the most effective way of animation. Star.java
package star3; import javax.swing.JFrame; public class Star extends JFrame { public Star() { add(new Board()); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize(280, 240); setLocationRelativeTo(null); setTitle("Star"); setResizable(false); setVisible(true); } public static void main(String[] args) { new Star(); }

573

This is the main class. Board.java


package star2; import import import import import java.awt.Color; java.awt.Graphics; java.awt.Graphics2D; java.awt.Image; java.awt.Toolkit;

import javax.swing.ImageIcon; import javax.swing.JPanel; public class Board extends JPanel implements Runnable { private Image star; private Thread animator; private int x, y; private final int DELAY = 50; public Board() { setBackground(Color.BLACK); setDoubleBuffered(true); ImageIcon ii = new ImageIcon(this.getClass().getResource("star.png")); star = ii.getImage(); x = y = 10; } public void addNotify() { super.addNotify(); animator = new Thread(this); animator.start(); } public void paint(Graphics g) { super.paint(g); Graphics2D g2d = (Graphics2D)g; g2d.drawImage(star, x, y, this); Toolkit.getDefaultToolkit().sync(); g.dispose();

public void cycle() { x += 1; y += 1; if (y > 240) { y = -45; x = -45; }

574

} public void run() { long beforeTime, timeDiff, sleep; beforeTime = System.currentTimeMillis(); while (true) { cycle(); repaint(); timeDiff = System.currentTimeMillis() - beforeTime; sleep = DELAY - timeDiff; if (sleep < 0) sleep = 2; try { Thread.sleep(sleep); } catch (InterruptedException e) { System.out.println("interrupted"); } } } } beforeTime = System.currentTimeMillis();

In the previous examples, we executed a task at specific intervals. In this example, the animation will take place inside a thread. The run() method is called only once. That's why we have a while loop in the method. From this method, we call the cycle() and the repaint() methods.
public void addNotify() { super.addNotify(); animator = new Thread(this); animator.start(); }

The addNotify() method is called after our JPanel has been added to the JFrame component. This method is often used for various initialization tasks. We want our game run smoothly. At constant speed. Therefore we compute the system time.
timeDiff = System.currentTimeMillis() - beforeTime; sleep = DELAY - timeDiff;

The cycle() and the repaint() methods might take different time at various while cycles. We calculate the time both methods run and subtract it from the DELAY constant. This way we want to ensure that each while cycle runs a constant time. In our case, 50ms each cycle. This part of the Java 2D games tutorial covered animation.

Moving sprites
575

In this part of the Java 2D games tutorial we will work with sprites. The term sprite has several meanings. It is used to denote an image or an animation in a scene. It is also used to represent any movable object in a game. Also one of the meanings is the code that encapsulates a character in a game. In our tutorial by using sprite we refer to a movable object or its java class.

R-Type
In the first example we will have a spacecraft. We can move the spacecraft on the board using the cursor keys. Craft.java
package rtype; import java.awt.Image; import java.awt.event.KeyEvent; import javax.swing.ImageIcon; public class Craft { private String craft = "craft.png"; private private private private private int dx; int dy; int x; int y; Image image;

public Craft() { ImageIcon ii = new ImageIcon(this.getClass().getResource(craft)); image = ii.getImage(); x = 40; y = 60; } public void move() { x += dx; y += dy; } public int getX() { return x; } public int getY() { return y; } public Image getImage() { return image; } public void keyPressed(KeyEvent e) { int key = e.getKeyCode();

576

if (key == KeyEvent.VK_LEFT) { dx = -1; } if (key == KeyEvent.VK_RIGHT) { dx = 1; } if (key == KeyEvent.VK_UP) { dy = -1; } if (key == KeyEvent.VK_DOWN) { dy = 1; } } public void keyReleased(KeyEvent e) { int key = e.getKeyCode(); if (key == KeyEvent.VK_LEFT) { dx = 0; } if (key == KeyEvent.VK_RIGHT) { dx = 0; } if (key == KeyEvent.VK_UP) { dy = 0; } if (key == KeyEvent.VK_DOWN) { dy = 0; } } }

This class represents a spacecraft. In this class we keep the image of the sprite and the coordinates of the sprite. The keyPressed() and keyReleased() methods control whether the sprite is moving or is in standstill.
public void move() { x += dx; y += dy; }

The move() method changes the coordinates of the sprite. These x, y values are used in the paint() method to draw the image of the sprite.
if (key == KeyEvent.VK_LEFT) { dx = 0; }

When we release the left cursor key, we set the dx variable to zero. The spacecraft will stop moving.

577

Board.java
package rtype; import import import import import import import import java.awt.Color; java.awt.Graphics; java.awt.Graphics2D; java.awt.Toolkit; java.awt.event.ActionEvent; java.awt.event.ActionListener; java.awt.event.KeyAdapter; java.awt.event.KeyEvent;

import javax.swing.JPanel; import javax.swing.Timer; public class Board extends JPanel implements ActionListener { private Timer timer; private Craft craft; public Board() { addKeyListener(new TAdapter()); setFocusable(true); setBackground(Color.BLACK); setDoubleBuffered(true); craft = new Craft(); timer = new Timer(5, this); timer.start();

public void paint(Graphics g) { super.paint(g); Graphics2D g2d = (Graphics2D)g; g2d.drawImage(craft.getImage(), craft.getX(), craft.getY(), this); Toolkit.getDefaultToolkit().sync(); g.dispose(); } public void actionPerformed(ActionEvent e) { craft.move(); repaint(); } private class TAdapter extends KeyAdapter { public void keyReleased(KeyEvent e) { craft.keyReleased(e); } public void keyPressed(KeyEvent e) { craft.keyPressed(e); }

578

} }

This is the Board class.


g2d.drawImage(craft.getImage(), craft.getX(), craft.getY(), this);

In the paint() method, we draw the spacecraft. We get the image and the coordinates from the sprite class.
public void actionPerformed(ActionEvent e) { craft.move(); repaint(); }

The actionPerformed() method is called every 5ms. We move the sprite and repaint the board. RType.java
package rtype; import javax.swing.JFrame; public class RType extends JFrame { public RType() { add(new Board()); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize(400, 300); setLocationRelativeTo(null); setTitle("R - Type"); setResizable(false); setVisible(true); } public static void main(String[] args) { new RType(); }

This is the main class.

579

Figure: R-Type

Shooting missiles
In the next example we will add another sprite type to our example. A missile. We can launch missiles with the space key. Missile.java
package rtype; import java.awt.Image; import javax.swing.ImageIcon; public class Missile { private int x, y; private Image image; boolean visible; private final int BOARD_WIDTH = 390; private final int MISSILE_SPEED = 2; public Missile(int x, int y) { ImageIcon ii = new ImageIcon(this.getClass().getResource("missile.png")); image = ii.getImage(); visible = true; this.x = x; this.y = y;

public Image getImage() { return image; } public int getX() {

580

return x;

public int getY() { return y; } public boolean isVisible() { return visible; } public void move() { x += MISSILE_SPEED; if (x > BOARD_WIDTH) visible = false; } }

Here we have a new sprite called Missile.


public void move() { x += MISSILE_SPEED; if (x > BOARD_WIDTH) visible = false; }

The missile moves at constant speed. When it hits the right border of the Board, it becomes invisible. It is then removed from the ArrayList of missiles. Craft.java
package rtype; import java.awt.Image; import java.awt.event.KeyEvent; import java.util.ArrayList; import javax.swing.ImageIcon; public class Craft { private String craft = "craft.png"; private private private private private int dx; int dy; int x; int y; Image image;

private ArrayList missiles; private final int CRAFT_SIZE = 20; public Craft() { ImageIcon ii = new ImageIcon(this.getClass().getResource(craft)); image = ii.getImage(); missiles = new ArrayList(); x = 40; y = 60;

581

} public void move() { x += dx; y += dy; } public int getX() { return x; } public int getY() { return y; } public Image getImage() { return image; } public ArrayList getMissiles() { return missiles; } public void keyPressed(KeyEvent e) { int key = e.getKeyCode(); if (key == KeyEvent.VK_SPACE) { fire(); } if (key == KeyEvent.VK_LEFT) { dx = -1; } if (key == KeyEvent.VK_RIGHT) { dx = 1; } if (key == KeyEvent.VK_UP) { dy = -1; } if (key == KeyEvent.VK_DOWN) { dy = 1; }

public void fire() { missiles.add(new Missile(x + CRAFT_SIZE, y + CRAFT_SIZE/2)); } public void keyReleased(KeyEvent e) { int key = e.getKeyCode(); if (key == KeyEvent.VK_LEFT) { dx = 0; } if (key == KeyEvent.VK_RIGHT) {

582

dx = 0;

if (key == KeyEvent.VK_UP) { dy = 0; } if (key == KeyEvent.VK_DOWN) { dy = 0; } } }

The Craft.java has changed a bit.


if (key == KeyEvent.VK_SPACE) { fire(); }

If we press the space key, we fire.


public void fire() { missiles.add(new Missile(x + CRAFT_SIZE, y + CRAFT_SIZE/2)); }

The fire() method creates a new Missile object and adds it to the missiles ArrayList.
public ArrayList getMissiles() { return missiles; }

The getMissiles() method returns the ArrayList of missiles. It is called from the Board class. Board.java
import import import import import import import import package rtype; java.awt.Color; java.awt.Graphics; java.awt.Graphics2D; java.awt.Toolkit; java.awt.event.ActionEvent; java.awt.event.ActionListener; java.awt.event.KeyAdapter; java.awt.event.KeyEvent;

import java.util.ArrayList; import javax.swing.JPanel; import javax.swing.Timer; public class Board extends JPanel implements ActionListener { private Timer timer; private Craft craft; public Board() {

583

addKeyListener(new TAdapter()); setFocusable(true); setBackground(Color.BLACK); setDoubleBuffered(true); craft = new Craft(); timer = new Timer(5, this); timer.start();

public void paint(Graphics g) { super.paint(g); Graphics2D g2d = (Graphics2D)g; g2d.drawImage(craft.getImage(), craft.getX(), craft.getY(), this); ArrayList ms = craft.getMissiles(); for (int i = 0; i < ms.size(); i++ ) { Missile m = (Missile) ms.get(i); g2d.drawImage(m.getImage(), m.getX(), m.getY(), this); } Toolkit.getDefaultToolkit().sync(); g.dispose();

public void actionPerformed(ActionEvent e) { ArrayList ms = craft.getMissiles(); for (int i = 0; i < ms.size(); i++) { Missile m = (Missile) ms.get(i); if (m.isVisible()) m.move(); else ms.remove(i); } craft.move(); repaint(); } private class TAdapter extends KeyAdapter { public void keyReleased(KeyEvent e) { craft.keyReleased(e); } public void keyPressed(KeyEvent e) { craft.keyPressed(e); } } }

This is the Board.java file.


ArrayList ms = craft.getMissiles();

584

for (int i = 0; i < ms.size(); i++ ) { Missile m = (Missile) ms.get(i); g2d.drawImage(m.getImage(), m.getX(), m.getY(), this); }

In the paint() method we draw all missiles from the array list.
ArrayList ms = craft.getMissiles(); for (int i = 0; i < ms.size(); i++) { Missile m = (Missile) ms.get(i); if (m.isVisible()) m.move(); else ms.remove(i); }

In the actionPerformed() method we parse all missiles from the array list. Depending on the visible flag, we move the missile or remove it from the container. RType.java
package rtype; import javax.swing.JFrame; public class RType extends JFrame { public RType() { add(new Board()); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize(400, 300); setLocationRelativeTo(null); setTitle("R - Type"); setResizable(false); setVisible(true); } public static void main(String[] args) { new RType(); }

Finally, this is the main class.

585

Figure: Shooting missiles In this chapter, we have covered sprites.

Collision detection
In this part of the Java 2D games tutorial we will talk about collision detection. Many games need to handle collisions. Especially arcade games. Simply said, we need to detect when two objects collide on screen. In the next code example, we will expand the previous example. We add a new Alien sprite. We will detect two collisions. When the missile hits an alien ship and when our spacecraft collides with an alien.

Shooting aliens
In the first example we will have a spacecraft. We can move the spacecraft on the board using the cursor keys. Craft.java
package collision; import java.awt.Image; import java.awt.Rectangle; import java.awt.event.KeyEvent; import java.util.ArrayList; import javax.swing.ImageIcon; public class Craft { private String craft = "craft.png"; private int dx; private int dy; private int x;

586

private private private private private private

int y; int width; int height; boolean visible; Image image; ArrayList missiles;

public Craft() { ImageIcon ii = new ImageIcon(this.getClass().getResource(craft)); image = ii.getImage(); width = image.getWidth(null); height = image.getHeight(null); missiles = new ArrayList(); visible = true; x = 40; y = 60; } public void move() { x += dx; y += dy; if (x < 1) { x = 1; } if (y < 1) { y = 1; } } public int getX() { return x; } public int getY() { return y; } public Image getImage() { return image; } public ArrayList getMissiles() { return missiles; } public void setVisible(boolean visible) { this.visible = visible; } public boolean isVisible() { return visible; } public Rectangle getBounds() { return new Rectangle(x, y, width, height); }

587

public void keyPressed(KeyEvent e) { int key = e.getKeyCode(); if (key == KeyEvent.VK_SPACE) { fire(); } if (key == KeyEvent.VK_LEFT) { dx = -1; } if (key == KeyEvent.VK_RIGHT) { dx = 1; } if (key == KeyEvent.VK_UP) { dy = -1; } if (key == KeyEvent.VK_DOWN) { dy = 1; } } public void fire() { missiles.add(new Missile(x + width, y + height/2)); } public void keyReleased(KeyEvent e) { int key = e.getKeyCode(); if (key == KeyEvent.VK_LEFT) { dx = 0; } if (key == KeyEvent.VK_RIGHT) { dx = 0; } if (key == KeyEvent.VK_UP) { dy = 0; } if (key == KeyEvent.VK_DOWN) { dy = 0; } } }

This class represents a spacecraft.


public Rectangle getBounds() { return new Rectangle(x, y, width, height); }

The getBounds() method returns the bounds of the spacecraft image. We need them in collision detection.

588

Board.java
package collision; import import import import import import import import import import import java.awt.Color; java.awt.Font; java.awt.FontMetrics; java.awt.Graphics; java.awt.Graphics2D; java.awt.Rectangle; java.awt.Toolkit; java.awt.event.ActionEvent; java.awt.event.ActionListener; java.awt.event.KeyAdapter; java.awt.event.KeyEvent;

import java.util.ArrayList; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.Timer; public class Board extends JPanel implements ActionListener { private private private private private private Timer timer; Craft craft; ArrayList aliens; boolean ingame; int B_WIDTH; int B_HEIGHT;

private int[][] pos = { {2380, 29}, {2500, 59}, {1380, 89}, {780, 109}, {580, 139}, {680, 239}, {790, 259}, {760, 50}, {790, 150}, {980, 209}, {560, 45}, {510, 70}, {930, 159}, {590, 80}, {530, 60}, {940, 59}, {990, 30}, {920, 200}, {900, 259}, {660, 50}, {540, 90}, {810, 220}, {860, 20}, {740, 180}, {820, 128}, {490, 170}, {700, 30} }; public Board() { addKeyListener(new TAdapter()); setFocusable(true); setBackground(Color.BLACK); setDoubleBuffered(true); ingame = true; setSize(400, 300); craft = new Craft(); initAliens(); timer = new Timer(5, this); timer.start(); }

589

public void addNotify() { super.addNotify(); B_WIDTH = getWidth(); B_HEIGHT = getHeight(); } public void initAliens() { aliens = new ArrayList(); for (int i=0; i<pos.length; i++ ) { aliens.add(new Alien(pos[i][0], pos[i][1])); } } public void paint(Graphics g) { super.paint(g); if (ingame) { Graphics2D g2d = (Graphics2D)g; if (craft.isVisible()) g2d.drawImage(craft.getImage(), craft.getX(), craft.getY(), this); ArrayList ms = craft.getMissiles(); for (int i = 0; i < ms.size(); i++) { Missile m = (Missile)ms.get(i); g2d.drawImage(m.getImage(), m.getX(), m.getY(), this); } for (int i = 0; i < aliens.size(); i++) { Alien a = (Alien)aliens.get(i); if (a.isVisible()) g2d.drawImage(a.getImage(), a.getX(), a.getY(), this); } g2d.setColor(Color.WHITE); g2d.drawString("Aliens left: " + aliens.size(), 5, 15); } else { String msg = "Game Over"; Font small = new Font("Helvetica", Font.BOLD, 14); FontMetrics metr = this.getFontMetrics(small); g.setColor(Color.white); g.setFont(small); g.drawString(msg, (B_WIDTH - metr.stringWidth(msg)) / 2, B_HEIGHT / 2); } Toolkit.getDefaultToolkit().sync(); g.dispose(); } public void actionPerformed(ActionEvent e) {

590

if (aliens.size()==0) { ingame = false; } ArrayList ms = craft.getMissiles(); for (int i = 0; i < ms.size(); i++) { Missile m = (Missile) ms.get(i); if (m.isVisible()) m.move(); else ms.remove(i); } for (int i = 0; i < aliens.size(); i++) { Alien a = (Alien) aliens.get(i); if (a.isVisible()) a.move(); else aliens.remove(i); } craft.move(); checkCollisions(); repaint();

public void checkCollisions() { Rectangle r3 = craft.getBounds(); for (int j = 0; j<aliens.size(); j++) { Alien a = (Alien) aliens.get(j); Rectangle r2 = a.getBounds(); if (r3.intersects(r2)) { craft.setVisible(false); a.setVisible(false); ingame = false; } } ArrayList ms = craft.getMissiles(); for (int i = 0; i < ms.size(); i++) { Missile m = (Missile) ms.get(i); Rectangle r1 = m.getBounds(); for (int j = 0; j<aliens.size(); j++) { Alien a = (Alien) aliens.get(j); Rectangle r2 = a.getBounds(); if (r1.intersects(r2)) { m.setVisible(false); a.setVisible(false); }

} } }

private class TAdapter extends KeyAdapter {

591

public void keyReleased(KeyEvent e) { craft.keyReleased(e); } public void keyPressed(KeyEvent e) { craft.keyPressed(e); } } }

This is the Board class.


private int[][] pos = { {2380, 29}, {2500, 59}, {1380, 89}, {780, 109}, {580, 139}, {680, 239}, {790, 259}, {760, 50}, {790, 150}, {980, 209}, {560, 45}, {510, 70}, {930, 159}, {590, 80}, {530, 60}, {940, 59}, {990, 30}, {920, 200}, {900, 259}, {660, 50}, {540, 90}, {810, 220}, {860, 20}, {740, 180}, {820, 128}, {490, 170}, {700, 30} };

These are initial positions of alien ships.


for (int i = 0; i < aliens.size(); i++) { Alien a = (Alien)aliens.get(i); if (a.isVisible()) g2d.drawImage(a.getImage(), a.getX(), a.getY(), this); }

The paint() method draws all aliens from the aliens ArrayList. They are drawn only if they have not been previously destroyed. This is checked by the isVisible() method.
if (aliens.size()==0) { ingame = false; }

If we destroy all alien ships, the game is finished.


ArrayList ms = craft.getMissiles(); for (int i = 0; i < ms.size(); i++) { Missile m = (Missile) ms.get(i); Rectangle r1 = m.getBounds(); for (int j = 0; j<aliens.size(); j++) { Alien a = (Alien) aliens.get(j); Rectangle r2 = a.getBounds(); if (r1.intersects(r2)) { m.setVisible(false); a.setVisible(false); } }

592

Here we check, if any of the missiles intersects with any of the aliens. If they do, both the missile and the alien are destroyed. Alien.java
package collision; import java.awt.Image; import java.awt.Rectangle; import javax.swing.ImageIcon; public class Alien { private String craft = "alien.png"; private private private private private private int x; int y; int width; int height; boolean visible; Image image;

public Alien(int x, int y) { ImageIcon ii = new ImageIcon(this.getClass().getResource(craft)); image = ii.getImage(); width = image.getWidth(null); height = image.getHeight(null); visible = true; this.x = x; this.y = y; } public void move() { if (x < 0) x = 400; x -= 1; } public int getX() { return x; } public int getY() { return y; } public boolean isVisible() { return visible; } public void setVisible(Boolean visible) { this.visible = visible; } public Image getImage() { return image;

593

} public Rectangle getBounds() { return new Rectangle(x, y, width, height); }

This is the Alien class.


public void move() { if (x < 0) x = 400; x -= 1; }

Aliens return to the screen, after they have disappeared on the left. Missile.java
package collision; import java.awt.Image; import java.awt.Rectangle; import javax.swing.ImageIcon; public class Missile { private private boolean private int x, y; Image image; visible; int width, height;

private final int BOARD_WIDTH = 390; private final int MISSILE_SPEED = 2; public Missile(int x, int y) { ImageIcon ii = new ImageIcon(this.getClass().getResource("missile.png")); image = ii.getImage(); visible = true; width = image.getWidth(null); height = image.getHeight(null); this.x = x; this.y = y;

public Image getImage() { return image; } public int getX() { return x; } public int getY() { return y;

594

} public boolean isVisible() { return visible; } public void setVisible(Boolean visible) { this.visible = visible; } public Rectangle getBounds() { return new Rectangle(x, y, width, height); } public void move() { x += MISSILE_SPEED; if (x > BOARD_WIDTH) visible = false; }

This is the Missile class. Collision.java


package collision; import javax.swing.JFrame; public class Collision extends JFrame { public Collision() { add(new Board()); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize(400, 300); setLocationRelativeTo(null); setTitle("Collision"); setResizable(false); setVisible(true);

public static void main(String[] args) { new Collision(); } }

Finally, this is the main class.

595

Figure: Shooting aliens This chapter was about collision detection.

The Puzzle game


In this chapter, we will create a simple puzzle game.

Puzzle
We have an image of a Sid character from the Ice Age movie. It is cut into 12 pieces. The goal is to form the picture. Puzzle.java
import import import import import import import import import import import import import import java.awt.BorderLayout; java.awt.Dimension; java.awt.GridLayout; java.awt.Image; java.awt.event.ActionEvent; java.awt.event.ActionListener; java.awt.image.CropImageFilter; java.awt.image.FilteredImageSource; javax.swing.Box; javax.swing.ImageIcon; javax.swing.JButton; javax.swing.JFrame; javax.swing.JLabel; javax.swing.JPanel;

public class Puzzle extends JFrame implements ActionListener { private private private private JPanel centerPanel; JButton button; JLabel label; Image source;

596

private Image image; int[][] pos; int width, height; public Puzzle() { pos = new int[][] { {0, {3, {6, {9, 1, 2}, 4, 5}, 7, 8}, 10, 11}

};

centerPanel = new JPanel(); centerPanel.setLayout(new GridLayout(4, 4, 0, 0)); ImageIcon sid = new ImageIcon(Puzzle.class.getResource("icesid.jpg")); source = sid.getImage(); width = sid.getIconWidth(); height = sid.getIconHeight(); add(Box.createRigidArea(new Dimension(0, 5)), BorderLayout.NORTH); add(centerPanel, BorderLayout.CENTER); for ( int i = 0; i < 4; i++) { for ( int j = 0; j < 3; j++) { if ( j == 2 && i == 3) { label = new JLabel(""); centerPanel.add(label); } else { button = new JButton(); button.addActionListener(this); centerPanel.add(button); image = createImage(new FilteredImageSource(source.getSource(), new CropImageFilter(j*width/3, i*height/4, (width/3)+1, height/4))); button.setIcon(new ImageIcon(image)); } } } setSize(325, 275); setTitle("Puzzle"); setResizable(false); setLocationRelativeTo(null); setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); setVisible(true);

public static void main(String[] args) { new Puzzle(); }

597

public void actionPerformed(ActionEvent e) { JButton button = (JButton) e.getSource(); Dimension size = button.getSize(); int int int int int int int labelX = label.getX(); labelY = label.getY(); buttonX = button.getX(); buttonY = button.getY(); buttonPosX = buttonX / size.width; buttonPosY = buttonY / size.height; buttonIndex = pos[buttonPosY][buttonPosX];

if (labelX == buttonX && (labelY - buttonY) == size.height ) { int labelIndex = buttonIndex + 3; centerPanel.remove(buttonIndex); centerPanel.add(label, buttonIndex); centerPanel.add(button,labelIndex); centerPanel.validate();

if (labelX == buttonX && (labelY - buttonY) == -size.height ) { int labelIndex = buttonIndex - 3; centerPanel.remove(labelIndex); centerPanel.add(button,labelIndex); centerPanel.add(label, buttonIndex); centerPanel.validate(); } if (labelY == buttonY && (labelX - buttonX) == size.width ) { int labelIndex = buttonIndex + 1; centerPanel.remove(buttonIndex); centerPanel.add(label, buttonIndex); centerPanel.add(button,labelIndex); centerPanel.validate(); } if (labelY == buttonY && (labelX - buttonX) == -size.width ) { int labelIndex = buttonIndex - 1; centerPanel.remove(buttonIndex); centerPanel.add(label, labelIndex); centerPanel.add(button,labelIndex); centerPanel.validate(); } } }

The goal of this little game is to form the original picture. We move the buttons by clicking on them. Only buttons adjacent to the label can be moved.
pos = new int[][] {

598

{0, {3, {6, {9, };

1, 2}, 4, 5}, 7, 8}, 10, 11}

These are the positions of the image parts.


ImageIcon sid = new ImageIcon(Puzzle.class.getResource("icesid.jpg")); source = sid.getImage();

We use the ImageIcon class to load the image.


for ( int i = 0; i < 4; i++) { for ( int j = 0; j < 3; j++) { if ( j == 2 && i == 3) { label = new JLabel(""); centerPanel.add(label); } else { button = new JButton(); button.addActionListener(this); centerPanel.add(button); image = createImage(new FilteredImageSource(source.getSource(), new CropImageFilter(j*width/3, i*height/4, (width/3)+1, height/4))); button.setIcon(new ImageIcon(image)); } } }

The code creates 11 buttons and one label. We crop the image into pieces and place them on the buttons.
int int int int labelX = label.getX(); labelY = label.getY(); buttonX = button.getX(); buttonY = button.getY();

We get the x, y coordinates of the button that we hit and an empty label. The x, y coordinates are important in the logic of the program.
int buttonPosX = buttonX / size.width; int buttonPosY = buttonY / size.height; int buttonIndex = pos[buttonPosY][buttonPosX];

Here we get the index of the button in the two dimensional array of the button positions.
if (labelX == buttonX && (labelY - buttonY) == size.height ) { int labelIndex = buttonIndex + 3; centerPanel.remove(buttonIndex); centerPanel.add(label, buttonIndex); centerPanel.add(button,labelIndex); centerPanel.validate(); }

599

In this case, we check if we clicked on the button, that is right above the empty label. If it is above the label, they share the x coordinate. If the button is right above the label, the equation (labelY - buttonY) == size.height is true.

Figure: Puzzle This was a Puzzle game.

Snake
In this part of the Java 2D games tutorial, we will create a Java Snake game clone.

Snake
Snake is an older classic video game. It was first created in late 70s. Later it was brought to PCs. In this game the player controls a snake. The objective is to eat as many apples as possible. Each time the snake eats an apple, its body grows. The snake must avoid the walls and its own body. This game is sometimes called Nibbles.

Development
The size of each of the joints of a snake is 10px. The snake is controlled with the cursor keys. Initially the snake has three joints. The game is started by pressing one of the cursor keys. If the game is finished, we display Game Over message in the middle of the Board. Board.java
import import import import import import import package snake2; java.awt.Color; java.awt.Font; java.awt.FontMetrics; java.awt.Graphics; java.awt.Image; java.awt.Toolkit; java.awt.event.ActionEvent;

600

import java.awt.event.ActionListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import javax.swing.ImageIcon; import javax.swing.JPanel; import javax.swing.Timer; public class Board extends JPanel implements ActionListener { private private private private private private final final final final final final int int int int int int WIDTH = 300; HEIGHT = 300; DOT_SIZE = 10; ALL_DOTS = 900; RAND_POS = 29; DELAY = 140;

private int x[] = new int[ALL_DOTS]; private int y[] = new int[ALL_DOTS]; private int dots; private int apple_x; private int apple_y; private private private private private private private private private boolean boolean boolean boolean boolean Timer Image Image Image left = false; right = true; up = false; down = false; inGame = true;

timer; ball; apple; head;

public Board() { addKeyListener(new TAdapter()); setBackground(Color.black); ImageIcon iid = new ImageIcon(this.getClass().getResource("dot.png")); ball = iid.getImage(); ImageIcon iia = new ImageIcon(this.getClass().getResource("apple.png")); apple = iia.getImage(); ImageIcon iih = new ImageIcon(this.getClass().getResource("head.png")); head = iih.getImage(); setFocusable(true); initGame(); } public void initGame() {

601

dots = 3; for (int z = 0; z < dots; z++) { x[z] = 50 - z*10; y[z] = 50; } locateApple(); timer = new Timer(DELAY, this); timer.start();

public void paint(Graphics g) { super.paint(g); if (inGame) { g.drawImage(apple, apple_x, apple_y, this); for (int z = 0; z < dots; z++) { if (z == 0) g.drawImage(head, x[z], y[z], this); else g.drawImage(ball, x[z], y[z], this); } Toolkit.getDefaultToolkit().sync(); g.dispose(); } else { gameOver(g); }

public void gameOver(Graphics g) { String msg = "Game Over"; Font small = new Font("Helvetica", Font.BOLD, 14); FontMetrics metr = this.getFontMetrics(small); g.setColor(Color.white); g.setFont(small); g.drawString(msg, (WIDTH - metr.stringWidth(msg)) / 2, HEIGHT / 2);

public void checkApple() { if ((x[0] == apple_x) && (y[0] == apple_y)) { dots++; locateApple(); } } public void move() { for (int z = dots; z > 0; z--) { x[z] = x[(z - 1)];

602

y[z] = y[(z - 1)];

if (left) { x[0] -= DOT_SIZE; } if (right) { x[0] += DOT_SIZE; } if (up) { y[0] -= DOT_SIZE; } if (down) { y[0] += DOT_SIZE; } } public void checkCollision() { for (int z = dots; z > 0; z--) { if ((z > 4) && (x[0] == x[z]) && (y[0] == y[z])) { inGame = false; } } if (y[0] > HEIGHT) { inGame = false; } if (y[0] < 0) { inGame = false; } if (x[0] > WIDTH) { inGame = false; } if (x[0] < 0) { inGame = false; }

public void locateApple() { int r = (int) (Math.random() * RAND_POS); apple_x = ((r * DOT_SIZE)); r = (int) (Math.random() * RAND_POS); apple_y = ((r * DOT_SIZE)); } public void actionPerformed(ActionEvent e) { if (inGame) { checkApple(); checkCollision(); move(); }

603

repaint(); } private class TAdapter extends KeyAdapter { public void keyPressed(KeyEvent e) { int key = e.getKeyCode(); if ((key left up = down } == KeyEvent.VK_LEFT) && (!right)) { = true; false; = false;

if ((key == KeyEvent.VK_RIGHT) && (!left)) { right = true; up = false; down = false; } if ((key == KeyEvent.VK_UP) && (!down)) { up = true; right = false; left = false; } if ((key == KeyEvent.VK_DOWN) && (!up)) { down = true; right = false; left = false; } } } }

First we will define the constants used in our game.


private private private private private private final final final final final final int int int int int int WIDTH = 300; HEIGHT = 300; DOT_SIZE = 10; ALL_DOTS = 900; RAND_POS = 29; DELAY = 140;

The WIDTH and HEIGHT constants determine the size of the Board. The DOT_SIZE is the size of the apple and the dot of the snake. The ALL_DOTS constant defines the maximum number of possible dots on the Board. (900 = 300*300/10*10) The RAND_POS constant is used to calculate a random position of an apple. The DELAY constant determines the speed of the game.
private int x[] = new int[ALL_DOTS]; private int y[] = new int[ALL_DOTS];

These two arrays store x, y coordinates of all joints of a snake.

604

In the move() method we have the key algorithm of the game. To understand it, look at how the snake is moving. You control the head of the snake. You can change its direction with the cursor keys. The rest of the joints move one position up the chain. The second joint moves where the first was, the third joint where the second was etc.
for (int z = dots; z > 0; z--) { x[z] = x[(z - 1)]; y[z] = y[(z - 1)]; }

This code moves the joints up the chain.


if (left) { x[0] -= DOT_SIZE; }

Move the head to the left. In the checkCollision() method, we determine if the snake has hit itself or one of the walls.
for (int z = dots; z > 0; z--) { if ((z > 4) && (x[0] == x[z]) && (y[0] == y[z])) { inGame = false; } }

Finish the game, if the snake hits one of its joints with the head.
if (y[0] > HEIGHT) { inGame = false; }

Finish the game, if the snake hits the bottom of the Board. Snake.java
package snake2; import javax.swing.JFrame; public class Snake extends JFrame { public Snake() { add(new Board()); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize(320, 340); setLocationRelativeTo(null); setTitle("Snake"); setResizable(false); setVisible(true); }

605

public static void main(String[] args) { new Snake(); }

This is the main class.

Figure: Snake This was the Snake game.

The Breakout game


In this part of the Java 2D games tutorial we will create a simple Breakout game clone. The Breakout is an arcade game originally developed by Atari Inc. The game was created in 1976. In this game, the player moves a paddle on the screen and bounces a ball/balls. The objective is to destroy bricks in the top of the window.

Development
In our game, we have one paddle, one ball and 30 bricks. I have created an image for a ball, paddle and a brick in Inkscape. We use a timer to create a game cycle. We do not work with angles, we simply change directions. Top, bottom, left and right. I was inspired by the pybreakout game. It was developed in PyGame library by Nathan Dawson. The game is intentionally simple. There are no bonuses, levels, score. So that it is easier to understand. The game consists of seven files.

606

Commons.java
package breakout; public interface Commons { public static final int public static final int public static final int public static final int public static final int } WIDTH = 300; HEIGTH = 400; BOTTOM = 390; PADDLE_RIGHT = 250; BALL_RIGHT = 280;

The Commons.java file has some common constants. Sprite.java


package breakout; import java.awt.Image; import java.awt.Rectangle; public class Sprite { protected protected protected protected protected int x; int y; int width; int heigth; Image image;

public void setX(int x) { this.x = x; } public int getX() { return x; } public void setY(int y) { this.y = y; } public int getY() { return y; } public int getWidth() { return width; } public int getHeight() { return heigth; } Image getImage() { return image; } Rectangle getRect() { return new Rectangle(x, y,

607

} }

image.getWidth(null), image.getHeight(null));

The Sprite class is a base class for all objects on the Board. We put here all methods and variables that are in Ball, Brick and Paddle objects. Like getImage() or getX() methods. Brick.java
package breakout; import javax.swing.ImageIcon; public class Brick extends Sprite { String brickie = "../images/brickie.png"; boolean destroyed; public Brick(int x, int y) { this.x = x; this.y = y; ImageIcon ii = new ImageIcon(this.getClass().getResource(brickie)); image = ii.getImage(); width = image.getWidth(null); heigth = image.getHeight(null); destroyed = false; } public boolean isDestroyed() { return destroyed; } public void setDestroyed(boolean destroyed) { this.destroyed = destroyed; } }

This is the Brick class.


boolean destroyed;

In the destroyed variable we keep the state of a brick. Ball.java


package breakout; import javax.swing.ImageIcon; public class Ball extends Sprite implements Commons {

608

private int xdir; private int ydir; protected String ball = "../images/ball.png"; public Ball() { xdir = 1; ydir = -1; ImageIcon ii = new ImageIcon(this.getClass().getResource(ball)); image = ii.getImage(); width = image.getWidth(null); heigth = image.getHeight(null); resetState(); } public void move() { x += xdir; y += ydir; if (x == 0) { setXDir(1); } if (x == BALL_RIGHT) { setXDir(-1); } if (y == 0) { setYDir(1); }

public void resetState() { x = 230; y = 355; } public void setXDir(int x) { xdir = x; } public void setYDir(int y) { ydir = y; } public int getYDir() { return ydir; }

609

This is the Ball class.


public void move() { x += xdir; y += ydir; if (x == 0) { setXDir(1); } if (x == BALL_RIGHT) { setXDir(-1); } if (y == 0) { setYDir(1); } }

The move() method moves the ball on the Board. If the ball hits the borders, the directions are changed accordingly.
public { xdir = } public { ydir = } void setXDir(int x) x; void setYDir(int y) y;

These two methods are called, when the ball hits the paddle or a brick. Paddle.java
package breakout; import java.awt.event.KeyEvent; import javax.swing.ImageIcon; public class Paddle extends Sprite implements Commons { String paddle = "../images/paddle.png"; int dx; public Paddle() { ImageIcon ii = new ImageIcon(this.getClass().getResource(paddle)); image = ii.getImage(); width = image.getWidth(null); heigth = image.getHeight(null); resetState(); }

610

public void move() { x += dx; if (x <= 2) x = 2; if (x >= Commons.PADDLE_RIGHT) x = Commons.PADDLE_RIGHT; } public void keyPressed(KeyEvent e) { int key = e.getKeyCode(); if (key == KeyEvent.VK_LEFT) { dx = -2; } if (key == KeyEvent.VK_RIGHT) { dx = 2; }

public void keyReleased(KeyEvent e) { int key = e.getKeyCode(); if (key == KeyEvent.VK_LEFT) { dx = 0; } if (key == KeyEvent.VK_RIGHT) { dx = 0; }

public void resetState() { x = 200; y = 360; }

This is the Paddle class. It encapsulates the paddle object in the Breakout game. The paddle is controlled with left and right arrow keys. By pressing the arrow key, we set the direction variable. By releasing the arrow key, we set the dx variable to zero. This way the paddle stops moving. Board.java
package breakout; import import import import import import import import import java.awt.Color; java.awt.Font; java.awt.FontMetrics; java.awt.Graphics; java.awt.Image; java.awt.Point; java.awt.Toolkit; java.awt.event.KeyAdapter; java.awt.event.KeyEvent;

611

import java.util.Timer; import java.util.TimerTask; import javax.swing.JPanel; public class Board extends JPanel implements Commons { Image ii; Timer timer; String message = "Game Over"; Ball ball; Paddle paddle; Brick bricks[]; boolean ingame = true; int timerId; public Board() { addKeyListener(new TAdapter()); setFocusable(true); bricks = new Brick[30]; setDoubleBuffered(true); timer = new Timer(); timer.scheduleAtFixedRate(new ScheduleTask(), 1000, 10); } public void addNotify() { super.addNotify(); gameInit(); } public void gameInit() { ball = new Ball(); paddle = new Paddle(); int k = 0; for (int i = 0; i < 5; i++) { for (int j = 0; j < 6; j++) { bricks[k] = new Brick(j * 40 + 30, i * 10 + 50); k++; } } } public void paint(Graphics g) { super.paint(g); if (ingame) { g.drawImage(ball.getImage(), ball.getX(), ball.getY(), ball.getWidth(), ball.getHeight(), this); g.drawImage(paddle.getImage(), paddle.getX(), paddle.getY(), paddle.getWidth(), paddle.getHeight(), this); for (int i = 0; i < 30; i++) {

612

if (!bricks[i].isDestroyed()) g.drawImage(bricks[i].getImage(), bricks[i].getX(), bricks[i].getY(), bricks[i].getWidth(), bricks[i].getHeight(), this); } } else { Font font = new Font("Verdana", Font.BOLD, 18); FontMetrics metr = this.getFontMetrics(font); g.setColor(Color.BLACK); g.setFont(font); g.drawString(message, (Commons.WIDTH - metr.stringWidth(message)) / 2, Commons.WIDTH / 2);

Toolkit.getDefaultToolkit().sync(); g.dispose(); } private class TAdapter extends KeyAdapter { public void keyReleased(KeyEvent e) { paddle.keyReleased(e); } public void keyPressed(KeyEvent e) { paddle.keyPressed(e); }

class ScheduleTask extends TimerTask { public void run() { ball.move(); paddle.move(); checkCollision(); repaint(); } } public void stopGame() { ingame = false; timer.cancel(); } public void checkCollision() { if (ball.getRect().getMaxY() > Commons.BOTTOM) { stopGame(); } for (int i = 0, j = 0; i < 30; i++) { if (bricks[i].isDestroyed()) { j++;

613

} if (j == 30) { message = "Victory"; stopGame(); }

if ((ball.getRect()).intersects(paddle.getRect())) { int paddleLPos = (int)paddle.getRect().getMinX(); int ballLPos = (int)ball.getRect().getMinX(); int int int int first = paddleLPos + 8; second = paddleLPos + 16; third = paddleLPos + 24; fourth = paddleLPos + 32;

if (ballLPos < first) { ball.setXDir(-1); ball.setYDir(-1); } if (ballLPos >= first && ballLPos < second) { ball.setXDir(-1); ball.setYDir(-1 * ball.getYDir()); } if (ballLPos >= second && ballLPos < third) { ball.setXDir(0); ball.setYDir(-1); } if (ballLPos >= third && ballLPos < fourth) { ball.setXDir(1); ball.setYDir(-1 * ball.getYDir()); } if (ballLPos > fourth) { ball.setXDir(1); ball.setYDir(-1); } } for (int i = 0; i < 30; i++) { if ((ball.getRect()).intersects(bricks[i].getRect())) { int int int int ballLeft = (int)ball.getRect().getMinX(); ballHeight = (int)ball.getRect().getHeight(); ballWidth = (int)ball.getRect().getWidth(); ballTop = (int)ball.getRect().getMinY();

Point pointRight = new Point(ballLeft + ballWidth + 1, ballTop); Point pointLeft = new Point(ballLeft - 1, ballTop); Point pointTop = new Point(ballLeft, ballTop - 1); Point pointBottom = new Point(ballLeft, ballTop + ballHeight + 1);

614

if (!bricks[i].isDestroyed()) { if (bricks[i].getRect().contains(pointRight)) { ball.setXDir(-1); } else if (bricks[i].getRect().contains(pointLeft)) { ball.setXDir(1); } if (bricks[i].getRect().contains(pointTop)) { ball.setYDir(1); } else if (bricks[i].getRect().contains(pointBottom)) { ball.setYDir(-1); } bricks[i].setDestroyed(true); } } } } }

This is the Board class. Here we put the game logic.


public void gameInit() { ball = new Ball(); paddle = new Paddle(); int k = 0; for (int i = 0; i < 5; i++) { for (int j = 0; j < 6; j++) { bricks[k] = new Brick(j * 40 + 30, i * 10 + 50); k++; } }

In the gameInit() method we create a ball a paddle and 30 bricks.


if (ingame) { g.drawImage(ball.getImage(), ball.getX(), ball.getY(), ball.getWidth(), ball.getHeight(), this); g.drawImage(paddle.getImage(), paddle.getX(), paddle.getY(), paddle.getWidth(), paddle.getHeight(), this); for (int i = 0; i < 30; i++) { if (!bricks[i].isDestroyed()) g.drawImage(bricks[i].getImage(), bricks[i].getX(), bricks[i].getY(), bricks[i].getWidth(), bricks[i].getHeight(), this); } }

If the game is not finished, we draw the ball, the paddle and the bricks inside the paint event.
class ScheduleTask extends TimerTask {

615

public void run() { ball.move(); paddle.move(); checkCollision(); repaint(); }

The ScheduleTask is triggerd every 10 ms. In the run method, we move the ball and the paddle. We check for possible collisiont. And we repaint the screen.
if (ball.getRect().getMaxY() > Commons.BOTTOM) stopGame(); }

If the ball hits the bottom, we stop the game.


for (int i = 0, j = 0; i < 30; i++) { if (bricks[i].isDestroyed()) { j++; } if (j == 30) { message = "Victory"; stopGame(); } }

We check how many bricks are destroyed. If we destroyed all 30 bricks, we win the game.
if (ballLPos < first) { ball.setXDir(-1); ball.setYDir(-1); }

If the ball hits the first part of the paddle, we change the direction of the ball to the north-east.
if (bricks[i].getRect().contains(pointTop)) { ball.setYDir(1); }

If the ball hits the bottom of the brick, we change the y direction of the ball. It goes down. The autoMove() method is called each game cycle to move the ball on the screen. If it hists the boundaries, the ball direction changes. Breakout.java
package breakout; import javax.swing.JFrame; public class Breakout extends JFrame { public Breakout() { add(new Board());

616

setTitle("Breakout"); setDefaultCloseOperation(EXIT_ON_CLOSE); setSize(Commons.WIDTH, Commons.HEIGTH); setLocationRelativeTo(null); setIgnoreRepaint(true); setResizable(false); setVisible(true);

public static void main(String[] args) { new Breakout(); } }

And finally, this is the Breakout class, which has the entry main method.

Figure: The Breakout game This was the Breakout game.

Tetris
In this chapter, we will create a Tetris game clone in Java Swing. It is modified and simplified.

Tetris
The Tetris game is one of the most popular computer games ever created. The original game was designed and programmed by a Russian programmer Alexey Pajitnov in 1985. Since

617

then, Tetris is available on almost every computer platform in lots of variations. Even my mobile phone has a modified version of the Tetris game. Tetris is called a falling block puzzle game. In this game, we have seven different shapes called tetrominoes. S-shape, Z-shape, T-shape, L-shape, Line-shape, MirroredL-shape and a Square-shape. Each of these shapes is formed with four squares. The shapes are falling down the board. The object of the Tetris game is to move and rotate the shapes, so that they fit as much as possible. If we manage to form a row, the row is destroyed and we score. We play the tetris game until we top out.

Figure: Tetrominoes

The development
We do not have images for our tetris game, we draw the tetrominoes using Swing drawing API. Behind every computer game, there is a mathematical model. So it is in Tetris. Some ideas behind the game.

We use a Timer class to create a game cycle The tetrominoes are drawn The shapes move on a square by square basis (not pixel by pixel) Mathematically a board is a simple list of numbers

I have simplified the game a bit, so that it is easier to understand. The game starts immediately, after it is launched. We can pause the game by pressing the p key. The space key will drop the tetris piece immediately to the bottom. The d key will drop the piece one line down. (It can be used to speed up the falling a bit.) The game goes at constant speed, no acceleration is implemented. The score is the number of lines, that we have removed. Tetris.java
package tetris; import java.awt.BorderLayout; import javax.swing.JFrame; import javax.swing.JLabel; public class Tetris extends JFrame { JLabel statusbar;

618

public Tetris() { statusbar = new JLabel(" 0"); add(statusbar, BorderLayout.SOUTH); Board board = new Board(this); add(board); board.start(); setSize(200, 400); setTitle("Tetris"); setDefaultCloseOperation(EXIT_ON_CLOSE);

public JLabel getStatusBar() { return statusbar; } public static void main(String[] args) { Tetris game = new Tetris(); game.setLocationRelativeTo(null); game.setVisible(true); } }

In the Tetris.java file, we set up the game. We create a board on which we play the game. We create a statusbar.
board.start();

The start() method starts the Tetris game. Immediately, after the window appears on the screen. Shape.java
package tetris; import java.util.Random; import java.lang.Math; public class Shape { enum Tetrominoes { NoShape, ZShape, SShape, LineShape, TShape, SquareShape, LShape, MirroredLShape }; private Tetrominoes pieceShape; private int coords[][]; private int[][][] coordsTable; public Shape() { coords = new int[4][2]; setShape(Tetrominoes.NoShape); } public void setShape(Tetrominoes shape) {

619

coordsTable = new int[][][] { { { 0, 0 }, { 0, 0 }, { 0, 0 }, { { 0, -1 }, { 0, 0 }, { -1, 0 }, { { 0, -1 }, { 0, 0 }, { 1, 0 }, { { 0, -1 }, { 0, 0 }, { 0, 1 }, { { -1, 0 }, { 0, 0 }, { 1, 0 }, { { 0, 0 }, { 1, 0 }, { 0, 1 }, { { -1, -1 }, { 0, -1 }, { 0, 0 }, { { 1, -1 }, { 0, -1 }, { 0, 0 }, };

{ { { { { { { {

0, 0 } }, -1, 1 } }, 1, 1 } }, 0, 2 } }, 0, 1 } }, 1, 1 } }, 0, 1 } }, 0, 1 } }

for (int i = 0; i < 4 ; i++) { for (int j = 0; j < 2; ++j) { coords[i][j] = coordsTable[shape.ordinal()][i][j]; } } pieceShape = shape; } private void setX(int index, int private void setY(int index, int public int x(int index) { return public int y(int index) { return public Tetrominoes getShape() { x) { coords[index][0] = x; } y) { coords[index][1] = y; } coords[index][0]; } coords[index][1]; } return pieceShape; }

public void setRandomShape() { Random r = new Random(); int x = Math.abs(r.nextInt()) % 7 + 1; Tetrominoes[] values = Tetrominoes.values(); setShape(values[x]); } public int minX() { int m = coords[0][0]; for (int i=0; i < 4; i++) { m = Math.min(m, coords[i][0]); } return m; } public int minY() { int m = coords[0][1]; for (int i=0; i < 4; i++) { m = Math.min(m, coords[i][1]); } return m; } public Shape rotateLeft() { if (pieceShape == Tetrominoes.SquareShape) return this; Shape result = new Shape(); result.pieceShape = pieceShape;

620

for (int i = 0; i < 4; ++i) { result.setX(i, y(i)); result.setY(i, -x(i)); } return result; } public Shape rotateRight() { if (pieceShape == Tetrominoes.SquareShape) return this; Shape result = new Shape(); result.pieceShape = pieceShape; for (int i = 0; i < 4; ++i) { result.setX(i, -y(i)); result.setY(i, x(i)); } return result;

} }

The Shape class provides information about a tetris piece.


enum Tetrominoes { NoShape, ZShape, SShape, LineShape, TShape, SquareShape, LShape, MirroredLShape };

The Tetrominoes enum holds all seven tetris shapes. Plus the empty shape called here NoShape.
public Shape() { coords = new int[4][2]; setShape(Tetrominoes.NoShape); }

This is the constructor of the Shape class. The coords array holds the actual coordinates of a Tetris piece.
coordsTable = new int[][][] { { { 0, 0 }, { 0, 0 }, { 0, 0 }, { { 0, -1 }, { 0, 0 }, { -1, 0 }, { { 0, -1 }, { 0, 0 }, { 1, 0 }, { { 0, -1 }, { 0, 0 }, { 0, 1 }, { { -1, 0 }, { 0, 0 }, { 1, 0 }, { { 0, 0 }, { 1, 0 }, { 0, 1 }, { { -1, -1 }, { 0, -1 }, { 0, 0 }, { { 1, -1 }, { 0, -1 }, { 0, 0 }, }; { { { { { { { { 0, 0 } }, -1, 1 } }, 1, 1 } }, 0, 2 } }, 0, 1 } }, 1, 1 } }, 0, 1 } }, 0, 1 } }

The coordsTable array holds all possible coordinate values of our tetris pieces. This is a template from which all pieces take their coordiate values.
for (int i = 0; i < 4 ; i++) { for (int j = 0; j < 2; ++j) {

621

} }

coords[i][j] = coordsTable[shape.ordinal()][i][j];

Here we put one row of the coordiate values from the coordsTable to a coords array of a tetris piece. Note the use of the ordinal() method. In C++, an enum type is esencially an integer. Unlike in C++, Java enums are full classes. And the ordinal() method returns the current position of the enum type in the enum object. The following image will help understand the coordinate values a bit more. The coords array saves the coordinates of the tetris piece. For example, numbers { 0, -1 }, { 0, 0 }, { -1, 0 }, { -1, 1 } , represent a rotated S-shape. The following diagram illustrates the shape.

Figure: Coordinates

public Shape rotateLeft() { if (pieceShape == Tetrominoes.SquareShape) return this; Shape result = new Shape(); result.pieceShape = pieceShape; for (int i = 0; i < 4; ++i) { result.setX(i, y(i)); result.setY(i, -x(i)); } return result; }

This code rotates the piece to the left. The square does not have to be rotated. That's why we simply return the reference to the current object. Looking at the previous image will help to understand the rotation. Board.java
import import import import import import package tetris; java.awt.Color; java.awt.Dimension; java.awt.Graphics; java.awt.event.ActionEvent; java.awt.event.ActionListener; java.awt.event.KeyAdapter;

622

import java.awt.event.KeyEvent; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.Timer; import tetris.Shape.Tetrominoes; public class Board extends JPanel implements ActionListener { final int BoardWidth = 10; final int BoardHeight = 22; Timer timer; boolean isFallingFinished = false; boolean isStarted = false; boolean isPaused = false; int numLinesRemoved = 0; int curX = 0; int curY = 0; JLabel statusbar; Shape curPiece; Tetrominoes[] board;

public Board(Tetris parent) { setFocusable(true); curPiece = new Shape(); timer = new Timer(400, this); timer.start(); statusbar = parent.getStatusBar(); board = new Tetrominoes[BoardWidth * BoardHeight]; addKeyListener(new TAdapter()); clearBoard();

public void actionPerformed(ActionEvent e) { if (isFallingFinished) { isFallingFinished = false; newPiece(); } else { oneLineDown(); } } int squareWidth() { return (int) getSize().getWidth() / BoardWidth; } int squareHeight() { return (int) getSize().getHeight() / BoardHeight; } Tetrominoes shapeAt(int x, int y) { return board[(y * BoardWidth) + x]; } public void start() { if (isPaused)

623

return; isStarted = true; isFallingFinished = false; numLinesRemoved = 0; clearBoard(); newPiece(); timer.start();

private void pause() { if (!isStarted) return; isPaused = !isPaused; if (isPaused) { timer.stop(); statusbar.setText("paused"); } else { timer.start(); statusbar.setText(String.valueOf(numLinesRemoved)); } repaint();

public void paint(Graphics g) { super.paint(g); Dimension size = getSize(); int boardTop = (int) size.getHeight() - BoardHeight * squareHeight(); for (int i = 0; i < BoardHeight; ++i) { for (int j = 0; j < BoardWidth; ++j) { Tetrominoes shape = shapeAt(j, BoardHeight - i - 1); if (shape != Tetrominoes.NoShape) drawSquare(g, 0 + j * squareWidth(), boardTop + i * squareHeight(), shape); } } if (curPiece.getShape() != Tetrominoes.NoShape) { for (int i = 0; i < 4; ++i) { int x = curX + curPiece.x(i); int y = curY - curPiece.y(i); drawSquare(g, 0 + x * squareWidth(), boardTop + (BoardHeight - y - 1) * squareHeight(), curPiece.getShape()); } } } private void dropDown() { int newY = curY; while (newY > 0) {

624

} pieceDropped();

if (!tryMove(curPiece, curX, newY - 1)) break; --newY;

private void oneLineDown() { if (!tryMove(curPiece, curX, curY - 1)) pieceDropped(); } private void clearBoard() { for (int i = 0; i < BoardHeight * BoardWidth; ++i) board[i] = Tetrominoes.NoShape; } private void pieceDropped() { for (int i = 0; i < 4; ++i) { int x = curX + curPiece.x(i); int y = curY - curPiece.y(i); board[(y * BoardWidth) + x] = curPiece.getShape(); } removeFullLines(); if (!isFallingFinished) newPiece(); } private void newPiece() { curPiece.setRandomShape(); curX = BoardWidth / 2 + 1; curY = BoardHeight - 1 + curPiece.minY(); if (!tryMove(curPiece, curX, curY)) { curPiece.setShape(Tetrominoes.NoShape); timer.stop(); isStarted = false; statusbar.setText("game over"); } } private boolean tryMove(Shape newPiece, int newX, int newY) { for (int i = 0; i < 4; ++i) { int x = newX + newPiece.x(i); int y = newY - newPiece.y(i); if (x < 0 || x >= BoardWidth || y < 0 || y >= BoardHeight) return false; if (shapeAt(x, y) != Tetrominoes.NoShape) return false; } curPiece = newPiece; curX = newX;

625

curY = newY; repaint(); return true;

private void removeFullLines() { int numFullLines = 0; for (int i = BoardHeight - 1; i >= 0; --i) { boolean lineIsFull = true; for (int j = 0; j < BoardWidth; ++j) { if (shapeAt(j, i) == Tetrominoes.NoShape) { lineIsFull = false; break; } } if (lineIsFull) { ++numFullLines; for (int k = i; k < BoardHeight - 1; ++k) { for (int j = 0; j < BoardWidth; ++j) board[(k * BoardWidth) + j] = shapeAt(j, k + 1); } } } if (numFullLines > 0) { numLinesRemoved += numFullLines; statusbar.setText(String.valueOf(numLinesRemoved)); isFallingFinished = true; curPiece.setShape(Tetrominoes.NoShape); repaint(); }

private void drawSquare(Graphics g, int x, int y, Tetrominoes shape) { Color colors[] = { new Color(0, 0, 0), new Color(204, 102, 102), new Color(102, 204, 102), new Color(102, 102, 204), new Color(204, 204, 102), new Color(204, 102, 204), new Color(102, 204, 204), new Color(218, 170, 0) }; Color color = colors[shape.ordinal()]; g.setColor(color); g.fillRect(x + 1, y + 1, squareWidth() - 2, squareHeight() - 2); g.setColor(color.brighter()); g.drawLine(x, y + squareHeight() - 1, x, y); g.drawLine(x, y, x + squareWidth() - 1, y); g.setColor(color.darker()); g.drawLine(x + 1, y + squareHeight() - 1, x + squareWidth() - 1, y + squareHeight() - 1); g.drawLine(x + squareWidth() - 1, y + squareHeight() - 1, x + squareWidth() - 1, y + 1);

626

} class TAdapter extends KeyAdapter { public void keyPressed(KeyEvent e) { if (!isStarted || curPiece.getShape() == Tetrominoes.NoShape) { } int keycode = e.getKeyCode(); if (keycode == 'p' || keycode == 'P') { pause(); return; } if (isPaused) return; switch (keycode) { case KeyEvent.VK_LEFT: tryMove(curPiece, curX - 1, curY); break; case KeyEvent.VK_RIGHT: tryMove(curPiece, curX + 1, curY); break; case KeyEvent.VK_DOWN: tryMove(curPiece.rotateRight(), curX, curY); break; case KeyEvent.VK_UP: tryMove(curPiece.rotateLeft(), curX, curY); break; case KeyEvent.VK_SPACE: dropDown(); break; case 'd': oneLineDown(); break; case 'D': oneLineDown(); break; } } } } return;

Finally, we have the Board.java file. This is where the game logic is located.
... isFallingFinished = false; isStarted = false; isPaused = false; numLinesRemoved = 0; curX = 0; curY = 0; ...

627

We initialize some important variables. The isFallingFinished variable determines, if the tetris shape has finished falling and we then need to create a new shape. The numLinesRemoved counts the number of lines, we have removed so far. The curX and curY variables determine the actual position of the falling tetris shape.
setFocusable(true);

We must explicitly call the setFocusable() method. From now, the board has the keyboard input.
timer = new Timer(400, this); timer.start();

Timer object fires one or more action events after a specified delay. In our case, the timer calls the actionPerformed() method each 400 ms.
public void actionPerformed(ActionEvent e) { if (isFallingFinished) { isFallingFinished = false; newPiece(); } else { oneLineDown(); } }

The actionPerformed() method checks if the falling has finished. If so, a new piece is created. If not, the falling tetris piece goes one line down. Inside the paint() method, we draw the all objects on the board. The painting has two steps.
for (int i = 0; i < BoardHeight; ++i) { for (int j = 0; j < BoardWidth; ++j) { Tetrominoes shape = shapeAt(j, BoardHeight - i - 1); if (shape != Tetrominoes.NoShape) drawSquare(g, 0 + j * squareWidth(), boardTop + i * squareHeight(), shape); } }

In the first step we paint all the shapes, or remains of the shapes, that have been dropped to the bottom of the board. All the squares are rememberd in the board array. We access it using the shapeAt() method.
if (curPiece.getShape() != Tetrominoes.NoShape) { for (int i = 0; i < 4; ++i) { int x = curX + curPiece.x(i); int y = curY - curPiece.y(i); drawSquare(g, 0 + x * squareWidth(), boardTop + (BoardHeight - y - 1) * squareHeight(), curPiece.getShape()); } }

In the second step, we paint the actual falling piece.

628

private void dropDown() { int newY = curY; while (newY > 0) { if (!tryMove(curPiece, curX, newY - 1)) break; --newY; } pieceDropped(); }

If we press the space key, the piece is dropped to the bottom. We simply try to drop the piece one line down until it reaches the bottom or the top of another fallen tetris piece.
private void clearBoard() { for (int i = 0; i < BoardHeight * BoardWidth; ++i) board[i] = Tetrominoes.NoShape; }

The clearBoard() method fills the board with empty NoSpapes. This is later used at collision detection.
private void pieceDropped() { for (int i = 0; i < 4; ++i) { int x = curX + curPiece.x(i); int y = curY - curPiece.y(i); board[(y * BoardWidth) + x] = curPiece.getShape(); } removeFullLines(); if (!isFallingFinished) newPiece();

The pieceDropped() method puts the falling piece into the board array. Once again, the board holds all the squares of the pieces and remains of the pieces that has finished falling. When the piece has finished falling, it is time to check, if we can remove some lines off the board. This is the job of the removeFullLines() method. Then we create a new piece. More precisely, we try to create a new piece.
private void newPiece() { curPiece.setRandomShape(); curX = BoardWidth / 2 + 1; curY = BoardHeight - 1 + curPiece.minY(); if (!tryMove(curPiece, curX, curY)) { curPiece.setShape(Tetrominoes.NoShape); timer.stop(); isStarted = false; statusbar.setText("game over"); } }

629

The newPiece() method creates a new tetris piece. The piece gets a new random shape. Then we compute the initial curX and curY values. If we cannot move to the initial positions, the game is over. We top out. The timer is stopped. We put game over string on the statusbar.
private boolean tryMove(Shape newPiece, int newX, int newY) { for (int i = 0; i < 4; ++i) { int x = newX + newPiece.x(i); int y = newY - newPiece.y(i); if (x < 0 || x >= BoardWidth || y < 0 || y >= BoardHeight) return false; if (shapeAt(x, y) != Tetrominoes.NoShape) return false; } curPiece = newPiece; curX = newX; curY = newY; repaint(); return true; }

The tryMove() method tries to move the tetris piece. The method returns false, if it has reached the board boundaries or it is adjacent to the already fallen tetris pieces.
for (int i = BoardHeight - 1; i >= 0; --i) { boolean lineIsFull = true; for (int j = 0; j < BoardWidth; ++j) { if (shapeAt(j, i) == Tetrominoes.NoShape) { lineIsFull = false; break; } } if (lineIsFull) { ++numFullLines; for (int k = i; k < BoardHeight - 1; ++k) { for (int j = 0; j < BoardWidth; ++j) board[(k * BoardWidth) + j] = shapeAt(j, k + 1); } } }

Inside the removeFullLines() method, we check if there is any full row among all rows in the board. If there is at least one full line, it is removed. After finding a full line we increase the counter. We move all the lines above the full row one line down. This way we destroy the full line. Notice, that in our Tetris game, we use so called naive gravity. This means, that the squares may be left floating above empty gaps. Every tetris piece has four squares. Each of the squares is drawn with the drawSquare() method. Tetris pieces have different colors.
g.setColor(color.brighter()); g.drawLine(x, y + squareHeight() - 1, x, y); g.drawLine(x, y, x + squareWidth() - 1, y);

630

The left and top sides of a square are drawn with a brighter color. Similarly, the bottom and right sides are drawn with darker colors. This is to simulate a 3D edge. We control the game with a keyboard. The control mechanism is implemented with a KeyAdapter. This is an inner class that overrides the keyPressed() method.
case KeyEvent.VK_RIGHT: tryMove(curPiece, curX + 1, curY); break;

If we pressed the left arrow key, we try to move the falling piece one square to the left.

Figure: Tetris This was the Tetris game.

Pacman
In this part of the Java 2D games tutorial we will create a simple Pacman game clone. Pacman is an arcade game originally developed by a Japanese company Namco in 1980. Pacman became one of the most popular arcade games ever.

Development
The following code example is a remake of a Pacman game by Brian Postma available at javaboutique. I have modified and simplified the code. So that it is easier to understand.

631

The goal of the game is to collect all the points in the maze and avoid the ghosts. The pacman is animated in two ways. His position in the maze and his body. We animate his body with four images, depending on the direction. The animation is used to create the illusion of pacman opening and closing his mouth. The maze consists of 15 x 15 squares. The structure of the maze is based on a simple array of integers. Pacman has three lives. We also count the score. The game consists of two files. Board.java
package pacman; import import import import import import import import import import import import import import java.awt.BasicStroke; java.awt.Color; java.awt.Dimension; java.awt.Event; java.awt.Font; java.awt.FontMetrics; java.awt.Graphics; java.awt.Graphics2D; java.awt.Image; java.awt.Toolkit; java.awt.event.ActionEvent; java.awt.event.ActionListener; java.awt.event.KeyAdapter; java.awt.event.KeyEvent;

import javax.swing.ImageIcon; import javax.swing.JPanel; import javax.swing.Timer; public class Board extends JPanel implements ActionListener { Dimension d; Font smallfont = new Font("Helvetica", Font.BOLD, 14); FontMetrics fmsmall, fmlarge; Image ii; Color dotcolor = new Color(192, 192, 0); Color mazecolor; boolean ingame = false; boolean dying = false; final final final final final final final int int int int int int int int int int int int int blocksize = 24; nrofblocks = 15; scrsize = nrofblocks * blocksize; pacanimdelay = 2; pacmananimcount = 4; maxghosts = 12; pacmanspeed = 6;

pacanimcount = pacanimdelay; pacanimdir = 1; pacmananimpos = 0; nrofghosts = 6; pacsleft, score; deathcounter;

632

int[] dx, dy; int[] ghostx, ghosty, ghostdx, ghostdy, ghostspeed; Image Image Image Image ghost; pacman1, pacman2up, pacman2left, pacman2right, pacman2down; pacman3up, pacman3down, pacman3left, pacman3right; pacman4up, pacman4down, pacman4left, pacman4right;

int pacmanx, pacmany, pacmandx, pacmandy; int reqdx, reqdy, viewdx, viewdy; final { 19, 21, 21, 21, 17, 17, 25, 1, 1, 1, 1, 1, 1, 1, 9, short leveldata[] = 26, 26, 26, 18, 18, 0, 0, 0, 17, 16, 0, 0, 0, 17, 16, 0, 0, 0, 17, 16, 18, 18, 18, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 24, 24, 17, 16, 20, 0, 0, 17, 16, 16, 18, 18, 17, 16, 16, 16, 16, 17, 16, 16, 16, 16, 17, 16, 16, 16, 16, 17, 16, 16, 16, 16, 25, 24, 24, 24, 24, 8, 8, 8, 8, 8, 18, 16, 16, 16, 20, 20, 28, 0, 22, 20, 20, 16, 16, 24, 8, 18, 16, 16, 24, 0, 0, 0, 0, 0, 0, 0, 18, 16, 24, 8, 18, 16, 16, 16, 17, 17, 25, 0, 19, 17, 17, 16, 16, 24, 8, 18, 16, 16, 16, 16, 16, 24, 0, 18, 16, 16, 16, 16, 24, 8, 18, 16, 16, 16, 16, 16, 24, 0, 18, 16, 16, 16, 16, 16, 25, 18, 16, 16, 16, 16, 16, 16, 17, 16, 16, 16, 16, 16, 16, 24, 18, 16, 16, 16, 16, 16, 20, 20, 20, 20, 20, 20, 20, 16, 24, 18, 16, 16, 16, 16, 24, 0, 0, 0, 0, 0, 0, 0, 18, 24, 22, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21, 20, 28 };

final int validspeeds[] = { 1, 2, 3, 4, 6, 8 }; final int maxspeed = 6; int currentspeed = 3; short[] screendata; Timer timer; public Board() { GetImages(); addKeyListener(new TAdapter()); screendata = new short[nrofblocks * nrofblocks]; mazecolor = new Color(5, 100, 5); setFocusable(true); d = new Dimension(400, 400); setBackground(Color.black); setDoubleBuffered(true); ghostx = new int[maxghosts]; ghostdx = new int[maxghosts]; ghosty = new int[maxghosts]; ghostdy = new int[maxghosts]; ghostspeed = new int[maxghosts]; dx = new int[4]; dy = new int[4]; timer = new Timer(40, this); timer.start(); }

633

public void addNotify() { super.addNotify(); GameInit(); } public void DoAnim() { pacanimcount--; if (pacanimcount <= 0) { pacanimcount = pacanimdelay; pacmananimpos = pacmananimpos + pacanimdir; if (pacmananimpos == (pacmananimcount - 1) || pacmananimpos == } } public void PlayGame(Graphics2D g2d) { if (dying) { Death(); } else { MovePacMan(); DrawPacMan(g2d); moveGhosts(g2d); CheckMaze(); } } public void ShowIntroScreen(Graphics2D g2d) { g2d.setColor(new Color(0, 32, 48)); g2d.fillRect(50, scrsize / 2 - 30, scrsize - 100, 50); g2d.setColor(Color.white); g2d.drawRect(50, scrsize / 2 - 30, scrsize - 100, 50); String s = "Press s to start."; Font small = new Font("Helvetica", Font.BOLD, 14); FontMetrics metr = this.getFontMetrics(small); g2d.setColor(Color.white); g2d.setFont(small); g2d.drawString(s, (scrsize - metr.stringWidth(s)) / 2, scrsize / } public void DrawScore(Graphics2D g) { int i; String s; g.setFont(smallfont); g.setColor(new Color(96, 128, 255)); s = "Score: " + score; g.drawString(s, scrsize / 2 + 96, scrsize + 16); for (i = 0; i < pacsleft; i++) { g.drawImage(pacman3left, i * 28 + 8, scrsize + 1, this); } } pacanimdir = -pacanimdir;

0)

2);

634

public void CheckMaze() { short i = 0; boolean finished = true; while (i < nrofblocks * nrofblocks && finished) { if ((screendata[i] & 48) != 0) finished = false; i++; } if (finished) { score += 50; if (nrofghosts < maxghosts) nrofghosts++; if (currentspeed < maxspeed) currentspeed++; LevelInit(); } }

public void Death() { pacsleft--; if (pacsleft == 0) ingame = false; LevelContinue();

public void moveGhosts(Graphics2D g2d) { short i; int pos; int count; for (i = 0; i < nrofghosts; i++) { if (ghostx[i] % blocksize == 0 && ghosty[i] % blocksize == 0) { pos = ghostx[i] / blocksize + nrofblocks * (int)(ghosty[i] / blocksize); count = 0; if ((screendata[pos] dx[count] = -1; dy[count] = 0; count++; } if ((screendata[pos] dx[count] = 0; dy[count] = -1; count++; } if ((screendata[pos] dx[count] = 1; dy[count] = 0; count++; } if ((screendata[pos] dx[count] = 0; dy[count] = 1; & 1) == 0 && ghostdx[i] != 1) {

& 2) == 0 && ghostdy[i] != 1) {

& 4) == 0 && ghostdx[i] != -1) {

& 8) == 0 && ghostdy[i] != -1) {

635

count++;

if (count == 0) { if ((screendata[pos] & 15) == 15) { ghostdx[i] = 0; ghostdy[i] = 0; } else { ghostdx[i] = -ghostdx[i]; ghostdy[i] = -ghostdy[i]; } } else { count = (int)(Math.random() * count); if (count > 3) count = 3; ghostdx[i] = dx[count]; ghostdy[i] = dy[count]; } } ghostx[i] = ghostx[i] + (ghostdx[i] * ghostspeed[i]); ghosty[i] = ghosty[i] + (ghostdy[i] * ghostspeed[i]); DrawGhost(g2d, ghostx[i] + 1, ghosty[i] + 1); if (pacmanx > (ghostx[i] - 12) && pacmanx < (ghostx[i] + 12) && pacmany > (ghosty[i] - 12) && pacmany < (ghosty[i] + 12) && ingame) { dying = true; deathcounter = 64; } } }

public void DrawGhost(Graphics2D g2d, int x, int y) { g2d.drawImage(ghost, x, y, this); } public void MovePacMan() { int pos; short ch; if (reqdx == -pacmandx && reqdy == -pacmandy) { pacmandx = reqdx; pacmandy = reqdy; viewdx = pacmandx; viewdy = pacmandy; } if (pacmanx % blocksize == 0 && pacmany % blocksize == 0) { pos = pacmanx / blocksize + nrofblocks * (int)(pacmany / blocksize); ch = screendata[pos]; if ((ch & 16) != 0) { screendata[pos] = (short)(ch & 15); score++; }

636

if (reqdx != 0 || reqdy != 0) { if (!((reqdx == -1 && reqdy == 0 && (ch & 1) != 0) || (reqdx == 1 && reqdy == 0 && (ch & 4) != 0) || (reqdx == 0 && reqdy == -1 && (ch & 2) != 0) || (reqdx == 0 && reqdy == 1 && (ch & 8) != 0))) { pacmandx = reqdx; pacmandy = reqdy; viewdx = pacmandx; viewdy = pacmandy; } } // Check for standstill if ((pacmandx == -1 && pacmandy == 0 && (ch & 1) != 0) || (pacmandx == 1 && pacmandy == 0 && (ch & 4) != 0) || (pacmandx == 0 && pacmandy == -1 && (ch & 2) != 0) || (pacmandx == 0 && pacmandy == 1 && (ch & 8) != 0)) { pacmandx = 0; pacmandy = 0; } } pacmanx = pacmanx + pacmanspeed * pacmandx; pacmany = pacmany + pacmanspeed * pacmandy;

public void DrawPacMan(Graphics2D g2d) { if (viewdx == -1) DrawPacManLeft(g2d); else if (viewdx == 1) DrawPacManRight(g2d); else if (viewdy == -1) DrawPacManUp(g2d); else DrawPacManDown(g2d); } public void DrawPacManUp(Graphics2D g2d) switch (pacmananimpos) { case 1: g2d.drawImage(pacman2up, pacmanx break; case 2: g2d.drawImage(pacman3up, pacmanx break; case 3: g2d.drawImage(pacman4up, pacmanx break; default: g2d.drawImage(pacman1, pacmanx + break; } } { + 1, pacmany + 1, this); + 1, pacmany + 1, this); + 1, pacmany + 1, this); 1, pacmany + 1, this);

public void DrawPacManDown(Graphics2D g2d) { switch (pacmananimpos) { case 1: g2d.drawImage(pacman2down, pacmanx + 1, pacmany + 1, this); break; case 2:

637

g2d.drawImage(pacman3down, pacmanx + 1, pacmany + 1, this); break; case 3: g2d.drawImage(pacman4down, pacmanx + 1, pacmany + 1, this); break; default: g2d.drawImage(pacman1, pacmanx + 1, pacmany + 1, this); break; }

public void DrawPacManLeft(Graphics2D g2d) { switch (pacmananimpos) { case 1: g2d.drawImage(pacman2left, pacmanx + 1, pacmany + 1, this); break; case 2: g2d.drawImage(pacman3left, pacmanx + 1, pacmany + 1, this); break; case 3: g2d.drawImage(pacman4left, pacmanx + 1, pacmany + 1, this); break; default: g2d.drawImage(pacman1, pacmanx + 1, pacmany + 1, this); break; } } public void DrawPacManRight(Graphics2D g2d) switch (pacmananimpos) { case 1: g2d.drawImage(pacman2right, pacmanx break; case 2: g2d.drawImage(pacman3right, pacmanx break; case 3: g2d.drawImage(pacman4right, pacmanx break; default: g2d.drawImage(pacman1, pacmanx + 1, break; } } public void DrawMaze(Graphics2D g2d) { short i = 0; int x, y; for (y = 0; y < scrsize; y += blocksize) { for (x = 0; x < scrsize; x += blocksize) { g2d.setColor(mazecolor); g2d.setStroke(new BasicStroke(2)); if ((screendata[i] & 1) != 0) // draws left { g2d.drawLine(x, y, x, y + blocksize - 1); } { + 1, pacmany + 1, this); + 1, pacmany + 1, this); + 1, pacmany + 1, this); pacmany + 1, this);

638

if ((screendata[i] & 2) != 0) // draws top { g2d.drawLine(x, y, x + blocksize - 1, y); } if ((screendata[i] & 4) != 0) // draws right { g2d.drawLine(x + blocksize - 1, y, x + blocksize - 1, y + blocksize - 1); } if ((screendata[i] & 8) != 0) // draws bottom { g2d.drawLine(x, y + blocksize - 1, x + blocksize - 1, y + blocksize - 1); } if ((screendata[i] & 16) != 0) // draws point { g2d.setColor(dotcolor); g2d.fillRect(x + 11, y + 11, 2, 2); } i++; } } public void GameInit() { pacsleft = 3; score = 0; LevelInit(); nrofghosts = 6; currentspeed = 3; } public void LevelInit() { int i; for (i = 0; i < nrofblocks * nrofblocks; i++) screendata[i] = leveldata[i]; } LevelContinue(); }

public void LevelContinue() { short i; int dx = 1; int random; for (i = 0; i < nrofghosts; i++) { ghosty[i] = 4 * blocksize; ghostx[i] = 4 * blocksize; ghostdy[i] = 0; ghostdx[i] = dx; dx = -dx; random = (int)(Math.random() * (currentspeed + 1)); if (random > currentspeed) random = currentspeed; ghostspeed[i] = validspeeds[random]; } pacmanx = 7 * blocksize; pacmany = 11 * blocksize;

639

pacmandx = 0; pacmandy = 0; reqdx = 0; reqdy = 0; viewdx = -1; viewdy = 0; dying = false;

public void GetImages() { ghost = new ImageIcon(Board.class.getResource("../pacpix/ghost.png")).getImage(); pacman1 = new ImageIcon(Board.class.getResource("../pacpix/pacman.png")).getImage(); pacman2up = new ImageIcon(Board.class.getResource("../pacpix/up1.png")).getImage(); pacman3up = new ImageIcon(Board.class.getResource("../pacpix/up2.png")).getImage(); pacman4up = new ImageIcon(Board.class.getResource("../pacpix/up3.png")).getImage(); pacman2down = new ImageIcon(Board.class.getResource("../pacpix/down1.png")).getImage(); pacman3down = new ImageIcon(Board.class.getResource("../pacpix/down2.png")).getImage(); pacman4down = new ImageIcon(Board.class.getResource("../pacpix/down3.png")).getImage(); pacman2left = new ImageIcon(Board.class.getResource("../pacpix/left1.png")).getImage(); pacman3left = new ImageIcon(Board.class.getResource("../pacpix/left2.png")).getImage(); pacman4left = new ImageIcon(Board.class.getResource("../pacpix/left3.png")).getImage(); pacman2right = new ImageIcon(Board.class.getResource("../pacpix/right1.png")).getImage(); pacman3right = new ImageIcon(Board.class.getResource("../pacpix/right2.png")).getImage(); pacman4right = new ImageIcon(Board.class.getResource("../pacpix/right3.png")).getImage(); } public void paint(Graphics g) { super.paint(g); Graphics2D g2d = (Graphics2D) g; g2d.setColor(Color.black); g2d.fillRect(0, 0, d.width, d.height); DrawMaze(g2d); DrawScore(g2d); DoAnim(); if (ingame) PlayGame(g2d); else ShowIntroScreen(g2d); g.drawImage(ii, 5, 5, this);

640

Toolkit.getDefaultToolkit().sync(); g.dispose(); } class TAdapter extends KeyAdapter { public void keyPressed(KeyEvent e) { int key = e.getKeyCode(); if (ingame) { if (key == KeyEvent.VK_LEFT) { reqdx=-1; reqdy=0; } else if (key == KeyEvent.VK_RIGHT) { reqdx=1; reqdy=0; } else if (key == KeyEvent.VK_UP) { reqdx=0; reqdy=-1; } else if (key == KeyEvent.VK_DOWN) { reqdx=0; reqdy=1; } else if (key == KeyEvent.VK_ESCAPE && timer.isRunning()) { ingame=false; } else if (key == KeyEvent.VK_PAUSE) { if (timer.isRunning()) timer.stop(); else timer.start(); } } else { if (key == 's' || key == 'S') { ingame=true; GameInit(); } } public void keyReleased(KeyEvent e) { int key = e.getKeyCode(); if (key == Event.LEFT || key == Event.RIGHT || key == Event.UP || key == Event.DOWN) { reqdx=0; reqdy=0; } }

641

} public void actionPerformed(ActionEvent e) { repaint(); }

The Commons.java file has some common constants.


final short leveldata[] = { 19, 26, 26, 26, 18, ... };

These numbers make up the maze. They provide information out of which we create the corners and the points. For example number 19 in the upper left corner means, that the square will have top and left borders and a point. (16 + 2 + 1)
public void doAnim() { pacanimcount--; if (pacanimcount <= 0) { pacanimcount = pacanimdelay; pacmananimpos = pacmananimpos + pacanimdir; if (pacmananimpos == (pacmananimcount - 1) || pacmananimpos == 0) pacanimdir = -pacanimdir; } }

The doAnim() counts the pacmananimpos variable, which determines what pacman image is drawn. There are four pacman images. There is also a pacanimdelay variable, which makes the animation a bit slower. Otherwise the pacman would open his mouth too fast.
boolean finished = true; while (i < nrofblocks * nrofblocks && finished) { if ((screendata[i] & 16) != 0) finished = false; i++; }

This code is part of the checkMaze() method. It checks, if there are any points left for the Pacman to eat. Number 16 stands for a point. If all points are consumed, we move to the next level. Next we will examine the moveGhosts() method. The ghosts move one square and then decide, if they change the direction.
if (ghostx[i] % blocksize == 0 && ghosty[i] % blocksize == 0) {

Continue only if you have finished moving one square.


pos = ghostx[i] / blocksize + nrofblocks * (int)(ghosty[i] / blocksize);

This line determines, where the ghost is situated. In which position/square. There are 225 theoretical positions. (A ghost cannot move over walls. )
if ((screendata[pos] & 1) == 0 && ghostdx[i] != 1) {

642

dx[count] = -1; dy[count] = 0; count++;

If there is no obstacle on the left and the ghost is not already moving to the right, the ghost will move to the left. What does this code really mean? If the ghost enters a tunnel, he will continue in the same direction until he is out of the tunnel. Moving of ghosts is partly random. We do not apply this randomness inside long tunnels. The ghost might get stuck there.
if (pacmanx > (ghostx[i] - 12) && pacmanx < (ghostx[i] + 12) && pacmany > (ghosty[i] - 12) && pacmany < (ghosty[i] + 12) && ingame) { dying = true; deathcounter = 64; }

If there is a collision between ghosts and a pacman, the pacman dies. Next we are going to examine the movePacman() method. The reqdx and reqdy variables are determined in the TAdapter inner class. These variables are controlled with cursor keys.
if ((ch & 16) != 0) { screendata[pos] = (short)(ch & 15); score++; }

If the pacman moves to a position, where there is a point, we remove it from the maze and increase the score value.
if ((pacmandx == -1 && pacmandy == 0 && (ch & 1) != 0) || (pacmandx == 1 && pacmandy == 0 && (ch & 4) != 0) || (pacmandx == 0 && pacmandy == -1 && (ch & 2) != 0) || (pacmandx == 0 && pacmandy == 1 && (ch & 8) != 0)) { pacmandx = 0; pacmandy = 0; }

If the pacman cannot move further it his current direction, there is a standstill.
public void drawPacMan(Graphics2D g2d) { if (viewdx == -1) drawPacManLeft(g2d); else if (viewdx == 1) drawPacManRight(g2d); else if (viewdy == -1) drawPacManUp(g2d); else drawPacManDown(g2d); }

There are four possible directions for a pacman. There are four images for all directions. The images are used to animate pacman opening a closing his mouth.

643

The drawMaze() method draws the maze out of the numbers in the screendata array. Number 1 is a left border, 2 is a top border, 4 is a right border, 8 is a bottom border and 16 is a point. We simply go through all 225 squares int the maze. For example we have 9 in the screendata array. We have the first bit (1) and the fourth bit (8) set. So we draw a bottom and a left border on this particular square.
if ((screendata[i] & 1) != 0) // draws left { g2d.drawLine(x, y, x, y + blocksize - 1); }

Draw a left border if the first bit of a number is set. PacMan.java


package packman; import javax.swing.JFrame; import pacman.Board; public class PacMan extends JFrame { public PacMan() { add(new Board()); setTitle("Pacman"); setDefaultCloseOperation(EXIT_ON_CLOSE); setSize(380, 420); setLocationRelativeTo(null); setVisible(true); } public static void main(String[] args) { new PacMan(); }

This is a PacMan file with a main method.

644

Figure: Pacman This was the Pacman game.

Space Invaders
In this part of the Java 2D games tutorial we will create a simple Space Invaders game clone. Space Invaders is an arcade video game designed by Tomohiro Nishikado. It was first released in 1978. The player controls a cannon. He is about to save the Earth from invasion of evil space invaders.

Development
In our java clone we have 24 invaders. These aliens heavily shell the ground. When the player shoots a missile, he can shoot another one only when it hits an alien or the top of the Board. The player shoots with the alt key. Aliens launch randomly their bombs. Each alien shoots a bomb only after the previous one hits the bottom. SpaceInvaders.java
package spaceinvaders; import javax.swing.JFrame; public class SpaceInvaders extends JFrame implements Commons { public SpaceInvaders()

645

add(new Board()); setTitle("Space Invaders"); setDefaultCloseOperation(EXIT_ON_CLOSE); setSize(BOARD_WIDTH, BOARD_HEIGTH); setLocationRelativeTo(null); setVisible(true); setResizable(false);

} public static void main(String[] args) { new SpaceInvaders(); }

This is the main class. Commons.java


package spaceinvaders; public interface Commons { public public public public public public public public public public public public public public } static static static static static static static static static static static static static static final final final final final final final final final final final final final final int int int int int int int int int int int int int int BOARD_WIDTH = 358; BOARD_HEIGTH = 350; GROUND = 290; BOMB_HEIGHT = 5; ALIEN_HEIGHT = 12; ALIEN_WIDTH = 12; BORDER_RIGHT = 30; BORDER_LEFT = 5; GO_DOWN = 15; NUMBER_OF_ALIENS_TO_DESTROY = 24; CHANCE = 5; DELAY = 17; PLAYER_WIDTH = 15; PLAYER_HEIGHT = 10;

The Commons.java file has some common constants. They are self-explanatory. Alien.java
package spaceinvaders; import javax.swing.ImageIcon; public class Alien extends Sprite { private Bomb bomb; private final String shot = "../spacepix/alien.png"; public Alien(int x, int y) { this.x = x; this.y = y; bomb = new Bomb(x, y); ImageIcon ii = new ImageIcon(this.getClass().getResource(shot)); setImage(ii.getImage());

646

} public void act(int direction) { this.x += direction; } public Bomb getBomb() { return bomb; } public class Bomb extends Sprite { private final String bomb = "../spacepix/bomb.png"; private boolean destroyed; public Bomb(int x, int y) { setDestroyed(true); this.x = x; this.y = y; ImageIcon ii = new ImageIcon(this.getClass().getResource(bomb)); setImage(ii.getImage()); } public void setDestroyed(boolean destroyed) { this.destroyed = destroyed; } public boolean isDestroyed() { return destroyed; }

} }

This is the Alien sprite. Each alien has an inner Bomb class.
public void act(int direction) { this.x += direction; }

The act() method is called from the Board class. It is used to position an alien in horizontal direction.
public Bomb getBomb() { return bomb; }

The getBomb() method is called, when the alien is about to drop a bomb. Player.java
package spaceinvaders; import java.awt.event.KeyEvent; import javax.swing.ImageIcon; public class Player extends Sprite implements Commons{

647

private final int START_Y = 280; private final int START_X = 270; private final String player = "../spacepix/player.png"; private int width; public Player() { ImageIcon ii = new ImageIcon(this.getClass().getResource(player)); width = ii.getImage().getWidth(null); setImage(ii.getImage()); setX(START_X); setY(START_Y);

public void act() { x += dx; if (x <= 2) x = 2; if (x >= BOARD_WIDTH - 2*width) x = BOARD_WIDTH - 2*width; } public void keyPressed(KeyEvent e) { int key = e.getKeyCode(); if (key == KeyEvent.VK_LEFT) { dx = -2; } if (key == KeyEvent.VK_RIGHT) { dx = 2; } } public void keyReleased(KeyEvent e) { int key = e.getKeyCode(); if (key == KeyEvent.VK_LEFT) { dx = 0; } if (key == KeyEvent.VK_RIGHT) { dx = 0; } } }

This is the Player sprite. We control the cannon with the cursor keys.
private final int START_Y = 280; private final int START_X = 270;

648

These are the initial coordinates of the player sprite.


if (key == KeyEvent.VK_LEFT) { dx = -2; }

If we press the left cursor key, the dx variable is set to -2. Next time the act() method is called, the player moves to the left.
if (key == KeyEvent.VK_LEFT) { dx = 0; } if (key == KeyEvent.VK_RIGHT) { dx = 0; }

If we release the left or the right cursor, the dx variable is set to zero. The player sprite stops moving. Shot.java
package spaceinvaders; import javax.swing.ImageIcon; public class Shot extends Sprite { private String shot = "../spacepix/shot.png"; private final int H_SPACE = 6; private final int V_SPACE = 1; public Shot() { } public Shot(int x, int y) { ImageIcon ii = new ImageIcon(this.getClass().getResource(shot)); setImage(ii.getImage()); setX(x + H_SPACE); setY(y - V_SPACE);

} }

This is the Shot sprite. The shot is triggered with the alt key. The H_SPACE and the V_SPACE constants are used to position the missile appropriately. Sprite.java
package spaceinvaders; import java.awt.Image; public class Sprite {

649

private boolean visible; private Image image; protected int x; protected int y; protected boolean dying; protected int dx; public Sprite() { visible = true; } public void die() { visible = false; } public boolean isVisible() { return visible; } protected void setVisible(boolean visible) { this.visible = visible; } public void setImage(Image image) { this.image = image; } public Image getImage() { return image; } public void setX(int x) { this.x = x; } public void setY(int y) { this.y = y; } public int getY() { return y; } public int getX() { return x; } public void setDying(boolean dying) { this.dying = dying; } public boolean isDying() { return this.dying; }

This is the basic Sprite class. Other sprites inherit from it. It has some common functionality. Board.java
package spaceinvaders;

650

import import import import import import import import

java.awt.Color; java.awt.Dimension; java.awt.Font; java.awt.FontMetrics; java.awt.Graphics; java.awt.Toolkit; java.awt.event.KeyAdapter; java.awt.event.KeyEvent;

import java.util.ArrayList; import java.util.Iterator; import java.util.Random; import javax.swing.ImageIcon; import javax.swing.JPanel; public class Board extends JPanel implements Runnable, Commons { private private private private private private private private private private private private Dimension d; ArrayList aliens; Player player; Shot shot; int int int int alienX = 150; alienY = 5; direction = -1; deaths = 0;

boolean ingame = true; final String expl = "../spacepix/explosion.png"; final String alienpix = "../spacepix/alien.png"; String message = "Game Over";

private Thread animator; public Board() { addKeyListener(new TAdapter()); setFocusable(true); d = new Dimension(BOARD_WIDTH, BOARD_HEIGTH); setBackground(Color.black); gameInit(); setDoubleBuffered(true); } public void addNotify() { super.addNotify(); gameInit(); } public void gameInit() { aliens = new ArrayList(); ImageIcon ii = new ImageIcon(this.getClass().getResource(alienpix)); for (int i=0; i < 4; i++) {

651

for (int j=0; j < 6; j++) { Alien alien = new Alien(alienX + 18*j, alienY + 18*i); alien.setImage(ii.getImage()); aliens.add(alien); }

player = new Player(); shot = new Shot(); if (animator == null || !ingame) { animator = new Thread(this); animator.start(); } } public void drawAliens(Graphics g) { Iterator it = aliens.iterator(); while (it.hasNext()) { Alien alien = (Alien) it.next(); if (alien.isVisible()) { g.drawImage(alien.getImage(), alien.getX(), alien.getY(), } if (alien.isDying()) { alien.die(); }

this);

} }

public void drawPlayer(Graphics g) { if (player.isVisible()) { g.drawImage(player.getImage(), player.getX(), player.getY(), this); } if (player.isDying()) { player.die(); ingame = false; }

public void drawShot(Graphics g) { if (shot.isVisible()) g.drawImage(shot.getImage(), shot.getX(), shot.getY(), this); } public void drawBombing(Graphics g) { Iterator i3 = aliens.iterator(); while (i3.hasNext()) { Alien a = (Alien) i3.next(); Alien.Bomb b = a.getBomb();

652

} }

if (!b.isDestroyed()) { g.drawImage(b.getImage(), b.getX(), b.getY(), this); }

public void paint(Graphics g) { super.paint(g); g.setColor(Color.black); g.fillRect(0, 0, d.width, d.height); g.setColor(Color.green); if (ingame) { g.drawLine(0, GROUND, BOARD_WIDTH, GROUND); drawAliens(g); drawPlayer(g); drawShot(g); drawBombing(g);

Toolkit.getDefaultToolkit().sync(); g.dispose();

public void gameOver() { Graphics g = this.getGraphics(); g.setColor(Color.black); g.fillRect(0, 0, BOARD_WIDTH, BOARD_HEIGTH); g.setColor(new Color(0, 32, 48)); g.fillRect(50, BOARD_WIDTH/2 - 30, BOARD_WIDTH-100, 50); g.setColor(Color.white); g.drawRect(50, BOARD_WIDTH/2 - 30, BOARD_WIDTH-100, 50); Font small = new Font("Helvetica", Font.BOLD, 14); FontMetrics metr = this.getFontMetrics(small); g.setColor(Color.white); g.setFont(small); g.drawString(message, (BOARD_WIDTH - metr.stringWidth(message))/2, BOARD_WIDTH/2); {

public void animationCycle()

if (deaths == NUMBER_OF_ALIENS_TO_DESTROY) { ingame = false; message = "Game won!"; } // player player.act(); // shot

653

if (shot.isVisible()) { Iterator it = aliens.iterator(); int shotX = shot.getX(); int shotY = shot.getY(); while (it.hasNext()) { Alien alien = (Alien) it.next(); int alienX = alien.getX(); int alienY = alien.getY(); if (alien.isVisible() && shot.isVisible()) { if (shotX >= (alienX) && shotX <= (alienX + ALIEN_WIDTH) && shotY >= (alienY) && shotY <= (alienY+ALIEN_HEIGHT) ) { ImageIcon ii = new ImageIcon(getClass().getResource(expl)); alien.setImage(ii.getImage()); alien.setDying(true); deaths++; shot.die(); } } } int y = shot.getY(); y -= 4; if (y < 0) shot.die(); else shot.setY(y);

// aliens Iterator it1 = aliens.iterator(); while (it1.hasNext()) { Alien a1 = (Alien) it1.next(); int x = a1.getX(); if (x >= BOARD_WIDTH - BORDER_RIGHT && direction != -1) { direction = -1; Iterator i1 = aliens.iterator(); while (i1.hasNext()) { Alien a2 = (Alien) i1.next(); a2.setY(a2.getY() + GO_DOWN); } } if (x <= BORDER_LEFT && direction != 1) { direction = 1; Iterator i2 = aliens.iterator(); while (i2.hasNext()) { Alien a = (Alien)i2.next(); a.setY(a.getY() + GO_DOWN); } } }

654

Iterator it = aliens.iterator(); while (it.hasNext()) { Alien alien = (Alien) it.next(); if (alien.isVisible()) { int y = alien.getY(); if (y > GROUND - ALIEN_HEIGHT) { ingame = false; message = "Invasion!"; } } } // bombs Iterator i3 = aliens.iterator(); Random generator = new Random(); while (i3.hasNext()) { int shot = generator.nextInt(15); Alien a = (Alien) i3.next(); Alien.Bomb b = a.getBomb(); if (shot == CHANCE && a.isVisible() && b.isDestroyed()) { b.setDestroyed(false); b.setX(a.getX()); b.setY(a.getY()); } int int int int bombX = bombY = playerX playerY b.getX(); b.getY(); = player.getX(); = player.getY(); alien.act(direction);

if (player.isVisible() && !b.isDestroyed()) { if ( bombX >= (playerX) && bombX <= (playerX+PLAYER_WIDTH) && bombY >= (playerY) && bombY <= (playerY+PLAYER_HEIGHT) ) { ImageIcon ii = new ImageIcon(this.getClass().getResource(expl)); player.setImage(ii.getImage()); player.setDying(true); b.setDestroyed(true);; } } if (!b.isDestroyed()) { b.setY(b.getY() + 1); if (b.getY() >= GROUND - BOMB_HEIGHT) { b.setDestroyed(true); } }

} }

655

public void run() { long beforeTime, timeDiff, sleep; beforeTime = System.currentTimeMillis(); while (ingame) { repaint(); animationCycle(); timeDiff = System.currentTimeMillis() - beforeTime; sleep = DELAY - timeDiff; if (sleep < 0) sleep = 2; try { Thread.sleep(sleep); } catch (InterruptedException e) { System.out.println("interrupted"); } beforeTime = System.currentTimeMillis(); } gameOver(); } private class TAdapter extends KeyAdapter { public void keyReleased(KeyEvent e) { player.keyReleased(e); } public void keyPressed(KeyEvent e) { player.keyPressed(e); int x = player.getX(); int y = player.getY(); if (ingame) { if (e.isAltDown()) { if (!shot.isVisible()) shot = new Shot(x, y); } } } } }

The main logic of the game is located in the Board class.


for (int i=0; i < 4; i++) { for (int j=0; j < 6; j++) { Alien alien = new Alien(alienX + 18*j, alienY + 18*i); alien.setImage(ii.getImage()); aliens.add(alien); } }

656

player = new Player(); shot = new Shot();

In the gameInit() method we set up 24 aliens. The alien image size is 12x12px. We put 6px space among the aliens. We also create the player and the shot objects.
public void drawBombing(Graphics g) { Iterator i3 = aliens.iterator(); while (i3.hasNext()) { Alien a = (Alien) i3.next(); Alien.Bomb b = a.getBomb(); if (!b.isDestroyed()) { g.drawImage(b.getImage(), b.getX(), b.getY(), this); } } }

The drawBombing() method draws bombs launched by the aliens.


if (ingame) { g.drawLine(0, GROUND, BOARD_WIDTH, GROUND); drawAliens(g); drawPlayer(g); drawShot(g); drawBombing(g);

Inside the paint() method, we draw the ground, the aliens, the player, the shot and the bombs. Next we will examine the animationCycle() method.
if (deaths == NUMBER_OF_ALIENS_TO_DESTROY) { ingame = false; message = "Game won!"; }

If we destroy all aliens, we win the game. (24 in this game)


if (alien.isVisible() && shot.isVisible()) { if (shotX >= (alienX) && shotX <= (alienX + ALIEN_WIDTH) && shotY >= (alienY) && shotY <= (alienY+ALIEN_HEIGHT) ) { ImageIcon ii = new ImageIcon(getClass().getResource(expl)); alien.setImage(ii.getImage()); alien.setDying(true); deaths++; shot.die(); } }

657

If the shot triggered by the player collides with an alien, the alien ship is destroyed. More precisely, the dying flag is set. We use it to display an explosion. The deaths variable increases and the shot sprite is destroyed.
if (x >= BOARD_WIDTH - BORDER_RIGHT && direction != -1) { direction = -1; Iterator i1 = aliens.iterator(); while (i1.hasNext()) { Alien a2 = (Alien) i1.next(); a2.setY(a2.getY() + GO_DOWN); } }

If the aliens reach the right end of the Board, they move down and change their direction to the left.
Iterator it = aliens.iterator(); while (it.hasNext()) { Alien alien = (Alien) it.next(); if (alien.isVisible()) { int y = alien.getY(); if (y > GROUND - ALIEN_HEIGHT) { ingame = false; message = "Invasion!"; } alien.act(direction); } }

Aliens move. If they reach the bottom, the invasion begins.


int shot = generator.nextInt(15); Alien a = (Alien) i3.next(); Alien.Bomb b = a.getBomb(); if (shot == CHANCE && a.isVisible() && b.isDestroyed()) { b.setDestroyed(false); b.setX(a.getX()); b.setY(a.getY()); }

This is the code that determines whether the alien will drop a bomb. The alien must not be destroyed. Eg. it must be visible. The bomb's destroyed flag must be set. In other words, it is alien's first bomb dropping or previous dropped bomb already hit the ground. If these two conditions are fulfilled, the bombing is left to the chance.
if (!b.isDestroyed()) { b.setY(b.getY() + 1); if (b.getY() >= GROUND - BOMB_HEIGHT) { b.setDestroyed(true); } }

If the bomb is not destroyed, it goes one px to the ground. If it hits the bottom, the destroyed flag is set. The alien is now ready to drop another bomb.

658

public void keyReleased(KeyEvent e) { player.keyReleased(e); }

The actual processing of this particular KeyEvent is delegated to the player sprite.

Figure: Space Invaders This was the Space Invaders game.

Minesweeper
In this part of the Java 2D games tutorial, we will create a Minesweeper game clone.

Minesweeper
Minesweeper is a popular board game shipped with many operating systems by default. The goal of the game is to sweep all mines from a mine field. If the player clicks on the cell which contains a mine, the mine detonates and the game is over. Further a cell can contain a number or it can be blank. The number indicates how many mines are adjacent to this particular cell. We set a mark on a cell by right clicking on it. This way we indicate, that we believe, there is a mine. The following Minesweeper clone is a modified and simplified version of an Java applet which I found at javaside.com. Board.java
package mines; import java.awt.Graphics; import java.awt.Image;

659

import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.Random; import import import import javax.swing.BorderFactory; javax.swing.ImageIcon; javax.swing.JLabel; javax.swing.JPanel;

public class Board extends JPanel { private final int NUM_IMAGES = 13; private final int CELL_SIZE = 15; private private private private private private private private private private private private private private private private private private private final final final final final final final final final final int int int int int int int int int int COVER_FOR_CELL = 10; MARK_FOR_CELL = 10; EMPTY_CELL = 0; MINE_CELL = 9; COVERED_MINE_CELL = MINE_CELL + COVER_FOR_CELL; MARKED_MINE_CELL = COVERED_MINE_CELL + MARK_FOR_CELL; DRAW_MINE = 9; DRAW_COVER = 10; DRAW_MARK = 11; DRAW_WRONG_MARK = 12;

int[] field; boolean inGame; int mines_left; Image[] img; int mines = 40; int rows = 16; int cols = 16; int all_cells; JLabel statusbar;

public Board(JLabel statusbar) { this.statusbar = statusbar; img = new Image[NUM_IMAGES]; for (int i = 0; i < NUM_IMAGES; i++) { img[i] = (new ImageIcon(this.getClass().getResource((i) + ".png"))).getImage(); } setDoubleBuffered(true); addMouseListener(new MinesAdapter()); newGame(); } public void newGame() { Random random; int current_col;

660

int i = 0; int position = 0; int cell = 0; random = new Random(); inGame = true; mines_left = mines; all_cells = rows * cols; field = new int[all_cells]; for (i = 0; i < all_cells; i++) field[i] = COVER_FOR_CELL; statusbar.setText(Integer.toString(mines_left)); i = 0; while (i < mines) { position = (int) (all_cells * random.nextDouble()); if ((position < all_cells) && (field[position] != COVERED_MINE_CELL)) { current_col = position % cols; field[position] = COVERED_MINE_CELL; i++; if (current_col > 0) { cell = position - 1 - cols; if (cell >= 0) if (field[cell] != COVERED_MINE_CELL) field[cell] += 1; cell = position - 1; if (cell >= 0) if (field[cell] != COVERED_MINE_CELL) field[cell] += 1; cell = position + cols - 1; if (cell < all_cells) if (field[cell] != COVERED_MINE_CELL) field[cell] += 1;

cell = position - cols; if (cell >= 0) if (field[cell] != COVERED_MINE_CELL) field[cell] += 1; cell = position + cols; if (cell < all_cells) if (field[cell] != COVERED_MINE_CELL) field[cell] += 1; if (current_col < (cols - 1)) { cell = position - cols + 1; if (cell >= 0) if (field[cell] != COVERED_MINE_CELL) field[cell] += 1;

661

cell = position + cols if (cell < all_cells) if (field[cell] != field[cell] += cell = position + 1; if (cell < all_cells) if (field[cell] != field[cell] += } } } }

+ 1; COVERED_MINE_CELL) 1; COVERED_MINE_CELL) 1;

public void find_empty_cells(int j) { int current_col = j % cols; int cell; if (current_col > 0) { cell = j - cols - 1; if (cell >= 0) if (field[cell] > MINE_CELL) { field[cell] -= COVER_FOR_CELL; if (field[cell] == EMPTY_CELL) find_empty_cells(cell); } cell = j - 1; if (cell >= 0) if (field[cell] > MINE_CELL) { field[cell] -= COVER_FOR_CELL; if (field[cell] == EMPTY_CELL) find_empty_cells(cell); } cell = j + cols - 1; if (cell < all_cells) if (field[cell] > MINE_CELL) { field[cell] -= COVER_FOR_CELL; if (field[cell] == EMPTY_CELL) find_empty_cells(cell); }

cell = j - cols; if (cell >= 0) if (field[cell] > MINE_CELL) { field[cell] -= COVER_FOR_CELL; if (field[cell] == EMPTY_CELL) find_empty_cells(cell); } cell = j + cols; if (cell < all_cells) if (field[cell] > MINE_CELL) { field[cell] -= COVER_FOR_CELL; if (field[cell] == EMPTY_CELL) find_empty_cells(cell); }

662

if (current_col < (cols - 1)) { cell = j - cols + 1; if (cell >= 0) if (field[cell] > MINE_CELL) { field[cell] -= COVER_FOR_CELL; if (field[cell] == EMPTY_CELL) find_empty_cells(cell); } cell = j + cols + 1; if (cell < all_cells) if (field[cell] > MINE_CELL) { field[cell] -= COVER_FOR_CELL; if (field[cell] == EMPTY_CELL) find_empty_cells(cell); } cell = j + 1; if (cell < all_cells) if (field[cell] > MINE_CELL) { field[cell] -= COVER_FOR_CELL; if (field[cell] == EMPTY_CELL) find_empty_cells(cell); } } } public void paint(Graphics g) { int cell = 0; int uncover = 0; for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { cell = field[(i * cols) + j]; if (inGame && cell == MINE_CELL) inGame = false; if (!inGame) { if (cell == COVERED_MINE_CELL) { cell = DRAW_MINE; } else if (cell == MARKED_MINE_CELL) { cell = DRAW_MARK; } else if (cell > COVERED_MINE_CELL) { cell = DRAW_WRONG_MARK; } else if (cell > MINE_CELL) { cell = DRAW_COVER; } } else { if (cell > COVERED_MINE_CELL) cell = DRAW_MARK; else if (cell > MINE_CELL) { cell = DRAW_COVER; uncover++; }

663

} g.drawImage(img[cell], (j * CELL_SIZE), (i * CELL_SIZE), this); } }

if (uncover == 0 && inGame) { inGame = false; statusbar.setText("Game won"); } else if (!inGame) statusbar.setText("Game lost");

class MinesAdapter extends MouseAdapter { public void mousePressed(MouseEvent e) { int x = e.getX(); int y = e.getY(); int cCol = x / CELL_SIZE; int cRow = y / CELL_SIZE; boolean rep = false; if (!inGame) { newGame(); repaint(); } if ((x < cols * CELL_SIZE) && (y < rows * CELL_SIZE)) { if (e.getButton() == MouseEvent.BUTTON3) { if (field[(cRow * cols) + cCol] > MINE_CELL) { rep = true; if (field[(cRow * cols) + cCol] <= COVERED_MINE_CELL) { MARK_FOR_CELL; if (mines_left > 0) { field[(cRow * cols) + cCol] += mines_left--; statusbar.setText(Integer.toString(mines_left)); } else statusbar.setText("No marks left"); } else { field[(cRow * cols) + cCol] -= MARK_FOR_CELL; mines_left++; statusbar.setText(Integer.toString(mines_left)); } } } else {

664

if (field[(cRow * cols) + cCol] > COVERED_MINE_CELL) { return; } if ((field[(cRow * cols) + cCol] > MINE_CELL) && (field[(cRow * cols) + cCol] < MARKED_MINE_CELL)) { field[(cRow * cols) + cCol] -= COVER_FOR_CELL; rep = true; if (field[(cRow * cols) + cCol] == MINE_CELL) inGame = false; if (field[(cRow * cols) + cCol] == EMPTY_CELL) find_empty_cells((cRow * cols) + cCol);

} }

if (rep) repaint(); } } } }

First we will define the constants used in our game.


private final int NUM_IMAGES = 13; private final int CELL_SIZE = 15;

There are 13 images used in this game. A cell can be surrounded by maximum of 8 mines, so we need numbers 1..8. We need images for an empty cell, a mine, a covered cell, a marked cell and finally for a wrongly marked cell. The size of each of the images is 15x15 px.
private final int COVER_FOR_CELL = 10; private final int MARK_FOR_CELL = 10; private final int EMPTY_CELL = 0; ...

A mine field is an array of numbers. For example 0 denotes an empty cell. Number 10 is used for a cell cover as well as for a mark. Using constants improves readability of the code.
private int[] field;

The field is an array of numbers. Each cell in the field has a specific number. E.g. a mine cell has number 9. A cell with number 2, meaning it is adjacent to two mines, has number two. The numbers are added. For example, a covered mine has number 19, 9 for the mine and 10 for the cell cover etc.
private int mines = 40; private int rows = 16; private int cols = 16;

The minefield in our game has 40 hidden mines. There are 16 rows and 16 columns in this field. So there are 256 cells together in the minefield.

665

for (int i = 0; i < NUM_IMAGES; i++) { img[i] = (new ImageIcon(this.getClass().getResource((i) + ".png"))).getImage(); }

Here we load our images into the image array. The images are named 0.png, 1.png ... 12.png. The newGame() initiates the Minesweeper game.
all_cells = rows * cols; field = new int[all_cells]; for (i = 0; i < all_cells; i++) field[i] = COVER_FOR_CELL;

These lines set up the mine field. Every cell is covered by default.
while (i < mines) { position = (int) (all_cells * random.nextDouble()); if ((position < all_cells) && (field[position] != COVERED_MINE_CELL)) { current_col = position % cols; field[position] = COVERED_MINE_CELL;

...

In the while cycle we randomly position all mines in the field.


cell = position - cols; if (cell >= 0) if (field[cell] != COVERED_MINE_CELL) field[cell] += 1;

Each of the cells can be surrounded up to 8 cells. (This does not apply to the border cells.) We raise the number for adjacent cells for each of the randomly placed mine. In our example, we add 1 to the top neighbor of the cell in question. In the find_empty_cells() method, we find empty cells. If the player clicks on a mine cell, the game is over. If he clicks on a cell adjacent to a mine, he uncovers a number indicating how many mines the cell is adjacent to. Clicking on an empty cell leads to uncovering many other empty cells plus cells with a number that form a border around a space of empty borders. We use a recursive algorithm to find empty cells.
cell = j - 1; if (cell >= 0) if (field[cell] > MINE_CELL) { field[cell] -= COVER_FOR_CELL; if (field[cell] == EMPTY_CELL) find_empty_cells(cell); }

666

In this code, we check the cell that is left to an empty cell in question. If it is not empty, uncover it. If it is empty, repeat the whole process by recursively calling the find_empty_cells() method. The paint() method turns numbers into images.
if (!inGame) { if (cell == COVERED_MINE_CELL) { cell = DRAW_MINE; } else if (cell == MARKED_MINE_CELL) { cell = DRAW_MARK; } else if (cell > COVERED_MINE_CELL) { cell = DRAW_WRONG_MARK; } else if (cell > MINE_CELL) { cell = DRAW_COVER; } }

If the game is over and we lost, we show all uncovered mines if any and show all wrongly marked cells if any.
g.drawImage(img[cell], (j * CELL_SIZE), (i * CELL_SIZE), this);

This code line draws every cell. In the mousePressed() method we react to mouse clicks. The Minesweeper game is controlled solely by mouse. We react to left and right mouse clicks.
field[(cRow * cols) + cCol] += MARK_FOR_CELL; mines_left--;

If we right click on an unmarked cell, we add MARK_FOR_CELL to the number representing the cell. This leads to drawing a covered cell with a mark in the paint() method.
if (field[(cRow * cols) + cCol] > COVERED_MINE_CELL) { return; }

Nothing happens, if we click on the covered & marked cell. It must by first uncovered by another right click and only then it is possible to left click on it.
field[(cRow * cols) + cCol] -= COVER_FOR_CELL;

Left click removes a cover from the cell.


if (field[(cRow * cols) + cCol] == MINE_CELL) inGame = false; if (field[(cRow * cols) + cCol] == EMPTY_CELL) find_empty_cells((cRow * cols) + cCol);

In case we left clicked on a mine, the game is over. If we left clicked on an empty cell, we call the find_empty_cells() method, which recursively finds all adjacent empty cells.

667

Mines.java
package mines; import java.awt.BorderLayout; import javax.swing.JFrame; import javax.swing.JLabel; public class Mines extends JFrame { private final int WIDTH = 250; private final int HEIGHT = 290; private JLabel statusbar; public Mines() { setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize(WIDTH, HEIGHT); setLocationRelativeTo(null); setTitle("Minesweeper"); statusbar = new JLabel(""); add(statusbar, BorderLayout.SOUTH); add(new Board(statusbar)); setResizable(false); setVisible(true);

public static void main(String[] args) { new Mines(); } }

This is the main class.

Figure: Minesweeper

668

In this part of the Java 2D games tutorial, we created a Java clone of the Minesweeper game.

Sokoban
In this part of the Java 2D games tutorial, we will create a Java Sokoban game clone.

Sokoban
Sokoban is another classic computer game. It was created in 1980 by Hiroyuki Imabayashi. Sokoban means a warehouse keeper in Japanese. The player pushes boxes around a maze. The objective is to place all boxes in designated locations.

Development
We control the sokoban object with cursor keys. We can also press the R key to restart the level. When all bags are placed on the destination areas, the game is finished. We draw "Completed" string in the left upper corner of the window. Board.java
package sokoban; import import import import import import java.awt.Color; java.awt.Graphics; java.awt.event.KeyAdapter; java.awt.event.KeyEvent; java.util.ArrayList; javax.swing.JPanel;

public class Board extends JPanel { private private private private private private private private private private private private private final final final final final final int int int int int int OFFSET = 30; SPACE = 20; LEFT_COLLISION = 1; RIGHT_COLLISION = 2; TOP_COLLISION = 3; BOTTOM_COLLISION = 4; new ArrayList(); new ArrayList(); new ArrayList();

ArrayList walls = ArrayList baggs = ArrayList areas = Player soko; int w = 0; int h = 0; boolean completed

= false;

private String level = " ######\n" + " ## #\n" + " ##$ #\n" + " #### $##\n" + " ## $ $ #\n" + "#### # ## # ######\n" + "## # ## ##### ..#\n" + "## $ $ ..#\n" + "###### ### #@## ..#\n"

669

+ " + "

## #########\n" ########\n";

public Board() { addKeyListener(new TAdapter()); setFocusable(true); initWorld(); } public int getBoardWidth() { return this.w; } public int getBoardHeight() { return this.h; } public final void initWorld() { int x = OFFSET; int y = OFFSET; Wall wall; Baggage b; Area a; for (int i = 0; i < level.length(); i++) { char item = level.charAt(i); if (item == '\n') { y += SPACE; if (this.w < x) { this.w = x; } x = OFFSET; } else if (item == '#') { wall = new Wall(x, y); walls.add(wall); x += SPACE; } else if (item == '$') { b = new Baggage(x, y); baggs.add(b); x += SPACE; } else if (item == '.') { a = new Area(x, y); areas.add(a); x += SPACE; } else if (item == '@') { soko = new Player(x, y); x += SPACE; } else if (item == ' ') { x += SPACE; } } } h = y;

670

public void buildWorld(Graphics g) { g.setColor(new Color(250, 240, 170)); g.fillRect(0, 0, this.getWidth(), this.getHeight()); ArrayList world = new ArrayList(); world.addAll(walls); world.addAll(areas); world.addAll(baggs); world.add(soko); for (int i = 0; i < world.size(); i++) { Actor item = (Actor) world.get(i); if ((item instanceof Player) || (item instanceof Baggage)) { g.drawImage(item.getImage(), item.x() + 2, item.y() + 2, } else { g.drawImage(item.getImage(), item.x(), item.y(), this); } if (completed) { g.setColor(new Color(0, 0, 0)); g.drawString("Completed", 25, 20); } } } @Override public void paint(Graphics g) { super.paint(g); buildWorld(g); } class TAdapter extends KeyAdapter { @Override public void keyPressed(KeyEvent e) { if (completed) { return; } int key = e.getKeyCode(); if (key == KeyEvent.VK_LEFT) { if (checkWallCollision(soko, LEFT_COLLISION)) { return; } if (checkBagCollision(LEFT_COLLISION)) { return; }

this);

671

soko.move(-SPACE, 0); } else if (key == KeyEvent.VK_RIGHT) { if (checkWallCollision(soko, RIGHT_COLLISION)) { return; } if (checkBagCollision(RIGHT_COLLISION)) { return; } soko.move(SPACE, 0); } else if (key == KeyEvent.VK_UP) { if (checkWallCollision(soko, TOP_COLLISION)) { return; } if (checkBagCollision(TOP_COLLISION)) { return; } soko.move(0, -SPACE); } else if (key == KeyEvent.VK_DOWN) { if (checkWallCollision(soko, BOTTOM_COLLISION)) { return; } if (checkBagCollision(BOTTOM_COLLISION)) { return; } soko.move(0, SPACE); } else if (key == KeyEvent.VK_R) { restartLevel(); } repaint(); } }

private boolean checkWallCollision(Actor actor, int type) { if (type == LEFT_COLLISION) { for (int i = 0; i < walls.size(); i++) { Wall wall = (Wall) walls.get(i); if (actor.isLeftCollision(wall)) { return true; } } return false;

672

} else if (type == RIGHT_COLLISION) { for (int i = 0; i < walls.size(); i++) { Wall wall = (Wall) walls.get(i); if (actor.isRightCollision(wall)) { return true; } } return false; } else if (type == TOP_COLLISION) { for (int i = 0; i < walls.size(); i++) { Wall wall = (Wall) walls.get(i); if (actor.isTopCollision(wall)) { return true; } } return false; } else if (type == BOTTOM_COLLISION) { for (int i = 0; i < walls.size(); i++) { Wall wall = (Wall) walls.get(i); if (actor.isBottomCollision(wall)) { return true; } } return false;

} return false;

private boolean checkBagCollision(int type) { if (type == LEFT_COLLISION) { for (int i = 0; i < baggs.size(); i++) { Baggage bag = (Baggage) baggs.get(i); if (soko.isLeftCollision(bag)) { for (int j=0; j < baggs.size(); j++) { Baggage item = (Baggage) baggs.get(j); if (!bag.equals(item)) { if (bag.isLeftCollision(item)) { return true; } } if (checkWallCollision(bag, LEFT_COLLISION)) { return true; } } bag.move(-SPACE, 0); isCompleted(); } } return false; } else if (type == RIGHT_COLLISION) {

673

for (int i = 0; i < baggs.size(); i++) { Baggage bag = (Baggage) baggs.get(i); if (soko.isRightCollision(bag)) { for (int j=0; j < baggs.size(); j++) { Baggage item = (Baggage) baggs.get(j); if (!bag.equals(item)) { if (bag.isRightCollision(item)) { return true; } } if (checkWallCollision(bag, RIGHT_COLLISION)) { return true; }

} bag.move(SPACE, 0); isCompleted(); } } return false;

} else if (type == TOP_COLLISION) { for (int i = 0; i < baggs.size(); i++) { Baggage bag = (Baggage) baggs.get(i); if (soko.isTopCollision(bag)) { for (int j = 0; j < baggs.size(); j++) { Baggage item = (Baggage) baggs.get(j); if (!bag.equals(item)) { if (bag.isTopCollision(item)) { return true; } } if (checkWallCollision(bag, TOP_COLLISION)) { return true; } } bag.move(0, -SPACE); isCompleted();

} }

return false; } else if (type == BOTTOM_COLLISION) { for (int i = 0; i < baggs.size(); i++) { Baggage bag = (Baggage) baggs.get(i); if (soko.isBottomCollision(bag)) { for (int j = 0; j < baggs.size(); j++) { Baggage item = (Baggage) baggs.get(j); if (!bag.equals(item)) { if (bag.isBottomCollision(item)) {

674

return true;

} bag.move(0, SPACE); isCompleted(); } } } return false; }

} if (checkWallCollision(bag, BOTTOM_COLLISION)) { return true; }

public void isCompleted() { int num = baggs.size(); int compl = 0; for (int i = 0; i < num; i++) { Baggage bag = (Baggage) baggs.get(i); for (int j = 0; j < num; j++) { Area area = (Area) areas.get(j); if (bag.x() == area.x() && bag.y() == area.y()) { compl += 1; } } } if (compl == num) { completed = true; repaint(); }

public void restartLevel() { areas.clear(); baggs.clear(); walls.clear(); initWorld(); if (completed) { completed = false; } } }

The game is simplified. It only provides the very basic functionality. The code is than easier to understand. The game has one level.
private private private private private private final final final final final final int int int int int int OFFSET = 30; SPACE = 20; LEFT_COLLISION = 1; RIGHT_COLLISION = 2; TOP_COLLISION = 3; BOTTOM_COLLISION = 4;

675

The wall image size is 20x20px. This reflects the SPACE constant. The OFFSET is the distance between the borders of the window and the game world. There are four types of collisions. Each one is represented by a numerical constant.
private ArrayList walls = new ArrayList(); private ArrayList baggs = new ArrayList(); private ArrayList areas = new ArrayList();

The walls, baggs and areas are special containers, which will hold all the walls, baggs and areas of the game.
private String level = " ######\n" + " ## #\n" + " ##$ #\n" + " #### $##\n" + " ## $ $ #\n" + "#### # ## # ######\n" + "## # ## ##### ..#\n" + "## $ $ ..#\n" + "###### ### #@## ..#\n" + " ## #########\n" + " ########\n";

This is the level of the game. Except for the space, there are five characters. The hash (#) stands for a wall. The dollar ($) represents the box to move. The dot (.) character represents the place where we must move the box. The at character (@) is the sokoban. And finally the new line character (\n) starts a new row of the world.
public final void initWorld() { int x = OFFSET; int y = OFFSET;

...

The initWorld() method initiates the game world. It goes through the level string and fills the above mentioned lists.
} else if (item == '$') { b = new Baggage(x, y); baggs.add(b); x += SPACE;

In case of the dollar character, we create a Baggage object. The object is appended to the baggs list. The x variable is increased accordingly.
public void buildWorld(Graphics g) { ...

The buildWorld() method draws the game world on the window.


ArrayList world = new ArrayList(); world.addAll(walls); world.addAll(areas); world.addAll(baggs);

676

world.add(soko);

We create a world list which includes all objects of the game.


for (int i = 0; i < world.size(); i++) { Actor item = (Actor) world.get(i); if ((item instanceof Player) || (item instanceof Baggage)) { g.drawImage(item.getImage(), item.x() + 2, item.y() + 2, this); } else { g.drawImage(item.getImage(), item.x(), item.y(), this); }

...

We iterate through the world container and draw the objects. The player and the baggage images are a bit smaller. We add 2px to their coordinates to center them.
if (completed) { g.setColor(new Color(0, 0, 0)); g.drawString("Completed", 25, 20); }

If the level is completed, we draw "Completed" in the upper left corner of the window.
if (key == KeyEvent.VK_LEFT) { if (checkWallCollision(soko, LEFT_COLLISION)) { return; } if (checkBagCollision(LEFT_COLLISION)) { return; } ... soko.move(-SPACE, 0);

Inside the keyPressed() method, we check what keys were pressed. We control the sokoban object with the cursor keys. If we press the left cursor key, we check if the sokoban collides with a wall or with a baggage. If it does not, we move the sokoban to the left.
} else if (key == KeyEvent.VK_R) { restartLevel(); }

We restart the level, if we press the R key.


if (type == LEFT_COLLISION) { for (int i = 0; i < walls.size(); i++) { Wall wall = (Wall) walls.get(i); if (actor.isLeftCollision(wall)) { return true; } }

677

...

return false;

The checkWallCollision() method was created to ensure, that the sokoban or a baggage don't pass the wall. There are four types of collisions. The above lines check for the left collision.
private boolean checkBagCollision(int type) { }

The checkBagCollision() is a bit more involved. A baggage can collide with a wall, with a sokoban object or with another baggage. The baggage can be moved only if it collides with a sokoban and does not collide with another baggage or a wall. When the baggage is moved, it is time to check, if the level is completed by calling the isCompleted() method.
for (int i = 0; i < num; i++) { Baggage bag = (Baggage) baggs.get(i); for (int j = 0; j < num; j++) { Area area = (Area) areas.get(j); if (bag.x() == area.x() && bag.y() == area.y()) { compl += 1; } } }

The isCompleted() method checks, if the level is completed. We get the number of bags. We compare the x, y coordinates of all the bags and the destination areas.
if (compl == num) { completed = true; repaint(); }

The game is finished, when the completed variable equals the number of bags in the game.
public void restartLevel() { areas.clear(); baggs.clear(); walls.clear(); initWorld(); if (completed) { completed = false; } }

If we do some bad move, we can restart the level. We delete all objects from the important lists and initiate the world again. The completed variable is set to false. Actor.java
package sokoban; import java.awt.Image; public class Actor {

678

private final int SPACE = 20; private int x; private int y; private Image image; public Actor(int x, int y) { this.x = x; this.y = y; } public Image getImage() { return this.image; } public void setImage(Image img) { image = img; } public int x() { return this.x; } public int y() { return this.y; } public void setX(int x) { this.x = x; } public void setY(int y) { this.y = y; } public boolean isLeftCollision(Actor actor) { if (((this.x() - SPACE) == actor.x()) && (this.y() == actor.y())) { return true; } else { return false; } } public boolean isRightCollision(Actor actor) { if (((this.x() + SPACE) == actor.x()) && (this.y() == actor.y())) { return true; } else { return false; } } public boolean isTopCollision(Actor actor) { if (((this.y() - SPACE) == actor.y()) && (this.x() == actor.x())) { return true; } else { return false; }

679

} public boolean isBottomCollision(Actor actor) { if (((this.y() + SPACE) == actor.y()) && (this.x() == actor.x())) { return true; } else { return false; } } }

This is the Actor class. The class is a base class for other actors in the game. It encapsulates the basic functionality of an object in the Sokoban game.
public boolean isLeftCollision(Actor actor) { if (((this.x() - SPACE) == actor.x()) && (this.y() == actor.y())) { return true; } else { return false; } }

This method checks, if the actor collides with another actor (wall, baggage, sokoban) to the left. Wall.java
package sokoban; import java.awt.Image; import java.net.URL; import javax.swing.ImageIcon; public class Wall extends Actor { private Image image; public Wall(int x, int y) { super(x, y); URL loc = this.getClass().getResource("wall.png"); ImageIcon iia = new ImageIcon(loc); image = iia.getImage(); this.setImage(image); } }

This is the Wall class. It inherits from the Actor class. Upon construction, it loads a wall image from the filesystem. Player.java
package sokoban; import java.awt.Image; import java.net.URL; import javax.swing.ImageIcon;

680

public class Player extends Actor { public Player(int x, int y) { super(x, y); URL loc = this.getClass().getResource("sokoban.png"); ImageIcon iia = new ImageIcon(loc); Image image = iia.getImage(); this.setImage(image); } public void move(int x, int y) { int nx = this.x() + x; int ny = this.y() + y; this.setX(nx); this.setY(ny); } }

This is the Player class. It is the class to create the sokoban object.
public void move(int x, int y) { int nx = this.x() + x; int ny = this.y() + y; this.setX(nx); this.setY(ny); }

This class has a move() method, which moves the object inside the world. Baggage.java
package sokoban; import java.awt.Image; import java.net.URL; import javax.swing.ImageIcon; public class Baggage extends Actor { public Baggage(int x, int y) { super(x, y); URL loc = this.getClass().getResource("baggage.png"); ImageIcon iia = new ImageIcon(loc); Image image = iia.getImage(); this.setImage(image); } public void move(int x, int y) { int nx = this.x() + x; int ny = this.y() + y; this.setX(nx); this.setY(ny); }

This is the class for the Baggage object. This object is movable, so it has the move() method also.

681

Area.java
package sokoban; import java.awt.Image; import java.net.URL; import javax.swing.ImageIcon; public class Area extends Actor { public Area(int x, int y) { super(x, y); URL loc = this.getClass().getResource("area.png"); ImageIcon iia = new ImageIcon(loc); Image image = iia.getImage(); this.setImage(image);

} }

The Area class. It is the object, on which we try to place the baggages. Sokoban.java
package sokoban; import javax.swing.JFrame; public final class Sokoban extends JFrame { private final int OFFSET = 30; public Sokoban() { InitUI(); } public void InitUI() { Board board = new Board(); add(board); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize(board.getBoardWidth() + OFFSET, board.getBoardHeight() + 2*OFFSET); setLocationRelativeTo(null); setTitle("Sokoban");

public static void main(String[] args) { Sokoban sokoban = new Sokoban(); sokoban.setVisible(true); } }

This is the main class.

682

Figure: Sokoban This was the Sokoban game.

683

Вам также может понравиться