ZetCode Java tutorial.doc



Comments



Description

ZetCode Java tutorialThis 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. 2 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 3 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"); 4 } 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; 5 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 { 6 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). 8 • • • • • • 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 9 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 We can put two statements into one line. System. We declare a city variable of the string type and initialize it to the "New York" value. System.println(city).println(name). We assign a new value to the city variable and later print it. they cannot be modified.println(city). 14 .out.Variables New York Paul 34 American London This is the output of the example. System.out. final int HEIGHT= 150.out. Constants are created with the final keyword. We declare and initialize two variables. int age = 34.println(nationality).println(age).zetcode. String name = "Paul". Constants Unlike variables. } } //WIDTH = 110. package com. public class Constants { public static void main(String[] args) { final int WIDTH = 100.String city = "New York". each statement should be on a separate line. city = "London". We print the values of the variables to the terminal. final int WIDTH = 100. var = 50. But for readability reasons. Once initialized.out.zetcode.out. System. the Java compiler knows that there are two statements in one line. we declare two constants and one variable. In this example. $ java com. System. constants cannot change their initial values. Since each statement is finished with a semicolon. int var = 40. package com. Variable interpolation is replacing variables with their values inside string literals. age). we will get a compilation error: "Uncompilable source code . PHP or Ruby support variable interpolation. It is legal. We use the format() method of the built-in String class. We also use values from two variables. age). We must create a new string from existing strings and other types. we create a new string.println(output). name. The %s and %d are control characters which are later evaluated. It is a convention to write constants in uppercase letters. one integer and one string. 15 .format() method to format strings.". we assign a new value to the variable.out. Some dynamic languages like Perl.format("%s is %d years old. String name = "William". The %s accepts string values. Assigning new values to constants is not possible. We declare and initialize a variable.". } } System. public class StringFormatting { public static void main(String[] args) { int age = 34. int var = 40. We cannot modify an existing string. String name = "William". String output = String. Later. String output = String. int age = 34. We use the final keyword to inform the compiler that we declare a constant. name. It has string formatting insted.format("%s is %d years old.zetcode. String formatting Building strings from variables is a very common task in programming. Java language does not allow this. Here we have two variables. the %d integer values. // WIDTH = 110. Java language has the System.final int HEIGHT= 150. In the code example. strings are immutable.cannot assign a value to final variable WIDTH". var = 50. In Java. If we uncomment this line. Primitive types are not objects in Java. Strong static typing helps detect errors at compile time. once a variable is declared to be of a certain data type. Primitive types are: • • • • • • • • boolean char byte short int long float double There is a specific keyword for each of these types in Java. Spreadsheets. 16 . In Java. They can be placed into arrays instead. everything is an object. This chapter covered some basics of the Java language. Data types In this part of the Java tutorial.zetcode. calculators or chat clients. we will talk about data types. Computer programs work with data. This is the output of the example. limit the operations supported on those values. The reference types are: • • • class types interface types array types There is also a special null type which represents a non-existing value. It means that every variable and every expression has a type that is known at compile time. Variables in dynamically typed languages like Ruby or Python can receive different data types over the time. A data type is a set of values and the allowable operations on those values. Java language is also a strongly typed language. text editors. Primitive data types cannot be stored in Java collections which work only with objects. it cannot hold values of other data types. and determine the meaning of the operations. There are two fundamental data types in Java: primitive types and reference types.StringFormatting William is 34 years old. because types limit the values that a variable can hold or that an expression can produce. Even basic data types.$ java com. Java programming language is a statically typed language. In Ruby programming language. Tools to work with various data types are essential part of a modern computer language. out. It has primitive data types and wrapper classes. Java has a different approach. Wrapper classes transform primitive types into objects. Random r = new Random().#!/usr/bin/ruby 4.util.nextBoolean().times { puts "Ruby" } This Ruby script prints four times "Ruby" string to the console. public class BooleanType { public static void main(String[] args) { String name = "". Boolean values There is a duality built in our world. 17 . In Java the boolean data type is a primitive data type having one of two values: true or false. if (male == true) { name = "Robert". they have chosen Victoria. they have chosen Robert. Wrapper classes are covered later in this chapter. package com.zetcode. System. man and woman.Random.nextBoolean(). Random r = new Random(). } if (male == false) { } name = "Victoria".format("We will use name %s%n". name). These two lines randomly choose a boolean value. water and fire. boolean male = r. jing and jang.out. } } The program uses a random number generator to simulate our case. We call a times method on the 4 number. This number is an object in Ruby. boolean male = r.println(9 > 8). import java. There is a Heaven and Earth. System. love and hatred. Happy parents are waiting a child to be born. If it is going to be a girl. They have chosen a name for both possibilities. If it is going to be a boy. 372. This line prints true to the console. integers are (usually) primitive data types.372. The oldest verified person 18 . 1.BooleanType We will use name Robert true $ java com. 6 humans. Relational operators result in a boolean value.zetcode. 2.648 to 2. or 0.. Integers fall within a set Z = {.147. Integers Integers are a subset of the real numbers...564 days.535 int 32 bits -2.854.036. We can then use the byte type for a variable that stores the number of children a woman gave birth to. We can have 3. Integers are used to count discrete entities.775. $ java com. Computers can practically work only with a subset of integer values. The if keyword works with boolean values.println(9 > 8).775.807 Table: Integer types in Java These integer types may be used according to our needs.if (male == true) { } name = "Robert". but we cannot have 3..147. System..zetcode.BooleanType We will use name Victoria true Running the program several times. In computer languages.808 to 9.33 kilograms.} Integers are infinite. -1.767 char 16 bits 0 to 65. If the boolean variable male equals to true.483. -2.768 to 32.4532 kilomenters. we set the name variable to "Robert". We can have 3. .zetcode. if (male == false) { } name = "Victoria". because computers have finite capacity. 0.223. 4.036.33 humans.BooleanType We will use name Victoria true $ java com.223. They are written without a fraction or a decimal component.647 long 64 bits -9.854.out. 4. If the random generator chooses false than we set the name variable to "Victoria".483. Type Size Range byte 8 bits -128 to 127 short 16 bits -32. we must add the L suffix. hexadecimal. long x = 2147483648L. Integer literals may be expressed in decimal. int total = baskets * applesInBasket. package com. the compiler does not protest and performs a conversion automatically. When we work with integers.Apples 19 . Otherwise it is of type int. } } System. We have five assignments. int a = 34. 120. There are no integer literals for byte and short types. The capital letter L is preffered for specifying long numbers.zetcode. long e = 320000L. $ java com. since lowercase l can be easily confused with number 1. the L suffix is optional. Multiplying those values we get an integer too. For long numbers smaller than Integer. 34. If a number has an ASCII letter L or l suffix. short c = 32000. long y = 2147483649L. int total = baskets * applesInBasket. therefore we would probably choose at least the short type for the age variable. int applesInBasket = 24. public class Apples { public static void main(String[] args) { int baskets = 16. or binary notations. 32000 and 45000 are integer literals of type int. octal. we count the total amount of apples. byte b = 120. int applesInBasket = 24.died at 122. long d = 45000. it is of type long.MAX_VALUE. we deal with discrete items. For instance. In our program.out. For long numbers larger than Integer. we can use integers to count apples.zetcode. int baskets = 16. total). The number of baskets and the number of apples in each basket are integer values. If the values fit into the destination type.MAX_VALUE.format("There are total of %d apples%n". This will save us some memory. We use the multiplication operation. octal. Integers can be specified in four different notations in Java.println(n3). int int int int n1 n2 n3 n4 = = = = 31. 031. $ java com. public class IntegerNotations { public static void main(String[] args) { int int int int n1 n2 n3 n4 = = = = 31.IntegerNotations 31 49 25 9 We see the output of the com. Outside computers. Each of the variables is assigned a value with a different integer notation.zetcode. } } We have four integer variables. the third octal and the fourth binary. package com.zetcode. The binary notation was introduced in Java 7. Decimal.println(n1). Binary numbers start with 0b and are followed by binary numbers. it is possible to separate integers with an underscore. package com. Big numbers are difficult to read. 20 . Since Java SE 1. Decimal numbers are used normally.7.out. 0x31. Hexadecimal numbers are preceded with 0x characters and followed by hexadecimal numbers. 0x31. 0b1001. big numbers are separated by spaces or commas.out. The underscore cannot be used at the beginning or end of a number.IntegerNotations program. 031. hexadecimal and binary. System.out.zetcode. adjacent to a decimal point in a floating point literal. we find it difficult to read it quickly. and prior to an F or L suffix. 0b1001.There are total of 384 apples This is the output of the program.println(n4). If we have a number like 245342395423452. as we know them.out. Octal numbers are preceded with a 0 character and followed by octal numbers. System. System.println(n2).zetcode. The first is decimal. System. the second hexadecimal. Arbitrary precision integers are only limited by the amount of computer memory available.BigInteger. } } This code sample demonstrates the usage of underscores in Java. long a = 23482345629L. short.BigInteger class. 21 .out.math. In the second one we separate every three digits in a number.BigInteger class. import java. we have to use the java. BigInteger c = new BigInteger("52498235605326345645"). } } With the help of the java. BigInteger a = b.multiply(c). System. The largest integer number that a long type can represent is 9223372036854775807. package com. that they can represent a limited amount of integers.println(a). System. BigInteger c = new BigInteger("52498235605326345645").zetcode. public class VeryLargeIntegers { public static void main(String[] args) { System. Comparing these two numbers we receive a boolean true.println(Long.out. int and long types are used do represent fixed precision numbers. we multiply two very large numbers. The L suffix tells the compiler that we have a long number literal. Which means.println(Long.MAX_VALUE). System. They both hold larger values that a long type can hold.out. BigInteger b = new BigInteger("92233720368547758071").public class UsingUnderscores { public static void main(String[] args) { long a = 23482345629L.println(a == b). long b = 23_482_345_629L. It is used to represet immutable arbitrary precision integers.MAX_VALUE). We have two identical long numbers. Java byte. We define two BigInteger objects. BigInteger b = new BigInteger("92233720368547758071").math.math. If we deal with even larger numbers.out. We print the largest integer value which can be represented by a long type. long b = 23_482_345_629L. System. } } System. The computed integer is printed to the console. public class Overflow { public static void main(String[] args) { byte a = 126.println(a). In contrast. With the multiply() method. a++. Visual Basic programming language would throw an exception.println(a). This leads to an arithmetic overflow.println(a).out. $ java com. we try to assign a value beyond the range of a data type. the variable is reset to negative upper range value.println(a). System. 22 .VeryLargeIntegers 9223372036854775807 4842107582663807707870321673775984450795 This is the example output. System.zetcode.zetcode. 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.BigInteger a = b. a++. Note that the BigInteger numbers are immutable.out. The operation returns a new value which we assign to a new variable. package com.out. System. we multiply the two numbers.Overflow 126 127 -128 -127 When an overflow occurs. a++. $ java com.out.multiply(c).zetcode. In this example.println(a).out. The suffix for double numbers is optional. The low precision of the float data type does not pose a problem in this case. What is his speed in km/h? package com. Floating point numbers with an F/f suffix are of type float. or speed. distance = 0. 100m is 0.1f. speed = distance / time. height.87f / 3600. time = 9.format("The average speed of a sprinter is %f km/h%n".Sprinter The average speed of a sprinter is 36. double numbers have D/d suffix. Floating point numbers represent an approximation of real numbers in computing. Let's say a sprinter for 100m ran 9.Floating point numbers Real numbers measure continuous quantities.87s. } } System. In Java we have two primitive floating point types: float and double.87f / 3600. In this example. To get the speed. we can use the BigDecimal class.1km.zetcode. float speed.zetcode. speed = distance / time.87/60*60h.87s is 9.474163 km/h 23 . $ java com. The float is a single precision type which store numbers in 32 bits.1f. time = 9. The double is a double precision type which store numbers in 64 bits. public class Sprinter { public static void main(String[] args) { float distance. speed). In situations where we have to work with precise numbers. like weight. we divide the distance by the time.out. distance = 0. it is necessary to use floating point values. float time. These two types have fixed precision and cannot represent exactly all real numbers. 9. println(a).3 false There is a small margin error. double b = 0. When we work with money. and generally in business applications. System.1. At first sight.3. Printing them will show a very small difference. Therefore.1.This is the output of the com.FloatingInPrecision 0.zetcode.out.zetcode. we need to work with precise numbers. System. The rounding errors of the basic floating point types are not acceptable. public class FloatingInPrecision { public static void main(String[] args) { double a = 0. System. they should be equal.zetcode.30000000000000004 0. double a = 0.println(a == b). 24 . currency.out. We define two double values.1 + 0.1 + 0. the comparison operator returns a boolean false. package com.1 + 0.Sprinter program. package com.3.out.println(a).46f. System.out.1 + 0. A small rounding error in the number does not affect our understanding of the sprinter's speed. $ java com. public class CountingMoney { public static void main(String[] args) { float c = 1. float sum = 0f.println(b).out.println(a == b). The float and double types are inexact. The D/d suffix is optional. double b = 0.zetcode.println(b). This line will return false. System. } } System. The code example illustrates the inexact nature of the floating point values.out. out. i++) { sum = sum. i<100_000.CountingMoney 146002. i++) { } } sum = sum. public class CountingMoney2 { public static void main(String[] args) { BigDecimal c = new BigDecimal("1.55 The calculation leads to an error of 55 Cents.46").zetcode.println(sum). We create a sum from 100000 such amounts. arbitrary precision signed decimal numbers. i++) { sum += c. BigDecimal sum = new BigDecimal("0").println(sum). } We do the same operation with the same amount of money. To avoid this margin error. i++) { sum += c. BigDecimal c = new BigDecimal("1. i<100_000.BigDecimal. We define two BigDecimal numbers. The 1. i<100_000. $ java com.46"). BigDecimal sum = new BigDecimal("0"). for (int i=0. we utilize the BigDecimal class. package com. } } } System.out.46f represents 1 Euro and 46 Cents. i<100_000. for (int i=0. It is used to hold immutable.math. 25 . import java. we create a sum from 100000 such amounts of money. System.add(c). } In this loop. for (int i=0.add(c).zetcode.for (int i=0. out. it is a way of writing numbers too large or small to be conveniently written in standard decimal notation.zetcode. therefore a new object is always assigned to the sum variable in every loop. System. System. import java. Also known as exponential notation.00"). Java supports the scientific syntax of the floating point values.out.toPlainString()).out.println(bd.CountingMoney2 146000. DecimalFormat dec = new DecimalFormat("#.toEngineeringString()).zetcode. 26 . System.out. We use two methods of the class to print the value in the engineering and plain strings. } } We define two floating point values using the scientific notation.println(bd.212e-19").math. $ java com. double n = 1.DecimalFormat. We use the DecimalFormat class to arrange our double value into standard decimal format.toEngineeringString()).212e-19").println(bd.println(dec.format(n)). This is a floating point value of a double type. DecimalFormat dec = new DecimalFormat("#. BigDecimal bd = new BigDecimal("1. we get the precise value.} The BigDecimal number is immutable.235E10.00"). System.println(dec.println(bd. System.text.BigDecimal. written in scientific notation.out.out. public class ScientificNotation { public static void main(String[] args) { double n = 1.235E10.format(n)).00 In this example. The BigDecimal class takes a floating poing value in a scientific notation as a parameter. System. import java. BigDecimal bd = new BigDecimal("1.toPlainString()). package com. In our code example.out.2E-21 0. WEDNESDAY.out.out. A variable that has been declared as having an enumerated type can be assigned any of the enumerators as a value. package com.$ java com. FRIDAY.println(day). Enumerations make the code more readable.zetcode.ScientificNotation 12350000000. for (Days d : Days. Enumerations Enumerated type (also called enumeration or enum) is a data type consisting of a set of named values. SATURDAY. enum Days { MONDAY. if (day == Days. THURSDAY. SUNDAY } public static void main(String[] args) { Days day = Days. public class Enumerations { enum Days { MONDAY.MONDAY. SATURDAY. TUESDAY.00 121.println("It is Monday").values()) { } } } System. we create an enumeration for week days.zetcode. TUESDAY. } System.println(d). Enumerations are useful when we deal with variables that can only take one out of a small set of possible values. WEDNESDAY. 27 .MONDAY) { System. FRIDAY. THURSDAY.0000000000000000001212 This is the example output. Days day = Days. It is Monday MONDAY MONDAY TUESDAY WEDNESDAY THURSDAY FRIDAY SATURDAY SUNDAY This is the example output. By convention.println(day). The enhanced for goes through the array. System.out. We have a variable called day which is of enumerated type Days. Since strings are very important in every programming language. we will dedicate a whole chapter to them.println("It is Monday"). This loop prints all days to the console. Items of an enumeration are constants. A char is a single character.zetcode.values()) { } System.MONDAY) { System. Strings are enclosed by double quotes. element by element. in the order they are declared.MONDAY. and prints them to the terminal. A string in Java is a sequence of characters. if (day == Days. Here we only drop a small example. } This code is more readable than if comparing a day variable to some number. This method may be used to iterate over the constants with the enhanced for statement. for (Days d : Days.println(d). package com.out. constants are written in uppercase letters.out. This line prints Monday to the console. The static values() method returns an array containing the constants of this enum type. Strings and chars A String is a data type representing textual data in computer programs. It is initialized to Monday. public class StringsChars { 28 .} SUNDAY An enumeration representing the days of a week is created with a enum keyword. package com. The charAt() method returns the char value at the specified index. char c = word.println(c). here we show only a small example.charAt(3). Each of the elements can be accessed by an index. char d = word. The first char value of the sequence is at index 0. i < len. Here we create a string variable and assign it "ZetCode" value. } } The program prints Z character to the terminal.charAt(0).public static void main(String[] args) { String word = "ZetCode". String word = "ZetCode". public class ArraysExample { public static void main(String[] args) { int[] numbers = new int[5]. 29 .charAt(0).zetcode. Arrays Array is a complex data type which handles a collection of elements.out. i++) { System. 1.println(d). int len = numbers. and so on. System. 6. char c = word. We dedicate a whole chapter to arrays.length.println(numbers[i]).zetcode. numbers[0] numbers[1] numbers[2] numbers[3] numbers[4] = = = = = 3.StringsChars Z C The program prints the first and the fourth character of the "ZetCode" string to the console. 5. the next at index 1. for (int i = 0. System.out.out. $ java com. All the elements of an array must be of the same data type. 2. length. 1. 2. Java collections only 30 .ArraysExample 3 2 1 5 6 This is the output of the com. with indexes 0. We create an integer array which can store up to 5 integers. int len = numbers. We traverse the array and print the data to the console. Here we assign values to the created array. We can access the elements of an array by the array access notation. Each array has a length property which returns the number of elements in the array.. Wrapper classes are used to represent primitive values when an Object is required. int[] numbers = new int[5]. Wrapper classes Wrapper classes are object representations of primitive data types. we declare an array. i++) { } System. numbers[0] numbers[1] numbers[2] numbers[3] numbers[4] = = = = = 3. So we have an array of five elements. In this part of the Java tutorial. 5.zetcode. for (int i = 0. For example. we will continue covering data types of Java. i < len. $ java com. Inside the brackets we specify the index to the element that we want.println(numbers[i]). 6.ArraysExample program.4.} } } In this example. we covered data types in Java. fill it with data and then print the contents of the array to the console.out. It consists of the array name followed by square brackets. Data types II In this part of the Java tutorial.zetcode. Once they are created. In scientific computing and other large scale number processing.toOctalString(a).out. 31 .intValue(). It contains constants and methods useful when dealing with an int. float d = b.floatValue(). String oct = Integer. we use wrapper classes when we have some reason for it. wrapper classes may cause significant performance hit. As a general rule.toHexString(a).out. public class IntegerWrapper { public static void main(String[] args) { int a = 55.work with objects.println(oct).println(hex).zetcode.println(bin).out. } } This example works with the Integer wrapper class. String hex = Integer. They cannot take primitive types. int c = b. System.out.println(c). Placing primitive types into wrapper classes is called boxing. String bin = Integer. Otherwise.out.println(a). package com. System. System.out.out. Wrapper classes also include some useful methods. System. for doing data type conversions. we use primitive types. Primitive types are faster than boxed types. 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. Integer b = new Integer(a).println(b). 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.println(d). System.toBinaryString(a). Wrapper classes are immutable. System. The reverse process is called unboxing. System. they cannot be changed. For example. int a = 55. 56)). Collections are powerful tools for working with groups of objects.println(n).out. the floatValue() returns a float data type.toHexString(a).println(n.getClass()). ls.util. $ java com. for (Number n : ls) { System. String bin = Integer. package com.This line creates an integer primitive data type. Integer b = new Integer(a). import java.toOctalString(a).floatValue(). int c = b. float d = b. Likewise.add(new Double(235. ls.out. System.add(new Byte("102")). public class Numbers { public static void main(String[] args) { ArrayList<Number> ls = new ArrayList<>(). ls.IntegerWrapper 55 55 55 55. hexadecimal and octal representations of the integer.zetcode. These three methods return a binary. we can put them into collections. ls.add(new Short("1245")).add(new Integer(1342341)). } } } 32 .0 110111 37 67 This is the program output.add(new Float(34. String hex = Integer.242)). ls.zetcode. The intValue() method converts the Integer to int. An Integer wrapper class is created from the primitive int type. String oct = Integer.toBinaryString(a). After we box the primitive values.ArrayList.intValue(). Primitive data types cannot be placed into Java collections. It is converting of object types back into primitive types. The Number is an abstract base class for all five numeric primitive types in Java.56 class java.242 class java. for (Number n : ls) { System.println(n). Boxing Converting from primitive types to object types is called boxing.Numbers program gives this output.longValue(). ls.Numbers class java. An ArrayList is a dynamic. Float(34.add(new Integer(1342341)).lang. We add five wrapper classes to the container. ArrayList<Number> ls = new ArrayList<>().add(new ls.lang. System. An ArrayList instance is created.Integer 1342341 class java. $ java com.Double 235.out.lang. In angle brackets we specify the type that the container will hold. Double(235.lang. resizable array. package com.Byte 102 class java. 33 .println(c). } We iterate through the container and print the class name and its value of each of the elements. Unboxing is the opposite operation.out.Float 34.lang. Long b = new Long(a).println(n.zetcode.In the example. Byte("102")).56)).zetcode.add(new ls.242)). public class BoxingUnboxing { public static void main(String[] args) { long a = 124235L.add(new ls. we put various numbers into an ArrayList. System.Short 1245 The com.out. long c = b.add(new ls. Short("1245")).getClass()).zetcode. long c = b..out. } Inside the square brackets of the if expression.longValue().. Long b = new Long(a). we box a long value into a Long object and vice versa. int j = i.println(j). System. Autoboxing is automatic conversion between primitive types and their corresponding object wrapper classes. if (i < 100) { . public class Autoboxing { private static int cube(int x) { } return x * x * x. In this line we do unboxing. The programmer does not need to do the conversions manually.zetcode. public static void main(String[] args) { Integer i = 10. Automatic unboxing is done.println(i). Autoboxing makes the programming easier. an Integer is compared with an int.} } In the code example. The Integer object is transformed into the primitive int type and compared with the 100 value. This line performs boxing. package com. System. Autoboxing Java SE 5 introduced autoboxing. 34 .out. 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). } } 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 package com.zetcode. Concatenating strings Immutable strings can be added using the + operator or the concat() method.println("Return". sb.append("the king.concat("the king.out.MutableImmutable example.out. } } The example creates three sentences by adding strings. sb.out."). This is the example output. Mutable strings have the append() method which builds a string from any number of other strings."))."). 48 . StringBuilder sb = new StringBuilder().concat("the king.println(sb). They will form a new string which is a chain of all concatenated strings. A new string is formed by using the + operator. StringBuilder sb = new StringBuilder().out. sb. System.ConcatenateStrings of the king.append(" of ").println("Return" + " of " + "the king. of the king.println("Return". System.")).zetcode.append("Return"). public class ConcatenateStrings { public static void main(String[] args) { System. System.append("the king.println("Return" + " of " + "the king. of the king.append(" of "). The concat() method returns a string that represents the concatenation of this object's characters followed by the string argument's characters.concat(" of "). System."). sb.append("Return"). A mutable object of the StringBuilder type is created by calling the append() method three times.Kate This is the output of the com. $ java Return Return Return com."). sb. sb.out.concat(" of ").zetcode. println("He said: \"Which one are you looking at?\"").MultilineString example.Using quotes What if we wanted to display quotes.Quotes There are may stars He said: "Which one are you looking at?" Here we see the output of the com. public class MultilineString { static String lyrics = "I cheated myself\n" + "like I knew I would\n" + "I told ya. public static void main(String[] args) { System.zetcode. System.println(lyrics). package com. we need to do a concatenation operation. I was trouble\n" + "you know that I'm no good".println("There are may stars"). 49 .zetcode. $ java com.zetcode.out. package com. I was trouble you know that I'm no good We see the output of the com.MultilineString I cheated myself like I knew I would I told ya. Multiline strings It is not possible to create a multiline string in Java. public class Quotes { public static void main(String[] args) { System.zetcode.zetcode. In order to span a string on multiple lines. } } We use the (\) character to escape additional quotes. The four strings are concatenated with the + operator.zetcode.out.Quotes program. the inner quotes must be escaped. for example in a direct speech? In such a case. $ java com. } } One strophe spans four lines.out. char[] crs = {'Z'.out.contains("t")). public class StringElements { public static void main(String[] args) { char[] crs = {'Z'. System. A new immutable string is formed from an array of characters. 'C'. String s = new String(crs). int i2 = s. package com.length()-1). 'e' }. String s = new String(crs). 't'. we get the first and the last occurence of the character 'e'. for (char el : elements) { } } } System. With the above methods.lastIndexOf('e').indexOf('e'). 'e'. we get the first and the last char value of the string. int i1 = s. char c2 = s. 'e' }. 'C'.toCharArray().println(s. char c2 = s. System. In the first example.println(s.println(c1). 'o'. char[] elements = s. System.length()-1). 50 .println("The first index of character e is " + i1). System. int i1 = s.println(s. System. A character is a basic element of a string. The following two examples will show some methods that work with characters of a string.charAt(s.contains("f")).out.out.contains("t")).indexOf('e').println(c2). 'd'. System.println(el).out.charAt(0).println("The last index of character e is " + i2). char c1 = s. char c1 = s. System. 't'. int i2 = s. 'o'. 'd'.out. we will work with an immutable string.lastIndexOf('e').charAt(0).out.zetcode.out. With the charAt() method.charAt(s.String elements A string is a sequence of characters.out. 'e'. $ java com.zetcode.out. 'm'). System.length()-1). The method returns a boolean value.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. System. sb.out. 'T').insert(1. } } 51 . System. 'e'). sb. System. sb. System.insert(0. In the second example. we will work with the elements of a StringBuilder class. The toCharArray() method creates a character array from the string. sb.insert(2.println(sb).println(sb). ' ').println(el). sb. 'h'). char[] elements = s. public class StringBuilderElements { public static void main(String[] args) { StringBuilder sb = new StringBuilder("Misty mountains").With the contains() method.toCharArray(). for (char el : elements) { } System.append('s').insert(3.out. sb.out.println(sb). We go through the array and print each of the characters.out.deleteCharAt(sb.setCharAt(4.println(sb). sb.println(sb).zetcode.out. we check if the string contains the 't' character. package com. setCharAt(4. The deleted character is appended back to the string.deleteCharAt(sb. System. System. sb. $ java com. except that it ignores the case. public class ComparingStrings { public static void main(String[] args) { String a = "book". 'T').insert(0. This line deletes the last character.A mutable string is formed. sb. sb. sb. 52 . sb. sb. ' ').out.println(a.out. 'e'). We insert four characters at the beginning of the string. The equalsIgnoreCase() does the same thing. whether the strings are equal or not. } } We compare two strings using the aforementioned methods.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. we replace a character at index 4. The equals() method compares the contents of two strings and returns a boolean value indicating. String b = "Book".equals(b)). package com. sb.insert(1.equalsIgnoreCase(b)).zetcode.zetcode. appending. inserting and replacing characters. String b = "Book".println(a. 'm'). String a = "book". We modify the contents of the string by deleting. 'h').append('s'). Finally.insert(2.length()-1). Comparing strings There are two basic methods for comparing strings.insert(3. out.equals(d)) { System.zetcode. In the code example. The equals() method returns false.equals(b)). public class ComparingStrings2 { public static String readString() { Random r = new Random(). we compare the strings properly. the strings are equal.println(a. System.println("Strings are not equal"). The two strings differ in the first character. it is important to remember that the string is on the left side of the comparing method.println("Strings are equal").nextBoolean().We define two strings that we will compare. System.println(a. The equalsIgnoreCase() method returns true.out. } } } return null. boolean b = r.ComparingStrings false true This is the output of the com. Otherwise we might get the NullPointerException. if (b == true) { return "ZetCode".util. $ java com. public static String readString() { 53 .out.equalsIgnoreCase(b)).zetcode.out. If we are comparing a variable to a string. When we ignore the case. } else { } } public static void main(String[] args) { String d = readString(). avoiding possible NullPointerException. } else { System. if ("ZetCode".Random. import java.ComparingStrings program. trim(). if we try to read a value from a database. The equals() method compares the characters of two strings. All string literals are interned automatically in Java. System. we compare string objects with the == operator. 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". String d = readString(). new String("ZetCode"). These strings literals are interned. Therefore.out. The d variable can contain the null value. " ZetCode ". System.intern(). this would lead to NullPointerException if the d variable would contain the null value. This could happen. They are placed inside a string pool. If two variables contain two equal string literals. package com.nextBoolean(). they in fact refer to the same string object inside a string pool. System. The == operator tests for reference equality.out.out. In this code example. } } The readString() method simulates the case where a method invocation can result in a null value. This happens at compile time. if (b == true) { return "ZetCode".println(b).println(d). the identity comparison operator returns true.println(a).Random r = new Random(). "Zet" + "Code".println(c).equals(d)) { The above line is the correct way of comparing two strings where one string is a known literal. boolean a = "ZetCode" == "ZetCode".println(e).out. } else { return null.out. 54 . if ("ZetCode". System. boolean b = r. } } System. If we placed the d variable on the left side.zetcode. new String("ZetCode"). for instance. the extra arguments are ignored.ComparingStrings3 true false true true false This is the output of the example. the d variable holds a boolean true.trim(). Formatting strings We can use both System. The result is a true. The trim() method is called at runtime.printf() and System. These two methods write a formatted string to the output stream using the specified format string and arguments. The string literals result in the same object. The set of valid flags depends on the conversion. generating a distinct object. character.boolean b = "ZetCode" == new String("ZetCode"). $ java com. If there are more arguments than format specifiers.out. Strings are concatenated at compile time. The argument_index is a decimal integer indicating the position of the argument in the argument list.zetcode.precision]conversion The format specifiers for general. The width is a non-negative decimal integer indicating the minimum number of characters to be written to the output.intern(). %[argument_index$][flags][width][. Strings created with the new operator are not interned. The comparison operator results in a false value. boolean d = "ZetCode" == new String("ZetCode").out.format() methods to format strings in Java. 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 optional items are placed between the square brackets. The precision is a non-negative 55 . The intern() object puts the string object on the right side into the pool. They work the same. The e variable holds a boolean false. The flags is a set of characters that modify the output format. boolean e = "ZetCode" == " ZetCode ". Therefore. 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. boolean c = "ZetCode" == "Zet" + "Code". out.zetcode. System.out.zetcode.format(). we have three format specifiers. "pencils").util.Conversions There are 5 pencils. System. } } In this program. y. 5. package com.out. The System. Month: %<tm. The %n outputs a platform-specific line terminator. This is the output of the com. z).%n".decimal integer usually used to restrict the number of characters. "pencils"). The rock weighs 5. The f formats a floating point value as a decimal value. x.Conversions program. The required conversion is a character indicating how the argument should be formatted.format("Year: %tY.345).Calendar. In this code line. int z = 43.%n". The d specifier formats integer values.zetcode.zetcode. c). int y = 32.out.format("There are %2$d apples.345).out. 5.out. $ java com.format("There are %d %s.format("There are %d apples. System. The specific behavior depends on the conversion.%n". import java. Calendar c = Calendar. 5.getInstance(). System.%n". } } 56 .out.out. we format two simple sentences. Each specifier starts with the % character. x. %d oranges and " + "%d pears%n". y. %3$d oranges and " + "%1$d pears%n". package com. The s specifier expects string values.format("There are %d %s.printf() works the same as the System. Day: %<td%n". 5. z).345000 kilograms. public class Conversions { public static void main(String[] args) { System.printf("The rock weighs %f kilograms. public class IndexPosition { public static void main(String[] args) { int x = 12.out. System. it does not require an argument. System.printf("The rock weighs %f kilograms. System.zetcode.The example uses argument index to refer to variables included the list of arguments. 553).out.format("There are %d apples. } } The example presents a few flags of the string format specifier. public class Flags { public static void main(String[] args) { System. Day: 17 This is the output of the com.out. All three specifiers refer to the c variable. we have 7 leading zeros in the output. Therefore.out.format("%10d%n". 32 oranges and 43 pears There are 32 apples. -553).out. the + flag requires the output to include a positive sign for all positive numbers. $ java com. System. c).format("There are %2$d apples.IndexPosition program. y. Our number has three digits. If we do not specify the index.out. The d specifier formats an integer value as a decimal value. The minimum width is 10. 553). 553). Month: %<tm.zetcode. 553). y.format("%010d%n". System. 43 oranges and 12 pears Year: 2013.format("%d%n". z). 57 . %d oranges and " + "%d pears%n". There are several flags available. System. 553).out. System.out.format("%10d%n". System.format("%(d%n". For instance.format("%-10d%n".IndexPosition There are 12 apples. x. The 0 flag will cause the output to be padded with leading zeros to the minimum field width. the 2$ referes to the y variable and the 3$ refers to the z variable. Day: %<td%n".out.format("%+d%n". The flag modifies the format in a specific way.out. The '<' flag causes the argument for the previous format specifier to be reused. z). package com. Month: 07. System. System. the variables automatically match the specifiers. -553).out. %3$d oranges and " + "%1$d pears%n".zetcode.format("Year: %tY. System.format("%010d%n". System.out. The 1$ referes to the x variable. x. 553). System.Flags +553 0000000553 553 553 -553 (553) Here we see the output of the com. If we use the ( flag.out.out. 1). The .format("%10d%n".out. The numbers are right aligned.out. In the second case.format("%d%n". the number is right aligned.out. Number 10 states that the string output must have at least ten characters.println(1). It cannot be used together with the line separator.out. 16567).zetcode. -553). $ java com. public class WidthSpecifier { public static void main(String[] args) { System.zetcode.out. $ java com.format("%10d%n". the negative values will be put inside round brackets. package com. 1).zetcode.out. System.out.format("%-10d%n".format("%10d%n".println(16).Without the 0 flag.WidthSpecifier 1 58 .println(16567). System. 166701). System. System. System. } } First. System. we have a field width of 10. -553). 553). we print five numbers without specifying the field width.out.zetcode. Each of the 5 outputs has a minimum length of 10 characters.out.format("%10d%n".format("%(d%n". By default. The width field is the minimum number of characters to be written to the output.out. 16). System.flag will cause the number to be left aligned. System. negative numbers have a minus sign. 1655).format("%10d%n". The width of the output is equal to the number of the characters being displayed. System. System.format("%10d%n". System.Flags example.out.out.println(1655). System.println(166701). format("%. the precision is the maximum number of characters to be written to the output. "ZetCode").format("%.3s%n". For floating point values. System. } } The precision specifier is demonstrated on three outputs.out. public class PrecisionSpecifier { public static void main(String[] args) { System.34263).PrecisionSpecifier 6.format("%.3f%n".out.out.343 Zet This is the example output. package com. System.0000006). The precision field has different meaning for different conversions. System. If the g conversion is used. 54. For general argument types. 0. it is the maximum number of printed characters.00e-07 54. $ java com. the precision is the number of digits after the decimal separator. "ZetCode").16 1655 16567 166701 1 16 1655 16567 166701 We can see that in the second case the numbers are right aligned. The next example will format numeric data. For strings.0000006).format("%.3g%n".format("%.3g%n".34263). Only three characters out of seven are printed to the console.out.out.format("%.zetcode. System.zetcode.zetcode. System. 0. 54. then the precision is the total number of digits in the resulting magnitude after rounding. public class FormatNumbers { 59 . package com.3s%n".out.3f%n". System.format("%o%n".out. c). The %% characters are used to print a percent sign.format("%d%%%n".Calendar.format("%e%n".out. 45).getInstance(). 12263). public class FormatDateTime { public static void main(String[] args) { Calendar c = Calendar. System.format("%x%n". Using the 'e' specifier.out. System.out. the number is printed in a scientific notation. System.out.format("%d%n".FormatNumbers 12263 27747 2fe7 3. 12263). System. System. 45). The o conversion specifier will format the number into the octal base. System.public static void main(String[] args) { System. Finally.format("%tD%n". System. package com. With the x specifier. the result is formatted as a hexadecimal integer.format("%tF%n".format("%d%n".format("%e%n".zetcode.out. 0.format("%d%%%n". 60 .util.format("%o%n". 12263). 12263).03452342263). we will format date and time data.out. System.format("%x%n". } } The example demonstrates the standard formatting specifiers for numbers. 12263). System.452342e-02 45% The program prints numbers in different formats. The d conversion specifier will turn an integer value into a decimal value.out.out.out.out. System. import java. 12263). 0. $ java com.03452342263).out.zetcode. c). 2013 1374078588 This is the output of the com. Arrays are zero based. we print a date in the form that is used in Slovakia.%1$tY%n". This line prints a date in a complete ISO 8601 format. Arrays In this part of the Java tutorial.System.format("%ts%n".format("%1$td. These items are called elements of the array.zetcode. The parts are separated by the dot character and the day precedes the month and the month precedes the year.out. Arrays store data of the same data type.format("%1$tA. c). The 's' conversion character creates a Unix time.out. System.%1$tm.format("%tF%n". $ java com. c). It is a number of seconds since the beginning of the epoch starting at 1 January 1970 00:00:00 UTC. We declare arrays to be of a certain data type. The length of an array is established when the array is created. c). c).zetcode. After creation.format("%tT%n".07. The conversion part of the date and time format string starts with the 't' character. System.Jul 2013 17. Using these format specifiers. Arrays can hold multiple items. We have several methods 61 . we will cover arrays. An array is a container object that holds a fixed number of values of a single type. The index of the first element is zero. And we initialize arrays with data.FormatDateTime program. its length is fixed. System. System.%1$tm.out. This part of the Java tutorial covered strings in more detail.%1$tb %1$tY%n". } } The preceding example demonstrates the standard formatting specifiers for dates. c). Arrays are used to store data of our applications.out. We specify their length. All three format specifiers refer to the c variable.%1$tY%n".out. A scalar variable can hold only one item at a time. System. c).format("%1$td.format("%ts%n". c).out. System.FormatDateTime 2013-07-17 07/17/13 18:29:48 Wednesday.out. Each element can be referred to by an index. as a result of the tF conversion. 4. 2. a[0] a[1] a[2] a[3] a[4] } = = = = = 1. int[] a = new int[5]. Initializing arrays There are several ways how we can initialize an array in Java. In the first example.toString(a)). 2.Arrays. 5. float in our case) and a pair of square brackets []. 3. 3. The declaration consists of two parts. sort them.for working with arrays. Collections serve a similar purpose like arrays. 4.util. Here we create an array which can contain five elements. They are more powerful than arrays. 62 . The brackets indicate that we have an array. package com. The type of an array has a data type that determines the types of the elements within an array (int. String. copy them or search for them. float[] weights. We can modify the elements. String[] names. An array is an object and therefore it is created with the new keyword. public class InitArray { public static void main(String[] args) { int[] a = new int[5]. the type (int in our case) tells us what type of values the array will hold. The contents of the array are printed to the console. The square brackets are used for declaring an array.zetcode. } We create and initialize a numerical array.println(Arrays. an array is created and initialized in two steps. The statement allocates memory for five integers. import java. We have three array declarations. They will be described later in a separate chapter. 5. System.out. a[0] a[1] a[2] a[3] a[4] = = = = = 1. The type of the array and the array name. int[] ages. We did not specify the length of the array. 6. int[] array = new int[] { 2. System. 2 }. import java. 3. The toString() method returns a string representation of the contents of the specified array.util.Arrays. Number 1 is going to be the first element of the array. import java. 2 }.zetcode. The indexes are in the square brackets. 6. public class InitArray3 { public static void main(String[] args) { int[] a = { 2. The Arrays class is a helper class which contains various methods for manipulating arrays. Number 2 is the second etc. The elements are specified in curly brackets. System. 5. System.zetcode. 3. $ java com. 3. public class InitArray2 { public static void main(String[] args) { int[] a = new int[] { 2.println(Arrays.toString(a)).toString(a)).toString(a)). 4. 4. 7. package com. package com. 7. 2.We initialize the array with some data. 5] Output of the com. 5. The one step creation and initialization can be further simplified by only specifying the numbers between the curly brackets.out. 5. 6.zetcode. This is assignment initialization. 2 }. An array is created and initialized in one step. We can declare and initialize an array in one statement.out.InitArray example.println(Arrays. } } 63 .InitArray [1. } } This is a modified version of the previous program. 4. This method is helpful in debugging.zetcode. 3. 4.util.out. The compiler will do it for us.println(Arrays.Arrays. 7. 3.println(names[3]). we create an array of string names. The index is a number placed inside square brackets which follow the array name. 6. "David"}. System. } } In the example. 64 . "Lucy". 7.out. its elements can be accessed by their index. Each of the elements of the array is printed to the console.println(names[1]).out.out.zetcode.println(names[2]). System.println(names[1]). System. The elements are not immutable. System. 5.out. "Thomas". $ java com. Accessing elements After the array is created. package com. System. System. "Lucy". int[] a = { 2. It is possible to change the elements of an array. "Thomas".AccessingElements Jane Thomas Lucy David Running the example we get the above output.out.println(names[3]).zetcode. An array of strings is created. System. public class AccessingElements { public static void main(String[] args) { String[] names = {"Jane".println(names[0]). The right side of the statement is an array literal notation.out. The new int[] construct can be omitted. Even if we drop the new keyword.out. String[] names = {"Jane". We access each of the elements by its index and print them to the terminal. It resembles the C/C++ style of array initialization. With the names[0] construct. 2 }.println(names[2]). System. This is just a convenient shorthand notation.println(names[0]). 4. the array is created the same way as in previous two examples.An array of integers is created using the most simple way of array creation.out. "David"}. we refer to the first element of the names array. Arrays.util. 2. vals[0] *= 2.zetcode.zetcode. "Neptune". "Pluto" }. "Jupiter". 6] All three integers have been multiplied by number 2. } } System. i++) { System. "Mars". Using the element access. i < planets. 2. "Saturn". $ java com.out. Each of the values will be multiplied by two.AccessingElements2 [2. "Venus". "Earth". public class AccessingElements2 { public static void main(String[] args) { int[] vals = { 1.println(Arrays. vals[2] *= 2. An array of three integers is created. import java. vals[1] *= 2. vals[0] *= 2. We show two common methods for traversing an array. vals[1] *= 2. for (int i=0. "Uranus". 3 }.package com. int[] vals = { 1.zetcode.toString(vals)). Traversing arrays We often need to go through all elements of an array. public class TraversingArrays { public static void main(String[] args) { String[] planets = { "Mercury". package com. 3 }. vals[2] *= 2. 4. } for (String planet : planets) { 65 . We have an array of three integers.out.println(planets[i]). we multiply each value in the array by two.length. println(twodim[i][j]). for (int i = 0.out. public class TwoDimensions { public static void main(String[] args) { int[][] twodim = new int[][] { {1. } } } } In this example.length. 2. int d1 = twodim. 66 . 3}. i++) { } System. {1. Multidimensional arrays So far we have been working with one-dimensional arrays. for (String planet : planets) { } System. package com. for (int i=0. the elements are themselves arrays. 3} }. We use the for loop to print all the values. i++) { for (int j = 0. In each cycle. 2. The number of elements is stored in the length constant. j++) { System. An array of planet names is created. the planet variable is passed the next value from the planets array. An enhanced for keyword can be used to make the code more compact when traversing arrays or other collections.println(planets[i]).println(planet). we can create multidimensional arrays. int[][] twodim = new int[][] { {1. i < d1. {1.println(planet). 3} }.length. 2. int d2 = twodim[1]. i < planets.out. we use two or more sets of brackets. we create a two-dimensional array of integers. In Java.zetcode. In this loop. 3}. A multidimensional array is an array of arrays.} } } System. 2.length.out. we utilize the fact that we can get the number of elements from the array object. In multidimensional arrays. In such an array.out. j < d2. for (int i = 0. k++) { System. They represent two inner arrays. j < d2. int d2 = n3[0].length. j++) { } } System. 5. we have additional two pairs of curly brackets. {8. We determine the length of the outer array that holds other two arrays and the second inner array. 26. j < d2. int d1 = twodim. 1}}.length. int d3 = n3[0][0]. for (int i = 0. public class ThreeDimensions { public static void main(String[] args) { int[][][] n3 {{12. In a similar fashion.out. 11.length. Two for loops are used to print all the six values from the two-dimensional array. 6}} int d1 = n3. {0. {{3. {0.out.length.TwoDimensions 1 2 3 1 2 3 This is the output of the com. j++) { for (int k = 0. package com. }. i++) { for (int j = 0.length. 7. 4}}. int d2 = twodim[1]. 5. {{4. i < d1. 2. we create a three-dimensional array of integers. 67 . 2. 2}. The second index refers to the element of the chosen inner array. $ java com.TwoDimensions program. = { 8}. 9}. 9.print(n3[i][j][k] + " "). 1}}. i < d1. Inside the curly brackets. k < d3.Two pairs of square brackets are used to declare a two-dimensional array.zetcode. {0. 2}. i++) { for (int j = 0.println(twodim[i][j]).zetcode. The first index of the twodim[i][j] array refers to one of the inner arrays. {{14.zetcode. We need three for loops to traverse a three dimensional array. It is an array that has elements which are themselves arrays of arrays. 2. 7. 26. }. {0. k < d3. 5.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.length.} } } } } System. int d1 = n3.length. package com. 2}. We get the length of all three dimensions.print(n3[i][j][k] + " "). {{4. $ java com. j < d2.out.out. for (int i = 0. 2. 1}}. 9}. int d2 = n3[0].length. = { 8}. int[][][] n3 {{12. {8. 9.zetcode. 6}} Three-dimensional array n3 is created. public class IrregularArrays { public static void main(String[] args) { 68 . i++) { for (int j = 0. Irregular arrays Arrays that have elements of the same size are called rectangular arrays. {0.zetcode. In C# such arrays are called jagged arrays.print('\n'). {0. j++) { for (int k = 0. 11. {{3. 2}. The values are place inside three pairs of curly brackets. k++) { } } } System. A variable that holds a tree-dimensional array is declared with three pairs of square brackets. 5. 4}}. {{14. 1}}. int d3 = n3[0][0]. It is possible to create irregular arrays where the arrays have a different size. i < d1. util package.IrregularArrays 1 2 1 2 3 1 2 3 4 This is the output of the example. The three inner arrays have 2. $ java com.print(e + " ").Arrays. is a helper class that contains methods for working with arrays. import java. ir = new int[][] { 2}. 3. 2. 3. 4} for (int[] a : ir) { for (int e : a) { System. for (int[] a : ir) { for (int e : a) { System. } } The enhanced for loop is used to go through all the elements of the array. Array methods The Arrays class.out. copying.zetcode. } } This is an example of an irregular array. {1. }. These methods that we use are static methods of the Array class.print(e + " "). or searching data. These methods can be used for modifying. sorting. int[][] {1.out. 3}. 3}. {1. {1.out.int[][] {1. }. ir = new int[][] { 2}. available in the java. 4} This is a declaration and initialization of an irregular array. } } System.zetcode. {1.util. 2. 2.print('\n'). public class ArrayMethods { public static void main(String[] args) { 69 . package com. 2. 3 and 4 elements. 8). 2.out. 5). 4.out. Arrays. b)) { System. The toString() method returns a string representation of the contents of the specified array. 1}.println("Arrays a.toString(a)). 8). b are not equal").equals(a.toString(a)). int[] b = Arrays. } else { } } } System.out.out. The copyOf() method copies the specified number of elements to a new array. 1}. b)) { System. The sort() method sorts the integers in an ascending order. if (Arrays. We will use the shorthand notation for the Arrays class.fill(a.util. 4.out.Arrays. The fill() method assigns the specified integer value to each element of the array. Arrays. we will present five methods of the Arrays class. b are not equal"). int[] b = Arrays. int[] a = {5. b are equal").sort(a).println("Arrays a.out.int[] a = {5.fill(a. b are equal"). 5).copyOf(a. System. 3. Arrays.toString(a)). 3. import java.println("Arrays a. Arrays.equals(a. In the code example. if (Arrays. } else { System. } 70 .println("Arrays a.sort(a).out. We have an array of five integers. System. System. 2.copyOf(a.println(Arrays.println(Arrays.println(Arrays. The deepEquals() method also compares references to arrays inside arrays. 8. int[][] d = { a.out. $ java [1. 2. Comparing arrays There are two methods for comparing arrays. import java.The equals() method compares the two arrays. 8] a.print("deepEquals() method: "). 0. The equals() method and the deepEquals() method. b are equal Running the com. 3.out. d are not equal").deepEquals(c.out. System. d)) { System. {0. 5] 8. 1}. 4. public class ComparingArrays { public static void main(String[] args) { int[] a = {1. } else { System. Two arrays are equal if they contain the same elements in the same order. 1. d are equal"). we get the above result. 0. 2. 2.println("Arrays c. } } } 71 .zetcode. package com.print("equals() method: "). if (Arrays.out.zetcode. 0}.util. b }.zetcode. int[][] c = { {1. } else { System. 3. Arrays com. 0} }. int[] b = {0.println("Arrays c. 1. 8. d are not equal"). 0. [8. 0.out. 1}. 1. } System.equals(c.println("Arrays c. 1.println("Arrays c. if (Arrays.ArrayMethods 3.ArrayMethods application.Arrays. d are equal").out. d)) { System. 72 . The d array contains references to a. } Now the c. b }.The example explains the difference between the two methods. 0. The c array has two inner arrays. The elements of the inner arrays are equal to the a. } else { System. d)) { System. the c.out.deepEquals(c. The deepEquals() method goes deeper in the referenced arrays and retrieves their elements for comparison. b arrays. 1. 0} }. d are equal"). d are equal This is the example output. 2.equals(c. For the equals() method.out. d are not equal").out. d are not equal"). 1. d are equal").print("deepEquals() method: "). } else { System.out.println("Arrays c. 1. 2. 0. int[] a = {1. We have two arrays of integers. 3. int[] b = {0. int[][] c = { {1. $ java com. {0. For this method. 1. 0. d are not equal deepEquals() method: Arrays c.println("Arrays c.out. 1}. 0. int[][] d = { a.print("equals() method: "). d arrays are compared using both methods. d)) { System. if (Arrays. System. the arrays are not equal. d arrays are equal.zetcode.ComparingArrays equals() method: Arrays c. 3. 1}. b arrays. if (Arrays. } System. 0}.out.println("Arrays c.println("Arrays c. p). The binarySearch() method only works on sorted arrays. It is called the binarySearch(). } else { msg = p + " was not found". package com. In such a case.sort(planets). if (r >= 0) { msg = String.out. "Neptune". Arrays. we search for the "Earth" string in an array of planets. } System. the return value is greater or equal to zero. The method searches for elements using a binary search algorithm. r). The binarySearch() method is called. it is the index of the element in the sorted array. "Venus". int r = Arrays. int r = Arrays. 73 .util. if (r >= 0) { msg = String. the second the element we are looking for. "Uranus". The first parameter is the array name. We will be searching for the "Earth" element. "Pluto" }.sort(planets). p. r). "Saturn". } } In the example. String msg. String p = "Earth". we must sort the array first.Searching arrays The Arrays class has a simple method for searching elements in an array.binarySearch(planets.Arrays. String p = "Earth". Since the algorithm only works on sorted arrays. If the element is found.format("%s was found at position %d of the " + "sorted array". "Mars". public class Searching { public static void main(String[] args) { String[] planets = { "Mercury". p).zetcode. Arrays.println(msg). "Jupiter". p.format("%s was found at position %d of the " + "sorted array".binarySearch(planets. "Earth". import java. Searching Earth was found at position 0 of the sorted array This is the example output. They are used to indicate or change the sign of a value. Expressions In this part of the Java tutorial. In this part of the Java tutorial. 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.out.zetcode.out. we create a message. The operators are used to process data.println(-2).println(+2).println(2).signs indicate the sign of a value. } Depending on the returned value.out. It can be used in different cases. concatenates strings or indicates the sign of a number. The order of evaluation of operators in an expression is determined by the precedence and associativity of the operators. Operators in programming languages are taken from mathematics. package com. The plus sign can be used to signal that we have a positive number. The operators of an expression indicate which operations to apply to the operands.zetcode.} else { msg = p + " was not found". we worked with arrays. } } The + and . we will talk about expressions. $ java com. It can be omitted and it is mostly done so. System. 74 . Sign operators There are two sign operators. An operator is a special symbol which indicates a certain process is carried out. Programmers work with data. It adds numbers. An operand is one of the inputs (arguments) of an operator. We say that the operator is overloaded. Expressions are constructed from operands and operators. public class SignOperators { public static void main(String[] args) { System. For example the + operator. Certain operators may be used in different contexts. System. + and -. There is also one ternary operator (?:) which works with three operands. package com.out.zetcode. 75 .").println(-(-a)). System. System.out. int x = 1.out. But it is legal in programming. public class MinusSign { public static void main(String[] args) { int a = 1. 3 = x.concat(" of").zetcode. This code line results in syntax error. The assignment operator The assignment operator = assigns a value to a variable. We cannot assign a value to a literal.println("Return". Here we assign a number to the x variable.")).println("Return " + "of " + "the king. public class ConcatenateStrings { public static void main(String[] args) { System."). System. Strings are joined with the + operator. } } The minus sign changes the sign of a value. The expression adds 1 to the x variable.out. Concatenating strings In Java the + operator is also used to concatenate strings. System. In mathematics. A variable is a placeholder for a value. The right side is equal to 2 and 2 is assigned to x.println(-a).concat(" the king.println("Return " + "of " + "the king.out. package com. } } We join three strings together. The left side of the equation is equal to the right one. the = operator is an equality operator. This expression does not make sense in mathematics. x = x + 1. In an equation. the = operator has a different meaning. Java has two convenient operators for this: ++ and --. package com. we demonstrate the usage of both operators. System.out. System.1. An alternative method for concatenating strings is the concat method. x++.out. 76 .concat(" of"). x++. y--.println("Return". We initiate the x variable to 6.concat(" the king."))..println(x). public class IncDec { public static void main(String[] args) { int x = 6. x = x + 1. Increment..zetcode. y = y . int x = 6. } } In the above example. . Now the variable equals to 8.out. x++.System.ConcatenateStrings program. This is the output of the com. x--. x--. x++. The above two pairs of expressions do the same.zetcode. x++.println(x). Return of the king.zetcode. We use the decrement operator. Then we increment the x two times. decrement operators Incrementing or decrementing a value by one is a common task in programming.ConcatenateStrings Return of the king. $ java com. Now the variable equals to 7. div = c / 3.println(add).out. int b = 11. 9 modulo 4 is 1.$ java com.println(sb). public class Arithmetic { public static void main(String[] args) { int a = 10. } } In the preceding example.println(rem).println(mult).out.Arithmetic 33 77 . It finds the remainder of division of one number by another. For example.zetcode.IncDec 8 7 And here is the output of the example.out. System. sb = c . System. int c = 12. This is all familiar from the mathematics. multiplication.println(div). 9 % 4.zetcode. System. System.zetcode. rem = c % a. division and remainder operations. The % operator is called the remainder or the modulo operator. we use addition.out. int rem = c % a. Arithmetic operators The following is a table of arithmetic operators in Java. $ java com.out.a. System. Symbol Name + Addition Subtraction * Multiplication / Division % Remainder The following example shows arithmetic operations. mult = a * b. because 4 goes into 9 twice with a remainder of 1. int int int int int add = a + b + c. package com. subtraction. 2 110 4 2 This is the output of the example. In our case.Division 2 2. double d = 5 / 2. } } In the preceding example.zetcode. we have done integer division.Division program. the second operand is a double so the result is a double. The returned value of the division operation is an integer. we perform a floating point division. In this code. When we divide two integers the result is an integer. we divide two numbers. Symbol Name && logical and || logical or ! negation Boolean operators are also called logical. If one of the values is a double or a float.println(d). The boolean keyword is used to declare a Boolean value.zetcode. Next we will show the distinction between integer and floating point division.zetcode. System. Boolean operators In Java we have three logical operators.out. double d = 5 / 2.0. 78 .5 We see the result of the com. package com.out. $ java com.println(c). int c = 5 / 2.0. System. public class Division { public static void main(String[] args) { int c = 5 / 2. false && false.println("y is greater than x"). if (y > x) { } } } System. public class BooleanOperators { public static void main(String[] args) { int x = 3. The code example shows the logical and operator.out. It evaluates to true only if both operands are true.println(y > x). Many expressions result in a boolean value.out.out. so the message "y is greater than x" is printed to the terminal. int y = 8. public class AndOperator { public static void main(String[] args) { boolean boolean boolean boolean a b c d = = = = true && true.package com. System.println("y is greater than x").out.zetcode. 79 .println(x == y).println(a).println(x == y). The y > x returns true. true && false. } } System.zetcode.println(y > x). System.out. false && true. The true and false keywords represent boolean literals in Java. Boolean values are used in conditional statements. package com. System. if (y > x) { } System.println(d). These two lines print false and true. Relational operators always result in a boolean value.println(b). The body of the if statement is executed only if the condition inside the parentheses is met. System.println(c). System. System.out. System.out.out.out.out. out.$ java com. false || false.println(! false). $ java com.AndOperator true false false false Only one expression results in true. public class Negation { public static void main(String[] args) { System. } } If one of the sides of the operator is true.out.out. System. System. System. if either of the operands is true.zetcode.println(a).println(c). } } The example shows the negation operator in action. true || false.out. System. The negation operator ! makes true false and false true. the outcome of the operation is true.zetcode.zetcode.println(! true).zetcode. System. System. The logical or || operator evaluates to true. $ java com. package com. package com. false || true.out.zetcode.println(! (4 < 3)).out.out.OrOperator true true true false Three of four expressions result in true.Negation false true 80 . public class OrOperator { public static void main(String[] args) { boolean boolean boolean boolean a b c d = = = = true || true.println(d).println(b). return false. the result of the logical conclusion is always false. return true.out.zetcode. } System.println("Short circuit"). It is not necessary.out. the overall value must be true. if (Two() || One()) { } } } System.println("#############"). } public static boolean Two() { System.println("Pass").out.true This is the output of the com. and when the first argument of logical or evaluates to true.Negation program. } public static void main(String[] args) { System.out. An example may clarify this a bit more.println("Pass"). the overall value must be false. if (One() && Two()) { System. Only "Inside one" is only printed to the console.println("Inside one").out.zetcode.out.println("Pass"). 81 . and && operators are short circuit evaluated. Short circuit evaluation is used mainly to improve performance. Once an operand is false.println("Inside two"). public class ShortCircuit { public static boolean One() { System. We will see if they are called or not. The short circuit && does not evaluate the second method. The One() method returns false. package com. if (One() && Two()) { } System.out. They are used as operands in boolean expressions. 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. We have two methods in the example. The ||. we have four expressions. 82 .zetcode.println(4 System.out. == 4). Some languages like Ada. Visual Basic. public class Relational { public static void main(String[] args) { System. the logical or is always true. or Pascal use = for comparing numbers. In the code example.println("Pass"). != 3). It is again not necessary to evaluate the second operand. These operators always result in a boolean value.ShortCircuit Short circuit Inside one ############# Inside two Pass We see the result of the com.ShortCircuit program. The result of each of the expressions is either true or false. we use the || operator and use the Two() method as the first operand. package com. In the second case.println(3 System.println(4 } } < 4). >= 3). In this case. $ java com.out. 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.out.println(3 System. These expressions compare integer values. In Java we use the == to compare numbers.out.out.if (Two() || One()) { } System. since once the first operand evaluates to true.zetcode. Relational Operators Relational operators are used to compare values.zetcode. "Inside two" and "Pass" strings are printed to the terminal. out.println(3 | 6). The result for a bit position is 1 if either of the corresponding bits in the operands is 1. Binary numbers are native to computers. // prints 7 System.println(~ -8). // prints 7 The operator reverts all bits of a number 7. decimal or hexadecimal symbols are only notations of the same number. 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.Bitwise operators Decimal numbers are natural to humans.out. The result is 2. & = 00110 00011 00010 The first number is a binary notation of 6.out.out. 00110 | 00011 = 00111 The result is 00110 or decimal 7. The second is 3. // prints -8 System. whether the number is negative or not. // prints 2 The bitwise or operator performs bit-by-bit comparison between two numbers.println(6 | 3). Binary. System.out. we get number 7 again. Symbol Meaning ~ bitwise negation ^ bitwise exclusive or & bitwise and | bitwise or The bitwise negation operator changes each 1 to 0 and 0 to 1.println(6 & 3). octal. One of the bits also determines. 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. Bitwise operators work with bits of a binary number. // prints 2 System.println(~7). // prints 7 The bitwise exclusive or operator performs bit-by-bit comparison between two numbers. ^ = 00110 00011 00101 83 . System. If we negate all the bits one more time. System.out.println(3 & 6). Bitwise operators are seldom used in higher level languages like Java. |= <<= >>= public class CompoundOperators { public static void main(String[] args) { int a = 1.The result is 00101 or decimal 5. System.println(3 ^ 6).println(a). int a = 1. The statement is equal to a = a + 5. 1 is added to the variable using the non-shorthand notation. we use two compound operators. System.. Other compound operators are: -= *= /= %= &= package com. // prints 5 Compound assignment operators The compound assignment operators consist of two operators. Using a += compound operator.out. They are shorthand operators. System. a *= 3. a += 5. System.zetcode. a += 3. $ java com.CompoundOperators 2 84 . a = a + 3. The statement is equal to a = a * 3.. The += compound operator is one of these shorthand operators. a = a + 1.println(a).out. Using the *= operator. // prints 5 System. a = a + 1. the a is multiplied by 3.println(a). Value 3 is added to the a variable.out.out. The a variable is initiated to one. } } In the example. The above two expressions are equal.zetcode. a *= 3. we add 5 to the a variable.out.println(6 ^ 3). a += 5. println(d instanceof Base). 85 . One base and one derived from the base. This line checks if the variable d points to the class that is an instance of the Base class.println(d instanceof Object).out. This line prints false. The line prints true.out.InstanceofOperator true false true This is the output of the com.zetcode. The instanceof operator The instanceof operator compares an object to a specified type. class Base {} class Derived extends Base {} public class InstanceofOperator { public static void main(String[] args) { Base b = new Base(). package com. the d object is also an instance of the Object class.zetcode. System. $ java com.out. it is also an instance of the Base class too. System.println(b instanceof Derived).out.InstanceofOperator program. The b object is not an instance of the Derived class.println(d instanceof Object).out.zetcode. Therefore. System. we have two classes. Since the Derived class inherits from the Base class.7 21 This is the example output. System.println(b instanceof Derived).println(d instanceof Base). System. Every class has Object as a superclass. } } In the example. System.out. Derived d = new Derived(). What is the outcome of the following expression? 28 or 40? 3 + 5 * 5 Like in mathematics.zetcode. type cast. package com. division. The result of the above expression is 40.Operator precedence The operator precedence tells us which operators are evaluated first. The following table shows common Java operators ordered by precedence (highest precedence first): Meaning Associativity [] () . If we use operators with the same precedence. method invoke. modulo Left-to-right + addition. object creation Right-to-left * / % multiplication. decrement. The precedence level is necessary to avoid ambiguity in expressions. object member access Left-to-right ++ -. (3 + 5) * 5 To change the order of evaluation. unary plus and minus Right-to-left ! ~ (type) new negation. Expressions inside parentheses are always evaluated first. So the outcome is 28. array access. then the associativity rule is applied. bitwise NOT. the multiplication operator has a higher precedence than addition operator. public class Precedence { Operator 86 .+ increment. 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. we can use parentheses. The multiplication operator has a higher precedence than addition. then 3 is added.zetcode. $ java com. So the expression is evaluated this way: (9 / 3) * 3 and the result is 9. } } In this code example.println(3 + 5 * 5). There is another rule called associativity. The outcome of each expression is dependent on the precedence level. ternary operator. Arithmetic.out. The assignment operators. System.zetcode. which gives true in the end.println(! true | true).println((3 + 5) * 5). First. then the | operator combines false and true. Associativity Sometimes the precedence is not satisfactory to determine the outcome of an expression. 9 / 3 * 3 What is the outcome of this expression? 9 or 1? The multiplication.public static void main(String[] args) { System. the initial true value is negated to false. The associativity of operators determines the order of evaluation of operators with the same precedence level. This line prints 28.println(! true | true). negation. decrement. we show a few expressions.Precedence 28 40 true false This is the example output. public class Associativity { public static void main(String[] args) { 87 .out. relational and bitwise operators are all left to right associated. the negation operator has a higher precedence than the bitwise OR.out.out. increment.println(! (true | true)). System. the product of 5*5 is calculated. deletion and the modulo operator are left to right associated. boolean. object creation operators are right associated. System. System. unary plus and minus. In this case.println(3 + 5 * 5). bitwise NOT. First.out. System. type cast. package com.out. boolean adult = age >= 18 ? true : false. int j = 0. int j = 0. c. d. The compound assignment operators are right to left associated. b.zetcode. adult)). It is a convenient operator for cases where we want to pick up one of two values.format("Adult: %s". c.out. The assignment operator is right to left associated.Associativity program.out.println(String. c. The expression on the right is evaluated first and than the compound assignment operator is applied. The ternary operator The ternary operator ? : is a conditional operator. If the associativity was left to right.println(str). exp1 is evaluated and the result is returned. package com.out. j *= 3 + 1.println(j).int a. a = b = c = d = 0. j *= 3 + 1. } } In the example. cond-exp ? exp1 : exp2 If cond-exp is true.zetcode. System. But the actual result is 0. $ java com. Because of the associativity. We might expect the result to be 1. } 88 . we have two cases where the associativity rule determines the expression.Associativity 0 0 0 0 0 Output of the com.format("%d %d %d %d". d. String str = String. public class TernaryOperator { public static void main(String[] args) { int age = 31. d).zetcode. If the cond-exp is false. a. depending on the conditional expression. a = b = c = d = 0. b. System. b. int a. the previous expression would not be possible. System. exp2 is evaluated and its result is returned. the value following the ? character is returned. bool adult = age >= 18 ? true : false. First the expression on the right side of the assignment operator is evaluated.sqrt(num). 20. we are going to calculate prime numbers. package com. 19. while (i > 1) { if (num % i == 0) { } i--. If not. for (int num : nums) { if (num == 1 || num == 2 || num == 3) { System. 22. 6. 15.zetcode. 8. So if the age is greater or equal to 18. $ java com. The first phase of the ternary operator is the condition expression evaluation.} In most countries the adulthood is based on your age. public class PrimeNumbers { public static void main(String[] args) { int[] nums = {1.out.zetcode. 21. 12. System. 14. 7.print(num + " "). This is a situation for a ternary operator. 23. You are adult if you are older than a certain age. 24}. 5. 3. 11.out. } if (isPrime) { isPrime = false. Calculating prime numbers In the following example. 89 . continue. boolean isPrime = true. 17.print("Prime numbers: "). 9. 18. the value following the : character is returned. 13. 4. } int i = (int) Math. 2. 16.TernaryOperator Adult: true A 31 years old person is adult. The returned value is then assigned to the adult variable. 10. In this part of the Java tutorial. we can divide by numbers up to the square root of the chosen number. 3 numbers. 3. we covered Java expressions.. 13. 9. In the above example. if (num % i == 0) { } isPrime = false.print(num + " "). 4. 6. 11. 18. we terminate the loop. 23. 22. We pick up a number and divide it by numbers. we have number 9. 5. int[] nums = {1.. 10. i--.print(num + " "). The i is the calculated square root of the number. The == has a higher precedence than the || operator.print('\n'). 7. The formula will work. int i = (int) Math. we do not have to try all smaller numbers. When the i is smaller than 1. We will calculate primes from these numbers. If the remainder division operator returns 0 for any of the i values. while (i > 1) { . 2. The square root of 9 is 3. This is sufficient for our calculation. 90 . We will divide the 9 number by 3 and 2.out. 21.sqrt(num). System. 12. We are OK if we only try numbers smaller than the square root of a number in question. 19. we deal with several operators. We use the decrement operator to decrease the i by one each loop cycle.} } } } System. Actually. 20.out. 15. 14. We use the remainder division operator. from 1 up to the picked up number. } We skip the calculations for the 1. They are primes. 2. then the number in question is not a prime. A prime number (or a prime) is a natural number that has exactly two distinct natural number divisors: 1 and itself. continue. if (num == 1 || num == 2 || num == 3) { System. 8. 16. Note the usage of the equality and conditional or operators. 17. So we do not need to use parentheses.out. 24}. } This is a while loop. For example. we print a message to the terminal. A block is code enclosed by curly brackets. int num = r. public class IfStatement { public static void main(String[] args) { Random r = new Random().util. if (num > 0) { 91 . The if statement The if statement has the following general form: if (expression) { } statement. These two lines generate a random integer. } } } A random number is generated. If it is true. Some statements are called conditional statements. Statements can be executed multiple times.out. One by one. A compound statement consists of multiple statements enclosed by a block. The brackets are optional if we have only one statement in the body. we will talk about the flow control. import java. If the number is greater than zero.nextInt().zetcode. a statement is then executed. if (num > 0) { System. The if keyword is used to check if an expression is true. This flow can be altered by specific keywords. We will define several keywords that enable us to control the flow of a Java program.Flow control In this part of the Java tutorial. package com. Random r = new Random().nextInt(). int num = r. the statements are executed from the top of the source file to the bottom. They are executed only if a specific condition is met.Random. The number can be both positive and negative. When the program is run. In Java language there are several keywords that are used to alter the flow of the program.println("The number is positive"). The statement can be a single statement or a compound statement. nextInt(). In our case.} System. or the block following the else keyword. we place an expression. int num = r. import java. then the block enclosed by two curly brackets is executed.Branch The number is positive $ java com. The if keyword is followed by a pair of round brackets. If the random value is negative. the string "The number is positive" is printed to the terminal. the statement following the else keyword is automatically executed.println("The number is positive"). } else { System. If the expression inside the square brackets following the if keyword evaluates to false.zetcode.println("The number is negative"). if (num > 0) { System. package com. Using the if keyword.Branch 92 .Random. The curly brackets are optional if we have only one expression. It has its own block enclosed by a pair of curly brackets. If the boolean value is true. Inside the brackets.out. we check if the generated number is greater than zero. nothing is done. if (num > 0) { System.util.zetcode.out.println("The number is negative"). } The else keyword follows the right curly bracket of the if block. public class Branch { public static void main(String[] args) { Random r = new Random(). } } } Either the block following the if keyword is executed.println("The number is positive").out.out.out. The expression results in a boolean value. We can use the else keyword to create a simple branch.println("The number is positive").Branch The number is negative $ java com. } else { System.zetcode. $ java com.zetcode. Note that we can use multiple else if keywords in our tests. A prompt to enter an integer is written to the standard output.out. 93 . package com.in). we read an integer value from the standard input.util package. } else if (num == 0) { System. } else if (num == 0) { System. System. The else if keyword tests for another condition.println("The integer is negative"). int num = sc.out. if (num < 0) { System. The following program will fix this.println("The integer equals to zero").out. Zero was given to negative values.print("Enter an integer:"). } else { } } } System.out. The previous program had a slight issue.out. Using the Scanner class of the java.Scanner. if and only if the previous condition was not met. import java. This is a sample output.util. if (num < 0) { System. Scanner sc = new Scanner(System. Scanner sc = new Scanner(System.println("The integer equals to zero").zetcode.nextInt(). We receive a value from the user and test it if it is a negative number or positive.out. We can create multiple branches using the else if keyword. or if it equals to zero.The number is negative We run the example three times.println("The integer is negative").nextInt().in).print("Enter an integer:").out.println("The integer is positive"). int num = sc. public class MultipleBranches { public static void main(String[] args) { System. MultipleBranches Enter an integer:-3 The integer is negative We run the example three times. The else block is always executed if the previous conditions were not met. Scanner sc = new Scanner(System. We have a variable or an expression. package com. The switch keyword is used to test a value from the variable or the expression against a list of values. The switch statement The switch statement is a selection control flow statement. domain = domain. If the first condition is not met.out.} else { } System.out.zetcode. The list of values is presented with the case keyword. else if statements.println("The integer is positive"). the first block is executed and the remaining two blocks are skipped.zetcode. then the second condition following the if else keywords is checked. the second block is executed.trim(). The zero is correctly handled. It allows the value of a variable or expression to control the flow of a program execution via a multi-way branch. If the values match. switch (domain) { case "us": 94 .toLowerCase(). so that all conditions are tested. public class SwitchStatement { public static void main(String[] args) { System. If not. There is an optional default statement.g.in). the statement following the case is executed. e. If the first condition evaluates to true.print("Enter a domain:").util.Scanner.nextLine().zetcode.MultipleBranches Enter an integer:0 The integer equals to zero $ java com. the third block following the else keyword is executed. It creates multiple branches in a simpler way than using the combination of if. import java.zetcode. Each branch is ended with the break keyword. It is executed if no other match is found. the entered value is less than zero. $ java com. If the second condition evaluates to true. String domain = sc.MultipleBranches Enter an integer:4 The integer is positive $ java com. In this case option. String domain = sc. If one of the options is successfully evaluated. domain = domain. enum or String data type. 95 .println("Hungary").println("Germany"). There are several options.System. the break keyword terminates the switch block. the switch keyword takes an input which is going to be tested. The variable is tested with the switch keyword against a list of options. In our program.println("Slovakia"). we have a domain variable. we test if the domain variable is equal to "us" string. case "us": System. short. The input can be of byte. The toLowerCase() converts the characters to lowercase.toLowerCase().println("United States").println("Unknown")..out.println("United States"). The option is ended with the break keyword. Now the "us". If true. "US". If the value equals for example to "us" the "United States" string is printed to the console. default: System. case "sk": System. The trim() method strips the variable from potential leading and trailing white spaces. The domain name is read and stored in a variable. We read a value for the variable from the command line. int. Each option is ended with the break keyword. } } } The user is requested to enter a domain name. case "hu": System. "us " are viable options for the us domain name. we print a message to the console. Input from the user is read from the console.trim(). case "de": System.out. In the body.in). break.out. we can place multiple case options.out.. switch (domain) { . break. break. The body of the switch keyword is placed inside a pair or curly brackets. break. char.out. } In the round brackets. break.nextLine(). Scanner sc = new Scanner(System. We use the case statement to test for the value of the variable. break.out. $ java com. } In the code example. This is the general form of the while loop: while (expression) { } statement. then the default section is executed. The statements are executed each time the expression is evaluated to true. int sum = 0. Initialization. } } System. break. we calculate the sum of values from a range of numbers.default: System. 96 . The while statement The while statement is a control flow statement that allows code to be executed repeatedly based on a given boolean condition. public class WhileStatement { public static void main(String[] args) { int i = 0.out. It is used as a counter.out. The while keyword executes the statements inside the block enclosed by the curly brackets.zetcode. We initiate the i variable.println(sum). Each execution of the statement is called a cycle.println("Unknown").SwitchStatement Enter a domain:us United States This is a sample output.zetcode. sum += i. The default keyword is optional. The while loop has three parts. package com. testing and updating. If none of the case options is evaluated. int i = 0. while (i < 10) { i++. We increment the counter. Even if the condition is not met. . public class DoWhile { public static void main(String[] args) { int count = 0. The for statement When the number of cycles is know before the loop is initiated.. i++. the testing.WhileStatement 55 The program calculated the sum of 0. } while (count != 0). 9 values. There is a modified version of the while statement. The last phase of the while loop is the updating.out. $ java com.zetcode.zetcode.println(count).. In our case.. In this construct we declare a counter variable.println(i).while (i < 10) { .zetcode. do { } } System. package com. i++) { System. public class ForStatement { public static void main(String[] args) { for (int i = 0.. which is automatically increased or decreased in value during each repetition of the loop. The statements in the body are executed until the expression is evaluated to false. } The expression inside the round brackets following the while keyword is the second phase. First the block is executed and then the truth expression is evaluated. It is guaranteed that the statements inside the block are run at least once.out. the condition is not met and the do while statement terminates. Note that improper handling of the while loops may lead to endless cycles. we can use the for statement. package com.. } } 97 . It is the do while statement. 1. i < 10. the statement inside the for block is executed.length .out. for (int i = 0. i < planets. when the counter i is equal to 10. public class ForStatement2 { public static void main(String[] args) { String[] planets = {"Mercury".1. 98 . "Earth". Next comes the condition. System. A for loop can be used for easy traversal of an array. } The arrays are accessed by zero-based indexing.println(i).length. for (int i = 0. we print the values in ascending and descending orders. "Saturn". we print numbers 0.out.} In this example. "Mars". i++) { System. i >= 0. If the condition is met. i--) { System.println(planets[i]).length. package com. i >= 0. } There are three phases. for (int i = planets. The first item has index 0.1.9 to the console. First. In the final phase.out. "Jupiter". "Pluto"}. Then comes the third phase. the i variable is initialized to zero. i++) { } System. Now we repeat the 2. for (int i = 0.out.println(planets[i]).zetcode. i++) { System. i--) { System. This phase is done only once. "Uranus". the for loop stops executing.println("In reverse:").out. we initiate the counter i to zero. 3 phases until the condition is not met and the for loop is left. for (int i = planets. From the length property of the array. } } } We have an array holding the names of planets in our Solar System.out. In our case. the couter is increased. i < planets. the i variable is incremented. The condition checks if the i variable is less than the lenght of the array. Using two for loops. we know the size of the array.. Therefore.length .println(planets[i]). "Venus".println(planets[i]). i < 10. } } In our example. import java. $ java com.util.nextInt(10). i++. package com.util. System. int num. 9.nextInt(10). the i counter is decremented by one. for (int i = 0. values[i] = num. (Array indexes cannot be negative). the last element has index array size-1. } In the third part of the for loop. we have two expressions separated by a comma character. A sum of the numbers is calculated. for (int i = 0. 0. 9. i++. we create an array of ten random numbers. 5. public class ForStatement3 { public static void main(String[] args) { Random r = new Random().Arrays. 99 . The i counter is incremented and the current number is added to the sum variable. } System. import java.} This for loop prints the elements of the array in reverse order.println("The sum of the values is " + sum).toString(values)). 5. values[i] = num. int sum=0.zetcode. More expressions can be placed in the initialization and iteration phase of the for loop. sum += num) { num = r. Since the indexing is zero based. 3] The sum of the values is 51 This is sample execution of the program. 2. 8. In the third step. The condition ensures that the counter is greater or equal to zero. sum += num) { num = r. The i counter is initialized to array size. i < 10.out. i < 10.zetcode.out. 9.println(Arrays.Random.ForStatement3 [1. int[] values = new int[10]. "Pluto" }.out.Random. "Earth". } } } In this example. The planets is the array that we iterate through. package com.The enhanced for statement The enhanced for statement simplifies traversing over collections of data. "Uranus".println(planet).zetcode. for or switch statements. 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. $ java com.out. } The usage of the for statement is straightforward.zetcode. for (String planet : planets) { System. for (String planet : planets) { System. "Jupiter". 100 . The for statement goes through all the planets and prints them to the console.util. The break statement The break statement can be used to terminate a block defined by while.println(planet). It has no explicit counter. The planet is the temporary variable that has the current value from the array.zetcode. we use the enhanced for statement to go through an array of planets. "Mars". "Venus". "Saturn". package com. public class EnhancedFor { public static void main(String[] args) { String[] planets = { "Mercury". import java.EnhancedFor Mercury Venus Earth Mars Jupiter Saturn Uranus Pluto Running the above Java program gives this output. .nextInt(30). We should avoid using such loops. we finish the endless while loop. We use the break statement to get out of this loop. When the randomly chosen value is equal to 22.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. while (true) { int num = random. Note that such code is error-prone. We define an endless while loop.. while (true) { . break. We choose a random value from 1 to 30. $ java com. If the value equals to 22.out. the break statement is executed. The continue statement The continue statement is used to skip a part of the loop and continue with the next iteration of the loop.print(num + " ").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.zetcode.public class BreakStatement { public static void main(String[] args) { Random random = new Random(). 101 . It can be used in combination with for and while statements. if (num == 22) { } } } } System. if (num == 22) { } break. We print the value.BreakStatement 23 19 29 27 3 28 2 2 26 0 0 24 17 4 7 12 8 20 22 $ java com. System.zetcode. We must terminate the loop ourselves.out. The while loop is terminated. } Placing true in the brackets of the while statement creates an endless loop.print('\n'). There are three widely used programming paradigms there. } We iterate through numbers 1. Object-oriented programming (OOP) is a programming paradigm that uses objects and their interactions to design applications and computer programs.. Object-oriented programming In this part of the Java tutorial. if ((num % 2) == 0) { continue. we will talk about object-oriented programming in Java. The next iteration is started. } If the expression num % 2 returns 0. There are some basic programming concepts in OOP: • Abstraction 102 .zetcode.print(num + " "). the number in question can be divided by 2.out. In this part of the Java tutorial. functional programming and object-oriented programming. } } } System. The continue statement is executed and the rest of the cycle is skipped.out.99 with the while loop. while (num < 100) { num++. Java supports both procedural and object-oriented programming.print('\n'). if ((num % 2) == 0) { continue. System. package com. we will print a list of numbers that cannot be divided by 2 without a remainder.In the following example. the last statement of the loop is skipped and the number is not printed to the console. we were talking about control flow structures. In our case. public class ContinueStatement { public static void main(String[] args) { int num = 0. Procedural programming. package com. send messages and process data. we create objects. In an OOP program. But we have not defined any method yet. We print the object to the console to get some basic description of the object. The encapsulation hides the implementation details of a class from other objects. class Being {} This is a simple class definition. class Being {} public class SimpleObject { public static void main(String[] args) { Being b = new Being().println(b). First. An object is a combination of data and methods. The b variable is the handle to the created object. System. Each object can receive messages. we define a class. It is because every object created inherits from the 103 . These objects communicate together through methods. to print an object? When we print an object. The inheritance is a way to form new classes using classes that have already been defined. Being b = new Being(). A class can be used to create many objects. There are two steps in creating an object. It does not have any data or methods. For this we have the new keyword. The polymorphism is the process of using an operator or function in different ways for different data input.out. The body of the template is empty. It is a blueprint which describes the state and behavior that the objects of the class all share.out. System. Objects created at runtime from a class are called instances of that particular class. } } In our first example. we create a simple object.zetcode. Objects Objects are basic building blocks of a Java OOP program. What does it mean. A class is a template for an object. we in fact call its toString() method. We create a new instance of the Being class.println(b).• • • Polymorphism Encapsulation Inheritance The abstraction is simplifying complex reality by modeling classes appropriate to the problem. zetcode.println(p2.class SimpleObject.SimpleObject com. An instance variable is a variable defined in a class. } } In the above Java code.out. p1.Being@125ee71 We get a the name of the class of which the object is an instance.println(p1. } public class ObjectAttributes { public static void main(String[] args) { Person p1 = new Person(). System.name).out.name = "Beky". System.class is the application class and the Being. The public keyword specifies that the member field will be accessible outside the class block. class Person { public String name. Object attributes Object attributes is the data bundled in an instance of a class. Person p1 = new Person(). class Person { } public String name. 104 .java The compiler creates two class files.java $ ls com/zetcode/ Being. we have a Person class with one member field.zetcode. $ javac com/zetcode/SimpleObject.class SimpleObject.class is the custom class that we work with in the application. and the unsigned hexadecimal representation of the hash code of the object. the @ characer. $ java com. We declare a name member field. for which each object in the class has a separate copy.zetcode. Person p2 = new Person(). The SimpleObject.name = "Jane". One of this is the toString() method. package com. p2. It has some elementary functionality which is shared among all objects created. The object attributes are called instance variables or member fields.base Object.name). zetcode. package com. Methods bring modularity to our programs. Console. p2.WriteLine(p2.radius * Math. We need not to be informed how exactly the method connect() connects to the database.name). We create another instance of the Person class. class Circle { private int radius. We only have to know that it is used to connect to a database. Here we set the variable to "Beky". public double area() { return this. They are used to perform operations with the attributes of our objects. Console.zetcode.name = "Beky".name = "Jane". Especially in large applications. This is essential in dividing responsibilities in programming.name). We print the contents of the variables to the console. We use the dot operator to access the attributes of objects. we might have a connect() method in our AccessDatabase class. We create an instance of the Person class and set the name variable to "Jane". $ java com. public void setRadius(int radius) { } this.PI. Objects group state and behavior.radius * this.WriteLine(p1. For example. Person p2 = new Person(). Each instance of the Person class has a separate copy of the name member field.ObjectAttributes Jane Beky We see the output of the program.p1.radius = radius. Methods represent the behavioral part of the objects. Methods Methods are functions defined inside the body of a class. Methods are essential in the encapsulation concept of the OOP paradigm. } } public class Methods { public static void main(String[] args) { 105 . The this variable is a special variable which we use to access the member fields from 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. e. public void setRadius(int radius) { } this.g.setRadius(5).setRadius(5).radius * Math. The protected members can be accessed only within the class itself. We use the dot operator to call the method. we have a Circle class. the private members are limited to the containing type. We create an instance of the Circle class and set its radius by calling the setRadius() method on the object of the circle. If we want to modify this variable from the outside. only within its class or interface. by inherited classes and other classes from the same package. valid only inside the setRadius() method. The private keyword is an access specifier. $ java com. c.Methods 78. This way we protect our data.radius = radius. Java has three access modifiers: public. It is the radius of the circle. public double area() { } return this.Circle c = new Circle().radius is an instance variable. c. we must use the publicly available setRadius() method. The Math. while the radius is a local variable. In the code example. It tells that the variable is restricted to the outside world.area()).PI. The this. Access modifiers Access modifiers set the visibility of methods and member fields. We have one member field in the class.radius * this. In the class. This is the setRadius() method.53981633974483 Running the example we get the above output. Finally.println(c. The public members can be accessed from anywhere.zetcode.PI is a built-in constant. we define two methods. The area() method returns the area of a circle. Circle c = new Circle().out. } } System. protected and private. private int radius. If we do not specify an access 106 . public int getAge() { return this. the only way to access it is via methods. the method must be declared public.age. } If a member field is private. private int age. public void setAge(int age) { this. p.setAge(17).modifier. p.format("%s is %d years old". They make the programs more robust. package com.name = "Jane". this. we have two member fields.println(String. the other private. } } In the above program.age = age. } The setAge() method enables us to change the private age variable from outside of the class definition.age = age. } public void setAge(int age) { } } public class AccessModifiers { public static void main(String[] args) { Person p = new Person(). This is an important aspect of data protection. members and methods are accessible within the same package. public int getAge() { return this. In such a case. If we want to modify an attribute outside the class.out. we have a package-private visibility. System.age. p. One is declared public.getAge())).name. p. 107 . Access modifiers protect data against accidental modifications.zetcode. class Person { public String name. private boolean isDefined = true. which is printed to the console. Because the name attribute is public.println(this.out. The following program will show how access modifiers influence the way members are inherited by subclasses. The age cannot be accessed or modified directly.println(this.name = "Jane".name).println(this. because it is declared private. System.println(String.AccessModifiers Jane is 17 years old Running the example we have this output. p. Finally.zetcode.println("Members inherited:"). p. System. this is not recommended. } } public class ProtectedMember { public static void main(String[] args) { Derived drv = new Derived(). // System. We create a new instance of the Person class. we access both members to build a string.out. p. we can access it directly.out. However. drv. System. } class Derived extends Base { public void info() { System.out.out.info().name.setAge(17).zetcode. } } 108 .isDefined). p. $ java com. The setAge() method modifies the age member field.println("This is Derived class").id). protected int id = 5323. System.format("%s is %d years old". class Base { public String name = "Base".Person p = new Person(). package com.getAge())).out. being)).println(this. The new keyword invokes them.isDefined). The public and the protected members are inherited by the Derived class. 109 . Java provides an implicit default constructor. native. we receive this output. or static. Constructors cannot be directly invoked. They can be accessed. Constructors cannot be inherited. class Being { public Being() { System. All with different access modifiers. Constructors cannot be declared synchronized.out.out. If we do not write any constructor for a class.ProtectedMember This is Derived class Members inherited: Base 5323 Running the program. Constructors have the same name as the class. $ java com. we use the extends keyword.id). They are called in the order of inheritance. abstract. the code would not compile. If we provide any kind of a constructor then the default is not supplied. The isDefined member is not inherited. The purpose of the constructor is to initiate the state of an object.zetcode. System. } public Being(String being) { } } public class Constructor { System. To inherit from another class.out.println(String.format("Being %s is created". It is automatically called when the object is created.In this program. The private modifier prevents this. so they can be overloaded too. Constructors do not return values and also do not use the void keyword. System.name). we have a Derived class which inherits from the Base class.println("Being is created"). If we uncommented the line.out.println(this.out. The constructor A constructor is a special kind of a method. package com. // System.zetcode. The line accessing the member field is commented. final. The Base class has three member fields. class Derived extends Base { The Derived class inherits from the Base class.println(this. The constructors are methods. The private member is not inherited. Constructor Being is created Being Tom is created This is the output of the program. Normally this would be a suspicious activity. Another instance of the Being class is created. package com. Initiation of variables is a typical job for constructors. 110 . This class has two constructors. The first one does not take parameters.Calendar.zetcode. public Being() { } System. $ java com.util. An instance of the Being class is created. This constructor does not take any parameters. new Being("Tom"). import java. we initiate data members of the class. This time the constructor with a parameter is called upon object creation. The no-argument constructor is called upon object creation. @SuppressWarnings("ResultOfObjectAllocationIgnored") This annotation will suppress a warning that we do not assing our created objects to any variables.println(String. This constructor takes one string parameter. the second one takes one parameter.out. } } We have a Being class. import java.@SuppressWarnings("ResultOfObjectAllocationIgnored") public static void main(String[] args) { new Being(). In the next example.println("Being is created").out. public Being(String being) { } System.format("Being %s is created". new Being().util. new Being("Tom"). being)).zetcode.GregorianCalendar. YEAR)). } } public class MemberInit { public static void main(String[] args) { String name = "Lenka".class MyFriend { private GregorianCalendar born. this.MemberInit Lenka was born on 5/3/1990 111 . this.get(Calendar. When the names of constructor parameters and the names of members are equal. this.out. fr. this. } public void info() { System. using this keyword is required.format("%s was born on %s/%s/%s\n".get(Calendar. 5). The this variable is a handler used to reference the object variables. we initiate the two data members.DATE). $ java com. private String name.name.born = born. 3. private String name.born. Then we call the info() method of the object. Otherwise the usage is optional. } In the constructor.name = name.get(Calendar. born). private GregorianCalendar born. born). We create a MyFriend object with two arguments. public MyFriend(String name. MyFriend fr = new MyFriend(name.name = name. MyFriend fr = new MyFriend(name. this. this.info(). GregorianCalendar born) { this.info().born = born. GregorianCalendar born) { this. } } We have a MyFriend class with data members and methods. fr.MONTH). We have two private variables in the class definition.zetcode. GregorianCalendar born = new GregorianCalendar(1990. public MyFriend(String name.born.born. package com. } } return String. y). int y) { this. int x. r.r = r. 1).x = x. y:%d". To call another constructor from the same class. } protected int getY() { } } class Circle extends Shape { private int r. public class ConstructorChaining { public static void main(String[] args) { 112 .MemberInit program.y. getX().zetcode. } protected int getX() { return this. this. public Circle(int r. public Shape(int x.This is the output of the com. To call another constructor from a parent class.y = y. } public Circle() { } this(1. x:%d. return this. private int y. 1. we use the super keyword. this. Constructor chaining Constructor chaining is the ability to call another constructor from a constructor. int y) { super(x.x.format("Circle: r:%d. class Shape { private int x.zetcode. @Override public String toString() { getY()). we use the this keyword. } We have defined two methods to retrieve the values of the coordinates. . int x. protected int getY() { return this. public Shape(int x. protected int getX() { } return this.println(c2). One that takes one parameter and one that does not take any parameters. class Shape { private int x. It defines the radius member which is specific to this shape. 10. public Circle(int r. int y) { super(x.x..println(c1). . class Circle extends Shape { private int r.. } 113 . so the only access possible is through methods. this.r = r. System.out. 10). The members are private. int y) { this. Circle c2 = new Circle().y. y coordinates with the given parameters.y = y. } The constructor of the Shape class initiates the x. } The Shape class is responsible for dealing with the x. System. y coordinates of various shapes. private int y.out..x = x. The class has two constructors. } } We have a Circle class. y). this. } The Circle class inherits from the Shape class..Circle c1 = new Circle(5. println(Math.The first constructor of the Circle class takes three parameters. passing three default values. Class constants It is possible to create class constants. The second constructor takes no parameters. @Override public String toString() { return String. class Math { } public static final double PI = 3. 1.PI). x:1. y:10 Circle: r:1. Note that the super keyword must be the first statement in the constructor.out. The this keyword is used to call the three-parameter constructor of the same class. 114 . x:10. public class ClassConstant { public static void main(String[] args) { System. getY()). y coordinates. } } We have a Math class with a PI constant. package com. public static final double PI = 3. constants are written in uppercase letters.14159265359.zetcode. we provide a string representation of the Circle class. They belong to the class. } Inside the toString() method.format("Circle: r:%d. The radius and the x. In such a case.ConstructorChaining Circle: r:5. $ java com. y:1 This is the output of the example.14159265359. These constants do not belong to a concrete object. we call the parent's constructor passing the coordinates. The second statement initiates the radius member of the circle class. getX(). With the super keyword. y:%d". To determine the x. x:%d. public Circle() { } this(1. y coordinates. By convention.zetcode. we use the inherited getX() and getY() methods. 1). we provide some default values. r. Object o = new Object(). When we call the System.14159265359 Running the example we get the above output.toString()). The static keyword enables to reference the member without creating an instance of the class. Each class created inherits from the base Object. Being b = new Being().println(b. The @Override annotation informs the compiler that the element is meant to override an element declared in a superclass.println(o. The toString() method belongs to this object class.println(b). } } public class ThetoStringMethod { public static void main(String[] args) { Being b = new Being(). The compiler will then check that we did not create any error. } } We have a Being class in which we override the default implementation of the toString() method. System.zetcode. 115 . The toString() method Each object has a toString() method.out. package com. the toString() is being called. class Being { @Override public String toString() { return "This is Being class". $ java com.toString()).out. It returns a human-readable representation of the object.out. @Override public String toString() { } return "This is Being class". System.ClassConstant 3.println() method with an object as a parameter. System. The default implementation returns the fully qualified name of the type of the Object.out. The public keyword makes it accessible outside the body of the class.zetcode.The final keyword is used to define a constant. One custom defined and one built-in.out.out.WriteLine(b).out.ThetoStringMethod java.out. $ java com. System. We create two objects. Important benefits of inheritance are code reuse and reduction of complexity of a program. This time. System. Console.println() will call its toString() method.zetcode.println("Human is created").toString()). System. As we have specified earlier. class Being { public Being() { } } class Human extends Being { public Human() { } } public class Inheritance { @SuppressWarnings("ResultOfObjectAllocationIgnored") public static void main(String[] args) { new Human().println("Being is created"). placing an object as a parameter to the System. package com.toString()).zetcode. 116 .Object@125ee71 This is Being class This is Being class This is what we get when we run the example. We call the toString() method explicitly on these two objects.out.Object o = new Object().println(b. } } System. Inheritance The inheritance is a way to form new classes using classes that have already been defined.lang. The newly formed classes are called derived classes. the classes that we derive from are called base classes. The derived classes (descendants) override or extend the functionality of base classes (ancestors).println(o. we have called the method implicitly. In this program.Inheritance Being is created Human is created We can see that both constructors were called.println("Human is created"). A base Being class and a derived Human class. We instantiate the derived Human class. package com. A more complex example follows. $ java com. } } class Human extends Being { public Human() { System. class Human extends Being { In Java. we use the extends keyword to create inheritance relations. } } class Animal extends Being { public Animal() { System. we have two classes. then the constructor of the derived class. System. count).format("There are %d Beings%n". the constructor of the base class is called. new Human(). The derived class inherits from the base class.zetcode.println("Animal is created"). First.zetcode.out. public Being() { count++.println("Being is created").out. } } class Dog extends Animal { public Dog() { 117 .out. } public void getCount() { System. class Being { static int count = 0.out. Dog dog = new Dog(). The Animal inherits from the Being and the Dog inherits from the Animal..out. we increase the count variable by one.out.getCount(). } } We have four classes. static int count = 0. public class Inheritance2 { @SuppressWarnings("ResultOfObjectAllocationIgnored") public static void main(String[] args) { new Human(). class Animal extends Being { . And the Dog class inherits directly from the Animal class and indirectly from the Being class. the Dog inherits from the Being as well.Inheritance2 Being is created Human is created Being is created Animal is created Dog is created There are 2 Beings 118 . This way we keep track of the number of instances created. We define a static variable.println("Dog is created").} } System. Dog dog = new Dog(). Static members are shared by all instances of a class.. public Being() { count++. dog. class Dog extends Animal { . The inheritance hierarchy is more complicated.. The Human and the Animal classes inherit from the Being class. System. new Human().println("Being is created"). Indirectly.getCount(). We create instances from the Human and from the Dog classes. $ java com. } Each time the Being class is instantiated. We call the getCount() method of the Dog object..zetcode. dog. } public Shape(int x. y coordinates. public Circle(int r.y = y. The Shape class and the Circle class. like the x. public Shape() { } System.println("Shape is created"). We can put into this class some commonalities of the common shapes.out. 6). y). } @Override public String toString() { } } public class Shapes { public static void main(String[] args) { Circle c = new Circle(2. x:%d. protected int y. We have two classes.println("Shape is created"). y).zetcode. r. } } return String. int y) { this.format("Circle. package com. The Shape class is a base class for geometrical shapes. } } class Circle extends Shape { private int r. class Shape { protected int x.out. The Dog object calls three constructors.r = r. 5. 119 . y:%d". this. x. this.The Human object calls two constructors. System. int y) { super(x.println(c).out. r:%d. int x. public Shape() { System. There are two Beings instantiated. We use the super() keyword to call the parent's constructor explicitly.x = x. private constructor A class with a final modifier cannot be subclassed. We have a MyMath class. y:6 We can see the output of the com. As a consequence. Therefore. Final class. int x.x = x. This class has some static members and methods. } This is the constructor for the Circle class.println(MyMath.out.PI).zetcode. we declare it to be final.Shapes Circle. $ java com. the default constructor of the Shape class would be called. package com. int y) { this. y coordinates. } The Shape class has two constructors.y = y. the coordinates are initiated in the Shape class. int y) { super(x. Declaring a private constructor. public Circle(int r. y). the class cannot be instantiated. We do not want anyone to inherit from our class.zetcode.zetcode. 120 . } // other static members and methods public class FinalClass { public static void main(String[] args) { } } System. y coordinates.14159265358979323846. r:2.public Shape(int x. Have we not called the constructor explicitly with the super keyword. x:5.Shapes program. this. Furthermore. final class MyMath { public static final double PI = 3. to which it passes the x. A class with a constructor that has a private modifier cannot be instantiated. This constructor initiates the r member and calls the parent's second constructor.r = r. we also do not want to allow creation of instances from our class. We decide it to be used only from a static context. The second one takes two parameters: the x. this. The first one is a no-argument constructor. A method is a code block containing a series of statements. Methods bring modularity to programs.zetcode. Objects consists of data and methods. Methods change the state of the objects created. Our MyMath class cannot be instantiated and cannot be subclassed. } // other static members and methods public class PrivateConstructor { public static void main(String[] args) { } } System.lang. we work with objects.out.println(MyMath. you will learn about Java methods.14159265358979323846.PI). final class MyMath { private MyMath() {} public static final double PI = 3. This was the first part of the description of OOP in Java. This is how java.package com. the data is the static part. Methods In this part of the tutorial.Math is designed in Java language. In object oriented programming. They are the dynamic part of the objects. Objects are the basic building blocks of a program. Methods must be declared within a class. It is a good programming practice that methods do only one specific task. 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 . They set the visibility of methods. Any legal character can be used in the name of a method.out. They determine who can call the method. Each subsequent word starts with an uppercase character. Methods may return a value to the caller. The method names are verbs or verbs followed by adjectives or nouns.showInfo(). that are executed.println("This is Base class"). We have a showInfo() method that prints the name of its class. reference. The block contains one or more statements. Method signature does not include the return type. } } System. we declare its data type. Method parameters are surrounded by parentheses and separated by commas. The method block is surrounded with { } characters. Empty parentheses indicate that the method requires no parameters. class Base { public void showInfo() { 122 .zetcode. By convention. when the method is invoked.Access level of methods is controlled with access modifiers. class Base { public void showInfo() { } } public class ShowInfoMethod { public static void main(String[] args) { Base bs = new Base(). A method signature is a unique identification of a method for the Java compiler. and the type and kind (value. It is legal to have an empty method block. method names begin with a lowercase letter. or output) of each of its formal parameters. bs. The signature consists of a method name. The following are typical names of methods in Java: • • • • • • execute findId setName getName checkIfValid testValidity Simple example We start with a simple example. we use the void keyword to indicate that our method does not return any value. package com. In case our method returns a value. If not. 13). The keywords that precede the name of the method are access specifier and the return type. We create an instance of the Base class.zetcode. System.showInfo(). Parentheses follow the name of the method. Because it needs an instance to be called.. Method parameters A parameter is a value passed to the method.println("This is Base class"). package com. 123 . } } public class Addition { public static void main(String[] args) { AddValues a = new AddValues(). class AddValues { public int addTwoValues(int x.addThreeValues(12. followed by the member access operator — the dot. public static void main(String[] args) { .out. Methods can take one or more parameters. int x = a. The method is called by specifying the object instance. Our method does not take any parameters. int z) { return x + y + z. bs.out.addTwoValues(12. The method takes a string array of arguments. Base bs = new Base().} } System. public int addThreeValues(int x. we must pass the data to the methods. It must have a name. int y) { } return x + y. In our case the name is showInfo. } This is the main() method. In the method definition. we must provide a name and type for each parameter. Each method must be defined inside a class. They may contain parameters of the method. 14).out.println(x). We call the showInfo() method upon the object. int y = a. 13. We do it by specifying them inside the parentheses. System.println(y).. followed by the method name. int y. It is the entry point to each console or GUI Java application. If methods work with data. We say that the method is an instance method. These parameters have int type. The method returns a value which is assigned to the x variable. 2. System.out.println(s3). 4. For this we use the ellipsis. for (int val : vals) { sum += val.. the other one takes three parameters. 3. System.. 13). We use the return keyword to return a value from the method. int y) { } return x + y. One of them takes two parameters. It takes two values. } public static void main(String[] args) { int s1 = sum(1. int z) { } return x + y + z. public class SumOfValues { public static int sum(int. 5). We call the addTwoValues() method of the AddValues object. The method also returns an integer to the caller. 6. Variable number of arguments Since Java 5. The addTwoValues() method takes two parameters. 124 . 7). 3. It takes three parameters. 2. public int addTwoValues(int x. 5.out. int x = a.zetcode. a method can take variable number of arguments.println(s2).out. } return sum. The addThreeValues() is similar to the previous method.addTwoValues(12. we have an AddValues class which has two methods. int s3 = sum(1. int s2 = sum(1. 3). int y.} } In the above example. System.println(s1).vals) { int sum = 0. public int addThreeValues(int x. These values are passed to the method. package com. 4. 2. arguments are always passed by value to methods.. 125 . package com. public static int sum(int. We compute the sum of the values and return the calculated sum. copies of the references are handed to the methods.vals) { . like C# or C++ languages. 3). In case of objects. 3. int s3 = sum(1. 2.} } We create a sum() method which can take variable number of arguments.. we pass different number of parameters to the method. the copies of the values are sent to the methods. In each case. $ java com. When we pass primitive types. for (int val : vals) { sum += val. 7). } The sum() method can take variable number of integer values. 4. int sum = 0.. 4. The method will calculate the sum of integers passed to the method.zetcode. All values are added to an array. We call the sum() method three time. int s1 = sum(1.. 2. 2.SumOfValues 6 15 28 This is the output of the com. 6. class Cat {} class Dog {} public class PassByValue { private static void tryChangeInteger(int x) { x = 15. 3. } return sum.zetcode. Java does not support passing arguments by reference. int s2 = sum(1. 5).zetcode. Passing arguments by value In Java. 5.SumOfValues example. System. tryChangeObject(c). Cat c = new Cat().} private static void tryChangeObject(Object o) { Dog d = new Dog(). We define a Cat object and pass it to the tryChangeObject() method. o = d.getClass()). } The value of the passed variable is copied to the local variable x. System. tryChangeInteger(n). private static void tryChangeObject(Object o) { Dog d = new Dog().getClass()). The o is a local variable that refers to the Dog object. tryChangeObject(c). System. } The same applies for objects. int n = 10. Assigning a new value to the x variable does not affect the outside variable. $ java com.println(n).out.zetcode. private static void tryChangeInteger(int x) { x = 15. The object defined outside the tryChangeObject() is not affected.println(c.out.out.println(c. } public static void main(String[] args) { int n = 10. o = d. } } The example shows that it is not possible to change the values of primitive types and the references to objects inside methods. Cat c = new Cat().println(n). tryChangeInteger(n). System. We define the n variable and pass it to the tryChangeInteger() method.out. Later we print it to check if it was modified. We are passing copies of references to methods.PassByValue 10 126 . which is the language in which Qt is developed. The solution with method overloading is more elegant.zetcode.out. package com. If the C++ language.out.getSum(5. System. drawRectXYWH(). Their name is drawRect() and their parameters differ. and height. They differ in input parameters.out. } } public class Overloading { public static void main(String[] args) { Sum s = new Sum(). drawRectRect(). Method overloading Method overloading allows the creation of several methods with the same name which differ in the type of the input. The QPainter class has three methods to draw a rectangle. another takes a reference to an integer rectangle object. } 127 . didn't have method overloading. System. } public int getSum(int x) { } return x. the creators of the library would have to name the methods like drawRectRectF(). What is method overloading good for? The Qt4 library gives a nice example for the usage. class Sum { public int getSum() { return 0.println(s.Cat From the output we can see that neither the primitive value nor the object were modified.getSum(5)). public int getSum(int x) { return x. y. 10)). and the last one takes four parameters: x.zetcode.class com. int y) { return x + y. System. } } We have three methods called setSum().println(s. One takes a reference to a floating point rectangle object.getSum()). public int getSum(int x. width.println(s. System.zetcode. The compiler knows which method to call on the grounds of the method input.out. System. public class Recursion { static int factorial(int n) { if (n == 0) { return 1.Recursion 128 . package com.println(factorial(15)). Recursion Recursion.zetcode. a recursive method calls itself to do its job. System. we calculate the factorial of two numbers. $ java com. In other words. The function calls itself. $ java com. } else { } } public static void main(String[] args) { System.out.out. we call the factorial method with a modified argument. in mathematics and computer science. } } return n * factorial(n .out. A typical example is the calculation of a factorial.getSum(5. This is the essence of the recursion algorithm. System. 10)). Inside the body of the factorial method. Every problem solved using a recursion can be worked out with the iteration too.getSum()). Recursion is a widely used approach to solve many programming tasks. In this code example.println(s.zetcode.Overloading 0 5 15 And this is what we get when we run the example. is a way of defining methods in which the method being defined is applied within its own definition.getSum(5)).This one takes one parameter.1). return n * factorial(n .println(s.1).out. All methods have the same name.println(factorial(6)).println(s. We call all three methods. System.out. The variable has a class scope. System. public void exec1() { System. } The x variable.println(x). between its curly brackets.println(z). 129 . Both statements refer to the same variable.out. It is also valid inside the exec1() method and can be referred by its bare name. Method scope A variable declared inside a method has a method scope. And so it is accessible through the this keyword. } public void exec2() { int z = 5.println(x).exec2(). also called the x field. } } In the preceding example.println(this. package com. System.g. A variable which is declared inside a method has a method scope. public void exec1() { System. class Test { int x = 1.out.x).out. System.720 2004310016 These are the results.x). ts. } } public class MethodScope { public static void main(String[] args) { Test ts = new Test().println(this. The variable is valid only in this particular method.out. we have the x variable defined outside the instance methods.zetcode. is an instance variable.exec1(). ts.out. It is valid everywhere inside the definition of the Test class. It is also called a local scope.println(x). e. 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. } The x variable can be accessed also in the exec2() method. Both variables have the same name. A variable defined inside a method has a local/method scope.out.exec(). It is valid only in this method.out. public void exec() { int x = 3. The instance variable is still accessible inside the method by using the this keyword. } } public class Shadowing { public static void main(String[] args) { Test t = new Test(). t.zetcode.println(x).println(this.println(x). System. but they are not in conflict because they reside in different scopes.x). } } We declare an instance variable x. $ java com. class Test { int x = 1.MethodScope 1 1 1 5 This is the output of the com. 130 .out. System. System. System. We declare another x variable inside the exec() method.public void exec2() { int z = 5.x).out.println(this.out.println(x).zetcode. package com. If a local variable has the same name as an instance variable. The z variable is defined in the exec2() method.MethodScope program. System. It has a method scope. it shadows the instance variable.zetcode.out.println(z). System. System. Static methods can only work with static variables.println("This is Basic class"). } } public class StaticMethod { public static void main(String[] args) { } } Basic. An example is a math library which contains static methods for various calculations.The variables are accessed differently.println("This is Basic class").out. System. static int id = 2321. also called the local variable. It works with a static Id member. Static methods Static methods are called without an instance of the object.out. class Basic { static int id = 2321.format("The Id is: %d%n". is simply accessed by its name.zetcode. Static variables are not available to instance methods. we define a static ShowInfo() method.out. The x variable defined inside the method. public static void showInfo() { System. It can be used in instance methods only. id).Shadowing example.showInfo().format("The Id is: %d%n". we use the name of the class and the dot operator. } This is our static ShowInfo() method. A static method can only work with static variables.Shadowing 1 3 This is the output of the com. The instance variable can be referred by using the this keyword. the method is said to be an instance method. To call a static method.zetcode.zetcode. When no static modifier is present. Static methods are often used to represent data or calculations that do not change in response to object state. We use the static keyword to declare a static method or a static variable.out. We cannot use the this keyword in static methods. id). public static void showInfo() { System. In our code example. 131 . package com. $ java com. showInfo().showInfo().out. class Base { public static void showInfo() { } } class Derived extends Base { public static void showInfo() { } } public class Hiding { public static void main(String[] args) { Base. The Derived and the Base class.Basic.println("This is Derived class"). class Derived extends Base { public static void showInfo() { } } System.zetcode. } } System. The method to be called is determined at compile time.out. Hiding methods In case of static methods. 132 . a method in the derived class with the same signature as in the base class hides the one in the base class.out. we do not need an object instance. Derived. We call the method by using the name of the class and the dot operator. The Derived class inherits from the Base class. package com.println("This is Base class"). Both have a method called showInfo(). We have two classes.zetcode. System.showInfo().println("This is Derived class"). $ java com. This process is referred to as early or static binding.StaticMethod This is Basic class The Id is: 2321 This is the output of the example. To invoke a static method. out. 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. package com. for (Base obj : objs) { obj. new Derived().zetcode. } } } System.Hiding example.println("This is Derived class"). } } class Derived extends Base { @Override public void showInfo() { } } public class Overriding { public static void main(String[] args) { Base[] objs = { new Base(). Derived. class Base { public void showInfo() { System. new Base(). We call the showInfo() method for both classes. The determining of the method to be executed at runtime is called late or dynamic binding. The method to be executed is determined at runtime.The static class method showInfo() of the Derived class hides the showInfo() method of the Base class.println("This is Base class"). Base.showInfo(). new Base().showInfo().zetcode.showInfo().zetcode.out. new Derived() }. Each class calls its own method. new Base().Hiding This is Base class This is Derived class This is the output of the com. $ java com. We might want to use the @Override annotation that instructs the compiler that we intend to override a method in the superclass. 133 . It helps prevent some programming errors. We go through the array and invoke the showInfo() method upon all of them. } We traverse the array and call showInfo() on all objects in the array.out.zetcode.zetcode.We create an array of the Base and Derived objects.out. Final methods A final method cannot be overriden or hidden by derived classes.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.println("f1 of the Base"). The only way to have defferent objects in one array is to use a type which is shared by all objects. package com. The opposite is not true. @Override public void showInfo() { System. Here we create an array of Base and Derived objects. class Base { public void f1() { } System. The method to be called is determined at runtime. The Derived class can be converted to the Base class. } Here we are overriding the showInfo() method of the Base class. Note that we used the Base type in our array declaration.println("f2 of the Base"). new Derived() }. new Derived().out. new Base(). because it inherits from it. public final void f2() { System. $ java com.println("This is Derived class"). new Base(). for (Base obj : objs) { obj. 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. new Base(). 134 .showInfo(). Base[] objs = { new Base(). b. @Override public void f1() { System. public final void f2() { } System.out.} } class Derived extends Base { @Override public void f1() { } // // // // // } System. b. // // // // // @Override public void f2() { } System. } In the Derived class. 135 .out.println("f1 of the Derived"). } } In this example we have a final method f2() in the Base class.println("f2 of the Derived").println("f2 of the Base").f2(). We also use the @Override annotation to inform the compiler that we are overriding a method.out. public class FinalMethods { public static void main(String[] args) { Base b = new Base(). The f2() method is declared to be final. d.f1().out.println("f1 of the Derived").println("f2 of the Derived"). @Override public void f2() { } System.out.f1().f2(). d. Derived d = new Derived(). This method cannot be overriden. No overloading is possible. we can override the f1() method of the Base class. a book. Abstract classes and methods Designing an application. Abstract classes may have methods with full implementation and may also have defined member fields. 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.FinalMethods f1 of the Base f2 of the Base f1 of the Derived f2 of the Base This is the output. the above line will call the f2() method of the Base class. So abstract classes may provide a partial implementation.f2(). These methods can be implemented only in the subclasses of the Item class.zetcode. A single abstract class is subclassed by similar classes that have a lot in common (the implemented parts of the abstract class). When we inherit from an abstract class. An item has no weight or color. This way we reduce the size of our code and make our application more compact. $ java com. we often find that our classes have a lot of common functionality. On a desk we have a pen. The compiler would give the following error: Exception in thread "main" java. And these abstract classes are later 136 .lang. d. they merely declare the methods' signatures. Abstract methods cannot be implemented. We can implement a getId() method . The class will contain some common qualities of these items. unreal entity — an idea. In this part of the Java tutorial. because otherwise the code example would not compile. An abstract class or method is created using the abstract keyword. Object-oriented programming II In this chapter of the Java tutorial.zetcode. all abstract methods must be implemented by the derived class or the class must itself be abstract. we have abstract methods and classes.These lines are commented. we have covered methods. but we cannot implenment a getWeight() or getColor() methods in this class. An Item might be considered as a parent class for all these things. For example an id. a pencil or a cup of tea. Programmers often put some common functionality into abstract classes.VerifyError: class com. If a class contains at least one abstract method. We might find that the parent class is intangible. or a color. For these situations.Derived overrides final method f2. a weight. Abstract classes cannot be instantiated but they can be subclassed. Since it is not possible to override a final method. These commononalities can be extracted and put into parent classes. it must be declared abstract too. we continue the description of the OOP. but also have some differences (the abstract methods). this.format("Circle at x: %d. The common features are implemented in the abstract class.r * this. package com.PI. A protocol is a set of operations which all implementing objects must support. this. The static.y = y. Formally put. abstract classes are used to enforce a protocol. y: %d". int r) { this. } } public class AbstractClass { public static void main(String[] args) { 137 . public Circle(int x.x = x. abstract class Drawing { protected int x = 0. Likewise. this.r * Math. providing functionality common to buttons. } @Override public String toString() { return String. this.r = r. private. y: %d. this. because these types of methods cannot be overridden by a subclass. For example.r). a final class cannot have any abstract methods. } } class Circle extends Drawing { private int r.y.x. the differences are hinted by abstract methods.y). } @Override public double area() { return this.zetcode. QRadioButton. QCheckBox. QPushButton.x. protected int y = 0. radius: %d". public String getCoordinates() { return String.format("x: %d. int y. this. and final methods cannot be abstract. this. the Qt graphics library has a QAbstractButton which is the abstract base class of button widgets. public abstract double area(). and QToolButton inherit from this base abstract class and provide their specific functionality.subclassed to provide more specific implementation. Buttons Q3Button. } Here we are implementing the area() method. } } We have an abstract base Drawing class. The Drawing class is abstract. $ java com. We can draw a circle. abstract class Drawing { We use the abstract keyword to define an abstract class. The Drawing class has some common functionality to the objects that we can draw.println(c). An abstract method is also preceded with a abstract keyword. radius: 22 Area of circle: 1520. System. defines one method and declares one method. @Override public double area() { return this. It is unreal and we cannot implement the area() method for it.getCoordinates()). Interfaces A remote control is an interface between the viewer and the TV.area()). c. The method will be implemented in a more concrete entity like a circle. public abstract double area().r * this. a dot or a square.AbstractClass Circle at x: 12. A Drawing class is an idea. class Circle extends Drawing { A Circle is a subclass of the Drawing class. The class defines two member fields.out. y: 45. This is the kind of situation where we use abstract methods.println(c. Diplomatic protocol guides all activities in the diplomatic field. cyclists and pedestrians must follow. Interfaces in programming are analogous to the previous examples.530844 x: 12. 45. System. y: 45 We create a Circle object and print its area and coordinates.out. Therefore it must implement the abstract area() method. One of the methods is abstract. It is an interface to this electronic device. 22).zetcode.PI.format("Area of circle: %f%n".r * Math. Interfaces are: 138 . Rules of the road are rules that motorists. System. because we cannot draw it.out.Circle c = new Circle(12. the other one is fully implemented. These modifiers can be omitted. The body of the interface contains abstract methods.• • APIs Contracts Objects interact with the outside world with the methods they expose. and final. method signatures. Interfaces are fully abstract types. 139 . since all are buttons. It is about inheriting ideas or contracts which are described by the interfaces. similar to a class that can contain only constants. we have two buttons. An interface can also extend any number of interfaces. Whereas classes Database and SignIn are not related to each other. A company might sell a library and it does not want to disclose the actual implementation. without exposing too much about their inner workings. interfaces are ways through which objects interact with the outside world. They are used to design an architecture of an application. Implementing classes are related. The actual implementation is not important to the programmer. From the second point of view. the abstract keyword is not required. interface IInfo { } void doInform(). Interfaces on the other hand can be implemented by classes that are not related to each other. static. they must be followed. For example. An interface can contain constant member declarations in addition to method declarations. Interfaces cannot be instantiated— they can only be implemented by classes or extended by other interfaces. They help organize the code. A Java class can inherit only from one class. Interfaces are used to simulate multiple inheritance. In Java. or it also might be secret. There are no method bodies. package com. Interfaces cannot have fully implemented methods. All constant values defined in an interface are implicitly public. abstract. all methods are implicitly public. We can apply an ILoggable interface that would force them to create a method to do logging. by definition. A classic button and a round button. They are declared using the interface keyword. A programmer might call a maximize() method on a window of a GUI toolkit. but knows nothing about how this method is implemented. If agreed upon. A class that implements an interface must implement all method signatures of an interface. Abstract classes provide partial implementation for classes that are related in the inheritance hierarchy. From this point of view. A Java class may implement any number of interfaces. All interface members implicitly have public access. Since the interface specifies a set of exposed behaviors. an interface is a reference type. interfaces are contracts. Both inherit from an abstract button class that provides some common functionality to all buttons.zetcode. There is one important distinction between interfaces and abstract classes. and nested types. but since all methods in an interface are. A Java class can implement multiple interfaces. Multiple inheritance using the interfaces is not about inheriting methods and variables. class Some implements IInfo { We implement the IInfo interface. It allows to implement multiple interfaces. package com. } interface Volume { void volumeUp(). interface IInfo { void doInform(). void switchOff(). } } This is a simple Java program demonstrating an interface.zetcode. } The class provides an implementation for the doInform() method. The @Override annotation tells the compiler that we are overriding a method. The next example shows how a class can implement multiple interfaces.doInform(). interface Device { void switchOn(). we use the implements keyword. } } public class Interface1 { public static void main(String[] args) { Some sm = new Some(). It has the doInform() method signature.println("This is Some Class"). sm. Java does not allow to inherit from more than one class directly. } This is an interface IInfo. @Override public void doInform() { System. 140 .class Some implements IInfo { @Override public void doInform() { System. To implement a specific interface.out.println("This is Some Class").out. } void volumeDown().plugIn(). } class CellPhone implements Device. Pluggable { @Override public void switchOn() { System. 141 .out.out.out.println("Plugging in"). } @Override public void plugIn() { System. cp. } @Override public void volumeDown() { System.out.println("Plugging off"). void plugOff().volumeUp().println("Volume up"). } @Override public void volumeUp() { System. Volume.println("Switching on").println("Volume down"). cp. } @Override public void switchOff() { System.out. } } public class MultipleInterfaces { public static void main(String[] args) { CellPhone cp = new CellPhone(). } } We have a CellPhone class that inherits from three interfaces. } @Override public void plugOff() { System.out.switchOn(). interface Pluggable { void plugIn(). cp.println("Switching on"). out.zetcode.02").MultipleInterfaces Switching on Volume up Plugging in Running the program we get this output. The next example shows how interfaces can form a hierarchy.class CellPhone implements Device.zetcode. interface ILog extends IInfo. Volume.out. The CellPhone class must implement all method signatures from all three interfaces. Interfaces can inherit from other interfaces using the extends keyword. Pluggable { The class implements all three interfaces which are divided by a comma.out.println("Logging"). } @Override public void getVersion() { System. } class DBConnect implements ILog { @Override public void doInform() { System. package com.println("This is DBConnect class"). } @Override public void doLog() { System. IVersion { void doLog(). interface IInfo { void doInform(). } public void connect() { } } public class InterfaceHierarchy { System.println("Connecting to the database").println("Version 1. $ java com. } interface IVersion { } void getVersion().out. 142 . public static void main(String[] args) { DBConnect db = new DBConnect(). Polymorphism is concerned with the application of specific implementations to an interface or a more generic base class. Therefore it must implement the methods of all three interfaces. db. (Wikipedia) In general. polymorphism is the ability to appear in different forms. } The DBConnect class implements the doInform() method. @Override public void doInform() { System.zetcode. In practical terms.02 Logging Connecting to the database This is the example output. interface ILog extends IInfo. The interfaces are organized in a hierarchy. $ java com.getVersion().zetcode. db. Polymorphism The polymorphism is the process of using an operator or function in different ways for different data input.connect(). db. polymorphism means that if class B inherits from class A. } } We define three interfaces. package com.println("This is DBConnect class"). Polymorphism is the ability to redefine methods for derived classes. it can do some of the things that class A does differently. Technically.out.InterfaceHierarchy This is DBConnect class Version 1. This method was inherited by the ILog interface which the class implements. 143 . it is the ability to redefine methods for derived classes. it doesn't have to inherit everything about class A. IVersion { The ILog interface inherits from two interfaces.doLog(). db. class DBConnect implements ILog { The DBConnect class implements the ILog interface.doInform(). out. new Square(12)}. 4).y. } @Override public int area() { return this. int y) { this.y.x * this. } } public class Polymorphism { public static void main(String[] args) { Shape[] shapes = {new Square(5).x = x. } class Rectangle extends Shape { public Rectangle(int x.x = x. public abstract int area().x * this.y = y. Both provide their own implementation of the area() method.area()). we have an abstract Shape class. this.x. @Override public int area() { return this. In the above program. for (Shape shape : shapes) { } } } System.abstract class Shape { protected int x. } @Override public int area() { return this. } } class Square extends Shape { public Square(int x) { this. Rectangle and Square. 144 .x * this. Polymorphism brings flexibility and scalability to the OOP systems.println(shape. This class morphs into two descendant classes. protected int y. new Rectangle(9. Shape[] shapes = {new Square(5).out. @Override public int area() { return this. } We go through each shape and call the area() method on it.println(shape. public class SNCTest { private static int x = 5. This is the essence of polymorphism. 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. Nested classes It is possible to define a class within another class. 4). We create an array of three Shapes.x. For example in Java Swing toolkit. The compiler calls the correct method for each shape. It has access to the static variables and methods of the enclosing class. new Rectangle(9. A class that is not a nested class is called a top-level class. for (Shape shape : shapes) { System. } The Rectangle and the Square classes have their own implementations of the area() method. new Square(12)}. Static nested classes A static nested class is a nested class that can be created without the instance of the enclosing class. Inner classes are often used as callbacks in GUI.area())..} .x * this..zetcode. static class Nested { @Override public String toString() { 145 . package com. Such class is called a nested class in Java terminology. Inner classes are also called member classes. public static void main(String[] args) { SNCTest. System.} } return "This is a static nested class. Inner classes have acces to the members of the enclosing class. Inner classes An instance of a normal or top-level class can exist on its own.zetcode.zetcode. } } A static nested class is defined.Nested().Nested sn = new SNCTest. } } 146 . package com. public class InnerClassTest { private int x = 5. It has one method which prints a message and refers to the static x variable. static class Nested { @Override public String toString() { return "This is a static nested class. private static int x = 5.zetcode. x:" + x. x:" + x. x:" + x. an instance of an inner class cannot be instantiated without being bound to a top-level class. By contrast. } } The example presents a static nested class. $ java com. class Inner { @Override public String toString() { return "This is Inner class.SNCTest program. It can be accessed by a static nested class.out. x:5 This is the output of the com. This is a private static variable of the SNCTest class.println(sn). They belong to the instance of the enclosing class.SNCTest This is a static nested class. x:5 This is the uput of the com.println(this. InnerClassTest. First. Inner classes cannot exist without an instance of the enclosing class.zetcode. x:" + x.println(Shadowing. InnerClassTest nc = new InnerClassTest(). we need to create an instance of the top-level class.out.this. Shadowing If a variable in the inner scope has the same name as the variable in the outer scope then it is shadowing it. } } An Inner class is defined in the body of the InnerClassTest class. public class Shadowing { private int x = 0. $ java com.new Inner(). package com. void method1(int x) { System. System.InnerClassTest This is Inner class.out.public static void main(String[] args) { InnerClassTest nc = new InnerClassTest(). It is still possible to refer to the variable in the outer scope. we can create the instance of the inner class.x). Once we have the top-level class instantiated.x).Inner inner = nc.println(x). class Inner { @Override public String toString() { return "This is Inner class.println(inner).out.Inner inner = nc. InnerClassTest.out.new Inner(). 147 . } } A nested class is defined in the InnerClassTest class. It has access to the member x variable.zetcode.InnerClassTest program. System.zetcode. class Inner { private int x = 5. System. 148 .zetcode. private. They are not allowed for local variable declarations or local class declarations. the local variables are copied into the local class. or classes. Shadowing. The reason for this is technical. in the inner class and inside a method. Local classes A local class is a special case of an inner class.) A local class has access to the members of its enclosing class. Here we refer to the x variable of the Shadowing top-level class. } } si. System.zetcode. To solve this.method1(10). Except for constants that are declared static and final.println(Shadowing. protected or static. methods.x).Inner si = sh.new Inner().out. To ensure that they are later not changed. package com. Local classes are classes that are defined in a block.} } public static void main(String[] args) { Shadowing sh = new Shadowing(). Local classes cannot be public. This line refers to the x variable defined in the local scope of the method.x). they have to be declared final.this. 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. Using the this keyword. (A block is a group of zero or more statements between braces.println(x). a local class has access to local variables if they are declared final.Shadowing 10 5 0 Example output. We define an x variable in the top-level class. System.println(this. public class LocalClassTest { public static void main(String[] args) { final int x = 5. we refer to the x variable defined in the Inner class. System.out.out. $ java com. local classes cannot contain static fields. In addition. the anonymous class is a subclass of the named class. we cannot define any statements. A local class is defined in the body of the main() method. An anonymous class must implement an interface or inherit from a class. 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.println(loc). the anonymous class implements that interface and extends the Object. They enable us to declare and instantiate a class at the same time.out. 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. } A local class can access local variables if they are declared final. public class AnonymousClass { interface Message { public void send(). only methods or members. Since an anonymous class has no name. package com. x:" + x. If the name following new specifies an interface.class Local { @Override public String toString() { return "This is Local class. } public void createMessage() { Message msg = new Message() { @Override public void send() { 149 . x:" + x.zetcode. it is not possible to define a constructor for an anonymous class. Inside the body of an anonymous class. Anonymous classes Anonymous classes are local classes that do not have a name. System. But the implements and extends keywords are not used. If the name following the new keyword is the name of a class. @Override public String toString() { return "This is Local class. } } } } Local loc = new Local(). The Java source files must be placed in directories that match the package name. } }. } System.out. therefore the enclosing right bracket is followed by a semicolon. This statement must be placed at the top of every source file. interface Message { public void send(). In this part of the Java tutorial. hence it is defined in the body of a method. A package is a grouping of related types providing access protection and name space management. Our anonymous class will implement a Message interface.send(). Packages in Java is a similar concept to namespaces in C#. } } In this code example. A package is declared with the package keyword. Packages In this part of the Java tutorial. ac.out. } An anonymous class must be either a subclass or must implement an interface. 150 . public static void main(String[] args) { AnonymousClass ac = new AnonymousClass(). Otherwise. msg.createMessage(). the type would not be recognized by the compiler. } msg. There can be only one such statement in each source file. we create an anonymous class.send().} }. public void createMessage() { Message msg = new Message() { @Override public void send() { System.println("This is a message"). we will talk about Java packages. An anonymous class is a local class. we continued covering object-oriented programming in Java.println("This is a message"). An anonymous class is defined in an expression. The letters are written in lowercase. import java.util. This code line enables us to use the Random class without the package name.awt. Subpackages must be imported independently.awt. All types defined in the file with the above package are part of the com.println(c. A Java source file with a com. There are millions of Java programmers worldwide. int x = r. We declare a package with the package keyword. there is a naming convention in Java. annotations) or entire Java packages to be referred to later without including their package names in the reference. import java. } } The example uses two types.out.Random. The java.Being.zetcode for the packages will make them unique.0.zetcode.getInstance().*.println(x).zetcode.* import. import java. The Random class and the Calendar class. enumerations. Since Java SE 5.event subpackage is not imported with the java. Using the * wildcard character.java file must be located in the com/zetcode/ subdirectory. 151 .util.util.Calendar c = java.nextInt(). System.com domain name so using a reversed name com.awt.getTime()).util.*.awt. A class Being has a fully qualified name com.Calendar. To avoid potential name conflicts. The package names reversed Internet domain names. The first calss is imported and the second is reffered by its fully qualified name. public class Packages { public static void main(String[] args) { Random r = new Random(). we can refer to all types of the java.zetcode package.zetcode. import statements can import static members of a class (methods and variables).Random. System. package com. java.zetcode package must be located in the com/zetcode/ subdirectory. import java. package com.out. There can be only one zetcode. After this import.package com. The Packages. we can import the whole package at a time. interfaces.event. The import keyword is used at the beginning of a source file to specify types (classes.awt package without their fully qualified names.zetcode. util. $ ls com/zetcode/ Packages.getInstance().util. The import keyword saves a lot of typing. The tool is called from the parent directory of the com/zetcode/ directory. It is recommended to always place your types in a package.Random r = new Random(). we do not place the source file in a specific subdirectory.java We compile the source file with the javac tool.java source file is placed in the com/zetcode/ subdirectory.Packages program.zetcode. In such a case. The package name must reflect the directory structure. The DefaultPackage class is part of the default package. protected or public).java If we do not specify a package. private. $ javac com/zetcode/Packages. Classes in other packages cannot access classes and members declared with package-private access. all types defined in that file are part of a default unnamed package.Calendar in our case. If we did not use the import keyword on a type. variables and methods are accessible within the same package.Calendar.println("A class in a default package").g. Even for small programs. we can refer to it only by its full name java.util.Packages 928256084 Sun Jul 21 15:01:14 CEST 2013 This is the output of the com. public class DefaultPackage { public static void main(String[] args) { } } System. Default package If no package is declared. $ ls DefaultPackage. $ java com.java The Packages. java. Package-private visibility If we do not specify any access modifier (e.out.zetcode. we have a package-private visibility.Calendar c = java. 152 . Here we use the Random without using it's full name. println(Constants. The String and System classes are part of the java. The Constants class is located in the same package as the AutomaticImports class.out. The source file and the bytecode is located in the current working directory.java $ java DefaultPackage A class in a default package We compile the code and run the application.name"). we refer to some classes that are automatically imported by Java compiler.$ javac DefaultPackage.java Both AutomaticImports. public class Constants { public static final String version = "1.out.getProperty("os. } } In this example.lang package.name").version).0".zetcode. Constants. Therefore. public class AutomaticImports { public static void main(String[] args) { String os = System. we can access the class and its member without using the fully qualified name or utilizing the import keyword. System. $ ls com/zetcode/ AutomaticImports.version).java Constants. The java.lang package and the current package. AutomaticImports.java files are located in the same subdirectory.println(Constants. System.java package com.java and Constants.getProperty("os. } The Constants class is located in the same package as the AutomaticImports which is referring to its version member. 153 . Automatic imports Java compiler automatically imports two packages.zetcode. System.out.java package com.println(os). String os = System. We use the import static statement to enable referring to them without their full names.0 This is a sample output of the com. we can use import static statement to refer to them later without a full class name.println(E).Math.println(abs(-5)). import static java. System.AutomaticImports Linux 1.println(E).AutomaticImports program. Static imports If we often use some static members.lang.E. package com. 154 .zetcode. $ java com.$ javac com/zetcode/AutomaticImports.Math. System.out.out.Math. import static java.Math. $ java com.PI.StaticImport 2. import static java.lang.lang.println(abs(-5)).zetcode.PI.zetcode.out. Static imports should be used with caution.println(PI).java Both files are compiled.out.println(PI). we refer to two constants and one static method.lang.zetcode. System.out. import static java.718281828459045 3. We refer to these three members without their class name.Math.lang. import static java.java com/zetcode/Constants.E.Math.lang.StaticImport program.141592653589793 5 This is the output of the com.out.abs. System.zetcode. } } In this example. public class StaticImport { public static void main(String[] args) { System. This chapter covered packages in Java.abs. import static java. System. database problems. Unchecked Exceptions are subclasses of java. Examples of errors include InternalError. SQLException. An Internet connection might go down while our application tries to connect to a site. 6. Errors are instances of the java.Error class. unchecked exceptions and errors. 2 }. Errors are serious problems that programmers cannot solve. These errors can crash the application. IOException. After the exception is thrown. 2. 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. 155 . All subclasses of Exception except for RuntimeException and its subclasses are checked exceptions. Unchecked exceptions are error conditions that cannot be anticipated and recovered from. Checked exceptions are forced by Java compiler to be either catched or declared in the method signature (using the throws keyword). The try. Java uses exceptions to handle errors. make it unresponsive. NullPointerException. absent files). StackOverflowError or AssertionError.Exceptions In this chapter of the Java tutorial. System.lang.length]). It is a responsibility of a programmer to handle errors that can be anticipated. A disk might get full and we cannot save our data. network outages. the runtime system attempts to find an appropriate exception handler. 4.zetcode.out. Checked exceptions are error conditions that can be anticipated and recovered from (invalid user input. The call stack is a hierarchy of methods that are searched for the handler. and in some cases even compromise the security of a system. They are usually programming errors and cannot be handled at runtime.format("The last element in the array is %d%n". n[n. 7. or BufferOverflowException belong to this group of exceptions. package com. In Java we recognize three kinds of exceptions: checked exceptions. Errors and runtime exceptions are often referred to as unchecked exceptions. 5. we work with exceptions. The throw keyword causes the declared exception instance to be thrown. or PrinterException are examples of checked exceptions. A user fills invalid data to a form. During the execution of our application many things might go wrong. Unchecked exceptions are not enforced by the Java compiler. catch and finally keywords are used to handle exceptions.lang. public class UncheckedException { public static void main(String[] args) { int[] n = { 5.RuntimeException. For example hardware or system malfunctions cannot be handled by applications. OutOfMemoryError. ArithmeticException. This exception is an example of an unchecked exception.SEVERE. null. $ java com.out.UncheckedException Exception in thread "main" java.class.length]).InputMismatchException. package com. This is an example of an unchecked exception.Scanner.Level.java:9) A java. We are not forced to handle this exception by the compiler.println("Enter an integer: ").main(UncheckedException. try { Scanner sc = new Scanner(System. The exception class that is thrown must match the exception following the catch keyword. java.length . System.zetcode.ArrayIndexOutOfBoundsException is thrown by the runtime system. public class UncheckedException2 { public static void main(String[] args) { System.getName()). Therefore. try { 156 .Logger. This is a programming error.getLogger(UncheckedException2.ArrayIndexOutOfBoundsException: 7 at com.util. The Scanner class throws an InputMismatchException to indicate that the token retrieved does not match the pattern for the expected type.log(Level. } } } The error prone code is placed in the try block. e). There is no reason to handle this error. We try to access an element that does not exist. import import import import java. int x = sc.in). } catch (InputMismatchException e) { Logger. java.out.util. n[n. If an exception is thrown.println(x). there is a bug. the code jumps to the catch block.nextInt().zetcode. System.lang.UncheckedException.} } In the above program.util.logging. the last index is n.zetcode.out.logging.format("The last element in the array is %d%n". The array indexes start from zero. the code must be fixed. java.util.1.lang. try { con = DriverManager.logging.sql.createStatement(). String user = "testuser". e).Connection.util.ResultSet. Connecting to databases is error prone. java. ex). } The try keyword defines a block of statements which can throw an exception.sql. } catch (SQLException ex) { Logger lgr = Logger. public class Version { public static void main(String[] args) { Connection con = null. } catch (InputMismatchException e) { Logger. null.sql. st = con. String url = "jdbc:mysql://localhost:3306/testdb". String password = "test623".Level.Logger.in).SQLException. ex.getMessage(). if (rs.getName()).log(Level.getLogger(Version.getLogger(UncheckedException2. We use the Logger class to log the error. } The exception is handled in the catch block.class. } finally { 157 . lgr. ResultSet rs = null.getString(1)). Statement st = null.logging.sql.Statement. System.println(rs. java.executeQuery("SELECT VERSION()"). int x = sc. java.DriverManager.class.out.println(x). java.nextInt(). package zetcode. java.SEVERE. import import import import import import import java.Scanner sc = new Scanner(System.getName()). rs = st.getConnection(url.sql. The following code example connects to a MySQL database and finds out the version of the database system.SEVERE.util.log(Level. user.next()) { } System.out. java. password). getName()).println(rs. user. st = con. try { con = DriverManager. We are forced to handle it.getMessage(). ex). We handle the exception by logging what happened. } if (st != null) { st.getString(1)).getLogger(Version. lgr.WARNING. lgr.getMessage(). } if (st != null) { st.class.log(Level. if (rs. The code that might lead to an error is placed in the try block.executeQuery("SELECT VERSION()").class. } } } } An SQLException is an example of a checked exceptions.close().createStatement(). rs = st.log(Level.SEVERE. } if (con != null) { con. ex).close(). 158 .out. } } catch (SQLException ex) { Logger lgr = Logger.try { if (rs != null) { rs. } finally { try { if (rs != null) { rs.getConnection(url. password).getLogger(Version.close(). ex.close().getName()).next()) { } } System. we jump to the catch block. } When an exception occurs.close(). } catch (SQLException ex) { Logger lgr = Logger. ex. } 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 System.add("IBM"). This class offers constant time performance for the basic operations (add. we create a TreeMap and put domains with their country names into it.out.add("Amazon"). $ java com. ru=Russia.HashSet.out. hu=Hungary. ru=Russia. System.descendingMap()). TreeMap<String.out.TreeMapExample {de=Germany.TreeMapExample program printed keys with their values in ascending and descending sort order. System.util.zetcode. hu=Hungary. String> domains = new TreeMap<>(). "Poland"). remove. HashSet A HashSet is a collection that contains no duplicate elements. System. 172 .zetcode. brands. contains and size). public class HashSetExample { public static void main(String[] args) { HashSet<String> brands = new HashSet<>(). } } In the example. brands. pl=Poland. brands. de=Germany} The com. sk=Slovakia. System.out. THe descendingMap() method returns a reverse order view of the mappings contained in this map.println(domains.add("IBM").descendingMap()).out.domains. brands.println(domains). sk=Slovakia. System. package com.zetcode.put("pl". import java.println(domains). A TreeMap is created. us=United States} {us=United States.add("Volvo").println(brands.out.println(domains. This will print the keys/values in their natural sort order — in ascending order. A HashSet does not provide ordering of elements.println(brands).add("Pepsi"). pl=Poland.isEmpty()). brands. contains("Volvo")). So the brand names is a good example for a HashSet. We print all the elements in one shot.contains("Volvo")).out. brands. HashSet<String> brands = new HashSet<>().remove("Volvo").out.println(brands. Pepsi.println(brands.isEmpty()). brands.println(brands. System. brands.println(brands.out. brands.zetcode.add("Pepsi").clear(). However.add("IBM"). We remove the Volvo brand from the brands container. the IBM is present in the container only once.println(brands).clear().add("IBM"). System. The second line prints false. The line prints true.contains("Volvo")).println(brands. The clear() method removes all of the elements from the set.zetcode. Amazon] false true false [] This is the output of the com.System. System.out. 173 . System. Volvo. The isEmpty() method checks if the container is empty.out. brands.add("Amazon").contains("Volvo")).HashSetExample [IBM.println(brands). brands. With the contains() method we check if the Volvo brand is present in the brands container. brands.HashSetExample program.remove("Volvo").out.add("Volvo"). brands.out. System. $ java com. } } There can be only one brand registered under a name. The IBM brand is added twice. System. brands. We create a HashSet and add new elements. brands.addAll(brands). An ArrayList of various brands is created. System. brands. System. brands. brands.add("Apple"). A TreeSet is slower than a HashSet. true.headSet("IBM". import java. brands.descendingSet()). brands.out.TreeSet. With the help of the addAll() method. a new TreeSet is created from the ArrayList container. package com.add("Pepsi"). System.add("Pepsi").zetcode.out. brands. while a TreeSet cannot. true)). "Starbucks". we work with a TreeSet. System. false)). brands. 174 . A HashSet can contain null values.out. brands. brands.util. TreeSet<String> brands2 = new TreeSet<>().add("HP").TreeSet A TreeSet is a set which has elements ordered using their natural ordering.subSet("Apple".tailSet("IBM".println(brands2.first()).last()).addAll(brands). brands.out. ArrayList<String> brands = new ArrayList<>(). brands2.util. } } In this example.add("Starbucks").println(brands2.add("IBM").println(brands2.add("Amazon"). brands. System.add("HP").println(brands2.out.add("Volvo"). System. brands2. brands. TreeSet<String> brands2 = new TreeSet<>().out. brands.out. System.println(brands2. public class TreeSetExample { public static void main(String[] args) { ArrayList<String> brands = new ArrayList<>().add("Amazon"). true)).println(brands2).add("IBM").println(brands2. import java.add("Apple").ArrayList.add("Starbucks").add("Volvo"). println(brands2.println(brands2.out. because this container does not support ordered elements. The headSet() method returns a slice of the set whose elements are less than the specified element.out. System.TreeSetExample [Amazon.println(brands2. public class CollectionsExample { public static void main(String[] args) { 175 .zetcode. true.TreeSetExample example.tailSet("IBM".zetcode.util.ArrayList. true)). Pepsi.last()). it is not possible to use the sort() method on a HashSet. IBM] [Pepsi.println(brands2). IBM. For example. System.headSet("IBM".out. Starbucks. Volvo] [Apple. The second parameter controls whether the specified element is included. The tailSet() method returns a slice of the set whose elements are greater than the specified element.util. HP. Starbucks.out. System. System. It consists exclusively of static methods.out. $ java com.println(brands2. System. Starbucks] This is the output of the com.println(brands2. Apple.Arrays. IBM. Pepsi. HP. IBM.first()). Apple. Volvo] [Volvo. true)).System. We print the first and the last element of the container. import java.out.zetcode. import java.util. Pepsi. Starbucks. System. The elements of the container are printed to the console in ascending and descending orders. HP.subSet("Apple".Collections. Some of the methods are not applicable to all collection types. Apple.println(brands2.descendingSet()). The subSet() method returns a portion of the container whose elements range from the first specified element to the second one. package com. Amazon] Amazon Volvo [Amazon. HP. import java. "Starbucks".out. false)). Collections class The Collections is a utility class that provides many useful methods for working with containers. System. The reverse() method reverses the order of elements in the list.out. ArrayList<Integer> ns = new ArrayList<>(Arrays.out. 0. Collections.println(Collections. 3. The sort() method sorts the elements in ascending order. ArrayList<Integer> ns = new ArrayList<>(Arrays. we use several methods of the Collections class. 5.out.println(ns). 0. 4.println("Random order:"). Collections.out.println("Descending order:"). Collections. 2.asList(nums)).println(ns). 0.out. System. 0). Collections.println("Default order:").replaceAll(ns. 1 }.shuffle(ns). System. The first element with the last element in our case.out. ns.min(ns)). System. 8. Collections. 2.out. 4. 4. Collections.out. System. Collections. 3. System.size()-1).println(ns). System.println("Swapping the first and the last elements:"). Collections. The swap() method exchanges two elements. 4. The asList() method of the Arrays class is used to transform an array into a list which is then passed to the constructor. 7. System. System. 0). 1 }.println(ns). An ArrayList is created from an array of Integers. 9. This line replaces all occurrences of number 4 with 0.println("Ascending order:"). 8. 0. Integer[] nums = { 4. System.println(Collections. 4.reverse(ns).println("Replacing all 4s with 0s:"). 9.out. System.sort(ns).size()-1). 7.swap(ns.out. Collections. } } In the example. 5.println(ns). 6. System. 4.Integer[] nums = { 4. 176 . ns. 6.swap(ns. 2.max(ns)). 2.out.replaceAll(ns. System.out.asList(nums)).out.sort(ns).println(ns).reverse(ns). System.out. 8. 4. 0] 9 0 This is a sample output of the com. 1. 2. 2. 0. 0. 9] Replacing all 4s with 0s: [0. 8. 9. The shuffle() method randomly reorders the elements in the container. 0. 9] Descending order: [9.CollectionsExample Default order: [4. 2. 8.CollectionsExample program. 3. 2. 177 .out. 7.min(ns)). System.shuffle(ns). 0. 7. 3. 4. This part of the Java tutorial was dedicated to collections in Java. 5.println(Collections. 5. 9] Random order: [1. 2.out. Here we print the maximum and the minimum values of the list. 2. 4. 5. 2. 7. $ java com. 7. 5. 2. 4. 5. 6. 8. 2.zetcode. 0] Swapping the first and the last elements: [0. 9. 4.Collections. 3. 3. System. 1. 4.println(Collections. 3. 4. 4. 1. 6. 8. 3. 0.max(ns)). 6. 7. 2. 6. 2. 4. 6. 1] Ascending order: [0.zetcode. 4. 6. 0. 4. 8. 1. 0. 2. 5. 7. From the list of options.PostgreSQL Java tutorial This is a Java tutorial for the PostgreSQL database. If we are using Netbeans IDE. Older driver may cause problems on newer PostgreSQL versions. JDBC JDBC is an API for the Java programming language that defines how a client may access a database. we can add the newest driver from by choosing add JAR/Folder and selecting a driver from the filesystem. Microsoft Windows and Mac OS X. we need the PostgreSQL Java driver.sql package. Note that the driver may be older. Java Development Kit. than we have already the driver at hand. There is a similar PostgreSQL Python tutorial. 178 . FreeBSD. We need to install the JDK. PostgreSQL is developed by the PostgreSQL Global Development Group. Before we start We need to install postgresql package. Finally. the API is as a set of classes in the java. Figure: Netbeans project libs If we want to compile the examples from the command line. Inside the Projects tab. select PostgreSQL JDBC Driver. To use JDBC with a particular database. About PostgreSQL database PostgreSQL is a powerful.org/download. for compiling and running Java programs. It provides methods for querying and updating data in a database. It covers the basics of PostgreSQL programming with Java. MySQL Java tutorial or Apache Derby tutorial on ZetCode.postgresql. In such a case. right click on the Libraries node and select Add Library option. In this tutorial. The examples were created and tested on Ubuntu Linux. we need a JDBC driver for that database. open source object-relational database system. Solaris. we use the PostgreSQL JDBC Driver driver. JDBC is oriented towards relational databases.html and download the latest PostgreSQL Java Driver. go to the site http://jdbc. From a technical point of view. It is a multi-user database management system. It runs on multiple platforms including Linux. It is the official JDBC driver for PostgreSQL. d/S19postgresql /etc/rc3. it is automatically added to the start up scripts of the operating system. /etc/rc0.real (Ubuntu/Linaro 4. Next. If we don't already have PostgreSQL installed.d/postgresql status Running clusters: 9. it is unnecessary to start the database each time we boot the system.1 database server [ OK ] We use the service postgresql stop command to stop the PostgreSQL server.6. If not.6. The above command removes any system startup links for the PostgreSQL database.d/postgresql .d/S19postgresql /etc/rc4. $ sudo apt-get install postgresql This command installs the PostgreSQL server and various other packages.d -f postgresql remove Removing any system startup links for /etc/init. $ /etc/init.1 database server [ OK ] On Ubuntu Linux we can start the server with the service postgresql start command. 32-bit Assuming. $ sudo service postgresql stop [sudo] password for janbodnar: * Stopping PostgreSQL 9.$ javac zetcode/Version.1.2 on i686-pc-linux-gnu. we are going to create a new database user and a new database.jdbc4. we compile and run the first example this way. we must install it. $ sudo update-rc..1-9ubuntu3) 4.1/main $ service postgresql status Running clusters: 9.java $ java -cp . $ sudo -u postgres createuser -W user12 179 . These are installed with the PostgreSQL database.d/K21postgresql If we install the PostgreSQL database from packages. $ sudo service postgresql start * Starting PostgreSQL 9.jar zetcode/Version PostgreSQL 9.6. We will use the createuser and createdb commands.:lib/postgresql.1/main We check if the PostgreSQL server is running.d/K21postgresql /etc/rc2.d/S19postgresql /etc/rc5. If we are only learning to work with the database. we need to start the server..d/K21postgresql /etc/rc1.d/S19postgresql /etc/rc6.1. that we have put the driver jar file into the lib directory and using package zetcode. compiled by gcc-4. 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. We will use this database throughout the tutorial. } finally { try { if (rs != null) { rs.log(Level. rs = st. java.out. java. lgr. PostgreSQL version If the following program runs OK. java. $ sudo -u postgres createdb testdb -O user12 We create a new testdb database. String user = "user12". user.Statement. try { con = DriverManager. ex).getConnection(url.DriverManager.sql. st = con.sql. ex.sql.getMessage(). java. public class Version { public static void main(String[] args) { Connection con = null. String password = "34klq*". We check the version of the PostgreSQL server.class.ResultSet. then we have everything installed OK. java.close().sql.logging. java.getName()).println(rs. } } catch (SQLException ex) { Logger lgr = Logger. package zetcode.SQLException.Connection.getString(1)).util.sql. password). ResultSet rs = null.getLogger(Version.Logger. if (rs.next()) { System. String url = "jdbc:postgresql://localhost/testdb".util.createStatement(). 180 .logging.executeQuery("SELECT VERSION()"). Statement st = null. The user is called user12 and it is created with a password (W option). import import import import import import import java.SEVERE.Level. We set the new user to be the owner of the database (the -O option). close(). } } catch (SQLException ex) { Logger lgr = Logger.println(rs.out. The ResultSet is a table of data returned by a specific SQL statement. The first column has index 1. user. using the connection url. 181 .getConnection(url. String url = "jdbc:postgresql://localhost/testdb". we provide a host. a port and a database name.class.getMessage().getString(1)). ex). the method returns false. For this console example. user name and password.log(Level. st = con. password). ex.class.createStatement(). ex). In our case. } if (con != null) { con.executeQuery("SELECT VERSION()"). If there are no rows left. lgr. which returns a single ResultSet object. } } } } We connect to the database and get some info about the PostgreSQL server. The executeQuery() method of the connection object executes the given SQL statement.SEVERE. The next() method moves the cursor to the next row. We establish a connection to the database.getMessage(). if (rs. the message is displayed in the terminal. lgr. } A ResultSet object maintains a cursor pointing to its current row of data.log(Level.next()) { System.getLogger(Version.getLogger(Version. The getString() method retrieves the value of a specified column. Each driver has a different syntax for the url. The createStatement() method of the connection object creates a Statement object for sending SQL statements to the database.getName()).getName()). Initially the cursor is positioned before the first row. con = DriverManager. we log the error message.WARNING. rs = st. } catch (SQLException ex) { Logger lgr = Logger. ex. This is the connection url for the PostgreSQL database. } In case of an exception.close().} if (st != null) { st. CREATE TABLE IF NOT EXISTS authors ( id serial PRIMARY KEY.getMessage(). authors(id.close(). 'Jack London').log(Level.6. } We log an error message. 'Lion Feuchtwanger').6. when the resources could not be closed. testing. authors(id.WARNING. Creating and populating tables Next we are going to create database tables and fill them with data. 182 . DROP TABLE IF EXISTS books. INSERT INSERT INSERT INSERT INSERT INTO INTO INTO INTO INTO authors(id. This is to prevent null pointer exceptions. lgr. VALUES(3.. Inside the finally block.2 on i686-pc-linux-gnu. images.1.jdbc4. we close the database resources.:lib/postgresql.1. ex. } if (st != null) { st. 'Honore de Balzac').getName()).Version PostgreSQL 9. VALUES(4.jar zetcode. $ javac zetcode/Version. 'Truman Capote'). } . title VARCHAR(100) ). authors(id. ex). } if (con != null) { con. CREATE TABLE IF NOT EXISTS books ( id serial PRIMARY KEY. which would terminate the application and might leave the resources not cleaned up.real (Ubuntu/Linaro 4. name) name) name) name) name) VALUES(1.6.class.try { if (rs != null) { rs. VALUES(5.. authors(id. These tables will be used throughout this tutorial.getLogger(Version.close().1-9ubuntu3) 4. } catch (SQLException ex) { Logger lgr = Logger. We also check if the objects are not equal to null. data bytea). VALUES(2. CREATE TABLE IF NOT EXISTS images(id serial. compiled by gcc-4. 'Emile Zola'). author_id INT references authors(id). Otherwise we might get a NullPointerException. 32-bit This is a sample output of the program. name VARCHAR(25) ). CREATE TABLE IF NOT EXISTS testing(id INT). authors.close().java $ java -cp . We place a foreign key constraint on the author_id column of the books table. Prepared statements increase security and performance. 4. We fill the authors and books tables with initial data. books(id. 'Call of the Wild'). 'Breakfast at We have a tables. books(id.sql:6: NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "authors_pkey" for table "authors" CREATE TABLE . package zetcode. testdb=# testdb=# \i tables. we use placeholders instead of directly writing the values into the statements. title) title) title) title) title) title) title) title) title) VALUES(1.. $ psql testdb psql (9. The psql is a terminal-based front-end to PostgreSQL. VALUES(6. author_id. VALUES(8. Inside the psql tool. 183 . issue them to PostgreSQL.2) Type "help" for help.id" psql:tables.INSERT INTO INSERT INTO INSERT INTO INSERT INTO INSERT INTO INSERT INTO INSERT INTO INSERT INTO INSERT INTO Tiffany').sql file. books(id. 2. author_id. books(id. 3. authors. When we write prepared statements. 'Nana'). VALUES(3. 5. 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. author_id. 5. 1. We can use it to perfom interactive queries. 1. books(id. author_id. VALUES(4. author_id. 'Cousin Bette'). author_id. In Java a PreparedStatement is an object which represents a precompiled SQL statement. 'In Cold blood'). books(id. books(id.sql DROP TABLE psql:tables.sql file. 'Martin Eden'). books(id. we import and execute the tables. author_id. VALUES(2. testing and images. VALUES(5. author_id. 4. and see the query results.sql:6: NOTICE: CREATE TABLE will create implicit sequence "authors_id_seq" for serial column "authors. 'Old Goriot'). VALUES(9.1. books.. VALUES(7. It creates four database tables. author_id. 2. 'The Belly of Paris'). books(id. 'Jew Suess'). log(Level.sql.sql. author).util. java. PreparedStatement pst = null.getMessage(). java.logging. } if (con != null) { con. lgr. ex. } } } } We add a new author to the authors table.DriverManager.import import import import import import java. lgr. public class Prepared { public static void main(String[] args) { Connection con = null. java.getLogger(Prepared.SQLException. java.getName()). con = DriverManager. String author = "Trygve Gulbranssen".close().PreparedStatement. pst. String user = "user12".getConnection(url.setString(2.close().sql.setInt(1. id). } catch (SQLException ex) { Logger lgr = Logger.util.executeUpdate().SEVERE. } finally { try { if (pst != null) { pst. ?)". pst = con. Here we create a prepared statement.logging. name) VALUES(?. pst. pst.getName()).getMessage().Logger.SEVERE. When we write prepared statements. String stm = "INSERT INTO authors(id.prepareStatement(stm).prepareStatement(stm). Prepared statements 184 . user. java. String stm = "INSERT INTO authors(id. String url = "jdbc:postgresql://localhost/testdb". pst = con. ?)". try { int id = 6. password). we use placeholders instead of directly writing the values into the statements.Connection. ex). ex).Level.class. name) VALUES(?.log(Level. String password = "34klq*".getLogger(Prepared. ex.sql.class. } } catch (SQLException ex) { Logger lgr = Logger. sql.DriverManager. pst. Another value is bound to the placeholder. java. pst. id). java. 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. UPDATE. $ javac zetcode/Prepared. java. java.util.Prepared testdb=# SELECT * FROM authors.Logger. A value is bound to the placeholder. which is going to be filled later.logging.sql.sql. This is when we create databases or execute INSERT. Statement st = null. public class NotPrepared { public static void main(String[] args) { Connection con = null. package zetcode.Level.are faster and guard against SQL injection attacks. The prepared statement is executed. We use the executeUpdate() method of the statement object when we don't expect any data to be returned.util. DELETE statements.logging. We check.setString(2. java.Statement.Connection. if there is some difference in execution time.executeUpdate().sql.jar zetcode. For the following two examples. we will use the Testing table. 185 . The ? is a placeholder.java $ java -cp . In our case we will fill two values.SQLException.setInt(1. author). pst. We will execute a normal statement and a prepared statement 1000 times.jdbc4. import import import import import import java. An integer id and a string name.:lib/postgresql. user. Note that we use a standard Linux command.NotPrepared 0. st. 186 . package zetcode.getName()).executeUpdate(query). i++) { String query = "INSERT INTO Testing(Id) VALUES(" + 2*i + ")". It took 32s to insert 1000 rows into the table using the Statement object.getLogger(NotPrepared.java $ /usr/bin/time java -cp . for (int i=1.SEVERE. ex.14system 0:32. password).27elapsed 2%CPU (0avgtext+0avgdata 89824maxresident)k 960inputs+88outputs (1major+6008minor)pagefaults 0swaps We use the time command to measure the time.jdbc4. not the built-in bash time command. } } catch (SQLException ex) { Logger lgr = Logger.getMessage().class.class.getConnection(url. i<=1000. ex).executeUpdate(query). The first example uses the normal Statement object.jar zetcode. } } catch (SQLException ex) { Logger lgr = Logger.SEVERE.close(). i++) { String query = "INSERT INTO Testing(Id) VALUES(" + 2*i + ")". } } } } st.log(Level.getLogger(NotPrepared.createStatement().String url = "jdbc:postgresql://localhost/testdb". try { con = DriverManager.getName()). } if (con != null) { con.getMessage(). i<=1000. String password = "34klq*". lgr. ex). st = con.81user 0.log(Level. that the program ran. } finally { try { if (st != null) { st. $ javac zetcode/NotPrepared. lgr. for (int i=1.:lib/postgresql. } We build the query and execute it 1000 times. String user = "user12".close(). ex. String password = "34klq*".SQLException.prepareStatement("INSERT INTO Testing(Id) VALUES(?)").log(Level.sql. 187 . user.getName()). pst = con. java. i++) { pst.logging. ex).logging. } } catch (SQLException ex) { Logger lgr = Logger. i <= 1000.Connection. ex.setInt(1.getMessage(). lgr. pst = con.getConnection(url.getLogger(Prepared2.Level.close(). java. ex).DriverManager.getMessage().sql. i++) { pst. public class Prepared2 { public static void main(String[] args) { Connection con = null. i * 2). java. i * 2).setInt(1. i <= 1000.class. } } catch (SQLException ex) { Logger lgr = Logger. java.SEVERE. } if (con != null) { con. } } } } Now we use the PreparedStatement to do the same task. for (int i = 1. } finally { try { if (pst != null) { pst. for (int i = 1. PreparedStatement pst = null. java.SEVERE.executeUpdate().PreparedStatement.class.sql. try { con = DriverManager.sql.log(Level. lgr.getName()).util. String user = "user12".import import import import import import java.Logger. password). We create the prepared statement using the prepareStatement() method.getLogger(Prepared2. ex.util.prepareStatement("INSERT INTO Testing(Id) VALUES(?)").close(). pst. String url = "jdbc:postgresql://localhost/testdb". lgr.sql.getInt(1)). We have saved 17s using prepared statements.sql.out. We get all data from the authors table. password). String url = "jdbc:postgresql://localhost/testdb". } } catch (SQLException ex) { Logger lgr = Logger.print(rs.ResultSet.print(": "). PreparedStatement pst = null. String user = "user12".logging.SQLException. java.sql. java.sql. java. ResultSet rs = null. pst = con.getName()). System.executeUpdate().java $ /usr/bin/time java -cp . $ javac zetcode/Prepared2.11system 0:15. Retrieving data Next we will show. ex). import import import import import import import java.Level.Connection.getLogger(Retrieve. how to retrieve data from a database table.Logger. ex. public class Retrieve { public static void main(String[] args) { Connection con = null. java. java. java.getConnection(url. user.next()) { System.59user 0. while (rs.log(Level. String password = "34klq*". We bind a value to the prepared statement.out.} pst. } finally { 188 .Prepared2 0.util.getMessage().jdbc4.sql.getString(2)).logging. System.out. try { con = DriverManager.executeQuery().DriverManager. rs = pst.println(rs.:lib/postgresql.util.jar zetcode. execute it in a loop thousand times.SEVERE. package zetcode.PreparedStatement.class.prepareStatement("SELECT * FROM authors").08elapsed 4%CPU (0avgtext+0avgdata 76912maxresident)k 0inputs+64outputs (0major+4941minor)pagefaults 0swaps This time it took 15s to insert 1000 rows. pst = con. } The next() method advances the cursor to the next record. when there are no more rows in the result set. securing data etc.executeQuery().print(": "). We can change the user.class.try { if (rs != null) { rs.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.prepareStatement("SELECT * FROM authors").WARNING. It is especially useful in a dynamic environment.jdbc4. $ javac zetcode/Retrieve.getLogger(Retrieve. debugging.close().out.out. We use the executeQuery() method.close(). rs = pst. 189 .:lib/postgresql. Properties It is a common practice to put the configuration data outside the program in a separate file. } } } } We get all authors from the authors table and print them to the console.next()) { System. This way the programmers are more flexible. lgr.print(rs. 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. System. ex).getMessage(). } if (con != null) { con.log(Level.getString(2)). System.java $ java -cp .getName()). the password or the connection url without needing to recompile the program. which returns a single ResultSet object. It returns false. The method executes the given SQL statement. } if (pst != null) { pst.println(rs.getInt(1)).out. The ResultSet is the data table returned by the SQL query. ex. where is a need for a lot of testing.close(). while (rs. We execute a query that selects all columns from the authors table. } } catch (SQLException ex) { Logger lgr = Logger. In Java.user"). try { in = new FileInputStream("database. } } String url = props. java. java. db.Properties.getName()).log(Level.Connection. ex).log(Level. These are dynamically loaded during execution of the program. props. package zetcode. java. } } catch (IOException ex) { Logger lgr = Logger. lgr.PreparedStatement. Properties props = new Properties().getProperty("db.Logger. java. java. } finally { try { if (in != null) { in. java. in which we have three key/value pairs.getLogger(Retrieve2.getMessage(). ex.getMessage().properties"). java.sql. The class is used for easy reading and saving of key/value properties.getName()).SQLException. ResultSet rs = null.sql.logging.user=user12 db.io. ex.util.url").DriverManager.load(in). PreparedStatement pst = null.logging. java.io.properties file. ex). the Properties is a class used often for this.sql.SEVERE. 190 .Level.passwd=34klq* We have a database.class.io.IOException. public class Retrieve2 { public static void main(String[] args) { Connection con = null.util.sql. java.SEVERE.url=jdbc:postgresql://localhost/testdb db. String user = props.getProperty("db.sql.close().FileNotFoundException.getLogger(Retrieve2.class. import import import import import import import import import import import java. lgr. FileInputStream in = null.util.FileInputStream. } catch (IOException ex) { Logger lgr = Logger. java.ResultSet. user. System. String url = props. This time.executeQuery().getLogger(Retrieve2.out. The data is loaded from the file called database.out.close(). try { in = new FileInputStream("database.print(": ").properties. FileInputStream in = null. try { con = DriverManager.passwd").prepareStatement("SELECT * FROM Authors").. we load the connection properties from a file.. pst = con. props.close().user").log(Level.getLogger(Retrieve2.class.getMessage(). ex). while (rs.String passwd = props. The Properties class is created. System. String passwd = props.getProperty("db.getProperty("db. rs = pst. ex).passwd").getMessage(). } } catch (Exception ex) { Logger lgr = Logger. Properties props = new Properties(). . } if (con != null) { con.getName()). ex.log(Level. String user = props.close().println(rs. where we have our configuration data. They are not hard coded in the proram.getName()). } if (pst != null) { pst. ex. } finally { try { if (rs != null) { rs. lgr.properties").next()) { System.print(rs.class.url"). lgr. } } } } We connect to the testdb database and print the contents of the authors table to the console. 191 .getConnection(url.SEVERE. passwd).getInt(1)). } } catch (SQLException ex) { Logger lgr = Logger.getProperty("db.out.WARNING.getString(2)).getProperty("db.load(in). next()) { System. name FROM authors WHERE Id=3". java. java. } catch (SQLException ex) { Logger lgr = Logger.sql.getInt(1)).getName()). System.getConnection(url.DriverManager. try { con = DriverManager. } finally { 192 .print(": "). PreparedStatement pst = null. } while (isResult).log(Level.prepareStatement(query).getLogger(Multiple. ex.Logger.sql. name FROM authors WHERE Id=2. Multiple statements It is possible to execute multiple SQL statements in one query." + "SELECT id.class.getResultSet(). user.getMessage().util. name FROM authors WHERE Id=1. java.println(rs.sql. boolean isResult = pst.execute(). while (rs.PreparedStatement.out. ex). package zetcode. java.getMoreResults(). String url = "jdbc:postgresql://localhost/testdb".SQLException.out.util.ResultSet. public class Multiple { public static void main(String[] args) { Connection con = null.getString(2)). password). System. String password = "34klq*". pst = con.sql.Connection.logging.sql. lgr. java.SEVERE. java. String query = "SELECT id.print(rs.Level.out. import import import import import import import java. String user = "user12".logging. do { rs = pst.The values are retrieved with the getProperty() method. } isResult = pst." + "SELECT id. ResultSet rs = null. getMoreResults(). We use three SELECT statements to get three rows.log(Level. The statements are separated by a semicolon.Multiple 1: Jack London 2: Honore de Balzac 3: Lion Feuchtwanger 193 .getResultSet(). System. } while (isResult). To find out. boolean isResult = pst. we retrieve three rows from the authors table.getString(2)).getInt(1)). Here we have a query with multiple statements.getName()). } isResult = pst.print(": "). String query = "SELECT id.execute().close().out. name FROM authors WHERE Id=1. System. ex).close(). } } catch (SQLException ex) { Logger lgr = Logger.java $ java -cp .out. ex." + "SELECT id. } if (con != null) { con. The processing of the results is done inside the do/while loop. name FROM authors WHERE Id=3".WARNING.print(rs.jdbc4. do { rs = pst. } if (pst != null) { pst.close().try { if (rs != null) { rs. The method returns a boolean value indicating if the first result is a ResultSet object. } } } } In the code example. we call the getMoreResults() method.getLogger(Multiple.class.getMessage().jar zetcode. lgr." + "SELECT id. The ResultSet is retrieved with the getResultSet() method call. We call the execute() method of the prepared statement object.next()) { System. $ javac zetcode/Multiple.out. Subsequent results are called using the getMoreResults() method.println(rs.:lib/postgresql. if there are other results. name FROM authors WHERE Id=2. while (rs. colname1.executeQuery(). fmt1. rs = pst.sql. how to print column headers with the data from the database table. java.util.prepareStatement(query).util.Level. 194 . String url = "jdbc:postgresql://localhost/testdb". String colname2 = meta. Number of rows and columns returned in a result set belong to metadata as well. java. import import import import import import import import import java. String colname1 = meta. Metadata in PostgreSQL can be obtained by calling the getMetaData() method of the result set object or from the information_schema table. String password = "34klq*".sql. The first three rows were retrieved from the authors table.logging.Formatter. password). ResultSet rs = null.getMetaData(). " + "books WHERE authors. Next we will show.Logger. java.sql. try { con = DriverManager. in which we store data.getColumnName(2). public class ColumnHeaders { public static void main(String[] args) { Connection con = null. String user = "user12". java.getConnection(url. java.author_id".Connection. System. ResultSetMetaData meta = rs. Formatter fmt1 = new Formatter().The output of the example. java. user. colname2).ResultSet. Metadata Metadata is information about the data in the database.id=books.sql.sql. pst = con.logging. Metadata in a PostgreSQL database contains information about the tables and columns. java.ResultSetMetaData.sql. String query = "SELECT name.format("%-21s%s".println(fmt1). PreparedStatement pst = null.SQLException. package zetcode.DriverManager. java.out. title From authors.PreparedStatement.getColumnName(1). Number of rows affected by an SQL statement is a metadata.util. System. } if (con != null) { con.getMessage(). ResultSetMetaData meta = rs.getName()). } } catch (SQLException ex) { Logger lgr = Logger. String query = "SELECT name.class.println(fmt1) We print the column names to the console.while (rs. colname2).id=books.println(rs.getName()).format("%-21s". From the obtained metadata. System.getLogger(ColumnHeaders.close(). It is an object that can be used to get information about the types and properties of the columns in a ResultSet object. System. We use the Formatter object to format the data. fmt1. Formatter fmt1 = new Formatter().getLogger(ColumnHeaders. colname1. } } } } In this program.author_id".print(fmt2). lgr.getMessage(). rs.out. } } catch (SQLException ex) { Logger lgr = Logger.getMetaData().getColumnName(2).log(Level. fmt2.out.format("%-21s%s". } if (pst != null) { pst. " + "books WHERE authors. ex). ex. We print the names of the columns returned in the result set.getString(1)). String colname1 = meta.out.getColumnName(1).close(). lgr. 195 . This is the SQL statement which joins authors with their books. ex.SEVERE.getString(2)).WARNING. To get the column names we need to get the ResultSetMetaData.class. String colname2 = meta.close(). we get the column names.next()) { Formatter fmt2 = new Formatter(). We format the output. ex).log(Level. we select authors from the authors table and their books from the books table. } finally { try { if (rs != null) { rs. title From authors. public class ListTables { public static void main(String[] args) { Connection con = null. java. In the following example we will list all tables in the testdb database.getString(1)). 196 .util. PreparedStatement pst = null.Logger. java.sql.sql. java. java.out.prepareStatement(query). user. String password = "34klq*". pst = con.next()) { Formatter fmt2 = new Formatter(). import import import import import import import java. $ javac zetcode/ColumnHeaders.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.PreparedStatement.SQLException.util. rs. String query = "SELECT table_name FROM information_schema. String url = "jdbc:postgresql://localhost/testdb".getConnection(url.:lib/postgresql.format("%-21s". ResultSet rs = null.logging.println(rs. java. password). The first column is 21 characters wide and is aligned to the left.java $ java -cp .DriverManager. java. fmt2.tables " + "WHERE table_schema = 'public'".jdbc4.out. } We print the data to the console. We again use the Formatter object to format the data. String user = "user12". package zetcode.sql.while (rs. System.logging.sql.ResultSet.getString(2)).Connection. System.sql.Level.print(fmt2). try { con = DriverManager. getString(1)). lgr.:lib/postgresql.WARNING. $ javac zetcode/ListTables.jar zetcode. ex). The standard data type in databases is BLOB. some prefer to keep them on the file system for their applications.class.getLogger(ListTables.log(Level.getName()).log(Level. 197 . The table names are stored inside the system information_schema table. ex). This is a non-standard data type. lgr.getMessage().class.SEVERE. } if (con != null) { con.executeQuery(). PostgreSQL database has a special data type to store binary data called bytea.getName()).java $ java -cp . } if (pst != null) { pst. Images are binary data. Technical difficulties arise when we work with millions of images. Writing images Some people prefer to put their images into the database.close(). while (rs.next()) { System.ListTables authors books testing images Listing available tables in the testdb database. ex.jdbc4.getMessage(). } finally { try { if (rs != null) { rs. } } catch (SQLException ex) { Logger lgr = Logger.tables " + "WHERE table_schema = 'public'". ex. } } catch (SQLException ex) { Logger lgr = Logger.close(). } } } } The code example prints all available tables in the current database to the terminal. String query = "SELECT table_name FROM information_schema.close().rs = pst.getLogger(ListTables.println(rs.out. length()).Connection. java.util. ex).setBinaryStream(1. java.close(). PreparedStatement pst = null. java.io. (int) img. String url = "jdbc:postgresql://localhost/testdb". pst. java.log(Level.SQLException.DriverManager. FileInputStream fin = null. java. } catch (FileNotFoundException | SQLException ex) { Logger lgr = Logger.prepareStatement("INSERT INTO images(data) pst. we use the images table. fin. import import import import import import import import import import java.jpg"). } if (fin != null) { fin.SEVERE.sql. password).Level.Logger.close().IOException.class.getLogger(WriteImage.getName()). java. public class WriteImage { public static void main(String[] args) { Connection con = null. java. } finally { try { if (pst != null) { pst.getName()).getConnection(url.sql.io. ex). java. con = DriverManager.sql.logging. ex.logging.FileNotFoundException. } if (con != null) { con.getLogger(WriteImage.log(Level. lgr. String user = "user12". user.util. } } catch (IOException | SQLException ex) { Logger lgr = Logger.executeUpdate().io.close().class.getMessage(). java.File. pst = con. lgr.getMessage(). ex. VALUES(?)").sql.For this example.PreparedStatement. try { File img = new File("woman. } 198 .FileInputStream. package zetcode.io.WARNING. fin = new FileInputStream(img). String password = "34klq*". PreparedStatement. we have inserted an image into the database table.io. java.executeUpdate().util. java.Connection. we must also use the latest JDBC driver.SQLException. We create a File object for the image file. We execute the statement.FileOutputStream. Notice that Netbeans or other applications might use older drivers.InputStream. java. fin.io. pst = con. The parameters of the setBinaryStream() method are the parameter index to bind. pst. Therefore we run into troubles when using older driver with the PostgreSQL 9. java. Warning: if we are using PostgreSQL 9 and later.sql.sql. java.prepareStatement("INSERT INTO images(data) VALUES(?)"). we read a jpg image from the current working directory and insert in into the images table. File img = new File("woman.setBinaryStream(1.sql. fin = new FileInputStream(img). package zetcode.} } } In the preceding example.logging. the input stream and the number of bytes in the stream. java.logging. (int) img.io. PostgreSQL has changed the way bytea data is stored.jpg"). java.sql.IOException. To read bytes from this file.x.DriverManager. Reading images In the previous example. public class ReadImage { public static void main(String[] args) { 199 . Now we are going to read the image back from the table.sql. we create a FileInputStream object.Level. This is the SQL to insert an image. import import import import import import import import import import import java.util.Logger.io.ResultSet. The binary stream is set to the prepared statement.OutputStream. java. pst. java. java.length()). lgr.write(buf.jpg").close().close(). user. fos = new FileOutputStream("woman2. String user = "user12". FileOutputStream fos = null. lgr.log(Level.getMessage().getMessage(). } finally { try { if (pst != null) { pst.class. String query = "SELECT data. ex). } } } } We read one image from the images table. 200 .SEVERE. String url = "jdbc:postgresql://localhost/testdb".next().getName()).Connection con = null.getLogger(ReadImage.getLogger(ReadImage.executeQuery().jpg"). int len = result. len). LENGTH(data) FROM images WHERE id = 1". LENGTH(data) FROM images WHERE id = 1". ResultSet result = pst. String password = "34klq*". fos = new FileOutputStream("woman2. fos.getName()).close(). PreparedStatement pst = null. ex).prepareStatement(query). String query = "SELECT data. password).getConnection(url. pst = con. } if (con != null) { con.log(Level. ex.WARNING. } } catch (IOException | SQLException ex) { Logger lgr = Logger. byte[] buf = result. We select the data and the size of the image from the database table. try { con = DriverManager. result.class. } if (fos != null) { fos.getInt(2). ex.getBytes("data"). 0. } catch (IOException | SQLException ex) { Logger lgr = Logger. java.sql.util.The FileOutputStream object is created to write to a file. In PostgreSQL these statements are BEGIN and COMMIT. int len = result.write(buf. java. They are handled by the driver. The autocommit mode must be set by setting the autocommit property to True.logging. we turn the autocommit off.sql. The bytes are written to the output stream.sql. byte[] buf = result. import import import import import import java. java. when working with drivers these statements are omitted. In direct SQL.logging. The image is created on the filesystem. And to start a new transaction.DriverManager. String password = "34klq*". For example psycopg2 Python driver starts a transaction after the first SQL statement. len). including the PostgreSQL's one.SQLException. java. JDBC driver is by default in the autocommit mode. Statement st = null.getBytes("data"). the autocommit must be turned off. java. fos. In other words. we get the number of bytes. a transaction is started with BEGIN TRANSACTION statement and ended with END TRANSACTION/COMMIT statement. However. as an array of bytes. Exact details are specific to the driver.util. This means that each individual SQL statement is treated as a transaction and is automatically committed right after it is executed. Transaction support A transaction is an atomic unit of database operations against the data in one or more databases.Connection. When a connection is created. 201 .Statement. It is meant for writing streams of raw bytes such as image data. We get the length of the image data.getInt(2). The effects of all the SQL statements in a transaction can be either all committed to the database or all rolled back.Logger. In constrast. public class Transaction { public static void main(String[] args) { Connection con = null. The getBytes() method retrieves all bytes from the result set. This is true for all JDBC drivers. package zetcode. String user = "user12". To start a new transaction. String url = "jdbc:postgresql://localhost/testdb".sql. it is in autocommit mode.Level. 0. } catch (SQLException ex1) { Logger lgr = Logger. ex). } catch (SQLException ex) { if (con != null) { try { con. lgr.setAutoCommit(false).SEVERE. we must set the autocommit to false.getConnection(url. the data is corrupted. st. password). st. lgr. a database connection is in autocommit mode.executeUpdate("UPDATE books SET title = 'War and Peace' " + "WHERE Id = 1"). 202 . We must also change the books associated with this author. } if (con != null) { con.getName()). } } } } In this program. } } catch (SQLException ex) { Logger lgr = Logger.getLogger(Transaction.close(). st = con. st.getMessage().class.executeUpdate("UPDATE books SET titl = 'Anna Karenina' " + "WHERE Id = 2").createStatement().getMessage(). A good example where a transaction is necessary. } } Logger lgr = Logger. user.commit().WARNING.WARNING.class.executeUpdate("UPDATE authors SET name = 'Leo Tolstoy' " + "WHERE Id = 1"). ex1). con. By default. ex). con.close().try { con = DriverManager. ex1. If we change the author and do not change the author's books. lgr.getLogger(Transaction. To work with transactions.getName()). ex.log(Level.setAutoCommit(false). } finally { try { if (st != null) { st.log(Level.getLogger(Transaction. ex. we want to change the name of the author in the first row of the authors table.getName()).getMessage(). In this mode each statement is committed to the database.log(Level.rollback().class. con. util. title FROM authors. 203 ..log(Level. the data is not safe. A statement cannot be undone.:lib/postgresql. st. the transaction is committed. $ javac zetcode/Transaction. lgr.id=books. ex1). the transaction is rolled back. If there is no exception. There is no titl column in the table. package zetcode.Transaction main SEVERE: ERROR: column "titl" of relation "books" does not exist Position: 18 org.PSQLException: ERROR: column "titl" of relation "books" does not exist . } } In case of an exception. } catch (SQLException ex1) { Logger lgr = Logger.WARNING.rollback(). No changes are committed to the database. con.getMessage(). If the autocommit is turned off. The third SQL statement has an error.jdbc4..postgresql. ex1. 2012 1:48:51 PM zetcode. When the autocommit is turned off. we must explicitly call the commit() method. testdb=# SELECT name.author_id. if (con != null) { try { con.jar zetcode.as soon as it is executed.java $ java -cp . 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.commit(). The transaction was rolled back and no changes took place. books WHERE authors.class. we commit the changes by calling the commit() or roll it back by calling the rollback() method. However. without a transaction.getName()).Transaction Feb 03.getLogger(Transaction.executeUpdate("UPDATE books SET titl = 'Anna Karenina' " + "WHERE Id = 2"). password).util. This time.import import import import import import java.executeUpdate("UPDATE books SET title = 'War and Peace' " + "WHERE Id = 1").sql.Logger. public class NonTransaction { public static void main(String[] args) { Connection con = null. } catch (SQLException ex) { Logger lgr = Logger. java.executeUpdate("UPDATE books SET titl = 'Anna Karenina' " + "WHERE Id = 2"). } if (con != null) { con. user. without the transaction support.getName()).sql.getMessage().SEVERE.Statement.Connection.close().getLogger(NonTransaction. st. } } catch (SQLException ex) { Logger lgr = Logger.logging.executeUpdate("UPDATE authors SET name = 'Leo Tolstoy' " + "WHERE Id = 1"). st.getMessage(). st. ex. String user = "user12".util.log(Level. java.DriverManager.getName()).sql. lgr. String url = "jdbc:postgresql://localhost/testdb". ex). } finally { try { if (st != null) { st.WARNING.getLogger(NonTransaction.close().getConnection(url.class.createStatement().close(). String password = "34klq*". java. Statement st = null. } } } } We have the same example. java. try { con = DriverManager. 204 .sql. st = con. lgr.logging. con. ex).Level.log(Level.class.SQLException. ex. java. 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. password).java $ java -cp . testdb=# SELECT name.author_id.createStatement(). The data is corrupted. String url = "jdbc:postgresql://localhost/testdb".NonTransaction Feb 03.sql. user. java.util. package zetcode.logging. DELETE statements as well as for CREATE TABLE and DROP TABLE statements. java.id=books. title FROM authors. String password = "34klq*". import import import import import import java.NonTransaction main SEVERE: ERROR: column "titl" of relation "books" does not exist Position: 18 . Statement st = null. 2012 1:59:04 PM zetcode.sql. java. Batch updates When we need to update data with multiple statements. UPDATE.SQLException.sql.Statement. java.:lib/postgresql.sql. st = con. String user = "user12".getConnection(url.jdbc4. public class BatchUpdate { public static void main(String[] args) { Connection con = null.logging.setAutoCommit(false).Connection. con.. Leo Tolstoy did not write Martin Eden. we can use batch updates.util.Level. 205 .DriverManager. java.Logger. Batch updates are available for INSERT. books WHERE authors. try { con = DriverManager.jar zetcode..$ javac zetcode/NonTransaction. st.getLogger(BatchUpdate.log(Level. } if (con != null) { con.getMessage().getName()).addBatch("INSERT INTO friends(name) VALUES ('Robert')"). 206 . name VARCHAR(10))").SEVERE.log(Level. } } Logger lgr = Logger.rollback(). lgr.executeBatch(). lgr. } catch (SQLException ex) { System.addBatch("INSERT INTO friends(name) VALUES ('Tom')").out. int counts[] = st. Autocommit should always be turned off when doing batch updates.println("Committed " + counts. ex).setAutoCommit(false).class.addBatch("INSERT INTO friends(name) VALUES ('Rebecca')").getName()). System.getMessage(). ex1).st. } } catch (SQLException ex) { Logger lgr = Logger.class.WARNING.length + " updates"). st.addBatch("INSERT INTO friends(name) VALUES ('Jim')").close(). We create a new table called friends and insert five rows into it.getLogger(BatchUpdate.getMessage(). ex1. if (con != null) { try { con. } catch (SQLException ex1) { Logger lgr = Logger. st.close().addBatch("CREATE TABLE friends(id serial. con.addBatch("DROP TABLE IF EXISTS friends").addBatch("DROP TABLE IF EXISTS friends"). st. st.class.addBatch("CREATE TABLE friends(id serial. st.getName()). st. st.log(Level. ex. con.addBatch("INSERT INTO friends(name) VALUES ('Jane')").commit().getLogger(BatchUpdate. } finally { try { if (st != null) { st. lgr.getNextException()).println(ex.out. ex).WARNING. name VARCHAR(10))"). ex. } } } } This is an example program for a batch update. Level.sql.sql. We have created a new friends table and successfully inserted 5 rows. org.ResultSet.BatchUpdate Committed 7 updates testdb=# SELECT * FROM friends.:lib/postgresql.executeBatch().commit().BaseConnection. st. java. public class CopyTo { public static void main(String[] args) { Connection con = null. java. The method returns an array of committed changes.copy. id | name ----+--------1 | Jane 2 | Tom 3 | Rebecca 4 | Jim 5 | Robert (5 rows) We execute the BatchUpdate program. java. 207 .SQLException.postgresql. package zetcode. it is an extension to the standard.io. Export and import of data PostgreSQL has a COPY statement which can be used to copy data between a table and a file.java $ java -cp .st.jar zetcode. import import import import import import import import import import import java.Logger. java.jdbc4.util. java.IOException. Batch updates are committed in a transaction. From the JDBC point of view. java. org.DriverManager.. $ javac zetcode/BatchUpdate.Connection.core. con. java.addBatch("INSERT INTO friends(name) VALUES ('Jane')").sql.sql. .logging. java.CopyManager.. After adding all commands.io.FileWriter.util. int counts[] = st. We use teh addBatch() method to add a new command to the statement.logging.postgresql.PreparedStatement.addBatch("INSERT INTO friends(name) VALUES ('Tom')").sql. we call the executeBatch() to perform a batch update. class.close(). We will write the data to the friends. lgr. ex).close().copyOut("COPY friends TO STDOUT WITH DELIMITER AS '|'". try { con = DriverManager. user. } if (con != null) { con. ResultSet rs = null.getName()).txt").getConnection(url.txt file. CopyManager cm = new CopyManager((BaseConnection) con).PreparedStatement pst = null. CopyManager cm = new CopyManager((BaseConnection) con). String url = "jdbc:postgresql://localhost/testdb". lgr. String password = "34klq*". ex. } if (pst != null) { pst. } } catch (SQLException | IOException ex) { Logger lgr = Logger.close(). In the above code. ex).txt").getLogger(CopyTo. } if (fw != null) { fw. } finally { try { if (rs != null) { rs. 208 . we will copy the friends table to a file. } catch (SQLException | IOException ex) { Logger lgr = Logger.copyOut("COPY friends TO STDOUT WITH DELIMITER AS '|'".log(Level.class.close(). cm. FileWriter fw = null. ex. fw).getName()). password). } } } } In the previous example a simple friends table was created. Here we create an instance of the CopyManager.getMessage().getLogger(CopyTo.SEVERE. cm. fw = new FileWriter("friends.log(Level. String user = "user12". The CopyManager is the API for PostgreSQL COPY bulk data transfer.getMessage(). fw).WARNING. fw = new FileWriter("friends. Connection.sql.Logger.jdbc4. ex.FileReader. The columns will be delimited with the | character.jar zetcode/CopyTo.jdbc4.io. password). public class CopyFrom { public static void main(String[] args) { Connection con = null.logging. try { con = DriverManager.:lib/postgresql.class. java. CopyManager cm = new CopyManager((BaseConnection) con).util.SEVERE.sql. ResultSet rs = null.getConnection(url.sql. org.getName()). cm.sql.util.SQLException.PreparedStatement.ResultSet.postgresql.CopyTo $ cat friends. ex).sql. String url = "jdbc:postgresql://localhost/testdb". lgr. user. org. java. fr).txt"). java. java.:lib/postgresql. package zetcode. In the second example. } catch (SQLException | IOException ex) { Logger lgr = Logger.copy.DriverManager.java $ java -cp . java.copyIn("COPY friends FROM STDIN WITH DELIMITER '|'". fr = new FileReader("friends.getLogger(CopyFrom.BaseConnection.getMessage(). java.CopyManager. we do the reverse operation.logging.txt 1|Jane 2|Tom 3|Rebecca 4|Jim 5|Robert This is the file created. import import import import import import import import import import import java.log(Level. PreparedStatement pst = null.IOException. $ javac -cp .io.core. } finally { 209 . String user = "user12".Level. FileReader fr = null. We copy the data from the file into the database table.jar zetcode. java.We pass the results of a COPY TO STDOUT query from database into a Writer using the copyOut() method. java.postgresql. String password = "34klq*". id | name ----+--------1 | Jane 2 | Tom 3 | Rebecca 4 | Jim 5 | Robert (5 rows) First we delete the rows from the friends table. we check the contents of the friends table. 210 .close(). lgr.txt"). We will read from the friends.jar zetcode/CopyFrom.jdbc4.close().:lib/postgresql. DELETE 5 $ javac -cp . } if (pst != null) { pst.class.txt table and the COPY statement to transfer the data to the friends class. } } } } The example uses the FileReader class to read the contents of the friends.getName()). fr). } if (fr != null) { fr.getLogger(CopyFrom. This was the PostgreSQL Java tutorial.jar zetcode. testdb=# DELETE FROM friends.try { if (rs != null) { rs.txt file.CopyFrom testdb=# SELECT * FROM friends.copyIn("COPY friends FROM STDIN WITH DELIMITER '|'".close(). } } catch (SQLException | IOException ex) { Logger lgr = Logger.getMessage().log(Level.close(). ex).java $ java -cp .:lib/postgresql. cm. We copy the data from the file using the COPY statement. Then we compile and run the CopyFrom application.jdbc4. } if (con != null) { con.WARNING. Finally. fr = new FileReader("friends. ex. swing.table javax.swing javax. Swing Swing is a principal GUI toolkit for the Java programming language.basic javax.event javax.synth javax.border javax.colorchooser javax.swing.swing.swing. It is completely written in Java.plaf. It is a part of the JFC (Java Foundation Classes).plaf. you will be able to develop non-trivial Java Swing applications.swing.swing.accessibility javax.text javax.swing.Java Swing tutorial This is a Java Swing tutorial.text. which is an API for providing a graphical user interface for Java programs.metal javax.plaf. Introduction Java Swing This is an introductory Swing tutorial. It is used to create Graphical user interfaces with Java. After reading this tutorial. About Swing Swing library is an official Java GUI toolkit released by Sun Microsystems.swing. The Java Swing tutorial is suited for beginners and intermediate Swing developers.swing.multi javax. The tutorial has been created and tested on Linux.swing.plaf. The main characteristics of the Swing toolkit • • • • • platform independent customizable extensible configurable lightweight The Swing API has 18 public packages: • • • • • • • • • • • • • • javax.html 211 .plaf javax.swing.swing. The purpose of this tutorial is to get you started with the Java Swing toolkit.filechooser javax. swing. SWT uses the java native interface to do the job. It has a rich set of widgets. Similarly does the Qt4 toolkit. For example Borland's VCL is a heavyweight toolkit. SWT library There is also another GUI library for the Java programming language.rtf javax. JFC consists of AWT.swing. Swing was released in 1997 with JDK 1.undo Swing is an advanced GUI toolkit. and Drag and Drop. It is a mature toolkit. From basic widgets like buttons. It lets the underlying OS to create GUI. package com. Swing itself is written in Java. import javax.JFrame. Java Swing first programs In this chapter.text.zetcode. • • Lightweight Heavyweight A heavyweight toolkit uses OS's API to draw the widgets. Accessibility. It paints its own widgets.text. scrollbars to advanced widgets like trees and tables. The SWT is an example of a heavyweight toolkit.html.swing. which enables developers to create advanced 2D graphics and imaging. The Java platform has Java2D library. Java Foundation Classes.tree javax. The SWT library was initially developed by the IBM corporation.• • • • javax. Our first example In our first example. Swing is a lightweight toolkit. we will program our first programs in Swing toolkit.parser javax. Swing. labels. Now it is an open source project maintained by the Eclipse community. Swing is a part of JFC. There is a tutorial dedicated to SWT on ZetCode. which is built on top of X11 library. It is a collection of packages for creating full featured desktop applications. 212 . The examples are going to be very simple. The Standard widget toolkit. This was an introduction to Java Swing. On Unix systems. We will cover some basic functionality. It depends on WIN32 API. Java 2D. the built in Windows application programming interface.swing.2. we have GTK+ toolkit.swing. we will show a basic window on the screen. There are basically two types of widget toolkits. It is called SWT. All the complexity that comes with it has been hidden from the application programmer. 213 . we put other widgets.setVisible(true). 200). minimized. JFrame is a toplevel container.SwingUtilities.JFrame.swing. This code will resize the window to be 300px wide and 200px tall.invokeLater(new Runnable() { @Override public void run() { SimpleExample ex = new SimpleExample().swing. In the container. This line will center the window on the screen. ex. import javax.swing. import javax. setLocationRelativeTo(null).SwingUtilities. maximized. public class SimpleExample extends JFrame { public SimpleExample() { setTitle("Simple example"). public class Example extends JFrame { The Example class inherits from the JFrame widget. setLocationRelativeTo(null). the application window can do quite a lot. This method will close the window. } public static void main(String[] args) { SwingUtilities. setSize(300. setDefaultCloseOperation(EXIT_ON_CLOSE). } }). Here we import Swing classes that will be used in the code example. setDefaultCloseOperation(EXIT_ON_CLOSE).import javax. if we click on the close button of the titlebar. } } While this code is very short. It can be resized. By default nothing happens. 200). setTitle("Simple example"). Here we set the title of the window using the setTitle() method. setSize(300. We create an instance of our code example and make it visible on the screen. } }). getContentPane(). panel.SwingUtilities.event. we will have a button.swing. public class QuitButtonExample extends JFrame { public QuitButtonExample() { } initUI(). the application terminates.ActionEvent. it is to prevent GUI from hanging in certain situations. When we click on the button. import java. javax. import import import import javax. private void initUI() { JPanel panel = new JPanel(). javax. It is used to ensure that all UI updates are concurrency-safe.ActionListener.invokeLater(new Runnable() { @Override public void run() { SimpleExample ex = new SimpleExample(). In other words.setVisible(true). javax.zetcode. Figure: Simple example Quit button In our next example.swing. ex.SwingUtilities. The invokeLater() method places the application on the Swing Event Queue.add(panel).JFrame. package com. This topic is an advanced concept and we should not worry right now about it.swing. 214 .JButton.setLayout(null).event.swing.JPanel.awt.awt. import java. setVisible(true).invokeLater(new Runnable() { @Override public void run() { QuitButtonExample ex = new QuitButtonExample().addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent event) { System. } }). public QuitButtonExample() { } initUI().JButton quitButton = new JButton("Quit"). quitButton. We create a JPanel component. } public static void main(String[] args) { SwingUtilities. 60. 215 . For this.setBounds(50.exit(0). It is a good programming practice to put the code that creates the GUI inside a specific method. getContentPane(). 30). setDefaultCloseOperation(EXIT_ON_CLOSE). quitButton. JPanel panel = new JPanel(). 60. We position it by calling the setBounds() method. JButton quitButton = new JButton("Quit"). We will add an action listener to this button.add(quitButton). 80. setTitle("Quit button"). 200). setSize(300.setLayout(null). the JPanel has a FlowLayout manager. } }). setLocationRelativeTo(null). } } We position a JButton on the window. Here we create a button. ex. By default. 80. It is a generic lightweight container. panel. panel. If we call setLayout(null) we can position our components absolutely.add(panel).setBounds(50. quitButton. We add the JPanel to the JFrame. 30). we use the setBounds() method. The layout manager is used to place widgets onto the containers. panel. panel.swing. panel.JFrame. getContentPane().quitButton.JButton. The action listener will be called. } }). The click will terminate the application.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent event) { System.exit(0). JButton btn = new JButton("Button").swing. } private void initUI() { JPanel panel = new JPanel().SwingUtilities. Figure: Quit button A tooltip Tooltips are part of the internal application's help system.JPanel.swing.add(quitButton). if we hover a mouse pointer over an object. In our case. public class TooltipExample extends JFrame { public TooltipExample() { initUI(). import import import import javax. javax. package com. we must add it to the panel. when we perform an action on the button. javax.swing.setToolTipText("A Panel container"). In order to show the quit button. if we click on the button. The Swing shows a small rectangular window. javax. 216 . We add an action listener.add(panel).zetcode.setLayout(null). add(btn).setToolTipText("A Button component"). panel.setToolTipText("A Panel container").invokeLater(new Runnable() { @Override public void run() { TooltipExample ex = new TooltipExample(). There are accepted standards that further reduce the amount of time spending to learn a new application. 100. 217 . we have created some simple Java Swing programs. 200). To enable a tooltip.setBounds(100. setDefaultCloseOperation(EXIT_ON_CLOSE). ex. we call the setTooltipText() method. setTitle("Tooltip").btn. btn. setSize(300. 30). panel. Figure: Tooltip In this chapter. setLocationRelativeTo(null). } public static void main(String[] args) { SwingUtilities. While in console applications you had to remember all those arcane commands. It is a group of commands located in various menus. } } In the example. we set the tooltip for the frame and the button. Menus and toolbars in Java Swing A menubar is one of the most visible parts of the GUI application.setVisible(true). here we have most of the commands grouped into logical parts. 60. } }). ImageIcon.awt. javax. import import import import import import javax.add(eMenuItem). 200).event. import java.awt.swing.VK_F). file. A JMenuBar.swing. eMenuItem. eMenuItem.exit(0). setJMenuBar(menubar). package com.JMenuItem. } public static void main(String[] args) { SwingUtilities.In Java Swing. import java.swing.ActionListener.JFrame.add(file).setMnemonic(KeyEvent. } public final void initUI() { JMenuBar menubar = new JMenuBar(). setDefaultCloseOperation(EXIT_ON_CLOSE). javax.setToolTipText("Exit application"). public class Example extends JFrame { public Example() { initUI(). icon).ActionEvent. menubar. eMenuItem. to implement a menubar. setTitle("Simple menu"). ImageIcon icon = new ImageIcon(getClass().event.setMnemonic(KeyEvent. javax.invokeLater(new Runnable() { public void run() { 218 . JMenuItem eMenuItem = new JMenuItem("Exit".VK_E). } }).SwingUtilities. Simple menu We begin with a simple menubar example. we use three objects.JMenuBar.awt.getResource("exit. JMenu file = new JMenu("File"). a JMenu and a JMenuItem. javax. javax.swing.png")).swing.swing. setSize(300. setLocationRelativeTo(null).addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { System. file.event.KeyEvent.JMenu.zetcode. import java. the menu can be opened with the ALT + F shortcut. JMenuBar menubar = new JMenuBar(). }). print preview with a single separator. open. status bar or navigation bar into a submenu called toolbars. Menus commands can be launched via keyboard shortcuts. In our case. To bind a menu to a particular key. } } } Our example will show a menu with one item.setMnemonic(KeyEvent. Figure: Simple menu Submenu Each menu can also have a submenu.setVisible(true).Example ex = new Example(). We will display an icon in the menu. It is common practice to separate commands like new. Here we create a menubar.png")). file. ex. The menus can be accessed via the keybord as well. It is a simple line.setToolTipText("Exit application"). 219 . Selecting the exit menu item we close the application. Within a menu. This code line creates a tooltip for a menu item. eMenuItem. ImageIcon icon = new ImageIcon(getClass(). We create a menu object. we can seperate commands with a separator. This way we can group similar commnads into groups. we define menu item accelerators. JMenu file = new JMenu("File").VK_F). we use the setMnemonic() method. For this. save from commands like print.getResource("exit. address bar. For example we can place commands that hide/show various toolbars like personal bar. javax.event. public class Example extends JFrame { public Example() { initUI().VK_N).ActionEvent. javax. ActionEvent. ImageIcon iconNew = new ImageIcon(getClass().SwingUtilities. fileExit.png")). fileExit. iconSave). JMenu imp = new JMenu("Import")..ActionListener.setMnemonic(KeyEvent.awt.awt.png")).getResource("new. imp. JMenuItem fileOpen = new JMenuItem("Open".event.setMnemonic(KeyEvent.getResource("save. import java. JMenuItem fileSave = new JMenuItem("Save".add(mail). javax.swing.swing.png")).VK_M).zetcode..ImageIcon. import import import import import import import javax.setMnemonic(KeyEvent. fileNew. } public final void initUI() { JMenuBar menubar = new JMenuBar().event.getResource("exit. javax..awt. ImageIcon iconOpen = new ImageIcon(getClass().add(newsf). imp. iconExit). file. JMenu file = new JMenu("File"). import java.JMenuItem.VK_F).setToolTipText("Exit application"). JMenuItem mail = new JMenuItem("Import mail.JMenuBar. JMenuItem fileNew = new JMenuItem("New". iconOpen). javax. fileExit.swing.setMnemonic(KeyEvent.swing. JMenuItem fileExit = new JMenuItem("Exit".").CTRL_MASK)). fileNew.KeyStroke. javax.setMnemonic(KeyEvent.").png"))..VK_W.getKeyStroke(KeyEvent. ImageIcon iconSave = new ImageIcon(getClass().package com.. JMenuItem bookm = new JMenuItem("Import bookmarks.setAccelerator(KeyStroke.VK_C).").VK_O). ImageIcon iconExit = new ImageIcon(getClass().VK_S). JMenuItem newsf = new JMenuItem("Import newsfeed list..JFrame. fileSave.JMenu. imp.add(bookm). 220 . imp.swing. iconNew).getResource("open.swing.swing. import java.KeyEvent.setMnemonic(KeyEvent. file. file. A submenu is just like any other normal menu. setTitle("Submenu").add(fileSave).invokeLater(new Runnable() { public void run() { Example ex = new Example(). file. file. file. We simply add a menu to existing menu.add(imp).getKeyStroke(KeyEvent. } }). 250).setAccelerator(KeyStroke.setVisible(true).VK_W. In our case. setLocationRelativeTo(null).exit(0).fileExit. fileExit.addSeparator().addSeparator(). 221 . } } In this example.. file. file. It is created the same way. by pressing Ctrl + W we close the application. setJMenuBar(menubar).add(file). we create a submenu. } public static void main(String[] args) { SwingUtilities. A separator is a horizontal line that visually separates the menu items. This way we can group items into some logical places. ex..CTRL_MASK)). setDefaultCloseOperation(EXIT_ON_CLOSE).add(fileExit). a menu separator and an accelerator key. setSize(360.add(fileNew). . An accelerator is a key shortcut that launches a menu item. menubar. file.add(imp).addSeparator(). ActionEvent. file. } }).add(fileOpen). JMenu imp = new JMenu("Import").addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { System. awt.swing. } public final void initUI() { JMenuBar menubar = new JMenuBar(). javax.KeyEvent.VK_V). package com. 222 .setMnemonic(KeyEvent.BorderFactory.swing.Figure: Submenu JCheckBoxMenuItem A menu item that can be selected or deselected. or both.ActionEvent. javax. java. javax.VK_F). javax. javax.EtchedBorder. view. the menu item typically appears with a checkmark next to it.swing.JCheckBoxMenuItem.awt.swing.event. javax.JMenu.JMenuBar.zetcode.swing. file.awt. the menu item appears without a checkmark.ActionListener. java.swing. public class Example extends JFrame { private JLabel statusbar. JMenu view = new JMenu("View").swing. If unselected or deselected. Like a regular menu item.setMnemonic(KeyEvent. public Example() { initUI(). java. JMenu file = new JMenu("File").JFrame.JLabel. If selected.border.swing.event.awt.SwingUtilities. import import import import import import import import import import import import java.event.BorderLayout. a check box menu item can have either text or a graphic icon associated with it. javax. javax. } } }). sbar.setVisible(true). setLocationRelativeTo(null). add(statusbar. sbar. 250).setState(true).createEtchedBorder( EtchedBorder. } }). 223 .setVisible(true). menubar. The statusbar is initially visible.isVisible()) { statusbar. ex. statusbar.SOUTH). menubar.setState(true). if (statusbar. } else { statusbar. } The example shows a JCheckBoxMenuItem.add(view).invokeLater(new Runnable() { public void run() { Example ex = new Example().setVisible(true).isVisible()) { statusbar. By selecting the menu item. sbar. BorderLayout.setVisible(false).JCheckBoxMenuItem sbar = new JCheckBoxMenuItem("Show StatuBar"). setDefaultCloseOperation(EXIT_ON_CLOSE).add(file).setBorder(BorderFactory.add(sbar). we toggle the visibility of the statusbar.setVisible(false). } Here we toggle the visibility of the statusbar. setTitle("JCheckBoxMenuItem"). We create the JCheckBoxMenuItem and check it by default. view. } } public static void main(String[] args) { SwingUtilities. statusbar = new JLabel(" Statusbar"). JCheckBoxMenuItem sbar = new JCheckBoxMenuItem("Show StatuBar"). setJMenuBar(menubar).addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { if (statusbar.RAISED)). setSize(360. } else { statusbar. JPopupMenu.awt.MouseEvent.JMenuItem.awt. The statusbar is a simple JLabel component.event.swing.event. It is sometimes called a context menu.event.ActionListener. Say we have an image. public class PopupMenu extends JFrame { private JPopupMenu menu.JFrame. move etc the image. public PopupMenu(String title) { super(title).event. We put a raised EtchedBorder around the label. when we right click on a component.createEtchedBorder( EtchedBorder.swing. statusbar. java. javax.statusbar = new JLabel(" Statusbar").setBorder(BorderFactory.SwingUtilities. By right clicking on the image. javax.MouseAdapter.swing.swing. java. this.getToolkit().Toolkit.zetcode. java. 224 . Figure: JCheckBoxMenuItem A popup menu Another type of a menu is a popup menu.awt.initUI().awt. It is usually shown. that are relevant to the current context.RAISED)). import import import import import import import import import java. java. package com.ActionEvent. private Toolkit toolkit. we get a window with commands to save.awt. so that it is visible. } private void initUI() { toolkit = this. javax. javax. rescale. The idea is to provide only the commands. getComponent(). e. pm. this. as with the standard JMenu this.invokeLater(new Runnable() { public void run() { PopupMenu pm = new PopupMenu("JPopupMenu").beep().menu = new JPopupMenu().EXIT_ON_CLOSE). JMenuItem menuItemClose = new JMenuItem("Close").setSize(250. e. } } Our example shows a demonstrational popup menu with two commands. JMenuItem menuItemBeep = new JMenuItem("Beep"). } } }).add(menuItemBeep).setDefaultCloseOperation(JFrame. this.add(menuItemClose).getButton() == MouseEvent.show(e. } }).getX(). In our example.BUTTON3) { menu. } public static void main(String[] args) { SwingUtilities. JMenuItem menuItemBeep = new JMenuItem("Beep").addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { toolkit. we create a submenu. the second one will close the window. } }). The first option of the popup menu will beep a sound.addMouseListener(new MouseAdapter() { 225 .exit(0). } }). menuItemClose. we have a class called JPopupMenu. 200).addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { System.setLocationRelativeTo(null). The menu item is the same. menu = new JPopupMenu(). this. menu. menu separators and create an accelerator key. menu. this. menuItemBeep.setVisible(true).getY()).addMouseListener(new MouseAdapter() { @Override public void mouseReleased(MouseEvent e) { if (e. To create a popup menu. JMenuBar. 226 .swing. javax. Toolbars provide a quick access to the most frequently used commands. javax. Figure: Popup menu JToolbar Menus group commands that we can use in an application. } } }).getComponent().ImageIcon. javax.BUTTON3) { menu.awt.awt.SwingUtilities.@Override public void mouseReleased(MouseEvent e) { if (e.getY()).ActionEvent.BorderLayout. e. import java. the JToolBar class creates a toolbar in an application. The popup menu is shown.swing.swing. where we clicked with the mouse button. javax.awt.ActionListener.JToolBar. import java.swing.swing. In Java Swing.BUTTON3 constant is here to enable the popup menu only for the mouse right click.JButton.event. e. public class Example extends JFrame { public Example() { initUI().JFrame. JMenu file = new JMenu("File"). javax.getX(). javax. The MouseEvent.event.swing.zetcode. import import import import import import import javax.show(e. import java.getButton() == MouseEvent.swing. package com.JMenu. } public final void initUI() { JMenuBar menubar = new JMenuBar(). exit(0). ImageIcon icon = new ImageIcon(getClass().add(exitButton). ex. setSize(300. add(toolbar.setVisible(true). 227 . This is the JToolBar constructor. } }).add(file). setJMenuBar(menubar).getResource("exit. JButton exitButton = new JButton(icon).addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { System. JToolBar toolbar = new JToolBar(). BorderLayout. } public static void main(String[] args) { SwingUtilities. setLocationRelativeTo(null). } } The example creates a toolbar with one exit button.NORTH). setTitle("Simple toolbar"). JToolBar toolbar = new JToolBar(). } }). setDefaultCloseOperation(EXIT_ON_CLOSE). exitButton. toolbar. We create a button and add it to the toolbar.invokeLater(new Runnable() { public void run() { Example ex = new Example().add(exitButton).png")). JButton exitButton = new JButton(icon).menubar. 200). toolbar. swing.add(openb). toolbar1. import java. import java.png")). javax.JButton.BorderLayout. ImageIcon open = new ImageIcon( getClass(). } public final void initUI() { JToolBar toolbar1 = new JToolBar(). toolbar1. JButton saveb = new JButton(save).ActionListener.add(saveb).awt.add(exitb). public class Example extends JFrame { public Example() { initUI().png")). javax.swing.png")). toolbar2.getResource("save. javax.awt.setAlignmentX(0). JButton openb = new JButton(open). import import import import import import import javax.exit(0). how we could do it. JButton newb = new JButton(newi). BoxLayout. panel. ImageIcon newi = new ImageIcon( getClass().SwingUtilities. 228 . package com. toolbar1.awt. ImageIcon exit = new ImageIcon( getClass().addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { System. import java.png")).add(newb).Figure: Simple toolbar Toolbars Say.ActionEvent.zetcode.JToolBar.getResource("new. we wanted to create two toolbars. JPanel panel = new JPanel().event. ImageIcon save = new ImageIcon( getClass(). javax.swing. javax.BoxLayout.swing.swing.swing. javax.JPanel.ImageIcon. JButton exitb = new JButton(exit). exitb. The next example shows.getResource("exit.swing.event.Y_AXIS)).JFrame. toolbar1.getResource("open.setAlignmentX(0). JToolBar toolbar2 = new JToolBar().setLayout(new BoxLayout(panel. toolbar2. NORTH).setVisible(true). panel. BorderLayout. setLocationRelativeTo(null). Creation of two toolbars.add(toolbar1). We add the toolbars to the panel. The panel has a vertical BoxLayout. panel.add(toolbar2).Y_AXIS)). there are several possibilities. the panel is located into the north part of the frame. toolbar1. BoxLayout. JToolBar toolbar1 = new JToolBar(). The panel has a vertical BoxLayout. The toolbar is left aligned. setSize(360. } } public static void main(String[] args) { SwingUtilities. setTitle("Toolbars"). } }). panel. 250). We put a JPanel to the north of the BorderLayout manager. JToolBar toolbar2 = new JToolBar(). BorderLayout. setDefaultCloseOperation(EXIT_ON_CLOSE). panel. ex. add(panel.} }).invokeLater(new Runnable() { public void run() { Example ex = new Example(). JPanel panel = new JPanel(). add(panel. 229 . Of course.add(toolbar1). how we could create toolbars.setLayout(new BoxLayout(panel. Finally.setAlignmentX(0). We add the two toolbars into this panel. panel. } We show only one way.NORTH).add(toolbar2). import javax. I believe. complex applications. For example.SwingUtilities. Mainly because they are usually poorly documented. Without layout manager. Layout managers are one of the most difficult parts of modern GUI programming. we need layout managers. we use layout managers. import javax. where we might not need a layout manager. in my code examples. The containers group children into suitable layouts. that GUI builder's like Matisse cannot replace the proper understanding of layout managers. I often go without a manager. Containers and children.swing. Many beginning programmers have too much respect for layout managers. 230 . package zetcode.swing. if we want. To create layouts. import javax.JButton. public class AbsoluteExample extends JFrame { public AbsoluteExample() { initUI(). The Swing layout management The Java Swing toolkit has two kind of components. we have mentioned menus and toolbars.swing. we position components using absolute values.Figure: Toolbars In this part of the Java Swing tutorial. It is because I did not want to make the examples too complex. 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. There might be situations. But to create truly portable.JFrame. setVisible(true). 50. 80.invokeLater(new Runnable() { public void run() { AbsoluteExample ex = new AbsoluteExample(). When calculating its children size. add(close). The setBounds() method positions the ok button. a flow layout lets each component assume its natural (preferred) size.EXIT_ON_CLOSE). 50. We use absolute positioning by providing null to the setLayout() method. The parameters are the x. 25). It is mainly used in combination with other layout managers. } } This simple example shows two buttons. setDefaultCloseOperation(JFrame. setLayout(null). If they do not fit into one row. The manager allows to align the components.setBounds(150. JButton ok = new JButton("OK"). The manager puts components into a row. they go into the next one. add(ok). } public static void main(String[] args) { SwingUtilities. In the order. 25). setLocationRelativeTo(null). y location values and the width and height of the component. 50.} public final void initUI() { setLayout(null). setSize(300. ex. FlowLayout manager This is the simplest layout manager in the Java Swing toolkit. setTitle("Absolute positioning"). close. they were added. The components can be added from the right to the left or vice versa. 80. ok. 231 . Implicitly. 250). 80.setBounds(50. the components are centered and there is 5px space among components and components and the edges of the container. ok. JButton close = new JButton("Close"). } }).setBounds(50. 25). JPanel.add(area). The first one creates a manager with implicit values.SwingUtilities. import import import import import import javax.swing. public class FlowLayoutExample extends JFrame { public FlowLayoutExample() { } initUI().JTree. ex.JTextArea.JButton. JTextArea area = new JTextArea("text area").FlowLayout() FlowLayout(int align) FlowLayout(int align. add(panel). panel.setPreferredSize(new Dimension(100. setLocationRelativeTo(null). The others allow to specify those parametes.add(tree). package zetcode.add(button). javax. setDefaultCloseOperation(JFrame. javax.swing.invokeLater(new Runnable() { public void run() { FlowLayoutExample ex = new FlowLayoutExample().JFrame. 100)). import java. javax. panel. 232 .EXIT_ON_CLOSE).awt. setTitle("FlowLayout Example").swing. pack().swing. panel.swing.Dimension. JButton button = new JButton("button"). public final void initUI() { JPanel panel = new JPanel(). area. Centered with 5px horizontal and vertical spaces. javax. int vgap) There are three constructors available for the FlowLayout manager.setVisible(true). int hgap.swing. JTree tree = new JTree(). } public static void main(String[] args) { SwingUtilities. javax. The container is divided into equally sized rectangles. If we didn't set the preferred size. a tree and a text area component in the window.swing. No more. javax.JButton.swing. Try to write or delete some text in the area component. This means. JPanel panel = new JPanel().swing. Figure: FlowLaout manager GridLayout The GridLayout layout manager lays out components in a rectangular grid. Interesingly. import import import import import import javax. no less.GridLayout. there are some default values inside the component.add(area).JLabel. The flow layout manager sets a preferred size for it's components. To put a component inside a container. if we create an empty tree component. javax. One component is placed in each rectangle. We do not have to set it manually. The component will grow and shrink accordingly. that in our case.JPanel.}). the component would have a size of its text. javax. we simply call the add() method.awt. the component would not be visible at all. the area component will have 100x100 px. public class GridLayoutExample extends JFrame { 233 .SwingUtilities. javax. package zetcode. 100)). } } } The example shows a button.swing. panel. area.setPreferredSize(new Dimension(100. JTextArea area = new JTextArea("text area").swing. javax.JFrame. import java. The implicit layout manager of the JPanel component is a flow layout manager.swing.BorderFactory. Without the text. ex. "1". ".length. the number of columns and the horizontal and vertical gaps between components. "3". "". 5. "6". 234 . } public static void main(String[] args) { SwingUtilities. "=". } public final void initUI() { JPanel panel = new JPanel(). "/". 5)). The example shows a skeleton of a simple calculator tool. String[] buttons = { "Cls".setBorder(BorderFactory.public GridLayoutExample() { initUI(). setSize(350. "8". i < buttons. "*". 5)).add(new JButton(buttons[i])). "9". 4. panel. setTitle("GridLayout").".setVisible(true). Notice. Here we set the grid layout manager for the panel component. that each button is of the same size.add(new JLabel(buttons[i])).EXIT_ON_CLOSE). 5)). else panel. "-". 4. 5. "Bck". 5. We put 19 buttons and one label into the manager. "2". i++) { if (i == 2) panel. "5". 5. The layout manager takes four parameters. The number of rows. } add(panel). "+" }. panel.invokeLater(new Runnable() { public void run() { GridLayoutExample ex = new GridLayoutExample(). It is an ideal example for this layout manager. 300). "4". "Close". setDefaultCloseOperation(JFrame. panel.setLayout(new GridLayout(5. setLocationRelativeTo(null).createEmptyBorder(5. for (int i = 0. "0". "7". } } } }).setLayout(new GridLayout(5. we can simply put a panel there with a manager of our choice.swing.swing. public class BorderExample extends JFrame { public BorderExample() { } initUI(). javax. West. java.JFrame. if child components are too close to each other. Each region can have only one component. It divides the space into five regions. E regions get their preferred size. java.JPanel. We must put some space among them. I haven't seen it elsewhere. If we need to put more components into a region.SwingUtilities. North. javax.Color. public final void initUI() { 235 . Each component in Swing toolkit can have borders around it's edges. package zetcode. import import import import import import import import java.BorderLayout.Dimension.Figure: GridLayout manager BorderLayout A BorderLayout manager is a very handy layout manager. javax. W. To create a border.Insets.swing. S. there are other types of borders as well. But for layout management we will use only this one. we either create a new instance of an EmptyBorder class or we use a BorderFactory.awt. South.awt.border.EmptyBorder. East and Centre. Except for EmptyBorder. It does not look good. The components in N.awt. The component in the centre takes up the whole space left.awt. java.swing. javax. pack(). panel. panel. bottom and right. JPanel top = new JPanel(). Here we placed a top panel into the panel component. 20))). } public static void main(String[] args) { SwingUtilities. They go counterclockwise. add(panel). 150)). JPanel top = new JPanel(). top.setBorder(new EmptyBorder(new Insets(20. 20. More precisely. 236 .add(top). panel. We place a panel into a panel. ex.setBorder(new EmptyBorder(new Insets(20.setBackground(Color. } } } }).setVisible(true).JPanel panel = new JPanel(new BorderLayout()).add(top). 20. panel. setDefaultCloseOperation(JFrame. JPanel panel = new JPanel(new BorderLayout()). 20))). 20.gray). setTitle("Border Example").invokeLater(new Runnable() { public void run() { BorderExample ex = new BorderExample(). we placed into the center area of the BorderLayout manager. The example will display a gray panel and border around it. top. Here we created a 20px border around the panel.EXIT_ON_CLOSE). because this manager will resize its children. left. The border values are as follows: top. setLocationRelativeTo(null). 20.setPreferredSize(new Dimension(250. We used a BorderLayout manager for the first panel. JFrame. 0)). bexit.swing.png"). } public final void initUI() { JMenuBar menubar = new JMenuBar(). JButton bexit = new JButton(exit).swing. javax.swing.add(bexit). javax.JToolBar.JButton.add(file).ImageIcon. javax.swing.swing. toolbar. javax.LineBorder. menubar. public class BorderLayoutExample extends JFrame { public BorderLayoutExample() { initUI(). import import import import import import import import import import import javax.setFloatable(false). import java.JMenu. JMenu file = new JMenu("File"). JToolBar toolbar = new JToolBar().Figure: Border example The next example will show a typical usage of a border layout manager. import java. 237 .awt.JMenuBar.BorderLayout.awt. setJMenuBar(menubar).Dimension.swing.swing. import java.swing.Insets.border.0. javax. package zetcode. javax. javax. toolbar.JLabel.swing.SwingUtilities.setBorder(new EmptyBorder(0 .JTextArea.awt. 0. javax.EmptyBorder.border. javax.swing. ImageIcon exit = new ImageIcon("exit. javax.swing. JButton freehandb = new JButton(freehand). (a text area) A default layout manager for a JFrame component is BorderLayout manager. 5. setDefaultCloseOperation(JFrame.setVisible(true). JButton selectb = new JButton(select). BorderLayout. setSize(350. JLabel statusbar = new JLabel(" Statusbar"). 0)).add(freehandb).invokeLater(new Runnable() { public void run() { BorderLayoutExample ex = new BorderLayoutExample().png"). 300). vertical.WEST). vertical. add(new JTextArea(). 238 . BorderLayout.createGrayLineBorder()). The example shows a typical application skeleton. 0. statusbar. vertical.NORTH). selectb. add(statusbar. BorderLayout. We show a vertical and horizontal toolbars. add(toolbar. 5)). 0)). a statusbar and a central component.setFloatable(false).setBorder(new EmptyBorder(3.png"). ImageIcon freehand = new ImageIcon("computer. ImageIcon shapeed = new ImageIcon("printer. BorderLayout. JToolBar vertical = new JToolBar(JToolBar. 0.add(shapeedb). 0)). } public static void main(String[] args) { SwingUtilities.CENTER). 3.EXIT_ON_CLOSE). 5.NORTH). vertical.setBorder(new EmptyBorder(3. setTitle("BorderLayout"). } } } }). So we don't have to set it. BorderLayout.setPreferredSize(new Dimension(-1.setMargin(new Insets(10. statusbar. 3. 22)). vertical.VERTICAL). 0. add(vertical. JButton shapeedb = new JButton(shapeed).SOUTH).setBorder(LineBorder.setBorder(new EmptyBorder(3. setLocationRelativeTo(null). ex. ImageIcon select = new ImageIcon("drive. shapeedb.png"). 3. freehandb.add(toolbar. Simply put the toolbar to the north of the layout.add(selectb). We will use the boxlayut managers to accomplish this. This layout manager puts components into a row or into a column. BorderLayout. Figure: BorderLayout manager BoxLayout BoxLayout is a powerful manager. The box layout manager is often used with the Box class. 239 . add(new JTextArea(). It means. Put the text area to the center. Put the vertical toobar to the west. BorderLayout.awt.SOUTH). BorderLayout. which affect the final layout.Dimension. we want to put two buttons into the right bottom corner of the window. This class creates several invisible components. It enables nesting.CENTER). that makes this manager very flexible. add(statusbar. package zetcode. Put the statusbar to the south. that can be used to create sofisticated layouts. • • • glue strut rigid area Let's say.WEST). a powerful feature. that we can put a box layout into another box layout. import java.add(vertical. swing.add(Box. The following drawing illustrates the example.SwingUtilities. 0))).JFrame.add(ok).createVerticalGlue()). 0))).setVisible(true). bottom.X_AXIS)).BoxLayout.Y_AXIS)). javax. javax.JPanel.add(Box.setAlignmentX(1f). bottom. bottom.setLayout(new BoxLayout(bottom. JPanel bottom = new JPanel().createRigidArea(new Dimension(5. setTitle("Two Buttons"). bottom.invokeLater(new Runnable() { public void run() { TwoButtonsExample ex = new TwoButtonsExample(). bottom. basic.createRigidArea(new Dimension(15. javax. setDefaultCloseOperation(JFrame.add(close). BoxLayout. javax.swing. BoxLayout. bottom. add(basic).import import import import import import javax. setLocationRelativeTo(null).swing.add(Box.JButton. public final void initUI() { JPanel basic = new JPanel(). setSize(300. basic.EXIT_ON_CLOSE). ex.add(Box.Box. } } } }). 240 . basic.swing.swing.swing. basic. javax. } public static void main(String[] args) { SwingUtilities.setLayout(new BoxLayout(basic.createRigidArea(new Dimension(0. 15))). JButton close = new JButton("Close"). 150).add(bottom). public class TwoButtonsExample extends JFrame { public TwoButtonsExample() { } initUI(). JButton ok = new JButton("OK"). We will right align the bottom panel.setLayout(new BoxLayout(bottom. The space between the top of the window and the bottom panel is expandable.Figure: Two buttons We will create two panels. BoxLayout. The basic panel has a vertical box layout. Figure: Two buttons 241 . The bottom panel has a horizontal one. Here we create a basic panel with the vertical BoxLayout. basic.createRigidArea(new Dimension(0. 15))).setAlignmentX(1f).setLayout(new BoxLayout(basic. 0))). Here we put the bottom panel with a horizontal box layout to the vertical basic panel.createRigidArea(new Dimension(5. It is done by the vertical glue.add(bottom). We put some rigid space between the buttons.add(Box. We will put a bottom panel into the basic panel.X_AXIS)).add(Box. The bottom panel is right aligned. bottom. JPanel bottom = new JPanel().Y_AXIS)). The panel has a horizontal layout. This is done by the setAlignmentX() method. basic. bottom. BoxLayout. We also put some space between the bottom panel and the border of the window. basic. bottom. EXIT_ON_CLOSE). panel. panel. } public final void initUI() { JPanel panel = new JPanel(). javax.swing.Insets.BoxLayout.add(new JButton("Button")). we can set a rigid area among our components.setBorder(new EmptyBorder(new Insets(40.SwingUtilities. import java. package zetcode.swing.When we use a BoxLayout manager.createRigidArea(new Dimension(0.add(Box.add(Box.awt.swing. 60))).Y_AXIS)).add(new JButton("Button")). import import import import import import import javax.JPanel. BoxLayout.add(new JButton("Button")). pack(). panel. 5))). panel. javax. panel. 40. 5))). } public static void main(String[] args) { SwingUtilities.swing.border. ex.createRigidArea(new Dimension(0.JButton. javax. setDefaultCloseOperation(JFrame.add(new JButton("Button")). 60. import java. panel. javax.Box.JFrame.swing. panel.createRigidArea(new Dimension(0. setLocationRelativeTo(null). setTitle("RigidArea").awt. panel.EmptyBorder. 5))). javax.setLayout(new BoxLayout(panel.Dimension. } } } }).invokeLater(new Runnable() { public void run() { RigidAreaExample ex = new RigidAreaExample(). public class RigidAreaExample extends JFrame { public RigidAreaExample() { initUI().add(Box. javax.swing. panel. 242 . add(panel).setVisible(true).swing. BoxLayout. To put some space among them. Figure: Rigid area Tip of the Day JDeveloper has a dialog window called Tip of the Day.createRigidArea(new Dimension(0. panel. 5))). panel.setLayout(new BoxLayout(panel. panel. panel.In this example.add(new JButton("Button")). We add buttons and create a rigid area in between them. there is no space among the buttons. We use a vertical BoxLayout manager for our panel.add(new JButton("Button")).Y_AXIS)). 243 . we add some rigid area. we display four buttons.add(Box. By default. We will use a combination of various layout managers. java.KeyEvent.swing.awt.FlowLayout.Figure: Tip of the Day We will create a similar dialog.awt. flow layout and box layout manager. javax. java.swing.JTextPane.swing. javax. Namely a border layout.swing.JPanel.awt.swing. java. javax.swing. javax. javax. javax.BoxLayout. public class TipOfDayExample extends JDialog { public TipOfDayExample() { initUI().Color.swing. javax.Dimension.swing.event. } public final void initUI() { 244 . javax. javax.swing. java.swing.awt.awt.BorderFactory. package zetcode.ImageIcon. javax.JCheckBox.JDialog.JLabel. javax.SwingUtilities.JSeparator.JButton.swing.BorderLayout. import import import import import import import import import import import import import import import import java. setMnemonic(KeyEvent. 245 . textPanel.</p>". JSeparator separator = new JSeparator().createEmptyBorder(0. 20. 25.RIGHT)). close. separator. add(basic). 0)). JButton close = new JButton("Close"). textPanel. 0. pane.add(pane).setLayout(new BoxLayout(basic. String text = "<p><b>Closing windows using the mouse wheel</b></p>" + window.VK_N).setForeground(Color. topPanel. basic.add(boxPanel). JPanel boxPanel = new JPanel(new FlowLayout(FlowLayout. JPanel bottom = new JPanel(new FlowLayout(FlowLayout. topPanel.setBorder(BorderFactory. basic.setMaximumSize(new Dimension(450. bottom. BorderLayout.Y_AXIS)). basic. JLabel label = new JLabel(icon).add(bottom). JLabel hint = new JLabel("JDeveloper Productivity Hints"). basic.add(textPanel).setMnemonic(KeyEvent.add(label. hint.setMnemonic(KeyEvent.createEmptyBorder(15. label. "This method works also with dockable windows or Log window tabs. topPanel. 5. 5)). topPanel. pane. ntip.VK_C). box. boxPanel.createEmptyBorder(5. " + "<p>Clicking with the mouse wheel on an editor tab closes the 25)).add(ntip).add(topPanel).add(close). pane.setEditable(false).setBorder(BorderFactory. BoxLayout.add(separator. basic. JPanel topPanel = new JPanel(new BorderLayout(0.SOUTH).setText(text). BorderLayout.LEFT. 15.JPanel basic = new JPanel(). 25.VK_S). ImageIcon icon = new ImageIcon("jdev.setBorder(BorderFactory. JPanel textPanel = new JPanel(new BorderLayout()). JCheckBox box = new JCheckBox("Show Tips at startup"). 0)).setContentType("text/html").add(hint). JButton ntip = new JButton("Next Tip").gray).EAST). bottom. JTextPane pane = new JTextPane(). 5. 0)).add(box).png"). 0)). setMaximumSize(new Dimension(450. setResizable(false). The example uses a mix of layout managers. BoxLayout. setLocationRelativeTo(null). we must set it's maximum size. JPanel basic = new JPanel().LEFT. setSize(new Dimension(450.invokeLater(new Runnable() { public void run() { TipOfDayExample ex = new TipOfDayExample(). The flow layout manager has a 20px horizontal gap. add(basic). . 350)). } } } }). } public static void main(String[] args) { SwingUtilities. ex.bottom. 20. The manager calculates the necessary heights. Two labels and a separator. basic.setLayout(new BoxLayout(basic..Y_AXIS)). setDefaultCloseOperation(JDialog. Exactly. Other components have 25px. 0)). It is left aligned. The text pane component is added to the center area of the border layout manager.DISPOSE_ON_CLOSE). JPanel textPanel = new JPanel(new BorderLayout()). The topPanel panel has a border layout manager. The zero value is ignored.setMaximumSize(new Dimension(450. textPanel. as we wanted. It takes all space left. that is not greater than it's components.. It has a vertical box layout manager. If we want to have a panel. topPanel. This is the very bottom panel. 246 . 0)). JPanel boxPanel = new JPanel(new FlowLayout(FlowLayout. JPanel topPanel = new JPanel(new BorderLayout(0.setVisible(true). setTitle("Tip of the Day").add(pane). 0)). 0)). Simply we put four panels into the vertically organized basic panel. The check box is shown in the boxPanel panel. The basic panel is added to the default JDialog component. We will put three components into it. This component has a border layout manager by default. Why is that? It is because the flow layout manager puts some space to between the component and the edge as well. Event source object delegates the task of handling an event to the event listener. window manager. Events are generated mainly by the user of an application. All GUI applications are event-driven. bottom. In the event model. In order to show the buttons on the right edge of the dialog.. the panel must stretch horizontally from the beginning to the end. The Event listener is the object that wants to be notified. It has a right aligned flow layout manager. .JPanel bottom = new JPanel(new FlowLayout(FlowLayout. e. The bottom panel displays two buttons. 0)). there are three participants: • • • event source event object event listener The Event source is the object whose state changes. It generates Events..setMaximumSize(new Dimension(450. timer. 247 . But they can be generated by other means as well.RIGHT)). Figure: Tip of the Day In this chapter. An application reacts to different event types which are generated during its life. The Event object (Event) encapsulates the state changes in the event source.g. we have mentioned layout management in Swing. internet connection. Java Swing Events Events are an important part in any GUI program. ComponentEvent etc. javax.event.swing.util.Event handling in Java Swing toolkit is very powerful and flexible.setBounds(150.awt. import java. when we click on the button or select an item from a list. java.ActionEvent. TextEvent.JList.DefaultListModel. list. javax. add(panel). Event object has information about an event. panel. an event object is created.Date. public class Example extends JFrame implements ActionListener { private JList list.text. 220.ActionListener. import java. javax. 35.awt. java.event.util. 248 .addActionListener(this). that has happened. There are several types of events. 250). javax.swing. 25). Java uses Event Delegation Model. We specify the objects that are to be notified when a specific event occurs.swing.setBounds(30. javax. okButton. 80. package zetcode.swing. model = new DefaultListModel(). For example. panel. public Example() { initUI().swing. we will analyze an ActionEvent in more detail. An ActionEvent.JPanel.setLayout(null). private DefaultListModel model. In the next example. FocusEvent. 150).JFrame. setTitle("Event object"). setSize(420.add(okButton). panel. } public final void initUI() { JPanel panel = new JPanel(). import import import import import import import import import java.Locale.swing. JButton okButton = new JButton("Ok").add(list).DateFormat. Each of them is created under specific conditions.JButton. javax. list = new JList(model). An event object When something happens in the application.SwingUtilities. okButton. 30. } if (e.setLocationRelativeTo(null). } model.append("Meta "). public static void main(String[] args) { SwingUtilities.getClass().isEmpty()) { model.getID() == ActionEvent.getSource().ALT_MASK) > 0) { buffer. The code example shows a button and a list. } } model.getModifiers(). model. ex.getTimeInstance(DateFormat.addElement(" Time: " + s).append("Shift ").SHIFT_MASK) > 0) { buffer. String source = e. if ((mod & ActionEvent.addElement(" Event Id: ACTION_PERFORMED").addElement(buffer).ACTION_PERFORMED) { model.append("Alt "). int mod = e. } if ((mod & ActionEvent. we are talking about an ActionEvent class. } public void actionPerformed(ActionEvent e) { Locale locale = Locale. Date date = new Date(e.CTRL_MASK) > 0) { buffer.addElement(" Source: " + source).getDefault().getName().format(date). } if ((mod & ActionEvent.invokeLater(new Runnable() { public void run() { Example ex = new Example(). if (!model. } if ((mod & ActionEvent.append("Ctrl ").getWhen()). setDefaultCloseOperation(EXIT_ON_CLOSE). The 249 . } } } }). In our case.clear().setVisible(true). information about the event is displayed in the list.SHORT. StringBuffer buffer = new StringBuffer(" Modifiers: ").META_MASK) > 0) { buffer. If we click on the button. locale). String s = DateFormat. that has occurred. Locale locale = Locale. In our case the source is a JButton. We get the modifier keys.SHIFT_MASK) > 0) buffer. • • Anonymous inner class Inner class 250 .addElement(" Source: " + source). Here we add the name of the source of the event to the list. model. whether we have pressed a Shift key.append("Shift ").format(date). It is a bitwise-or of the modifier constants.getDefault(). The getWhen() method returns time value in milliseconds. String s = DateFormat.getTimeInstance(DateFormat. the id of the event. how we can implement event handling in Java Swing toolkit. locale).getWhen()). when the event occurred.getName().getSource(). Here we determine. String source = event. Date date = new Date(e. when the event occurred. Figure: Event Object Implementation There are several ways. public void actionPerformed(ActionEvent event) { The ActionEvent is an instance of the event. So we must format it appropriately.getModifiers(). the event source and the modifier keys. Here we get the time. if ((mod & ActionEvent. int mod = event.SHORT.getClass().data will be the time. swing.awt. 200).swing.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { System. } } }). 25). import java.setLayout(null).JPanel. add(panel). setTitle("Anonymous inner class"). package zetcode. 50. javax.ActionListener. import import import import javax. public class AnonymousInnerClassExample extends JFrame { public AnonymousInnerClassExample() { initUI().exit(0).SwingUtilities.swing.swing.awt. panel. javax. } }). closeButton. 251 .• Derived class Anonymous inner class We will illustrate these concepts on a simple event example. } public static void main(String[] args) { SwingUtilities.ActionEvent.JButton. 80. setSize(300. ex. import java.add(closeButton).event.JFrame. setLocationRelativeTo(null).setVisible(true). closeButton. panel.invokeLater(new Runnable() { public void run() { AnonymousInnerClassExample ex = new AnonymousInnerClassExample().event. } public final void initUI() { JPanel panel = new JPanel().setBounds(40. javax. JButton closeButton = new JButton("Close"). setDefaultCloseOperation(EXIT_ON_CLOSE). add(panel). In this code. import java. public class InnerClassExample extends JFrame { public InnerClassExample() { initUI(). It will generate events. package zetcode.swing.add(closeButton). we have a button that closes the window upon clicking.event.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { System.JPanel. panel. This way. JButton closeButton = new JButton("Close").swing.addActionListener(listener). setSize(300. Inner class Here we implement the example using an inner ActionListener class. the events are sent to the event target. JButton closeButton = new JButton("Close").ActionListener. setLocationRelativeTo(null). import import import import javax. javax.} In this example. 252 .SwingUtilities.JButton.awt. closeButton.setBounds(40. 200). javax.setLayout(null).exit(0).swing. The button is the event source. } public final void initUI() { JPanel panel = new JPanel(). we use an anonymous inner class. closeButton. } }). setTitle("Inner class example"). ButtonCloseListener listener = new ButtonCloseListener(). javax. The event target in our case is ActionListener class. closeButton. 25).awt.JFrame.swing. 80.event. panel. Here we register an action listener with the button. 50.ActionEvent. import java. setDefaultCloseOperation(EXIT_ON_CLOSE). JButton.swing. package zetcode. javax. ex. which has a name. javax.exit(0). javax.exit(0).invokeLater(new Runnable() { public void run() { InnerClassExample ex = new InnerClassExample().awt. } } We have on close button on the panel. class ButtonCloseListener implements ActionListener { public void actionPerformed(ActionEvent e) { System. The listener is defined inside an inner class.JPanel. } }).swing. import java. A derived class implementing the listener The following example will derive a class from a component and implement an action listener inside the class.setVisible(true).SwingUtilities.addActionListener(listener).JFrame.swing. public class DerivedClassExample extends JFrame { public DerivedClassExample() { 253 .event.event.ActionEvent. } } The button listener is defined here. } } public static void main(String[] args) { SwingUtilities. import java.ActionListener.awt. import import import import javax. ButtonCloseListener listener = new ButtonCloseListener(). closeButton.swing. Here we have a non anonymous inner class.} class ButtonCloseListener implements ActionListener { public void actionPerformed(ActionEvent e) { System. ex. the event handling is managed within the MyButton class. 254 . addActionListener(this). setLocationRelativeTo(null).add(closeButton). It implements the ActionListener interface. 80.setText(text). class MyButton extends JButton implements ActionListener { The MyButton class is extended from the JButton class.setVisible(true). setSize(300. MyButton closeButton = new MyButton("Close"). closeButton. Here we add the action listener to the MyButton class. This way. 200). setTitle("Derived class"). Here we create the MyButton custom class. In this example.} initUI(). which will implement the action listener. } } } }). MyButton closeButton = new MyButton("Close").setLayout(null). 25). } public void actionPerformed(ActionEvent e) { System. addActionListener(this). } } public static void main(String[] args) { SwingUtilities. panel.invokeLater(new Runnable() { public void run() { DerivedClassExample ex = new DerivedClassExample(). add(panel).setBounds(40. public final void initUI() { JPanel panel = new JPanel().exit(0). setDefaultCloseOperation(EXIT_ON_CLOSE). we create a MyButton class. 50. } class MyButton extends JButton implements ActionListener { public MyButton(String text) { super. panel. swing.awt. 80.swing. javax. add(panel).setBounds(40.swing. find.swing. 80. statusbar = new JLabel(" ZetCode").JLabel. javax. add(statusbar. 180. 30. find. 25). statusbar. import java. panel.swing. javax.Multiple sources A listener can be plugged into several sources. javax. panel.SOUTH). JButton open = new JButton("Open").add(find).setBounds(40.event. close.awt.swing. javax. import java. 25).awt.JFrame.addActionListener(new ButtonListener()). 25). 80. panel. import java. panel.setBounds(40.add(close). open.addActionListener(new ButtonListener()).setBounds(40.setBorder(BorderFactory. public class MultipleSources extends JFrame { JLabel statusbar.createEtchedBorder( EtchedBorder.RAISED)). close. panel.add(save).border. 25).ActionListener. package zetcode. 80.addActionListener(new ButtonListener()).ActionEvent. JButton find = new JButton("Find"). import import import import import import import javax.swing. public MultipleSources() { } initUI(). JButton save = new JButton("Save"). javax. 255 . save.add(open).addActionListener(new ButtonListener()).SwingUtilities. public final void initUI() { JPanel panel = new JPanel().BorderFactory.setLayout(null). JButton close = new JButton("Close"). 80.JPanel.event.BorderLayout. This will be explained in the next example. BorderLayout. 130. open. save.JButton.EtchedBorder. getText().setText(" " + label + " button clicked") We update the statusbar. 256 . which button was pressed.setVisible(true).setText(" " + label + " button clicked"). open. setDefaultCloseOperation(EXIT_ON_CLOSE). String label = o. statusbar.} setTitle("Multiple Sources"). statusbar. Each button will be registered against a ButtonListener class. 300)..addActionListener(new ButtonListener()). . } } } }). close. JButton o = (JButton) e.. . Here we determine. setSize(400.addActionListener(new ButtonListener()).getText().invokeLater(new Runnable() { public void run() { MultipleSources ms = new MultipleSources()..getSource(). We create four buttons and a statusbar.. } } public static void main(String[] args) { SwingUtilities. The statusbar will display an informative message upon clicking on the button. String label = o. class ButtonListener implements ActionListener { public void actionPerformed(ActionEvent e) { JButton o = (JButton) e.getSource(). setLocationRelativeTo(null). ms. javax.JLabel.Figure: Multiple Sources Multiple listeners We can register several listeners for one event. import import import import import import import import import import javax. javax.event.swing.swing.ActionEvent.SwingUtilities.SpinnerModel.swing. } public final void initUI() { JPanel panel = new JPanel().swing.swing.util.JFrame. import java. javax.swing.swing.SpinnerNumberModel. public class MultipleListeners extends JFrame { private JLabel statusbar.swing.swing. javax.awt. javax. package zetcode.awt.BorderLayout.swing. private static int count = 0. 257 .JButton.JPanel.EtchedBorder. import java.border. private JSpinner spinner.JSpinner.awt.BorderFactory. javax. javax.event.ActionListener. import java. public MultipleListeners() { initUI().Calendar. import java. javax. javax. panel. currentYear + 100.setBorder(BorderFactory. 30. } class ButtonListener1 implements ActionListener { public void actionPerformed(ActionEvent e) { Integer val = (Integer) spinner.SOUTH). JButton add = new JButton("+"). 25). setLocationRelativeTo(null). add. 258 .toString(++count)). setDefaultCloseOperation(EXIT_ON_CLOSE). } } class ButtonListener2 implements ActionListener { public void actionPerformed(ActionEvent e) { statusbar. int currentYear = calendar. 25). panel. currentYear .setBounds(190.add(add). } }).setLayout(null).NumberEditor(spinner.get(Calendar.100. spinner.statusbar = new JLabel("0"). 30. add(panel).YEAR).setValue(++val). add. setTitle("Multiple Listeners"). setSize(300.setVisible(true).getInstance().addActionListener(new ButtonListener2()). add(statusbar.setBounds(40. Calendar calendar = Calendar. 80.addActionListener(new ButtonListener1()). BorderLayout.setText(Integer. statusbar.add(spinner).getValue(). 80.RAISED)). 200). 1).invokeLater(new Runnable() { public void run() { MultipleListeners ml = new MultipleListeners(). SpinnerModel yearModel = new SpinnerNumberModel(currentYear. ml. spinner. add. } } public static void main(String[] args) { SwingUtilities.createEtchedBorder( EtchedBorder. panel.setEditor(new JSpinner. spinner. "#")). spinner = new JSpinner(yearModel). ItemListener.event.awt. We use a year model for the spinner.NumberEditor(spinner. how many times we have clicked on the button.awt. The SpinnerNumberModel arguments are initial value.100.addActionListener(new ButtonListener1()). currentYear . min. Here we increase the year number.event. Integer val = (Integer) spinner.awt. We remove the thousands separator. spinner = new JSpinner(yearModel).getValue(). Here we create the spinner component. We use two button listeners for one event. add.ItemEvent. java.awt.ActionListener.setEditor(new JSpinner.setValue(++val). java. java. "#")). SpinnerModel yearModel = new SpinnerNumberModel(currentYear. we have a button.event. spinner. package zetcode. spinner. Figure: Multiple Listeners Removing listeners The Java Swing toolkit enables us to remove the registered listeners. max values and the step. We register two button listeners. currentYear + 100.addActionListener(new ButtonListener2()). spinner and a statusbar. One click of a button will add one year to the spinner component and update the statusbar. import import import import java. The statusbar will show. 259 . add.event.} } In this example.ActionEvent. 1). } class ButtonListener implements ActionListener { public void actionPerformed(ActionEvent e) { text.add(text).isSelected()) { add.JCheckBox. text = new JLabel("0"). active = new JCheckBox("Active listener"). setTitle("Remove listener").swing.add(add). javax.setBounds(160. 200). JCheckBox active. JButton add. setSize(310. setLocationRelativeTo(null). } else { add.JFrame. 25).toString(++count)).swing. 80.setBounds(40.setText(Integer. setDefaultCloseOperation(EXIT_ON_CLOSE). 30.import import import import import import javax. 80. panel.setBounds(40. } 260 . 25).addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent event) { if (active. add(panel). panel.SwingUtilities. javax. javax. add.swing. 30.JButton.add(active). buttonlistener = new ButtonListener().removeActionListener(buttonlistener). static int count = 0. active. 140. 80. panel.swing.setLayout(null).JLabel.addActionListener(buttonlistener).swing. active. javax. javax. public class RemoveListenerExample extends JFrame { private private private private private JLabel text. 25). panel. public RemoveListenerExample() { JPanel panel = new JPanel().JPanel. text. ButtonListener buttonlistener.swing. } } }). add = new JButton("+"). By toggling the check box. } We determine. A button. Figure: Remove listener Moving a window The following example will look for a position of a window on the screen.JFrame.ComponentEvent.JLabel. check box and a label. import java. buttonlistener = new ButtonListener().event. Then we add or remove the listener. ex. if we want to later remove it.invokeLater(new Runnable() { public void run() { RemoveListenerExample ex = new RemoveListenerExample(). import javax.swing. import java. import javax.event. we add or remove the listener for a button.} public static void main(String[] args) { SwingUtilities. if (active. 261 . } } } }). package zetcode. import java. We have to create a non anonymous listener. We need a reference to it. We have three components on the panel.} else { add.setVisible(true). whether the check box is selected.addActionListener(buttonlistener).swing.isSelected()) { add.ComponentListener.Font.removeActionListener(buttonlistener).awt.awt.awt. public MovingWindowExample() { } initUI().setBounds(20. import javax. public final void initUI() { JPanel panel = new JPanel().add(labelx).BOLD. setTitle("Moving window"). 14)).import javax. private JLabel labely. labely. add(panel). labelx. } public void componentShown(ComponentEvent e) { } public void componentHidden(ComponentEvent e) { } public static void main(String[] args) { SwingUtilities.getX(). Font. 200).setFont(new Font("Serif".setLayout(null). labely = new JLabel("y: ").getY(). labelx = new JLabel("x: ").invokeLater(new Runnable() { 262 . Font. } public void componentResized(ComponentEvent e) { } public void componentMoved(ComponentEvent e) { int x = e.BOLD. panel. labely. setDefaultCloseOperation(EXIT_ON_CLOSE).swing. 20. int y = e.JPanel.setText("x: " + x).swing. addComponentListener(this). 25).SwingUtilities. labelx. setLocationRelativeTo(null).add(labely).setText("y: " + y). 45. 14)). panel. 60. panel. 60.getComponent().setFont(new Font("Serif". 25). labely.setBounds(20.getComponent(). public class MovingWindowExample extends JFrame implements ComponentListener { private JLabel labelx. labelx. setSize(310. we had to implement all four methods of a ComponentListener class.awt.getComponent(). that we have to implement all four methods. int x = e. There is no adapter for a button click event.getComponent(). the default one is a bit small. int y = e. } The example shows the current window coordinates on the panel. Font. 14)). where we have more than one method to implement. Adapter is a class that implements all necessary methods. Here we get the x and the y positions. we can use adapters. We can use adapters in situations.setFont(new Font("Serif". labelx. Because there we have only one method to implement. We make the font bigger.}). Notice. They are empty. 263 .Font. We then use only those methods.getY(). import java. that are available in the ComponentListener. if we do not use them. package zetcode.BOLD. To avoid unnecessary coding. ex.ComponentAdapter. using a ComponentAdapter. that we actually need.awt.getX(). The actionPerformed() method. we use the ComponentListener.setVisible(true). Even.event. The following example is a rewrite of the previous one. import java. In the previous code example. Even if we did not use them. To get the window position. } } public void run() { MovingWindowExample ex = new MovingWindowExample(). Figure: Moving a window Adapters Adapters are convenient classes. labely.setLayout(null). labely. import import import import javax.JFrame. 45.add(labelx).invokeLater(new Runnable() { public void run() { AdapterExample ex = new AdapterExample(). public final void initUI() { JPanel panel = new JPanel().awt. int y = e. labelx.JPanel. 60. javax. setTitle("Adapter example"). javax. add(panel). 14)).setFont(new Font("Serif". labely. setLocationRelativeTo(null).setBounds(20.setFont(new Font("Serif". private JLabel labely. } } public static void main(String[] args) { SwingUtilities. Font. 20. panel. javax. labelx.setText("y: " + y). labelx = new JLabel("x: ").BOLD.setText("x: " + x). addComponentListener(new MoveAdapter()). ex. labelx. setSize(310. Font.setBounds(20.ComponentEvent. labely = new JLabel("y: ").setVisible(true).swing. 25).swing.swing. setDefaultCloseOperation(EXIT_ON_CLOSE). } class MoveAdapter extends ComponentAdapter { @Override public void componentMoved(ComponentEvent e) { int x = e. 200).JLabel.swing.getX(). 60. panel. 25). public class AdapterExample extends JFrame { private JLabel labelx. 14)).BOLD.import java.event.getComponent(). public AdapterExample() { } initUI().getY().SwingUtilities. panel.getComponent().add(labely). 264 . Custom dialogs and standard dialogs. modify data. Modal dialogs block input to other top level windows.getY(). Custom dialogs are dialogs. Here we register the component listener. While choosing a file to open. addComponentListener(new MoveAdapter()). 265 . no other operation should be permitted. For example JColorChooser or JFileChooser. } } Inside the MoveAdapter inner class. loading and saving files etc. A dialog is used to input data. A dialog is defined as a conversation between two or more persons. we can create two kinds of dialogs. we define the componentMoved() method. These are dialogs for common programming tasks like showing text. receiving input . class MoveAdapter extends ComponentAdapter { @Override public void componentMoved(ComponentEvent e) { int x = e. Modeless dialogs allow input to other windows. int y = e. Modal and modeless. In a computer application a dialog is a window which is used to "talk" to the application.getComponent(). change the application settings etc. created by the programmer. Here we use the ComponentAdapter.setText("y: " + y). They save programmer's time and enhance using some standard behaviour. It is handy to have the ability to move the cursor in the text control and define.getComponent(). This part of the Java Swing tutorial was dedicated to Swing events. An open file dialog is a good example of a modal dialog.setText("x: " + x). They are based on the JDialog class.getX(). There are two basic types of dialogs. Standard dialogs preedefined dialogs available in the Swing toolkit. depends on the circumstances. What type of dialog to use. In Java Swing toolkit. labelx. A typical modeless dialog is a find text dialog. labely. } } } This example is a rewrite of the previous one. All the other methods are left empty. Java Swing dialogs Dialog windows or dialogs are an indispensable part of most modern GUI applications.}). where to start the finding of the particular text. Dialogs are important means of communication between a user and a computer program. awt. javax. add(Box.KeyEvent. JButton close = new JButton("Close"). add(label). add(Box.Dimension.JDialog. JLabel label = new JLabel(icon).A simple custom dialog In the following example we create a simple custom dialog. name. close.JMenuBar. ImageIcon icon = new ImageIcon("notes.JFrame. package zetcode. class AboutDialog extends JDialog { public AboutDialog() { } initUI(). 10))).awt. java.JMenu. 266 . public final void initUI() { setLayout(new BoxLayout(getContentPane(). javax.swing. JLabel name = new JLabel("Notes. It is a sample about dialog. add(Box.swing. BoxLayout. 1. Font.BoxLayout. javax. label.swing. javax.createRigidArea(new Dimension(0. java. 10))). java. javax.awt.swing.ActionListener.BOLD. javax. 13)).event. javax.swing.ImageIcon.swing.swing. javax. import import import import import import import import import import import import import import import import java.JLabel.event.awt.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { dispose().setAlignmentX(0.Box. usually located in the help menu. java.createRigidArea(new Dimension(0.png").Y_AXIS)). add(name).createRigidArea(new Dimension(0. javax.swing.5f). } }). 50))). javax.swing. found in most GUI applications.swing.Font. name.swing. javax.JButton.ActionEvent.5f).setFont(new Font("Serif".SwingUtilities.awt.setAlignmentX(0.JMenuItem.23").event. menubar. } }). add(close).APPLICATION_MODAL).setMnemonic(KeyEvent. JMenuItem about = new JMenuItem("About").5f). setJMenuBar(menubar).VK_H).setVisible(true). about. file. 267 . setSize(300.setMnemonic(KeyEvent. } } public class SimpleDialog extends JFrame { public SimpleDialog() { } initUI(). ad. setLocationRelativeTo(null).add(file).addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { AboutDialog ad = new AboutDialog(). help. } } } }). menubar. setDefaultCloseOperation(EXIT_ON_CLOSE). JMenu help = new JMenu("Help"). JMenu file = new JMenu("File"). 200). 200).VK_F).close. setLocationRelativeTo(null). help.add(help). setTitle("About Notes"). setModalityType(ModalityType.add(about).invokeLater(new Runnable() { public void run() { SimpleDialog sd = new SimpleDialog(). setSize(300.setVisible(true). public final void initUI() { JMenuBar menubar = new JMenuBar(). } public static void main(String[] args) { SwingUtilities. sd. setDefaultCloseOperation(DISPOSE_ON_CLOSE). setTitle("Simple Dialog").setAlignmentX(0. setModalityType(ModalityType. import import import import import javax.JFrame.swing. import java. import java. javax.ActionListener. Here we make the dialog modal. javax. Here we display the about dialog.event. javax.setVisible(true).SwingUtilities.JOptionPane.The sample code will popup a small dialog box. The dialog will display an icon a text and one close button. public class MessageBoxes extends JFrame { private JPanel panel.swing.awt.swing. javax.JButton. package zetcode.APPLICATION_MODAL).GridLayout.event. Here we set the defaul close operation. setDefaultCloseOperation(DISPOSE_ON_CLOSE).awt.swing. import java. AboutDialog ad = new AboutDialog().swing. from the menu of the main frame.ActionEvent. class AboutDialog extends JDialog { The custom dialog is based on the JDialog class.awt. public MessageBoxes() { 268 . ad.JPanel. Figure: Simple custom dialog Message boxes Message boxes provide information to the user. panel. "Error".WARNING_MESSAGE). } public static void main(String[] args) { 269 .add(warning). "Question". "Warning". }). information. warning = new JButton("Warning"). add(panel).addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { JOptionPane.showMessageDialog(panel.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { JOptionPane. setLocationRelativeTo(null). JOptionPane.showMessageDialog(panel. JButton JButton JButton JButton error = new JButton("Error").ERROR_MESSAGE). JOptionPane. panel.QUESTION_MESSAGE). JOptionPane. setTitle("Message Boxes"). panel. question.showMessageDialog(panel. "A deprecated call". information = new JButton("Information").add(question). JOptionPane. 200).setLayout(new GridLayout(2. setDefaultCloseOperation(EXIT_ON_CLOSE). panel. "Could not open file". "Download completed".showMessageDialog(panel. } }). warning. } "Question".initUI(). panel. } }). } public final void initUI() { panel = new JPanel(). error. setSize(300.INFORMATION_MESSAGE).addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { JOptionPane. 2)). question = new JButton("Question").add(information).addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { JOptionPane. } }).add(error). "Are you sure to quit?". title and a message type. The message type is determined by the constant we choose.SwingUtilities. Here are the four buttons.ActionEvent. import java.event. We use a GridLayout layout manager to organize buttons. "Error". } }). package zetcode.setLayout(new GridLayout(2. JOptionPane. question = new JButton("Question"). warning and information message boxes.showMessageDialog(panel. import java. message text. 2)). import java. panel.ActionListener. that will popup message boxes. To create a message box. warning = new JButton("Warning"). 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. question.awt.event.ERROR_MESSAGE).setVisible(true). "Could not open file". we call the showMessageDialog static method of the JOptionPane class.BorderLayout.awt.awt. } } The example shows an error. 270 . JButton JButton JButton JButton error = new JButton("Error").invokeLater(new Runnable() { public void run() { MessageBoxes mb = new MessageBoxes(). We provide the component name. that we will use. JOptionPane. information = new JButton("Information"). mb. area = new JTextArea(). 271 .swing.swing.JPanel. javax.import import import import import import import import import import import import import import import import java.setBorder(BorderFactory.swing. 10.swing.BufferedReader.io. javax.swing.swing.showDialog(panel. int ret = fileopen. javax.JTextArea.createEmptyBorder(10. String text = readFile(file).setLayout(new BorderLayout()). public final void initUI() { panel = new JPanel(). java. 10)).APPROVE_OPTION) { File file = fileopen. area. private JTextArea area.filechooser. area.JFrame. } }).swing.filechooser.swing. if (ret == JFileChooser. javax. javax. javax.JScrollPane.IOException.swing.ImageIcon. JButton openb = new JButton(open). FileFilter filter = new FileNameExtensionFilter("c files". public FileChooserDialog() { } initUI(). fileopen. panel. javax. openb. javax. toolbar.BorderFactory.File.io.JFileChooser.io.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { JFileChooser fileopen = new JFileChooser(). ImageIcon open = new ImageIcon("open.getSelectedFile(). javax.addChoosableFileFilter(filter).FileReader. javax. "Open file").FileFilter. javax. } "c").FileNameExtensionFilter. 10.JToolBar.SwingUtilities.io. javax.swing.add(openb). java.setText(text).swing.swing.JButton. public class FileChooserDialog extends JFrame { private JPanel panel.png"). JToolBar toolbar = new JToolBar(). java. pane.separator")).append( System. setLocationRelativeTo(null).add(area).JScrollPane pane = new JScrollPane().append(line). } in. fileString = fileBuffer.createEmptyBorder(10.setBorder(BorderFactory.NORTH). This is the constructor of the file chooser dialog. try { FileReader in = new FileReader(file).close(). setSize(400. panel. BorderLayout. } public static void main(String[] args) { SwingUtilities. setDefaultCloseOperation(EXIT_ON_CLOSE).setVisible(true).add(pane). 300). fileBuffer = new StringBuffer().getViewport(). } return fileString. } } } }).toString(). while ((line = brd. String fileString = null. 10. 10)).invokeLater(new Runnable() { public void run() { FileChooserDialog fcd = new FileChooserDialog(). String line = null. 272 . setTitle("FileChooserDialog"). JFileChooser fileopen = new JFileChooser(). panel.readLine()) != null) { fileBuffer. add(toolbar. BufferedReader brd = new BufferedReader(in). add(panel). The code example will demonstrate how to use a file chooser dialog in order to load file contents into the text area component.getProperty("line. } public String readFile(File file) { StringBuffer fileBuffer = null. } catch (IOException e) { return null. fcd. 10. event.awt.c.BorderLayout. Here we define the file filter.awt.APPROVE_OPTION. java. Figure: JFileChooser dialog JColorChooser JColorChooser is a standard dialog for selecting a color. "c"). Here we show the file chooser dialog. fileopen.event. String text = readFile(file).ActionListener.FileFilter filter = new FileNameExtensionFilter("c files". area. In our case. import import import import java.awt. package zetcode.showDialog(panel.getSelectedFile().addChoosableFileFilter(filter). 273 . we will have c files with extension .awt. java. } Here we get the name of the selected file. "Open file"). int ret = fileopen. java.ActionEvent. if (ret == JFileChooser.setText(text). We have also the default All files option. We read the contents of the file and set the text into the textarea. the return value is equal to JFileChooser.Color.APPROVE_OPTION) { File file = fileopen. Upon clicking on the open file button. public ColorChooserDialog() { initUI().JColorChooser. JToolBar toolbar = new JToolBar(). setSize(400.SwingUtilities.swing. } }). javax. setLocationRelativeTo(null).setBackground(color). } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { 274 .JFrame.JToolBar. add(panel). openb. 50. BorderLayout. panel.add(openb). javax. display = new JPanel().swing. 50)).JButton. private JPanel display. Color color = clr.setLayout(new BorderLayout()). setDefaultCloseOperation(EXIT_ON_CLOSE).add(display). javax.createEmptyBorder(30. JButton openb = new JButton(open).png").swing. javax. javax.white). Color.swing. javax.ImageIcon. public class ColorChooserDialog extends JFrame { private JPanel panel.swing. display.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { JColorChooser clr = new JColorChooser(). panel. 30.import import import import import import import import javax.swing. 300). toolbar.swing.BorderFactory.NORTH). panel. add(toolbar.swing. javax.JPanel. "Choose Color".WHITE).showDialog(panel. setTitle("ColorChooserDialog"). ImageIcon open = new ImageIcon("color. } public final void initUI() { panel = new JPanel().setBackground(Color. display.setBorder(BorderFactory. check boxes. Buttons. Everything a programmer needs for his job. we have a white panel. 275 . sliders. } } } In the example. list boxes etc.ColorChooserDialog ccd = new ColorChooserDialog(). Basic Swing components Swing components are basic building blocks of an application. We change the display panel background to the newly selected color. We will change the background color of the panel by selecting a color from the color chooser dialog.setBackground(color). Color. In this section of the tutorial.setVisible(true).white). This code shows a color chooser dialog. ccd. The showDialog() method returns the selected color value.showDialog(panel. we have covered dialogs. we will describe several useful components. display. JColorChooser clr = new JColorChooser(). Swing toolkit has a wide range of various widgets. }). "Choose Color". Color color = clr. Figure: JColorChooser dialog In this part of the Java Swing tutorial. java.Toolkit. package com. } private void initUI() { setTitle("No Sleep").awt.swing. javax.BorderFactory. 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. java.JPanel.Dimension.swing.swing.JLabel Component JLabel is a simple component for displaying text.awt. public LabelExample() { initUI(). images or both.JFrame. java.zetcode.BorderLayout.swing. java.SwingUtilities.JLabel.Font. import import import import import import import import import import java.awt.swing. 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 . javax.awt. public class LabelExample extends JFrame { private Toolkit toolkit.Color. javax. javax. It does not react to input events.awt. javax. setFont(new Font("Georgia".createEmptyBorder(10. add(panel). 10.setVisible(true). We put the label into the center of the panel.PLAIN. } } In our example. setDefaultCloseOperation(EXIT_ON_CLOSE).setForeground(new Color(50.height .setBorder(BorderFactory. We put 10px around the label. We set it's font to plain georgia. } public static void main(String[] args) { SwingUtilities. 277 .PLAIN.getWidth())/2. panel. BorderLayout. JLabel label = new JLabel(lyrics).add(label. pack(). JPanel panel = new JPanel(). panel. 25)).width . 10)). (screensize.setFont(new Font("Georgia". 50. label. panel. 14)). panel. panel.add(label. We create a panel and set a BorderLayout manager. JLabel label = new JLabel(lyrics). 10)). 10)).getScreenSize()."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.invokeLater(new Runnable() { @Override public void run() { LabelExample ex = new LabelExample(). ex.CENTER). 50. JPanel panel = new JPanel().setLayout(new BorderLayout(10. We use the <br> tag to separate lines. 10. We also change the foreground color. 14)).createEmptyBorder(10. 10. label. toolkit = getToolkit(). setLocation((screensize.CENTER). world<br>" + "Sleep in peace</html>". Here we create the label component. we show lyrics of no sleep song from cardigans.setBorder(BorderFactory. panel. 10. } }).setLayout(new BorderLayout(10. Font. label. 25)). 10)). BorderLayout. Dimension screensize = toolkit. We can use html tags in JLabel component.setForeground(new Color(50. label. 14 px tall.getHeight())/2). Font. awt. it is represented by a tick in a box. import java. On and Off. The panel is added to the frame component.ActionEvent.add(panel). pack(). so that all components are visible. toggle visibility of a toolbar etc.event. We call the pack() method.ActionListener. Figure: JLabel JCheckBox JCheckBox is a component that has two states. package com. which will resize the window. import java. If the check box is checked.awt. It is a box with a label. A check box can be used to show/hide splashscreen at startup.Dimension. 278 .zetcode. import java.event.awt. true).swing.import import import import import javax. 279 . if (state) { this. setDefaultCloseOperation(JFrame. checkbox. } public final void initUI() { setLayout(new BoxLayout(getContentPane().SwingUtilities. ex.JCheckBox.addActionListener(this). BoxLayout.isSelected(). } else { this.EXIT_ON_CLOSE).createRigidArea(new Dimension(15. add(checkbox). } }).setVisible(true). 200).setFocusable(false). setTitle("JCheckBox example"). add(Box. javax. boolean state = source.JFrame.Y_AXIS)).invokeLater(new Runnable() { @Override public void run() { CheckBoxExample ex = new CheckBoxExample(). javax. } @Override public void actionPerformed(ActionEvent e) { JCheckBox source = (JCheckBox) e. javax. BoxLayout.swing.swing. } } Our code example shows or hides the title of the window depending on its state. 20))).swing. javax.setTitle("JCheckbox example"). setLocationRelativeTo(null). JCheckBox checkbox = new JCheckBox("Show Title".getSource(). public class CheckBoxExample extends JFrame implements ActionListener { public CheckBoxExample() { initUI().Y_AXIS)). checkbox.swing.BoxLayout. setLayout(new BoxLayout(getContentPane(). setSize(280.Box.setTitle(""). } } public static void main(String[] args) { SwingUtilities. swing.zetcode.swing. Our example will show a volume control. we use a BoxLayout layout manager.BorderFactory.awt. We provide text and state. import java. JCheckBox checkbox = new JCheckBox("Show Title". we show or hide the title of the window.createRigidArea(new Dimension(15. we get the source component. package com. javax. } else { this.swing. import java. } From the event object.Box.getSource(). javax. true). import import import import javax.add(Box.setTitle(""). Figure: JCheckBox JSlider JSlider is a component that lets the user graphically select a value by sliding a knob within a bounded interval. if (state) { this. boolean state = source.ImageIcon.isSelected(). JCheckBox source = (JCheckBox) e. Depending on the state of the check box. 20))).setFocusable(false).BoxLayout. checkbox. javax. We find out the selection state of the check box.swing. A JCheckBox that has a focus may be selected or unselected with a spacebar. We put some space there. Here we have a constructor for the checkbox. so that the checkbox is not too close to the corner. In this example.Dimension.BorderLayout. 280 .setTitle("JCheckbox example"). In our case is the a check box. We have disabled the focus for the checkbox.awt. slider = new JSlider(0.png")). } private void loadImages() { mute = new ImageIcon(getClass(). max. 150. if (value == 0) { label.setLayout(new BoxLayout(panel. setLayout(new BorderLayout()). initUI(). 30)).setPreferredSize(new Dimension(150. panel.event.swing.swing.createEmptyBorder(40. javax. 40. slider. javax. slider.setIcon(min).setIcon(max). javax.JLabel.add(Box.import import import import import import import javax.getResource("min.swing.ChangeEvent. BoxLayout.swing.png")).swing.JFrame. min = new ImageIcon(getClass(). mute = new ImageIcon(getClass(). 40.JSlider. } public final void initUI() { JPanel panel = new JPanel().JPanel.getResource("mute. javax. private private private private ImageIcon ImageIcon ImageIcon ImageIcon mute. min. panel. med. 40)).png")). } else if (value > 30 && value < 80) { label.createHorizontalGlue()). } } }). public SliderExample() { loadImages().setIcon(med).png")).setBorder(BorderFactory.ChangeListener. med = new ImageIcon(getClass(). javax. 281 .swing.getResource("max.png")).SwingUtilities. panel.getValue(). } else if (value > 0 && value <= 30) { label. max = new ImageIcon(getClass().X_AXIS)). public class SliderExample extends JFrame { private JSlider slider.getResource("med.setIcon(mute). } else { label.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent event) { int value = slider. private JLabel label.swing.event.getResource("mute. 0). javax. add(Box.createEmptyBorder(40. We creare a 40px border around the panel. 40.getResource("mute. 0).getResource("max. panel. label = new JLabel(mute. we load the image files from the disk. 40)).setBorder(BorderFactory.createHorizontalGlue()). med = new ImageIcon(getClass().add(label).invokeLater(new Runnable() { @Override public void run() { SliderExample ex = new SliderExample(). By dragging the slider. 40. panel.png")).X_AXIS)).png")). panel. we show a JSlider and a JLabel. JLabel.png")). } In the loadImages() method.setVisible(true).CENTER). setLocationRelativeTo(null). setDefaultCloseOperation(EXIT_ON_CLOSE). It is to prevent JSlider from growing to unnatural sizes.createRigidArea(new Dimension(5. max = new ImageIcon(getClass(). we change the icon on the label component.panel.getResource("med.png")).setLayout(new BoxLayout(panel. slider = new JSlider(0. private void loadImages() { mute = new ImageIcon(getClass(). panel. pack().add(Box.add(Box. panel. setTitle("JSlider"). min = new ImageIcon(getClass(). Panel component has a horizontal BoxLayout.png")). } }).createHorizontalGlue()).getResource("min. We put resizable space to bo both sides. mute = new ImageIcon(getClass(). ex. } public static void main(String[] args) { SwingUtilities. } } In the code example.CENTER). BoxLayout. panel. 150. BorderLayout. 0))). 282 .add(slider). add(panel. left and right.getResource("mute. We place a 5px rigid space between the two components. java. maximum value and current value.swing. javax. java.swing.addChangeListener(new ChangeListener() { .swing. We add a ChangeListener to the slider.awt. Figure: JSlider JComboBox Combobox is a component that combines a button or editable field and a drop-down list.add(Box.ActionEvent..event.swing.swing. javax. java. slider. "John Galsworthy". public class ComboBox extends JDialog implements ActionListener. 283 .This is a JSlider constructor. javax.swing. panel.JComboBox. "Stefan Zweig". javax. then the combo box includes an editable field into which the user can type a value. ItemListener { final String[] authors = { "Leo Tolstoy". we determine the current slider value and update the label accordingly.swing. import import import import import import import import import import import import import import java. javax.ActionListener. The parameters are minimum value.awt. javax. }).ItemListener.event.swing.awt..JButton. when the slider is at the end position.awt. java.event. javax.Box. which appears at the user's request.JDialog.awt. "Honore de Balzac". java.ItemEvent.JLabel.LineBorder. They are too close to each other.createRigidArea(new Dimension(5.Dimension. If you make the combo box editable.event. 0))). Inside the listener.border.ImageIcon. javax. The user can select a value from the drop-down list.Component.awt.BoxLayout. add(button). display = new JLabel(). 22)). add(Box.addItemListener(this).createGrayLineBorder()).setMaximumSize(new Dimension(100.CENTER_ALIGNMENT). BoxLayout.setBorder(LineBorder.createRigidArea(new Dimension(0. private JComboBox combobox = null. "zweig.setPreferredSize(new Dimension(100.exit(0). combobox.jpg". 127)). combobox = new JComboBox(authors).jpg". setSize(300.jpg". combobox.jpg". "Tom Wolfe" final String[] images = { "tolstoy.jpg")). private JButton button = null. } public void actionPerformed(ActionEvent e) { System.DISPOSE_ON_CLOSE). setTitle("JComboBox"). combobox. button. display. button. 22)). add(display).jpg" }.setPreferredSize(new Dimension(140. 15))).setMaximumSize(new Dimension(140.setSelectedIndex(-1). setDefaultCloseOperation(JDialog.getSystemResource("balzac. "pasternak. button = new JButton("Close"). 35))).setAlignmentX(Component. private JLabel display = null. combobox. setLocationRelativeTo(null). "Boris Pasternak". display. "wolfe. "galsworthy. add(Box.setAlignmentX(Component. 127)). display.jpg".CENTER_ALIGNMENT). "balzac. } public void itemStateChanged(ItemEvent e) { 284 .createRigidArea(new Dimension(0. display. 15))). 300).Y_AXIS)).createRigidArea(new Dimension(0. add(combobox). } public static void main(String[] args) { new ComboBox(). add(Box. public ComboBox() { setLayout(new BoxLayout(getContentPane().}. ImageIcon icon = new ImageIcon( ClassLoader. setVisible(true).addActionListener(this). we have three components. public class ComboBox extends JDialog implements ActionListener. a combobox and a button. A label.getSelectedIndex(). The button closes the window. The constructor of the JComboBox takes a string array of novelists.if (e.setIcon(new ImageIcon( ClassLoader. If we select a name. no item to be selected. If we provide -1 as an argument in the setSelectedIndex() method. combobox. display. combobox = new JComboBox(authors). an image is displayed in the label.addItemListener(this).setSelectedIndex(-1). combobox. ItemListener { This is a dialog based application example.SELECTED) { JComboBox combo = (JComboBox) e.getStateChange() == ItemEvent.getSource(). } } } In our example. int index = combo. We add an ItemListener to our combobox. The display area is a simple JLabel. 285 . We have six names of famous novelists in our combobox. 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.getSystemResource(images[index]))). display = new JLabel(). In the event handler. public ProgressBarExample() { } initUI().swing. when we process lengthy tasks.zetcode.swing.BoxLayout. javax.swing. It is animated so that the user knows.event.swing. and the maximum is 100.BorderFactory.awt. javax. javax. JButton button.swing.ActionListener.JButton.swing. javax.swing. The initial and minimum values are 0. javax.awt. The JProgressBar widget provides a horizontal or vertical progress bar.awt.JFrame.swing. javax.JProgressBar.swing.Timer. that our task is progressing. JProgressBar progressBar.Dimension.JPanel. javax.Figure: JComboBox JProgressBar A progress bar is a widget that is used. 286 . public class ProgressBarExample extends JFrame { private private private private ActionListener updateProBar.ActionEvent. import java. import import import import import import import import import javax. import java.Box. javax. Timer timer. package com.SwingUtilities. import java.event. panel. add(panel). if (val >= 100) { timer. progressBar.isRunning()) { timer.setText("End").start(). panel.setBorder(BorderFactory. button. } timer = new Timer(50. button. progressBar = new JProgressBar(). 20)). panel. }.setLayout(new BoxLayout(panel. } progressBar. button = new JButton("Start").equals(button.stop().setMinimumSize(new Dimension(150. panel. progressBar.setAlignmentX(0f).private void initUI() { setTitle("JProgressBar"). progressBar. 20)). button.setMaximumSize(new Dimension(150.add(Box.setValue(++val). 40)). button.createEmptyBorder(40.setFocusable(false).setText("Start").createRigidArea(new Dimension(0.setPreferredSize(new Dimension(150. panel. } else if (!"End".setMaximumSize(button. progressBar. button.Y_AXIS)). 20)). pack(). JPanel panel = new JPanel(). 20))). 40.setText("Stop"). 40.getPreferredSize()). updateProBar = new ActionListener() { @Override public void actionPerformed(ActionEvent actionEvent) { int val = progressBar.getValue(). BoxLayout. return. setResizable(false). setDefaultCloseOperation(EXIT_ON_CLOSE).add(progressBar).addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if (timer.add(button). button. updateProBar).getText())) { timer. 287 .stop(). } } }). panel. better disable it. progressBar. The button starts and stops the progress. } public static void main(String[] args) { SwingUtilities.setText("Stop").stop(). The minimum value is 0. ex. or update the progress bar. button. progressBar = new JProgressBar().isRunning()) { timer. Inside that listener. progressBar.setAlignmentX(0f). 20)).equals(button. } } The example displays a progress bar and a button. These lines are for design purposes only. The default height on my box was only 14px which looked bad.setLocationRelativeTo(null). Here we put some rigid space between the two components.setText("Start"). The timer object launches updateProBar listener every 50ms. } else if (!"End".invokeLater(new Runnable() { @Override public void run() { ProgressBarExample ex = new ProgressBarExample().setVisible(true). } 288 . To the left. This line aligns both progress bar with the button.getText())) { timer.setMinimumSize(new Dimension(150. button. progressBar. progressBar.add(Box. Focus looks bad. if (timer. 20)). maximum 100 and the initial value is 0.start().createRigidArea(new Dimension(0.setPreferredSize(new Dimension(150. timer = new Timer(50. } }). we check. updateProBar). setVisible(true). 20))). if the progress bar reached the value 100 and stop the timer. These are the default values. 20)).setFocusable(false). I want my examples to look nice.setMaximumSize(new Dimension(150. Here we create the JProgressBar. button. setLayout(new BoxLayout(bottom.event.Clicking on the button starts or stops the progress.Box. 20.JDialog. javax. bottom.Color.swing. private void initUI() { JPanel bottom = new JPanel(). javax.JToggleButton.swing. BoxLayout. javax. public class ToggleButtonExample extends JDialog implements ActionListener { private private private private JToggleButton redButton. Pressed and not pressed.SwingUtilities. JToggleButton package com.swing. javax.ActionEvent. Figure: JProgressBar JToggleButton is a button that has two states.BoxLayout.swing.zetcode. 20. There are situations where this functionality fits well. public ToggleButtonExample() { } initUI().awt.awt.X_AXIS)). java. 20)). JPanel display.swing.swing.setBorder(BorderFactory.awt. 289 .JPanel.border. Stop or End String values. java. javax.createEmptyBorder(20. import import import import import import import import import import import import java. java.swing.event. JToggleButton greenButton. javax. javax. You toggle between these two states by clicking on it. bottom.swing.awt. javax.BorderFactory. JToggleButton blueButton.Dimension.ActionListener. JPanel leftPanel = new JPanel(). It can have Start. The text of the button is updated dynamically.LineBorder. getGreen().equals("green")) { if (green == 0) { green = 255. leftPanel. redButton.DISPOSE_ON_CLOSE).leftPanel.add(Box.add(leftPanel). } else { red = 0.createRigidArea(new Dimension(25. greenButton.add(display).setBackground(Color.addActionListener(this). display. setResizable(false). blueButton.setPreferredSize(new Dimension(110. } } if (e.addActionListener(this). } else { 290 . setDefaultCloseOperation(JDialog. add(bottom).add(redButton).addActionListener(this).getActionCommand(). setTitle("JToggleButton").createRigidArea(new Dimension(25. setLocationRelativeTo(null).add(greenButton ). greenButton = new JToggleButton("green"). display = new JPanel(). leftPanel. bottom. bottom.black). leftPanel. } @Override public void actionPerformed(ActionEvent e) { Color color = display.getMaximumSize()). blueButton = new JToggleButton("blue"). leftPanel.setMaximumSize(greenButton. 7))).getBackground(). blueButton.getMaximumSize()).setMaximumSize(greenButton.add(blueButton). display.getRed(). int green = color.add(Box.getBlue().equals("red")) { if (red == 0) { red = 255. 7))). display. bottom. redButton.createRigidArea(new Dimension(20.getActionCommand().setLayout(new BoxLayout(leftPanel. 110)).add(Box.createGrayLineBorder()). pack(). int blue = color. int red = color. BoxLayout. 0))).Y_AXIS)).setBorder(LineBorder. redButton = new JToggleButton("red"). if (e. leftPanel. 291 . display. } else { blue = 0.getActionCommand(). The bottom panel is use to organize the left and display panels. if (e. We set the background color of the display panel to black. } }).setVisible(true). we determine the current red.equals("blue")) { if (blue == 0) { blue = 255. The toggle buttons will toggle the red. Color color = display.getBlue(). ex.getRed(). Panels are bottom panel.getActionCommand(). } public static void main(String[] args) { SwingUtilities. For this. } } Color setCol = new Color(red. if (e. int red = color.} } green = 0.setMaximumSize(greenButton. int blue = color.getMaximumSize()). The left panel will holt three toggle buttons. green. blue).addActionListener(this). The background color will depend on which togglebuttons we have pressed. We make all three buttons of equal size.getGreen().getMaximumSize()). This time we use vertical BoxLayout manager.equals("red")) { if (red == 0) { red = 255. blueButton. left panel and display panel. int green = color.invokeLater(new Runnable() { @Override public void run() { ToggleButtonExample ex = new ToggleButtonExample().setMaximumSize(greenButton. redButton = new JToggleButton("red").setBackground(setCol). blue parts of the display background color. green. we use horizontal BoxLayout manager. } } The example has three panels and three toggle buttons. Here we create a toggle button and set an action listener to it. redButton. redButton.getBackground(). green and blue parts of the color value. In the actionPerformed method. Swing toolkit has a wide range of various components. javax.swing. package com. javax.swing. 292 .swing. Basic Swing components II Swing components are basic building blocks of an application.JScrollPane.swing.swing. green.GraphicsEnvironment. Everything a programmer needs for his job.JFrame. import import import import import import import import import import import java.swing. Figure: JToggleButton In this part of the Java Swing tutorial.JLabel.setBackground(setCol).JPanel. which button was toggled. javax. JList Component JList is a component that displays a list of objects. } We determine. display. javax. check boxes. It allows the user to select one or more items.awt. we have covered basic Swing components.awt. blue). Buttons. list boxes etc. javax.SwingUtilities.BorderLayout. Here a new color is created and the display panel is updated to a new color. java. sliders.BorderFactory. In this section of the tutorial. Color setCol = new Color(red.Dimension.zetcode. we will describe several useful components.swing. java.} } else { red = 0. java. javax.Font. and update the color part of the RGB value accordingly.awt. javax.awt.JList. } public static void main(String[] args) { SwingUtilities.SOUTH).getSelectedValue(). 20)). public ListExample() { } initUI().invokeLater(new Runnable() { @Override public void run() { ListExample ex = new ListExample(). add(panel).add(list). import javax. 12). BorderLayout.setLayout(new BorderLayout()).import javax. 20. setDefaultCloseOperation(EXIT_ON_CLOSE). der Zorn Gottes").event.setBorder(BorderFactory.setFont(font). Font.getLocalGraphicsEnvironment().ListSelectionEvent. panel. pane.getAvailableFontFamilyNames(). String[] fonts = ge. 200)). list = new JList(fonts). GraphicsEnvironment ge = GraphicsEnvironment. add(label. public class ListExample extends JFrame { private JLabel label.ListSelectionListener. label = new JLabel("Aguirre. private JList list.getValueIsAdjusting()) { String name = (String) list. list. label.createEmptyBorder(20. panel.swing. 293 . pane. JScrollPane pane = new JScrollPane(). setTitle("JList"). private void initUI() { JPanel panel = new JPanel().event. Font.addListSelectionListener(new ListSelectionListener() { @Override public void valueChanged(ListSelectionEvent e) { if (!e. 20. label.swing. } } }).getViewport().setFont(new Font("Serif". setLocationRelativeTo(null). Font font = new Font(name. pack(). 12)). panel.PLAIN.PLAIN.add(pane).setPreferredSize(new Dimension(250. We get the selected item and set a new font for the label. We put the list into the JScrollPane to make it scrollable. JScrollPane pane = new JScrollPane(). the label will be displayed in a font. Font font = new Font(name. we have chosen.getAvailableFontFamilyNames(). 12). The list component contains a list of all available font family names on our system.getSelectedValue(). public void valueChanged(ListSelectionEvent e) { if (!e. If we select an item from the list. String name = (String) list. Here we obtain all possible font family names on our system. we will display a JList and JLabel components.PLAIN. To filter only the selecting events. In our example. JLabel 294 .} } } }).setFont(font). String[] fonts = ge.getViewport(). list = new JList(fonts). ex. Font. GraphicsEnvironment ge = GraphicsEnvironment. component is not scrollable by default.getLocalGraphicsEnvironment(). we use the getValueIsAdjusting() method. We create a JList component.getValueIsAdjusting()) { Events in list selection are grouped. We receive events for both selecting and deselecting. pane.setVisible(true). label.add(list). 8.setWrapStyleWord(true). It is lightweight component for working with text. import java.Figure: JList JTextArea component A JTextArea is a multi-line text area that displays plain text.JPanel. javax. import java. area.add(area). javax. JTextArea area = new JTextArea(). 295 . javax. javax.BorderFactory.swing.setBorder(BorderFactory. area. 8. The component does not handle scrolling. 20. public class TextAreaExample extends JFrame { public TextAreaExample() { } initUI().awt. panel. package com.JScrollPane. private void initUI() { JPanel panel = new JPanel().swing. 8)). javax.swing.createEmptyBorder(20.JFrame. area.swing.swing.swing.awt.SwingUtilities. panel.setLineWrap(true). For this task. we use JScrollPane component. 20.createEmptyBorder(8.BorderLayout. import import import import import import javax. JScrollPane pane = new JScrollPane(). pane.zetcode.getViewport().setLayout(new BorderLayout()).setBorder(BorderFactory. 20)).JTextArea.Dimension. lines will be wrapped at word boundaries.setWrapStyleWord(true).setVisible(true).setBorder(BorderFactory.setLineWrap(true). We put some border around the text in the component. 300)). setDefaultCloseOperation(EXIT_ON_CLOSE).createEmptyBorder(8. how is line going to be wrapped. add(panel). 296 . Here we specify.add(area). To make the text scrollable. area. area. JTextArea area = new JTextArea(). whitespaces.getViewport(). Make the lines wrapped. ex. This is the constructor of the JTextArea component. } } The example shows a simple JTextArea component. pane. setLocationRelativeTo(null). 8. } }). 8. setSize(new Dimension(350. setTitle("JTextArea").invokeLater(new Runnable() { @Override public void run() { TextAreaExample ex = new TextAreaExample(). we put the JTextArea component into the JScrollPane component. In our case. 8)). area.add(pane).panel. if they are too long to fit the width. } public static void main(String[] args) { SwingUtilities. util. javax.logging. private void initUI() { JPanel panel = new JPanel().swing. 20)). panel.setBorder(BorderFactory.logging.JPanel. It can display also HTML documents.swing. public TextPaneExample() { } initUI().swing. import import import import import import javax.swing.Figure: JTextAra JTextPane component component is a more advanced component for working with text.JFrame.zetcode. javax.SwingUtilities. 297 .util. import java.JTextPane.createEmptyBorder(20. public class TextPaneExample extends JFrame { JTextPane textPane. import java. import java.swing.awt.Level. The component can do some complex formatting operations over the text. JScrollPane pane = new JScrollPane().setLayout(new BorderLayout()). javax. javax.JScrollPane.BorderLayout. panel. 20. javax.Logger.BorderFactory.IOException. JTextPane package com.swing. import java. 20.io. 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. textPane. } } This is the HTML code.invokeLater(new Runnable() { @Override public void run() { TextPaneExample ex = new TextPaneExample().setBorder(BorderFactory. setTitle("JTextPane"). ex). null. ex. setDefaultCloseOperation(EXIT_ON_CLOSE). textPane. textPane.createEmptyBorder(8.setEditable(false).textPane = new JTextPane().class.getName()). pack(). } private void loadFile() { try { String cd = System.html"). 8.dir") + "/". 8)).getLogger(TextPaneExample.log(Level. } }). add(panel).setVisible(true).getProperty("user. </p> 298 .add(textPane).SEVERE.setContentType("text/html"). setLocationRelativeTo(null). panel. textPane. 8. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.getViewport(). } } public static void main(String[] args) { SwingUtilities. The component does not handle scrolling. pane. that we are loading into the JTextPane component.setPage("File:///" + cd + "test.add(pane). } catch (IOException ex) { Logger. loadFile(). getName()).setContentType("text/html"). textpane. JTextPane textpane = new JTextPane(). </pre> <br> <small>The Java Swing tutorial. 299 . set the content of the component to be a HTML document and disable editing. private void loadFile() { try { String cd = System. textpane. textPane.log(Level. } } Here we determine the current working directory of the user.SEVERE.setPage("File:///" + cd + "test. textpane.setContentType("text/html"). null. } catch (IOException ex) { Logger.dir") + "/". Example shows formatting capabilities of the component.<br> <pre> JScrollPane pane = new JScrollPane(). We create a JTextPane component.html"). 2013</small> </body> </html> In our example we show a JTextPane component and load a HTML document.setEditable(false). textpane. JTextPane textpane = new JTextPane().getLogger(TextPaneExample. ex).setEditable(false).class.getProperty("user. We load a HTML document into the pane. a view and a cotroller. we have continued covering basic Swing components. 300 . A model. The Swing toolkit uses a modified MVC design pattern. every component has it's model. The Swing has single UI object for both the view and the controller. it is displaying. The view is the visual representation of the data. For example the model keeps track whether the component is selected or pressed. typically user actions. A list component keeps a list of items. they work with. And finally the controller processes and responds to events. The model represents the data in the application. and may invoke changes on the model. by introducing an intermediate component: the controller. The traditional MVC pattern divides an application into three parts. Java Swing model architecture Swing engineers created the Swing toolkit implementing a modified Model View Controller design pattern. • • state models data models The state models handle the state of the component.Figure: JTextPane In this chapter. The data models handle data. The idea is to separate the data access and business logic from data presentation and user interaction. In the Swing toolkit. There are two kinds of models in Swing toolkit. This enables efficient handling of data and using pluggable look and feel at runtime. This modified MVC is sometimes called a separable model architecture. Even the basic ones like buttons. For example the JList component has ListDataEvent and ListSelectionEvent. we find out. javax.awt.swing. import java.DefaultButtonModel.swing. It would be an overkill to work with models directly in such simple situations.ActionEvent. We have only one single event (ChangeEvent) for all notifications coming from the component. we have two kinds of notifications. For convenience. } For example the getValue() method of the JSlider component. because no data can be associated with a push button. javax.JLabel. But there are exceptions.java source file. Icon icon) { // Create the model setModel(new DefaultButtonModel()). javax. radio boxes and for menu items. For more complicated components. we have different kinds of events. check boxes. import java.For Swing developer it means.awt. To query the state of the model. javax. For example the button component has DefaultButtonModel model public JButton(String text.JButton. • • lightweight notification stateful notification The lightweight notification uses a ChangeListener class. the stateful notification is used. Because of this. that the default model is created at the construction of the component. // initialize init(text. 301 . import import import import import import javax. icon). If we do not set a model for a component. the access to the model is done behind the scenes.swing.JFrame.JPanel. The following example illustrates the model for a JButton.event.ActionListener. the Swing tookit provides some convenience methods like the previous one. We can manage only the state of the button.swing. ButtonModel The model is used for various kinds of buttons like push buttons.getValue(). javax. For such notifications. there are some methods that return data without the model.event.JCheckBox. public int getValue() { return getModel(). Instead. a default one is created. The developer does not need to work with the model directly.swing. } If we look at the JButton. that we often need to get a model instance in order to manipulate the data in the component.swing. 90. pressed = new JLabel("Pressed: false").setEnabled(true). JLabel enabled.ChangeListener. 25). true).isEnabled()) ok.ChangeEvent. ok.swing.setEnabled(false).setText("Armed: true"). else pressed. 25).setBounds(40.setBounds(40. 90. if (model.isEnabled()) enabled. 120. ok = new JButton("ok"). else enabled. 100. 25). pressed.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { DefaultButtonModel model = (DefaultButtonModel) ok.getModel().setText("Armed: false").setText("Enabled: true"). } }). cb.event. if (model.isPressed()) pressed.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (ok. ok.setBounds(180. public ButtonModel() { setTitle("ButtonModel"). import javax. JLabel armed. JLabel pressed. 302 . 25). armed = new JLabel("Armed: false"). else ok. public class ButtonModel extends JFrame { private private private private JButton ok. if (model. enabled = new JLabel("Enabled: true").import javax. panel.setBounds(40. JCheckBox cb = new JCheckBox("Enabled".setText("Pressed: false"). 90. enabled. else armed. } }).swing.setText("Pressed: true"). 30.isArmed()) armed. 80. cb.setText("Enabled: false"). JPanel panel = new JPanel().setLayout(null).event. 30. add(pressed).add(enabled).java file. We query the model. add(panel).add(cb). model.isEnabled()) enabled.isEnabled()) ok.armed. we have a button. Where is the model? The answer lies in the AbstractButton.add(armed). panel. panel. The labels represent three properties of the button.setEnabled(false). } 303 . 250). 90.setEnabled(b). So we change the state of the button. ok. public void setEnabled(boolean b) { if (!b && model.setText("Enabled: true"). we call the setEnable() method.add(ok). To enable the ok button. if (model. check box and three labels. panel.setBounds(40. setDefaultCloseOperation(EXIT_ON_CLOSE). 150. setVisible(true). Here we get the default button model.setText("Enabled: false"). } super. Whether it is pressed. else ok.addChangeListener(new ChangeListener() { We use a lightweight ChangeListener to listen for button state changes. panel.setEnabled(b). We update the label accordingly. if (ok. The check box enables or disables the button. DefaultButtonModel model = (DefaultButtonModel) ok.setRollover(false). } } In our example.setEnabled(true). else enabled. panel. disabled or armed. whether the button is enabled or not. 25).isRollover()) { model.getModel(). setLocationRelativeTo(null). setSize(350. } public static void main(String[] args) { new ButtonModel(). swing.event. this.setLayout(null). that internally. import java.*. } private void initUI() { setTitle("ButtonModel").setBounds(40. public ButtonModel2(String title) { super(title). 25). import java. cb.ActionEvent. import javax. JLabel pressed. The setEnable() is another convenience method for programmers. we used a default button model. we the Swing toolkit works with a model. ok = new JButton("ok"). JLabel enabled.The answer is.addActionListener(new ActionListener() { 304 . panel. JPanel panel = new JPanel(). 30. ok. JLabel armed. Figure: ButtonModel Custom ButtonModel In the previous example. package zetcode. In the following code example we will use our own button model. public class ButtonModel2 extends JFrame { private private private private JButton ok. 80.ActionListener.awt. true). JCheckBox cb = new JCheckBox("Enabled".initUI().event.awt. 305 . 120. panel. enabled. panel. ButtonModel model = new DefaultButtonModel() { @Override public void setEnabled(boolean b) { if (b) enabled.setEnabled(b). cb. } }. else ok.add(armed). @Override public void setArmed(boolean b) { if (b) armed. else armed.setModel(model).setBounds(40.setText("Enabled: false").setText("Enabled: true"). panel. panel.add(enabled). super. enabled = new JLabel("Enabled: true"). 25).setArmed(b). 25).add(ok). 30.@Override public void actionPerformed(ActionEvent e) { if (ok. } }). } super. armed = new JLabel("Armed: false").setText("Armed: true"). 150. 25). 120.setBounds(40.setText("Pressed: false"). 120.setBounds(180.add(pressed). 120. armed.setText("Armed: false"). pressed = new JLabel("Pressed: false").setEnabled(true). pressed. panel.setText("Pressed: true"). 25). else enabled.setBounds(40.setPressed(b). ok.add(cb).isEnabled()) ok. 100. 90. else pressed.setEnabled(false). @Override public void setPressed(boolean b) { if (b) pressed. } super. setVisible(true). And the ListSelectionModel works with the GUI. import import import import import import java. We must not forget to call the parent method as well to procede with the processing. 306 . setLocationRelativeTo(null).setText("Enabled: false"). java. We set the custom model for the button.awt.awt. ex.BorderLayout.invokeLater(new Runnable() { public void run() { ButtonModel2 ex = new ButtonModel2("ButtonModel").event. We overwrite the setEnabled() method and add some functionality there. The difference is that we do not use a change listener and we use a custom button model. java.setModel(model). java. The JList component has the following models: ListModel and ListSelectionModel.MouseAdapter.awt.setEnabled(b). ButtonModel model = new DefaultButtonModel() { We create a button model and overwrite the necessary methods.add(panel). } } This example does the same thing as the previous one. ok. JList models Several components have two models.Dimension.event. 250).ActionListener. } }).awt. } public static void main(String[] args) { SwingUtilities. The following example shows both models. setVisible(true). } super. The ListModel handles data. java. setSize(350.MouseEvent. @Override public void setEnabled(boolean b) { if (b) enabled. setDefaultCloseOperation(EXIT_ON_CLOSE).awt. java.setText("Enabled: true"). else enabled.event.awt.event.ActionEvent. newitem).swing. javax.BorderFactory.DefaultListModel. 2. 307 . javax. public class List extends JFrame { private DefaultListModel model.setBorder(BorderFactory. String newitem = null. list. model.addElement("Fargo").swing.getElementAt(index). javax.addElement("Aguirre.setLeadSelectionIndex(index).swing. JPanel panel = new JPanel().Y_AXIS)).addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e) { if(e.setSelectionMode(ListSelectionModel. model.swing. BoxLayout.Box.JOptionPane. private JList list. else return.addElement("Schindler list"). public List() { setTitle("JList models"). list = new JList(model).JList. model = new DefaultListModel(). model.JScrollPane. list.swing.swing.import import import import import import import import import import import javax.setLayout(new BorderLayout()).getPoint()).BoxLayout.ListSelectionModel.addElement("Exorcist"). model. der Zorn Gottes"). list.swing.setLayout(new BoxLayout(rightPanel. model.remove(index).swing.isEmpty()) { model. javax.JButton.JPanel. javax. panel. 2)). item).X_AXIS)).setLayout(new BoxLayout(panel. BoxLayout.createEmptyBorder(2. JPanel leftPanel = new JPanel().getClickCount() == 2){ int index = list. if (!newitem. javax.locationToIndex(e.trim(). javax.addElement("Amelie").swing. javax.JFrame.swing. selmodel.showInputDialog("Rename item".SINGLE_SELECTION). JPanel rightPanel = new JPanel(). 2. leftPanel. if (text != null) newitem = text.getSelectionModel().add(index. String text = JOptionPane. rightPanel. javax.swing. model. ListSelectionModel selmodel = list. javax. Object item = model. 20.getElementAt(index).} } }).setBorder(BorderFactory. if (!item. JButton removeall = new JButton("Remove All").getMaximumSize()). delete. rename. if (index == -1) return. else return. if (text != null) item = text.getMinSelectionIndex(). JButton add = new JButton("Add").showInputDialog("Add a new item"). String newitem = null.setMaximumSize(removeall.getMaximumSize()).isEmpty()) model.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { ListSelectionModel selmodel = list. if (text != null) { newitem = text. pane.getMinSelectionIndex(). add. } 20)). add.add(list). String text = JOptionPane.setMaximumSize(removeall. }).addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { ListSelectionModel selmodel = list.trim(). leftPanel. 308 .addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { String text = JOptionPane.showInputDialog("Rename item". int index = selmodel.trim(). } else item).getViewport(). JScrollPane pane = new JScrollPane().getMaximumSize()). leftPanel. JButton rename = new JButton("Rename").getSelectionModel().addElement(item). } }).remove(index).add(pane). JButton delete = new JButton("Delete"). 20. rename.getSelectionModel(). String item = null. int index = selmodel. if (index >= 0) model. } delete.createEmptyBorder(20.setMaximumSize(removeall. Object item = model. model = new DefaultListModel(). rightPanel.createRigidArea(new Dimension(0. rightPanel. The parameter of the constructor is the model.createEmptyBorder(2. rightPanel. 2. panel.createRigidArea(new Dimension(0. if (!newitem.isEmpty()) { model. } public static void main(String[] args) { new List(). model. model. list = new JList(model).. rightPanel.4))). } }).add(delete). 2)).add(rightPanel). We create a list model and add elements into it.addElement("Amelie"). model. rightPanel. } }). The example is a bit larger.createRigidArea(new Dimension(0.add(Box.add(Box. 309 .addElement("Aguirre. panel.SINGLE_SELECTION). list. We do not allow to input empty spaces into the list component.setBorder(BorderFactory.return. setVisible(true). } } The example shows a list component and four buttons. We also put some space around the list.4))). rightPanel. setSize(350. 20)).setSelectionMode(ListSelectionModel. . setDefaultCloseOperation(EXIT_ON_CLOSE). der Zorn Gottes").. 0. setLocationRelativeTo(null). We create a list component. add(panel).add(index. rightPanel. we have created.setBorder(BorderFactory.createEmptyBorder(0. 2.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { model. We put the list into the single selection mode.add(removeall).add(leftPanel).remove(index). The buttons control the data in the list component. 250). } removeall.clear().add(rename). list. because we did some additional checks there. newitem).4))). rightPanel.add(Box.add(add). 0. We add only items that are not equal to null and are not empty. that contain at least one character other than white space. Removing an item is working with data. For this. This is the code. ListSelectionModel selmodel = list.Dimension.swing. if (index >= 0) model. java. in our example we used both list models. We called add().awt. so we use a ListSelectionModel.awt. e.awt. So. In a JTextPane component. Figure: List Models A document model This is an excellent example of a separation of a data from the visual representation. This is a GUI work. which is a GUI job.BorderLayout. if (!item. 310 .trim(). int index = selmodel. import javax.addElement(item). java. For that we use the list data model. java. we have a StyledDocument for setting the style of the text data.getMinSelectionIndex(). it must be selected.ActionListener.if (text != null) item = text.ActionEvent. else return.event.g. import import import import java.remove(index).BorderFactory. And we used a list selection model in order to find out the selected item. So we must figure out the currently selected item. In order to delete an item from the list.isEmpty()) model. that runs when we press the delete button.awt.getSelectionModel(). It makes no sense to add white spaces or null values into the list.event. remove() and clear() methods of the list data model to work with our data. we call the getSelectionModel() method. javax. true). JPanel panel = new JPanel(). boldb italb strib undeb toolbar.JButton.createEmptyBorder(20.text. javax. javax. strike = new ImageIcon("strike. 20.Style. panel. style = textpane. 8.JTextPane. = = = = new new new new JButton(bold). 20)). italic = new ImageIcon("italic.JToolBar. true).png"). toolbar. textpane.setBorder(BorderFactory.StyledDocument. underline = new ImageIcon("underline. public class DocumentModel extends JFrame { private StyledDocument doc.swing. BorderLayout.swing. null).text.add(italb). javax. toolbar.ImageIcon. JButton(underline). javax.StyleConstants.setBold(style. StyleConstants.JScrollPane.add(boldb). StyleConstants. javax.png"). style = textpane.addStyle("Bold".getStyledDocument().addStyle("Strike".add(undeb).add(strib). true). Style style = textpane. 311 . add(toolbar. 20. null). JButton(italic).swing.swing. 8)). panel. ImageIcon ImageIcon ImageIcon ImageIcon JButton JButton JButton JButton bold = new ImageIcon("bold. StyleConstants.JFrame. JToolBar toolbar = new JToolBar().setItalic(style.swing. null).NORTH).createEmptyBorder(8.swing.addStyle("Underline".setUnderline(style.setLayout(new BorderLayout()). StyleConstants.swing. JScrollPane pane = new JScrollPane().import import import import import import import import import import javax. private JTextPane textpane.addStyle("Italic". public DocumentModel() { setTitle("Document Model").swing. javax.png"). doc = textpane. javax. JButton(strike). javax. 8. toolbar.swing. true).setBorder(BorderFactory. textpane = new JTextPane(). null).setStrikeThrough(style.JPanel.swing. style = textpane.text.png"). textpane. textpane.add(pane). } } 312 . textpane. panel.getStyle("Bold"). false).getSelectionEnd() textpane.getSelectionStart().getStyle("Italic").addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { doc. false). strib. textpane. false).getViewport(). } public static void main(String[] args) { new DocumentModel().getSelectionStart().addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { doc.setCharacterAttributes(textpane.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { doc. textpane.add(textpane). textpane. false).addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { doc. italb.getSelectionStart().setCharacterAttributes(textpane. undeb.boldb. 320)).getSelectionStart(). textpane. setSize(new Dimension(380. setDefaultCloseOperation(EXIT_ON_CLOSE). add(panel). } }).getSelectionStart().setCharacterAttributes(textpane.getStyle("Strike").getSelectionEnd() textpane. } }).getSelectionStart().getSelectionStart().getSelectionEnd() textpane.getSelectionEnd() textpane. pane. } }).getSelectionStart().getStyle("Underline"). textpane. setVisible(true). setLocationRelativeTo(null). } }).setCharacterAttributes(textpane. we are not replacing an old style with a new one. In the toolbar. Figure: Document model In this chapter. length of the selection.setCharacterAttributes(textpane.getSelectionEnd() . Here we register a bold style for the text pane component.The example has a text pane and a toolbar. if the text is underlined and we make it bold.setBold(style. textpane. where we apply the bold text. The registered styles can be retrieved at any time. we have four buttons. the style and the boolean value replace. true). Boolean value false means.getSelectionStart(). textpane.getSelectionStart(). StyleConstants. doc = textpane. we have mentioned Swing models. A style is a set of text attributes. We get the length value by substracting the selection end and selection start values. 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 . The parameters are the offset. Drag and Drop in Swing In computer graphical user interfaces.textpane.getStyle("Bold"). Here we get the styled document. false). The offset is the beginning of the text. the result is an underlined bold text. but we merge them. that change attributes of the text. doc. size.getStyledDocument(). Style style = textpane.addStyle("Bold". This means. such as color. which is a model for the text pane component. null). Here we change the attributes of the text. or create various types of associations between two abstract objects. we drag and drop a graphical component.object. The transferred data can be of various type. The component. Usually.JFrame. In situations. If we drag an image from one application to another. we can drag and drop two things.JTextField. If we drag a tab in Firefox and move it to another place. javax. import import import import javax.swing. Data or some graphical objects.JButton. javax. we drag and drop binary data. where there is no built-in support. a Swing programmer uses a TransferHandler to manage the drag and drop functionality. We will utilize a TransferHandler class.swing. We will work with built-in drag and drop support. Several Swing components have already a built-in support for drag and drop operations. In general. The best way how to cope with this complexity is to create a small example for all situations. the programmer has to create everything from scratch. A simple drag and drop example We will demonstrate a simple drag and drop example. javax. A DataFlavor object provides information about the data being transferred. And slowly make it to more complex examples. A DropTarget is an object responsible for accepting drops in an drag and drop operation.TransferHandler. where the drag operation begins must have a DragSource object registered. A Transferable encapsulates data being transferred. In such cases.swing.swing. 314 . (Wikipedia) Drag and drop functionality is one of the most visible aspects of the graphical user interface. it can be used to invoke many kinds of actions. Drag and drop operation enables users to do complex things intuitively. The sheer amount of various classes involved with drag and drop operations in Java Swing toolkit might be overwhelming. public class SimpleDnD extends JFrame { JTextField field. button. The TransferHandler is a class responsible for transfering data between components.setTransferHandler(new TransferHandler("text")). setDefaultCloseOperation(JFrame. } } In our example we have a text field and a button.setBounds(30. setLocationRelativeTo(null). add(button). public SimpleDnD() { setTitle("Simple Drag & Drop"). 90. setVisible(true). 50. We must enable it. } public static void main(String[] args) { new SimpleDnD(). button = new JButton("Button").setTransferHandler(new TransferHandler("text")). 25).EXIT_ON_CLOSE).setDragEnabled(true). button. JButton button. The text field has a built in support for dragging. field = new JTextField(). field. 150). field. 25). field. 150. The constructor takes a property name as a parameter. add(field).setBounds(200. Figure: Simple drag & drop example 315 . We can drag a text from the field and drop it onto the button. 50. button. setLayout(null). setSize(330.setDragEnabled(true). javax. 50.setTransferHandler(new TransferHandler("icon")).add(button).awt.setTransferHandler(new TransferHandler("icon")). setLocationRelativeTo(null).EXIT_ON_CLOSE).swing. ImageIcon icon2 = new ImageIcon("plain. = new JLabel(icon3.FlowLayout.TransferHandler. JButton button = new JButton(icon2).addMouseListener(listener).swing.awt.event. add(panel). java.addMouseListener(listener). javax. javax. javax. 15)). We have to code the drag functionality ourselves.CENTER).MouseListener. we used a text property. MouseListener listener = new DragMouseAdapter().png").swing. We will drag and drop icons.png"). JLabel.awt.swing. setVisible(true).setTransferHandler(new TransferHandler("icon")). JLabel label1 JLabel label2 = new JLabel(icon1.swing. java.event.swing. button. public class IconDnD extends JFrame { public IconDnD() { setTitle("Icon Drag & Drop"). setDefaultCloseOperation(JFrame. panel.JComponent. label1. panel. This time we will use an icon property.setFocusable(false). ImageIcon icon1 = new ImageIcon("sad. javax.JLabel.Icon drag & drop Some of the Java Swing components do not have built in drag support. ImageIcon icon3 = new ImageIcon("crying. javax. javax.JButton. } 316 . JLabel. JPanel panel = new JPanel(new FlowLayout(FlowLayout.png").add(label1).awt.JFrame. In the previous example.ImageIcon. label2. JLabel component is such a component.CENTER).LEFT. import import import import import import import import import import import java.add(label2).swing. button. java.event. label1.MouseEvent. label2.JPanel. pack(). panel.MouseAdapter. e. TransferHandler handler = c. Text without a comma will go into one row. In our case it is a label instance. Figure: Icon drag & drop example Custom JList drop example Some components do not have a default drop support. There is a good reason for this. We register a custom mouse adapter for both labels. The TransferHandler is needed for both drag sources and drag targets as well. if the data will be inserted into one row.getSource(). JComponent c = (JComponent) e.setTransferHandler(new TransferHandler("icon")). These code lines initiate the drag support. MouseListener listener = new DragMouseAdapter().addMouseListener(listener). the button accepts a drop gesture. We don't know. TransferHandler. handler. button. We get the drag source.COPY). So we must implement manually the drop support for the list component.class DragMouseAdapter extends MouseAdapter { public void mousePressed(MouseEvent e) { JComponent c = (JComponent) e. we have two labels and a button. The drag support is not enabled by default for the label. label2. } } In the code example. And finally initiate the drag support with the exportAsDrag() method call.addMouseListener(listener).getSource(). 317 .exportAsDrag(c. label2. TransferHandler handler = c. Each component displays an icon. label1. One of them is a JList component. } } public static void main(String[] args) { new IconDnD(). Each of the three components has a TransferHandler class for an icon property. label1. We get it's transfer handler object. The two labels enable drag gestures.setTransferHandler(new TransferHandler("icon")). The comma separated text will be inserted into two or more rows. handler.getTransferHandler().exportAsDrag(c.COPY). e. or two or more rows.getTransferHandler(). TransferHandler.setTransferHandler(new TransferHandler("icon")). DefaultListModel model. 15)).setSelectionMode(ListSelectionModel. panel.setTransferHandler(new ListHandler()). field.add(field).swing. JPanel panel = new JPanel(new FlowLayout(FlowLayout. javax.ListSelectionModel.JScrollPane.add(list).LEFT. panel.SINGLE_SELECTION).JList. javax.Dimension. javax.swing. javax. java.getViewport().awt.setPreferredSize(new Dimension(150. javax. setLocationRelativeTo(null).JFrame.INSERT). list. 15. list. java.swing. javax. public ListDrop() { setTitle("ListDrop").awt.swing.EXIT_ON_CLOSE).swing.import import import import import import import import import import import import import java.DefaultListModel. field.DropMode. } 318 .awt.datatransfer. javax. pack(). JList list = new JList(model).swing. javax.setDropMode(DropMode. setVisible(true).swing. list.TransferHandler. add(panel). 25)). public class ListDrop extends JFrame { JTextField field. java. JScrollPane pane = new JScrollPane(). field = new JTextField("").setPreferredSize(new Dimension(180.Transferable. javax.swing. model = new DefaultListModel().FlowLayout.swing. setDefaultCloseOperation(JFrame.add(pane).datatransfer. pane.awt.isDrop()) { return false.setDragEnabled(true). } private class ListHandler extends TransferHandler { public boolean canImport(TransferSupport support) { if (!support. pane.JPanel. 150)).JTextField.DataFlavor. } In the above example. The text in the text field can be dragged and dropped into the list. } } } public static void main(String[] args) { new ListDrop().stringFlavor).add(index++.isEmpty()) model. If we chose DropMode. If the text is comma separated.getIndex(). that we are going to insert new items into the list component.trim()). public boolean importData(TransferSupport support) { if (!canImport(support)) { return false. String[] data = line. for (String item: data) { if (!item.getTransferData(DataFlavor.split(".setDragEnabled(true). field. If not. the words will be split into rows. list. we have a text field and a list component. int index = dl. } 319 .DropLocation) support. We enable the drag support for the text field component. the text will be inserted into one row. try { line = (String) transferable. We set a custom transfer handler class.INSERT specifies. item. } return true. Here we specify a drop mode. list.DropLocation dl = (JList.getTransferable(). } Transferable transferable = support.setDropMode(DropMode.INSERT.INSERT).setTransferHandler(new ListHandler()).isDrop()) { return false. we would drop new items onto the existing ones. } catch (Exception e) { return false.} return support.stringFlavor). String line.isDataFlavorSupported(DataFlavor."). public boolean canImport(TransferSupport support) { if (!support.getDropLocation(). The DropMode. } JList. We retrieve the index. This method tests suitability of a drop operation. Figure: JList drop example The previous examples used components with built-in drag and drop support. We retrieve our data. where the data will be inserted.stringFlavor). Next we are going to create a drag and drop functionality from scratch. Here we filter out the clipboard paste operations and allow only String drop operations.getDropLocation(). public boolean importData(TransferSupport support) { . for (String item: data) { if (!item.DropLocation dl = (JList. } The importData() method transfers the data from the clipboard or from the drag and drop operation to the drop location. String[] data = line.isEmpty()) model.} return support.DropLocation) support."). The Transferable is the class.getTransferable(). JList.add(index++.getIndex(). We get a drop location for the list. int index = dl..getTransferData(DataFlavor. the drop operation is cancelled. line = (String) transferable. 320 .isDataFlavorSupported(DataFlavor.split(". If the method returns false. } Here we split the text into parts and insert it into one or more rows. where the data is bundled.trim()). Transferable transferable = support..stringFlavor). item. awt.awt. A DragSource. java.add(left). this). java.setPreferredSize(new Dimension(120. if (event. java.JPanel. Transferable.LEFT. this).awt.DragGestureListener.Cursor.awt. } public void dragGestureRecognized(DragGestureEvent event) { System.awt.getDragAction() == DnDConstants.awt. import javax. ds. import javax.dnd. JPanel panel = new JPanel(new FlowLayout(FlowLayout.EXIT_ON_CLOSE). 50. DnDConstants.println("grag gesture").dnd.JFrame. java. pack(). setDefaultCloseOperation(JFrame. 15)).swing.awt.datatransfer. We will work with several classes needed to create a drag gesture.startDrag(cursor.swing.datatransfer.dnd.ACTION_COPY.Transferable. java.out.DefaultCopyDrop.awt. DragSource ds = new DragSource(). Transferable { public DragGesture() { setTitle("Drag Gesture"). import import import import import import import import import import java. JPanel left = new JPanel().DragGestureEvent.setBackground(Color. java. } event. java. Cursor cursor = null.FlowLayout. panel. DragGestureListener.Drag Gesture In the following example we will inspect a simple drag gesture. setVisible(true). public class DragGesture extends JFrame implements DragGestureListener. setLocationRelativeTo(null). 120)).ACTION_COPY) { cursor = DragSource.createDefaultDragGestureRecognizer(left. } public static void main(String[] args) { new DragGesture(). } 321 .awt.DataFlavor. DragGestureEvent. left.Dimension.red). add(panel).Color.DnDConstants. java. left.DragSource.awt.dnd. java. we will not transfer any data. So the three necessary methods of the Transferable interface are left unimplemented.public Object getTransferData(DataFlavor flavor) { return null. The Transferable handles data for a transfer operation. public Object getTransferData(DataFlavor flavor) { return null. The startDrag() method of the DragGestureEvent finally starts the drag operation. when we click on a component and move a mouse pointer.getDragAction() == DnDConstants. We will specify two parameters.createDefaultDragGestureRecognizer(left. } } This simple example demostrates a drag gesture. public class DragGesture extends JFrame implements DragGestureListener. Here we create a DragSource object and register it for the left panel. The cursor type and the Transferable object. public void dragGestureRecognized(DragGestureEvent event) { } The dragGestureRecognized() method responds to a drag gesture. We will only demonstrate a drag gesture. Transferable { The DragGesture implements two interfaces. DnDConstants. ds.startDrag(cursor. DragSource ds = new DragSource(). The DragSource is the entity responsible for the initiation of the Drag and Drop operation. while the button is pressed. how we can create a DragSource for a component. this). if (event. } event.ACTION_COPY) { cursor = DragSource. The DragGestureListener will listen for drag gestures. In the example.DefaultCopyDrop. this). } public DataFlavor[] getTransferDataFlavors() { return new DataFlavor[0]. } public DataFlavor[] getTransferDataFlavors() { 322 . } public boolean isDataFlavorSupported(DataFlavor flavor) { return false. The createDefaultDragGestureRecognizer() associates a drag source and DragGestureListener with a particular component. The drag gesture is created. The example will show. Cursor cursor = null.ACTION_COPY. DnDConstants. public class ComplexExample extends JFrame implements DragGestureListener { JPanel panel.setFocusable(false).DragGestureListener.JColorChooser. java.LEFT. JPanel left.DropTarget.event.setBackground(Color. panel = new JPanel(new FlowLayout(FlowLayout. java.DropTargetDropEvent.datatransfer. 15)).awt.setPreferredSize(new Dimension(100.awt.return new DataFlavor[0]. openb.UnsupportedFlavorException.dnd.ActionEvent. import import import import import import import import import import import import import import import import import import import import java.DropTargetAdapter.red).awt.dnd. we left these methods unimplemented for now.FlowLayout. java.DragSource. java.awt.awt.JButton.Color. java. java. java.datatransfer.awt. we create a complex drag and drop example.swing.JPanel. A complex drag and drop example In the following example.awt. java.dnd.awt. javax. left.dnd.dnd.awt.event.awt. java. 50. JButton openb = new JButton("Choose Color").DataFlavor. java.Cursor.dnd.awt.awt.swing. We create a drag source a drop target and a transferable object.awt. As I have already mentioned. left.awt.datatransfer. java. javax.Dimension. java. left = new JPanel(). java. } public boolean isDataFlavorSupported(DataFlavor flavor) { return false.ActionListener. javax.JFrame. javax.swing. openb.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { 323 .dnd. } The object that implements the Transferable interface must implement these three methods. public ComplexExample() { setTitle("Complex Example"). java.awt.swing.awt.DragGestureEvent. 100)).Transferable. java. this. ds.white). Color.white).add(openb). Color color = (Color) tr.ACTION_COPY) { cursor = DragSource.JColorChooser clr = new JColorChooser(). setLocationRelativeTo(null). add(panel). new MyDropTargetListener(right).colorFlavor).showDialog(panel. pack(). DnDConstants. JPanel panel = (JPanel) event. 324 .setBackground(Color. } public void drop(DropTargetDropEvent event) { try { Transferable tr = event. null).startDrag(cursor.ACTION_COPY.EXIT_ON_CLOSE). } public void dragGestureRecognized(DragGestureEvent event) { Cursor cursor = null.panel = panel. right.ACTION_COPY. setVisible(true). true.getDragAction() == DnDConstants. public MyDropTargetListener(JPanel panel) { this. 100)). panel. this). private JPanel panel. DnDConstants.getTransferData(TransferableColor.DefaultCopyDrop.createDefaultDragGestureRecognizer(left. panel.getBackground(). class MyDropTargetListener extends DropTargetAdapter { private DropTarget dropTarget.setPreferredSize(new Dimension(100.getComponent(). setDefaultCloseOperation(JFrame. "Choose Color".getTransferable(). JPanel right = new JPanel(). new TransferableColor(color)).add(left). }).setBackground(color). if (event. right. Color color = panel. } left. } } event. dropTarget = new DropTarget(panel. DragSource ds = new DragSource(). Color color = clr.add(right). panel. event. return.stringFlavor)) return true.equals(DataFlavor.printStackTrace().{ if (event. protected static DataFlavor[] supportedFlavors = { colorFlavor. } public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException { if (flavor.ACTION_COPY).stringFlavor)) return color.class.equals(colorFlavor)) return color. }. public boolean isDataFlavorSupported(DataFlavor flavor) { if (flavor. } catch (Exception e) { e. Color color. this.isDataFlavorSupported(TransferableColor. The button displays a color chooser dialog and sets a color for the first panel.setBackground(color).panel. } } public static void main(String[] args) { new ComplexExample(). } } } event.toString().equals(DataFlavor.acceptDrop(DnDConstants.color = color. } } public DataFlavor[] getTransferDataFlavors() { return supportedFlavors. } } The code example shows a button and two panels. The color can be dragged into the second panel. event.stringFlavor.dropComplete(true). 325 . DataFlavor.colorFlavor)) event. return false. public TransferableColor(Color color) { this. } class TransferableColor implements Transferable { protected static DataFlavor colorFlavor = new DataFlavor(Color. else throw new UnsupportedFlavorException(flavor).rejectDrop().rejectDrop(). "A Color Object").equals(colorFlavor) || flavor. else if (flavor. new TransferableColor(color)).class. Here we specify. dropTarget = new DropTarget(panel.panel = panel.acceptDrop(DnDConstants.setBackground(color). In our case it is a color object.colorFlavor)) { event.panel. null). DnDConstants. "A Color Object"). we reject it. } We get the data being transferred.This example will enhance the previous one.isDataFlavorSupported(TransferableColor. what data flavors we support.ACTION_COPY).stringFlavor. new MyDropTargetListener(right).ACTION_COPY. Here we set the color of the right panel. In our case it is a custom defined color flavor and a pre-defined DataFlavor. event. this. return. } In the MyDropTargetListener we create a drop target object. event. this. event. Transferable tr = event. true. We register a drop target listener with the right panel. The startDrag() method has two parameters.rejectDrop().startDrag(cursor. }.colorFlavor). Color color = (Color) tr.getTransferData(TransferableColor. protected static DataFlavor[] supportedFlavors = { colorFlavor. DataFlavor.equals(colorFlavor)) 326 . In the TransferableColor.getTransferable(). protected static DataFlavor colorFlavor = new DataFlavor(Color.dropComplete(true). public MyDropTargetListener(JPanel panel) { this. We will add a drop target and a custom transferable object. A cursor and a Transferable object. public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException { if (flavor.stringFlavor. we create a new DataFlavor object. if (event. If the conditions for a drag and drop operation are not fulfilled. In the painting process. Painting in Swing Painting is used. Or if we are creating a custom widget from scratch. filling or rotating does not degrade the quality of an image Types of primitives • • • • points lines polylines polygons 327 .equals(DataFlavor. Both types of computer graphics have advantages and disadvantages. To do the painting. 2D Vector Graphics There are two different computer graphics. These primitives are created using mathematical equations. Return an object for a specific data flavor. we use the Graphics2D object. else throw new UnsupportedFlavorException(flavor). Vector graphics is the use of geometrical primitives such as points. Raster graphics represents images as a collection of pixels. The painting is done within the paintComponent() method.stringFlavor)) return color. scaling. else if (flavor. The advantages of vector graphics over raster are: • • • smaller size ability to zoom indefinitely moving. lines. Figure: A complex example This part of the Java Swing tutorial was dedicated to Swing drap and drop operations. when we want to change or enhance an existing widget.toString(). Vector and raster graphics. curves or polygons to represent images.} return color. we use the painting API provided by the Swing toolkit. Insets insets = getInsets(). javax. } } public class PointsExample extends JFrame { public PointsExample() { initUI().height . javax. javax.abs(r. We use one point twice.awt.width .blue). } } @Override public void paintComponent(Graphics g) { super.Graphics2D.zetcode. y.right. Random r = new Random(). int w = size. we use the drawLine() method. To draw a point. java. x. class DrawPanel extends JPanel { private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g.nextInt()) % h. package com. It is a single dot on the window.Dimension.awt. java.bottom.left .insets.Random.top .setColor(Color. i <= 1000.insets.nextInt()) % w. g2d.paintComponent(g).awt.abs(r.Insets. java.util. import import import import import import import import import java.JFrame.swing.Graphics. } 328 .insets. int h = size.SwingUtilities. doDrawing(g). for (int i = 0. g2d.swing. java. i++) { Dimension size = getSize().drawLine(x.swing.insets.awt. java.Color.• • • circles ellipses Splines Points The most simple graphics primitive is point. int y = Math.awt. y). int x = Math.JPanel. There is no method to draw a point in Swing. blue). g2d. 200). setDefaultCloseOperation(JFrame. We don't paint there. Dimension size = getSize(). setLocationRelativeTo(null). The super. Actual drawing is delegated to the doDrawing() method. It does some necessary work to prepare component for drawing. Graphics2D g2d = (Graphics2D) g. 329 .setVisible(true). The size of the window includes borders and titlebar.invokeLater(new Runnable() { @Override public void run() { PointsExample ex = new PointsExample(). } public static void main(String[] args) { SwingUtilities.public final void initUI() { DrawPanel dpnl = new DrawPanel(). doDrawing(g). add(dpnl). Painting in Swing is done on the Graphics2D object. The drawing panel will later be added to a JFrame component.paintComponent(g). Insets insets = getInsets(). ex. setTitle("Points"). we will randomly draw 1000 points on the panel surface. which is a JPanel component. which we override.setColor(Color. setSize(250. @Override public void paintComponent(Graphics g) { super.EXIT_ON_CLOSE). } Custom painting is performed inside the paintComponent() method.paintComponent() method calls the method of the parent class. We will paint our points in blue color. class DrawPanel extends JPanel { We are drawing on a custom drawing panel. Therefore. } }). } } One point is difficult to observe. int w = int h = size.Graphics2D.left . 250. we use a drawLine() method.Graphics.drawLine(20. 0f.awt. where we will effectively paint our points.abs(r. class DrawPanel extends JPanel { private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g.awt. y.insets.SwingUtilities. 1f. It is drawn using two points.zetcode.drawLine(x.nextInt()) % h. that we computed above. 2f}. package com.top . {1f.insets. g2d. 2f}. int x = Math.nextInt()) % w.swing. Here we calculate the area.JPanel.bottom. javax. g2d. java. 4f.swing. import import import import import import java. {4f. y). Here we draw the point. float[] float[] float[] float[] dash1 dash2 dash3 dash4 = = = = {2f. 0f.insets.insets.BasicStroke. Random r = new Random(). javax. x. 330 .width . 1f}.JFrame. As we already said. {4f. Figure: Points Lines A line is a simple graphics primitive.height . javax. We specify the same point twice. java. 40). 1f}.right. int y = Math.awt. 40.abs(r.swing. size. We get a random number in range of the size of area. 160.setStroke(bs2).0f.paintComponent(g). } } 331 .CAP_BUTT. g2d. BasicStroke bs4 = new BasicStroke(1. } } public class LinesExample extends JFrame { public LinesExample() { initUI(). 80. g2d. BasicStroke.drawLine(20.CAP_BUTT. 120). BasicStroke. 250. BasicStroke. BasicStroke bs3 = new BasicStroke(1.invokeLater(new Runnable() { @Override public void run() { LinesExample ex = new LinesExample().0f. g2d. BasicStroke. BasicStroke. dash1. 1.setVisible(true). 1.BasicStroke bs1 = new BasicStroke(1. BasicStroke bs2 = new BasicStroke(1. BasicStroke. BasicStroke. setDefaultCloseOperation(JFrame. 270). g2d. 2f). dash4. } public final void initUI() { DrawPanel dpnl = new DrawPanel(). 1.JOIN_ROUND.drawLine(20. setTitle("Lines"). g2d.0f. } }). g2d. g2d. } public static void main(String[] args) { SwingUtilities. 2f).EXIT_ON_CLOSE). 250.JOIN_ROUND.setStroke(bs3).drawLine(20. 80). doDrawing(g). ex.CAP_BUTT. setLocationRelativeTo(null). 200).CAP_BUTT. 250.setStroke(bs1). 160). dash2. 1. g2d.setStroke(bs4). 2f). } @Override public void paintComponent(Graphics g) { super. 2f). add(dpnl). 200. BasicStroke.JOIN_ROUND. dash3. 250. setSize(280.JOIN_ROUND.0f.drawLine(20. 120. javax. that we use in the stroke object. 60). dash and the dash phase. Other will have a different stroke. BasicStroke. BasicStroke. It defines a basic set of rendering attributes for the outlines of graphics primitives.JOIN_ROUND. package com.swing. javax. 332 .0f. 15. The first line is drawn using the default values. java.CAP_BUTT.Graphics. 212. Here we create a dash. float[] dash1 = { 2f.drawRect(130. import import import import import import java. The stroke defines the line width. Figure: Lines Rectangles To draw rectangles. javax.Color.setColor(new Color(212. 90. g2d. 2f ) This code creates a stroke.SwingUtilities.Graphics2D. g2d. java. dash1. 212)). class DrawPanel extends JPanel { private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g. end caps.drawRect(10. The stroke is created using the BasicStroke class. miter limit. we use the drawRect() method. g2d.In the example.JFrame. 0f. 2f }.zetcode. 90.JPanel.awt. we use the fillRect() method. 1. 60). line joins.awt. BasicStroke bs1 = new BasicStroke(1. 15. we draw five lines.swing. To fill rectangles with the current color.swing.awt. EXIT_ON_CLOSE). g2d. 60). g2d. 61)). 90. g2d. g2d.setColor(new Color(42. 167. 105. g2d. } public final void initUI() { DrawPanel dpnl = new DrawPanel(). } 333 . 146. g2d. 100.fillRect(130.fillRect(10.setColor(new Color(125. 300).fillRect(250.setColor(new Color(63. 60). g2d. 105. 90.drawRect(250. 195. add(dpnl). 90. g2d. 123)). 90. 54)). 90.drawRect(10.fillRect(250. 60).drawRect(10. 211.drawRect(250. g2d. 90. g2d. 195. 60). 195.drawRect(130. 90. 60).g2d. 60). 69)). g2d. 90. 195. 121.setColor(new Color(241. 231)). 90. 60). 105.drawRect(250. g2d. g2d.fillRect(250.setColor(new Color(130. } } public class RectanglesExample extends JFrame { public RectanglesExample() { initUI(). g2d. 60). 60). setTitle("Rectangles"). 179. 90.paintComponent(g). 195. g2d. 105. 186)).setColor(new Color(70. } @Override public void paintComponent(Graphics g) { super. 90.setColor(new Color(217. 60). setDefaultCloseOperation(JFrame.drawRect(130. g2d. 15.fillRect(130. 90. 60). 1)). 90.fillRect(10. 15. 60). 195. 116)). 15. 15. 105. 90. g2d. g2d. 90. 67.fillRect(10. doDrawing(g). 60). setSize(360. 105. 60). 21.setColor(new Color(252. 60). setLocationRelativeTo(null). g2d. g2d. g2d.fillRect(130. 98.setColor(new Color(31. g2d. 84)). 90. 60). g2d. g2d. invokeLater(new Runnable() { public void run() { RectanglesExample ex = new RectanglesExample().setVisible(true).awt. Figure: Rectangles Textures A texture is a bitmap image applied to a shape. ex. package com. .public static void main(String[] args) { SwingUtilities. To fill the rectangle with a color. 15. we use the drawRect() method. import java. 212)). } }).awt. We set the color of the outline of the rectangle to a soft gray color.drawRect(10. 60). 60). we use the TexturePaint class.zetcode. The third and fourth are width and height. 15.Rectangle.. import java. g2d. import java.awt. To draw the outline of the rectangle. 334 . g2d. so that it does not interfere with the fill color. g2d. 90. we use the fillRect() method. 90. The first two parameters are the x and y values.Graphics.. To work with textures in Java 2D.setColor(new Color(212. } } In the example we draw nine colored rectangles. 212.fillRect(10.Graphics2D. swing.BufferedImage. import javax. class DrawPanel extends JPanel { BufferedImage slate.ImageIO. g2d.Logger. g2d.getClass().TexturePaint. } catch (IOException ex) { Logger.import java. BufferedImage pane.getLogger(Textures.png")). 0. } } @Override public void paintComponent(Graphics g) { super. import java.logging. javax.image. java = ImageIO. javax. 0.getResource("java.SEVERE. 0.setPaint(panetp).imageio. } doDrawing(g). g2d.IOException. pane = ImageIO.read(this. 15.log(Level. new Rectangle(0.Level.swing. java.awt.paintComponent(g). null.getResource("slate.class. 60). TexturePaint slatetp.getName()).swing.getClass().setPaint(slatetp). 60)).fillRect(130. slatetp = new TexturePaint(slate. javatp = new TexturePaint(java.io.getResource("pane.png")). 90. 90. 60)). java. TexturePaint panetp. new Rectangle(0. 60). 335 . ex). 90.awt. 15. g2d.read(this. panetp = new TexturePaint(pane. BufferedImage java.fillRect(10. new Rectangle(0. 90.read(this.util. public DrawPanel() { loadImages(). g2d. 60)). import javax.JFrame.util.JPanel.SwingUtilities.getClass(). private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g. } private void loadImages() { try { slate = ImageIO. 90. import import import import import java. TexturePaint javatp.logging.setPaint(javatp).png")). Here we read the image into the buffered image.fillRect(10. 0. 90. public class Textures extends JFrame { public Textures() { initUI(). 120). We create a TexturePaint class out of the buffered image. } public static void main(String[] args) { SwingUtilities. setTitle("Textures"). new Rectangle(0. 15. setDefaultCloseOperation(JFrame. We fill a rectangle with a texture. 15. setLocationRelativeTo(null).EXIT_ON_CLOSE). 60)). g2d.read(this.getResource("slate. 90.getClass().invokeLater(new Runnable() { @Override public void run() { Textures ex = new Textures(). slate = ImageIO. setSize(360. add(dpnl). ex. g2d. 60). } public final void initUI() { DrawPanel dpnl = new DrawPanel(). 90.} } g2d. Figure: Textures 336 .fillRect(250. } } In the code example. } }).setVisible(true).setPaint(slatetp). we fill three rectangles with three different textures. slatetp = new TexturePaint(slate. 60).png")). setPaint(gp4).black. 20.black.Color. 300.awt.GradientPaint.setPaint(gp1). javax. 5. true).JFrame.setPaint(gp2).fillRect(20. 260. Color. Color. GradientPaint gp3 = new GradientPaint(5.green. 140.awt.fillRect(20.fillRect(20.Graphics. class DrawPanel extends JPanel { private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g. 25.SwingUtilities. Color. Color. g2d.yellow. g2d. 20.paintComponent(g).awt. true). g2d. GradientPaint gp2 = new GradientPaint(5. 40). Color. GradientPaint gp1 = new GradientPaint(5. 0. 2.swing. Color.JPanel. 2. g2d.orange. javax. g2d. 40). In 2D drawing programs and paint programs. java.swing. Color.setPaint(gp3). 300. Color. true). } @Override public void paintComponent(Graphics g) { super.Gradients In computer graphics. gradient is a smooth blending of shades from light to dark or from one color to another. 15.black.awt. GradientPaint gp4 = new GradientPaint(25. true). Color.black. 2.zetcode. g2d. GradientPaint gp5 = new GradientPaint(0. 20.setPaint(gp5). true).fillRect(20. java. (answers.Graphics2D. import import import import import import import java.com) package com. java.red.blue. 337 .swing. 300. 25. javax. doDrawing(g).fillRect(20. 200. 25. 20. g2d. 40). g2d. g2d. gradients are used to create colorful backgrounds and special effects as well as to simulate lights and shadows. 25. Color. g2d. 20. 0. 80.black. 300. 300. 40). 40). To work with gradients. g2d. setTitle("Gradients").invokeLater(new Runnable() { @Override public void run() { GradientsExample ex = new GradientsExample(). setLocationRelativeTo(null).blue.black. 25. } }). } } Our code example presents five rectangles with gradients.setPaint(gp5).} } public class GradientsExample extends JFrame { public GradientsExample() { initUI(). 15. 338 . setSize(350. Color. } public final void initUI() { DrawPanel dpnl = new DrawPanel(). we use Java Swing's GradientPaint class. By manipulating the color values and the starting end ending points. true). add(dpnl).EXIT_ON_CLOSE). The gradient is activated calling the setPaint() method. Color. setDefaultCloseOperation(JFrame. 350).setVisible(true). } public static void main(String[] args) { SwingUtilities. ex. 25. GradientPaint gp4 = new GradientPaint(25. we can get different results. VALUE_RENDER_QUALITY). 30). nor the gilded monuments". Font.KEY_RENDERING.setFont(font).JFrame.swing. We specify the string we want to draw and the position of the text on the window area.Graphics2D. g2d.VALUE_ANTIALIAS_ON).swing.awt. javax.Graphics.setRenderingHints(rh).awt. g2d. package com.put(RenderingHints.BOLD. java.awt.drawString("Not marble. 339 .zetcode. RenderingHints.Figure: Gradients Drawing text Drawing is done with the drawString() method.RenderingHints. 20.Font. javax.awt. 21). class DrawPanel extends JPanel { private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g. import import import import import import import java. RenderingHints. java.KEY_ANTIALIASING.SwingUtilities.JPanel.swing. javax. rh. RenderingHints rh = new RenderingHints( RenderingHints. Font font = new Font("URW Chancery L". g2d. java. 20. } public static void main(String[] args) { SwingUtilities. g2d.drawString("You live in this. } } 340 . doDrawing(g).". 20.setVisible(true). 300). setSize(500.invokeLater(new Runnable() { @Override public void run() { DrawingText ex = new DrawingText(). ex. 20. g2d.drawString("Than unswept stone. your praise shall still " + "find room".drawString("'Gainst death. } }). g2d. add(dpnl). nor war's quick " + "fire shall burn".paintComponent(g). 20. setLocationRelativeTo(null). 20. 210). 270). 20. @Override public void paintComponent(Graphics g) { super.".drawString("Even in the eyes of all posterity". 20. 420).". 20.drawString("Nor Mars his sword. 20.". 390). 20. } } public class DrawingText extends JFrame { public DrawingText() { initUI(). setTitle("Sonnet55"). 470).".drawString("That wear this world out to the ending doom.drawString("So. 20. g2d.EXIT_ON_CLOSE). 360). g2d. besmear'd with sluttish time.drawString("But you shall shine more bright in these contents". 330). 240). and dwell in lovers' eyes. and all oblivious enmity". g2d.".drawString("When wasteful war shall statues overturn. g2d.". 60). 150). g2d. till the judgment that yourself arise. 180).drawString("Of princes.drawString("And broils root out the work of masonry. 20. g2d. shall outlive this powerful rhyme. } public final void initUI() { DrawPanel dpnl = new DrawPanel(). setDefaultCloseOperation(JFrame. g2d.drawString("The living record of your memory.20. g2d. g2d.". 120). } g2d. 90).drawString("Shall you pace forth. awt. java. We apply a technique called antialiasing. An image is an array of pixels.In our example. nor the gilded monuments". package com.SwingUtilities. public DrawPanel() { loadImage(). setPreferredSize(dm).Graphics2D.getImage().setRenderingHints(rh).JPanel.ImageIcon. import import import import import import import import java. g2d.setFont(font). } private void loadImage() { img = new ImageIcon("slanec.zetcode. RenderingHints. class DrawPanel extends JPanel { Image img. We choose a specific font for our text.VALUE_RENDER_QUALITY). Each pixel represents a color at a given position. RenderingHints. Images On of the most important capabililies of a toolkit is the ability to display images.VALUE_ANTIALIAS_ON).awt.awt. Font. 30).drawString("Not marble. javax. that draws the text. javax.swing. 21).Image. Font font = new Font("URW Chancery L". java.png"). We can use components like JLabel to display an image.Graphics. This code is to make our text look better. rh.swing. we draw a sonnet on the panel component. Dimension dm = new Dimension(img. 20.put(RenderingHints.Dimension. javax.swing. javax.JFrame.getHeight(null)). java. or we can draw it using the Java 2D API.BOLD.KEY_RENDERING. img. g2d. RenderingHints rh = new RenderingHints( RenderingHints.getWidth(null). This is the code.swing. } private void doDrawing(Graphics g) { 341 .KEY_ANTIALIASING. g2d.awt. } In the constructor of the DrawPanel class. 0. img. null). } public final void initUI() { DrawPanel dpnl = new DrawPanel(). } } public class ImageExample extends JFrame { public ImageExample() { initUI().drawImage(img. } g2d.png"). setTitle("Image"). } public static void main(String[] args) { SwingUtilities. setLocationRelativeTo(null). public DrawPanel() { loadImage(). } } This example will draw an image on the panel. private void loadImage() { img = new ImageIcon("slanec.invokeLater(new Runnable() { @Override public void run() { ImageExample ex = new ImageExample(). Dimension dm = new Dimension(img. We determine the image dimensions and set the preffered size of the panel component.Graphics2D g2d = (Graphics2D) g. } }). setDefaultCloseOperation(JFrame. } 342 . ex. This will together with the pack() method display the image to fit exactly the window. pack(). 0.getImage(). doDrawing(g). The image will fit the JFrame window.getHeight(null)). add(dpnl).setVisible(true). we call the loadImage() method.paintComponent(g). @Override public void paintComponent(Graphics g) { super.getWidth(null).EXIT_ON_CLOSE). setPreferredSize(dm). event.JPanel.swing. 343 . For example. We must not use a layout manager. g2d. We use the ImageIcon class.MouseAdapter. 0. we will create a resizable component.JFrame. In order to create a component that can be freely dragged over a panel. package resizablecomponent. In order to distinguish which component has a focus.awt. JPanel area = new JPanel(). private Resizable resizer. that the component has focus.awt.Color. import import import import java. import javax. The chart can be moved over the grid widget of the application and resized.The method loads an image from the disk.java */ public class ResizableComponent extends JFrame { private JPanel panel = new JPanel(null). import javax.img. /* ResizableComponent. where we can draw the component and start resizing. I have learnt to use resizable components from this blog. we will create a component (a JPanel) that we can freely move over a parent window and resize. The most common resizable component is a chart in a spreadsheet application. Resizable components in Java Swing In this part of the Java Swing tutorial. we did some painting.Dimension.drawImage(this.event.swing. In this chapter. The rectangles serve as a dragging points.MouseEvent. Resizable component Resizable components are most often used when creating charts. we draw 8 small rectangles on the border of our resizable component.awt. we need a panel with absolute positioning enabled. java. This way we know. 0. The image is drawn using the drawImage() method. public ResizableComponent() { add(panel).awt. In our example. java. null). This class simplyfies the work with the images in Java Swing. java. diagrams and similar. when we create a chart in a OpenOffice application. import javax. we create a panel with absolute positioning. setLocationRelativeTo(null). } }).awt. setDefaultCloseOperation(JFrame. 50.repaint().awt. 300)). addMouseListener(new MouseAdapter() { public void mousePressed(MouseEvent me) { requestFocus(). 150). resizer. } }). We must use absolute positioning for resizable component.Border.SwingConstants.MouseEvent.Graphics. that we cannot use any layout manager.EXIT_ON_CLOSE).setVisible(true). We have already mentioned. 200. } public static void main(String[] args) { ResizableComponent rc = new ResizableComponent().Cursor. setSize(new Dimension(350.Component.awt.swing. setTitle("Resizable Component"). resizer. java. java.setBackground(Color. resizer = new Resizable(area). java. we grab focus and repaint the component.swing.repaint().awt. java. java. } } The ResizableComponent sets up the panel and the component.add(resizer). panel.area.border. 344 .white).awt. The rectangles over he border will disappear. rc.awt.awt.setBounds(50. resizer.event. java. If we press on the parent panel. private JPanel panel = new JPanel(null). addMouseListener(new MouseAdapter() { public void mousePressed(MouseEvent me) { requestFocus(). import import import import import import import java. By providing null to the constructor. package resizablecomponent. import javax.Color.g outside the resizable component. e.Insets.Rectangle. width . int locations[] = { SwingConstants. rect. Cursor.dist / 2. rect.setColor(Color. Cursor. dist. case SwingConstants. Cursor. dist). SwingConstants. i < locations. Cursor. y + h . int x.height . w .width .1. i++) { Rectangle rect = getRectangle(x. g. y. Cursor. SwingConstants.NORTH.black). rect.y.WEST: return new Rectangle(x. Cursor.WHITE). g. dist.SOUTH: return new Rectangle(x + w / 2 . 345 .NORTH_WEST.SE_RESIZE_CURSOR }.// ResizableBorder.SOUTH.setColor(Color. SwingConstants.W_RESIZE_CURSOR.dist = dist.NW_RESIZE_CURSOR. int y. dist).setColor(Color.y.E_RESIZE_CURSOR. public ResizableBorder(int dist) { this. int h) { g.hasFocus()) { for (int i = 0. SwingConstants. } public boolean isBorderOpaque() { return false. g. g.x. locations[i]).NORTH_EAST.SW_RESIZE_CURSOR. w. rect. int cursors[] = { Cursor.java public class ResizableBorder implements Border { private int dist = 8. h. y + h / 2 . rect.N_RESIZE_CURSOR.1).dist.WEST.1). } public void paintBorder(Component component. g. case SwingConstants.NORTH: return new Rectangle(x + w / 2 . } } { } private Rectangle getRectangle(int x. int y.NE_RESIZE_CURSOR.drawRect(x + dist / 2. if (component.x.length. h . y.SOUTH_EAST }. dist. dist. dist). dist). SwingConstants. int location) switch (location) { case SwingConstants. Cursor. SwingConstants.SOUTH_WEST.1. SwingConstants.height . Graphics g. dist. } public Insets getBorderInsets(Component component) { return new Insets(dist.dist).dist / 2.dist / 2. int h.BLACK).S_RESIZE_CURSOR.fillRect(rect. y + dist / 2.dist. rect.EAST. int w.drawRect(rect. int w. x. y. g.SOUTH. dist). y.dist.NORTH_EAST.width .MOVE_CURSOR.dist. h.contains(me. g. rect. The ResizableBorder is responsible for drawing the border of the component and determining the type of the cursor to use. y.WHITE).dist.setColor(Color. y + h .drawRect(rect. dist. dist. w . y + h . h.SOUTH_WEST. SwingConstants. we draw the border of the resizable component. SwingConstants. g. if (rect. i < locations. g. w. g.dist.NORTH_WEST. rect.width . h .NORTH_WEST: return new Rectangle(x.hasFocus()) { for (int i = 0. rect.1). SwingConstants. y + dist / 2. int h = c. case SwingConstants. In the paintBorder() method. SwingConstants.drawRect(x + dist / 2.length. locations[i]).black). The upper code draws the outer border of the component. where we can grab the component and resize it. rect. dist.1.y.WEST. These are locations. public int getCursor(MouseEvent me) { Component c = me.length. i < locations.} case SwingConstants. g. locations[i]).getComponent(). int w = c. dist). } return null. SwingConstants.dist). i++) { Rectangle rect = getRectangle(x.height .NORTH.getWidth(). int locations[] = { SwingConstants. rect.EAST. dist). dist). dist.1).EAST: return new Rectangle(x + w .dist.x. if (component. SwingConstants.getHeight().dist / 2. dist). rect. case SwingConstants. 346 .setColor(Color.getPoint())) return cursors[i]. w.fillRect(rect. y + h / 2 .setColor(Color. } } } return Cursor. SwingConstants.SOUTH_EAST }. for (int i = 0.y. where we will draw rectangles.SOUTH_WEST: return new Rectangle(x. case SwingConstants. case SwingConstants. dist.height .dist.SOUTH_EAST: return new Rectangle(x + w . 0. i++) { Rectangle rect = getRectangle(0.1.NORTH_EAST: return new Rectangle(x + w .BLACK). These locations are grabbing points. ResizableBorder border) { setLayout(new BorderLayout()).event.Rectangle.MouseInputListener. private Point startPos = null.awt. java.event.getPredefinedCursor(border.awt.MouseInputAdapter.event.swing. } } MouseInputListener resizeListener = new MouseInputAdapter() { public void mouseMoved(MouseEvent me) { if (hasFocus()) { ResizableBorder border = (ResizableBorder)getBorder().awt. public void mousePressed(MouseEvent me) { ResizableBorder border = (ResizableBorder)getBorder().awt. } public Resizable(Component comp.getDefaultCursor()). addMouseListener(resizeListener).Cursor. java.JComponent.awt. java. } private int cursor.} } The eight rectangles are drawn only in case that the resizable component has currently focus. add(comp). import import import import import import java.Point. import javax. setBorder(border). import javax.swing. java.getCursor(me))). addMouseMotionListener(resizeListener). import javax.MouseEvent. // Resizable. setCursor(Cursor. java. Finally.BorderLayout. } private void resize() { if (getParent() != null) { ((JComponent)getParent()). package resizablecomponent.swing. new ResizableBorder(8)). the getRectangle() method gets the coordinates of the rectangles and the getCursor() methods gets the cursor type for the grab point in question.Component.java public class Resizable extends JComponent { public Resizable(Component comp) { this(comp.revalidate().awt. 347 . } } public void mouseExited(MouseEvent mouseEvent) { setCursor(Cursor. w . h . resize(). int dy = me. resize().dy). requestFocus().dy < 50)) { setBounds(x. getWidth(). y. } break. getY(). } public void mouseDragged(MouseEvent me) { if (startPos != null) { int int int int x y w h = = = = getX(). resize().dy < 50)) { setBounds(x. int dx = me. w + dx. case Cursor. w.getCursor(me). startPos. y. y + dy. switch (cursor) { case Cursor.dx. resize(). } break.dx < 50)) { setBounds(x + dx.startPos. case Cursor. w.dx. startPos = new Point(me. startPos = me.NW_RESIZE_CURSOR: if (!(w . case Cursor. 348 .dy < 50)) { setBounds(x + dx. case Cursor. y. getHeight().getY() .getX(). h). h + dy).cursor = border.getPoint().y.dy). resize().dy).N_RESIZE_CURSOR: if (!(h .startPos.y).getPoint().E_RESIZE_CURSOR: if (!(w + dx < 50)) { setBounds(x. startPos = me. resize().S_RESIZE_CURSOR: if (!(h + dy < 50)) { setBounds(x.dx < 50) && !(h .NE_RESIZE_CURSOR: if (!(w + dx < 50) && !(h . } break. y + dy. w . repaint(). h). case Cursor. h . h .x. startPos = me.W_RESIZE_CURSOR: if (!(w . } break. y + dy. } break.getPoint(). w + dx.getX() . w + dx. } } } public void mouseReleased(MouseEvent mouseEvent) { startPos = null. setCursor(Cursor.getPredefinedCursor(border.revalidate().getCursor(me))). y. } } The resize() method is called. w . case Cursor. The Resizable class represents the component. startPos = me.getPredefinedCursor(cursor)). } break.SW_RESIZE_CURSOR: if (!(w . h + dy). bounds.MOVE_CURSOR: Rectangle bounds = getBounds(). MouseInputListener resizeListener = new MouseInputAdapter() { public void mouseMoved(MouseEvent me) { if (hasFocus()) { ResizableBorder border = (ResizableBorder)getBorder(). startPos = new Point(startPos.dx.getPoint(). The revalidate() method will cause the component to be redrawn.x. h + dy). resize().dx < 50) && !(h + dy < 50)) { setBounds(x + dx. dy). resize(). after we have resized the component. } setCursor(Cursor.} break. } }. y. setBounds(bounds). case Cursor. } break. resize().SE_RESIZE_CURSOR: if (!(w + dx < 50) && !(h + dy < 50)) { setBounds(x. me. that is being resized and moved on the window. private void resize() { if (getParent() != null) { ((JComponent)getParent()).getY()).translate(dx. case Cursor. } } 349 . } If we click on the resizable component. startPos = me.getCursor(me).dy < 50)) { setBounds(x.startPos. repaint(). 350 . that the component is not smaller than 50 px. we could make it so small.y.dy). int dy = me.getPoint(). give focus to the component and redraw it. Otherwise. getWidth().getX() . The cursor type changes only if the component has focus. y + dy. getY(). y coordinates of the cursor. requestFocus(). that we would eventually hide the component. resize(). get the starting point of dragging. The setBounds() method relocates and resizes the component. h . w.startPos. we determine the x. that we make during the mouse drag event. case Cursor.getY() . cursor = border. we change the cursor. int dx = me. when we hover the cursor over the grip points. getHeight(). int int int int x y w h = = = = getX(). We calculate the distances. For all resizing we ensure. } break.We change the cursor type.N_RESIZE_CURSOR: if (!(h .x. width and height of the component. public void mousePressed(MouseEvent me) { ResizableBorder border = (ResizableBorder)getBorder(). In the mouseDragged() method. java.swing.swing. {6.awt. public class Puzzle extends JFrame implements ActionListener { private JPanel centerPanel.JPanel. javax.awt. The goal is to form the picture. 4.event.swing. java.image.image. javax. javax. import import import import import import import import import import import import import import import java. } public final void initUI() { pos = new int[][]{ {0.ActionEvent. java.JButton. {9.BorderLayout. package zetcode. private JButton button. 7. public Puzzle() { initUI().swing.Figure: Resizable component In this part of the Java Swing tutorial. 11} }.ImageIcon. javax. height. private JLabel label.JFrame. 8}.awt. 10. java. The Puzzle in Java Swing In this chapter. 2}. int[][] pos.Box. 351 . 1. int width.swing. java. private Image source.swing. It is cut into 12 pieces. {3.GridLayout.awt. java. we have created a resizable component.awt.event.JLabel. javax.awt.CropImageFilter.swing. 5}. javax. private Image image.ActionListener.SwingUtilities.awt. javax.awt. Puzzle We have an image of a Sid character from the Ice Age movie.FilteredImageSource.Dimension. java. we will create a simple puzzle game in Java Swing toolkit.Image. setResizable(false).setLayout(new GridLayout(4.remove(buttonIndex).getX().jpg")). setLocationRelativeTo(null).add(label). add(centerPanel. centerPanel.buttonY) == size.add(button). button. BorderLayout. int int int int int int int labelX = label. labelY = label. 352 .DISPOSE_ON_CLOSE). height / 4))).setIcon(new ImageIcon(image)). } public void actionPerformed(ActionEvent e) { JButton button = (JButton) e. setTitle("Puzzle"). for (int i = 0. Dimension size = button. add(Box. 275). } else { button = new JButton(). 0)). buttonY = button.class. source = sid. button. buttonPosX = buttonX / size. image = createImage(new FilteredImageSource(source.getSource().getResource("icesid. width = sid. centerPanel.getIconWidth(). buttonX = button. j++) { if (j == 2 && i == 3) { label = new JLabel(""). i < 4. (width / 3) + 1. i++) { for (int j = 0. setDefaultCloseOperation(JFrame.centerPanel = new JPanel(). BorderLayout.CENTER).getImage().getSource().getSize().getY(). 0. buttonPosY = buttonY / size. if (labelX == buttonX && (labelY .NORTH). new CropImageFilter(j * width / 3. 4. centerPanel.getIconHeight(). height = sid. buttonIndex = pos[buttonPosY][buttonPosX]. ImageIcon sid = new ImageIcon(Puzzle.getX().height) { int labelIndex = buttonIndex + 3. 5)).createRigidArea(new Dimension(0.width.addActionListener(this). } } } setSize(325.getY(). j < 3. i * height / 4. centerPanel.height. 3. centerPanel.setVisible(true). if (labelX == buttonX && (labelY .add(label. 4.add(button. {3. puzzle. centerPanel. centerPanel.remove(buttonIndex).width) { int labelIndex = buttonIndex . int[][] { 1.remove(labelIndex).1.buttonX) == -size. 10.height) { int labelIndex = buttonIndex . centerPanel. buttonIndex). centerPanel. The goal of this little game is to form the original picture. centerPanel.validate(). labelIndex).remove(buttonIndex).buttonY) == -size. labelIndex).invokeLater(new Runnable() { public void run() { Puzzle puzzle = new Puzzle(). buttonIndex). 5}.add(button. } if (labelY == buttonY && (labelX . centerPanel.add(button.validate().add(label. centerPanel. labelIndex). {9. buttonIndex).validate(). We move the buttons by clicking on them. 353 . }. } } public static void main(String[] args) { SwingUtilities. pos = new {0.} centerPanel. 11} These are the positions of the image parts.add(label. centerPanel. 2}. centerPanel. labelIndex). centerPanel. Only buttons adjacent to the label can be moved. } } } }). labelIndex). centerPanel. centerPanel.add(label.validate(). 7. {6. 8}.add(button.buttonX) == size.width) { int labelIndex = buttonIndex + 1. } if (labelY == buttonY && (labelX . centerPanel. centerPanel. centerPanel. We crop the image into pieces and place them on the buttons. labelY = label. height / 4))). int buttonPosY = buttonY / size.width. buttonX = button. 354 . } In this case. } else { button = new JButton(). The x. (width / 3) + 1. button. new CropImageFilter(j * width / 3. int buttonPosX = buttonX / size. source = sid. We use the ImageIcon class to load the image. y coordinates of the button that we hit and an empty label. centerPanel. button. i++) { for (int j = 0.labelIndex). centerPanel.addActionListener(this). y coordinates are important in the logic of the program. buttonIndex). i < 4.remove(buttonIndex). int int int int labelX = label.buttonY) == size. if (labelX == buttonX && (labelY . that is right above the empty label.jpg")). j < 3. centerPanel.height is true. If the button is right above the label. centerPanel.ImageIcon sid = new ImageIcon(Puzzle.class. buttonY = button.getImage().add(button). they share the x coordinate.getX(). We get the x. j++) { if (j == 2 && i == 3) { label = new JLabel(""). int buttonIndex = pos[buttonPosY][buttonPosX]. image = createImage(new FilteredImageSource(source.add(label.getResource("icesid.buttonY) == size.height. the equation (labelY .validate(). } } } The code creates 11 buttons and one label.getY().getX().getSource(). we check if we clicked on the button. If it is above the label.add(button.add(label). for (int i = 0.height ) { int labelIndex = buttonIndex + 3. Here we get the index of the button in the two dimensional array of the button positions.getY().setIcon(new ImageIcon(image)). i * height / 4. Tetris The Tetris game is one of the most popular computer games ever created. Z-shape. so that they fit as much as possible. In this game. Tetris In this chapter. Tetris is available on almost every computer platform in lots of variations. Figure: Tetrominoes The development We do not have images for our Tetris game. 355 . we will create a Tetris game clone in Java Swing. Since then. We play the Tetris game until we top out. Behind every computer game. The object of the Tetris game is to move and rotate the shapes. Each of these shapes is formed with four squares. MirroredL-shape and a Square-shape. Tetris is called a falling block puzzle game.Figure: Puzzle This was a puzzle game. the row is destroyed and we score. there is a mathematical model. Line-shape. S-shape. The shapes are falling down the board. T-shape. The original game was designed and programmed by a Russian programmer Alexey Pajitnov in 1985. L-shape. If we manage to form a row. we draw the tetrominoes using Swing drawing API. we have seven different shapes called tetrominoes. So it is in Tetris. setSize(200. after it is launched.SOUTH). BorderLayout.swing. Board board = new Board(this). no acceleration is implemented.swing.zetcode.swing. that we have removed.java package com.start(). 400). board.invokeLater(new Runnable() { @Override public void run() { 356 . We can pause the game by pressing the p key. The space key will drop the Tetris piece immediately to the bottom. } private void initUI() { statusbar = new JLabel(" 0"). The d key will drop the piece one line down.awt. public static void main(String[] args) { SwingUtilities.) The game goes at constant speed. setLocationRelativeTo(null). import javax.BorderLayout.Some ideas behind the game. (It can be used to speed up the falling a bit.SwingUtilities. add(statusbar.JLabel. The score is the number of lines. } public JLabel getStatusBar() { } return statusbar. Tetris.JFrame. public class Tetris extends JFrame { private JLabel statusbar. • • • • 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. setTitle("Tetris"). import javax. setDefaultCloseOperation(EXIT_ON_CLOSE). add(board). public Tetris() { initUI(). import java. The game starts immediately. so that it is easier to understand. import javax. { 0.Tetris game = new Tetris(). We create a statusbar. { 0. -1. { 0. { 1. 0 }. SquareShape. ZShape. public Shape() { coords = new int[4][2]. 1 } }. 0. -1 }. -1 }. } public void setShape(Tetrominoes shape) { coordsTable = new int[][][] { { { 0. 0 }. 1 } }. { 0. { { -1. -1 }. Immediately. { 0. -1 }. import java.util. 1 } } 357 .start().java package com. 1 } }. -1 }. { { 0. -1 }. { 0. j < 2. 0 }. -1 }. 0 }. LShape. { { 0. 0. We create a board on which we play the game.java file. 0 } }. for (int i = 0. { 1. { 0. i++) { for (int j = 0. 0. private Tetrominoes pieceShape. 1 } }. { 1. 1 }. 1. { { 1. { 0. game. TShape. { { 0. The start() method starts the Tetris game. 0 }. { 0. private int coords[][]. 0. 0 }. 0 }. we set up the game. { 0. coords[i][j] = coordsTable[shape. { -1. public class Shape { protected enum Tetrominoes { NoShape. 0 }. }).ordinal()][i][j]. 0 }. 0 }. board. 1. { 0. i < 4 . private int[][][] coordsTable. 0 }. ++j) { } } } pieceShape = shape. { { 0. } } } In the Tetris. { 0.zetcode. 0 }. 1 }. 0 }. 2 } }.Random. MirroredLShape }.NoShape). 0 }. 1 } }. 0 }. Shape.setVisible(true). setShape(Tetrominoes. LineShape. after the window appears on the screen. SShape. }. { { { { { { { { 0. { { -1. } coords[index][0]. ++i) { result. for (int i = 0. result. int public int x(int index) { return public int y(int index) { return public Tetrominoes getShape() { public void setRandomShape() { x) { coords[index][0] = x.private void setX(int index.SquareShape) return this. } } return result.SquareShape) return this. for (int i=0. i < 4. public int minX() { int m = coords[0][0]. i < 4. coords[i][1]). -x(i)).setX(i. Shape result = new Shape(). i < 4.nextInt()) % 7 + 1. i++) { m = Math. int x = Math. } } return m. setShape(values[x]). public Shape rotateRight() { if (pieceShape == Tetrominoes. } y) { coords[index][1] = y.setY(i.values().pieceShape = pieceShape. int private void setY(int index. } coords[index][1]. } } Random r = new Random(). 358 .min(m. i++) { } } public Shape rotateLeft() { if (pieceShape == Tetrominoes. return m. m = Math. result. y(i)). for (int i=0.min(m.abs(r. coords[i][0]). public int minY() { int m = coords[0][1]. } return pieceShape. Tetrominoes[] values = Tetrominoes. -1 }. 0 }. 1 } }. The Tetrominoes enum holds all seven Tetris shapes. result. { 0. { { -1. ZShape. }. { 0. setShape(Tetrominoes. -1 }. -1. 1 } }. { 0. { 1. 0 }. -1 }.NoShape). ++i) { result. 1 } }. { 0. { { 0. The Shape class provides information about a Tetris piece. { 0. { { 0. { 0. Here we put one row of the coordinate values from the coordsTable to a coords array of a Tetris piece. 0 }. SShape. 0 }. LShape. -1 }. 0 }. i < 4 . ++j) { } } coords[i][j] = coordsTable[shape. { 0. 1 } }. LineShape. { 0. 0. { { 1. 0 }. { 1. } This is the constructor of the Shape class. -1 }. { 0. 2 } }. public Shape() { coords = new int[4][2]. 0. } } } return result. 0 }. for (int i = 0. SquareShape. { 1. { 0.Shape result = new Shape(). 0 }. 1. protected enum Tetrominoes { NoShape. 1 } }. for (int i = 0.pieceShape = pieceShape. 1. 1 }. i < 4. MirroredLShape }. 1 } } The coordsTable array holds all possible coordinate values of our Tetris pieces. 0 }. { { { { { { { { 0. 1 }.ordinal()][i][j]. coordsTable = new int[][][] { { { 0.setY(i. 0. -1 }. TShape. an enum type is esencially an 359 . { -1. x(i)). Plus the empty shape called here NoShape. -y(i)). 0 } }. result. { 0. 0 }. j < 2. { 0. 0 }. Note the use of the ordinal() method. The coords array holds the actual coordinates of a Tetris piece. In C++. { { 0. { { 0. This is a template from which all pieces take their coordinate values. 0. 0 }. 0 }. i++) { for (int j = 0. 0 }. -1 }. 0 }.setX(i. { { -1. for (int i = 0. result. For example. { -1.swing.Dimension.JLabel. The following image will help understand the coordinate values a bit more. java.pieceShape = pieceShape. y(i)). { -1.zetcode.swing. java.JPanel.awt.awt.setY(i. Figure: Coordinates public Shape rotateLeft() { if (pieceShape == Tetrominoes.ActionEvent.Graphics.setX(i. javax. -x(i)). Unlike in C++.awt. This code rotates the piece to the left. java. } } return result.Color. The following diagram illustrates the shape.ActionListener. i < 4. Shape result = new Shape().java package com. Java enums are full classes. javax.SquareShape) return this.awt. And the ordinal() method returns the current position of the enum type in the enum object. Looking at the previous image will help to understand the rotation.awt. That's why we simply return the reference to the current object.KeyAdapter. javax. 0 }. import import import import import import import import import import java.KeyEvent. 360 . numbers { 0. represent a rotated S-shape.event. -1 }.event. Board.integer. java. ++i) { result.awt. java.event. -1 } .Timer. The square does not have to be rotated. result. java.awt.event. { 0.swing. The coords array saves the coordinates of the Tetris piece. 0 }. clearBoard(). newPiece().start(). private private private private private private private private private private Timer timer. public Board(Tetris parent) { initBoard(parent). board = new Tetrominoes[BoardWidth * BoardHeight]. addKeyListener(new TAdapter()). int y) { return board[(y * BoardWidth) + x]. } else { } } private int squareWidth() { return (int) getSize(). JLabel statusbar. } public void start() if (isPaused) { oneLineDown(). Shape curPiece.Tetrominoes.Shape. Tetrominoes[] board. boolean isStarted = false. curPiece = new Shape(). boolean isFallingFinished = false. private final int BoardHeight = 22. int numLinesRemoved = 0.getWidth() / BoardWidth.import com. } private void initBoard(Tetris parent) { setFocusable(true). } private Tetrominoes shapeAt(int x. boolean isPaused = false.getStatusBar(). public class Board extends JPanel implements ActionListener { private final int BoardWidth = 10. } @Override public void actionPerformed(ActionEvent e) { if (isFallingFinished) { isFallingFinished = false.zetcode. 361 . } private int squareHeight() { return (int) getSize(). statusbar = parent. int curX = 0. int curY = 0. timer = new Timer(400.getHeight() / BoardHeight. timer. this). drawSquare(g. ++i) { int x = curX + curPiece. statusbar. newPiece(). isPaused = !isPaused. clearBoard(). statusbar. { } private void pause() if (!isStarted) return.1) * curPiece.stop().valueOf(numLinesRemoved)). i < BoardHeight. i < 4.i . timer. if (shape != Tetrominoes.1). 0 + x * squareWidth(). } } if (curPiece. j < BoardWidth.NoShape) drawSquare(g. numLinesRemoved = 0. 0 + j * squareWidth().y(i). int y = curY . } } 362 . for (int i = 0.curPiece. boardTop + i * squareHeight(). } else { timer.return.NoShape) { for (int i = 0. ++i) { for (int j = 0. } } repaint(). boardTop + (BoardHeight .getShape() != Tetrominoes.getShape()).y . isStarted = true. ++j) { Tetrominoes shape = shapeAt(j.x(i).getHeight() .BoardHeight * squareHeight().start(). if (isPaused) { timer. BoardHeight . squareHeight(). isFallingFinished = false.start(). shape). int boardTop = (int) size. private void doDrawing(Graphics g) { Dimension size = getSize().setText("paused").setText(String. curX.1 + curPiece. curY = BoardHeight .paintComponent(g). curY)) { curPiece.1)) pieceDropped().} @Override public void paintComponent(Graphics g) { super.curPiece. { } private void newPiece() curPiece. } } pieceDropped(). i < 4. if (!isFallingFinished) newPiece(). ++i) { int x = curX + curPiece. 363 . newY . board[(y * BoardWidth) + x] = curPiece. i < BoardHeight * BoardWidth. timer. } removeFullLines(). } private void pieceDropped() { for (int i = 0. if (!tryMove(curPiece.setShape(Tetrominoes.stop(). doDrawing(g).x(i). --newY.y(i). curX. curY .getShape(). curX.NoShape).setRandomShape(). curX = BoardWidth / 2 + 1.minY(). while (newY > 0) { if (!tryMove(curPiece. { private void oneLineDown() } if (!tryMove(curPiece.1)) break. } private void dropDown() { int newY = curY. private void clearBoard() { for (int i = 0.NoShape. ++i) board[i] = Tetrominoes. int y = curY . i >= 0. repaint(). } } private boolean tryMove(Shape newPiece. ++k) { for (int j = 0.setText(String. int newX. curPiece.1. } } 364 . j < BoardWidth.NoShape) { lineIsFull = false. } private void removeFullLines() { int numFullLines = 0. int newY) { for (int i = 0. repaint().valueOf(numLinesRemoved)).y(i). ++j) board[(k * BoardWidth) + j] = shapeAt(j. } } } if (numFullLines > 0) { numLinesRemoved += numFullLines. break.NoShape) return false.setShape(Tetrominoes. k < BoardHeight .NoShape). int y = newY .x(i). --i) { boolean lineIsFull = true. for (int k = i. statusbar. k + 1). ++i) { int x = newX + newPiece.newPiece. ++j) { if (shapeAt(j. isFallingFinished = true. if (shapeAt(x. j < BoardWidth. curX = newX. return true. for (int i = BoardHeight . } } if (lineIsFull) { ++numFullLines. statusbar. curY = newY. i < 4.isStarted = false.1.setText("game over"). if (x < 0 || x >= BoardWidth || y < 0 || y >= BoardHeight) return false. y) != Tetrominoes. } curPiece = newPiece. i) == Tetrominoes. for (int j = 0. g.getShape() == Tetrominoes. case KeyEvent. 102). 102. int keycode = e.drawLine(x + 1.private void drawSquare(Graphics g. Tetrominoes shape) Color colors[] = { new Color(102.1. y + squareHeight() .1). y + squareHeight() . squareHeight() .1. new Color(204. int x. 204.darker()). case KeyEvent. switch (keycode) { case KeyEvent. curX + 1. break.1. 204). new Color(218. y + 1).1.drawLine(x.1. break. 204. y.setColor(color). x + squareWidth() . return. g.1. g. y). 204). g. if (keycode == 'p' || keycode == 'P') { pause(). g.brighter()).VK_LEFT: tryMove(curPiece.VK_DOWN: tryMove(curPiece. }.fillRect(x + 1. y + squareHeight() .drawLine(x. } if (isPaused) return.1. 102. x + squareWidth() . g.setColor(color.1.NoShape) } return. y). new Color(0.2). curX. g.2.rotateRight(). 170. 102. curX . g. curY).getKeyCode(). 204). curY). int y. new Color(102. } class TAdapter extends KeyAdapter { @Override public void keyPressed(KeyEvent e) { { if (!isStarted || curPiece. 102). curX.drawLine(x + squareWidth() . y + 1.VK_UP: tryMove(curPiece.rotateLeft(). new Color(204.ordinal()]. y + squareHeight() . 365 .VK_RIGHT: tryMove(curPiece. new Color(102. squareWidth() . curY). 0. 0) { Color color = colors[shape. x. x + squareWidth() . 0). new Color(204. 102). case KeyEvent.setColor(color. 204. curY). break. } } } } Finally..start(). newPiece(). int numLinesRemoved = 0.. the timer calls the actionPerformed() method each 400ms. object fires one or more action events after a specified delay. the board has the keyboard input. timer. break.java file.. we have removed so far. From now. we have the Board. The curX and curY variables determine the actual position of the falling Tetris shape. The isFallingFinished variable determines. case 'd': oneLineDown(). break.. We initialize some important variables. boolean isFallingFinished = false. timer = new Timer(400. this). int curY = 0. } else { oneLineDown(). if the Tetris shape has finished falling and we then need to create a new shape. case KeyEvent. private private private private private private . We must explicitly call the setFocusable() method. In our case.break. . break.VK_SPACE: dropDown(). int curX = 0. boolean isStarted = false. The numLinesRemoved counts the number of lines. Timer @Override public void actionPerformed(ActionEvent e) { if (isFallingFinished) { isFallingFinished = false. boolean isPaused = false. case 'D': oneLineDown(). } } 366 . setFocusable(true). This is where the game logic is located. y . curPiece. i < BoardHeight. shape). newY . the piece is dropped to the bottom. We access it using the shapeAt() method. i < 4. int y = curY . if (shape != Tetrominoes.i . while (newY > 0) { if (!tryMove(curPiece. private void dropDown() { int newY = curY. If so.x(i).NoShape) drawSquare(g.NoShape. } } In the first step we paint all the shapes.getShape() != Tetrominoes.y(i). the falling Tetris piece goes one line down. drawSquare(g. BoardHeight . } 367 . ++i) { int x = curX + curPiece. If not. ++i) board[i] = Tetrominoes.1) * squareHeight(). we draw all objects on the board. The painting has two steps. All the squares are remembered in the board array. We simply try to drop the piece one line down until it reaches the bottom or the top of another fallen Tetris piece. for (int i = 0. --newY. curX. boardTop + i * squareHeight(). that have been dropped to the bottom of the board. } } In the second step. } } pieceDropped(). boardTop + (BoardHeight . we paint the actual falling piece. 0 + x * squareWidth(). 0 + j * squareWidth(). private void clearBoard() { for (int i = 0. or remains of the shapes.curPiece. ++i) { for (int j = 0. i < BoardHeight * BoardWidth. Inside the doDrawing() method. if (curPiece. If we press the space key.getShape()). j < BoardWidth.1)) break.The actionPerformed() method checks if the falling has finished. a new piece is created.NoShape) { for (int i = 0. ++j) { Tetrominoes shape = shapeAt(j.1). NoShape). The timer is stopped.stop(). curX. int y = newY .setText("game over"). isStarted = false. private void pieceDropped() { for (int i = 0. curX = BoardWidth / 2 + 1. board[(y * BoardWidth) + x] = curPiece. Then we create a new piece. We top out. We put game over string on the statusbar. } } The newPiece() method creates a new Tetris piece. ++i) { int x = curX + curPiece. } The pieceDropped() method puts the falling piece into the board array. y) != Tetrominoes. the board holds all the squares of the pieces and remains of the pieces that has finished falling. When the piece has finished falling.y(i). The piece gets a new random shape. Once again. if (!isFallingFinished) newPiece(). curY)) { curPiece. int y = curY . statusbar. int newY) { for (int i = 0.newPiece. curY = BoardHeight . timer.minY().y(i). private boolean tryMove(Shape newPiece.x(i). the game is over. This is the job of the removeFullLines() method.setRandomShape(). ++i) { int x = newX + newPiece.The clearBoard() method fills the board with empty NoShapes. if we can remove some lines off the board. it is time to check. i < 4. If we cannot move to the initial positions. } 368 . private void newPiece() { curPiece.setShape(Tetrominoes. } removeFullLines(). we try to create a new piece.curPiece. if (!tryMove(curPiece. This is later used at collision detection.getShape(). int newX.1 + curPiece.x(i). More precisely. Then we compute the initial curX and curY values. if (shapeAt(x. if (x < 0 || x >= BoardWidth || y < 0 || y >= BoardHeight) return false. i < 4.NoShape) return false. that the squares may be left floating above empty gaps.curPiece = newPiece. j < BoardWidth. y. This means. Every Tetris piece has four squares.drawLine(x. x.brighter()).NoShape) { lineIsFull = false. Notice. --i) { boolean lineIsFull = true. If there is at least one full line. ++j) { if (shapeAt(j. x + squareWidth() . we check if there is any full row among all rows in the board. y + squareHeight() .1. break. k < BoardHeight . g. Each of the squares is drawn with the drawSquare() method. This way we destroy the full line. y). it is removed. break. We control the game with a keyboard.1. for (int i = BoardHeight . The tryMove() method tries to move the Tetris piece. for (int j = 0.1.setColor(color. Similarly. After finding a full line we increase the counter. curY). i >= 0.drawLine(x. case KeyEvent. we use so called naive gravity. i) == Tetrominoes. } } } Inside the removeFullLines() method. g. The left and top sides of a square are drawn with a brighter color. curY = newY.1. repaint(). if it has reached the board boundaries or it is adjacent to the already fallen Tetris pieces. ++k) { for (int j = 0. } return true. for (int k = i. k + 1). Tetris pieces have different colors. that in our Tetris game. ++j) board[(k * BoardWidth) + j] = shapeAt(j. curX = newX. j < BoardWidth. g. curX . The method returns false. y). We move all the lines above the full row one line down. the bottom and right sides are drawn with darker colors. 369 . The control mechanism is implemented with a KeyAdapter.1. This is to simulate a 3D edge. } } if (lineIsFull) { ++numFullLines. int numFullLines = 0.VK_LEFT: tryMove(curPiece. This is an inner class that overrides the keyPressed() method. 370 . we try to move the falling piece one square to the left.If we press the left arrow key. Figure: Tetris This was the Tetris game. Standard Edition. GTK+ to create its widgets via the JNI. It uses the native GUI APIs like Winapi. we will introduce the Java SWT library and create our first programs. SWT The Standard Widget Toolkit (SWT) is a graphical widget toolkit for the Java programming language. Centering a window In the first example.swt.eclipse. It uses the native GUI APIs like Winapi. The tutorial is suitable for beginners and intermediate programmers.Java SWT tutorial This is Java SWT tutorial. It is an alternative to the Java Swing GUI toolkit provided by Sun Microsystems. org. About The Standard Widget Toolkit (SWT) is a graphical widget toolkit for the Java programming language. It is an alternative to the Java Swing GUI toolkit provided by Sun Microsystems. import import import import org.eclipse. Introduction to SWT In this part of the Java SWT programming tutorial.com 371 . org. /** * ZetCode Java SWT tutorial * * In this program.widgets.swt.swt. The purpose of this tutorial is to get you started with the Java SWT toolkit.graphics.eclipse.Shell. we will learn the basics of GUI programming with Java SWT library. SWT is written in Java.Display.zetcode. which is a part of the Java Platform. which is a part of the Java Platform. package com.swt. It was originally developed by IBM. It was originally developed by IBM. SWT is written in Java. Standard Edition.widgets. In this tutorial. Images used in this tutorial can be downloaded here. we create a simple window. we show a window in * the center of the screen * * @author jan bodnar * website zetcode.eclipse. GTK+ to create its widgets via the JNI.Rectangle. The window is centered on the screen.graphics. I used some icons from the tango icons pack of the Gnome project. org.Point. int nTop = (bds. These take a Display as a parent. while (!shell. The Shell represents a window. 200). shell.isDisposed()) { if (!display.p.p.* last modified June 2009 */ public class SWTApp { public SWTApp(Display display) { Shell shell = new Shell(display).readAndDispatch()) { display. Point p = shell. new SWTApp(display). } } } public void center(Shell shell) { Rectangle bds = shell.getSize(). } } This example shows a 250x200px window in the centre of the screen.setText("Center"). p.x) / 2.setSize(250. shell.height . The Display is the connection between the SWT and the underlying OS.x.sleep().open(). there are two important classes. shell. shell.setText("Center"). Top level window is created.getBounds(). p. 372 . center(shell). There are top level shells.width .dispose().y).setBounds(nLeft. Other shells are called secondary shells. nTop. shell. int nLeft = (bds. We set a title for the window. } public static void main(String[] args) { Display display = new Display(). In each SWT application. Shell shell = new Shell(display).y) / 2. The Display and the Shell.getDisplay(). display. It implements the event loop and provides information about the OS. After the application terminates. int nTop = (bds. Here we set a size for the shell/window.zetcode.swt.widgets. int nLeft = (bds. import org.isDisposed()) { if (!display. It is usually a GUI component.y) / 2.width . we release the OS resources. package com. The window is shown on the screen. A tooltip is a small rectangular window.eclipse.eclipse.x) / 2.shell.widgets. We calculate the left and top coordinates of the window. import org.Shell. If you work with more than one display.x. We instantiate our example program. shell. p. Display display = new Display(). We get the resolution of the screen.open(). Display is created.y). It is part of the help system of the application. which gives a brief information about an object.sleep().height . } } These lines start the event mainloop. display. p.p. you might need to call the getMonitor() method instead of getDisplay().dispose(). Rectangle bds = shell. nTop. 200).setBounds(nLeft. We set the shell's bounds.readAndDispatch()) { display.getDisplay(). while (!shell.setSize(250.swt. new SWTApp(display). Creating a Tooltip The second example will show a tooltip.getBounds(). 373 .Display. shell.p. com * last modified June 2009 */ public class SWTApp { public SWTApp(Display display) { Shell shell = new Shell(display). we show a tooltip * * @author jan bodnar * website zetcode. shell. shell.open().dispose().setToolTipText("This is a window").setLocation(300. new SWTApp(display). If we hover a mouse pointer over the area of the window.setToolTipText("This is a window"). 200).isDisposed()) { if (!display. shell. } } } public static void main(String[] args) { Display display = new Display(). while (!shell. 300). shell. This line creates a tooltip for the window. 374 . display.setSize(250. a tooltip pops up./** * ZetCode Java SWT tutorial * * In this program.sleep(). shell.readAndDispatch()) { display.setText("Tooltip"). shell. } } The example creates a window. When we press this button. } } } public void initUI() { Button quit = new Button(shell. /** * ZetCode Java SWT tutorial * * This example shows a button on a window. 300).eclipse. 375 . quit. 30).swt. org.SelectionAdapter.open(). package com.widgets. 50.eclipse.SelectionEvent. 200).swt. shell.events. shell.setLocation(300.zetcode. the application terminates.SWT.eclipse. * * @author jan bodnar * website zetcode.sleep().Button.eclipse.swt.setText("Button"). we will create a quit button. we terminate the * application. org.setText("Quit"). org.setBounds(50. public SWTApp(Display display) { shell = new Shell(display). quit.events. shell.com * last modified June 2009 */ public class SWTApp { private Shell shell. initUI().Display. while (!shell.swt.Shell.swt. 80.widgets. import import import import import import org. org. shell.PUSH).Figure: Tooltip Quit button In the last example of this section.swt. SWT.widgets.eclipse.readAndDispatch()) { display.isDisposed()) { if (!display. org.eclipse.setSize(250. * Clicking on the button. We set a label and the size for the button. When we click on the button.PUSH specifies the type of the button. This is a very common widget.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { shell. initUI().setText("Quit"). quit. 30).exit(0). We add a selection listener for the button. the widgetSelected() method is called. quit. } public static void main(String[] args) { Display display = new Display(). } }). Button widget is created. } We use a Button widget.exit(0).PUSH). System.setBounds(50.} quit. Inside this method.getDisplay(). } }). quit. Button quit = new Button(shell. The SWT. display. 80.getDisplay().dispose().dispose(). we release the OS resources and exit the application.dispose(). 50. new SWTApp(display). We delegate the creation of the user interface to the initUI() method.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { shell. 376 . image or both. System. The parent is the shell. SWT. It shows a text label. org.Figure: Quit button This section was an introduction to the Java SWT library. /** * ZetCode Java SWT tutorial * * In this program. When we design the GUI of our application. we position two * buttons using absolute coordinates * * @author jan bodnar * website zetcode.swt. use the absolute positioning only when you have a reason to do so. might not look OK on Mac.widgets.com * last modified June 2009 */ 377 .swt. Layout management In this chapter we will show how to lay out our widgets in windows or dialogs. we decide what widgets we will use and how we will organize those widgets in the application. org. There are a few situations. GridLayout and FormLayout.eclipse.widgets.eclipse. The size and the position of a widget do not change.zetcode. the programmer specifies the position and the size of each widget in pixels. we use specialized non visible widgets called layout containers. where we can use absolute positioning. import import import import org.Button.eclipse. you must redo your layout. we will mention absolute positioning.SWT. If you translate your application into another language.Shell.Display. For all these issues. In this chapter. In absolute positioning. package com.widgets. Changing fonts in your application might spoil the layout. Absolute positioning In most cases.swt. FillLayout. if you resize a window. RowLayout. and what looks OK on Linux.eclipse. To organize our widgets. org.swt. programmers should use layout managers. Applications look different on various platforms. shell. 200).setSize(250. Button button2 = new Button(shell. shell. 30). The setBounds() method does two things. } public static void main(String[] args) { Display display = new Display(). button2.setSize(80. 30).sleep(). First we size the button using the setSize() method. shell.open().setLocation(50. It also sizes the button. 378 . new SWTApp(display).readAndDispatch()) { display.setSize(80.setText("Button"). 80.setBounds(20. button1. button2. 80.setBounds(20. } } In our example. button2. 30). display. 100).PUSH). } } } public void initUI() { Button button1 = new Button(shell. button1.setText("Absolute"). we place two buttons on the window using absolute positioning. SWT.PUSH).public class SWTApp { private Shell shell. public SWTApp(Display display) { shell = new Shell(display). 100). width=80. SWT. y=50 coordinates. height=30.setLocation(300. 30). button2. It positions the button at x=20. button2.isDisposed()) { if (!display. initUI(). Here we do the same in two steps. shell. 300). Then we locate it on the window using the setLocation() method.setLocation(50. 50.setText("Button").dispose(). while (!shell. button1. 50. org. org.widgets. org.Display.Rectangle.swt.graphics. private Image castle.eclipse.eclipse. Device dev = shell.eclipse.getDisplay().widgets. org. shell.swt. shell. 379 .SWT.zetcode.swt. try { castle = new Image(dev.Device. public SWTApp(Display display) { shell = new Shell(display).eclipse.eclipse.swt.Image.swt. package com. org.layout.graphics.widgets.setLayout(new FillLayout()).eclipse.Shell. It makes them to be the same size.png"). org.com * last modified June 2009 */ public class SWTApp { private Shell shell.eclipse. "redrock.swt.Label. import import import import import import import import org.Figure: Absolute FillLayout manager The FillLayout manager lays out widgets in a single row or column. /** * ZetCode Java SWT tutorial * * This program demonstrates the FillLayout * manager * * @author jan bodnar * website zetcode.eclipse.setText("FillLayout").graphics.swt.FillLayout. org.swt. } } In our example. while (!shell. } } } public void initUI() { Label label = new Label(shell.width.png"). } @Override public void finalize() { System. } public static void main(String[] args) { Display display = new Display(). label.pack().out.readAndDispatch()) { display. display. try { castle = new Image(dev.setLayout(new FillLayout()).dispose().} catch(Exception e) { System.println(e. System. app.println("disposing"). shell. rect. System.out.println(e. System. 300).finalize().out. } initUI(). 380 .dispose(). } catch(Exception e) { System.println("Cannot load image"). SWT.out. label.setLocation(300.height). castle.exit(1).setSize(rect. } We load an image.getBounds().getMessage()).exit(1). "redrock.setImage(castle).out. shell. we use this manager to display an image.isDisposed()) { if (!display.println("Cannot load image"). shell. We set the FillLayout to be the layout class for the shell.IMAGE_PNG).getMessage()). System. Rectangle rect = castle.sleep(). shell. SWTApp app = new SWTApp(display).open(). widgets.Button. SWT.swt. package com.Display.layout. org. org.eclipse.eclipse. We set the image to the label widget. label. label.Rectangle rect = castle. We find out the size of the picture to resize the shell to exactly fit the image size.RowLayout.setImage(castle). /** * ZetCode Java SWT tutorial * * This program demonstrates the RowLayout * manager * * @author jan bodnar * website zetcode.swt. import import import import import import org.widgets.swt. org.widgets.IMAGE_PNG). 381 . shell.eclipse.eclipse. rect.height).eclipse. Figure: FillLayout RowLayout The RowLayout manager places all widgets either in one row or in one column.zetcode.swt.getBounds().setSize(rect.swt.eclipse.width.RowData.layout. Label label = new Label(shell.swt. org.SWT.com * last modified June 2009 */ public class SWTApp { private Shell shell.pack().Shell. org. setLayoutData(new RowData(80. Button button2 = new Button(shell. we create a row of three buttons. We create a row.readAndDispatch()) { display. rowLayout. 300). shell. button1. rowLayout. button1.marginRight = 5.setLocation(300.marginBottom = 10.setText("Button").setLayoutData(new RowData(80. } } } public void initUI() { RowLayout rowLayout = new RowLayout(SWT.setText("Button"). 30)).marginLeft = 5. while (!shell. shell. rowLayout. 382 .setLayout(rowLayout).sleep().PUSH). 30)). button3. shell.public SWTApp(Display display) { shell = new Shell(display). RowLayout rowLayout = new RowLayout(SWT.PUSH).isDisposed()) { if (!display.PUSH). rowLayout. new SWTApp(display).marginTop = 10.HORIZONTAL).dispose(). SWT. SWT.setText("Button").open(). initUI().marginTop = 10.setText("RowLayout").spacing = 10.pack(). SWT. } public static void main(String[] args) { Display display = new Display(). 30)).marginBottom = 10. display. rowLayout. Button button1 = new Button(shell. shell. shell. } } In our example.HORIZONTAL).setLayoutData(new RowData(80. button2. rowLayout. Button button3 = new Button(shell. button2. button3. rowLayout. 300). rowLayout.widgets.Text.swt.SWT.widgets.Display.marginRight = 5.Shell.swt. org.eclipse.widgets. org.pack().eclipse.swt.eclipse.swt.marginLeft = 5.layout. org. initUI().swt.swt. Figure: RowLayout manager Calculator In the following example. package com. We specify the row layout to be the layout for the shell.spacing = 10.com * last modified June 2009 */ public class SWTApp { private Shell shell.zetcode. shell. /** * ZetCode Java SWT tutorial * * In this program.rowLayout.widgets.layout.GridLayout. shell.swt.widgets. The GridLayout manager puts its child widgets into a grid.eclipse.setLayout(rowLayout).Button.setLocation(300. shell.Label. org. rowLayout.setText("Calculator").swt. we use the GridLayout manager to create a skeleton of a calculator. org.eclipse. shell.eclipse. we use the GridLayout to * create a calculator skeleton * * @author jan bodnar * website zetcode. public SWTApp(Display display) { shell = new Shell(display). org.eclipse.GridData. We change the margins and the spacing for the row. org. 383 . import import import import import import import import org.eclipse. gl. A text widget.PUSH). lbl. shell.widthHint = 50. SWT. "2".marginTop = 5. "9". "Bck". display. "0". } else { Button btn = new Button(shell. false. true).open(). SWT. gl. SWT.horizontalSpacing = 4. gd.sleep(). "5". SWT.setText(buttons[i]).readAndDispatch()) { display. GridData gridData = new GridData().verticalSpacing = 4. "+" }. false). GridData gd = new GridData(SWT. "/". for (int i = 0. false). "3". GridData gd = new GridData(SWT. i < buttons.marginBottom = 5. 384 .shell. Text display = new Text(shell.FILL. ".setLayout(gl).heightHint = 30.setLayoutData(gridData). "7". btn.setLayoutData(gd). false. gridData. "-".setLayoutData(gd). "1". } } } } public static void main(String[] args) { Display display = new Display(). btn.horizontalSpan = 4. display. "4". "8". "=". gd. while (!shell. a label widget and several buttons. gridData.horizontalAlignment = GridData.dispose().FILL.length. String[] buttons = { "Cls".FILL.". i++) { if (i == 2) { Label lbl = new Label(shell. "Close". new SWTApp(display). SWT. "*". gl. We use three types of widgets.FILL.CENTER). "6". } We create a skeleton of a calculator with the GridLayout manager. "". } } } public void initUI() { GridLayout gl = new GridLayout(4.isDisposed()) { if (!display. gl.FILL.SINGLE). FormData and FormAttachment. Button btn = new Button(shell.eclipse.verticalSpacing = 4. The GridData is the layout data object associated with GridLayout. SWT.FormAttachment.FormLayout. org. btn. org. We create a GridLayout with 4 columns. gd.eclipse.setLayoutData(gridData).marginTop = 5.FILL.layout.heightHint = 30.eclipse. gl. gridData.swt. shell. gl. SWT.SWT. true). false. we will create an example with the FormLayout manager. import import import import org. we create buttons and put them into the grid. org. gd. gridData.horizontalAlignment = GridData.horizontalSpan = 4.FILL. GridData gridData = new GridData(). Inside the for loop.setText(buttons[i]).SINGLE).layout.setLayoutData(gd).horizontalSpacing = 4.marginBottom = 5. btn. Text display = new Text(shell. SWT. 385 .FormData. false). We provide some spacing and margins.FILL.setLayout(gl).swt.widthHint = 50.zetcode.PUSH).GridLayout gl = new GridLayout(4. gl.swt.eclipse. display. Figure: Calculator skeleton Buttons In the last example. We make the text widget span all four columns.swt. GridData gd = new GridData(SWT. gl. package com.layout. This manager controls the position and size of the children using two objects. SWT.readAndDispatch()) { display.BOTTOM). FormData cancelData = new FormData(80.setText("Cancel").setLayout(layout).setText("OK"). shell.swt.widgets.open(). import org.setLocation(300.eclipse. SWT. shell. shell. cancelData. 0. initUI().Shell.Button.swt. import org. cancelButton. okData.bottom = new FormAttachment(95). okButton.import org.widgets. } 386 . } } } public void initUI() { FormLayout layout = new FormLayout().Display. /** * ZetCode Java SWT tutorial * * In this program. while (!shell.eclipse.PUSH). 300). okButton.eclipse. okData.isDisposed()) { if (!display. public SWTApp(Display display) { shell = new Shell(display). 30).com * last modified June 2009 */ public class SWTApp { private Shell shell. SWT.setSize(350.swt.setText("Buttons"). shell.sleep(). -5. 30). 200). cancelButton.widgets. Button cancelButton = new Button(shell.right = new FormAttachment(98).PUSH). shell. SWT.setLayoutData(cancelData). FormData okData = new FormData(80. cancelData. we position two buttons * in the bottom right corner of the window * * @author jan bodnar * website zetcode.right = new FormAttachment(cancelButton. Button okButton = new Button(shell.LEFT).bottom = new FormAttachment(cancelButton.setLayoutData(okData). Two buttons are created. okData.setText("OK"). 30). FormData cancelData = new FormData(80. The right side of the button is attached at 98% of the width of the window. cancelButton. 0. cancelData. The right side of the ok button goes 5px to the left of the cancel button. FormLayout layout = new FormLayout().LEFT). The cancel button's size is 80x30. SWT.BOTTOM).PUSH).PUSH). FormLayout manager is created. } } In this code example. we talked about layout management of widgets. okData. 387 .setText("Cancel"). shell.dispose(). SWT.bottom = new FormAttachment(cancelButton.bottom = new FormAttachment(95). SWT.setLayout(layout). new SWTApp(display). The bottom side of the button is attached at 95% of the height of the window. Button okButton = new Button(shell.public static void main(String[] args) { Display display = new Display(). cancelData. Figure: Buttons In this part of the Java SWT tutorial. Button cancelButton = new Button(shell. we position two buttons in the bottom right corner of the window.right = new FormAttachment(cancelButton. The bottom side of the ok button is aligned with the bottom of the cancel button. SWT. okButton.right = new FormAttachment(98). display. -5. /** * ZetCode Java SWT tutorial * * This program uses the Label widget to * show lyrics of a song * * @author jan bodnar * website zetcode..swt..Label. Label A Label widget shows text.swt.swt. org.A murderer".widgets. public SWTApp(Display display) { shell = new Shell(display).eclipse.Display. org. several widgets became a standard in all toolkits on all OS platforms.graphics. For example a button.Widgets in SWT In this part of the Java SWT programming tutorial. Over the years. org.swt. 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.swt. org.com * last modified June 2009 */ public class SWTApp { Shell shell. Think of widgets as parts of a lego.eclipse. Widgets are basic building blocks of a GUI application. 388 .widgets.zetcode.Shell.eclipse. package com. a check box or a scroll bar. we will introduce some SWT widgets.eclipse.widgets.SWT.Point.eclipse. import import import import import org. shell.DEFAULT). new SWTApp(display).sleep(). } } } public void initUI() { Label label = new Label(shell. label. label.isDisposed()) { if (!display. We compute the size of the text in order put some space round the text.LEFT). Label label = new Label(shell. label. shell. while (!shell.open().y+5). Text is left aligned. SWT. 5. Point p = label.setBounds(5.dispose().LEFT).x+5.pack().readAndDispatch()) { display. p. String lyrics = "And I know that he knows I'm unfaithful\n"+ "And it kills him inside\n"+ . display.setLocation(300. 5. label.setBounds(5. SWT. p. shell. The Label widget is created..setText(lyrics). p.DEFAULT.shell. initUI().DEFAULT).y+5).setText("Unfaithful"). } The code example shows some lyrics on the window. 300). SWT.DEFAULT. Point p = label. 389 .x+5.computeSize(SWT.setText(lyrics). p. We build a multiline text. SWT..computeSize(SWT. } } public static void main(String[] args) { Display display = new Display(). It is used to denote some boolean property.zetcode. private Button cb. check button is a special case of a Button.eclipse.eclipse.eclipse. /** * 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.eclipse.Button. org.widgets.swt.eclipse.SelectionAdapter. shell.swt.events. that has two states. org.eclipse.widgets.widgets. It is a widget.swt.setText("Check button").events. 390 . org.Figure: Label Widget CheckButton In SWT.Shell. public SWTApp(Display display) { shell = new Shell(display). org. On and Off.SWT.SelectionEvent. org.swt. package com. import import import import import import org. The On state is visualised by a check mark.swt.Display.swt. isDisposed()) { if (!display.dispose(). depending on the state of the check button. CheckButton widget is created. SWT.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { if (cb. } else { shell. cb = new Button(shell. cb. cb. cb.setText("Show title"). } } }). 50). while (!shell.getSelection()) { shell. new SWTApp(display).open(). display.setLocation(50. shell.CHECK). cb.setText("Show title").setText("Check button").setSelection(true). } public static void main(String[] args) { Display display = new Display(). shell. cb. if (cb. } else { 391 . The title is visible by default.pack().getSelection()) { shell. cb.setText("Check button").setLocation(300. shell.setSize(250. } } We will display a title in the titlebar of the window.CHECK). } } } public void initUI() { cb = new Button(shell.setText(""). so we check the check button by default. cb.setSelection(true). SWT.sleep(). 300). 200).initUI().readAndDispatch()) { display. } shell.widgets. Depending on the state of the CheckButton.swt. public SWTApp(Display display) { shell = new Shell(display). org.swt.layout. package com.eclipse.swt.com * last modified June 2009 */ public class SWTApp { Shell shell.eclipse. org.Display.eclipse.swt.widgets. /** * ZetCode Java SWT tutorial * * This program shows the List * widget * * @author jan bodnar * website zetcode. org. 392 .FormData.eclipse.eclipse. we show or hide the title of the window.widgets.FormAttachment.widgets.setText("").Listener.layout.Event.eclipse.eclipse. org.widgets.widgets.zetcode. org. import import import import import import import import import import org.swt.FormLayout.SWT. org.Label.eclipse. org. org.swt. This widget enables a user to select an option from a list of items.swt.swt.eclipse.layout.swt.List. org.eclipse.swt. Figure: CheckButton List widget The next example introduces the List widget.Shell. shell. listData.getSelection(). 300). display. 30. FormData labelData = new FormData().add("Capote"). status.top = new FormAttachment(shell.setLayout(layout). SWT. while (!shell. labelData.setText("Ready"). listData. status.setSize(300.add("Starship troopers"). new Listener () { public void handleEvent (Event e) { String[] items = list. 30.left = new FormAttachment(0). shell. SWT. list. list.add("Omen").add("Aliens").BORDER). FormData listData = new FormData().setText("List"). shell. list. new SWTApp(display).add("Exorcist").readAndDispatch()) { display.left = new FormAttachment(shell. SWT.open().add("Neverending story"). list. shell.right = new FormAttachment(100).setText(items[0]).addListener(SWT.setLocation(300. list. } } } public void initUI() { final Label status = new Label(shell. } public static void main(String[] args) { Display display = new Display(). list. labelData. } 393 .bottom = new FormAttachment(100). final List list = new List(shell. } }).dispose().isDisposed()) { if (!display.TOP). labelData.shell. 250).LEFT). initUI(). status.Selection.sleep(). list. SWT.setLayoutData(listData).setLayoutData(labelData). FormLayout layout = new FormLayout().BORDER). list. setText(items[0]).add("Aliens"). status.TOP).add("Capote").top = new FormAttachment(shell.getSelection(). list.add("Exorcist").left = new FormAttachment(shell.addListener(SWT.add("Starship troopers").LEFT). final Label status = new Label(shell. final List list = new List(shell. listData. list. SWT.setText("Ready"). shell. } }). status. list. We add a listener to the List widget. y = 30px. 30. FormData listData = new FormData(). labelData. The List widget is created.bottom = new FormAttachment(100). 394 . FormData labelData = new FormData().right = new FormAttachment(100).} In this example. we set the selected text to the status label.Selection. SWT.add("Neverending story").setLayout(layout). labelData. The label has a border.left = new FormAttachment(0). SWT library has no statusbar widget. SWT. labelData.add("Omen").setLayoutData(listData). 30. We use a simple label for this. This code puts the List widget at x = 30px.setLayoutData(labelData). new Listener () { public void handleEvent (Event e) { String[] items = list.BORDER). list. This code will attach the status label to the bottom of the window. status. list.BORDER). list. the handleEvet() method is executed. SWT. FormLayout layout = new FormLayout(). We use the FormLayout widget to arrange our widgets on the window. Where we usually see the statusbar. listData. When we select an option. the selected item from the list widget is shown in the statusbar. list. It is filled with data. list. In this method. min.swt.Display.Label. we use the slider * widget to create a volume control * * @author jan bodnar * website zetcode. org.widgets.Listener. med.graphics. max.zetcode. org.eclipse.swt.swt.swt.Image. org. /** * ZetCode Java SWT tutorial * * In this program.graphics.eclipse.widgets.Device.widgets.widgets. import import import import import import import import import org. public SWTApp(Display display) { 395 .swt. org.eclipse. org.widgets. Our example will show a volume control.eclipse. org. that lets the user graphically select a value by sliding a knob within a bounded interval.eclipse.eclipse.Figure: List widget Slider The Slider is a widget. org.Shell. org.swt.com * last modified June 2009 */ public class SWTApp { private Shell shell.eclipse.Slider. package com.widgets.eclipse.swt.eclipse.swt. private private private private Image Image Image Image mute.Event.SWT.swt. println("Cannot load images").out. 200). "mute. if (value == 0) { label. } }). shell. } catch(Exception e) { System. 180.setLocation(240. } else if (value > 30 && value < 80) { label.open().exit(1). } } } public void initUI() { final Label label = new Label(shell. slider. med = new Image(dev. SWT. } shell. slider. "max. new Listener () { public void handleEvent (Event e) { int value = slider. } } 396 .png").png"). label.setLocation(300. System.getMessage()).getDisplay(). label.setImage(min). shell. 30). Device dev = shell.addListener (SWT. while (!shell.isDisposed()) { if (!display.out.setImage(max).png").HORIZONTAL).setImage(mute).setMaximum(100).setSize(350.setImage(mute).println(e.sleep().shell = new Shell(display).setBounds(30. final Slider slider = new Slider(shell.Selection. label. "med.png"). initUI().setText("Slider").readAndDispatch()) { display. slider. System. } else if (value > 0 && value <= 30) { label.pack(). } else { label. 30). shell. label. 30.getSelection(). min = new Image(dev. SWT.IMAGE_PNG).setImage(med). try { mute = new Image(dev.pack(). 300). "min. max = new Image(dev. setImage(mute).setMaximum(100).dispose().@Override public void finalize() { System. min.out. @Override public void finalize() { System. if (value == 0) { label.dispose(). mute. med. } } In the example above.dispose().println("disposing"). min. Inside the listener object.setImage(max). } else if (value > 0 && value <= 30) { label.println("disposing").HORIZONTAL).pack().dispose(). SWTApp app = new SWTApp(display).dispose(). 397 . label. int value = slider. app.setImage(med). } public static void main(String[] args) { Display display = new Display(). we have Slider and Image widgets. max. final Slider slider = new Slider(shell. we change the picture in the label widget. SWT.dispose(). max. } else if (value > 30 && value < 80) { label. display. } We release the resources. } Depending on the obtained value.dispose(). we obtain the value of the slider widget. By dragging the knob of the slider we change the picture on the Label widget.setImage(min). mute.finalize().dispose().getSelection(). slider. Its maximum value is 100. med.out. } else { label. Slider widget is created.dispose(). swt.SWT.widgets.events. shell.eclipse.swt.setLocation(300.zetcode.com * last modified June 2009 */ public class SWTApp { Shell shell.SelectionEvent. we use the Combo * widget to select an option.swt. shell. org.eclipse. * * @author jan bodnar * website zetcode. org.widgets.eclipse. initUI(). /** * ZetCode Java SWT tutorial * * In this program.open(). org.swt.eclipse. 398 . org. 300).eclipse.SelectionAdapter.widgets. * The selected option is shown in the * Label widget.Figure: Slider widget Combo widget Combo is a widget that allows the user to choose from a drop down list of options.events. org. 250). shell.Label.eclipse. import import import import import import import org. public SWTApp(Display display) { shell = new Shell(display). shell.Display.setText("Combo").swt. org.Shell.setSize(300. package com.eclipse.swt.widgets.swt.Combo. combo.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { label. combo. new SWTApp(display). SWT. } } The example shows a combo box and a label.add("Mandriva"). combo.add("Red Hat").dispose().pack(). label.add("Mint").while (!shell..LEFT).add("Mandriva").pack(). label.setLocation(50. The label widget shows the selected option from the combo box. combo. combo. } } } public void initUI() { final Label label = new Label(shell. combo. display.sleep()."). final Combo combo = new Combo(shell. } public static void main(String[] args) { Display display = new Display().setText(combo. 399 . SWT. combo..DROP_DOWN). 30).add("Ubuntu").add("Ubuntu"). combo. combo. label. These are the names of Linux Distros. combo. label. The combo box has a list of six options. final Combo combo = new Combo(shell. }). 100). combo.pack().getText()).setLocation(50.isDisposed()) { if (!display.add("Fedora"). }.setText(". SWT. combo. Combo widget is created.add("Fedora").readAndDispatch()) { display.add("Red Hat").DROP_DOWN). eclipse. import import import import import org. }. The menu will have only one menu item.eclipse. we will work with menus & toolbars. We set the selected text to the label widget. It is a group of commands located in various menus. Menus & toolbars In this part of the Java SWT tutorial. org.setText(combo.widgets. here we have most of the commands grouped into logical parts.zetcode. These are accepted standards that further reduce the amount of time spending to learn a new application. A menubar is one of the most common parts of the GUI application. org.SelectionAdapter.SWT. Simple menu In our first example.eclipse.swt. Figure: Combo widget In this part of the Java SWT tutorial. label.add("Mint").Menu.pack().events.swt. we will create a menubar with one file menu. @Override public void widgetSelected(SelectionEvent e) { label. 400 .eclipse. package com.SelectionEvent. we described some widgets of the SWT library.eclipse.combo. It is filled with data. While in console applications you have to remember all those arcane commands. org. org.getText()).events.swt.Display.swt.widgets.swt. By selecting the item the application quits. com * last modified June 2009 */ public class SWTApp { private Shell shell.eclipse.open(). cascadeFileMenu.setLocation(300. shell.setText("&File").setSize(250. System.readAndDispatch()) { display. cascadeFileMenu. SWT. SWT. SWT. exitItem. 200).import org. /** * ZetCode Java SWT tutorial * * This program creates a simple menu * * @author jan bodnar * website zetcode.setText("&Exit"). public SWTApp(Display display) { shell = new Shell(display).widgets.DROP_DOWN). } }).PUSH). import org.Shell. shell.setText("Simple menu").widgets.setMenu(fileMenu).isDisposed()) { if (!display.eclipse. 300). MenuItem cascadeFileMenu = new MenuItem(menuBar. } 401 . shell.MenuItem. shell.swt.getDisplay(). while (!shell. exitItem. shell.dispose().swt. SWT. initUI(). Menu fileMenu = new Menu(shell.BAR).CASCADE).sleep().setMenuBar(menuBar).addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { shell.exit(0). MenuItem exitItem = new MenuItem(fileMenu. } } } public void initUI() { Menu menuBar = new Menu(shell. When we select the exit push menu item. } } This is a small example with minimal menubar functionality.setText("&Exit"). SWT.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { shell. new SWTApp(display). A push menu item is plugged into a drop down menu. Here we create a menu bar. Menu fileMenu = new Menu(shell.BAR).getDisplay().public static void main(String[] args) { Display display = new Display(). exitItem. SWT.dispose(). } }). 402 . cascadeFileMenu.setMenuBar(menuBar).CASCADE). we terminate the application. We set a menu bar for our shell. SWT.setText("&File"). MenuItem cascadeFileMenu = new MenuItem(menuBar. We create a drop down menu. Menu menuBar = new Menu(shell. cascadeFileMenu. MenuItem exitItem = new MenuItem(fileMenu. System. display.setMenu(fileMenu). shell. Toplevel menu items are cascade menu items.exit(0).DROP_DOWN). exitItem. SWT.dispose().PUSH). MenuItem cascadeMenu = new MenuItem(menuBar. SWT. /** * ZetCode Java SWT tutorial * * This program creates a submenu * * @author jan bodnar * website zetcode.widgets.eclipse. initUI().swt.open().zetcode. org.widgets.swt. 200).SelectionEvent.Shell.setSize(250.setText("Submenu").readAndDispatch()) { display.isDisposed()) { if (!display. Menu fileMenu = new Menu(shell.MenuItem.swt.events.CASCADE). SWT.DROP_DOWN). 403 .swt.eclipse.com * last modified June 2009 */ public class SWTApp { private Shell shell. shell. while (!shell.SelectionAdapter.Menu.events. } } } public void initUI() { Menu menuBar = new Menu(shell.sleep(). public SWTApp(Display display) { shell = new Shell(display).CASCADE).eclipse.eclipse. 300).setMenu(fileMenu).SWT. package com.widgets. shell. org. org.BAR).swt.widgets.setText("&File"). org.eclipse. cascadeFileMenu. shell. MenuItem cascadeFileMenu = new MenuItem(menuBar. org. import import import import import import import org. org.Display.setLocation(300.Figure: Simple menu Submenu The next example demonstrates how to create a submenu in Java SWT.eclipse. cascadeFileMenu.eclipse. shell. SWT.swt. SWT.swt. PUSH). subMenuItem.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { shell. This time the parent is the menu object. subMenuItem..dispose(). MenuItem bmarks = new MenuItem(submenu.. MenuItem subMenuItem = new MenuItem(fileMenu.DROP_DOWN).setText("&Exit"). } } Submenu creation.setText("&Import bookmarks.cascadeMenu. display. we create a cascade menu item. exitItem.setMenuBar(menuBar). feedItem. Menu submenu = new Menu(shell..setText("&Import news feed.setText("&Import mail. } public static void main(String[] args) { Display display = new Display(). SWT.CASCADE). First. shell. mailItem. to which the submenu belongs. MenuItem feedItem = new MenuItem(submenu."). MenuItem exitItem = new MenuItem(fileMenu.exit(0). SWT.setText("&Import news feed.PUSH).")... SWT. new SWTApp(display).setMenu(submenu).dispose(). SWT. SWT. MenuItem feedItem = new MenuItem(submenu.")."). subMenuItem. SWT.PUSH).setText("&File"). feedItem. SWT. MenuItem mailItem = new MenuItem(submenu. MenuItem subMenuItem = new MenuItem(fileMenu. exitItem.CASCADE).PUSH).PUSH). SWT. bmarks. We create a push menu item.setText("Import"). 404 .getDisplay()... The only difference is the parent widget. The parent widget is the submenu object.. A submenu creation is similar to creating a normal menu. System.setText("Import"). } }). swt. private MenuItem statItem.events.swt.swt. org.swt.eclipse.eclipse. org. org.eclipse. org.swt.setText("Check menu item").widgets. org.layout.swt.swt.Figure: Submenu CheckMenuItem A CheckMenuItem is a menu item with a check box.eclipse. * * @author jan bodnar * website zetcode. org.widgets.zetcode. package com.layout.MenuItem.widgets. org.eclipse.widgets.Display.swt. It can be used to work with boolean properties.Event.eclipse. * It will show or hide a statusbar.SelectionEvent.swt.SWT. public SWTApp(Display display) { shell = new Shell(display). shell.FormData.layout.eclipse.com * last modified June 2009 */ public class SWTApp { private Shell shell.FormAttachment.widgets. /** * ZetCode Java SWT tutorial * * This program creates a check menu item.events.Label.widgets.SelectionAdapter. 405 .Shell. import import import import import import import import import import import import import org.eclipse. org.eclipse.Menu. org.eclipse.eclipse.swt.eclipse.swt. org.Listener.widgets.swt. org. private Label status.eclipse. org.FormLayout.swt. isDisposed()) { if (!display. SWT. shell.DROP_DOWN).setLocation(300. statItem.CHECK). } else { status.setText("&Exit"). System.initUI(). SWT.PUSH).setText("&View"). statListener). public void initUI() { Menu menuBar = new Menu(shell. cascadeViewMenu.getSelection()) { status. 250). MenuItem cascadeViewMenu = new MenuItem(menuBar. MenuItem cascadeFileMenu = new MenuItem(menuBar. 300).CASCADE). statItem.setMenu(fileMenu). cascadeFileMenu. cascadeFileMenu. exitItem.sleep().exit(0). statItem = new MenuItem(viewMenu.setText("&File").getDisplay(). } } } Listener statListener = new Listener() { public void handleEvent(Event event) { if (statItem.CASCADE).addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { shell. } }). MenuItem exitItem = new MenuItem(fileMenu.setVisible(false). shell.Selection.readAndDispatch()) { display. shell.setMenuBar(menuBar).setSize(300.setVisible(true). exitItem. SWT. shell.BAR). SWT. Menu viewMenu = new Menu(shell.addListener(SWT. SWT. SWT. cascadeViewMenu. while (!shell.open().setSelection(true).DROP_DOWN). Menu fileMenu = new Menu(shell. statItem.setText("&View Statusbar").dispose().setMenu(viewMenu). } } }. 406 . SWT. FormLayout layout = new FormLayout().left = new FormAttachment(0). If the check box is activated. we show or hide the statusbar widget.setLayoutData(labelData).status = new Label(shell. status. Figure: CheckMenuItem 407 . SWT.setVisible(false). labelData. shell.getSelection()) { status. If not. status. the statusbar is hidden.bottom = new FormAttachment(100). } } In our code example we show a check menu item. statItem = new MenuItem(viewMenu. SWT. new SWTApp(display).CHECK flag will create a check menu item. } else { status. labelData. } public static void main(String[] args) { Display display = new Display(). FormData labelData = new FormData().dispose(). labelData. if (statItem.setVisible(true). The SWT. display.setLayout(layout).CHECK).BORDER).setSelection(true). statItem. The setSelection() method checks/unchecks the check menu item.right = new FormAttachment(100).setText("Ready"). } Depending on the state of the check menu item. the statusbar widget is shown. POP_UP).isDisposed()) { if (!display.widgets. org.swt.zetcode.SWT. This type of menu is shown. import import import import import import import org. while (!shell. shell. shell. we will create a popup menu.swt.events.Menu.eclipse.SelectionAdapter.swt. initUI().swt.swt. } } } public void initUI() { Menu menu = new Menu(shell.setText("Minimize").Shell.setLocation(300. org. org. org.eclipse.MenuItem.setText("Popup menu"). org. SWT.widgets.swt. minItem. package com.Popup menu In the next example. shell.Display. when we right click on an object.PUSH). It is also called a context menu.eclipse. org.com * last modified June 2009 */ public class SWTApp { private Shell shell.eclipse. shell.widgets.swt.SelectionEvent. SWT.eclipse.sleep().eclipse. /** * ZetCode Java SWT tutorial * * This program creates a popup * menu * * @author jan bodnar * website zetcode.widgets. MenuItem minItem = new MenuItem(menu.setSize(300. public SWTApp(Display display) { shell = new Shell(display).addSelectionListener(new SelectionAdapter() { @Override 408 .events.eclipse. minItem.open(). 250). 300).readAndDispatch()) { display. POP_UP).setText("Exit"). Menu menu = new Menu(shell.setMenu(menu). shell.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { shell. 409 . } MenuItem exitItem = new MenuItem(menu. Popup menu is created with the SWT.setMinimized(true).getDisplay(). we create a popup menu with two menu items. Menu items inside a popup menu are normal push menu items.dispose(). SWT. } }).setText("Minimize"). display.setMinimized(true). } } In our code example.PUSH). MenuItem minItem = new MenuItem(menu. public void widgetSelected(SelectionEvent e) { shell. } public static void main(String[] args) { Display display = new Display(). new SWTApp(display). shell.dispose(). SWT. public void widgetSelected(SelectionEvent e) { shell. minItem. } This code will minimize the window. System. We set a popup menu for the shell.setMenu(menu). exitItem. SWT. The first will minimize the window and the second will terminate the application.exit(0). exitItem.PUSH).}).POP_UP flag. eclipse. package com. /** * ZetCode Java SWT tutorial * * This program creates a simple toolbar * * @author jan bodnar * website zetcode.events.eclipse.swt.events.eclipse. org. shell.SWT. In the following example. 410 .swt.eclipse.swt.eclipse.swt.swt.swt.eclipse.com * last modified June 2009 */ public class SWTApp { private private private private Shell Image Image Image shell.widgets. Toolbars provide a quick access to the most frequently used commands.swt.SelectionEvent. org. opei.SelectionAdapter.eclipse. import import import import import import import import import org.Figure: Popup menu Simple toolbar Menus group commands that we can use in application. quii. org.graphics. org. org. we will create a simple toolbar.widgets.Image. org.eclipse. public SWTApp(Display display) { shell = new Shell(display).swt.Device. newi.widgets.ToolBar. org.ToolItem.Shell.swt.zetcode.eclipse.graphics.widgets.setText("Simple toolbar"). org.Display. "open.open().setSize(300. ToolItem item3 = new ToolItem(toolBar. } ToolBar toolBar = new ToolBar(shell.sleep(). item3.setImage(opei). "new.PUSH). item1. shell.PUSH). new ToolItem(toolBar. SWT.SEPARATOR).addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { shell.readAndDispatch()) { display. try { newi = new Image(dev. toolBar. quii = new Image(dev.PUSH).png").getDisplay().setImage(quii). public static void main(String[] args) { Display display = new Display(). } } } public void initUI() { Device dev = shell.out. "quit.dispose().getDisplay().png").BORDER). } } }). display. 250). new SWTApp(display). SWT.initUI(). ToolItem item1 = new ToolItem(toolBar. System.setImage(newi).exit(1).pack(). shell.isDisposed()) { if (!display.setLocation(300.getMessage()). SWT. System.exit(0). item2.println("Cannot load images").dispose(). 300). while (!shell. System.println(e. ToolItem item2 = new ToolItem(toolBar. item3. shell.png"). SWT. SWT. opei = new Image(dev.out. } } 411 . } catch (Exception e) { System. eclipse. new ToolItem(toolBar. Dialog windows or dialogs are an indispensable part of most modern GUI applications.swt.zetcode. A dialog is used to input data. SWT. import org.BORDER). we will introduce dialogs.setImage(newi).The example shows a toolbar and three tool items. ToolBar toolBar = new ToolBar(shell.SWT.SEPARATOR). We create a tool item with an image.PUSH). change the application settings etc. In a computer application a dialog is a window which is used to "talk" to the application.eclipse. how to work with menus & toolbars. item1. 412 . modify data. SWT. Dialogs are important means of communication between a user and a computer program.events. A dialog is defined as a conversation between two or more persons. which is used to select a path to a certain directory. Here we create a vertical separator. Figure: Toolbar In this chapter of the Java SWT tutorial. Dialogs In this part of the Java SWT tutorial. ToolItem item1 = new ToolItem(toolBar. we showed.swt. Toolbar widget is created. package com. Directory dialog The directory dialog is a dialog. import org.MouseAdapter. SWT. widgets.setSize(350. SWT. org.FormAttachment.MouseEvent.eclipse. initUI().setLayout(layout).widgets.eclipse. labelData.sleep().DirectoryDialog.layout.import import import import import import import import org.swt. org.eclipse.Display. shell.FormData. /** * ZetCode Java SWT tutorial * * This example shows a directory dialog * * @author jan bodnar * website zetcode.eclipse.setLocation(300.swt. 300). shell.addMouseListener(new MouseAdapter() { @Override 413 . while (!shell.swt. org. org.layout. shell. status. public SWTApp(Display display) { shell = new Shell(display).BORDER).setLayoutData(labelData).com * last modified June 2009 */ public class SWTApp { private Shell shell. shell. 250). labelData.widgets.swt.swt.eclipse.widgets.layout. labelData.readAndDispatch()) { display. shell.eclipse.setText("Ready").eclipse. } } } public void initUI() { final Label status = new Label(shell. FormData labelData = new FormData(). org.FormLayout.eclipse.isDisposed()) { if (!display.Shell. FormLayout layout = new FormLayout(). org.swt.Label.swt.setText("DirectoryDialog").events.swt.right = new FormAttachment(100).open().bottom = new FormAttachment(100). status.left = new FormAttachment(0). shell. org. The dialog is shown by clicking on the area of the window.setText(path). 414 . String path = dialog. } }). display. we use the directory dialog to select a directory. we show the path in the status label.open().dispose(). } public static void main(String[] args) { Display display = new Display(). We get the path to the selected directory. If the path is not null. new SWTApp(display).open(). DirectoryDialog is created. } } In our example.setText(path). String path = dialog. DirectoryDialog dialog = new DirectoryDialog(shell). The path to the directory is shown in the status label.public void mouseDown(MouseEvent event) { DirectoryDialog dialog = new DirectoryDialog(shell). if (path != null) status. if (path != null) status. swt.Shell.events. org.swt. org.com 415 .swt.FontDialog.FontData.eclipse. import import import import import import import import import org. package com.Font. /** * ZetCode Java SWT tutorial * * This example shows a font dialog * * @author jan bodnar * website zetcode.zetcode. org.events.eclipse. org.eclipse.swt.widgets.widgets. It is typically used in applications.widgets.widgets.graphics. org.eclipse.eclipse.swt. that do some text editing or formatting.eclipse. org.SWT.Display.swt.graphics.Figure: Directory dialog FontDialog The FontDialog is a dialog for selecting fonts.eclipse.Label.eclipse.MouseAdapter.MouseEvent. org.swt.swt. org.eclipse.swt. readAndDispatch()) { display.addMouseListener(new MouseAdapter() { @Override public void mouseDown(MouseEvent event) { FontDialog dialog = new FontDialog(shell). shell. 50).setText("ZetCode Java SWT tutorial"). shell.open().setLocation(50.setLocation(300.NONE).setFont(font). public SWTApp(Display display) { shell = new Shell(display).open(). font.sleep(). fdata).pack(). shell.setSize(350.pack(). FontData fdata = dialog. display. shell. 300). label. initUI(). } }). label. } } } public void initUI() { final Label label = new Label(shell.dispose().setText("FontDialog"). } 416 . label. label. 250).* last modified June 2009 */ public class SWTApp { private Shell shell.dispose(). } } public static void main(String[] args) { Display display = new Display().isDisposed()) { if (!display.getDisplay(). if (fdata != null) { Font font = new Font(shell. SWT. shell. label. new SWTApp(display). while (!shell. swt.swt.} In the code example.ColorDialog. package com. org.eclipse.MouseEvent.Label. org. org.swt.widgets. import import import import import import import import import org.MouseAdapter.eclipse.Shell.graphics. We create the FontDialog. org.widgets. Font object is created from the font data.SWT. org. org. fdata).swt.swt.widgets.events. Figure: FontDialog ColorDialog ColorDialog is a dialog for selecting a color.eclipse. FontDialog dialog = new FontDialog(shell).swt.swt.eclipse.eclipse.graphics.events. we use a FontDialog to change the font of a label. org.eclipse.eclipse.widgets.eclipse.eclipse.Display.Color. org.getDisplay(). /** * ZetCode Java SWT tutorial * 417 .swt.RGB.zetcode. returned by the font dialog.swt. Font font = new Font(shell. rgb). label. } public static void main(String[] args) { Display display = new Display().open(). while (!shell.setForeground(col).addMouseListener(new MouseAdapter() { @Override public void mouseDown(MouseEvent event) { ColorDialog dialog = new ColorDialog(shell). shell.setLocation(300.isDisposed()) { if (!display. if (rgb != null) { Color col = new Color(shell. label.pack(). SWT. initUI().dispose(). public SWTApp(Display display) { shell = new Shell(display). shell. 300). label.NONE). 418 . shell.setSize(350. label. shell. } } } public void initUI() { final Label label = new Label(shell.open(). col. shell.setText("ZetCode Java SWT tutorial"). RGB rgb = dialog.sleep().readAndDispatch()) { display. 250).com * last modified June 2009 */ public class SWTApp { private Shell shell.* This example shows a color dialog * * @author jan bodnar * website zetcode.setText("ColorDialog"). } } }).setLocation(50. 50).getDisplay(). Color col = new Color(shell. RGB rgb = dialog. org. org. org.open(). org.Display.FileDialog.events.swt. package com. We create the ColorDialog.getDisplay(). ColorDialog dialog = new ColorDialog(shell).swt. org.MouseEvent.eclipse. We get the RGB value.eclipse. } } The example is very similar to the previous one. Figure: ColorDialog FileDialog The FileDialog is used to select a name of a file.Label.widgets.dispose().widgets. display.swt.swt. rgb).zetcode.eclipse. org.SWT.events.widgets.eclipse.eclipse. import import import import import import import org.eclipse.swt.swt. 419 .swt.setForeground(col).new SWTApp(display). label. This time we change the color of the label.Shell.widgets.MouseAdapter.eclipse. We get the color value and modify the color of the label. String[] filterNames = new String[] {"Java sources". 50). 300).setLocation(300.NONE). shell. public SWTApp(Display display) { shell = new Shell(display).open(). } } } public void initUI() { final Label label = new Label(shell.com * last modified June 2009 */ public class SWTApp { private Shell shell./** * ZetCode Java SWT tutorial * * This example shows a file dialog * * @author jan bodnar * website zetcode. shell. SWT.setLocation(50.sleep(). 250).setText("FileDialog").java". "All Files (*)"}. initUI(). dialog. String path = dialog.readAndDispatch()) { display.OPEN). 420 . shell. String[] filterExtensions = new String[] {"*. shell.setFilterExtensions(filterExtensions).setText("ZetCode Java SWT tutorial"). SWT.isDisposed()) { if (!display. dialog. label. label. "*"}. label.setFilterNames(filterNames). while (!shell.pack(). shell.setSize(350.open().addMouseListener(new MouseAdapter() { @Override public void mouseDown(MouseEvent event) { FileDialog dialog = new FileDialog(shell. When do we need to paint? There are situations.PaintEvent. package com. The dialog can be used to open or save files. import org.dispose().OPEN flag. label. Green. In such a case. In Java SWT.OPEN).swt. The name of the file is shown in the label.java".if (path != null) { label. when we need to create a widget from scratch. "*"}. "All Files (*)"}. } public static void main(String[] args) { Display display = new Display().events. effects or widget enhancements. We create an FileDialog with a SWT. Or we want to create charts.setFilterExtensions(filterExtensions). import org. display. The dialog uses a filter to show only the java sources. Painting in Java SWT In this part of the Java SWT tutorial. we will do some painting. FileDialog dialog = new FileDialog(shell. A color is an object representing a combination of Red.events. 421 .pack().setText(path). } } } The code example uses a FileDialog to select a file. This part of the Java SWT tutorial was about dialogs in SWT.eclipse.eclipse. new SWTApp(display).setFilterNames(filterNames). We use a filter to narrow the files to Java sources. SWT. valid RGB values are in the range 0 to 255. String[] filterExtensions = new String[] {"*. Colors In the first example. special ornaments. } }). we need to do painting.PaintListener.zetcode. dialog.swt. we will work with colors. String[] filterNames = new String[] {"Java sources". dialog. and Blue (RGB) intensity values. c1.dispose(). e. public SWTApp(Display display) { shell = new Shell(display). 90. 200).readAndDispatch()) { display. 90. shell. 50. e. shell. 60). 100). 120). shell. 15.open(). 90.addPaintListener(new ColorsPaintListener()). shell.eclipse.setSize(360.import org. 200.widgets. import org. } } } private class ColorsPaintListener implements PaintListener { public void paintControl(PaintEvent e) { drawRectangles(e). /** * ZetCode Java SWT tutorial * * This program draws three rectangles.Shell.gc.gc. e.isDisposed()) { if (!display.fillRectangle(10.fillRectangle(250.fillRectangle(130. 90.display.setText("Colors").dispose(). import org.setBackground(c2). 60). shell. 422 .swt.sleep(). 300). 60). e. Color c2 = new Color(e.setBackground(c3). 50.swt. 60).eclipse.setBackground(c1).eclipse.setLocation(300.Color.graphics.display.display. e.gc. * * @author jan bodnar * website zetcode.gc. 105.Display. } } private void drawRectangles(PaintEvent e) { Color c1 = new Color(e.swt. 15.gc. 15.gc.gc. e. * The interiors are filled with * different colors. Color c3 = new Color(e. e. 33.widgets.com * last modified June 2009 */ public class SWTApp { private Shell shell. while (!shell. the paintControl() is executed. e. c3.dispose().gc.dispose(). 15. } } When a paint event is received.dispose(). } public static void main(String[] args) { Display display = new Display(). shell.display.setBackground(c1). display. we will draw three rectangles and fill them with three different colors. new SWTApp(display). } } In our example.gc. e. 50.dispose().c2. e.dispose(). Resources are released.dispose(). c3. c2. We create a listener for paint events.gc. 423 .dispose(). 50.dispose(). 200). Color c1 = new Color(e. e. 60). We create a color object and fill a rectangle with it. The actual drawing is delegated to the drawRectangles() method. Figure: Colors Basic shapes The next example draws some basic shapes onto the window.gc. 90. c1.addPaintListener(new ColorsPaintListener()). private class ColorsPaintListener implements PaintListener { public void paintControl(PaintEvent e) { drawRectangles(e).fillRectangle(10. 120.Shell.dispose(). org.package com. e.setBackground(new Color(e.setLocation(300.widgets.Color.sleep(). shell.Display. org. } } private void drawShapes(PaintEvent e) { e.swt. org.eclipse. shell.display. public SWTApp(Display display) { shell = new Shell(display).PaintEvent.eclipse. e. 424 .eclipse. shell.gc.fillOval(290.zetcode.widgets.ON).swt.PaintListener. 20.gc. e. org.addPaintListener(new ArcExamplePaintListener()).gc.setSize(430.swt.swt. we draw some * basic shapes of the Java SWT library * * @author jan bodnar * website zetcode.swt. 70).SWT. import import import import import import org. while (!shell. shell. 120.graphics. 80). 80. 300).open().eclipse. org.gc. 150. e.readAndDispatch()) { display.fillRectangle(20.setText("Basic shapes"). } } } private class ArcExamplePaintListener implements PaintListener { public void paintControl(PaintEvent e) { drawShapes(e). e.eclipse.swt.com * last modified June 2009 */ public class SWTApp { private Shell shell.gc.gc. 300).events. 20. 20. 150.isDisposed()) { if (!display. shell. 150)).events.fillRectangle(180.eclipse. 80). /** * ZetCode Java SWT tutorial * * In this program.setAntialias(SWT. 25. 100. 80). 70). 100.fillArc(280.fillOval(20. 115). 25). 20. 80. e. a square. 80). 80). e. 0. Here the fillOval() method draws a circle. 150.fillRoundRectangle(150. Technically.gc. e. 150.gc. 100. e. display. These lines draw a rectangle a square and an ellipse.} e.gc. Figure: Basic shapes Transparent rectangles Transparency is the quality of being able to see through a material. 115). an ellipse. 0. 150. 150. the rays of light can go through the glass and this way we can see objects behind the glass. 100. e. 120.gc.fillOval(20. 80. 25).fillRectangle(20. The easiest way to understand transparency is to imagine a piece of glass or water. public static void main(String[] args) { Display display = new Display().gc. e.gc. 100.dispose(). 80). 150. } } In this example. e.fillRoundRectangle(150. 150.fillOval(290. a circle.gc. 120. 20.gc. a rounded rectangle.gc. e. 425 . 80. 80. new SWTApp(display). 25. These two lines draw a rounded rectangle and an arc. 80. 20. 100. we will create a rectangle.fillArc(280.fillRectangle(180. and an arc. org.widgets. 0.swt. shell. 300).org.gc. shell.eclipse.zetcode. org.setText("Transparent rectangles"). shell.eclipse. /** * ZetCode Java SWT tutorial * * This program draws ten * rectangles with different * levels of transparency * * @author jan bodnar * website zetcode.swt.swt.eclipse.display. we can achieve transparency effects using alpha compositing.Shell.eclipse. org.In computer graphics. while (!shell.dispose().swt.open(). } } } private class ArcExamplePaintListener implements PaintListener { public void paintControl(PaintEvent e) { drawRectangles(e).com) package com.addPaintListener(new ArcExamplePaintListener()). Alpha compositing is the process of combining an image with a background to create the appearance of partial transparency. 426 .setSize(590.isDisposed()) { if (!display.events. The composition process uses an alpha channel. org.widgets. import import import import import org. shell. } } private void drawRectangles(PaintEvent e) { Color blue = new Color(e. public SWTApp(Display display) { shell = new Shell(display).Color. (wikipedia. answers.sleep(). shell. 0.eclipse.PaintEvent.readAndDispatch()) { display.PaintListener.setLocation(300.graphics. 255).Display.events. 120). e.com * last modified June 2009 */ public class SWTApp { private Shell shell.swt. e.PaintListener. 427 .eclipse.SWT. 40.gc.eclipse.gc. org.eclipse.swt.dispose().swt.gc. e. } } In the example we will draw ten rectangles with different levels of transparency. e.widgets.Shell.setBackground(blue).com * last modified June 2009 */ public class SWTApp { private Shell shell.Display. i++) { e.swt.dispose(). } blue.Transform.gc.graphics. Figure: Transparent rectangles Donut In the following example we create an complex shape by rotating a bunch of ellipses. org. new SWTApp(display).swt. /** * ZetCode Java SWT tutorial * * This program creates a donut shape * * @author jan bodnar * website zetcode. org.swt.widgets. 20.PaintEvent. display. package com. 40).eclipse.zetcode.fillRectangle(50 * i.events.setAlpha(i * 25). org. } public static void main(String[] args) { Display display = new Display().setAlpha(i * 25). org.eclipse.swt. for (int i = 1.eclipse. import import import import import import org. i < 11.events. Here we set the aplha transparency value for the painting process. dispose().gc. -40.addPaintListener(new DonutPaintListener()). We move the middle of the axis to the center of the window.display).setTransform(tr). shell.readAndDispatch()) { display. e. } } In this example. rot < 36.drawOval(-125. } } private void drawDonut(PaintEvent e) { int w = e. display. h / 2). for (int rot = 0.gc. 300).gc.setTransform(tr). 80). Transform tr = new Transform(e. 250. The shapes resembles a cookie.translate(w / 2.setSize(430.gc. e. h / 2). shell.isDisposed()) { if (!display. we create a donut. 428 . 300). tr. shell.sleep(). } } } private class DonutPaintListener implements PaintListener { public void paintControl(PaintEvent e) { drawDonut(e). hence the name donut.gc. Transform tr = new Transform(e. e. e. } } public static void main(String[] args) { Display display = new Display(). while (!shell. int h = e.setText("Donut"). rot++) { tr.height.ON).rotate(5f). new SWTApp(display). tr.setAntialias(SWT.gc. e. shell. e. shell.translate(w / 2.width.setLocation(300.open().dispose().setTransform(tr).display).public SWTApp(Display display) { shell = new Shell(display). swt.widgets. import import import import import import import org.graphics. rot++) { tr. rot < 36.setTransform(tr).eclipse.eclipse.eclipse.com * last modified June 2009 */ public class SWTApp { Shell shell.swt.swt.PaintEvent.Display. we draw some text on the window. /** * ZetCode Java SWT tutorial * * This program draws text * on the window * * @author jan bodnar * website zetcode. org. } We do several rotations and draw the ellipses.Shell.eclipse.graphics.swt.gc.rotate(5f).events. e.eclipse. org. 80).swt. org. org.swt. e. org. org.eclipse.widgets.Font.PaintListener.SWT.swt. package com.events.gc. -40. 250. Figure: Donut Drawing text In the next example.eclipse. 429 .Color.zetcode.drawOval(-125.for (int rot = 0. font.addPaintListener(new LyricsExamplePaintListener()).dispose(). } public static void main(String[] args) { Display display = new Display(). "Purisa". 60).open().readAndDispatch()) { display. shell. 20. 25.display.drawText("Most relationships seem so transitory". normal. 20. 25). 10. 10. shell. that we use. SWT.display. while (!shell. shell.drawText("Somebody tell me why I'm on my own".drawText("Who knows how to love without being told".isDisposed()) { if (!display. SWT. e. e. 30). e.setSize(380. 10px. 430 . e. Font font = new Font(e.gc. 25.gc. e.gc.dispose(). new SWTApp(display). e. } } } private class LyricsExamplePaintListener implements PaintListener { public void paintControl(PaintEvent e) { drawLyrics(e). Here we specify the font.setText("Soulmate"). display. 20. 180).drawText("If there's a soulmate for everyone".dispose().setLocation(300.ON). 20.gc.gc.gc.drawText("They're good but not the permanent one". e.NORMAL).gc. Color color = new Color(e.sleep(). shell. e.gc. 20.drawText("Who doesn't long for someone to hold". 20. shell. "Purisa".gc.public SWTApp(Display display) { shell = new Shell(display).setFont(font). Font font = new Font(e.NORMAL). 150). 120). } } private void drawLyrics(PaintEvent e) { e.setForeground(color). } } We display part of the lyrics from the Natasha Bedingfields Soulmate song. 300).display. e. 210).gc. 300). Purisa.setAntialias(SWT. 30).events. org.Image.swt. package com.swt.eclipse. the snake has three joints.zetcode.eclipse.swt. we will create a Nibbles game clone.graphics. import import import import import import import import import org. org. The drawText() method draws text onto the window.eclipse.swt. The snake must avoid the walls and its own body. Figure: Soulmate In this chapter of the Java SWT tutorial.drawText("Most relationships seem so transitory".Color.graphics. we did some painting. Later it was brought to PCs. Initially.graphics.events.Point.eclipse. The snake is controlled with the cursor keys.swt.graphics. its body grows.events.PaintEvent.SWT. The objective is to eat as many apples as possible. org.eclipse.swt.gc.swt.Font.ImageData.eclipse. org. Development The size of each of the joints of a snake is 10px.eclipse.eclipse. we display "Game Over" message in the center of the window. org.swt. org.eclipse.PaintListener.e. It was first created in late 70s. In this game the player controls a snake. Each time the snake eats an apple.KeyAdapter. 431 . Nibbles In this part of the Java SWT tutorial. The game starts immediately.swt. 20. org. org.graphics. Nibbles is an older classic video game. When the game is finished. apple = new Image(display. 0. HEIGHT). this.NULL). private private private private private boolean boolean boolean boolean boolean left = false. private Shell shell.shell = shell.swt. private int apple_x. ALL_DOTS = 900. private Image ball. iib). import org. 0).png"). up = false. inGame = true. setSize(WIDTH. this. this. ImageData iia = new ImageData("apple. private Image head. private int x[] = new int[ALL_DOTS].swt.eclipse. ball = new Image(display. Color col = new Color(shell. ImageData iib = new ImageData("dot. right = true.getDisplay().widgets. private int y[] = new int[ALL_DOTS]. private Display display.import org. private Image apple.dispose(). display = shell.getDisplay(). HEIGHT = 300.png"). iia). private int dots.addPaintListener(new BoardPaintListener()).Canvas.addKeyListener(new BoardKeyListener()). import org. RAND_POS = 29. 432 . public Board(Shell shell) { super(shell.setBackground(col). col.swt. SWT.Shell.eclipse. private int apple_y. this.eclipse.widgets. DOT_SIZE = 10. 0. down = false. private Runnable runnable. 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.Display.widgets. DELAY = 140. timerExec(DELAY.gc. } else { gameOver(e). private class BoardPaintListener implements PaintListener { public void paintControl(PaintEvent e) { Color col = new Color(shell. z < dots. } e. 433 . runnable). y[z] = 50.ImageData iih = new ImageData("head. for (int z = 0. } } public void drawObjects(PaintEvent e) { e. 0.timerExec(DELAY. iih).png"). col. apple_x. checkCollision().drawImage(apple. } locateApple().getDisplay(). 0). for (int z = 0.ON). initGame(shell. apple_y).setAntialias(SWT.dispose(). 0. }. head = new Image(display.getDisplay()). if (inGame) { drawObjects(e). this). redraw(). z++) { x[z] = 50 .gc.gc.setBackground(col). } public void initGame(final Display display) { dots = 3.gc. } display. runnable = new Runnable() { public void run() { if (inGame) { checkApple(). z < dots. e. z++) { } display.z*10. e. }. move().dispose(). (HEIGHT . 255. } public void checkApple() { if ((x[0] == apple_x) && (y[0] == apple_y)) { dots++.size. font. display.getDisplay(). x[z]. y[z] = y[(z . 255.drawText(msg. (WIDTH .y) / 2). } else { e.gc. } public void gameOver(PaintEvent e) { String msg = "Game Over". z > 0. runnable). SWT. y[z]).x) / 2. Font font = new Font(e. locateApple().gc.gc. } if (up) { y[0] -= DOT_SIZE. e.size. "Helvetica".display. 12. } if (right) { x[0] += DOT_SIZE.setForeground(white). e.timerExec(-1.dispose().1)]. Point size = e. } if (down) { y[0] += DOT_SIZE.gc.dispose().gc. e. } } public void move() { for (int z = dots.1)]. 255).dispose(). white.} } if (z == 0) { e.gc. e. x[z].gc. } 434 . } if (left) { x[0] -= DOT_SIZE.drawImage(head.setFont(font).textExtent (msg). Color white = new Color(shell. z--) { x[z] = x[(z .NORMAL). y[z]).drawImage(ball. } } public void locateApple() { int r = (int) (Math.} public void checkCollision() { for (int z = dots. down = false. right = false. if ((key left up = down } == SWT. z--) { if ((z > 4) && (x[0] == x[z]) && (y[0] == y[z])) { inGame = false.keyCode.eclipse. r = (int) (Math.ARROW_LEFT) && (!right)) { = true. apple_x = ((r * DOT_SIZE)). apple_y = ((r * DOT_SIZE)). } 435 . } if (x[0] < 0) { inGame = false.ARROW_UP) && (!down)) { up = true.DOT_SIZE) { inGame = false.KeyEvent e) { int key = e.events. } if ((key == SWT. left = false. up = false. } } if (y[0] > HEIGHT .swt. false.ARROW_RIGHT) && (!left)) { right = true.random() * RAND_POS).random() * RAND_POS). if ((key == SWT.DOT_SIZE) { inGame = false. = false. } private class BoardKeyListener extends KeyAdapter { @Override public void keyPressed(org. z > 0. } if (x[0] > WIDTH . } if (y[0] < 0) { inGame = false. we call three methods. left = false. for (int z = 0. move(). z < dots. y[z]). y coordinates of all possible joints of a snake. If it is true. private int y[] = new int[ALL_DOTS]. The DOT_SIZE is the size of the apple and the dot of the snake. we draw our objects. } display. x[z].drawImage(apple. redraw(). } First we will define some globals used in our game. runnable = new Runnable() { public void run() { if (inGame) { checkApple(). The DELAY constant determines the speed of the game. right = false. The ALL_DOTS constant defines the maximum number of possible dots on the Board. } Inside the paintControl() method.} } } if ((key == SWT. apple_x. The RAND_POS constant is used to calculate a random position of an apple. private int x[] = new int[ALL_DOTS]. apple_y). loads images and starts a timeout function. } else { gameOver(e). this). The WIDTH and HEIGHT constants determine the size of the Board. } }. Otherwise we display "Game over" text. } 436 .drawImage(head. If we are in the game.timerExec(DELAY.ARROW_DOWN) && (!up)) { down = true. The apple and the snake joints. Every DELAY ms. that build the logic of the game.drawImage(ball. public void drawObjects(PaintEvent e) { e. if (inGame) { drawObjects(e). we check the inGame variable.gc. } else { e.gc. These two arrays store x. the run() method is called. The initGame() method initializes variables. y[z]). checkCollision(). x[z]. z++) { if (z == 0) { e.gc. You can change its direction with the cursor keys. for (int z = dots. locateApple(). if (left) { x[0] -= DOT_SIZE. which is represented by a red circle. The locateApple() method locates an apple randomly on the board.1)]. z--) { if ((z > 4) && (x[0] == x[z]) && (y[0] == y[z])) { inGame = false. 437 . if the snake hits the bottom of the Board. we determine if the snake has hit itself or one of the walls. In the checkCollision() method. } This code moves the joints up the chain.1)]. If so. we add another snake joint and call the locateApple() method. if the snake hits one of its joints with the head. The second joint moves where the first was. The rest of the joints move one position up the chain. } Move the head to the left. y[z] = y[(z . z > 0. if the snake has hit the apple object. public void checkApple() { if ((x[0] == apple_x) && (y[0] == apple_y)) { dots++. for (int z = dots. You control the head of the snake. if (y[0] > HEIGHT . look at how the snake is moving. z--) { x[z] = x[(z . which randomly places a new apple object. } } Finish the game.} } The drawObjects() method draws the apple and the joints of the snake. z > 0. the third joint where the second was etc. To understand it.DOT_SIZE) { inGame = false. The first joint of a snake is its head. In the move() method we have the key algorithm of the game. } } The checkApple() method checks. } Finish the game. . public SWTApp(Display display) { shell = new Shell(display). Notice also.eclipse.setText("Nibbles"). 300). If we hit the left cursor key.. we set left variable to true. we determine the keys that were pressed.swt.Display.layout. that when the snake is heading to the right.eclipse. initUI(). shell. shell.swt.zetcode. These line set the x. We get a random number from 0 to RAND_POS .isDisposed()) { 438 .setLocation(300. import org. we create * a Nibbles game clone * * @author jan bodnar * website zetcode. This variable is used in the move() method to change coordinates of the snake object.widgets. import org. import org. = false. 330). apple_y = ((r * DOT_SIZE)).random() * RAND_POS).widgets.ARROW_LEFT) && (!right)) { = true. apple_x = ((r * DOT_SIZE)).eclipse. package com. false.1.FillLayout. shell. /** * ZetCode Java SWT tutorial * * In this code example. if ((key left up = down } == SWT.Shell.setSize(305. shell.open(). we cannot turn immediately to the left.. y coordinates of the apple object.com * last modified June 2009 */ public class SWTApp { private Shell shell. In the keyPressed() method of the BoardKeyListener class.swt.int r = (int) (Math. while (!shell. readAndDispatch()) { display.dispose(). 330).} } if (!display. } new Board(shell). Or I didn't find it. } In this class. 439 . display. shell.setSize(305. new SWTApp(display). shell.sleep(). These two numbers fit my case. there is no platform independent way of calculating the size of the decorations of the window. } public void initUI() { FillLayout layout = new FillLayout(). Borders and title bar. In SWT library. 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. we set up the Nibbles game.setLayout(layout). } public static void main(String[] args) { Display display = new Display(). curves or polygons to represent images. It can be used to create rich user interfaces. Introduction In this part of the Java 2D tutorial. animations. The images used in this tutorial can be downloaded here. Java 2D API Java 2D is an API for drawing two-dimensional graphics using the Java programming language.the tutorial was updated and cleaned. The Java 2D API provides the following capabilities: • A uniform rendering model for display devices and printers 440 . Java 2D Java 2D is an API for drawing two-dimensional graphics using the Java programming language. multimedia applications or various special effects. Raster (bitmap) graphics represent images as a collection of pixels. lines. About This is Java 2D tutorial. Java 2D is a powerful technology. games.Java 2D tutorial This is Java 2D tutorial. The Java 2D tutorial is suitable for beginners and intermediate Java programmers. The advantages of vector graphics are: • • • smaller size ability to zoom indefinitely moving. Both types of computer graphics have advantages and disadvantages. These primitives are created using mathematical equations. Revisions • • September 2008 . Vector and raster graphics. This tutorial will teach you basics of programming in Java 2D. 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. It is aimed at beginners.the tutorial was created. we will learn the basics of programming in Java 2D. Vector graphics There are two different computer graphics. we will introduce the Java 2D technology. In this tutorial. Vector graphics is the use of geometrical primitives such as points. scaling. June 2013 . awt. javax.swing.SwingUtilities. 50).paintComponent(g). g2d. This method is invoked when it is time to paint. text. games.Graphics. java. The paint mechanism The custom painting code should be placed in the paintComponent() method. This method invokes the following three methods: • • • paintComponent() paintBorder() paintChildren() In specific cases. animations. Skeleton. 441 . Simple Java 2D example We will create a simple example of a Java 2D application.java import import import import import package com.swing. Java 2D is technically a superset of the AWT toolkit. It can be used to create rich user interfaces.JFrame. text.awt. and imaging capabilities of the Abstract Windowing Toolkit (AWT). 50. The paint subsystem first calls the paint() method.Graphics2D. AWT was the original toolkit for creating user interfaces and graphics in Java. For compatibility purposes. javax. } @Override public void paintComponent(Graphics g) { super. we override the paintComponent() method. Java 2D is a powerful technology. we might want to override the paintBorder() or the paintChildren() methods. In most cases.drawString("Java 2D". java.JPanel.swing. and images A compositing model Enhanced color support Printing documents Control of the quality of the rendering The Java 2D API enhances the graphics. javax.zetcode.• • • • • • A wide range of geometric primitives Hit detection on shapes. class Surface extends JPanel { private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g. multimedia applications or various special effects. class Surface extends JPanel { . public class Skeleton extends JFrame { public Skeleton() { } initUI().. add(new Surface()). We draw a text on a JPanel component. sk. setDefaultCloseOperation(JFrame. setLocationRelativeTo(null). It represents number of devices in a generic way. @Override 442 . } We create a Surface class.drawString("Java 2D". Much of the code repeats throughout the Java 2D tutorial. g2d. This casting is necessary to get access to all advanced operations. 200). It inherits from the JPanel component.EXIT_ON_CLOSE). Graphics2D g2d = (Graphics2D) g. 50).. 50. It extends the old Graphics object. This class will be our drawing panel.invokeLater(new Runnable() { @Override public void run() { Skeleton sk = new Skeleton(). Here we draw a string on the panel with the drawString() method.} } doDrawing(g). } public static void main(String[] args) { SwingUtilities. The Graphics2D class is a fundamental class for rendering graphics in Java 2D.setVisible(true). private void initUI() { setTitle("Simple Java 2D example"). setSize(300. } } } }). }). } We create an instance of our code example and make it visible on the screen. Standard Edition 6 API Specification 443 .paintComponent(g). } The initUI() method initiates the user interface of the application.setVisible(true). The surface is added to the JFrame container. The super. Figure: Simple Java 2D example Reference The following resources were used to create this tutorial: • Java Platform.paintComponent() method calls the method of the parent class.public void paintComponent(Graphics g) { super. It is used to ensure that all UI updates are concurrency-safe. We delegate the drawing to the doDrawing() method. doDrawing(g).. } Custom painting is performed inside the paintComponent() method. The invokeLater() method places the application on the Swing Event Queue. It does some necessary work to prepare component for drawing. which we override. add(new Surface()). sk. private void initUI() { . SwingUtilities.invokeLater(new Runnable() { @Override public void run() { Skeleton sk = new Skeleton().. insets. java.awt.drawLine(x. class Surface extends JPanel { private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g. g2d.nextInt()) % h.JFrame. g2d. java.Insets. Basic drawing In this part of the Java 2D tutorial. java.SwingUtilities. i < 1000.insets. javax. y).height .awt. where we supplied one point twice.abs(r. Dimension size = getSize().zetcode.setColor(Color.Color. Points.awt. java.swing. for (int i = 0. javax. int y = Math. We used the drawLine() method. i++) { int x = Math.nextInt()) % w.insets.bottom. y. javax.swing.blue). x.width .awt. Points The most simple graphics primitive is a point.top .JPanel.java import import import import import import import import import package com. Insets insets = getInsets(). } } @Override public void paintComponent(Graphics g) { 444 .abs(r. Random r = new Random(). It is a single dot on the window.• • • 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. we will do some basic drawing. int h = size.Random.swing.Graphics2D.Dimension.right.util.Graphics. but there is no method to to draw a point. java.awt.left . int w = size. There is a Point class for representing a point in a coordinate space. java.insets. insets. We will paint our points in blue color. setSize(350. g2d.insets. setDefaultCloseOperation(JFrame. int y = Math. ps. 250). that we computed above.invokeLater(new Runnable() { @Override public void run() { Points ps = new Points().bottom. One point is difficult to observe.top .super.insets.insets. } } public class Points extends JFrame { public Points() { initUI().abs(r.paintComponent(g).setVisible(true). Random r = new Random(). Dimension size = getSize(). 445 . we will randomly draw 1000 points on the panel surface. int w = int h = size.abs(r. We don't paint there. setLocationRelativeTo(null). Here we calculate the area where we will effectively paint our points.width . add(new Surface()).height .nextInt()) % h. doDrawing(g).blue). int x = Math. Insets insets = getInsets().left .nextInt()) % w. } private void initUI() { setTitle("Points").right. size.EXIT_ON_CLOSE). Therefore. } } } }). } public static void main(String[] args) { SwingUtilities.setColor(Color. We get a random number in range of the size of area. The size of the window includes borders and a titlebar. 30). } } 446 . x. g2d. g2d. 30.zetcode. We specify the same point twice. javax. 200).swing.paintComponent(g). 30.swing. 200).drawLine(200. Figure: Points Lines A line is a simple graphics primitive.SwingUtilities. Lines2.drawLine(30. 30). As we already mentioned. class Surface extends JPanel { private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g.JPanel. 30.awt. javax. we use the drawLine() method. g2d. 30.Graphics.drawLine(200.drawLine(30.g2d. } @Override public void paintComponent(Graphics g) { super. y).Graphics2D. g2d. 200.drawLine(x. 200. Lines are drawn with the drawLine() method. javax. y. Here we draw the point.awt. 200.JFrame. java. java. 200.swing. doDrawing(g). A line is an object which connects two points.java import import import import import package com. add(new Surface()). A straight line is drawn. } public static void main(String[] args) { SwingUtilities. } private void initUI() { setTitle("Lines"). }). setSize(350.public class Lines2 extends JFrame { public Lines2() { initUI(). 30. setLocationRelativeTo(null). lines. 200. g2d. The parameters of the method are the x.drawLine(30. 30). y coordinates of the two points. Figure: Lines 447 .EXIT_ON_CLOSE). 250). } } } We draw a simple object with four lines.setVisible(true).invokeLater(new Runnable() { @Override public void run() { Lines2 lines = new Lines2(). setDefaultCloseOperation(JFrame. g2d. javax. dash2. javax. 1f}. {4f. 1f.JFrame. 0f. doDrawing(g). end caps.drawLine(20.Graphics2D. dash1. 2f}.drawLine(20. BasicStroke. 1.JOIN_ROUND. java. 200). 448 . 250.0f. 80). 250. BasicStroke bs4 = new BasicStroke(1. 250. 2f). 120). BasicStroke bs3 = new BasicStroke(1. 80. 1. 160).setStroke(bs1). 1f}. BasicStroke.0f.CAP_BUTT. g2d.Graphics. BasicStroke bs1 = new BasicStroke(1. } @Override public void paintComponent(Graphics g) { super.BasicStroke The BasicStroke class defines a basic set of rendering attributes for the outlines of graphics primitives.JOIN_ROUND. 40). 160.SwingUtilities.swing. 250.drawLine(20. BasicStrokes. g2d.BasicStroke. 200.setStroke(bs3).java package com. float[] float[] float[] float[] dash1 dash2 dash3 dash4 = = = = {2f. BasicStroke. BasicStroke.JPanel. g2d. g2d. BasicStroke. {1f.setStroke(bs4).JOIN_ROUND. 2f}. BasicStroke.drawLine(20. g2d.CAP_BUTT. g2d.setStroke(bs2). g2d.paintComponent(g). 1. 2f). 120. java. BasicStroke.drawLine(20. dash4. BasicStroke bs2 = new BasicStroke(1. 2f).awt. 2f). 40.CAP_BUTT. These rendering attributes include width. miter limit and dash attributes. 4f. import import import import import import java. class Surface extends JPanel { private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g.swing. {4f. 1. BasicStroke.awt. g2d. line joins.awt.JOIN_ROUND.0f.zetcode.swing.0f. 0f.CAP_BUTT. dash3. javax. 250. We use the setStroke() method to apply the BasicStroke to the current graphics context. BasicStroke.EXIT_ON_CLOSE). } private void initUI() { setTitle("Basic strokes"). } } } In this example. }. bs. } public static void main(String[] args) { SwingUtilities. BasicStroke. }. add(new Surface()).setVisible(true). Finally.drawLine(20. g2d. float[] float[] float[] float[] dash1 dash2 dash3 dash4 = = = = { { { { 2f. Here we define four different dash patterns.0f. 250.JOIN_ROUND. 1. 449 . 80). This line constructs a BasicStroke object. 0f. g2d. setSize(280. which is created by mixing opaque and transparent sections. 4f. 1f. 80. we show various types of dashes. BasicStroke bs1 = new BasicStroke(1. 4f. dash1. 2f ). 2f 1f 2f 1f }.} } public class BasicStrokes extends JFrame { public BasicStrokes() { initUI(). }). 1f. we draw the line.setStroke(bs1). 270).CAP_BUTT. A dash attribute is a pattern. 4f. setLocationRelativeTo(null).invokeLater(new Runnable() { @Override public void run() { BasicStrokes bs = new BasicStrokes(). }. setDefaultCloseOperation(JFrame. 0f. BasicStroke.VALUE_ANTIALIAS_ON). java.awt. javax.RenderingHints. java. 250. BasicStroke.JOIN_BEVEL).zetcode.VALUE_RENDER_QUALITY). CAP_ROUND and CAP_SQUARE.swing.awt. g2d.put(RenderingHints. java. BasicStroke bs1 = new BasicStroke(8.swing.java import import import import import import import package com.drawLine(20. CAP_BUTT.KEY_ANTIALIASING.Graphics.CAP_ROUND. javax. java.Graphics2D.SwingUtilities. RenderingHints rh = new RenderingHints( RenderingHints. Caps.KEY_RENDERING. 450 .awt. BasicStroke bs2 = new BasicStroke(8.setStroke(bs2). RenderingHints.BasicStroke.JPanel. class Surface extends JPanel { private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g.swing. 30. BasicStroke. javax. 30). BasicStroke.setStroke(bs1). rh. RenderingHints.Figure: Basic strokes Caps Caps are decorations applied to the ends of unclosed subpaths and dash segments.JOIN_BEVEL). g2d. There are three different end caps in Java 2D.CAP_BUTT. g2d.awt.setRenderingHints(rh). g2d.JFrame. BasicStroke. 451 . BasicStroke. 254. g2d.drawLine(250.JOIN_BEVEL).g2d. 250.CAP_SQUARE.paintComponent(g). add(new Surface()). setSize(280. 20. BasicStroke.drawLine(20. 80).invokeLater(new Runnable() { @Override public void run() { Caps caps = new Caps(). we show all three types of end caps. } } } In our example. BasicStroke bs4 = new BasicStroke(). 140). g2d. 140).EXIT_ON_CLOSE). setLocationRelativeTo(null).drawLine(20. } @Override public void paintComponent(Graphics g) { super. g2d. doDrawing(g). BasicStroke bs1 = new BasicStroke(8. caps.setVisible(true). } } public class Caps extends JFrame { public Caps() { initUI(). g2d.drawLine(254. 80. } public static void main(String[] args) { SwingUtilities. BasicStroke bs3 = new BasicStroke(8.setStroke(bs1). BasicStroke.drawLine(20. g2d. setDefaultCloseOperation(JFrame.setStroke(bs3).CAP_BUTT. 130). 270). 20. 20.setStroke(bs4).JOIN_BEVEL). } private void initUI() { setTitle("Caps"). 140). 130. g2d. 250. }). 250. 20. g2d. import import import import import import java. 50).drawLine(250. javax. g2d.setStroke(bs1). 80. BasicStroke bs1 = new BasicStroke(8.java package com. It is clear from the picture. 20. There are three decorations are. 254.JPanel.awt. Joins.drawLine(20. 250. BasicStroke. g2d. g2d. javax.BasicStroke. java.awt. In our case a line is 8px thick.Graphics.swing. 140).drawLine(254. class Surface extends JPanel { private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g. 15.Graphics2D. Lines with CAP_ROUND and CAP_SQUARE are bigger than the line with CAP_BUTT.awt. 4px on the left and 4px on the right. 140). 20.CAP_ROUND. java. javax.zetcode.JOIN_BEVEL). g2d. 20. g2d.swing. 140).swing. JOIN_MITER and JOIN_ROUND.SwingUtilities. We draw three vertical lines to explain the differences among the end caps. Lines are bigger by 8px.drawRect(15.JFrame. Exactly how much bigger depends on the line size.A basic stroke with a butt cap is created and applied. 452 . JOIN_BEVEL. BasicStroke. 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. 20. 50). doDrawing(g). This code example show three different line joins in action. 80.JOIN_MITER).drawRect(125. } } } }).BasicStroke bs2 = new BasicStroke(8. 80. Here we create a rectangle with a JOIN_BEVEL join.JOIN_ROUND).setStroke(bs3). g2d. 50). add(new Surface()). } } public class Joins extends JFrame { public Joins() { } initUI(). 15. BasicStroke.CAP_ROUND. setLocationRelativeTo(null).paintComponent(g). g2d. BasicStroke. setSize(340. 15.JOIN_BEVEL).invokeLater(new Runnable() { @Override public void run() { Joins js = new Joins(). 15. BasicStroke. } public static void main(String[] args) { SwingUtilities. setDefaultCloseOperation(JFrame.setStroke(bs2).EXIT_ON_CLOSE). g2d. 50). 110).CAP_ROUND. 80. private void initUI() { setTitle("Joins"). js. BasicStroke bs3 = new BasicStroke(8. 453 .CAP_ROUND. BasicStroke. BasicStroke.drawRect(235. g2d. g2d. } @Override public void paintComponent(Graphics g) { super. BasicStroke.setStroke(bs1).drawRect(15.setVisible(true). BasicStroke bs1 = new BasicStroke(8. g2d. fillRect(120.fill(new Ellipse2D. class Surface extends JPanel { private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g.swing.awt. rh. 150). 25.zetcode.swing.VALUE_ANTIALIAS_ON). BasicShapes. gradients and textures.setColor(new Color(150. 20. g2d.awt. Then we will fill shapes with solid colors. 50). RenderingHints.KEY_ANTIALIASING. g2d. we did some basic drawing. 150)). 454 . javax. 150. 100. 50. 60.KEY_RENDERING. RenderingHints.fillArc(120.awt. javax. java. 20. import import import import import import import import java.Figure: Joins In this part of the Java 2D tutorial.Ellipse2D.Double(10. 20.Graphics2D. g2d.JFrame. 5. 70. 100)). Shapes and fills In this part of the Java 2D tutorial. java.fillRoundRect(250. g2d. 80.geom.VALUE_RENDER_QUALITY).SwingUtilities. 100. javax. java.setRenderingHints(rh).RenderingHints.Color. 90.Graphics. we will create some basic and more advanced shapes. 110.put(RenderingHints. g2d.awt. RenderingHints rh = new RenderingHints(RenderingHints.awt. 130.fillRect(30. 25). Basic shapes First we draw some basic Java 2D shapes.java package com.swing. java.JPanel. g2d. 60). g2d. g2d.fillRoundRect(250. } } } In this example. } } public class BasicShapes extends JFrame { public BasicShapes() { initUI(). The last two parameters are the width and the height of the shape.invokeLater(new Runnable() { @Override public void run() { BasicShapes bs = new BasicShapes(). 90.fillOval(270. 150. 150)). The fillRect() method is used to draw both a rectangle and a square. 25. A square. bs. 25). 50. add(new Surface()). 20.paintComponent(g). 50).setVisible(true). setDefaultCloseOperation(JFrame. setLocationRelativeTo(null).EXIT_ON_CLOSE).} g2d. } public static void main(String[] args) { SwingUtilities. a rectangle. 455 . 130. a rounded rectangle. @Override public void paintComponent(Graphics g) { super. 60. y coordinates of a shape to be drawn. g2d.fillRect(20. an ellipse. The shapes will be drawn in a gray background. we draw six basic shapes on the panel. g2d. 60). g2d. doDrawing(g). 20. 250). setSize(350. } private void initUI() { setTitle("Basic Shapes"). 20. 50. }). 70.setColor(new Color(150. 50).fillRect(120. an arc and a circle. The first two parameters are x. 10 }.VALUE_RENDER_QUALITY). { 125.swing.KEY_ANTIALIASING. { 0. The last two parameters are the horizontal and vertical diameters of the arc at the four corners. { 50. g2d. 5). import import import import import import import java. 150 }.JFrame.awt. java.Here we create a rounded rectangle.swing.zetcode. { 75. and quadratic and cubic Bézier curves.KEY_RENDERING. javax.swing. 85 } }. 75 }. { 100. It represents a geometric path constructed from straight lines. javax.setRenderingHint(RenderingHints. javax.translate(25. 85 }.RenderingHints. { 100.Graphics2D. 75 }. Figure: Basic shapes General path More complex shapes can be constructed with a GeneralPath class.awt. java.awt. { 200. 190 }. we will create a star with this class. g2d.java package com. 456 . In the next example.geom. g2d.JPanel. class Surface extends JPanel { private double points[][] = { { 0.SwingUtilities. 85 }. RenderingHints. RenderingHints.VALUE_ANTIALIAS_ON).setRenderingHint(RenderingHints. 125 }.awt. 125 }. java. 190 }. Star.GeneralPath. { 150. private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D)g. { 160. { 40.Graphics. fill(star). private void initUI() { setTitle("Star").length.EXIT_ON_CLOSE). star. { 125. points[k][1]). g2d. k < points. setLocationRelativeTo(null). We will create a star from a series of points. { 100. { 40. { 160. setSize(350. 190 }. k++) star. star. { 75. 85 }. sr. for (int k = 1. 85 }. 457 . points[0][1]). { 200.paintComponent(g).GeneralPath star = new GeneralPath(). doDrawing(g). 125 }. private double points[][] = { { 0. 250). 125 }. { 100. setDefaultCloseOperation(JFrame. 190 }. 75 }. } public static void main(String[] args) { SwingUtilities. 75 }.closePath().invokeLater(new Runnable() { @Override public void run() { Star sr = new Star(). add(new Surface()). } @Override public void paintComponent(Graphics g) { super. } } } }). { 50.setVisible(true). 85 } }.moveTo(points[0][0]. { 150. } } public class Star extends JFrame { public Star() { } initUI(). 150 }. { 0.lineTo(points[k][0]. 10 }. g2d. class Surface extends JPanel { 458 . k < points.swing. To fill rectangles with the current color.Graphics2D. star. Figure: Star Colors The Color class is used to work with colors in Java 2D.lineTo(points[k][0].length.swing. points[0][1]). java.swing.awt. for (int k = 1. Colors. javax. Here we connect all the coordinates of the star. k++) star.closePath().JPanel. we use the fillRect() method. We move to the initial coordinate of the GeneralPath. We close the path and fill the interior of the star.Color. GeneralPath star = new GeneralPath().zetcode. Here we instantiate the GeneralPath class. javax. star.awt.awt. import import import import import import java.fill(star).JFrame.Graphics. javax.moveTo(points[0][0]. java.These are the coordinates of the star.SwingUtilities. points[k][1]).java package com. fillRect(10. 121.fillRect(10.setColor(new Color(70. 100. 10. 1)). 190. g2d. g2d. 10. 167. 231)). 60). 90. 54)).public void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g. doDrawing(g).paintComponent(g). 90. 10.fillRect(250.setColor(new Color(241. 90.setColor(new Color(130.fillRect(130. private void initUI() { setTitle("Colors"). 60). 60).fillRect(10. 100. g2d.setColor(new Color(31. 67. 69)). 90.EXIT_ON_CLOSE). 90.fillRect(130. g2d.setColor(new Color(252.setColor(new Color(217. g2d. g2d. 90. } @Override public void paintComponent(Graphics g) { super. 186)). g2d. 61)). 300).fillRect(250. 190. 60). 90. 211. setSize(360. g2d. 190.setColor(new Color(125. 179. 98. setDefaultCloseOperation(JFrame. 146. g2d. g2d. 90. 90. 60). } } public class Colors extends JFrame { public Colors() { } initUI().setColor(new Color(63. g2d. 116)). setLocationRelativeTo(null). g2d. g2d. g2d. 100. 100.setColor(new Color(42. 84)). 21. g2d.fillRect(250.fillRect(130. g2d. 60). 60). g2d. 60).invokeLater(new Runnable() { 459 . 123)). g2d. add(new Surface()). 60). } public static void main(String[] args) { SwingUtilities. zetcode. gradients are used to create colorful backgrounds and special effects as well as to simulate lights and shadows. gradient is a smooth blending of shades from light to dark or from one color to another.awt. In 2D drawing programs and paint programs. 15. In the example we draw nine colored rectangles. 90. green and blue parts of the new color. 460 . we use the fillRect() method. } } } }).fillRect(10.Color. The setColor() method sets the graphics context's current color to the specified color.@Override public void run() { Colors col = new Colors(). g2d.setColor(new Color(125. Figure: Colors Gradients In computer graphics. The parameters of the constructor are the red. col.java package com.setVisible(true). (answers.com) Gradients. 167. 116)). A new color is created with the Color class. To fill the rectangle with a color. All subsequent graphics operations using this graphics context use this specified color. 60). import java. g2d. 25.swing. 0. Color. g2d. Color. Color.orange.swing.green. g2d. 25. 40). g2d. 20. 40). 200.setPaint(gp1). Color. javax.black. 260. GradientPaint gp1 = new GradientPaint(5.black.awt. g2d. 40). g2d. 25. 300. private void initUI() { 461 . GradientPaint gp2 = new GradientPaint(5. 20.paintComponent(g).fillRect(20.GradientPaint.JPanel.Graphics.setPaint(gp5).black. 300.fillRect(20. doDrawing(g). Color. 40).JFrame. java. 25.yellow.fillRect(20.SwingUtilities. 300. g2d. 40). java. GradientPaint gp5 = new GradientPaint(0. 5.Graphics2D. true). 300. 2.setPaint(gp3).fillRect(20. Color. true). g2d.awt. 15. GradientPaint gp3 = new GradientPaint(5. javax.black.setPaint(gp2). 2. g2d.import import import import import import java.blue. GradientPaint gp4 = new GradientPaint(25. true).black. class Surface extends JPanel { private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g. javax. Color. 20. Color. true). 2. } @Override public void paintComponent(Graphics g) { super. } } public class Gradients extends JFrame { public Gradients() { } initUI(). 20. 0. true). Color.fillRect(20. g2d.red. 80.awt.swing. Color. 300. 140. g2d.setPaint(gp4). 20. setSize(350. To work with gradients.EXIT_ON_CLOSE).setTitle("Gradients"). setDefaultCloseOperation(JFrame.invokeLater(new Runnable() { @Override public void run() { Gradients gr = new Gradients(). g2d. true).setPaint(gp5). The gradient is activated calling the setPaint() method. 25.blue. GradientPaint gp4 = new GradientPaint(25. 25. Color.setVisible(true). Color. 350). By manipulating the color values and the starting and ending points. 15. we can get different results. setLocationRelativeTo(null). } public static void main(String[] args) { SwingUtilities. add(new Surface()). } } } Our code example presents five rectangles with gradients. }).black. 462 . we use the GradientPaint class. gr. } private void loadImages() { try { slate = ImageIO. To work with textures in Java 2D.png")). java.java package com.SwingUtilities.imageio.io. javax.BufferedImage.image.getLogger(Surface. we use the TexturePaint class.logging. TexturePaint slatetp. java.swing. 90. 90. public Surface() { loadImages().File.logging. 60)).read(new File("pane. TexturePaint javatp.Graphics.Logger. TexturePaint panetp.IOException. java. ex).getName()). slatetp = new TexturePaint(slate.log(Level. java = ImageIO.swing.read(new File("slate. java. 0.Graphics2D. pane = ImageIO. javatp = new TexturePaint(java.SEVERE. javax. } } Logger. java.JPanel. javax.png")).util.ImageIO. java. javax.JFrame. Textures.TexturePaint. 60)).util.awt. 0. new Rectangle(0. } catch (IOException ex) { null. BufferedImage pane. import import import import import import import import import import import import import java.png")).Rectangle. new Rectangle(0.awt. java. 463 .swing. class Surface extends JPanel { private private private private private private BufferedImage slate.io. private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g.zetcode. java.awt.class.awt.Figure: Gradients Textures A texture is a bitmap image applied to a shape.awt.read(new File("java.Level. BufferedImage java. 15. 60)).fillRect(250. new Rectangle(0. 464 . doDrawing(g). Using the ImageIO class. 60)). setDefaultCloseOperation(JFrame.fillRect(10.paintComponent(g). 15. g2d. 60). 90.panetp = new TexturePaint(pane. 90.fillRect(130. new Rectangle(0.setVisible(true). g2d. } public static void main(String[] args) { SwingUtilities.png")).EXIT_ON_CLOSE). 60). g2d. add(new Surface()). slatetp = new TexturePaint(slate. } @Override public void paintComponent(Graphics g) { super. we fill three rectangles with three different textures. } } public class Textures extends JFrame { public Textures() { } initUI(). 90.setPaint(panetp). tx. setSize(360.read(new File("slate. setLocationRelativeTo(null). 90. In the code example. 0.invokeLater(new Runnable() { @Override public void run() { Textures tx = new Textures(). } } } }). g2d. slate = ImageIO.setPaint(javatp). g2d. we read the image into the buffered image. private void initUI() { setTitle("Textures").setPaint(slatetp). 120). 90. 60). 0. 15. g2d. wikipedia. 90. We fill a rectangle with a texture. (answers. we will talk about transparency.0f (completely opaque). It implements the basic alpha compositing rules for combining source and destination pixels to achieve blending and transparency effects with graphics and images. we can achieve transparency effects using alpha compositing. To create an AlphaComposite. Alpha compositing is the process of combining an image with a background to create the appearance of partial transparency. The easiest way to understand transparency is to imagine a piece of glass or water. we have covered some basic and more advanced shapes of the Java 2D library. Most often it is AlphaComposite.com. We will provide some basic definitions and several interesting transparency effects. you provide two values. 15.org) The AlphaComposite class is used to work with transparency in Java 2D.setPaint(slatetp). The alpha value can range from 0. 465 . The composition process uses an alpha channel. Transparency explained Transparency is the quality of being able to see through a material. the rays of light can go through the glass and this way we can see objects behind the glass. The rule designator and the alpha value.0f (completely transparent) to 1. Transparent rectangles The first example will draw ten rectangles with different levels of transparency. Technically.fillRect(10. The rule specifies how we combine source and destination pixels. The extra eight bits per pixel serves as a mask and represents 256 levels of translucency. g2d. g2d. In computer graphics. 60). Figure: Textures In this part of the Java 2D tutorial. Alpha channel is an 8-bit layer in a graphics file format that is used for expressing translucency (transparency).SRC_OVER. Transparency In this part of the Java 2D .We create a TexturePaint class out of the buffered image. 20.swing.invokeLater(new Runnable() { @Override public void run() { TransparentRectangles tr = new TransparentRectangles(). } public static void main(String[] args) { SwingUtilities. doDrawing(g).BLUE). } } public class TransparentRectangles extends JFrame { public TransparentRectangles() { initUI(). class Surface extends JPanel { private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g. javax.swing.Graphics. add(new Surface()).getInstance(AlphaComposite.EXIT_ON_CLOSE).SRC_OVER.awt. } } @Override public void paintComponent(Graphics g) { super. g2d. 120).fillRect(50 * i. import import import import import import import java. java. for (int i = 1. javax. 466 .JPanel. g2d.Graphics2D.swing.awt.setComposite(AlphaComposite.Color. 40). java.paintComponent(g). i++) { g2d.1f)).SwingUtilities.JFrame. setLocationRelativeTo(null). } private void initUI() { setTitle("Transparent rectangles"). 40.java package com. setSize(590. javax. i * 0.setColor(Color. i <= 10.AlphaComposite.awt.TransparentRectangles.zetcode. setDefaultCloseOperation(JFrame. java.awt. JFrame. java.zetcode. private Timer timer.awt. In our example we draw 10 blue rectangles with various levels of transparency applied.ActionEvent. public Surface() { loadImage().} } } }).swing. javax.ImageIcon.1f)). java. java.SRC_OVER.swing.event. We use the forementioned AlphaComposite. This is the key line of the example.getInstance(AlphaComposite. class Surface extends JPanel implements ActionListener { private Image img. tr.ActionListener.swing.event.awt. The image will gradually get more transparent until it is completely invisible.setVisible(true). initTimer(). java. javax.AlphaComposite.awt. g2d. private float alpha = 1f. Figure: Transparent rectangles Fade out demo In the next example. javax.Graphics.swing.setComposite(AlphaComposite. javax. java.java import import import import import import import import import import import import package com.awt. javax.awt. i * 0. The alpha value dynamically changes in the for loop.SwingUtilities. FadeOut.Image.SRC_OVER rule.Graphics2D.swing.awt. java.Dimension. we will fade out an image. 467 .JPanel.Timer.awt. setSurfaceSize(). java. getHeight(this). 0.setComposite(AlphaComposite. int w = img.start(). this).SRC_OVER. g2d. private void setSurfaceSize() { int h = img. private void initUI() { 468 .paintComponent(g). } } } public class FadeOut extends JFrame { public FadeOut() { } initUI(). timer.jpg"). timer. setPreferredSize(new Dimension(w.stop(). doDrawing(g).drawImage(img.01f. null). } private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g. alpha)).getImage().} private void loadImage() { } img = new ImageIcon("mushrooms. } private void initTimer() { timer = new Timer(20. 0. if (alpha <= 0) { alpha = 0. } @Override public void paintComponent(Graphics g) { super. h)). } @Override public void actionPerformed(ActionEvent e) { alpha += -0.getWidth(this). g2d.getInstance(AlphaComposite. repaint(). EXIT_ON_CLOSE). alpha += -0. In the actionPerformed() method we gradually decrease the alpha value. repaint().setVisible(true).drawImage(castle. h)).getInstance(AlphaComposite.getWidth(this).setTitle("Fade out").SRC_OVER. g2d. alpha)). this).setComposite(AlphaComposite. setPreferredSize(new Dimension(w. private void initTimer() { timer = new Timer(20. 469 . } The initTimer() method starts the timer. 0. add(new Surface()). } The setSurfaceSize() method finds out the size of the image and sets a preferred size for the panel. fo.invokeLater(new Runnable() { @Override public void run() { FadeOut fo = new FadeOut(). pack(). g2d. } public static void main(String[] args) { SwingUtilities.getHeight(this). }).start(). setLocationRelativeTo(null). 0. } } } With the AlphaComposite we gradually fade out the image on the panel. Note that the alpha value must not be negative. we will change the alpha value and repaint the panel. timer. private void setSurfaceSize() { int h = img. int w = img. setDefaultCloseOperation(JFrame. The preferred size with the combination of the pack() method will display the whole image on the window. In reaction to an action event.01f. The timer fires action events after a specified delay. null). These two methods draw an image with increasing levels of transparency on the panel. 1.65. 0. import import import import import import import import import import import java.5.0.0. 0. {0.java package com. {0. {0.8.0.9. 0.3.8.swing. javax. 0. 0. 0. 0.event. 0. 0. Waiting In this example. javax.65. 0.awt. 0. {0.swing.5. {0. we use transparency effect to create a waiting demo. 1. 0. timer.5}.0. 0. 0.8}.9. 0.SwingUtilities.Graphics. 0. 0. 0.swing. 0.9.15. 0.5.0.15.65}. } private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g.JFrame. 0. private int count.15.0}. class Surface extends JPanel implements ActionListener { private Timer timer.0. 0. Such effects are often used to inform users. 0. 1.awt.Graphics2D.3.3.awt. 0.15.5. 0. 0.ActionEvent. that a line is moving. } private void initTimer() { timer = new Timer(80.65. java.65.} }.8. that a lengthy task is going on behind the scenes.9.9}. 1.3. java.0. An example is streaming video over the Internet.65.3. 0.9. 1.0.0. public Surface() { initTimer(). 0.awt. java. 0. 0.The method repaints the component. 0.start(). 1.30.0.5. 0.15. this). javax. private final double[][] trs = { {0.awt.awt. 0. {1. 0.5.9. 0. javax.RenderingHints. Waiting.65.15. 0. 0. java.zetcode. 0. 0.15}. 0. 0.3}. 0. 0. 1.0. 470 .80.0. timer. 0.Timer. 0.AlphaComposite.0. 0. We will draw 8 lines that will gradually fade out creating an illusion.BasicStroke.event. java.30.65.awt.8.JPanel. 0.swing.setInitialDelay(190). 0. It calls the paint() method of this component (JPanel) as soon as possible.5. java.8. {0.9.ActionListener. 0.8.0.0.15. doDrawing(g).CAP_ROUND.paintComponent(g).setRenderingHint(RenderingHints. setSize(300. i < 8.drawLine(0. } @Override public void actionPerformed(ActionEvent e) { repaint(). g2d.KEY_RENDERING. -10. add(new Surface()).getInstance(AlphaComposite.rotate(Math. private void initUI() { setTitle("Waiting").EXIT_ON_CLOSE).VALUE_RENDER_QUALITY). int height = getHeight(). g2d. RenderingHints.translate(width / 2.JOIN_ROUND)). (float) trs[count % 8][i])).setRenderingHint(RenderingHints. 0. g2d. } } @Override public void paintComponent(Graphics g) { super. BasicStroke. count++.g2d.VALUE_ANTIALIAS_ON). i++) { g2d. height / 2).PI / 4f). for (int i = 0. setDefaultCloseOperation(JFrame. g2d.setComposite(AlphaComposite. -40). } } public class Waiting extends JFrame { public Waiting() { } initUI().SRC_OVER.invokeLater(new Runnable() { @Override 471 . int width = getWidth(). g2d. setLocationRelativeTo(null). RenderingHints. BasicStroke.KEY_ANTIALIASING.setStroke(new BasicStroke(3. } public static void main(String[] args) { SwingUtilities. 200). setStroke(new BasicStroke(3.PI/4f).CAP_ROUND. This code will draw each of the eight lines.setVisible(true).public void run() { Waiting wt = new Waiting(). we have talked about transparency. -10. wt. BasicStroke. Each of the 8 lines will continuously use these values. BasicStroke. } } } We draw eight lines with eight different alpha values. We make the lines a bit thicker. g2d.drawLine(0. The rotate() method is used to rotate the lines alongside a circle. Composition In this part of the Java 2D programming tutorial. }. This is a two dimensional array of transparency values used in this demo.JOIN_ROUND)). We draw the lines with rounded caps. so that they are better visible. Compositing is the combining of visual elements from separate sources into single images. g2d. Figure: Waiting In this part of the Java 2D tutorial. 472 .. private final double[][] trs = { . }).rotate(Math. 0. each for one state. They are used to create the illusion that all those elements are parts of the same scene. There are 8 rows. g2d. we will define compositing operations. -40).. 473 .awt. 40).setComposite(ac). gbi. Compositing. 5. The first object drawn is called a destination. y = 20. where the two objects overlap. i < rules. java. gbi. AlphaComposite.Graphics. The second one a source. AlphaComposite. the pixels of the source object will be drawn.awt. AlphaComposite. The AlphaComposite class implements basic alpha compositing rules for combining source and destination colors to achieve blending and transparency effects with graphics and images.setPaint(Color. i = 0. 0. Graphics2D gbi = buffImg.8f).Compositing is widely used in film industry to create crowds. BufferedImage buffImg = new BufferedImage(60. java. for (int x = 20.swing. 0. 40.getInstance(rules[i].BufferedImage.awt.swing. 40. class Surface extends JPanel { private int rules[] = { AlphaComposite. The AlphaComposite class determines how these two are going to be blended together. gbi.fillRect(0. gbi. We will show some of them in the next code example.createGraphics().DST_OUT. AlphaComposite.org) Operations There are several compositing operations.JFrame.SRC. If we have a AlphaComposite. private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g.SRC_OVER rule.JPanel.fillRect(5.awt.SRC_OUT }.awt.DST_ATOP. gbi.Color. 40).length.zetcode. (wikipedia.AlphaComposite. java. x += 60.SRC_ATOP.java package com. java.blue). AlphaComposite. javax. BufferedImage. javax.setPaint(Color. import import import import import import import import java.Graphics2D. i++) { AlphaComposite ac = AlphaComposite.DST. entire new worlds which would be expensive or impossible to create otherwise. Say we want to draw two objects on a panel.SwingUtilities.green).TYPE_INT_ARGB).image.swing. 60. javax. DST_OUT. AlphaComposite. } public static void main(String[] args) { SwingUtilities.SRC_OUT }.getInstance(rules[i]). } } } We draw two rectangles and combine them with six different compositing operations. 474 .SRC_ATOP. x. AlphaComposite. setLocationRelativeTo(null). If not sure what compositing rule to use.paintComponent(g).EXIT_ON_CLOSE).drawImage(buffImg. cm. setSize(400. y. setDefaultCloseOperation(JFrame.setVisible(true). AlphaComposite.} } g2d. add(new Surface()). Here we have six different compositing rules. doDrawing(g). } } public class Composition extends JFrame { public Composition() { setTitle("Composition"). @Override public void paintComponent(Graphics g) { super. AlphaComposite. private int rules[] = { AlphaComposite.SRC. Only two of these have practical significance. pick up the SRC_OVER rule. null). }). AlphaComposite ac = AlphaComposite. AlphaComposite.DST.DST_ATOP.invokeLater(new Runnable() { @Override public void run() { Composition cm = new Composition(). Here we get the AlphaComposite class. 120). g2d. class Surface extends JPanel implements ActionListener { private private private private Image Image Timer float sun. We use a buffer image to perform the compositing operations.awt. We will use composition technique in this animation.SwingUtilities.Timer.awt.swing.awt. java. null).ImageIcon. timer.BufferedImage.awt. javax. javax. x.awt.JFrame.Graphics. alpha. java.zetcode.ActionEvent. gbi. y. 60. javax. Sets the Composite for the Graphics2D context.image. java.swing. Figure: Composition Sun and Cloud In the next example we show the Sun coming from behind a cloud. SunAndCloud.java import import import import import import import import import import import import package com. Graphics2D gbi = buffImg. BufferedImage.TYPE_INT_ARGB). We draw the image on the panel. initTimer(). public Surface() { loadImages().awt. 475 . javax. java. java.swing.Graphics2D.BufferedImage buffImg = new BufferedImage(60. javax.swing.drawImage(buffImg. java. cloud.swing.event.Image.event.setComposite(ac).createGraphics().JPanel.ActionListener. java.awt.AlphaComposite. png").getInstance(AlphaComposite. alpha). } @Override public void paintComponent(Graphics g) { super. 30.paintComponent(g). 40.drawImage(buffImg.getImage(). gbi. null). timer. if (alpha <= 0) { alpha = 0.getImage().start(). timer.setComposite(ac). } @Override public void actionPerformed(ActionEvent e) { alpha -= 0. } private void initTimer() { timer = new Timer(800.TYPE_INT_ARGB).createGraphics().drawImage(sun. cloud = new ImageIcon("cloud. gbi. AlphaComposite ac = AlphaComposite. } } repaint(). 140. gbi. Graphics2D gbi = buffImg.stop(). this).} private void loadImages() { sun = new ImageIcon("sun.SRC_OVER. } public class SunAndCloud extends JFrame { public SunAndCloud() { 476 .drawImage(cloud. 0.1. alpha = 1f. BufferedImage buffImg = new BufferedImage(220.out. 20. null). } private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g."). BufferedImage.println("Timer stopped. System. 20. g2d.png"). 0. doDrawing(g). null). gbi. The Sun comes from behind the cloud.drawImage(sun. private void initTimer() { timer = new Timer(800.getInstance(AlphaComposite. add(new Surface()).drawImage(cloud.SRC_OVER. setDefaultCloseOperation(JFrame.setVisible(true).drawImage(buffImg. setSize(300. timer. } We load two images from the disk. private void loadImages() { sun = new ImageIcon("sun.getImage().invokeLater(new Runnable() { @Override public void run() { SunAndCloud sac = new SunAndCloud(). We use the AlphaComposite. alpha). 0. 477 . g2d. setLocationRelativeTo(null). 0. 20. AlphaComposite ac = AlphaComposite. null). } public static void main(String[] args) { SwingUtilities. } Inside the initTimer() method the timer is activated. gbi.} initUI().start().EXIT_ON_CLOSE). 30.SRC_OVER rule. gbi. 20.png"). alpha = 1f. private void initUI() { setTitle("Sun and Cloud"). The cloud finally disappears. null). Here the source blends with destination and overwrites empty pixels. this). cloud = new ImageIcon("cloud.setComposite(ac).png"). } } } }). 40. sac.getImage(). 210). null). Timer.swing.RenderingHints. javax. java.awt.awt.The images are rendered into a BufferedImage and are later copied to the screen.ImageIcon. Clipping. 478 .Graphics.awt.Graphics2D.event. java. we will talk about clipping. javax.swing.awt. The setComposite() specifies how new pixels are to be combined with the existing pixels on the graphics device during the rendering process.zetcode. java. java.awt.swing.event. In the following example we will be clipping an image. import import import import import import import import import import import import import java.swing.ActionEvent.Image.geom. java. javax. javax.ActionListener. Clipping In this part of the Java 2D tutorial. This is done for efficiency reasons and to create various effects. Figure: Sun & Cloud In this part of the Java 2D tutorial. javax.awt.awt.java package com. Clipping Clipping is restricting of drawing to a certain area. java.Dimension.swing.JPanel.Ellipse2D.JFrame.awt. java. class Surface extends JPanel implements ActionListener { private int pos_x = 8. we have talked about image composition.SwingUtilities. 5.start(). initTimer().jpg"). private Image image. } private void initTimer() { timer = new Timer(35. rh.private int pos_y = 8. 5.getWidth(this). null). determineAndSetImageSize(). private Timer timer. this). g2d.getHeight(this).put(RenderingHints.KEY_RENDERING. radius)). } private void loadImage() { image = new ImageIcon("mushrooms.VALUE_RENDER_QUALITY).VALUE_ANTIALIAS_ON). h)). RenderingHints. int h = getHeight().Double(pos_x. pos_y.drawImage(image. } private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g. private int radius = 90. 479 . private double delta[] = { 3.getImage(). } @Override public void actionPerformed(ActionEvent e) { int w = getWidth(). RenderingHints. public Surface() { loadImage(). 3 }. @Override public void paintComponent(Graphics g) { super. } g2d.paintComponent(g). timer. RenderingHints rh = new RenderingHints(RenderingHints. int w = image. doDrawing(g). } private void determineAndSetImageSize() { int h = image.KEY_ANTIALIASING. setPreferredSize(new Dimension(w. radius.setClip(new Ellipse2D. In this example. pos_y.random() % 4 + 5. } else if (pos_y > h .radius) { } delta[0] = -(Math. setDefaultCloseOperation(JFrame. cl.setClip(new Ellipse2D.if (pos_x < 0) { delta[0] = Math. } else if (pos_x > w . radius)). we will clip an image.invokeLater(new Runnable() { @Override public void run() { Clipping cl = new Clipping().random() % 4 + 5. } } } }).setVisible(true). if (pos_y < 0 ) { delta[1] = Math. radius. A circle is moving on the screen and showing a part of the underlying image.random() % 4 + 5). } public static void main(String[] args) { SwingUtilities.EXIT_ON_CLOSE). This is as if we looked through a hole. repaint(). 480 . add(new Surface()). setLocationRelativeTo(null). pos_x += delta[0].Double(pos_x. } } public class Clipping extends JFrame { public Clipping() { } initUI(). pack(). g2d.radius) { } delta[1] = -(Math. pos_y += delta[1].random() % 4 + 5). private void initUI() { setTitle("Clipping"). event. int radius = 60. } If the circle hits the left or the right side of the window.Color.event.geom. if (pos_x < 0) { delta[0] = Math.awt. double delta[] = {1.RenderingHints.java import import import import import import import import import import import import import import package com. Here we restrict drawing to a specific shape. Same for the top and bottom sides.Graphics2D.radius) { delta[0] = -(Math. the direction of the circle movement changes randomly.swing.Ellipse2D. java. double rotate = 1.random() % 4 + 5. java. javax.awt. java.start().awt.swing. Clipping shapes In the following example.geom.This is the key part of the code. } private void initTimer() { timer = new Timer(10. class Surface extends JPanel implements ActionListener { private private private private private private Timer timer.AffineTransform.geom. public Surface() { initTimer().Rectangle. java. java.awt.awt. } 481 . javax. } else if (pos_x > w . java.ActionEvent.JFrame.Timer.awt. this).SwingUtilities.random() % 4 + 5). 1}. int pos_y = 8.awt.GeneralPath. java.swing. java. javax.Graphics. java.zetcode. In our case it is a circle.JPanel.awt.awt. timer. int pos_x = 8.ActionListener. java.awt. we will be clipping two shapes. javax.swing. ClippingShapes. A rectangle and a circle. clip(circle). GeneralPath path = new GeneralPath(). } else if (pos_x > w . g2d.Double(pos_x.createTransformedShape(rect1). h / 2). g2d. doDrawing(g). g2d.clip(path). tx. g2d. w / 2. int w = getWidth().setRenderingHint(RenderingHints. g2d.fill(circle).setRenderingHint(RenderingHints. 0. false).100. 80).VALUE_RENDER_QUALITY). h / 2 . g2d. Rectangle rect1 = new Rectangle(0. AffineTransform tx = new AffineTransform(). } @Override public void paintComponent(Graphics g) { super. 0.append(tx.paintComponent(g). g2d. radius. path. tx. g2d. step(). RenderingHints. 110. 110)). int h = getHeight().toRadians(rotate). RenderingHints. if (pos_x < 0) { delta[0] = 1. } public void step() { int w = getWidth().draw(path). pos_y.VALUE_ANTIALIAS_ON). radius). int h = getHeight(). h)).setColor(new Color(110.translate(w / 2 .private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g.40).draw(circle).radius) { } delta[0] = -1. g2d.KEY_ANTIALIASING.KEY_RENDERING.setClip(new Rectangle(0. if (pos_y < 0) { 482 . 200. Ellipse2D circle = new Ellipse2D.rotate(Math. w. translate(w / 2 . } } public class ClippingShapes extends JFrame { public ClippingShapes() { } initUI(). 200. private void initUI() { setTitle("Clipping shapes"). } public static void main(String[] args) { SwingUtilities. It is always positioned in the middle of the panel. In our example.radius) { delta[1] = -1. repaint(). setDefaultCloseOperation(JFrame.invokeLater(new Runnable() { @Override public void run() { ClippingShapes cs = new ClippingShapes().100. Rectangle rect1 = new Rectangle(0. } else if (pos_y > h . we have a bouncing circle and a rotating rectangle. w / 2. tx.toRadians(rotate).delta[1] = 1. add(new Surface()). h / 2 . When these shapes overlap.EXIT_ON_CLOSE). 483 . setLocationRelativeTo(null). } pos_x += delta[0].40). 300). tx.setVisible(true). 80). the resulting area is filled with color. pos_y += delta[1]. h / 2).rotate(Math. setSize(350. AffineTransform tx = new AffineTransform(). 0. cs. } @Override public void actionPerformed(ActionEvent e) { rotate += 1. } } } }). The rectangle is being rotated. g2d.com) 484 . we have talked about clipping.clip(path). before we draw the shapes. Here we get the shape of the rotated rectangle. A rotation is a transformation that moves a rigid body around a fixed point. false).org. A shear is a transformation that moves an object perpendicular to a given axis. path. g2d. An affine transform is composed of zero or more linear transformations (rotation. We reset the clip areas. the interior of the resulting shape is filled with color. scaling or shear) and translation (shift).setClip(new Rectangle(0. g2d. Transformations In this part of the Java 2D programming tutorial. with greater value on one side of the axis than the other.append(tx. If they overlap. The scale factor is the same in all directions. sources: (wikipedia. 0.GeneralPath path = new GeneralPath(). w. A translation is a transformation that moves every point a constant distance in a specified direction.clip(circle). Figure: Clipping Shapes In this part of the Java 2D tutorial. we will talk about transformations. h)).createTransformedShape(rect1). A scaling is a transformation that enlarges or diminishes objects. freedictionary.fill(circle) Here we restrict drawing to these two shapes. Several linear transformations can be combined into a single matrix. g2d. Translation The following example describes a simple translation.swing.JFrame. javax. 50). 50). } } public class Translation extends JFrame { public Translation() { initUI().awt.java package com. 150)). doDrawing(g). Translation.EXIT_ON_CLOSE). class Surface extends JPanel { private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g.awt. } @Override public void paintComponent(Graphics g) { super.invokeLater(new Runnable() { @Override 485 . add(new Surface()). 150. java. g2d. g2d.swing. setSize(300. 20. 20. g2d.awt.fillRect(20.JPanel.Graphics2D. 80. } private void initUI() { setTitle("Translation"). } public static void main(String[] args) { SwingUtilities. javax.paintComponent(g). 200).translate(150. setLocationRelativeTo(null).SwingUtilities. javax. g2d. 80. 50).The AffineTransform is the class in Java 2D to perform affine transformations.Graphics.Color. import import import import import import java. setDefaultCloseOperation(JFrame.zetcode.setColor(new Color(150. java.fillRect(20.swing. g2d. 20. }). javax.setColor(new Color(150.zetcode. } @Override 486 .swing.fillRect(80.java import import import import import import package com. 150.translate(150.Color. trl.awt. Then we do a translation and draw the same rectangle again. 50).rotate(Math.awt. java. g2d. This line moves the origin of the Graphics2D context to a new point. -50). g2d. 80.swing.swing. 50). java.SwingUtilities. Rotation.fillRect(20. javax.awt.PI/4).JFrame. g2d.JPanel. javax. class Surface extends JPanel { private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g. 50). g2d.Graphics2D.translate(180. Figure: Translation Rotation The next example demonstrates a rotation.setVisible(true). java.public void run() { Translation trl = new Translation(). } } } The example draws a rectangle.Graphics. 150)). g2d. 80. 80. setDefaultCloseOperation(JFrame. 487 . rt.paintComponent(g). doDrawing(g).EXIT_ON_CLOSE).PI/4). setLocationRelativeTo(null). } private void initUI() { setTitle("Rotation").rotate(Math. The rotate() method performs rotation. } } } The example draws a rectangle. Note that the rotation parameter is in radians.invokeLater(new Runnable() { @Override public void run() { Rotation rt = new Rotation(). g2d. 200).public void paintComponent(Graphics g) { super. setSize(300.setVisible(true). } } public class Rotation extends JFrame { public Rotation() { initUI(). performs a translation and a rotation and draws the same rectangle again. add(new Surface()). }). } public static void main(String[] args) { SwingUtilities. awt. 0.fillRect(0. They are the x scale and y scale factor. g2d.translate(170.setColor(new Color(150.zetcode. Scaling is done with the scale() method.awt. 50). 150.swing. 20). 150)). tx2.fillRect(20. java.scale(0. } @Override public void paintComponent(Graphics g) { super. 22).Graphics. private void initUI() { 488 . 0. doDrawing(g). Scaling. g2d. tx2.translate(110.Graphics2D.Color. import import import import import import import java. g2d. by which coordinates are scaled along the x or y axis respectively. 1. AffineTransform tx1 = new AffineTransform().SwingUtilities.setTransform(tx1).swing. java. g2d.swing. we provide two parameters. 50).5.scale(1.5. javax.JPanel. 20. g2d.awt. javax. java.5).java package com.paintComponent(g). 0.5). 80.AffineTransform. 80. g2d.Figure: Rotation Scaling The next example demonstrates scaling of an object.fillRect(0. tx1. In this method. javax. class Surface extends JPanel { private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g.JFrame.awt. } } public class Scaling extends JFrame { public Scaling() { } initUI(). tx1.setTransform(tx2).geom. 50). 80. AffineTransform tx2 = new AffineTransform(). sc. tx2. 1. tx2.awt. Figure: Scaling Shearing In the following example we perform shearing.AffineTransform. java. We have a rectangle.awt.5.awt. Another scaling would be added to the first one. java. AffineTransform tx2 = new AffineTransform().Graphics2D.zetcode. setSize(330. So we need to create and apply a new affine transform. 20). java. First we scale it down and then we scale it up a bit. java.translate(170. } public static void main(String[] args) { SwingUtilities.java package com.awt. } } } }).scale(1.Graphics. import import import import import java.setVisible(true). 489 .invokeLater(new Runnable() { @Override public void run() { Scaling sc = new Scaling().awt. We use the share() method. 160).Color. Scale. add(new Surface()). setDefaultCloseOperation(JFrame.setTitle("Scaling"). setLocationRelativeTo(null).Rectangle.5).EXIT_ON_CLOSE).geom. setColor(Color. 90).green).setTransform(tx2).setTransform(tx3). 1).setColor(Color. setSize(330.shear(0. g2d.red). import javax. } } public class Shearing extends JFrame { public Shearing() { initUI().drawRect(0. 50). setDefaultCloseOperation(JFrame. AffineTransform tx2 = new AffineTransform(). class Surface extends JPanel { private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g.JPanel. tx3. g2d. 0.translate(50. } 490 .translate(50. } private void initUI() { setTitle("Shearing"). 80.setTransform(tx1). g2d. add(new Surface()).swing. import javax. tx2. 270). setLocationRelativeTo(null). tx2.JFrame. AffineTransform tx3 = new AffineTransform(). doDrawing(g).SwingUtilities.swing.swing. 160. 0.blue). AffineTransform tx1 = new AffineTransform(). 0. tx1.drawRect(0. 10). } @Override public void paintComponent(Graphics g) { super. 80. 50).paintComponent(g). tx3. 50)).import javax. g2d.EXIT_ON_CLOSE). g2d. g2d. 90).setColor(Color.shear(0. 1). g2d.draw(new Rectangle(0. g2d.translate(130. g2d. shear(0.Graphics.awt.awt.geom. The two parameters are multipliers by which coordinates are shifted in the direction of the x and y axis.invokeLater(new Runnable() { @Override public void run() { Shearing sh = new Shearing(). java.zetcode.Ellipse2D. java.public static void main(String[] args) { SwingUtilities.JFrame.awt.swing. 1). java. java. java. sh. Donut.Graphics2D.Color. Figure: Shearing Donut In the following example we create an complex shape by rotating an ellipse. They form a structure.awt. java.awt.java import import import import import import import import import package com.RenderingHints.setVisible(true).BasicStroke. Two of them are sheared.awt.geom.AffineTransform. we draw three rectangles in three different colors.awt. tx2.awt. } } } }). javax. java. java. In this example. 491 .Dimension. setRenderingHints(rh).rotate(Math. deg += 5) { AffineTransform at = AffineTransform. doDrawing(g).SwingUtilities.VALUE_ANTIALIAS_ON). setLocationRelativeTo(null). RenderingHints.toRadians(deg)). double w = size.swing.getTranslateInstance(w / 2.createTransformedShape(e)).JPanel. } 492 . RenderingHints. RenderingHints rh = new RenderingHints(RenderingHints.setStroke(new BasicStroke(1)).getHeight().gray). setSize(370.Double(0. g2. import javax. g2. Ellipse2D e = new Ellipse2D. g2.draw(at.put(RenderingHints. 130).KEY_ANTIALIASING.EXIT_ON_CLOSE). at.import javax. rh.getWidth().KEY_RENDERING.VALUE_RENDER_QUALITY). } private void initUI() { setTitle("Donut"). 80. g2. double h = size. setDefaultCloseOperation(JFrame.swing. h / 2). 320).paintComponent(g). deg < 360.setColor(Color. add(new Surface()). for (double deg = 0. class Surface extends JPanel { private void doDrawing(Graphics g) { Graphics2D g2 = (Graphics2D) g. } } public class Donut extends JFrame { public Donut() { initUI(). 0. } } @Override public void paintComponent(Graphics g) { super. Dimension size = getSize(). 0.awt.java package com. we have talked about transformations.createTransformedShape(e)).Graphics.public static void main(String[] args) { SwingUtilities. g2.event.ActionEvent. deg < 360.BasicStroke.event.Ellipse2D.setStroke(new BasicStroke(1)). we will show some effects. 130). java. In this part of the Java 2D tutorial.gray). java.invokeLater(new Runnable() { @Override public void run() { Donut dn = new Donut().awt. Effects In this part of the Java 2D programming tutorial. } After several rotations.awt. Bubbles. The example comes from the Java 2D demo. java. Bubbles In the first example. at. 493 . } } } }). import import import import import import import import import import java. java. java.getTranslateInstance(w / 2.swing.JFrame.toRadians(deg)).Double(0.awt.awt. we create a donut shape.setColor(Color. g2.ActionListener. g2. dn. javax. there is a donut.Graphics2D. java. deg += 5) { AffineTransform at = AffineTransform.draw(at. h / 2).rotate(Math.RenderingHints. java. In the beginning there was an ellipse.setVisible(true). 80. java. In this example.awt.Color. for (double deg = 0.Dimension.awt. Ellipse2D e = new Ellipse2D. that randomly appear and disappear on the screen.awt. we will see growing colored bubbles.zetcode.awt.geom. cyan.green. for (int i = 0.Timer.lightGray. 494 .magenta. = 1. Color. double esize[].SwingUtilities. double maxSize = 0.random() * (w .pink. public Surface() { initSurface().random(). i++) { ellipses[i] = new Ellipse2D. } private void initSurface() { setBackground(Color.Float(). Color.black). Math.setInitialDelay(190). initEllipses().white }.start(). import javax.(maxSize / 2)). initTimer().0f.orange. } private void initEllipses() { int w = 350. Color.swing.import javax. Color. Color. Color. import javax.length. w.random() * (h . Timer timer.blue. float estroke[].(maxSize / 2)). esize = new double[ellipses.swing.yellow. Math.length].length].red. timer. Color. estroke = new float[ellipses. class Surface extends JPanel implements ActionListener { private Color colors[] = { Color.swing. } } private void initTimer() { timer = new Timer(30. int h) { esize[i] = estroke[i] double x = double y = size. posRandEllipses(i. maxSize * Math. private private private private private Ellipse2D. maxSize = w / 10. h). timer. double size. } private void posRandEllipses(int i. int w. Color.Float[] ellipses.Float[25]. ellipses = new Ellipse2D.JPanel. this). i < ellipses. Color. int h = 250. paintComponent(g). g2. size). 495 . } } } private void drawEllipses(Graphics2D g2) { for (int i = 0.width. i < ellipses. h).VALUE_ANTIALIAS_ON). } } private void doDrawing(Graphics g) { Graphics2D g2 = (Graphics2D) g. private void doStep(int w. size.} ellipses[i]. int h) { for (int i = 0. Dimension size = getSize(). if (esize[i] > maxSize) { posRandEllipses(i. RenderingHints rh = new RenderingHints(RenderingHints. } else { ellipses[i].setRenderingHints(rh).height). 1. i++) { estroke[i] += 0.setFrame(x. i++) { g2.KEY_ANTIALIASING. i < ellipses.KEY_RENDERING. ellipses[i]. RenderingHints. g2.length. drawEllipses(g2). esize[i]). y. g2. } @Override public void actionPerformed(ActionEvent e) { repaint(). w. RenderingHints. doStep(size. } @Override public void paintComponent(Graphics g) { super.VALUE_RENDER_QUALITY).length.setColor(colors[i % colors.025f. esize[i].put(RenderingHints.getY(). size.setStroke(new BasicStroke(estroke[i])).setFrame(ellipses[i]. rh. esize[i]++.draw(ellipses[i]).getX(). doDrawing(g).length]). yellow.Float[25]. setLocationRelativeTo(null). estroke = new float[ellipses.green.pink.orange.cyan. private void initSurface() { setBackground(Color. 496 . Both size and stroke of the bubble grow during the animation.black). esize = new double[ellipses. ellipses = new Ellipse2D. } public static void main(String[] args) { SwingUtilities. We create three arrays. Color. Color. Color. Color.length]. 250).lightGray.length].invokeLater(new Runnable() { @Override public void run() { Bubbles bb = new Bubbles(). bb.setVisible(true). An array for the ellipses (a circle is a special case of an ellipse).blue. Color. Color. private Color colors[] = { Color. } } } This is bubbles example. Color.white }. } The initSurface() method sets a black background for the panel. an array for the size of each of the ellipses and an array for ellipses' strokes. Color.} } public class Bubbles extends JFrame { public Bubbles() { initUI(). Color.magenta. }). setDefaultCloseOperation(JFrame. setSize(350. } private void initUI() { setTitle("Bubbles"). These colors will be used to paint the bubbles. add(new Surface()).red.EXIT_ON_CLOSE). } } The ellipses array is filled with ellipse objects. size). double x = Math. } } } 497 . estroke[i] = 1. timer. posRandEllipses(i.random() * (h . esize[i]). private void posRandEllipses(int i.Float(). i++) { ellipses[i] = new Ellipse2D. The posRandEllipses() method positions randomly the ellipse objects on the window. 1. if (esize[i] > maxSize) { posRandEllipses(i.setFrame(x.getY(). It is used for the animation of the example.setInitialDelay(190). double y = Math. } The posRandEllipses() method positions randomly the ellipses on the window.random() * (w .(maxSize / 2)). The esize and estroke arrays are filled with values. ellipses[i]. timer. The initial sizes of the ellipses are chosen also randomly. private void initTimer() { timer = new Timer(30.start(). h). private void doStep(int w. esize[i]++. w. int w. int h) { for (int i = 0. maxSize * Math. The setFrame() method sets the location and size of the framing rectangle of an ellipse. maxSize = w / 10. h). i < ellipses. int h = 250. double size. } A timer object is created ans started.length. w.setFrame(ellipses[i]. } else { ellipses[i]. size. i < ellipses. ellipses[i]. y. i++) { estroke[i] += 0.(maxSize / 2)). this).private void initEllipses() { int w = 350.0f.random().length.getX(). int h) { esize[i] = size. esize[i]. for (int i = 0.025f. 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 Graphics2D.drawImage(mshi. null).ConvolveOp.image.logging.java package com.awt.logging. java. int h = bufimg.IOException. SPACE.getWidth(). import import import import import import import import import import import import import import import java. showing an unfocused vision of a human being etc. It is a mathematical operation which is also used in edge detection or noise elimination. SPACE).io. For example creating speed illusion.BufferedImage. To blur an image.swing. The same arithmetic is repeated for every pixel in the image.awt.JPanel.Kernel. javax. } Both images are painted on the panel. java.Logger.Graphics. java. h+2*SPACE). private void setSurfaceSize() { int w = bufimg. we use a convolution operation. 511 . java.Level.image. java. Dimension d = new Dimension(3*SPACE+2*w.drawImage(bufimg.getWidth(). 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. 2*SPACE+bufimg. applying a kernel to an image can be visualized as a small grid (the kernel) moving across a substantially larger grid (the image). java.awt.imageio.JFrame.image.com) BlurredImage. null. javax.private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g.Dimension.BufferedImageOp.getHeight().swing. (developer.SwingUtilities. Convolutions are per-pixel operations.File.image.util.ImageIO. } setPreferredSize(d).apple. A blur means an unfocused image. Since images can also be thought of as two-dimensional grids of numbers. performing calculations along the way. Blurred image The next code example will blur an image. Blur operations can be used in various graphical effects.awt. java. java.io. javax.awt. g2d. We set the preferred size for the panel.awt. java. javax.zetcode. A kernel can be thought of as a two-dimensional grid of numbers that passes over each pixel of an image in sequence.swing. SPACE.awt. java. The blur filter operation replaces each pixel in image with an average of the pixel and its neighbours. g2d.util. mshi. new BufferedImage(mshi.log(Level.getType())). null). blurKernel)). } private void loadImage() { try { mshi = ImageIO. BufferedImage. g. blurKernel = { 9f. createBlurredImage(). mshi.getName()).getWidth(null). public Surface() { loadImage().TYPE_INT_BGR).drawImage(mshi.filter(mshi. } } Logger. } 512 .class Surface extends JPanel { private BufferedImage mshi. null.dispose(). 1 / 9f. d. 3.read(new File("mushrooms. mshi. } private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g.drawImage(mshi. 1 / 9f. setSurfaceSize(). private BufferedImage databuf. setPreferredSize(d).getLogger(Surface.getHeight(null). } private void setSurfaceSize() { Dimension d = new Dimension().class. 3).getWidth(null).height = mshi. } catch (IOException ex) { null.getHeight(). 1 / 9f.jpg")).width = mshi. 3. g2d. Graphics g = databuf.SEVERE. private void createBlurredImage() { databuf = new BufferedImage(mshi. ex). 255. d. 1 / 9f. 9f. g.getWidth(). 1 / 9f BufferedImageOp blur = new ConvolveOp(new Kernel(3. mshi = blur. 1 / 9f. float[] 1 / 1 / 1 / }. 455.getHeight(null).getGraphics(). 9f. log(Level.@Override public void paintComponent(Graphics g) { super.invokeLater(new Runnable() { @Override public void run() { BlurredImage bi = new BlurredImage().class.setVisible(true). 1 / 9f. } } public class BlurredImage extends JFrame { public BlurredImage() { setTitle("Blurred image").getName()). } } Here we use an alternative method of loading an image in Java 2D. 1 / 9f. }).paintComponent(g). setLocationRelativeTo(null). blurKernel = { 9f. 1 / 9f. bi.EXIT_ON_CLOSE). } public static void main(String[] args) { SwingUtilities. This way we directly have a BufferedImage class. pack(). ex).getLogger(Surface. setDefaultCloseOperation(JFrame. } } } In the code example. 1 / 9f. we load an image from the disk. doDrawing(g). 1 / 9f 513 . 1 / 9f.jpg")).SEVERE. null. float[] 1 / 1 / 1 / }. perform a blur operation on the image and display the result on the window. add(new Surface()).read(new File("mushrooms. 9f. private void loadImage() { try { mshi = ImageIO. } catch (IOException ex) { Logger. We use the read() method of the ImageIO class. 9f. 3. Here we apply the blur filter to the image.BufferedImage.AlphaComposite.swing.awt.SwingUtilities. int img_h.com. int img_w. mshi = blur. java. } catch (Exception ex) { null. class Surface extends JPanel { private private private private private BufferedImage image. java.Color. public Surface() { loadImage(). java. The following code example was inspired by the code from jhlabs.awt.Graphics.getLogger(Surface. createReflectedImage(). The values are weights that are applied to the neighbouring values of the pixel being changed. java. javax.Logger.SEVERE. javax. java.awt.jpg")). javax. blurKernel)). ex).java import import import import import import import import import import import import import import package com.This matrix is called a kernel. java.logging.Level.imageio. mshi.getType())). final int SPACE = 30. java.ImageIO. mshi.util. java. Reflection In the next example we show a reflected image.getHeight().filter(mshi.JPanel. } private void loadImage() { try { image = ImageIO.getName()). BufferedImage refImage. 514 . } Logger.read(new File("rotunda.class.Dimension. BufferedImageOp blur = new ConvolveOp(new Kernel(3.swing.logging.awt.Graphics2D.awt.image.log(Level.io. javax.getWidth().File.zetcode.JFrame. Reflection. java. java.GradientPaint.util.awt. new BufferedImage(mshi.awt. getImageSize(). This effect makes an illusion as if the image was reflected in water.swing. 0f). 2 * img_h + 3 * SPACE). g2d. img_w. 0.0f. 0. 0. 0.DST_IN)). rg.0f. 0. g2d.fillRect(0. -1).translate((win_w . rg.translate(0.dispose().createGraphics(). 0.black. } @Override public Dimension getPreferredSize() { } } public class Reflection extends JFrame { return new Dimension(img_w + 2 * SPACE. int gap = 20. win_h). g2d. BufferedImage. img_h. 0. g2d. img_h. opacity))). } private void createReflectedImage() { float opacity = 0.fillRect(0. win_h. null). 0. null). 0. img_h * fadeHeight. } private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g. 0.drawImage(refImage.paintComponent(g). g2d.drawImage(image. img_h = image.TYPE_INT_ARGB). doDrawing(g).scale(1. img_h).0f. win_w.drawImage(image. 0. new Color(0.} private void getImageSize() { img_w = image. win_h / 2 . 0. 0.3f.setPaint(new GradientPaint(0. Color. 2 * img_h + gap). Graphics2D rg = refImage.0f.img_w) / 2. rg.darkGray)).getHeight(). g2d.0f.4f. g2d. int win_w = getWidth(). new Color(0. rg. rg.getInstance(AlphaComposite.getWidth(). null). 0. Color.setPaint(new GradientPaint(0. refImage = new BufferedImage(img_w. 515 . 0.setComposite(AlphaComposite.img_h). } @Override public void paintComponent(Graphics g) { super. float fadeHeight = 0. 0.0f. int win_h = getHeight(). fillRect(0.TYPE_INT_ARGB). The background of the window is filled with a gradient paint.setComposite(AlphaComposite. null). 0.img_w) / 2. setDefaultCloseOperation(JFrame. Color. } public static void main(String[] args) { SwingUtilities.drawImage(image. 0.darkGray)). img_h).img_h). BufferedImage. img_w. null).0f.createGraphics().setPaint(new GradientPaint(0. rg.translate((win_w . rg.fillRect(0. g2d. setLocationRelativeTo(null).setPaint(new GradientPaint(0. 0.DST_IN)). g2d.0f. bi. pack(). add(new Surface()).drawImage(image. rg.0f. new Color(0. This is the most important part of the code. We make the second image transparent. 0.EXIT_ON_CLOSE). 0. } }). rg. Graphics2D rg = refImage.0f. 0.setVisible(true). opacity))). 0. This is achieved with the GradientPaint. g2d. img_h. img_h * fadeHeight. we create an illusion of a reflected image. But the transparency is not constant. 0. win_h / 2 . win_w. 0. Color. g2d.invokeLater(new Runnable() { @Override public void run() { Reflection bi = new Reflection(). win_h). win_h. A copy of a loaded image is created. } } In the example.0f).public Reflection() { initUI().getInstance(AlphaComposite. 0. } private void initUI() { setTitle("Reflection"). 0.0f. 0. The paint is a smooth blending from black to dark gray.0f. The image gradually fades out. new Color(0. img_h. 516 . 0. refImage = new BufferedImage(img_w. 0.black. This code flips the image and translates it below the original image. g2d. Text and Fonts In this part of the Java 2D tutorial. 0. 2 * imageHeight + gap). null). 0.drawImage(refImage. simply take a photograph and place it on the table and flip it. The reflected image is drawn. Figure: Reflection In this part of the Java2D tutorial we have worked with images. Another way to set a preferred size for a component is to override the getPreferredSize() method.translate(0.scale(1. @Override public Dimension getPreferredSize() { } return new Dimension(img_w + 2 * SPACE.The normal image is moved to the center of the window and drawn. To understand what happens. The translation operation is necessary. g2d. 2 * img_h + 3 * SPACE). we will work with texts and fonts. -1). because the scaling operation makes the image upside down and translates the image up. g2d. 517 . i++) { System. Physical and logical. System.getFamily()). In other writing systems. Logical fonts are not actual font libraries. There are basically two types of fonts. ž. answers. } } } We print the name and family of each of the installed fonts.oracle. public class AllFonts { public static void main(String[] args) { GraphicsEnvironment ge = GraphicsEnvironment. a character may be composed of several glyphs. Fonts are among these objects. In the Latin alphabet a glyph typically represents one character.java package com. SansSerif.awt.getFontName() + " : ").out. Georgia. A collection of glyphs with a particular style form a font face.print(fonts[i]. AllFonts. These are latin characters with accents.com. A character is a symbol that represents an item such as a letter. i < fonts. A font is a set of type characters of a particular typeface design and size. The collection of fonts on a Unix. Physical fonts are the actual font libraries. import java.out. GraphicsEnvironment ge = GraphicsEnvironment. Various typefaces include Helvetica.getLocalGraphicsEnvironment(). import java. Font[] fonts = ge. Here we only provide some basic examples. Times or Verdana. Logical font names are mapped to physical fonts by the Java runtime environment.getLocalGraphicsEnvironment(). There are objects that are typical for a particular platform. and DialogInput. a digit or a punctuation.com) System fonts This console example will print all available fonts on your platform.awt. Dialog. The 518 .println(fonts[i]. A collection of font faces forms a font family.zetcode. OS X and Windows platform differ. It would easily fill a specialized book. for (int i = 0.getAllFonts(). Monospaced. Like ť. ú.Font. A glyph is a shape used to render a character or a sequence of characters. Text can be drawn on the window using various fonts. Logical fonts are the five font families defined by the Java platform. ô.length. Serif.GraphicsEnvironment. (docs.Text and fonts Rendering text is another complicated topic. Soulmate. class Surface extends JPanel { private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g. java.print(fonts[i].swing. System. java.. This is an excerpt of all fonts on Ubuntu Linux.awt.JFrame. java. The getAllFonts() returns all fonts available in the GraphicsEnvironment.JPanel. import import import import import import import java.awt. javax.Graphics2D.. Soulmate In the next example.. The font name and the font family is printed to the terminal. javax.swing.RenderingHints.Font.zetcode.awt.Graphics.java package com.out. 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 . RenderingHints rh = 519 .getAllFonts().. javax.println(fonts[i].awt.getFamily()).getFontName() + " : ").swing.out. we will display some lyrics on the panel. GraphicsEnvironment Font[] fonts = ge. System. .SwingUtilities.class describes the collection of GraphicsDevice objects and Font objects available on a particular platform. PLAIN.KEY_RENDERING. g2d. } public static void main(String[] args) { SwingUtilities. g2d. } @Override public void paintComponent(Graphics g) { super. g2d. we draw text on the panel.new RenderingHints(RenderingHints.drawString("Somebody tell me why I'm on my own". 13)).KEY_ANTIALIASING. 20. setSize(420. 20. doDrawing(g). g2d.invokeLater(new Runnable() { @Override public void run() { Soulmate so = new Soulmate().setFont(new Font("Purisa". g2d. 520 .drawString("Who knows how to love you without being told".VALUE_ANTIALIAS_ON). 60). add(new Surface()). rh. RenderingHints. 180).drawString("If there's a soulmate for everyone".VALUE_RENDER_QUALITY).drawString("They're all good but not the permanent one". 30). 20. RenderingHints. 20. } } public class Soulmate extends JFrame { public Soulmate() { } initUI(). In this example. g2d.drawString("Who doesn't long for someone to hold". 20. so.EXIT_ON_CLOSE). 90).setVisible(true). g2d.setRenderingHints(rh).paintComponent(g). 150). 20. private void initUI() { setTitle("Soulmate").put(RenderingHints. We choose a specific font type. Font. 120). 250). g2d. setLocationRelativeTo(null). } } } }). setDefaultCloseOperation(JFrame.drawString("Most relationships seem so transitory". . 30).. g2d. $ cat unicode \u0424\u0451\u0434\u043e\u0440 \u041c\u0438\u0445\u0430\u0439\u043b\u043e\u0432\u0438\u0447 . how to display unicode text. Font. the text would be placed outside of the code in a separate resource. The drawString() method renders the text using the current text attribute state in the Graphics2D context.PLAIN.drawString("Most relationships seem so transitory". We have a file called fyodor where we have the text in azbuka.setFont(new Font("Purisa". Михаил Андреевич. $ cat fyodor Фёдор Михайлович Достоевский родился 30 октября (11 ноября) 1821 года в Москве. $ native2ascii fyodor unicode We use the tool called native2ascii. Note that in real world applications. Был вторым из 7 детей. Отец. The second parameter is the output file. The first parameter is the input file. 521 . which can be found in the bin directory of the jdk. The same text in unicode encoding. Figure: Soulmate Unicode The next example demonstrates. . работал в госпитале для бедных. 20. 13)). It converts a file with native-encoded characters to one with Unicode-encoded characters.g2d.. Here we set a Purisa font type.. 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.java package com.Font.swing. \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. \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 .". java. \u0438 \u043e\u0442\u0435\u0446 \u043e\u0442\u043f\u0440" + "\u0430\u0432\u0438\u043b".JPanel.Graphics2D. javax.zetcode.Graphics.awt. " + "\u041c\u0438\u0445\u0430\u0438\u043b \u0410\u043d\u0434\u0440\u0435\u0435" + "\u0432\u0438\u0447. \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. \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).Unicode. java. String sent3 = "\u041c\u0430\u0442\u044c.awt. java.SwingUtilities. ". String sent5 = "\u0441\u0442\u0430\u0440\u0448\u0438\u0445 \u0441\u044b" + "\u043d\u043e\u0432\u0435\u0439. 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.awt. import import import import import import import java. \u0435\u0433\u043e \u043c\u0430\u0442\u044c " + "\u0443\u043c\u0435\u0440\u043b\u0430 \u043e\u0442 \u0447\u0430\u0445\u043e" + "\u0442\u043a\u0438.". javax. javax.swing. String sent2 = "\u0411\u044b\u043b \u0432\u0442\u043e\u0440\u044b\u043c " + "\u0438\u0437 7 \u0434\u0435\u0442\u0435\u0439.JFrame.swing.awt.RenderingHints. \u041e\u0442\u0435\u0446. drawString(sent1.EXIT_ON_CLOSE). rh. String sent6 = "\u0432 \u043f\u0430\u043d\u0441\u0438\u043e\u043d \u041a.drawString(sent4. RenderingHints rh = new RenderingHints(RenderingHints.PLAIN. RenderingHints. 145).drawString(sent2. } } public class Unicode extends JFrame { public Unicode() { initUI().". 20. } private void initUI() { setTitle("Unicode")."\u0441\u0442\u0430\u0432\u0448\u0435\u0433\u043e \u043f\u0438\u0441\u0430" + "\u0442\u0435\u043b\u0435\u043c).KEY_RENDERING. g2d. 55). g2d. setSize(550. g2d. 20.KEY_ANTIALIASING.". setLocationRelativeTo(null).paintComponent(g).drawString(sent6. setDefaultCloseOperation(JFrame. " + "\u0424. g2d. 230). 20. 30).put(RenderingHints. Font.VALUE_ANTIALIAS_ON). g2d. g2d.VALUE_RENDER_QUALITY). doDrawing(g). 20. g2d. 20.drawString(sent5. g2d. \u041a\u043e\u0441\u0442\u043e\u043c\u0430\u0440\u043e\u0432\u0430 " + "\u0432 \u041f\u0435\u0442\u0435\u0440\u0431\u0443\u0440\u0433\u0435. } @Override public void paintComponent(Graphics g) { super. private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g.setFont(new Font("Franklin Gothic Medium". add(new Surface()).setRenderingHints(rh). RenderingHints. 170). 120). 20. 80). 11)).drawString(sent3. } public static void main(String[] args) { 523 . java. uni. import import import import import import import java. Here the text is kept inside the source for simplicity reasons.awt. java..ConvolveOp. java. String sent1 = "\u0424\u0451\u0434\u043e\u0440 \u041c\u0438\u0445" + .awt. java. colored in light gray and blurred. And the sentence is drawn.font.Color. 524 . ShadowedText. 30).awt.image. 20.drawString(sent1.awt. that the text would go outside source code in real world programs.java package com.image.awt. This is the first unicode line. The effect is created by drawing the same text two times.invokeLater(new Runnable() { @Override public void run() { Unicode uni = new Unicode().zetcode.awt..BufferedImage. java.SwingUtilities. we will create a shadowed text. Note. the other one as a shadow.Font. g2d. java. Figure: Unicode Shadowed text In the next example.TextLayout.setVisible(true).RenderingHints. One text serves as the main text.awt. } } } }).Graphics2D. The shadowed text is moved a bit. SwingUtilities. javax. public ShadowedText() { initUI().TYPE_INT_RGB). g1. BufferedImage. height). 525 . public class ShadowedText extends JFrame { private int width = 490. BufferedImage image = new BufferedImage(width. 0.ImageIcon.KEY_FRACTIONALMETRICS. 150. RenderingHints. int y = 100. setSize(width.setPaint(Color.swing. } private void setRenderingHints(Graphics2D g) { g. g1. x+3. Graphics2D g1 = image. y+3). } private BufferedImage createImage() int x = 10. setLocationRelativeTo(null).image.swing. setRenderingHints(g1).fillRect(0. add(new JLabel(new ImageIcon(image))). Font.setRenderingHint(RenderingHints.ITALIC. javax.KEY_TEXT_ANTIALIASING.draw(g1.JFrame. } private void initUI() { setTitle("Shadowed Text"). g.VALUE_TEXT_ANTIALIAS_ON). textLayout. private int height = 150. 150)). 50). g1.setRenderingHint(RenderingHints. g1.swing.JLabel.EXIT_ON_CLOSE).dispose().setPaint(new Color(150.WHITE). javax. RenderingHints. BufferedImage image = createImage(). private TextLayout textLayout. 1f / 9f.getFontRenderContext()).import import import import import java. height). { Font font = new Font("Georgia".createGraphics().Kernel. 1f / 9f. float[] kernel = { 1f / 9f. height.swing. javax. width. g1. private String text = "Disciplin ist macht". setDefaultCloseOperation(JFrame.VALUE_FRACTIONALMETRICS_ON). font.awt. textLayout = new TextLayout(text. BufferedImage image2 = op. textLayout. TextLayout is an immutable graphical representation of styled character data.ITALIC. 1f / 9f. return image2. ConvolveOp. y). textLayout = new TextLayout(text. The origin of the layout is placed at x. null). ConvolveOp op = new ConvolveOp(new Kernel(3. Font font = new Font("Georgia". we don't draw in the paintComponent() method. This time. 1f / 9f. 526 . Graphics2D g1 = image. kernel). The code renders this TextLayout at the specified location in the specified Graphics2D context. x+3. } public static void main(String[] args) { SwingUtilities. Font.filter(image.setVisible(true). y+3). From the buffered image a Graphics2D object is created.createGraphics(). It will be used to draw into the buffered image. g2. 50). of 50 points size. g1.createGraphics(). We create a image that we put inside a JLabel.TYPE_INT_RGB).BLACK). BufferedImage. 1f / 9f. null). BufferedImage image = new BufferedImage(width. height. 1f / 9f. font. } } } }). We create the first buffered image. It is used for advanced manipulation with the text and font. textLayout.EDGE_NO_OP.setPaint(Color.getFontRenderContext()).1f / 9f. 1f / 9f }. italic.draw(g1. st.draw(g2. y. We create a TextLayout class. Our font is Georgia. setRenderingHints(g2). x. 3.invokeLater(new Runnable() { @Override public void run() { ShadowedText st = new ShadowedText(). Graphics2D g2 = image2. java. At this point. we have both the original text and the blurred text in the TaxLayout object. null). We apply the blur effect on the first image and copy the outcome to the second buffered image.swing.font. y).AttributedString. java.TextAttribute. AttributeString classes. 1f / 9f. import import import import import import import import import import java. Finally. class Surface extends JPanel { 527 . 1f / 9f }.awt. java.JPanel. 1f / 9f. 3. kernel). 1f / 9f. null).awt. 1f / 9f.filter(image. java. the AttributedString class holds text and related attribute information.SwingUtilities.JFrame. TextAttributes. The Font class represents fonts. The TextAttribute class defines attribute keys and attribute values used for text rendering. which are used to render text.zetcode. ConvolveOp op = new ConvolveOp(new Kernel(3. x.swing. This creates the blur effect. java.draw(g2.text.Color. java. we can control its various attributes.float[] kernel = { 1f / 9f.swing. Figure: Shadowed text Text attributes When we draw text. ConvolveOp. textLayout.Graphics2D. javax.Graphics. TextAttributes.awt.java package com.Font.RenderingHints. javax. 1f / 9f. 1f / 9f.awt. We can modify text rendering with Font class.awt.EDGE_NO_OP. 1f / 9f. BufferedImage image2 = op.awt. javax. getIterator().PLAIN. Font font = new Font("Serif". 19). } } public class TextAttributes extends JFrame { public TextAttributes() { } initUI(). 40). private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g. } @Override public void paintComponent(Graphics g) { super. 7). as1. 12.addAttribute(TextAttribute. 6). 15. AttributedString as2 = new AttributedString(java). TextAttribute.LIGHT_GRAY. setSize(620.SUPERSCRIPT.drawString(as2. } public static void main(String[] args) { 528 .private String words = "Valour fate kinship darkness".addAttribute(TextAttribute.UNDERLINE_ON.addAttribute(TextAttribute. AttributedString as1 = new AttributedString(words).addAttribute(TextAttribute.setRenderingHint(RenderingHints.drawString(as1. as1. 130.EXIT_ON_CLOSE). as1. as2. Font. 125). as2. 20. doDrawing(g). Color.SUPERSCRIPT_SUPER. 5.FONT. 7.getIterator().FOREGROUND. 60). 0. private void initUI() { setTitle("TextAttributes").addAttribute(TextAttribute. setLocationRelativeTo(null). add(new Surface()).VALUE_ANTIALIAS_ON). font). 11).KEY_ANTIALIASING. RenderingHints. as1.red.addAttribute(TextAttribute. 28).SIZE. g2d. TextAttribute.paintComponent(g).BACKGROUND. Color. setDefaultCloseOperation(JFrame.UNDERLINE. as1.STRIKETHROUGH_ON. 190). private String java = "Java TM". g2d. g2d. TextAttribute. 40).addAttribute(TextAttribute.STRIKETHROUGH. We will work with several important classes. So in our code example. Color. 15. The GlyphVector object is a collection 529 . a glyph is a shape used to render a character. Because at the moment we work with a AttributeString class and not directly with the string. g2d. To rotate a text. 60). ta. As we already stated. } } } In our example.invokeLater(new Runnable() { @Override public void run() { TextAttributes ta = new TextAttributes(). }).getIterator(). 6). The FontRenderContext class is a container for the information needed to correctly measure text. which takes a AttributedCharacterIterator instance as its first parameter. we demonstrate various text rendering possibilities.setVisible(true). This attribute specifies that the first seven characters will be rendered in red color. we will do rotation and translation operations. We create an AttributeString out of the words string. The first text is drawn on the panel.FOREGROUND. get their measurements and manipulate them one by one.SwingUtilities. Figure: Text Attributes Rotated Text In the last example. we use an overloaded drawString() method.red. we will show a rotated text on the panel. as1.addAttribute(TextAttribute.drawString(as1. Here we add a new attribute to the AttributeString class. we need to get all glyphs of our text. 0. AttributedString as1 = new AttributedString(words). Font font = new Font("Courier". javax. java.getNumGlyphs().getY()). g2d. 12). FontRenderContext frc = g2d.Graphics.Font. AffineTransform at = AffineTransform. s).awt. class Surface extends JPanel { private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g.fill(transformedGlyph). java.awt.awt. i < length. Shape glyph = gv. int length = gv. i++) { Point2D p = gv.PLAIN.getGlyphOutline(i). g2d. javax.java package com. GlyphVector gv = font.createTransformedShape(glyph).rotate(theta).zetcode.Graphics2D.Point2D.geom. } } @Override public void paintComponent(Graphics g) { super.awt.translate(20.KEY_ANTIALIASING.createGlyphVector(frc. double theta = (double) i / (double) (length .RenderingHints. java.awt.font. java.AffineTransform. Shape transformedGlyph = at.awt.Shape.swing. tutorials for programmers".JPanel.swing. g2d.setRenderingHint(RenderingHints. import import import import import import import import import import import import java. 20).VALUE_ANTIALIAS_ON).geom.getFontRenderContext().awt.SwingUtilities. String s = "ZetCode.1) * Math.awt.getTranslateInstance(p. java. for (int i = 0. at.FontRenderContext. RenderingHints. java.GlyphVector. p.paintComponent(g). java. java.getX().awt. javax.swing. 530 .font.of glyphs containing geometric information for the placement of each glyph in a transformed coordinate space. RotatedText.PI / 3.getGlyphPosition(i). Font.JFrame. we get 34. s).getNumGlyphs(). This is our text. Because the text is in Latin1 encoding. A GlyphVector is a collection of glyphs and their positions. For each glyph we calculate its position.} } doDrawing(g). glyphs correspond to characters in a one-to-one manner. each character is one glyph. String s = "ZetCode. 300).getGlyphPosition(i).invokeLater(new Runnable() { @Override public void run() { RotatedText ta = new RotatedText(). If we print the number to the console. setSize(400. setLocationRelativeTo(null). Point2D p = gv. ta. Here we create a GlyphVector object.createGlyphVector(frc. 531 . add(new Surface()).setVisible(true). This is a rotated text example. } } } }). We iterate through the vector of glyphs. int length = gv.EXIT_ON_CLOSE). tutorials for programmers". setDefaultCloseOperation(JFrame. So in our case. public class RotatedText extends JFrame { public RotatedText() { } initUI(). } public static void main(String[] args) { SwingUtilities. GlyphVector gv = font. private void initUI() { setTitle("TextAttributes"). Here we get the number of glyphs of our text. fill(transformedGlyph). how to determine if we have clicked inside a shape on a panel. Hit testing. The createTransformedShape() method returns a new Shape object modified by our affine transform operation. p. Shape transformedGlyph = at. by which the glyph is going to be rotated. moving objects In this part of the Java 2D programming tutorial. we will first talk about hit testing. we paint the glyph. we will create two shapes that we can move with a mouse on the panel and resize them with a mouse wheel. g2d. we will be resizing a rectangle with two controlling points. We do an affine rotate transformation. at.createTransformedShape(glyph).getGlyphOutline(i). 532 . In the last example.getTranslateInstance(p. AffineTransform at = AffineTransform.PI / 3. We will show. Shape glyph = gv.getY()). In the second example.1) * Math. The getGlyphOutline() method returns a Shape of the specified glyph.rotate(theta).double theta = (double) i / (double) (length . We calculate the degree. Figure: Rotated text In this part of the Java 2D tutorial.getX(). we covered Text and Fonts. Finally. 20f.logging.KEY_RENDERING.Graphics.Level.logging.Graphics2D. java. java. java.awt. Each Shape has a contains() method.awt. rh.awt. 50. rect = new Rectangle2D. float alpha_rectangle.geom.SwingUtilities. float alpha_ellipse.event.awt.geom.java package com.util. private void initSurface() { this. Ellipse2D ellipse. HitTesting. 60f).util.awt.JFrame.addMouseListener(new HitTestAdapter()).AlphaComposite. alpha_ellipse = 1f.VALUE_RENDER_QUALITY).RenderingHints. java. class Surface extends JPanel { private private private private Rectangle2D rect. 50f).Float(20f. RenderingHints. import import import import import import import import import import import import import import java.swing.Ellipse2D.awt. public Surface() { } initSurface(). g2d. javax.event. 50)). 80f. RenderingHints. 533 .awt.swing.MouseEvent.setColor(new Color(50.KEY_ANTIALIASING. javax.MouseAdapter.Color. java.awt.VALUE_ANTIALIAS_ON).JPanel. RenderingHints rh = new RenderingHints(RenderingHints. javax.Hit testing Hit testing is determining if we have clicked inside a Shape with a mouse pointer. } private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g.awt. The method tests if a specified Point2D is inside the boundary of a Shape. ellipse = new Ellipse2D.Rectangle2D. alpha_rectangle = 1f. java.zetcode.put(RenderingHints. java. 60f. 30f.Float(120f.Logger.swing. java. java. java. } class RectRunnable implements Runnable { private Thread runner.g2d. alpha_rectangle += -0.sleep(50). g2d.SRC_OVER.01f.start(). ex).SEVERE. } try { Thread. } @Override public void paintComponent(Graphics g) { super.fill(ellipse). g2d. if (alpha_rectangle < 0) { alpha_rectangle = 0. null. } catch (InterruptedException ex) { Logger.getInstance(AlphaComposite.setComposite(AlphaComposite. } } } } 534 . g2d.setComposite(AlphaComposite.SRC_OVER. } private void initThread() { runner = new Thread(this).paintComponent(g).log(Level.setRenderingHints(rh).fill(rect). } @Override public void run() { while (alpha_rectangle >= 0) { repaint(). public RectRunnable() { initThread().getName()).getInstance(AlphaComposite. g2d. doDrawing(g). runner.getLogger(Surface. alpha_rectangle)). alpha_ellipse)).class. } catch (InterruptedException ex) { Logger. try { Thread.getY(). } } @Override public void run() { while (alpha_ellipse >= 0) { repaint().SEVERE.contains(x. private Thread ellipseAnimator. null. if (ellipse. y)) { ellipseAnimator = new Thread(this).getLogger(Surface. 535 . add(new Surface()). } } } } } public class HitTesting extends JFrame { public HitTesting() { setTitle("Hit testing"). setDefaultCloseOperation(JFrame.class HitTestAdapter extends MouseAdapter implements Runnable { private RectRunnable rectAnimator. y)) { } rectAnimator = new RectRunnable(). ellipseAnimator.EXIT_ON_CLOSE). int y = e. if (rect.contains(x. if (alpha_ellipse < 0) { } alpha_ellipse = 0. setSize(250.01f.getName()). alpha_ellipse += -0.getX().log(Level.sleep(50).start().class. @Override public void mousePressed(MouseEvent e) { int x = e. ex). 150). ellipseAnimator.invokeLater(new Runnable() { @Override public void run() { HitTesting ht = new HitTesting(). ht. it is the run() method of the class itself (HitTestAdapter).start(). The HitTestAdapter class is responsible for handling of mouse events. }). if (rect. These two variables control the transparency of the two geometrical objects. In our case. private Rectangle2D rect.getInstance(AlphaComposite. we set the transparency of the rectangle.} setLocationRelativeTo(null).SRC_OVER. y)) { } rectAnimator = new RectRunnable(). In this example. Inside the doDrawing() method. if (ellipse. The thread calls the run() method. alpha_rectangle)). public static void main(String[] args) { SwingUtilities. The alpha_rectangle is computed inside a dedicated Thread. which means that it also creates the first thread. private float alpha_rectangle. g2d.setComposite(AlphaComposite. } If we press inside the ellipse a new Thread is created.contains(x. we work with Threads.setVisible(true). By clicking on them they gradually begin to fade away.contains(x. It does implement the Runnable interface. we have two Shapes. y)) { ellipseAnimator = new Thread(this). private Ellipse2D ellipse. g2d. A rectangle and a circle. } } } In our example. We work with a rectangle and an ellipse. 536 .fill(rect). private float alpha_ellipse. awt.awt. diagrams or other various objects in our application.swing.swing. java.JPanel.MouseWheelEvent. javax.zetcode.. java. To actually do something.Color. java.Rectangle2D. public void run() { while (alpha_ellipse >= 0) { repaint().event. .geom.geom. we have a separate inner class. java.awt. javax. 537 .awt.SwingUtilities.swing. MovingScaling.01f. java.java package com.awt.awt.event.MouseWheelListener. private ZEllipse zell. java.Graphics2D.event. class Surface extends JPanel { private ZRectangle zrect..Font. } Note that the run() method is only called once.awt.awt. The RectRunnable class. import import import import import import import import import import import import import import java.awt. javax.RenderingHints.Graphics.awt. java.MouseAdapter.For the rectangle. alpha_ellipse += -0.JFrame.awt. It can be used to move and scale charts.Ellipse2D. java. 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. This class creates its own thread in the constructor.MouseEvent. java. we have to implement a while loop.event. java. The while loop repaints the panel and decrements the alpha_ellipse variable. } 538 .paintComponent(g).KEY_TEXT_ANTIALIASING. } public boolean isHit(float x. setDoubleBuffered(true). 70.setRenderingHint(RenderingHints. addMouseListener(ma).fill(zell). y)) { return true. width. float height) { setFrame(x. 0)). 200. 80). zell = new ZEllipse(150.BOLD. y. RenderingHints. 50). g2d.fill(zrect).KEY_ANTIALIASING. 40). g2d. 0.VALUE_ANTIALIAS_ON). } private void doDrawing(Graphics g) { Graphics2D g2d = (Graphics2D) g. g2d. g2d. 80. Font.setFont(font). height). 50. } class ZEllipse extends Ellipse2D.setRenderingHint(RenderingHints. float y. float width.VALUE_TEXT_ANTIALIAS_ON). } else { return false. doDrawing(g). 200)). g2d.setColor(new Color(0. float y) { if (getBounds2D().Float { public ZEllipse(float x. } @Override public void paintComponent(Graphics g) { super. RenderingHints. addMouseWheelListener(new ScaleHandler()). zrect = new ZRectangle(50. } private void initUI() { MovingAdapter ma = new MovingAdapter(). 50. g2d.setColor(new Color(0.contains(x.public Surface() { initUI(). addMouseMotionListener(ma). Font font = new Font("Serif". g2d. y)) { return true. } public void addHeight(float h) { } } this. public void addHeight(float h) { this.width += w. height).x += x.x += x. float y) { if (getBounds2D(). float height) { setRect(x. public void addWidth(float w) { this. public void addY(float y) { this. } } class ZRectangle extends Rectangle2D.y += y.height += h. } public void addWidth(float w) { } this. 539 .y += y. float y.contains(x. } public void addY(float y) { } this. float width. y. } else { return false. width. } public boolean isHit(float x.width += w.Float { public ZRectangle(float x.height += h. } } public void addX(float x) { this.} public void addX(float x) { } this. zrect.WHEEL_UNIT_SCROLL) { if (zrect.getX().addWidth(amount). zrect. } if (zell.addWidth(amount). int dy = e.addHeight(amount). } } } x += dx.getY().addX(dx). zell.getScrollType() == MouseWheelEvent. y)) { float amount = e.x. y += dy. zell. class ScaleHandler implements MouseWheelListener { @Override public void mouseWheelMoved(MouseWheelEvent e) { int x = e.addY(dy). } if (zell. if (zrect.getX() . } @Override public void mouseDragged(MouseEvent e) { int dx = e.getWheelRotation() * 5f.addHeight(amount). zrect.isHit(x. private int y.addY(dy). repaint(). y)) { zell. if (e.isHit(x. 540 . y = e. zell. int y = e.getX().isHit(x. repaint().isHit(x. y)) { float amount = e. repaint().class MovingAdapter extends MouseAdapter { private int x.addX(dx). @Override public void mousePressed(MouseEvent e) { x = e.getY() .y.getY(). y)) { zrect.getWheelRotation() * 5f. } } In our code example. As we have already mentioned.EXIT_ON_CLOSE). addMouseMotionListener(ma). private ZEllipse zell. ms. addMouseWheelListener(new ScaleHandler()). Both classes extend the functionality of a built-in classes from Java AWT package. addMouseListener(ma). We can also scale them up or down by positioning the mouse cursor over the objects and moving the mouse wheel. These listeners will capture mouse press. float height) { 541 . we have two graphical objects. float width. private ZRectangle zrect.setVisible(true). mouse drag and mouse wheel events. A rectangle and a circle.invokeLater(new Runnable() { @Override public void run() { MovingScaling ms = new MovingScaling(). float y. setSize(300. setLocationRelativeTo(null). We register three listeners. 300). } }). } private void initUI() { setTitle("Moving and scaling"). public class MovingScaling extends JFrame { public MovingScaling() { initUI(). We can move both by clicking on them and dragging them. } public static void main(String[] args) { SwingUtilities. setDefaultCloseOperation(JFrame. we have a rectangle and an ellipse on our panel. add(new Surface()).} } } } } repaint(). class ZEllipse extends Ellipse2D.Float { public ZEllipse(float x. } In the mousePressed() method. y)) { zrect. For example. Inside the mouseDragged() method.x. float y) { if (getBounds2D().addX(dx).getY(). y)) { return true. y += dy.} setFrame(x. It adds functionality for scaling and moving an ellipse. public boolean isHit(float x. The MovingAdapter class handles the mouse press and mouse drag events. y coordinates of the rectangle and repaint the panel. It extends the built-in Ellipse2D. int dx = e. y coordinates of the object. repaint(). } Here if we are inside the area of the rectangle.contains(x.. zrect. } else { } } . we remember the initial x. This code excerpt shows a ZEllipse class.isHit(x. if (zrect. int dy = e. The initial coordinates are updated.getY() . we calculate the distance by which we have dragged the object. we update the x. 542 . y. width.getX() . } return false. The ScaleHandler class handles the scaling of the objects. height). x += dx. @Override public void mousePressed(MouseEvent e) { x = e.y.Float class. the isHit() method determines if the mouse pointer is inside the area of an ellipse.addY(dy).getX().. y = e. awt. zrect.swing. we will draw two small black rectangles. private int SIZE = 8.event. Resize Rectangle In the next example.JFrame.java import import import import import import import import import import package com. javax.JPanel.swing. points[1] = new Point2D. we will show how to resize a shape. ResizeRectangle. java.Graphics. java. points[0] = new Point2D. zrect.geom. On the rectangle.getWheelRotation() * 5f. java.MouseEvent.addWidth(amount).Point2D. y)) { float amount = e.Double(50.WHEEL_UNIT_SCROLL) { if (zrect. java. java.awt. repaint().isHit(x. public Surface() { } initUI(). java. private void initUI() { addMouseListener(new ShapeTestAdapter()).awt.zetcode.Graphics2D.Rectangle2D. java. private int pos. 100).swing. we can resize our main rectangle.awt.SwingUtilities. 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.awt.awt.geom. javax.. 543 . pos = -1. } . } If we move a mouse wheel and our cursor is inside the area of a rectangle.if (e.Point. javax. 50). Our shape will be a rectangle.Double(150.MouseAdapter.. class Surface extends JPanel { private Point2D[] points. By clicking on these tiny rectangles and dragging them. points = new Point2D[2].event.awt.addHeight(amount).getScrollType() == MouseWheelEvent. addMouseMotionListener(new ShapeTestAdapter()). double y = points[i]. double y = points[i]. i++) { double x = points[i]. } @Override public void mouseDragged(MouseEvent event) { if (pos == -1) { return. return.length.} private void doDrawing(Graphics g) { Graphics2D g2 = (Graphics2D) g. Rectangle2D r = new Rectangle2D. for (int i = 0. g2. } } 544 . } Rectangle2D s = new Rectangle2D.contains(p)) { pos = i.paintComponent(g).Double(x. } } @Override public void mouseReleased(MouseEvent event) { pos = -1.SIZE / 2. i < points.getPoint(). i++) { double x = points[i].setFrameFromDiagonal(points[0]. y.fill(new Rectangle2D.getY() . points[1]). SIZE. } private class ShapeTestAdapter extends MouseAdapter { @Override public void mousePressed(MouseEvent event) { Point p = event. if (r.getX() .Double(x. doDrawing(g). s.SIZE / 2.draw(s).getX() .SIZE / 2.Double(). SIZE. i < points. y.SIZE / 2. } g2.length. for (int i = 0.getY() . SIZE)). SIZE). @Override public void paintComponent(Graphics g) { super. points = new Point2D[2]. This is the size of the small black rectangles. s. { } private void initUI() { setTitle("Resize rectangle"). we will store points. points[1] = new Point2D.getPoint().Double(150.setVisible(true). rr. private int SIZE = 8. These are the initial coordinates for a rectangle.setFrameFromDiagonal(points[0]. Another way is to provide the top-left and bottom-right points. we will use both methods. 50). points[0] = new Point2D. private Point2D[] points. that will make our rectangle. In our code example. 545 . } } public class ResizeRectangle extends JFrame { public ResizeRectangle() } initUI(). Rectangle2D s = new Rectangle2D. By providing x. setDefaultCloseOperation(JFrame. 300). points[1]). 100).Double(50. setLocationRelativeTo(null). repaint().invokeLater(new Runnable() { @Override public void run() { ResizeRectangle rr = new ResizeRectangle(). In this array.EXIT_ON_CLOSE). add(new Surface()). setSize(300. } public static void main(String[] args) { SwingUtilities. y coordinates plus the width and height of the rectangle.points[pos] = event. There are two ways to create a rectangle.Double(). } } } }). g2.length. double y = points[i]. if (r. If we hit one of them. } points[pos] = event.SIZE / 2. for (int i = 0. SIZE)). the pos variable stores which of them it was. y. i < points. y. if we have clicked inside one of the two controlling points.getY() . } Here the rectangle is dynamically resized.length. Here we draw a rectangle from the points. repaint(). i++) { double x = points[i]. Rectangle2D r = new Rectangle2D.Double(x.getX() . @Override public void mousePressed(MouseEvent event) { Point p = event. update our array of points and repaint the panel. During the mouseDragged() event.g2. double y = points[i]. SIZE). SIZE.Double(x.getY() .SIZE / 2. i < points. SIZE.SIZE / 2. we determine.SIZE / 2. 546 . } } } In the mousePressed() method.fill(new Rectangle2D. for (int i = 0.getPoint().draw(s). } This code draws the two small controlling rectangles. return.getX() . i++) { double x = points[i].getPoint(). @Override public void mouseDragged(MouseEvent event) { if (pos == -1) { return.contains(p)) { pos = i. we get the current point. Since then. the row is destroyed and we score. Figure: Tetrominoes 547 . L-shape.Figure: Resizing a rectangle In this part of the Java 2D tutorial. S-shape. Line-shape. The shapes are falling down the board. Tetris is available on almost every computer platform in lots of variations. Even my mobile phone has a modified version of the Tetris game. so that they fit as much as possible. We play the Tetris game until we top out. T-shape. Tetris is called a falling block puzzle game. In this game. we covered hit testing and moving objects. The original game was designed and programmed by a Russian programmer Alexey Pajitnov in 1985. MirroredL-shape and a Square-shape. we have seven different shapes called tetrominoes. Tetris In this chapter. Tetris The Tetris game is one of the most popular computer games ever created. Each of these shapes is formed with four squares. If we manage to form a row. we will create a Tetris game clone in Java Swing. Z-shape. The object of the Tetris game is to move and rotate the shapes. board. import javax. Board board = new Board(this).JFrame. setTitle("Tetris"). Some ideas behind the game. setSize(200.java package com.awt. setDefaultCloseOperation(EXIT_ON_CLOSE). The space key will drop the Tetris piece immediately to the bottom. import javax. Tetris. that we have removed. add(statusbar.start(). public class Tetris extends JFrame { private JLabel statusbar. The game starts immediately. there is a mathematical model. Behind every computer game.SOUTH). BorderLayout. The d key will drop the piece one line down. The score is the number of lines. • • • • 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.The development We do not have images for our Tetris game. (It can be used to speed up the falling a bit. add(board). so that it is easier to understand.SwingUtilities.swing. import java. So it is in Tetris.zetcode.BorderLayout.JLabel. setLocationRelativeTo(null).) The game goes at constant speed. import javax. after it is launched.swing. } private void initUI() { statusbar = new JLabel(" 0"). public Tetris() { initUI(). no acceleration is implemented. 400).swing. } public JLabel getStatusBar() { } return statusbar. 548 . we draw the tetrominoes using Swing drawing API. We can pause the game by pressing the p key. 0 }. setShape(Tetrominoes. { 0. -1 }. -1 }. 0. { 0. 0 }. 1 } }. 0 }. { 0. 0 }.setVisible(true). 1 } }. TShape. 1 } } 549 . 0 }. 1 } }. } } } }). 0. The start() method starts the Tetris game. 0 }. 1 }. 0. 0. { 0. private int[][][] coordsTable. 1. { { -1. { 1. { { 0. { 0. 0 }. ++j) { { { { { { { { { 0.NoShape). 0 }. { { 0. -1. LShape. i++) { for (int j = 0. SquareShape. game. 0 }.Random. 1 }. -1 }. Immediately after the window appears on the screen. 0 }.util. 1 } }. } public void setShape(Tetrominoes shape) { coordsTable = new int[][][] { { { 0.zetcode. { 1. 0 } }. private Tetrominoes pieceShape. public Shape() { coords = new int[4][2]. j < 2. 0 }. 2 } }. 1. ZShape. { 0. Shape. { 1. i < 4 . { 0. In the Tetris. -1 }. { -1.invokeLater(new Runnable() { @Override public void run() { Tetris game = new Tetris(). { 0.java package com. 0 }. private int coords[][]. 0 }. MirroredLShape }. { { 1. we set up the game. { 0. }.java file.public static void main(String[] args) { SwingUtilities. { { 0. We create a board on which we play the game. 0 }. for (int i = 0. { { 0. { 0. -1 }. SShape.start(). public class Shape { protected enum Tetrominoes { NoShape. -1 }. { 0. board. { 0. import java. 1 } }. -1 }. { { -1. LineShape. 0 }. We create a statusbar. m = Math. int x = Math.SquareShape) return this. } coords[index][0]. int public int x(int index) { return public int y(int index) { return public Tetrominoes getShape() { public void setRandomShape() { Random r = new Random().setX(i.nextInt()) % 7 + 1. -x(i)). i++) { } } public int minY() { int m = coords[0][1].ordinal()][i][j]. y(i)). coords[i][0]). coords[i][1]). } return pieceShape. Shape result = new Shape(). setShape(values[x]). return m. } public int minX() { int m = coords[0][0]. for (int i=0. result. result. ++i) { result.values().min(m. } 550 .coords[i][j] = coordsTable[shape. public Shape rotateLeft() { if (pieceShape == Tetrominoes. i < 4. } } private void setX(int index. i < 4.min(m. } coords[index][1].abs(r. } y) { coords[index][1] = y.setY(i. int private void setY(int index.pieceShape = pieceShape. x) { coords[index][0] = x. Tetrominoes[] values = Tetrominoes. for (int i=0. for (int i = 0. } } return m. } } pieceShape = shape. i++) { m = Math. i < 4. 0 }. 1 } }. { { -1. } This is the constructor of the Shape class. 0 }. Plus the empty shape called here NoShape.setX(i. public Shape rotateRight() { if (pieceShape == Tetrominoes. LineShape. { 0. for (int i = 0. { 0. 0 }. for (int i = 0. i++) { for (int j = 0. The Tetrominoes enum holds all seven Tetris shapes. { 0. j < 2. 0 }. 1. { { { { { { { { 0. -1 }. { 0.} return result. ++j) { coords[i][j] = coordsTable[shape. { { 0. -1 }. 1. ZShape. 0. { 0. 0 }. MirroredLShape }. { 0. -1 }. 0 }. -1 }. { -1. Shape result = new Shape(). { 0. } The Shape class provides information about a Tetris piece.pieceShape = pieceShape.ordinal()][i][j]. 1 }. { 1. The coords array holds the actual coordinates of a Tetris piece. 0 }. 0 } }. 2 } }. setShape(Tetrominoes. 0 }. TShape. result. -1 }. 1 } }. LShape. { 1. }. 0. i < 4. { 0. 1 } }. { 1. 1 } } The coordsTable array holds all possible coordinate values of our Tetris pieces. -1 }. { { 0. 0 }. -1 }. 0 }. SquareShape. } } return result. { 0. 0 }. 1 } }. { { 1. result. { { 0. public Shape() { coords = new int[4][2].setY(i.NoShape). -1. i < 4 . coordsTable = new int[][][] { { { 0. 1 } }. 0 }. ++i) { result. { 0. 1 }. 0 }. 0 }. { { -1. -y(i)). 0. SShape. { { 0. 551 . { 0. 0 }. This is a template from which all pieces take their coordinate values. x(i)). 0. { 0.SquareShape) return this. protected enum Tetrominoes { NoShape. Unlike in C++. java.Graphics. } } return result.pieceShape = pieceShape. The coords array saves the coordinates of the Tetris piece.zetcode.awt. represent a rotated S-shape. java. And the ordinal() method returns the current position of the enum type in the enum object.awt. That's why we simply return the reference to the current object. 552 .java package com. java. Figure: Coordinates public Shape rotateLeft() { if (pieceShape == Tetrominoes. import import import import import java.SquareShape) return this.setY(i. { 0. This code rotates the piece to the left. -x(i)). Note the use of the ordinal() method. 0 }. an enum type is esencially an integer. The square does not have to be rotated. for (int i = 0. y(i)).ActionListener. Board. ++i) { result. { -1. result.setX(i. For example.ActionEvent. The following diagram illustrates the shape. i < 4. The following image will help understand the coordinate values a bit more.event.awt. { -1. java.event. Looking at the previous image will help to understand the rotation.awt. Shape result = new Shape().} } Here we put one row of the coordinate values from the coordsTable to a coords array of a Tetris piece. numbers { 0. 0 }. Java enums are full classes.Dimension.Color. result.awt. -1 }. -1 } . In C++. import import import import import java. timer. public Board(Tetris parent) { initBoard(parent). board = new Tetrominoes[BoardWidth * BoardHeight]. javax. java.event. } oneLineDown(). timer = new Timer(400.KeyAdapter.event.swing. boolean isFallingFinished = false. curPiece = new Shape(). boolean isPaused = false. 553 . boolean isStarted = false. javax. } private void initBoard(Tetris parent) { setFocusable(true).JPanel.JLabel.start(). } else { } } private int squareWidth() { return (int) getSize().getWidth() / BoardWidth.KeyEvent. int curX = 0. addKeyListener(new TAdapter()). public class Board extends JPanel implements ActionListener { private final int BoardWidth = 10.Timer.swing. javax. JLabel statusbar. private final int BoardHeight = 22.Tetrominoes. clearBoard(). newPiece().Shape. private private private private private private private private private private Timer timer. statusbar = parent.getStatusBar().swing. import com.zetcode. int numLinesRemoved = 0. Shape curPiece.awt.getHeight() / BoardHeight. this). } @Override public void actionPerformed(ActionEvent e) { if (isFallingFinished) { isFallingFinished = false. } private int squareHeight() { return (int) getSize().awt. Tetrominoes[] board. int curY = 0. start().getShape() != Tetrominoes.private Tetrominoes shapeAt(int x. private void doDrawing(Graphics g) { Dimension size = getSize().valueOf(numLinesRemoved)).x(i). if (shape != Tetrominoes. for (int i = 0. int y) { return board[(y * BoardWidth) + x].setText("paused"). } private void pause() if (!isStarted) return.i . ++i) { int x = curX + curPiece. i < BoardHeight.start().setText(String. newPiece(). 0 + j * squareWidth(). timer. ++j) { Tetrominoes shape = shapeAt(j.NoShape) { for (int i = 0. i < 4. boardTop + i * squareHeight(). isStarted = true. j < BoardWidth. { { } } repaint(). shape).NoShape) drawSquare(g.BoardHeight * squareHeight(). int boardTop = (int) size.stop(). 554 . isFallingFinished = false. numLinesRemoved = 0. if (isPaused) { timer. statusbar. } else { timer. clearBoard(). } } if (curPiece. } public void start() if (isPaused) return. statusbar. BoardHeight .getHeight() .1). ++i) { for (int j = 0. isPaused = !isPaused. i < 4.squareHeight().y(i).1) * curPiece. } } pieceDropped(). while (newY > 0) { if (!tryMove(curPiece. } removeFullLines(). boardTop + (BoardHeight .getShape(). private void oneLineDown() { if (!tryMove(curPiece.setRandomShape().paintComponent(g).y . @Override public void paintComponent(Graphics g) { super. } } } int y = curY . i < BoardHeight * BoardWidth. int y = curY . newY . } private void newPiece() { curPiece. --newY. } private void clearBoard() { for (int i = 0. curX. if (!isFallingFinished) newPiece().1)) pieceDropped().curPiece. ++i) board[i] = Tetrominoes. 555 .x(i).y(i). board[(y * BoardWidth) + x] = curPiece. curY . } private void pieceDropped() { for (int i = 0. 0 + x * squareWidth().curPiece. drawSquare(g. } private void dropDown() { int newY = curY.1)) break.getShape()).NoShape. doDrawing(g). curX. ++i) { int x = curX + curPiece. ++k) { for (int j = 0. i < 4. isStarted = false. i) == Tetrominoes. timer. for (int j = 0. curY = BoardHeight . curX.NoShape). curX = newX.NoShape) { lineIsFull = false. if (!tryMove(curPiece. repaint(). if (shapeAt(x.setShape(Tetrominoes. ++j) { if (shapeAt(j. } } } if (numFullLines > 0) { numLinesRemoved += numFullLines. int y = newY .newPiece. break. ++j) board[(k * BoardWidth) + j] = shapeAt(j. if (x < 0 || x >= BoardWidth || y < 0 || y >= BoardHeight) return false. int newY) { for (int i = 0. ++i) { int x = newX + newPiece. y) != Tetrominoes. k < BoardHeight . int newX.1 + curPiece. curY = newY.NoShape) return false. curY)) { curPiece. i >= 0.stop().x(i). } curPiece = newPiece. k + 1). j < BoardWidth.minY(). 556 . for (int i = BoardHeight .1.y(i). private void removeFullLines() { int numFullLines = 0. --i) { boolean lineIsFull = true. for (int k = i. statusbar. } } private boolean tryMove(Shape newPiece. } } if (lineIsFull) { ++numFullLines.setText("game over").1. } return true. j < BoardWidth.curX = BoardWidth / 2 + 1. setText(String. 204). isFallingFinished = true. curX . x + squareWidth() .1. int x.1. 0) Color color = colors[shape. switch (keycode) { case KeyEvent. g. y + 1). }.NoShape) { } int keycode = e. 0).2). squareHeight() . y + squareHeight() . new Color(204. y + 1. 170. } class TAdapter extends KeyAdapter { @Override public void keyPressed(KeyEvent e) { if (!isStarted || curPiece. break.1.1. x. y. g. g. y). 557 .drawLine(x + squareWidth() . y + squareHeight() .setColor(color.1. 102). repaint(). y). g. 102. new Color(204.setColor(color.drawLine(x + 1.getKeyCode().1. 204). g.NoShape).drawLine(x. } } { private void drawSquare(Graphics g. } if (isPaused) return. new Color(218. 204). x + squareWidth() . 102. if (keycode == 'p' || keycode == 'P') { pause(). new Color(204. break.1). g.fillRect(x + 1. g. squareWidth() . 204.setShape(Tetrominoes. 204.ordinal()].darker()).2.VK_LEFT: tryMove(curPiece. 102).statusbar. curY). new Color(102. new Color(102. 102). g.1. curPiece. Tetrominoes shape) Color colors[] = { new Color(102. 102.getShape() == Tetrominoes. x + squareWidth() . 0.1. curX + 1. return. y + squareHeight() . curY). y + squareHeight() .brighter()). 204.valueOf(numLinesRemoved)). int y. return. new Color(0.drawLine(x. case KeyEvent.VK_RIGHT: tryMove(curPiece.setColor(color). boolean isFallingFinished = false.. break. } } } } Finally.rotateLeft(). int numLinesRemoved = 0.VK_DOWN: tryMove(curPiece.. Timer @Override public void actionPerformed(ActionEvent e) { if (isFallingFinished) { 558 . if the Tetris shape has finished falling and we then need to create a new shape. The isFallingFinished variable determines. The numLinesRemoved counts the number of lines. In our case.rotateRight().java file. curY). curX.start(). curY). case 'D': oneLineDown(). we have removed so far. private private private private private private .VK_UP: tryMove(curPiece. the board has the keyboard input.. case KeyEvent. boolean isStarted = false. curX. We initialize some important variables.case KeyEvent. break. break. . The curX and curY variables determine the actual position of the falling Tetris shape. timer = new Timer(400. this). This is where the game logic is located. int curY = 0. timer.VK_SPACE: dropDown(). From now. the timer calls the actionPerformed() method each 400ms. object fires one or more action events after a specified delay. case 'd': oneLineDown(). We must explicitly call the setFocusable() method. case KeyEvent. int curX = 0. we have the Board. setFocusable(true). break. boolean isPaused = false. break.. int y = curY .i .1)) break. ++i) { int x = curX + curPiece. a new piece is created. ++i) { for (int j = 0. The actionPerformed() method checks if the falling has finished. If so. If not.getShape()). } else { } } oneLineDown(). 0 + x * squareWidth(). --newY. if (curPiece. that have been dropped to the bottom of the board.isFallingFinished = false. shape). we draw all objects on the board. j < BoardWidth. boardTop + i * squareHeight(). for (int i = 0. We access it using the shapeAt() method.curPiece. boardTop + (BoardHeight . curX.getShape() != Tetrominoes. 559 . ++j) { Tetrominoes shape = shapeAt(j. we paint the actual falling piece. } } pieceDropped(). } } In the first step we paint all the shapes.y(i).y . private void dropDown() { int newY = curY. if (shape != Tetrominoes.x(i). newPiece(). drawSquare(g. i < BoardHeight. All the squares are remembered in the board array. The painting has two steps.NoShape) { for (int i = 0. i < 4. while (newY > 0) { if (!tryMove(curPiece.1). BoardHeight .1) * squareHeight(). Inside the doDrawing() method. newY . the falling Tetris piece goes one line down. or remains of the shapes. curPiece. 0 + j * squareWidth(). } } In the second step.NoShape) drawSquare(g. private void pieceDropped() { for (int i = 0. it is time to check. } } The newPiece() method creates a new Tetris piece.setText("game over"). if we can remove some lines off the board.x(i). board[(y * BoardWidth) + x] = curPiece.y(i). Once again. the board holds all the squares of the pieces and remains of the pieces that has finished falling. we try to create a new piece. i < BoardHeight * BoardWidth.stop(). the game is over. If we cannot move to the initial positions.setShape(Tetrominoes. int newY) { for (int i = 0. int y = curY .If we press the space key. curX. We simply try to drop the piece one line down until it reaches the bottom or the top of another fallen Tetris piece.x(i). } The clearBoard() method fills the board with empty NoShapes. the piece is dropped to the bottom. ++i) { int x = newX + newPiece.minY(). Then we create a new piece. private void newPiece() { curPiece. } removeFullLines(). The timer is stopped. When the piece has finished falling. We top out. More precisely. The piece gets a new random shape. We put game over string on the statusbar.curPiece. curX = BoardWidth / 2 + 1. i < 4.getShape(). curY)) { curPiece. ++i) board[i] = Tetrominoes.NoShape). isStarted = false. This is later used at collision detection.setRandomShape().NoShape. statusbar. timer. 560 . if (!tryMove(curPiece. private void clearBoard() { for (int i = 0. i < 4. if (!isFallingFinished) newPiece(). } The pieceDropped() method puts the falling piece into the board array. int newX. Then we compute the initial curX and curY values.1 + curPiece. curY = BoardHeight . ++i) { int x = curX + curPiece. private boolean tryMove(Shape newPiece. This is the job of the removeFullLines() method. it is removed. for (int i = BoardHeight . Tetris pieces have different colors. } curPiece = newPiece.brighter()).drawLine(x. y). k + 1). --i) { boolean lineIsFull = true. j < BoardWidth. x.1. i >= 0. ++k) { for (int j = 0. y + squareHeight() .int y = newY . Notice. j < BoardWidth. Similarly.newPiece.y(i). return true. if (x < 0 || x >= BoardWidth || y < 0 || y >= BoardHeight) return false. } The tryMove() method tries to move the Tetris piece. we check if there is any full row among all rows in the board.NoShape) return false. } } } Inside the removeFullLines() method. repaint(). y).1. if (shapeAt(x. x + squareWidth() . This is to simulate a 3D edge. curX = newX. if it has reached the board boundaries or it is adjacent to the already fallen Tetris pieces. The left and top sides of a square are drawn with a brighter color. 561 . ++j) board[(k * BoardWidth) + j] = shapeAt(j. } } if (lineIsFull) { ++numFullLines.1.drawLine(x. i) == Tetrominoes. Each of the squares is drawn with the drawSquare() method. that in our Tetris game. g. curY = newY. This means. we use the so called naive gravity. After finding a full line we increase the counter. This way we destroy the full line. k < BoardHeight .setColor(color. If there is at least one full line. Every Tetris piece has four squares. ++j) { if (shapeAt(j. break.1. that the squares may be left floating above empty gaps. for (int j = 0. g. y) != Tetrominoes. We move all the lines above the full row one line down. for (int k = i. y. the bottom and right sides are drawn with darker colors. int numFullLines = 0. g.NoShape) { lineIsFull = false. The method returns false. 1. The control mechanism is implemented with a KeyAdapter. curY). Figure: Tetris This was the Tetris game. break. 562 . If we press the left arrow key. This is an inner class that overrides the keyPressed() method. we try to move the falling piece one square to the left.VK_LEFT: tryMove(curPiece. case KeyEvent.We control the game with a keyboard. curX . JFrame. public class Skeleton extends JFrame { public Skeleton() { add(new Board()).swing. public class Board extends JPanel { public Board() { } } The Board is a panel. In this tutorial.swing.Java 2D games tutorial This is Java 2D games tutorial. setTitle("Skeleton"). setDefaultCloseOperation(EXIT_ON_CLOSE). Skeleton We will show the skeleton of each of our Java 2D games. About This is Java 2D games tutorial. setSize(300. setResizable(false). All images used in this tutorial can be downloaded here. 280). Basics In this part of the Java 2D games tutorial.java package skeleton. It is aimed at beginners. Paint a donut and display a picture. Skeleton. setVisible(true). Board. setLocationRelativeTo(null). We will create a skeleton of a game. we will write about some basics needed to create games. 563 . where the game takes place. } public static void main(String[] args) { new Skeleton(). you will learn the basics of 2D game programming in Java. import javax. import javax.JPanel. The Java 2D games tutorial is suitable for beginners and intermediate programmers. This tutorial will teach you basics of programming 2D games in Java programming language and Swing GUI toolkit.java package skeleton. This line sets the size for our window. setLocationRelativeTo(null). 280). setVisible(true). Here we have the main method. We use the painting API. we draw a Donut. 564 . add(new Board()). Show the window on the screen. In the next example. We center the window.} } This is the entry point of the game. setResizable(false). Make the window unresizable. It is not the default behaviour. This will close the application when we click on the close button. setDefaultCloseOperation(EXIT_ON_CLOSE). setSize(300. Here we put the Board to the center of the JFrame component. Figure: Skeleton Donut The objects on the Board are either images or are drawn with the painting tools provided by the Java 2D API. for (double deg = 0.createTransformedShape(e)). java. RenderingHints. RenderingHints.VALUE_RENDER_QUALITY).Graphics2D.JPanel. deg += 5) { AffineTransform at = AffineTransform. RenderingHints rh = new RenderingHints(RenderingHints.KEY_RENDERING. g2. java. RenderingHints. Graphics2D g2 = (Graphics2D) g. java.Board.paint(g).VALUE_ANTIALIAS_ON).KEY_ANTIALIASING. public class Board extends JPanel{ public void paint(Graphics g) { super. at.awt. Dimension size = getSize().getTranslateInstance(w / 2.AffineTransform. double h = size.RenderingHints. The Graphics2D object provides a sophisticated control over painting. java.Graphics. deg < 360.Color.draw(at. rh. 80. 130). 0.Double(0.awt.setRenderingHints(rh). 565 . java.KEY_ANTIALIASING.toRadians(deg)).gray). Ellipse2D e = new Ellipse2D.awt.awt.rotate(Math. double w = size. } } } The painting is done inside the paint() method.BasicStroke.swing. import javax. Graphics2D g2 = (Graphics2D) g.Dimension.setStroke(new BasicStroke(1)).getWidth().awt. java. import import import import import import import import java.VALUE_ANTIALIAS_ON).Ellipse2D. g2.setColor(Color.awt.geom.getHeight().awt. RenderingHints rh = new RenderingHints(RenderingHints.geom.java package donut. h / 2). java. g2.awt. g2.put(RenderingHints. EXIT_ON_CLOSE). RenderingHints. setTitle("Donut"). 80.draw(at. 566 .Double(0. for (double deg = 0. setSize(360. import javax. setVisible(true).setColor(Color.KEY_RENDERING.getHeight().toRadians(deg)).rh.setRenderingHints(rh).rotate(Math. deg < 360. 310). setDefaultCloseOperation(JFrame.gray). Here we create the ellipse.java package donut. double h = size.VALUE_RENDER_QUALITY). Dimension size = getSize(). 0.getTranslateInstance(w / 2. } } This is the main class.setStroke(new BasicStroke(1)).JFrame. } public static void main(String[] args) { new Donut().swing. double w = size. } Here the ellispse is rotated 72 times to create a "donut". setLocationRelativeTo(null). Donut. g2. at. g2.put(RenderingHints. g2. deg += 5) { AffineTransform at = AffineTransform. public class Donut extends JFrame { public Donut() { add(new Board()). The rendering hints are used to make the drawing smooth. We get the height and the width of the window. Ellipse2D e = new Ellipse2D. g2. 130).getWidth().createTransformedShape(e)). h / 2). public Board() { ImageIcon ii = new ImageIcon(this.JPanel.Image. import javax. The image is drawn inside the paint() method.ImageIcon.Graphics. import java. public class Board extends JPanel { Image bardejov.awt. import javax. In the next example we load an image and display it on the Board.awt. Board. import java. 10. bardejov = ii. 10. } } We display an image of a town on the Board. } public void paint(Graphics g) { Graphics2D g2d = (Graphics2D) g. null). we often work with images.getImage().awt.Graphics2D.jpg")).Figure: Donut Image When we create computer games.getResource("bardejov.java package bardejov.swing.swing. g2d. import java.getClass().drawImage(bardejov. 567 . getClass(). We draw the image on the window. setVisible(true). setTitle("Bardejov").swing. We get an Image out of the ImageIcon. } public static void main(String[] args) { new Image().JFrame.java package bardejov. we have covered some basics of Java game programming.drawImage(bardejov. null). bardejov = ii. setLocationRelativeTo(null). public class Image extends JFrame { public Image() { add(new Board()).getResource("bardejov. Figure: Image In this chapter. } } This is the main class of the example. setDefaultCloseOperation(JFrame. 568 . We create an ImageIcon. 10.ImageIcon ii = new ImageIcon(this. 10.jpg")). Image. setSize(280. 240).getImage().EXIT_ON_CLOSE). import javax. g2d. awt.ImageIcon.EXIT_ON_CLOSE). Swing timer In the first example we will use a Swing timer to create animation.JPanel. } } This is the main class for the code example.awt. We will animate a star on our Board. Animation is a rapid display of sequence of images which creates an illusion of movement. Board.swing. import javax.event.awt. setDefaultCloseOperation(JFrame.ActionEvent. import javax.event. setLocationRelativeTo(null).Graphics. java. 240).Animation In this part of the Java 2D games tutorial.awt.Toolkit.Image. This is the easiest but also the least effective way of animating objects in Java games. import javax.swing. setTitle("Star"). java.ActionListener.awt. java.Timer. 569 . setVisible(true).swing. We will implement the movement in three basic ways. } public static void main(String[] args) { new Star(). import java.Graphics2D.swing.java package star.awt. we will work with animation. setSize(280. We will use a Swing timer. Star.Color. import java. public class Star extends JFrame { public Star() { add(new Board()).awt.JFrame. a standard utility timer and a thread. import java.java import import import import package star. setResizable(false). import javax. java. public class Board extends JPanel implements ActionListener { Image star. this). this). 570 .BLACK). x. } public void paint(Graphics g) { super.getResource("star. Timer timer. This means that all drawing will be done in memory first. We start the timer. this).start(). x. timer. g2d. timer. Toolkit. Here we create a Swing Timer class. } } setDoubleBuffered(true).png")). x = y = 10. this). if (y > 240) { y = -45. In this example. g2d. x = -45.getImage().drawImage(star.drawImage(star. Every 25 ms the timer will call the actionPerformed() method. y.getClass(). setDoubleBuffered(true). y += 1. Graphics2D g2d = (Graphics2D)g.start(). timer = new Timer(25. y. } public void actionPerformed(ActionEvent e) { x += 1. g.paint(g). In order to use the actionPerformed() method. Our JPanel component will use a buffer to paint. } repaint(). Later the off-screen buffer will be copied to the screen. y.getDefaultToolkit().sync(). I didn't notice any differences. timer = new Timer(25. public Board() { setBackground(Color.dispose(). int x. ImageIcon ii = new ImageIcon(this. star = ii. we must implement the ActionListener interface. y += 1.Timer.EXIT_ON_CLOSE). Then we call the repaint() method. setSize(280.Swing. x = -45. We must synchronize the painting on Linux systems.JFrame. setDefaultCloseOperation(JFrame. we draw the star.java package star2. Otherwise.In the paint() method.util.swing. if (y > 240) { y = -45. } In the actionPerformed() method we increase the x.Timer instead of the javax. the animation would not be smooth. import javax. Figure: Star Utility timer This is very similar to the previous way. public class Star extends JFrame { public Star() { add(new Board()).sync(). Star. y values. } repaint(). public void actionPerformed(ActionEvent e) { x += 1. Toolkit. We use the java.getDefaultToolkit(). This way we regularly repaint the Board thus making the animation. 240). 571 . For Java Swing games this way should be more accurate. java.util.paint(g).Timer.Image. import java. int x. ImageIcon ii = new ImageIcon(this.Graphics2D. Board.swing.sync().getDefaultToolkit().JPanel. import javax.Graphics. setTitle("Star").java import import import import import package star2.awt.drawImage(star. timer = new Timer(). } public void paint(Graphics g) { super. Toolkit. } public static void main(String[] args) { new Star(). x = y = 10. import javax. } } The main class.dispose().getClass(). Graphics2D g2d = (Graphics2D)g.util.png")). 10). Timer timer. 100.BLACK). java. timer. setVisible(true).awt.scheduleAtFixedRate(new ScheduleTask().getImage().ImageIcon.Toolkit. } { 572 . java.getResource("star.setLocationRelativeTo(null).awt. java.awt.swing.awt. import java. public class Board extends JPanel Image star. setDoubleBuffered(true).TimerTask. star = ii. g2d. y. y. this). public Board() { setBackground(Color.Color. java. g. x. setResizable(false). . 100. public void run() { . the timer will regularly call the run() method of the ScheduleTask class.. 10).scheduleAtFixedRate(new ScheduleTask().java package star3. There is 100 ms initial delay. timer = new Timer(). Star.JFrame. 240). } } } In this example. Thread Animating objects using a thread is the most effective way of animation. setLocationRelativeTo(null). setResizable(false). x = -45. y += 1. } public static void main(String[] args) { new Star(). } } 573 . setSize(280. public class Star extends JFrame { public Star() { add(new Board()). } Each 10 ms the timer will call this run() method. if (y > 240) { y = -45.swing. Here we create a timer. setTitle("Star"). And schedule a task at 10 ms interval. setDefaultCloseOperation(JFrame.class ScheduleTask extends TimerTask { public void run() { x += 1. setVisible(true). } repaint(). timer.EXIT_ON_CLOSE). import javax. animator.This is the main class.awt.Color.swing.sync(). private final int DELAY = 50. y.drawImage(star.getResource("star. this). } public void cycle() { x += 1.java package star2. java.addNotify(). private int x. public Board() { setBackground(Color.getClass(). g. x. x = -45. if (y > 240) { y = -45.Toolkit.png")). import import import import import java. java.getDefaultToolkit().awt.awt. animator = new Thread(this). import javax.dispose(). Board. y += 1. private Thread animator. Toolkit. star = ii.Image.JPanel. } 574 .paint(g). setDoubleBuffered(true).Graphics. public class Board extends JPanel implements Runnable { private Image star.awt.BLACK). import javax.ImageIcon.awt. } public void addNotify() { super. y.getImage(). ImageIcon ii = new ImageIcon(this. g2d.swing. x = y = 10. java. } public void paint(Graphics g) { super. java. Graphics2D g2d = (Graphics2D)g.Graphics2D.start(). } 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 VK_RIGHT) { dx = 1.VK_LEFT) { dx = 0. y + height/2)). We need them in collision detection. } if (key == KeyEvent. } public void keyReleased(KeyEvent e) { int key = e.getKeyCode(). y.add(new Missile(x + width. } if (key == KeyEvent.VK_RIGHT) { dx = 0. } The getBounds() method returns the bounds of the spacecraft image.VK_LEFT) { dx = -1. } if (key == KeyEvent. } } public void fire() { missiles. width. } if (key == KeyEvent. public Rectangle getBounds() { return new Rectangle(x. height). } if (key == KeyEvent.getKeyCode().VK_DOWN) { dy = 1. if (key == KeyEvent. } } } This class represents a spacecraft.VK_SPACE) { fire().VK_DOWN) { dy = 0.VK_UP) { dy = -1. 588 .public void keyPressed(KeyEvent e) { int key = e.VK_UP) { dy = 0. } if (key == KeyEvent. if (key == KeyEvent. } if (key == KeyEvent. {740.event. 239}. {1380. 259}.event. java. java. 150}.JFrame. craft = new Craft(). 20}. setBackground(Color. {760. 159}. 59}. 30}. {490. import import import import import import import import import import import java. {860. ArrayList aliens. public Board() { addKeyListener(new TAdapter()). 209}. {780. {820. java. 180}. setDoubleBuffered(true).awt. {530.Board.JPanel. {580.awt. import javax.awt.Graphics2D.Color. setFocusable(true). 70}.util.awt. 200}. 59}. 170}. {810. 50}. {660. {2500. {940. timer = new Timer(5. 220}. {680. java. {540. 259}. 30} }.FontMetrics. import java. {920. 139}. {790. import javax. {590.java package collision.KeyAdapter.ActionListener. boolean ingame.awt. } 589 . 60}. {930.KeyEvent. import javax. java.Font.swing.awt. java.start().awt. private int[][] pos = { {2380. 128}. ingame = true.Toolkit. {510.ArrayList. Craft craft. {900. {700.Rectangle. int B_HEIGHT. {980. 90}. java. {990.event. int B_WIDTH. {560. initAliens().awt. this). 45}. public class Board extends JPanel implements ActionListener { private private private private private private Timer timer. 89}. {790.BLACK). 29}.Timer.event. 300). java.ActionEvent.awt. timer. setSize(400.swing. 50}.swing.awt.Graphics. java. 109}. 80}.awt. java. craft. g.addNotify(). g2d. i<pos.drawImage(craft. g.getY(). i++) { Missile m = (Missile)ms. 14). if (ingame) { Graphics2D g2d = (Graphics2D)g. FontMetrics metr = this.setColor(Color. craft. i++ ) { aliens.length. } } public void paint(Graphics g) { super. } g2d. this). (B_WIDTH . for (int i=0.getX().add(new Alien(pos[i][0].metr. if (craft.getFontMetrics(small).isVisible()) g2d.getImage().getMissiles().size(). B_HEIGHT = getHeight().setColor(Color.getDefaultToolkit().size().drawImage(m. this). Font. m. 5.drawImage(a.getY().WHITE).size(). pos[i][1])). } else { String msg = "Game Over".dispose(). g2d.white).getY(). } for (int i = 0. 15).getImage(). a. if (a.setFont(small).public void addNotify() { super.getImage().getX().isVisible()) g2d.get(i). Font small = new Font("Helvetica". } public void initAliens() { aliens = new ArrayList(). i++) { Alien a = (Alien)aliens. i < ms. a. m. } public void actionPerformed(ActionEvent e) { 590 . ArrayList ms = craft. } Toolkit. B_HEIGHT / 2). i < aliens. this).getX().paint(g).get(i). g. B_WIDTH = getWidth().drawString("Aliens left: " + aliens.drawString(msg.sync().stringWidth(msg)) / 2.BOLD. for (int i = 0. g. setVisible(false).remove(i). i++) { Missile m = (Missile) ms.size().getBounds().isVisible()) m. i < ms. i < aliens. } } } } private class TAdapter extends KeyAdapter { 591 .setVisible(false).getMissiles(). for (int j = 0. j++) { Alien a = (Alien) aliens. ingame = false.get(j).move().move().remove(i). for (int j = 0.if (aliens. for (int i = 0. Rectangle r2 = a. if (r3.get(i). j<aliens.getBounds().size()==0) { ingame = false. Rectangle r1 = m. i < ms. checkCollisions(). else ms. } public void checkCollisions() { Rectangle r3 = craft. if (m.size().setVisible(false). else aliens. if (r1. j++) { Alien a = (Alien) aliens. for (int i = 0. } } ArrayList ms = craft. Rectangle r2 = a.intersects(r2)) { craft.get(i).setVisible(false).intersects(r2)) { m. i++) { Missile m = (Missile) ms.getMissiles(). } ArrayList ms = craft.getBounds(). if (a. repaint().isVisible()) a. a.get(j). } for (int i = 0.move().size().getBounds(). a.size().size(). i++) { Alien a = (Alien) aliens. j<aliens.get(i). } craft. getBounds(). 50}. {700. 159}. {1380. {990. private int[][] pos = { {2380. {930. the game is finished. 150}. {790. {860. This is checked by the isVisible() method. j++) { Alien a = (Alien) aliens.size()==0) { ingame = false. i++) { Alien a = (Alien)aliens. {820. } } 592 . if (a. 80}. a. They are drawn only if they have not been previously destroyed. 220}. 45}. {940.keyReleased(e). {530. 139}. 30} }. 60}. These are initial positions of alien ships. for (int i = 0. } The paint() method draws all aliens from the aliens ArrayList. 90}. {490. 170}. Rectangle r1 = m. {760. {580.isVisible()) g2d.public void keyReleased(KeyEvent e) { craft. {680. 259}. {780. {920.size(). for (int i = 0. {980. 109}. Rectangle r2 = a. {790. } } } This is the Board class. 259}. if (aliens. i < ms.getY().getBounds(). 200}. 239}. 20}. {590. 128}. } If we destroy all alien ships. 30}.size(). a.getImage(). 29}.get(i). 59}. if (r1. {740. j<aliens. 209}. {900.setVisible(false). i++) { Missile m = (Missile) ms.setVisible(false). ArrayList ms = craft.get(j). 70}. 180}.getMissiles().get(i). i < aliens. for (int j = 0. 89}.size().drawImage(a. {540. {660. 59}. {810. {2500.intersects(r2)) { m. } public void keyPressed(KeyEvent e) { craft.getX(). {510. a.keyPressed(e). 50}. {560. this). visible = visible. int y) { ImageIcon ii = new ImageIcon(this. } public Image getImage() { return image.ImageIcon. image = ii. if any of the missiles intersects with any of the aliens. this.png". } public void setVisible(Boolean visible) { this. this.x = x.getImage(). int height.getHeight(null). height = image. 593 . private private private private private private int x.getResource(craft)). } public int getX() { return x.} Here we check.Image. import java.getWidth(null). } public void move() { if (x < 0) x = 400. x -= 1.java package collision. } public int getY() { return y. int y. If they do. boolean visible. public class Alien { private String craft = "alien.getClass().awt.y = y. int width. import javax.Rectangle. } public boolean isVisible() { return visible. visible = true. import java. public Alien(int x.swing. both the missile and the alien are destroyed.awt. Image image. Alien. width = image. awt. public void move() { if (x < 0) x = 400. import javax.swing.Rectangle. y.x = x. height). } } This is the Alien class.getHeight(null). width. this. x -= 1. int y) { ImageIcon ii = new ImageIcon(this.getClass().ImageIcon.getResource("missile. } public int getX() { return x. visible.Image.awt.getWidth(null). } public int getY() { return y. private final int MISSILE_SPEED = 2. y. width = image. } public Image getImage() { return image. Image image. import java. height = image. this. Missile. int width.png")).} public Rectangle getBounds() { return new Rectangle(x. image = ii. visible = true. after they have disappeared on the left. } Aliens return to the screen. public class Missile { private private boolean private int x. height. import java. 594 . private final int BOARD_WIDTH = 390.java package collision. public Missile(int x.y = y.getImage(). import javax. 300). } public static void main(String[] args) { new Collision(). Collision. this is the main class. } public void move() { x += MISSILE_SPEED. setTitle("Collision"). } } This is the Missile class. public class Collision extends JFrame { public Collision() { add(new Board()). } public void setVisible(Boolean visible) { this. if (x > BOARD_WIDTH) visible = false. 595 .EXIT_ON_CLOSE).JFrame. setDefaultCloseOperation(JFrame.visible = visible. } public Rectangle getBounds() { return new Rectangle(x.} public boolean isVisible() { return visible. height). setLocationRelativeTo(null).swing. setResizable(false).java package collision. } } Finally. width. setSize(400. setVisible(true). y. swing.GridLayout. Image source.CropImageFilter.FilteredImageSource. java.swing.awt.awt.JButton. 596 .awt. javax. java. java.awt.swing.swing. public class Puzzle extends JFrame implements ActionListener { private private private private JPanel centerPanel. It is cut into 12 pieces.java import import import import import import import import import import import import import import java. javax. javax. java.Dimension. javax.Image.event. javax. java. JButton button. java.awt.JPanel.Figure: Shooting aliens This chapter was about collision detection.image.JFrame. java.JLabel. Puzzle.awt.BorderLayout.ActionEvent. The goal is to form the picture.image.swing.swing. javax. The Puzzle game In this chapter.awt.ActionListener.awt. JLabel label. Puzzle We have an image of a Sid character from the Ice Age movie.Box. we will create a simple puzzle game.ImageIcon.event. source = sid.getSource(). } public static void main(String[] args) { new Puzzle(). setTitle("Puzzle"). for ( int i = 0. public Puzzle() { pos = new int[][] { {0. 0.CENTER).getIconWidth(). i*height/4. setResizable(false).getResource("icesid.addActionListener(this).jpg")).class.setIcon(new ImageIcon(image)). ImageIcon sid = new ImageIcon(Puzzle. BorderLayout. height = sid. } } } setSize(325. new CropImageFilter(j*width/3. centerPanel = new JPanel(). setDefaultCloseOperation(JFrame. 5)).private Image image. height. (width/3)+1. 8}. width = sid. 4. image = createImage(new FilteredImageSource(source. button. add(centerPanel. j < 3. 2}. i < 4. {3. 0)). setLocationRelativeTo(null). BorderLayout. 275). height/4))). } else { button = new JButton(). 7. 10. 5}. int width.setLayout(new GridLayout(4. {6. 11} }.DISPOSE_ON_CLOSE). button.createRigidArea(new Dimension(0.NORTH). {9. add(Box.add(label).getIconHeight(). setVisible(true).getImage(). centerPanel. j++) { if ( j == 2 && i == 3) { label = new JLabel(""). i++) { for ( int j = 0. } 597 .add(button). 1. centerPanel. centerPanel. int[][] pos. 4. buttonY) == -size. buttonX = button.add(label.buttonX) == size.buttonY) == size. } if (labelY == buttonY && (labelX .remove(buttonIndex). centerPanel.labelIndex).remove(buttonIndex). centerPanel. centerPanel.add(label.public void actionPerformed(ActionEvent e) { JButton button = (JButton) e. centerPanel.add(button.getX().3. centerPanel. Dimension size = button. centerPanel.add(label.width ) { int labelIndex = buttonIndex + 1. buttonY = button.1.labelIndex).getX(). Only buttons adjacent to the label can be moved. centerPanel. centerPanel. centerPanel. buttonIndex = pos[buttonPosY][buttonPosX].getY().height.getSize(). centerPanel.add(label.getSource(). } } } The goal of this little game is to form the original picture. buttonIndex).validate().validate(). centerPanel.getY().height ) { int labelIndex = buttonIndex .add(button.height ) { int labelIndex = buttonIndex + 3.validate(). } if (labelX == buttonX && (labelY . centerPanel. centerPanel. labelY = label.remove(buttonIndex). We move the buttons by clicking on them. buttonIndex).remove(labelIndex). centerPanel. if (labelX == buttonX && (labelY . pos = new int[][] { 598 . } if (labelY == buttonY && (labelX . buttonIndex).buttonX) == -size. buttonPosY = buttonY / size. labelIndex). centerPanel. centerPanel.labelIndex).add(button.labelIndex).add(button.validate(). buttonPosX = buttonX / size. int int int int int int int labelX = label.width.width ) { int labelIndex = buttonIndex . {9. centerPanel.add(button. } } } The code creates 11 buttons and one label. buttonIndex).height. y coordinates are important in the logic of the program.buttonY) == size. {3. (width/3)+1. j < 3.height ) { int labelIndex = buttonIndex + 3.getResource("icesid. 10.setIcon(new ImageIcon(image)). 8}. 11} These are the positions of the image parts.width.getX(). buttonX = button. centerPanel. button. }. image = createImage(new FilteredImageSource(source. int buttonPosX = buttonX / size. new CropImageFilter(j*width/3.getSource(). Here we get the index of the button in the two dimensional array of the button positions. buttonY = button. source = sid. 5}. 4. 2}. centerPanel. button. 1. height/4))). We get the x.{0.getY().validate(). int int int int labelX = label. 7.addActionListener(this). centerPanel. We use the ImageIcon class to load the image. labelY = label. for ( int i = 0.add(label). ImageIcon sid = new ImageIcon(Puzzle. The x.getImage(). int buttonPosY = buttonY / size. y coordinates of the button that we hit and an empty label. i*height/4. if (labelX == buttonX && (labelY .getX().getY().add(label. } else { button = new JButton(). i < 4. j++) { if ( j == 2 && i == 3) { label = new JLabel(""). We crop the image into pieces and place them on the buttons. i++) { for ( int j = 0.add(button).jpg")). {6.labelIndex). centerPanel. centerPanel. } 599 .class. int buttonIndex = pos[buttonPosY][buttonPosX].remove(buttonIndex). awt.event. The snake must avoid the walls and its own body. Snake Snake is an older classic video game.Font.height is true.Graphics.buttonY) == size.Color. Later it was brought to PCs.awt. they share the x coordinate. In this game the player controls a snake.Toolkit. Development The size of each of the joints of a snake is 10px.Image. Board. The snake is controlled with the cursor keys. java. If the game is finished. we check if we clicked on the button.awt. Figure: Puzzle This was a Puzzle game. It was first created in late 70s. we will create a Java Snake game clone. 600 .awt. Initially the snake has three joints. java. we display Game Over message in the middle of the Board.awt. Snake In this part of the Java 2D games tutorial. If it is above the label. The game is started by pressing one of the cursor keys.awt. the equation (labelY .ActionEvent. java.FontMetrics. This game is sometimes called Nibbles.In this case.awt.java import import import import import import import package snake2. that is right above the empty label. java. If the button is right above the label. Each time the snake eats an apple. java. The objective is to eat as many apples as possible. java. java. its body grows. setBackground(Color. private int dots.awt.awt.swing.getClass(). private int apple_x.getClass().black). DELAY = 140.ActionListener.png")). right = true. import java. import javax. public Board() { addKeyListener(new TAdapter()).getResource("dot.getImage(). apple.event.KeyAdapter. down = false.KeyEvent.swing. 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.getResource("apple. head = iih.ImageIcon.event. head. ball = iid.getClass(). import javax. ImageIcon iih = new ImageIcon(this. timer.import java.getResource("head. ball. import javax. up = false. private int y[] = new int[ALL_DOTS]. private int apple_y. private int x[] = new int[ALL_DOTS]. } public void initGame() { 601 . private private private private private private private private private boolean boolean boolean boolean boolean Timer Image Image Image left = false.getImage(). DOT_SIZE = 10.swing.getImage(). setFocusable(true).png")). ALL_DOTS = 900. initGame(). ImageIcon iia = new ImageIcon(this. RAND_POS = 29.event.awt.Timer. ImageIcon iid = new ImageIcon(this. inGame = true.JPanel. HEIGHT = 300. import java.png")). apple = iia. z < dots. if (inGame) { g. else g. y[z]. } Toolkit. g. for (int z = 0.dispose(). HEIGHT / 2). this). g. z > 0. FontMetrics metr = this. } locateApple().getFontMetrics(small). (WIDTH .stringWidth(msg)) / 2. z < dots.BOLD. z++) { x[z] = 50 .paint(g).drawImage(ball.dots = 3. this). x[z].start(). } } public void gameOver(Graphics g) { String msg = "Game Over". this). } } public void move() { for (int z = dots.setColor(Color. timer = new Timer(DELAY. x[z].sync().drawImage(apple.getDefaultToolkit().z*10. g. for (int z = 0.metr. Font. 602 . Font small = new Font("Helvetica". locateApple(). g. z--) { x[z] = x[(z . apple_y.setFont(small). } else { gameOver(g). } public void paint(Graphics g) { super. y[z] = 50. y[z]. 14). z++) { if (z == 0) g. } public void checkApple() { if ((x[0] == apple_x) && (y[0] == apple_y)) { dots++.drawString(msg.white). apple_x. timer.1)]. this).drawImage(head. } if (right) { x[0] += DOT_SIZE.random() * RAND_POS). } } if (y[0] > HEIGHT) { inGame = false. } 603 .1)].random() * RAND_POS). z > 0. z--) { if ((z > 4) && (x[0] == x[z]) && (y[0] == y[z])) { inGame = false. } } public void locateApple() { int r = (int) (Math. move(). } if (y[0] < 0) { inGame = false. } if (x[0] > WIDTH) { inGame = false. } public void actionPerformed(ActionEvent e) { if (inGame) { checkApple(). r = (int) (Math. apple_y = ((r * DOT_SIZE)). apple_x = ((r * DOT_SIZE)). } if (down) { y[0] += DOT_SIZE. } } public void checkCollision() { for (int z = dots.} y[z] = y[(z . } if (x[0] < 0) { inGame = false. } if (up) { y[0] -= DOT_SIZE. if (left) { x[0] -= DOT_SIZE. checkCollision(). up = false. } } } } First we will define the constants used in our game.getKeyCode().VK_DOWN) && (!up)) { down = true. = false.VK_RIGHT) && (!left)) { right = true. y coordinates of all joints of a snake. false. The DELAY constant determines the speed of the game. } if ((key == KeyEvent. } if ((key == KeyEvent. (900 = 300*300/10*10) The RAND_POS constant is used to calculate a random position of an apple. if ((key == KeyEvent. DOT_SIZE = 10. left = false. private private private private private private final final final final final final int int int int int int WIDTH = 300. These two arrays store x.repaint(). private int y[] = new int[ALL_DOTS]. The ALL_DOTS constant defines the maximum number of possible dots on the Board. if ((key left up = down } == KeyEvent. left = false. private int x[] = new int[ALL_DOTS]. 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. DELAY = 140.VK_LEFT) && (!right)) { = true. } private class TAdapter extends KeyAdapter { public void keyPressed(KeyEvent e) { int key = e.VK_UP) && (!down)) { up = true. RAND_POS = 29. down = false. right = false. ALL_DOTS = 900. 604 . HEIGHT = 300. right = false. look at how the snake is moving. In the checkCollision() method. if (y[0] > HEIGHT) { inGame = false. z--) { if ((z > 4) && (x[0] == x[z]) && (y[0] == y[z])) { inGame = false. To understand it.EXIT_ON_CLOSE). the third joint where the second was etc. setResizable(false). } } Finish the game.java package snake2. setTitle("Snake").1)].1)].JFrame. z > 0. 340). setVisible(true).swing. for (int z = dots. Snake.In the move() method we have the key algorithm of the game. setDefaultCloseOperation(JFrame. setLocationRelativeTo(null). } This code moves the joints up the chain. The second joint moves where the first was. The rest of the joints move one position up the chain. import javax. z--) { x[z] = x[(z . we determine if the snake has hit itself or one of the walls. You can change its direction with the cursor keys. } Move the head to the left. setSize(320. z > 0. for (int z = dots. if the snake hits one of its joints with the head. public class Snake extends JFrame { public Snake() { add(new Board()). if the snake hits the bottom of the Board. } 605 . } Finish the game. You control the head of the snake. y[z] = y[(z . if (left) { x[0] -= DOT_SIZE. I was inspired by the pybreakout game. So that it is easier to understand. The game consists of seven files. we have one paddle. Development In our game. the player moves a paddle on the screen and bounces a ball/balls. } This is the main class. In this game. score. We do not work with angles. Top. 606 . The game was created in 1976. one ball and 30 bricks. paddle and a brick in Inkscape. left and right. We use a timer to create a game cycle. levels. bottom. There are no bonuses. we simply change directions. The game is intentionally simple. I have created an image for a ball. Figure: Snake This was the Snake game.} public static void main(String[] args) { new Snake(). The objective is to destroy bricks in the top of the window. The Breakout game In this part of the Java 2D games tutorial we will create a simple Breakout game clone. It was developed in PyGame library by Nathan Dawson. The Breakout is an arcade game originally developed by Atari Inc. } public void setY(int y) { this. int heigth. import java. } Rectangle getRect() { return new Rectangle(x. y.Image. Image image. HEIGTH = 400.java package breakout. BALL_RIGHT = 280. 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.awt. } public int getHeight() { return heigth.awt.y = y. } public int getWidth() { return width. } public int getX() { return x. 607 . Sprite. import java. public void setX(int x) { this.Commons. int y. public class Sprite { protected protected protected protected protected int x. int width. PADDLE_RIGHT = 250. The Commons.java file has some common constants.Rectangle.java package breakout. } Image getImage() { return image. } public int getY() { return y.x = x. BOTTOM = 390. In the destroyed variable we keep the state of a brick. } } This is the Brick class.swing.getWidth(null). Ball. import javax.y = y.} } image. import javax. int y) { this. public class Ball extends Sprite implements Commons { 608 ..getImage(). width = image.java package breakout. this.ImageIcon. ImageIcon ii = new ImageIcon(this. The Sprite class is a base class for all objects on the Board. heigth = image.getClass().ImageIcon. boolean destroyed. We put here all methods and variables that are in Ball./images/brickie. } public void setDestroyed(boolean destroyed) { this.png". public class Brick extends Sprite { String brickie = ".getHeight(null). destroyed = false. Brick.x = x. Like getImage() or getX() methods.destroyed = destroyed. } public boolean isDestroyed() { return destroyed. boolean destroyed.getWidth(null). Brick and Paddle objects.getResource(brickie)).swing.java package breakout. image = ii.getHeight(null)). public Brick(int x. image. if (x == 0) { setXDir(1). y = 355.getHeight(null). image = ii. ImageIcon ii = new ImageIcon(this.getImage(). y += ydir. resetState().getWidth(null). width = image.getResource(ball)). ydir = -1.png". } if (x == BALL_RIGHT) { setXDir(-1). } public void move() { x += xdir.getClass(). } } 609 ./images/ball. protected String ball = ". } public int getYDir() { return ydir.. } } public void resetState() { x = 230. private int ydir. } if (y == 0) { setYDir(1). heigth = image.private int xdir. } public void setYDir(int y) { ydir = y. public Ball() { xdir = 1. } public void setXDir(int x) { xdir = x. void setYDir(int y) y. public { xdir = } public { ydir = } void setXDir(int x) x. import javax.java package breakout.getImage()..getHeight(null). } 610 .awt. public class Paddle extends Sprite implements Commons { String paddle = ". if (x == 0) { setXDir(1). when the ball hits the paddle or a brick. public void move() { x += xdir.png". If the ball hits the borders. These two methods are called.getResource(paddle)). public Paddle() { ImageIcon ii = new ImageIcon(this. } if (x == BALL_RIGHT) { setXDir(-1)./images/paddle. resetState(). y += ydir.event. } } The move() method moves the ball on the Board. the directions are changed accordingly.This is the Ball class. heigth = image.getClass(). } if (y == 0) { setYDir(1). Paddle. int dx. image = ii. import java.swing. width = image.ImageIcon.KeyEvent.getWidth(null). awt. By releasing the arrow key. By pressing the arrow key.Toolkit. java. java.Graphics.public void move() { x += dx.awt.awt. The paddle is controlled with left and right arrow keys.PADDLE_RIGHT) x = Commons.getKeyCode(). y = 360. we set the dx variable to zero. java.VK_RIGHT) { dx = 2.event. It encapsulates the paddle object in the Breakout game.KeyAdapter. } public void keyPressed(KeyEvent e) { int key = e. if (key == KeyEvent. } if (key == KeyEvent. } if (key == KeyEvent.Font.event.Color.FontMetrics. This way the paddle stops moving. we set the direction variable.awt. java.VK_LEFT) { dx = 0.java package breakout.PADDLE_RIGHT. 611 . Board.awt. java. } } } public void resetState() { x = 200.VK_LEFT) { dx = -2. } } public void keyReleased(KeyEvent e) { int key = e.awt.KeyEvent.getKeyCode(). import import import import import import import import import java.Image.VK_RIGHT) { dx = 0. if (x <= 2) x = 2. java.awt. java. } This is the Paddle class.awt.awt.Point. if (key == KeyEvent. if (x >= Commons. java. timer = new Timer().getY(). } public void addNotify() { super. paddle.import java. i * 10 + 50). 1000. 10).util. i++) { for (int j = 0. ball.Timer.JPanel.getImage(). j++) { bricks[k] = new Brick(j * 40 + 30. } } } public void paint(Graphics g) { super.getImage().addNotify(). for (int i = 0. bricks = new Brick[30]. int timerId. i < 30. import javax. boolean ingame = true. public class Board extends JPanel implements Commons { Image ii. paddle. Brick bricks[].getY(). ball.util. timer. ball. this). Timer timer. g. this). int k = 0. paddle = new Paddle(). import java. i < 5.drawImage(ball.paint(g).TimerTask. k++. Paddle paddle.getWidth(). ball.swing. paddle. if (ingame) { g. i++) { 612 . paddle. setDoubleBuffered(true). public Board() { addKeyListener(new TAdapter()).getHeight(). for (int i = 0.scheduleAtFixedRate(new ScheduleTask().getWidth(). setFocusable(true).drawImage(paddle.getHeight(). } public void gameInit() { ball = new Ball().getX(). j < 6.getX(). gameInit(). Ball ball. String message = "Game Over". repaint(). } } class ScheduleTask extends TimerTask { public void run() { ball.isDestroyed()) g.getRect(). } public void keyPressed(KeyEvent e) { paddle.keyPressed(e).BLACK).setFont(font).getImage().drawString(message.sync(). g. g.stringWidth(message)) / 2.BOLD. bricks[i].move().dispose(). (Commons. bricks[i]. paddle.setColor(Color. FontMetrics metr = this. i < 30. } private class TAdapter extends KeyAdapter { public void keyReleased(KeyEvent e) { paddle. bricks[i]. 18).keyReleased(e). checkCollision().WIDTH .getY().WIDTH / 2). } public void checkCollision() { if (ball. timer.drawImage(bricks[i]. j = 0. } } else { Font font = new Font("Verdana".getWidth().if (!bricks[i].getX(). Commons.metr. this). g.getFontMetrics(font). 613 .cancel().isDestroyed()) { j++. g.move().getHeight().getMaxY() > Commons.BOTTOM) { stopGame(). } for (int i = 0. Font. } Toolkit.getDefaultToolkit(). } } public void stopGame() { ingame = false. i++) { if (bricks[i]. bricks[i]. ballTop .setXDir(1).1.getHeight(). if (ballLPos < first) { ball.getRect()). stopGame(). 614 . ball. ball. second = paddleLPos + 16.getRect(). int ballLPos = (int)ball. } } for (int i = 0.getMinX(). } if (ballLPos > fourth) { ball.setXDir(1).intersects(bricks[i].getRect().} } if (j == 30) { message = "Victory". i < 30.getWidth().getRect()).setXDir(-1).setYDir(-1 * ball.getRect(). } if (ballLPos >= second && ballLPos < third) { ball. Point pointLeft = new Point(ballLeft .getYDir()).getRect())) { int paddleLPos = (int)paddle. fourth = paddleLPos + 32. Point pointBottom = new Point(ballLeft.getRect(). ballTop = (int)ball.getRect(). i++) { if ((ball.getYDir()).setYDir(-1 * ball.getRect(). ball. int int int int first = paddleLPos + 8.setYDir(-1). third = paddleLPos + 24. ballTop).getMinY(). Point pointRight = new Point(ballLeft + ballWidth + 1.setYDir(-1).intersects(paddle.1).setXDir(0). } if (ballLPos >= first && ballLPos < second) { ball. Point pointTop = new Point(ballLeft. } if ((ball. ball. ballWidth = (int)ball.setYDir(-1). ball. } if (ballLPos >= third && ballLPos < fourth) { ball. ballHeight = (int)ball.getRect())) { int int int int ballLeft = (int)ball. ballTop).getMinX().setXDir(-1). ballTop + ballHeight + 1).getMinX(). getX().getHeight().getWidth().if (!bricks[i].setXDir(-1). for (int i = 0. paddle. ball. j++) { bricks[k] = new Brick(j * 40 + 30.setYDir(1). if (ingame) { g. ball.setYDir(-1). k++.drawImage(bricks[i].getImage().getRect().drawImage(ball. bricks[i]. class ScheduleTask extends TimerTask { 615 . bricks[i]. } if (bricks[i].setXDir(1).contains(pointBottom)) { ball. this). } } } } } This is the Board class.getY().isDestroyed()) g.getImage(). bricks[i].getY(). paddle.contains(pointRight)) { ball.getX(). i * 10 + 50).getX(). } bricks[i]. } } } In the gameInit() method we create a ball a paddle and 30 bricks. public void gameInit() { ball = new Ball().getHeight(). for (int i = 0. } } If the game is not finished. i++) { if (!bricks[i].contains(pointLeft)) { ball.getY(). the paddle and the bricks inside the paint event. Here we put the game logic. i < 30. this).getRect().getWidth(). } else if (bricks[i].isDestroyed()) { if (bricks[i]. g. paddle. paddle = new Paddle(). ball. } else if (bricks[i].getHeight(). this).setDestroyed(true).getWidth(). i++) { for (int j = 0.drawImage(paddle. ball. int k = 0.getRect(). j < 6. i < 5. we draw the ball.contains(pointTop)) { ball.getRect(). bricks[i]. paddle.getImage(). } If the ball hits the first part of the paddle. paddle. we move the ball and the paddle. } if (j == 30) { message = "Victory". ball. i < 30. import javax. In the run method. if (ball. checkCollision(). public class Breakout extends JFrame { public Breakout() { add(new Board()).JFrame. It goes down.contains(pointTop)) { ball. the ball direction changes. } If the ball hits the bottom of the brick.} public void run() { ball. If it hists the boundaries. we change the y direction of the ball.setXDir(-1).setYDir(-1).getRect(). we stop the game. repaint(). for (int i = 0.setYDir(1).move(). we win the game. And we repaint the screen. Breakout. } } We check how many bricks are destroyed.isDestroyed()) { j++.getMaxY() > Commons. If we destroyed all 30 bricks. The autoMove() method is called each game cycle to move the ball on the screen. if (bricks[i]. } The ScheduleTask is triggerd every 10 ms.getRect().move().swing. if (ballLPos < first) { ball. We check for possible collisiont. 616 . we change the direction of the ball to the north-east. stopGame(). j = 0. i++) { if (bricks[i]. } If the ball hits the bottom.java package breakout.BOTTOM) stopGame(). Figure: The Breakout game This was the Breakout game. setDefaultCloseOperation(EXIT_ON_CLOSE).} setTitle("Breakout"). setVisible(true). } } And finally.WIDTH. It is modified and simplified. Commons. which has the entry main method. Since 617 . public static void main(String[] args) { new Breakout(). Tetris The Tetris game is one of the most popular computer games ever created. setResizable(false).HEIGTH). The original game was designed and programmed by a Russian programmer Alexey Pajitnov in 1985. setLocationRelativeTo(null). we will create a Tetris game clone in Java Swing. this is the Breakout class. Tetris In this chapter. setIgnoreRepaint(true). setSize(Commons. the row is destroyed and we score. public class Tetris extends JFrame { JLabel statusbar.java package tetris.JLabel. Tetris is available on almost every computer platform in lots of variations. The d key will drop the piece one line down. The object of the Tetris game is to move and rotate the shapes. Behind every computer game. The score is the number of lines. If we manage to form a row. so that it is easier to understand.swing. no acceleration is implemented. Tetris is called a falling block puzzle game. we draw the tetrominoes using Swing drawing API. Line-shape. The space key will drop the tetris piece immediately to the bottom. import javax. after it is launched. Figure: Tetrominoes The development We do not have images for our tetris game. import javax. We can pause the game by pressing the p key. Tetris. S-shape.then. • • • • 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. L-shape. MirroredL-shape and a Square-shape. Each of these shapes is formed with four squares. In this game.) The game goes at constant speed. The shapes are falling down the board.JFrame. (It can be used to speed up the falling a bit. The game starts immediately. we have seven different shapes called tetrominoes. import java. Z-shape. that we have removed. We play the tetris game until we top out. T-shape. so that they fit as much as possible. 618 .BorderLayout.swing. So it is in Tetris. Some ideas behind the game.awt. there is a mathematical model. Even my mobile phone has a modified version of the Tetris game. lang.SOUTH). board.util. } public static void main(String[] args) { Tetris game = new Tetris(). board. SquareShape. setDefaultCloseOperation(EXIT_ON_CLOSE). Immediately. public Shape() { coords = new int[4][2]. We create a statusbar. SShape. TShape. we set up the game. private int coords[][]. game.java file.setLocationRelativeTo(null). Board board = new Board(this). } public JLabel getStatusBar() { return statusbar.public Tetris() { statusbar = new JLabel(" 0"). Shape. LShape.start(). add(board). BorderLayout. setSize(200.NoShape). setTitle("Tetris"). The start() method starts the Tetris game.Random. ZShape. import java. } } In the Tetris. private Tetrominoes pieceShape. LineShape. 400). setShape(Tetrominoes.start(). MirroredLShape }. public class Shape { enum Tetrominoes { NoShape. add(statusbar.setVisible(true). game. We create a board on which we play the game. after the window appears on the screen. private int[][][] coordsTable. } public void setShape(Tetrominoes shape) { 619 . import java.java package tetris.Math. -1 }. int x = Math. { 0.ordinal()][i][j]. { 0. 0 }. } public Shape rotateLeft() { if (pieceShape == Tetrominoes. { 0. { { 0.min(m. { { -1. 0 }.SquareShape) return this. 0.abs(r. } return m. { 0. } coords[index][1]. 0 } }. { { 0.values(). coords[i][0]). 0 }. i++) { m = Math. -1 }. 1 }. { 0. 1 } } for (int i = 0.coordsTable = new int[][][] { { { 0. 0 }. 0 }. i < 4. j < 2. 0 }. i++) { m = Math. } } pieceShape = shape. 0 }. Shape result = new Shape(). 1. { 0. { 0. { 0. } public int minY() { int m = coords[0][1]. Tetrominoes[] values = Tetrominoes. } return m. { 0.nextInt()) % 7 + 1. for (int i=0. int public int x(int index) { return public int y(int index) { return public Tetrominoes getShape() { x) { coords[index][0] = x. 1 } }. { 0. { 1. i++) { for (int j = 0. 0 }. } private void setX(int index. { -1. 620 . { 1. for (int i=0. int private void setY(int index.min(m. -1. { 1. -1 }. } coords[index][0]. 0 }. 0 }. i < 4 . -1 }.pieceShape = pieceShape. -1 }. -1 }. 0 }. i < 4. { 0. 0 }. 1 } }. } y) { coords[index][1] = y. } public void setRandomShape() { Random r = new Random(). 1 } }. 1 } }. }. 1 }. 0. } public int minX() { int m = coords[0][0]. 0 }. result. { { { { { { { { 0. coords[i][1]). { 0. 0 }. 2 } }. -1 }. { { 0. 1 } }. 0. { { -1. setShape(values[x]). { { 1. ++j) { coords[i][j] = coordsTable[shape. 0. } return pieceShape. 1. 0 }. { { 0. setY(i. result. 1 }. 0 }. SquareShape. -1 }.NoShape). 0 }. 0 }. 0 }. 1 } }. }. { 0. 1 }. The coords array holds the actual coordinates of a Tetris piece. Plus the empty shape called here NoShape. 0 }. -1. 1 } } The coordsTable array holds all possible coordinate values of our tetris pieces. 0 } }. i < 4 . { { 1. 0 }.setX(i. } return result. 0. ++i) { result. result. ZShape. { 0. -1 }. { 1. { 0. { { 0. This is a template from which all pieces take their coordiate values. setShape(Tetrominoes. 1 } }. { { -1. { 0. ++j) { 621 . } public Shape rotateRight() { if (pieceShape == Tetrominoes. } return result. { 1. { 0. 0 }. -y(i)). Shape result = new Shape(). i++) { for (int j = 0.pieceShape = pieceShape. -x(i)). for (int i = 0. { { -1. y(i)). 0. The Tetrominoes enum holds all seven tetris shapes. coordsTable = new int[][][] { { { 0. { 1. 0 }. x(i)). 1 } }. { 0. 0. -1 }. 1 } }. { { 0. j < 2. 0 }. 1. 0 }. 0. } This is the constructor of the Shape class. ++i) { result. TShape. { 0.setX(i.for (int i = 0. MirroredLShape }. 1. { 0. { { 0. { { 0. SShape. { -1. -1 }.setY(i. -1 }. { 0. 0 }. 0 }. i < 4. { 0. LShape. i < 4. enum Tetrominoes { NoShape. 1 } }. public Shape() { coords = new int[4][2]. -1 }. result. for (int i = 0. { { { { { { { { 0. { 0. -1 }. 0 }. LineShape. 2 } }. { 0. 0 }.SquareShape) return this. } } The Shape class provides information about a tetris piece. 0 }. represent a rotated S-shape. For example. java.awt. ++i) { result. } return result. } This code rotates the piece to the left. -x(i)).awt. Here we put one row of the coordiate values from the coordsTable to a coords array of a tetris piece. The coords array saves the coordinates of the tetris piece. java. { -1.awt. Figure: Coordinates public Shape rotateLeft() { if (pieceShape == Tetrominoes.SquareShape) return this.event.event. result. Shape result = new Shape(). java. The following image will help understand the coordinate values a bit more. And the ordinal() method returns the current position of the enum type in the enum object. java. 1 } . numbers { 0.java import import import import import import package tetris. { -1.setX(i. y(i)).KeyAdapter.setY(i. java. 622 . java. for (int i = 0.awt. 0 }. Unlike in C++.ordinal()][i][j].ActionEvent. Looking at the previous image will help to understand the rotation. That's why we simply return the reference to the current object. -1 }. The square does not have to be rotated.Graphics.pieceShape = pieceShape.Color.awt. an enum type is esencially an integer. Note the use of the ordinal() method. Java enums are full classes.awt. The following diagram illustrates the shape. { 0. In C++.} } coords[i][j] = coordsTable[shape. i < 4.ActionListener. result. 0 }.Dimension. Board.event. } public void start() { if (isPaused) 623 . boolean isStarted = false.import java.JLabel. clearBoard(). import javax. public class Board extends JPanel implements ActionListener { final int BoardWidth = 10.JPanel.swing. public Board(Tetris parent) { setFocusable(true).Shape. Shape curPiece. timer = new Timer(400. } Tetrominoes shapeAt(int x. this).getWidth() / BoardWidth.getStatusBar(). Tetrominoes[] board. int curY = 0. import javax. Timer timer. newPiece(). } public void actionPerformed(ActionEvent e) { if (isFallingFinished) { isFallingFinished = false. final int BoardHeight = 22. timer.getHeight() / BoardHeight. } int squareHeight() { return (int) getSize(). import tetris.KeyEvent.event. statusbar = parent.swing. curPiece = new Shape().start(). int numLinesRemoved = 0.Timer. boolean isPaused = false.Tetrominoes. int y) { return board[(y * BoardWidth) + x].awt. import javax. int curX = 0. board = new Tetrominoes[BoardWidth * BoardHeight]. } else { oneLineDown(). JLabel statusbar.swing. boolean isFallingFinished = false. } } int squareWidth() { return (int) getSize(). addKeyListener(new TAdapter()). i < BoardHeight. newPiece().getHeight() . curPiece. isStarted = true.setText(String. while (newY > 0) { 624 . } } } private void dropDown() { int newY = curY. i < 4. isPaused = !isPaused. j < BoardWidth. timer. for (int i = 0. isFallingFinished = false. int y = curY . statusbar. } public void paint(Graphics g) { super. } repaint().NoShape) { for (int i = 0.1).stop(). if (isPaused) { timer.start(). Dimension size = getSize(). boardTop + (BoardHeight .start().valueOf(numLinesRemoved)).i . BoardHeight . ++j) { Tetrominoes shape = shapeAt(j. 0 + j * squareWidth().y . drawSquare(g. } else { timer.setText("paused"). 0 + x * squareWidth(). shape). int boardTop = (int) size.curPiece. numLinesRemoved = 0.getShape()).1) * squareHeight(). boardTop + i * squareHeight().x(i).y(i).getShape() != Tetrominoes. if (shape != Tetrominoes.paint(g). } private void pause() { if (!isStarted) return.BoardHeight * squareHeight(). statusbar. } } if (curPiece. ++i) { int x = curX + curPiece. ++i) { for (int j = 0.NoShape) drawSquare(g.return. clearBoard(). ++i) { int x = curX + curPiece.NoShape. curX. if (x < 0 || x >= BoardWidth || y < 0 || y >= BoardHeight) return false.y(i).x(i). if (shapeAt(x. curX. } curPiece = newPiece. ++i) { int x = newX + newPiece.stop().setRandomShape().getShape(). board[(y * BoardWidth) + x] = curPiece. curY .1)) pieceDropped(). } private void clearBoard() { for (int i = 0. } removeFullLines(). if (!tryMove(curPiece. if (!tryMove(curPiece. statusbar. int y = newY .minY(). } private void pieceDropped() { for (int i = 0. if (!isFallingFinished) newPiece(). curX = newX.curPiece. 625 . curX = BoardWidth / 2 + 1.1 + curPiece.x(i). curY = BoardHeight .setShape(Tetrominoes. ++i) board[i] = Tetrominoes. newY . i < 4. } } private boolean tryMove(Shape newPiece. timer. y) != Tetrominoes. int newX. curY)) { curPiece. } private void newPiece() { curPiece. int y = curY . --newY.} } pieceDropped().newPiece. i < 4. private void oneLineDown() { if (!tryMove(curPiece.NoShape) return false.NoShape).y(i). i < BoardHeight * BoardWidth.1)) break. int newY) { for (int i = 0.setText("game over"). isStarted = false. curX. x + squareWidth() . 102. y + squareHeight() . g. Tetrominoes shape) { Color colors[] = { new Color(0. statusbar. new Color(102. y + squareHeight() .setColor(color. j < BoardWidth. y + 1. 626 .1. k < BoardHeight . 0) }. i >= 0. g. new Color(204. } } if (lineIsFull) { ++numFullLines. x + squareWidth() . 0. int x.drawLine(x. squareHeight() . break. ++j) { if (shapeAt(j. 204). 170. new Color(102. g. y + squareHeight() . k + 1).1. new Color(204.1.NoShape). 102). g. g. y + 1).1. 204.fillRect(x + 1.1).brighter()).darker()).1.setColor(color). curPiece.valueOf(numLinesRemoved)).drawLine(x + squareWidth() . new Color(218.} curY = newY. 0). new Color(102. y. Color color = colors[shape. j < BoardWidth. int y.2). y + squareHeight() .setColor(color. repaint(). 204. ++j) board[(k * BoardWidth) + j] = shapeAt(j. } } private void drawSquare(Graphics g.setText(String. 102. g. for (int k = i. 204. g.setShape(Tetrominoes.2. isFallingFinished = true. 204). y). x + squareWidth() . ++k) { for (int j = 0.1. g. repaint(). new Color(204. squareWidth() . return true.drawLine(x. --i) { boolean lineIsFull = true. 102).1. for (int j = 0. x. 102. i) == Tetrominoes.drawLine(x + 1. 102). y). private void removeFullLines() { int numFullLines = 0. for (int i = BoardHeight .NoShape) { lineIsFull = false.ordinal()]. } } } if (numFullLines > 0) { numLinesRemoved += numFullLines.1.1. 204). NoShape) { } int keycode = e. numLinesRemoved = 0. isPaused = false. switch (keycode) { case KeyEvent. curY). case 'd': oneLineDown(). curX.VK_SPACE: dropDown(). . curX. Finally. . case KeyEvent. } } } } return.VK_RIGHT: tryMove(curPiece. curX = 0. curX .VK_UP: tryMove(curPiece..getShape() == Tetrominoes. break. curY). isStarted = false. This is where the game logic is located.} class TAdapter extends KeyAdapter { public void keyPressed(KeyEvent e) { if (!isStarted || curPiece. break.rotateLeft(). case KeyEvent.. break. break. break. curY). curX + 1.1. curY). break. curY = 0. case KeyEvent.VK_LEFT: tryMove(curPiece.java file. 627 . return.VK_DOWN: tryMove(curPiece. case 'D': oneLineDown().. if (keycode == 'p' || keycode == 'P') { pause().getKeyCode(). case KeyEvent. we have the Board. } if (isPaused) return.rotateRight().. break. isFallingFinished = false. i . timer = new Timer(400. the board has the keyboard input. setFocusable(true). Timer object fires one or more action events after a specified delay. for (int i = 0. If so. i < 4. this). We access it using the shapeAt() method. if (shape != Tetrominoes. we paint the actual falling piece. } } In the first step we paint all the shapes.x(i).getShape() != Tetrominoes. 628 . the falling tetris piece goes one line down. drawSquare(g. j < BoardWidth.1). The numLinesRemoved counts the number of lines.start().NoShape) { for (int i = 0. public void actionPerformed(ActionEvent e) { if (isFallingFinished) { isFallingFinished = false. we draw the all objects on the board.We initialize some important variables. a new piece is created. boardTop + i * squareHeight(). if the tetris shape has finished falling and we then need to create a new shape. boardTop + (BoardHeight . } } The actionPerformed() method checks if the falling has finished. 0 + j * squareWidth(). shape). Inside the paint() method.getShape()). } else { oneLineDown(). The painting has two steps. the timer calls the actionPerformed() method each 400 ms.y(i). The curX and curY variables determine the actual position of the falling tetris shape. In our case. If not. The isFallingFinished variable determines. curPiece. 0 + x * squareWidth(). ++j) { Tetrominoes shape = shapeAt(j.NoShape) drawSquare(g. newPiece(). that have been dropped to the bottom of the board.curPiece. ++i) { int x = curX + curPiece. All the squares are rememberd in the board array. or remains of the shapes. From now. } } In the second step. timer. BoardHeight . int y = curY .y . we have removed so far. ++i) { for (int j = 0. We must explicitly call the setFocusable() method. if (curPiece.1) * squareHeight(). i < BoardHeight. More precisely. private void newPiece() { curPiece. private void pieceDropped() { for (int i = 0. This is the job of the removeFullLines() method. newY . board[(y * BoardWidth) + x] = curPiece. i < 4. This is later used at collision detection. } The clearBoard() method fills the board with empty NoSpapes. isStarted = false.setText("game over"). } pieceDropped().x(i).setRandomShape().curPiece. int y = curY . curX = BoardWidth / 2 + 1. the board holds all the squares of the pieces and remains of the pieces that has finished falling. it is time to check. i < BoardHeight * BoardWidth.NoShape). curY)) { curPiece. if (!isFallingFinished) newPiece(). curX.1 + curPiece. Once again. ++i) board[i] = Tetrominoes. } removeFullLines(). while (newY > 0) { if (!tryMove(curPiece. } The pieceDropped() method puts the falling piece into the board array.NoShape. if we can remove some lines off the board.getShape(). the piece is dropped to the bottom. } If we press the space key.private void dropDown() { int newY = curY. We simply try to drop the piece one line down until it reaches the bottom or the top of another fallen tetris piece. if (!tryMove(curPiece. curX. ++i) { int x = curX + curPiece. --newY. curY = BoardHeight . we try to create a new piece.stop().setShape(Tetrominoes.y(i). When the piece has finished falling. Then we create a new piece. timer. statusbar.1)) break. } } 629 . private void clearBoard() { for (int i = 0.minY(). drawLine(x. ++j) { if (shapeAt(j.drawLine(x. curY = newY. 630 . int newY) { for (int i = 0. The method returns false. Then we compute the initial curX and curY values. for (int j = 0. Notice. repaint(). curX = newX. x + squareWidth() . This way we destroy the full line. } } if (lineIsFull) { ++numFullLines. } curPiece = newPiece. The piece gets a new random shape.y(i). If there is at least one full line. i) == Tetrominoes. k < BoardHeight . Tetris pieces have different colors. we use so called naive gravity. } } } Inside the removeFullLines() method. ++i) { int x = newX + newPiece. If we cannot move to the initial positions. y).The newPiece() method creates a new tetris piece. We top out. for (int k = i. that in our Tetris game. return true. i < 4.NoShape) { lineIsFull = false. j < BoardWidth. break. Each of the squares is drawn with the drawSquare() method. ++k) { for (int j = 0. This means.1. y + squareHeight() . g.1.NoShape) return false. We move all the lines above the full row one line down.brighter()). } The tryMove() method tries to move the tetris piece. The timer is stopped. Every tetris piece has four squares. if (x < 0 || x >= BoardWidth || y < 0 || y >= BoardHeight) return false. private boolean tryMove(Shape newPiece. int newX. g. we check if there is any full row among all rows in the board.setColor(color. After finding a full line we increase the counter. ++j) board[(k * BoardWidth) + j] = shapeAt(j.1. k + 1).newPiece. that the squares may be left floating above empty gaps. g. j < BoardWidth. for (int i = BoardHeight . --i) { boolean lineIsFull = true. We put game over string on the statusbar. y). x.x(i). y) != Tetrominoes. if it has reached the board boundaries or it is adjacent to the already fallen tetris pieces. i >= 0. int y = newY .1. the game is over. if (shapeAt(x. it is removed. y. curY). Development The following code example is a remake of a Pacman game by Brian Postma available at javaboutique. Figure: Tetris This was the Tetris game. case KeyEvent. curX + 1. We control the game with a keyboard. So that it is easier to understand. the bottom and right sides are drawn with darker colors. I have modified and simplified the code. break. Pacman In this part of the Java 2D games tutorial we will create a simple Pacman game clone.VK_RIGHT: tryMove(curPiece. This is to simulate a 3D edge. Pacman became one of the most popular arcade games ever.The left and top sides of a square are drawn with a brighter color. Pacman is an arcade game originally developed by a Japanese company Namco in 1980. The control mechanism is implemented with a KeyAdapter. Similarly. 631 . If we pressed the left arrow key. This is an inner class that overrides the keyPressed() method. we try to move the falling piece one square to the left. pacmananimcount = 4. pacmanspeed = 6. import javax.awt.ImageIcon.KeyEvent.awt. 0). Font smallfont = new Font("Helvetica". We animate his body with four images. Color mazecolor. Font. java. java. scrsize = nrofblocks * blocksize. Image ii. boolean ingame = false. deathcounter. java. The structure of the maze is based on a simple array of integers. pacmananimpos = 0. The pacman is animated in two ways.awt. java.ActionListener.swing.Graphics. java.BasicStroke.awt. fmlarge.swing. java. import javax. Color dotcolor = new Color(192.FontMetrics.event. We also count the score. java. 192.awt. The game consists of two files.JPanel.awt. pacanimdir = 1.awt.awt. Pacman has three lives. FontMetrics fmsmall.KeyAdapter.java package pacman. final final final final final final final int int int int int int int int int int int int int blocksize = 24.awt. java.Font. pacsleft.event. depending on the direction.Dimension. java.swing.ActionEvent. pacanimdelay = 2.Timer. nrofblocks = 15. 14). 632 . nrofghosts = 6.awt. public class Board extends JPanel implements ActionListener { Dimension d. pacanimcount = pacanimdelay.event. java. java. maxghosts = 12. boolean dying = false. import import import import import import import import import import import import import import java.awt.Event.awt. Board. The maze consists of 15 x 15 squares. java.awt. import javax.The goal of the game is to collect all the points in the maze and avoid the ghosts. score.Image. The animation is used to create the illusion of pacman opening and closing his mouth.Color. java. His position in the maze and his body.Toolkit.Graphics2D.BOLD.awt.event. 16. 21. 17. 0. 21. 16. 21. 24. 16. 4. ghostspeed = new int[maxghosts]. 0. 16. 16. 16. 16. 24. 16. 17. 16. 17. pacmandx. 0. 2. 1. 18. 21. pacman4right. 16. 0. 16. 8. 9. 21. 16.start(). 24. 16. 16. 18. 20. 24. 0. 24. 24. 8. pacman1. 24. 16. pacman2up. 17. pacman2right. 16. 16. 16. 18. 16. 3. 18. 20. 16. 16. Image Image Image Image ghost. ghostx = new int[maxghosts]. 16. ghostdy. 1. dy = new int[4]. ghostspeed. 24. 18. 16. 16. ghosty = new int[maxghosts]. 16. 16. pacman3right. 20. 20. 16. 26. 22. 16. setBackground(Color. 16. 0. ghostdy = new int[maxghosts]. int currentspeed = 3. 16. int[] ghostx. 16. 16. 16. 25. 8. 16. 18. 20. 16. 25. timer = new Timer(40. 20. addKeyListener(new TAdapter()). 16. 18. 0. ghostdx = new int[maxghosts]. 0. 20. 16. 20. 8. 17. 16. 1. 100. 16. 16. 0. 18. 16. ghosty. final { 19. 16. 16. 18. 16. 8. 16. 16. 16. 16. ghostdx.int[] dx. 26. 0.black). 8. 16. 18. viewdx. 24. 21. 0. 20. 17. 8. 18. pacmandy. 17. viewdy. 16. 0. 0. 20. 1. 17. 0. reqdy. 17. 0. 18. 16. 16. Timer timer. 17. } 633 . mazecolor = new Color(5. 16. screendata = new short[nrofblocks * nrofblocks]. 20. 18. 22. 16. 16. 20. 16. 19. 25. 16. 16. setDoubleBuffered(true). 20. 17. timer. pacman2down. 24. 16. pacman4down. 0. 1. 0. 18. 18. 24. public Board() { GetImages(). 16. 25. 28 }. 16. 24. 21. 1. 17. pacman3down. 28. 0. 0. 8. 24. 20. short leveldata[] = 26. 16. 0. int reqdx. 21. 5). 18. 0. 400). 16. short[] screendata. dy. 16. 8 }. d = new Dimension(400. 16. 16. 0. 20. pacman4left. 21. pacmany. int pacmanx. 24. 16. 16. 16. 24. 21. 24. 16. 16. 0. 16. 16. 16. 17. final int validspeeds[] = { 1. 0. pacman3up. 16. 1. 16. 0. 24. setFocusable(true). pacman3left. pacman4up. dx = new int[4]. 20. 18. 16. 16. 17. 16. 0. final int maxspeed = 6. 16. 0. 0. 6. 18. 20. 17. 16. pacman2left. 16. 20. this). 16. 17. 16. 0. 16. 8. 18. 16. 255)).drawRect(50. g2d.white).fillRect(50. for (i = 0. 32. i++) { g. CheckMaze().30. g.metr.setColor(new Color(0.setColor(new Color(96. if (pacanimcount <= 0) { pacanimcount = pacanimdelay. g2d. scrsize / 2 + 96. Font.getFontMetrics(small). FontMetrics metr = this.white). (scrsize . scrsize / 2 . g2d. String s. 48)). g2d.100. scrsize + 1.drawString(s. scrsize / 2 .100. 0) 2). GameInit().setFont(small). g.public void addNotify() { super. this). DrawPacMan(g2d). 128. Font small = new Font("Helvetica". scrsize . s = "Score: " + score. } else { MovePacMan().BOLD. moveGhosts(g2d).". g2d.drawImage(pacman3left.setColor(Color. i < pacsleft. 50).addNotify().setFont(smallfont). String s = "Press s to start.stringWidth(s)) / 2.1) || pacmananimpos == } } public void PlayGame(Graphics2D g2d) { if (dying) { Death(). g. 634 . } public void DoAnim() { pacanimcount--. scrsize / } public void DrawScore(Graphics2D g) { int i. scrsize . if (pacmananimpos == (pacmananimcount . } } pacanimdir = -pacanimdir. scrsize + 16). pacmananimpos = pacmananimpos + pacanimdir. 14). i * 28 + 8.setColor(Color. } } public void ShowIntroScreen(Graphics2D g2d) { g2d.drawString(s. g2d.30. 50). i < nrofghosts. count++. } public void moveGhosts(Graphics2D g2d) { short i. } if ((screendata[pos] dx[count] = 0. boolean finished = true. dy[count] = 0. count++. dy[count] = -1. i++) { if (ghostx[i] % blocksize == 0 && ghosty[i] % blocksize == 0) { pos = ghostx[i] / blocksize + nrofblocks * (int)(ghosty[i] / blocksize). count++. int count. } if ((screendata[pos] dx[count] = 0. if ((screendata[pos] dx[count] = -1. for (i = 0. } } public void Death() { pacsleft--. LevelInit(). while (i < nrofblocks * nrofblocks && finished) { if ((screendata[i] & 48) != 0) finished = false. & 1) == 0 && ghostdx[i] != 1) { & 2) == 0 && ghostdy[i] != 1) { & 4) == 0 && ghostdx[i] != -1) { & 8) == 0 && ghostdy[i] != -1) { 635 . if (pacsleft == 0) ingame = false. if (nrofghosts < maxghosts) nrofghosts++.public void CheckMaze() { short i = 0. dy[count] = 0. } if ((screendata[pos] dx[count] = 1. LevelContinue(). dy[count] = 1. if (currentspeed < maxspeed) currentspeed++. i++. count = 0. } if (finished) { score += 50. int pos. ghostdx[i] = dx[count]. ghostdy[i] = 0. x. this). short ch. viewdx = pacmandx. } } else { count = (int)(Math. pacmandy = reqdy. y.12) && pacmanx < (ghostx[i] + 12) && pacmany > (ghosty[i] .drawImage(ghost. if (count > 3) count = 3. deathcounter = 64. int y) { g2d.12) && pacmany < (ghosty[i] + 12) && ingame) { dying = true. if (count == 0) { if ((screendata[pos] & 15) == 15) { ghostdx[i] = 0. DrawGhost(g2d. ghosty[i] + 1). int x. } } } public void DrawGhost(Graphics2D g2d. ghostdy[i] = -ghostdy[i]. } 636 . if (pacmanx > (ghostx[i] . ch = screendata[pos]. } if (pacmanx % blocksize == 0 && pacmany % blocksize == 0) { pos = pacmanx / blocksize + nrofblocks * (int)(pacmany / blocksize). } } ghostx[i] = ghostx[i] + (ghostdx[i] * ghostspeed[i]). if ((ch & 16) != 0) { screendata[pos] = (short)(ch & 15). if (reqdx == -pacmandx && reqdy == -pacmandy) { pacmandx = reqdx. viewdy = pacmandy.random() * count). ghostx[i] + 1.} count++. score++. ghostdy[i] = dy[count]. } public void MovePacMan() { int pos. } else { ghostdx[i] = -ghostdx[i]. ghosty[i] = ghosty[i] + (ghostdy[i] * ghostspeed[i]). else if (viewdy == -1) DrawPacManUp(g2d). case 2: g2d. default: g2d. this). } public void DrawPacManUp(Graphics2D g2d) switch (pacmananimpos) { case 1: g2d. pacmanx + break. viewdx = pacmandx. pacmany + 1. pacmandy = 0. this). pacmanx break. case 3: g2d. 1. pacmanx break. pacmany = pacmany + pacmanspeed * pacmandy. } } { + 1. + 1.drawImage(pacman2up. this). pacmanx + 1.drawImage(pacman4up. else if (viewdx == 1) DrawPacManRight(g2d). case 2: 637 . public void DrawPacManDown(Graphics2D g2d) { switch (pacmananimpos) { case 1: g2d. this). } public void DrawPacMan(Graphics2D g2d) { if (viewdx == -1) DrawPacManLeft(g2d). pacmany + 1.drawImage(pacman2down. viewdy = pacmandy. pacmandy = reqdy. pacmany + 1.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. + 1. this).drawImage(pacman3up. break. else DrawPacManDown(g2d). pacmany + 1. } } pacmanx = pacmanx + pacmanspeed * pacmandx.drawImage(pacman1. pacmanx break. pacmany + 1. } } // 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. pacmany + 1. this). 638 . this).setColor(mazecolor). this). this). if ((screendata[i] & 1) != 0) // draws left { g2d.drawImage(pacman4right. int x.drawImage(pacman2left. pacmanx break. this). pacmany + 1. default: g2d. x < scrsize. this). + 1. + 1. pacmanx + 1. break.drawImage(pacman1. x. pacmanx + 1. pacmany + 1. break.drawImage(pacman3left. break. y + blocksize . pacmany + 1. default: g2d. pacmany + 1. pacmany + 1. y += blocksize) { for (x = 0. break. pacmanx + 1. case 3: g2d. default: g2d. break.1). pacmanx break. pacmany + 1. } public void DrawPacManLeft(Graphics2D g2d) { switch (pacmananimpos) { case 1: g2d.drawImage(pacman1. this). this). pacmanx + 1. this). pacmanx + 1.drawLine(x. break.drawImage(pacman4left. pacmany + 1.drawImage(pacman3right. pacmanx break.drawImage(pacman3down.} g2d. this).drawImage(pacman1. y < scrsize. pacmanx + 1. pacmany + 1. case 3: g2d. g2d.setStroke(new BasicStroke(2)). case 2: g2d. break.drawImage(pacman2right. this).drawImage(pacman4down. break. pacmanx + 1. pacmany + 1. for (y = 0. case 3: g2d. } } public void DrawPacManRight(Graphics2D g2d) switch (pacmananimpos) { case 1: g2d. pacmanx + 1. pacmany + 1. } } public void DrawMaze(Graphics2D g2d) { short i = 0. x += blocksize) { g2d. y. } { + 1. y. case 2: g2d. random() * (currentspeed + 1)).1).drawLine(x. x + blocksize .1). for (i = 0. 2).fillRect(x + 11. y + 11. y + blocksize . ghostdy[i] = 0.1.1. g2d. } pacmanx = 7 * blocksize. LevelInit(). ghostspeed[i] = validspeeds[random]. } LevelContinue(). int dx = 1. i++) screendata[i] = leveldata[i]. y. } public void LevelInit() { int i.if ((screendata[i] & 2) != 0) // draws top { g2d.drawLine(x.1. 2. ghostx[i] = 4 * blocksize. pacmany = 11 * blocksize. for (i = 0. } if ((screendata[i] & 16) != 0) // draws point { g2d. i < nrofblocks * nrofblocks.1. y + blocksize . y.drawLine(x + blocksize .setColor(dotcolor). } if ((screendata[i] & 8) != 0) // draws bottom { g2d. score = 0. nrofghosts = 6. } public void LevelContinue() { short i. i++) { ghosty[i] = 4 * blocksize. } } public void GameInit() { pacsleft = 3. int random. ghostdx[i] = dx. x + blocksize . random = (int)(Math. y + blocksize . currentspeed = 3. } i++. } if ((screendata[i] & 4) != 0) // draws right { g2d. dx = -dx. if (random > currentspeed) random = currentspeed.1. x + blocksize . 639 . i < nrofghosts. y). pacman4left = new ImageIcon(Board.getImage().png")).png")).getResource(". pacman2right = new ImageIcon(Board.getResource(".class./pacpix/left2. Graphics2D g2d = (Graphics2D) g. if (ingame) PlayGame(g2d).getResource(".png")).getImage(). this).png")). DrawScore(g2d)..fillRect(0.black)./pacpix/up2./pacpix/right1. pacman4up = new ImageIcon(Board./pacpix/pacman.getResource(".png")). pacman2down = new ImageIcon(Board. pacman3right = new ImageIcon(Board.png")). d. dying = false. public void GetImages() { ghost = new ImageIcon(Board. pacmandy = 0. viewdy = 0.class.class.. reqdy = 0./pacpix/up1. 5./pacpix/down2.png")). 640 .. 5.getResource(".class. pacman4right = new ImageIcon(Board.getImage()./pacpix/right2.class. g2d.png")). DoAnim().height).} pacmandx = 0. pacman3down = new ImageIcon(Board.png")).getResource(". pacman1 = new ImageIcon(Board. pacman2left = new ImageIcon(Board.getImage().class..getResource(".class..png")).getResource(". else ShowIntroScreen(g2d). DrawMaze(g2d).getResource(".getImage().getImage(). g.setColor(Color. d..paint(g).class./pacpix/down3.getResource(".getImage().getResource(".getResource(".getImage(). pacman3left = new ImageIcon(Board.getImage().getImage()./pacpix/right3.getImage()../pacpix/left1.png"))..png"))...png")).getImage().. pacman3up = new ImageIcon(Board.getImage(). 0./pacpix/down1./pacpix/left3.width..getResource(".class./pacpix/up3. viewdx = -1. reqdx = 0.class. } public void paint(Graphics g) { super. pacman2up = new ImageIcon(Board. pacman4down = new ImageIcon(Board...class.png"))./pacpix/ghost.drawImage(ii.class. g2d.class.class.getImage().getResource(". VK_ESCAPE && timer. else timer. reqdy=1.Toolkit. } } public void keyReleased(KeyEvent e) { int key = e. reqdy=0.isRunning()) timer. } else if (key == KeyEvent. } else if (key == KeyEvent. GameInit().isRunning()) { ingame=false. g. } else if (key == KeyEvent.getKeyCode(). if (key == Event.UP || key == Event.VK_LEFT) { reqdx=-1.VK_DOWN) { reqdx=0. if (ingame) { if (key == KeyEvent. reqdy=0.sync().stop().VK_UP) { reqdx=0. reqdy=0. } class TAdapter extends KeyAdapter { public void keyPressed(KeyEvent e) { int key = e.dispose().LEFT || key == Event. } else if (key == KeyEvent.getKeyCode(). } } else { if (key == 's' || key == 'S') { ingame=true.getDefaultToolkit(). } else if (key == KeyEvent. } } } 641 .VK_RIGHT) { reqdx=1.VK_PAUSE) { if (timer.start().DOWN) { reqdx=0.RIGHT || key == Event. reqdy=-1. } This code is part of the checkMaze() method. final short leveldata[] = { 19. if there are any points left for the Pacman to eat. } } The Commons. if they change the direction. These numbers make up the maze.. Next we will examine the moveGhosts() method.java file has some common constants. i++. where the ghost is situated. which makes the animation a bit slower. For example number 19 in the upper left corner means. (A ghost cannot move over walls. . (16 + 2 + 1) public void doAnim() { pacanimcount--. Number 16 stands for a point. There are four pacman images. if (ghostx[i] % blocksize == 0 && ghosty[i] % blocksize == 0) { Continue only if you have finished moving one square. }. They provide information out of which we create the corners and the points.1) || pacmananimpos == 0) pacanimdir = -pacanimdir. 26. It checks. 26. This line determines. if (pacanimcount <= 0) { pacanimcount = pacanimdelay. 26. The ghosts move one square and then decide. while (i < nrofblocks * nrofblocks && finished) { if ((screendata[i] & 16) != 0) finished = false. ) if ((screendata[pos] & 1) == 0 && ghostdx[i] != 1) { 642 .. which determines what pacman image is drawn. 18. There are 225 theoretical positions. if (pacmananimpos == (pacmananimcount . If all points are consumed. } } The doAnim() counts the pacmananimpos variable. pacmananimpos = pacmananimpos + pacanimdir. In which position/square. Otherwise the pacman would open his mouth too fast. There is also a pacanimdelay variable. pos = ghostx[i] / blocksize + nrofblocks * (int)(ghosty[i] / blocksize). we move to the next level. that the square will have top and left borders and a point.} public void actionPerformed(ActionEvent e) { repaint(). boolean finished = true. Next we are going to examine the movePacman() method. if ((ch & 16) != 0) { screendata[pos] = (short)(ch & 15). if (pacmanx > (ghostx[i] . } If there is a collision between ghosts and a pacman. If there is no obstacle on the left and the ghost is not already moving to the right.12) && pacmanx < (ghostx[i] + 12) && pacmany > (ghosty[i] . 643 . The ghost might get stuck there. we remove it from the maze and increase the score value. We do not apply this randomness inside long tunnels. count++. where there is a point. } If the pacman moves to a position.12) && pacmany < (ghosty[i] + 12) && ingame) { dying = true. deathcounter = 64. } If the pacman cannot move further it his current direction. score++. he will continue in the same direction until he is out of the tunnel. Moving of ghosts is partly random.} dx[count] = -1. the ghost will move to the left. public void drawPacMan(Graphics2D g2d) { if (viewdx == -1) drawPacManLeft(g2d). The images are used to animate pacman opening a closing his mouth. else if (viewdy == -1) drawPacManUp(g2d). pacmandy = 0. What does this code really mean? If the ghost enters a tunnel. 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. There are four images for all directions. else if (viewdx == 1) drawPacManRight(g2d). } There are four possible directions for a pacman. The reqdx and reqdy variables are determined in the TAdapter inner class. there is a standstill. the pacman dies. These variables are controlled with cursor keys. else drawPacManDown(g2d). dy[count] = 0. 1). So we draw a bottom and a left border on this particular square.JFrame. import pacman. 8 is a bottom border and 16 is a point.Board. } public static void main(String[] args) { new PacMan(). if ((screendata[i] & 1) != 0) // draws left { g2d. setLocationRelativeTo(null). We have the first bit (1) and the fourth bit (8) set.The drawMaze() method draws the maze out of the numbers in the screendata array. setTitle("Pacman"). PacMan. Number 1 is a left border. We simply go through all 225 squares int the maze. 644 . 420). x.drawLine(x. 2 is a top border. setDefaultCloseOperation(EXIT_ON_CLOSE). } Draw a left border if the first bit of a number is set. For example we have 9 in the screendata array. import javax. 4 is a right border.java package packman. setVisible(true). y + blocksize . public class PacMan extends JFrame { public PacMan() { add(new Board()). y. setSize(380. } } This is a PacMan file with a main method.swing. When the player shoots a missile. public class SpaceInvaders extends JFrame implements Commons { public SpaceInvaders() 645 . he can shoot another one only when it hits an alien or the top of the Board. The player controls a cannon. import javax. He is about to save the Earth from invasion of evil space invaders. Each alien shoots a bomb only after the previous one hits the bottom. SpaceInvaders. The player shoots with the alt key. Development In our java clone we have 24 invaders.swing.Figure: Pacman This was the Pacman game.java package spaceinvaders. Space Invaders is an arcade video game designed by Tomohiro Nishikado. These aliens heavily shell the ground. Space Invaders In this part of the Java 2D games tutorial we will create a simple Space Invaders game clone.JFrame. Aliens launch randomly their bombs. It was first released in 1978. y). this./spacepix/alien. setDefaultCloseOperation(EXIT_ON_CLOSE). BOARD_HEIGTH = 350. Commons. ALIEN_WIDTH = 12. ImageIcon ii = new ImageIcon(this. PLAYER_WIDTH = 15.java package spaceinvaders.png". setSize(BOARD_WIDTH. int y) { this. BORDER_LEFT = 5. setImage(ii.ImageIcon.{ add(new Board()). private final String shot = ". GO_DOWN = 15.y = y.getResource(shot)). } } This is the main class. bomb = new Bomb(x.java package spaceinvaders. import javax. Alien. BOARD_HEIGTH). GROUND = 290.getClass(). They are self-explanatory.getImage()). public class Alien extends Sprite { private Bomb bomb. The Commons. setVisible(true). ALIEN_HEIGHT = 12. NUMBER_OF_ALIENS_TO_DESTROY = 24.java file has some common constants. CHANCE = 5. PLAYER_HEIGHT = 10. DELAY = 17.swing. setLocationRelativeTo(null). 646 .x = x. 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. setTitle("Space Invaders").. BORDER_RIGHT = 30. setResizable(false). public Alien(int x. } public static void main(String[] args) { new SpaceInvaders(). BOMB_HEIGHT = 5. getResource(bomb)).getClass().y = y. private boolean destroyed.KeyEvent.png". } public Bomb getBomb() { return bomb. public void act(int direction) { this. } public void setDestroyed(boolean destroyed) { this. } The getBomb() method is called.java package spaceinvaders./spacepix/bomb.ImageIcon. public Bomb(int x.} public void act(int direction) { this. } public boolean isDestroyed() { return destroyed.event. public Bomb getBomb() { return bomb.x += direction.x = x. this.. when the alien is about to drop a bomb. } public class Bomb extends Sprite { private final String bomb = ". } } } This is the Alien sprite. public class Player extends Sprite implements Commons{ 647 .awt.swing. import java.destroyed = destroyed. int y) { setDestroyed(true). } The act() method is called from the Board class. ImageIcon ii = new ImageIcon(this. Player. this. import javax.getImage()). setImage(ii. Each alien has an inner Bomb class.x += direction. It is used to position an alien in horizontal direction. } public void keyPressed(KeyEvent e) { int key = e..getKeyCode().getKeyCode(). setY(START_Y). setX(START_X). private final int START_X = 270. } } public void keyReleased(KeyEvent e) { int key = e. if (key == KeyEvent. private int width.VK_RIGHT) { dx = 0. private final String player = ".getClass().private final int START_Y = 280. private final int START_X = 270. } public void act() { x += dx.getWidth(null). if (x <= 2) x = 2. public Player() { ImageIcon ii = new ImageIcon(this.getResource(player)). } if (key == KeyEvent. setImage(ii.getImage().VK_LEFT) { dx = 0. private final int START_Y = 280./spacepix/player. width = ii. We control the cannon with the cursor keys.png". } if (key == KeyEvent.VK_LEFT) { dx = -2. if (x >= BOARD_WIDTH .2*width) x = BOARD_WIDTH .getImage()).2*width.VK_RIGHT) { dx = 2. } } } This is the Player sprite. 648 . if (key == KeyEvent. java package spaceinvaders./spacepix/shot. Sprite. } } This is the Shot sprite. the dx variable is set to zero.. } If we release the left or the right cursor. The player sprite stops moving.VK_LEFT) { dx = -2.getClass().getResource(shot)).VK_LEFT) { dx = 0. setX(x + H_SPACE). } if (key == KeyEvent.png". Shot. } If we press the left cursor key.getImage()). setImage(ii. The H_SPACE and the V_SPACE constants are used to position the missile appropriately.V_SPACE). the dx variable is set to -2.java package spaceinvaders. private final int V_SPACE = 1.Image.awt.ImageIcon.These are the initial coordinates of the player sprite. Next time the act() method is called. public class Shot extends Sprite { private String shot = ". the player moves to the left. import javax. int y) { ImageIcon ii = new ImageIcon(this. public class Sprite { 649 . setY(y . The shot is triggered with the alt key. if (key == KeyEvent. if (key == KeyEvent. public Shot() { } public Shot(int x.swing. import java. private final int H_SPACE = 6.VK_RIGHT) { dx = 0. } public void setImage(Image image) { this.dying = dying. } public boolean isVisible() { return visible. } protected void setVisible(boolean visible) { this. It has some common functionality.private boolean visible.y = y. } public void setY(int y) { this.visible = visible. } public Image getImage() { return image. } public void die() { visible = false. public Sprite() { visible = true. } public boolean isDying() { return this.image = image. protected int y. } public int getX() { return x. } public void setX(int x) { this. Other sprites inherit from it. protected boolean dying. } } This is the basic Sprite class.java package spaceinvaders. } public int getY() { return y. 650 . protected int x. } public void setDying(boolean dying) { this.dying. Board. protected int dx.x = x. private Image image. java.Dimension.awt. BOARD_HEIGTH). } public void addNotify() { super. final String alienpix = ".awt. java.util.ImageIcon. java.Color.Font.KeyAdapter. java.. direction = -1.Graphics. gameInit(). gameInit(). } public void gameInit() { aliens = new ArrayList().import import import import import import import import java.awt./spacepix/explosion. final String expl = ". Commons { private private private private private private private private private private private private Dimension d.util.awt.util. import java.JPanel.awt. int int int int alienX = 150. Shot shot. setBackground(Color. private Thread animator.awt. import java.swing. ArrayList aliens.png".Random.Toolkit. d = new Dimension(BOARD_WIDTH.Iterator. String message = "Game Over". i < 4. import javax. java.KeyEvent. import javax. i++) { 651 .awt.ArrayList. public class Board extends JPanel implements Runnable./spacepix/alien. Player player.event. deaths = 0.black). boolean ingame = true.getResource(alienpix)). ImageIcon ii = new ImageIcon(this. public Board() { addKeyListener(new TAdapter()).swing.awt.FontMetrics. java. java.getClass(). setDoubleBuffered(true). for (int i=0.event. import java..addNotify(). alienY = 5. setFocusable(true).png". drawImage(alien. this). Alien.} for (int j=0. this).getImage(). j < 6. } player = new Player(). while (i3.hasNext()) { Alien a = (Alien) i3.next(). } public void drawBombing(Graphics g) { Iterator i3 = aliens.iterator().drawImage(player.isVisible()) g.getY(). j++) { Alien alien = new Alien(alienX + 18*j.getImage(). shot.Bomb b = a.iterator(). player. } if (player.isDying()) { player.die(). 652 .isVisible()) { g.getImage()).getX().getImage().isVisible()) { g. shot = new Shot(). } } public void drawPlayer(Graphics g) { if (player.getX().getX().getY(). alien.drawImage(shot. player. shot.hasNext()) { Alien alien = (Alien) it. alien.getBomb(). aliens. if (animator == null || !ingame) { animator = new Thread(this).add(alien). alien.setImage(ii.getY(). while (it.next(). animator.start().die(). if (alien.isDying()) { alien. } } public void drawAliens(Graphics g) { Iterator it = aliens. ingame = false. } this). } if (alien. } } public void drawShot(Graphics g) { if (shot. alienY + 18*i). g. drawAliens(g). BOARD_WIDTH-100. GROUND). BOARD_WIDTH/2 . if (ingame) { g. g. BOARD_WIDTH. g.sync(). this). BOARD_WIDTH. GROUND.white).height). b.getDefaultToolkit(). 32.setColor(Color.getFontMetrics(small). g.white). BOARD_WIDTH-100.green).setColor(new Color(0. 14). 50).BOLD. d. BOARD_WIDTH/2). drawPlayer(g).metr. BOARD_WIDTH/2 . public void gameOver() { Graphics g = this. // shot 653 .isDestroyed()) { g.setFont(small).} } if (!b. drawShot(g). g.30. b.drawLine(0. g.fillRect(50. message = "Game won!". 50). d. BOARD_HEIGTH). (BOARD_WIDTH .getGraphics().act().width. { } public void animationCycle() if (deaths == NUMBER_OF_ALIENS_TO_DESTROY) { ingame = false.setColor(Color.drawRect(50. 0.setColor(Color. FontMetrics metr = this. g.black).paint(g).getY(). } } Toolkit.fillRect(0. g. Font small = new Font("Helvetica".dispose(). } public void paint(Graphics g) { super.getX(). g. Font.setColor(Color. 48)). g.drawString(message. } // player player. g.fillRect(0.setColor(Color.getImage().30. 0.stringWidth(message))/2.black). g. g.drawImage(b. drawBombing(g). while (it.die().isVisible()) { Iterator it = aliens. a2.next().hasNext()) { Alien a1 = (Alien) it1. y -= 4.next().hasNext()) { Alien alien = (Alien) it.setY(a2.iterator(). shot. Iterator i2 = aliens.setY(a.getX().iterator(). a. } } } int y = shot.next().setDying(true).getX().hasNext()) { Alien a = (Alien)i2. alien.BORDER_RIGHT && direction != -1) { direction = -1. int alienX = alien. if (alien. while (i1.getY().setY(y). while (it1. else shot.iterator(). if (x >= BOARD_WIDTH .if (shot. } } if (x <= BORDER_LEFT && direction != 1) { direction = 1.die().getY().getY() + GO_DOWN).getY() + GO_DOWN).isVisible() && shot. alien.setImage(ii. } // aliens Iterator it1 = aliens. deaths++. if (y < 0) shot.hasNext()) { Alien a2 = (Alien) i1.getY().next().getResource(expl)).isVisible()) { if (shotX >= (alienX) && shotX <= (alienX + ALIEN_WIDTH) && shotY >= (alienY) && shotY <= (alienY+ALIEN_HEIGHT) ) { ImageIcon ii = new ImageIcon(getClass(). int alienY = alien. Iterator i1 = aliens.getX(). while (i2. int shotX = shot.iterator(). int shotY = shot. int x = a1. } } } 654 .getImage()). hasNext()) { Alien alien = (Alien) it. = player. while (it.Iterator it = aliens.getY() + 1). if (y > GROUND .setDying(true).iterator(). if (shot == CHANCE && a. } int int int int bombX = bombY = playerX playerY b.isDestroyed()) { if ( bombX >= (playerX) && bombX <= (playerX+PLAYER_WIDTH) && bombY >= (playerY) && bombY <= (playerY+PLAYER_HEIGHT) ) { ImageIcon ii = new ImageIcon(this.isVisible()) { int y = alien. while (i3.getX(). b. if (alien.ALIEN_HEIGHT) { ingame = false.setX(a. b.getX()).setImage(ii.setDestroyed(false).getBomb(). b.getY() >= GROUND .getY().getY()).getClass().iterator(). if (b.hasNext()) { int shot = generator.nextInt(15). alien. if (player.getX().isVisible() && !b.Bomb b = a..getImage()).getY(). } } } } 655 . player. Alien a = (Alien) i3.BOMB_HEIGHT) { b. message = "Invasion!".setDestroyed(true).setDestroyed(true).getY().isDestroyed()) { b. } } } // bombs Iterator i3 = aliens. player.setY(b. = player.next().isDestroyed()) { b.isVisible() && b.getResource(expl)).setY(a. } } if (!b. Random generator = new Random(). b.next().act(direction). Alien. out.isVisible()) shot = new Shot(x. aliens.println("interrupted"). animationCycle(). } } } } } The main logic of the game is located in the Board class.beforeTime. if (ingame) { if (e.getX(). } beforeTime = System.keyPressed(e). } } 656 .getY().isAltDown()) { if (!shot. i++) { for (int j=0. for (int i=0. alien. y). sleep = DELAY . while (ingame) { repaint(). } private class TAdapter extends KeyAdapter { public void keyReleased(KeyEvent e) { player. int x = player.sleep(sleep).currentTimeMillis() .public void run() { long beforeTime. sleep. beforeTime = System.currentTimeMillis(). } public void keyPressed(KeyEvent e) { player.keyReleased(e). } gameOver().getImage()). if (sleep < 0) sleep = 2.currentTimeMillis(). int y = player. alienY + 18*i).timeDiff. } catch (InterruptedException e) { System. try { Thread. j++) { Alien alien = new Alien(alienX + 18*j. timeDiff. timeDiff = System. j < 6. i < 4.setImage(ii.add(alien). shot = new Shot(). while (i3.getResource(expl)).next(). We put 6px space among the aliens.drawLine(0. GROUND).drawImage(b. The alien image size is 12x12px. we draw the ground. Alien.getBomb(). drawShot(g). } } } The drawBombing() method draws bombs launched by the aliens. drawBombing(g).getY(). alien.isVisible() && shot.getImage(). b. Next we will examine the animationCycle() method. the aliens. } Inside the paint() method. shot.getImage()).player = new Player().getX(). message = "Game won!".setDying(true). the player. this). public void drawBombing(Graphics g) { Iterator i3 = aliens.Bomb b = a.setImage(ii. deaths++.isVisible()) { if (shotX >= (alienX) && shotX <= (alienX + ALIEN_WIDTH) && shotY >= (alienY) && shotY <= (alienY+ALIEN_HEIGHT) ) { ImageIcon ii = new ImageIcon(getClass(). GROUND. b. if (deaths == NUMBER_OF_ALIENS_TO_DESTROY) { ingame = false.iterator(). the shot and the bombs.isDestroyed()) { g. } } 657 . alien. if (ingame) { g. } If we destroy all aliens. if (!b. we win the game. drawAliens(g). We also create the player and the shot objects.die().hasNext()) { Alien a = (Alien) i3. In the gameInit() method we set up 24 aliens. BOARD_WIDTH. (24 in this game) if (alien. drawPlayer(g). getY() + GO_DOWN).setDestroyed(true). Iterator it = aliens. the dying flag is set.nextInt(15). b.hasNext()) { Alien a2 = (Alien) i1. if (shot == CHANCE && a. The alien must not be destroyed.isDestroyed()) { b.next(). If these two conditions are fulfilled. The deaths variable increases and the shot sprite is destroyed.isVisible()) { int y = alien. while (it.setY(a. int shot = generator. if (y > GROUND . message = "Invasion!".act(direction). } alien. if (b. if (alien. If it hits the bottom.setY(b.isDestroyed()) { b.getBomb().If the shot triggered by the player collides with an alien. while (i1.getY(). Alien a = (Alien) i3. } } Aliens move.setY(a2.iterator(). the bombing is left to the chance. Iterator i1 = aliens. 658 .BORDER_RIGHT && direction != -1) { direction = -1.setX(a. Eg.iterator(). The bomb's destroyed flag must be set. } This is the code that determines whether the alien will drop a bomb.getY() + 1).ALIEN_HEIGHT) { ingame = false. it goes one px to the ground.getX()).BOMB_HEIGHT) { b. We use it to display an explosion. Alien. } } If the aliens reach the right end of the Board. b.isVisible() && b. if (x >= BOARD_WIDTH . it is alien's first bomb dropping or previous dropped bomb already hit the ground.Bomb b = a. the invasion begins. the destroyed flag is set. If they reach the bottom.next(). the alien ship is destroyed. In other words. The alien is now ready to drop another bomb. they move down and change their direction to the left. it must be visible.getY()).setDestroyed(false). a2. if (!b. } } If the bomb is not destroyed. More precisely.hasNext()) { Alien alien = (Alien) it.getY() >= GROUND .next(). Graphics.keyReleased(e). we will create a Minesweeper game clone. import java. We set a mark on a cell by right clicking on it.awt. Figure: Space Invaders This was the Space Invaders game.awt. Minesweeper In this part of the Java 2D games tutorial. Board. This way we indicate. Minesweeper Minesweeper is a popular board game shipped with many operating systems by default. that we believe. If the player clicks on the cell which contains a mine.Image.public void keyReleased(KeyEvent e) { player. 659 . the mine detonates and the game is over. } The actual processing of this particular KeyEvent is delegated to the player sprite. there is a mine. The number indicates how many mines are adjacent to this particular cell. The following Minesweeper clone is a modified and simplified version of an Java applet which I found at javaside.java package mines. import java. Further a cell can contain a number or it can be blank. The goal of the game is to sweep all mines from a mine field.com. EMPTY_CELL = 0. private final int CELL_SIZE = 15. int mines = 40. newGame(). i < NUM_IMAGES.swing.event. 660 . addMouseListener(new MinesAdapter()). public Board(JLabel statusbar) { this. DRAW_WRONG_MARK = 12. int mines_left. MARKED_MINE_CELL = COVERED_MINE_CELL + MARK_FOR_CELL. int all_cells. javax.Random. import java. Image[] img. MARK_FOR_CELL = 10.getImage(). JLabel statusbar. img = new Image[NUM_IMAGES].BorderFactory.import java. int[] field. import java.statusbar = statusbar.png"))).MouseAdapter. boolean inGame.JLabel.swing. int rows = 16. for (int i = 0.util.getResource((i) + ".swing. } public void newGame() { Random random. javax. import import import import javax.awt. int current_col.JPanel. COVERED_MINE_CELL = MINE_CELL + COVER_FOR_CELL. int cols = 16.awt. DRAW_MARK = 11.MouseEvent. } setDoubleBuffered(true).ImageIcon. DRAW_COVER = 10. public class Board extends JPanel { private final int NUM_IMAGES = 13.event. i++) { img[i] = (new ImageIcon(this. javax. MINE_CELL = 9.swing. 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.getClass(). DRAW_MINE = 9. statusbar.cols. cell = position + cols. while (i < mines) { position = (int) (all_cells * random. random = new Random(). field[position] = COVERED_MINE_CELL. inGame = true. if (cell < all_cells) if (field[cell] != COVERED_MINE_CELL) field[cell] += 1.setText(Integer. field = new int[all_cells]. int cell = 0. if (current_col > 0) { cell = position .1)) { cell = position .cols + 1. if (cell >= 0) if (field[cell] != COVERED_MINE_CELL) field[cell] += 1. i = 0. 661 . if (cell >= 0) if (field[cell] != COVERED_MINE_CELL) field[cell] += 1. } cell = position .nextDouble()). if (cell < all_cells) if (field[cell] != COVERED_MINE_CELL) field[cell] += 1.cols. cell = position . i < all_cells.1. if ((position < all_cells) && (field[position] != COVERED_MINE_CELL)) { current_col = position % cols.toString(mines_left)). all_cells = rows * cols. i++. mines_left = mines.1. i++) field[i] = COVER_FOR_CELL.1 . if (cell >= 0) if (field[cell] != COVERED_MINE_CELL) field[cell] += 1.int i = 0. if (current_col < (cols . if (cell >= 0) if (field[cell] != COVERED_MINE_CELL) field[cell] += 1. for (i = 0. cell = position + cols . int position = 0. } cell = j + cols .1. if (cell >= 0) if (field[cell] > MINE_CELL) { field[cell] -= COVER_FOR_CELL. public void find_empty_cells(int j) { int current_col = j % cols.1.cols .cols. } } cell = j . if (field[cell] == EMPTY_CELL) find_empty_cells(cell).1. } cell = j .cell = position + cols if (cell < all_cells) if (field[cell] != field[cell] += cell = position + 1. if (cell < all_cells) if (field[cell] > MINE_CELL) { field[cell] -= COVER_FOR_CELL. if (field[cell] == EMPTY_CELL) find_empty_cells(cell). COVERED_MINE_CELL) 1. if (field[cell] == EMPTY_CELL) find_empty_cells(cell). if (cell >= 0) if (field[cell] > MINE_CELL) { field[cell] -= COVER_FOR_CELL. if (cell >= 0) if (field[cell] > MINE_CELL) { field[cell] -= COVER_FOR_CELL. } 662 . if (field[cell] == EMPTY_CELL) find_empty_cells(cell). int cell. } cell = j + cols. if (field[cell] == EMPTY_CELL) find_empty_cells(cell). if (cell < all_cells) if (field[cell] != field[cell] += } } } } + 1. if (cell < all_cells) if (field[cell] > MINE_CELL) { field[cell] -= COVER_FOR_CELL. if (current_col > 0) { cell = j . COVERED_MINE_CELL) 1. } } } public void paint(Graphics g) { int cell = 0. j < cols. if (field[cell] == EMPTY_CELL) find_empty_cells(cell). if (inGame && cell == MINE_CELL) inGame = false.1)) { cell = j . uncover++. } else if (cell > MINE_CELL) { cell = DRAW_COVER. else if (cell > MINE_CELL) { cell = DRAW_COVER. } cell = j + 1. if (cell >= 0) if (field[cell] > MINE_CELL) { field[cell] -= COVER_FOR_CELL. if (cell < all_cells) if (field[cell] > MINE_CELL) { field[cell] -= COVER_FOR_CELL. int uncover = 0. } else if (cell == MARKED_MINE_CELL) { cell = DRAW_MARK. j++) { cell = field[(i * cols) + j]. if (field[cell] == EMPTY_CELL) find_empty_cells(cell). i++) { for (int j = 0. } cell = j + cols + 1. } else if (cell > COVERED_MINE_CELL) { cell = DRAW_WRONG_MARK. } } else { if (cell > COVERED_MINE_CELL) cell = DRAW_MARK. if (cell < all_cells) if (field[cell] > MINE_CELL) { field[cell] -= COVER_FOR_CELL. i < rows. } 663 .if (current_col < (cols .cols + 1. if (field[cell] == EMPTY_CELL) find_empty_cells(cell). if (!inGame) { if (cell == COVERED_MINE_CELL) { cell = DRAW_MINE. for (int i = 0. int y = e.toString(mines_left)). int cRow = y / CELL_SIZE. class MinesAdapter extends MouseAdapter { public void mousePressed(MouseEvent e) { int x = e. statusbar.setText("Game lost"). } if ((x < cols * CELL_SIZE) && (y < rows * CELL_SIZE)) { if (e. statusbar.setText("Game won"). statusbar.setText(Integer. if (mines_left > 0) { field[(cRow * cols) + cCol] += mines_left--.toString(mines_left)). } else { field[(cRow * cols) + cCol] -= MARK_FOR_CELL. } else if (!inGame) statusbar. (i * CELL_SIZE). int cCol = x / CELL_SIZE. mines_left++. } } } if (uncover == 0 && inGame) { inGame = false. } } } else { 664 .setText("No marks left"). boolean rep = false. this).getY(). (j * CELL_SIZE).} g. if (!inGame) { newGame(). } else statusbar.drawImage(img[cell].setText(Integer. if (field[(cRow * cols) + cCol] <= COVERED_MINE_CELL) { MARK_FOR_CELL. repaint().getButton() == MouseEvent.BUTTON3) { if (field[(cRow * cols) + cCol] > MINE_CELL) { rep = true.getX(). if (field[(cRow * cols) + cCol] == MINE_CELL) inGame = false. private final int MARK_FOR_CELL = 10. For example. Using constants improves readability of the code. private int mines = 40. 9 for the mine and 10 for the cell cover etc.8.. Each cell in the field has a specific number. The minefield in our game has 40 hidden mines. } } if (rep) repaint(). meaning it is adjacent to two mines. private final int COVER_FOR_CELL = 10. So there are 256 cells together in the minefield. We need images for an empty cell. There are 13 images used in this game. A cell can be surrounded by maximum of 8 mines. The size of each of the images is 15x15 px. The numbers are added. private int[] field. The field is an array of numbers. a marked cell and finally for a wrongly marked cell. } } } } First we will define the constants used in our game.if (field[(cRow * cols) + cCol] > COVERED_MINE_CELL) { return. private final int NUM_IMAGES = 13.. 665 . For example 0 denotes an empty cell. if (field[(cRow * cols) + cCol] == EMPTY_CELL) find_empty_cells((cRow * cols) + cCol). private int rows = 16. There are 16 rows and 16 columns in this field. private final int EMPTY_CELL = 0. . E. a covered cell. has number two. so we need numbers 1. Number 10 is used for a cell cover as well as for a mark. a mine. a mine cell has number 9. a covered mine has number 19. private final int CELL_SIZE = 15.g. private int cols = 16. A mine field is an array of numbers. A cell with number 2.. } if ((field[(cRow * cols) + cCol] > MINE_CELL) && (field[(cRow * cols) + cCol] < MARKED_MINE_CELL)) { field[(cRow * cols) + cCol] -= COVER_FOR_CELL. rep = true. we find empty cells. } Here we load our images into the image array. } 666 .getClass(). If the player clicks on a mine cell. These lines set up the mine field. 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. cell = position . for (i = 0. if (cell >= 0) if (field[cell] != COVERED_MINE_CELL) field[cell] += 1. In the while cycle we randomly position all mines in the field. . if ((position < all_cells) && (field[position] != COVERED_MINE_CELL)) { current_col = position % cols. i++) { img[i] = (new ImageIcon(this.) We raise the number for adjacent cells for each of the randomly placed mine.. field = new int[all_cells]. we add 1 to the top neighbor of the cell in question. all_cells = rows * cols..1. he uncovers a number indicating how many mines the cell is adjacent to.png.getImage().png. In our example. In the find_empty_cells() method.nextDouble()). i < all_cells.png . if (field[cell] == EMPTY_CELL) find_empty_cells(cell).getResource((i) + ". If he clicks on a cell adjacent to a mine. We use a recursive algorithm to find empty cells. while (i < mines) { position = (int) (all_cells * random. i < NUM_IMAGES. (This does not apply to the border cells. the game is over. 12. cell = j . if (cell >= 0) if (field[cell] > MINE_CELL) { field[cell] -= COVER_FOR_CELL.cols.for (int i = 0.. Each of the cells can be surrounded up to 8 cells. The images are named 0. i++) field[i] = COVER_FOR_CELL.png"))). 1. field[position] = COVERED_MINE_CELL.. The newGame() initiates the Minesweeper game. Every cell is covered by default. repeat the whole process by recursively calling the find_empty_cells() method. if (!inGame) { if (cell == COVERED_MINE_CELL) { cell = DRAW_MINE. if we click on the covered & marked cell. } } If the game is over and we lost. field[(cRow * cols) + cCol] -= COVER_FOR_CELL. It must by first uncovered by another right click and only then it is possible to left click on it. This code line draws every cell. (j * CELL_SIZE). This leads to drawing a covered cell with a mark in the paint() method. In case we left clicked on a mine. this). mines_left--. In the mousePressed() method we react to mouse clicks.In this code. if (field[(cRow * cols) + cCol] > COVERED_MINE_CELL) { return. we call the find_empty_cells() method. } else if (cell == MARKED_MINE_CELL) { cell = DRAW_MARK. Left click removes a cover from the cell. if (field[(cRow * cols) + cCol] == EMPTY_CELL) find_empty_cells((cRow * cols) + cCol). We react to left and right mouse clicks. } else if (cell > COVERED_MINE_CELL) { cell = DRAW_WRONG_MARK. we check the cell that is left to an empty cell in question. uncover it. which recursively finds all adjacent empty cells. we show all uncovered mines if any and show all wrongly marked cells if any. we add MARK_FOR_CELL to the number representing the cell. If it is empty. The paint() method turns numbers into images. The Minesweeper game is controlled solely by mouse. field[(cRow * cols) + cCol] += MARK_FOR_CELL.drawImage(img[cell]. If it is not empty. g. 667 . if (field[(cRow * cols) + cCol] == MINE_CELL) inGame = false. (i * CELL_SIZE). If we right click on an unmarked cell. } Nothing happens. the game is over. } else if (cell > MINE_CELL) { cell = DRAW_COVER. If we left clicked on an empty cell. setTitle("Minesweeper"). HEIGHT).BorderLayout.JLabel. } } This is the main class. import javax. setSize(WIDTH. public class Mines extends JFrame { private final int WIDTH = 250. add(new Board(statusbar)). import javax. BorderLayout. import java. setVisible(true). Figure: Minesweeper 668 .awt. public Mines() { setDefaultCloseOperation(JFrame. } public static void main(String[] args) { new Mines(). statusbar = new JLabel(""). setResizable(false). private JLabel statusbar.java package mines. setLocationRelativeTo(null).swing.swing. private final int HEIGHT = 290.JFrame.SOUTH).Mines. add(statusbar.EXIT_ON_CLOSE). 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. LEFT_COLLISION = 1.swing. new ArrayList().JPanel. Development We control the sokoban object with cursor keys. int h = 0. Sokoban Sokoban is another classic computer game. import import import import import import java. java. The objective is to place all boxes in designated locations. RIGHT_COLLISION = 2.#\n" 669 . int w = 0. ArrayList walls = ArrayList baggs = ArrayList areas = Player soko. When all bags are placed on the destination areas.awt. new ArrayList(). Sokoban means a warehouse keeper in Japanese. the game is finished.awt.event.#\n" + "## $ $ .Color.Graphics.event. java.KeyEvent.. We can also press the R key to restart the level.java package sokoban. TOP_COLLISION = 3.KeyAdapter.ArrayList. SPACE = 20. we created a Java clone of the Minesweeper game. Board. java. BOTTOM_COLLISION = 4..awt. boolean completed = false. private String level = " ######\n" + " ## #\n" + " ##$ #\n" + " #### $##\n" + " ## $ $ #\n" + "#### # ## # ######\n" + "## # ## ##### . java. We draw "Completed" string in the left upper corner of the window.In this part of the Java 2D games tutorial. we will create a Java Sokoban game clone.awt. Sokoban In this part of the Java 2D games tutorial. new ArrayList().#\n" + "###### ### #@## .util. The player pushes boxes around a maze. javax.. It was created in 1980 by Hiroyuki Imabayashi. } else if (item == ' ') { x += SPACE.add(a). } public final void initWorld() { int x = OFFSET.w.add(b). initWorld(). i < level. for (int i = 0. Baggage b. y). i++) { char item = level.w = x. if (item == '\n') { y += SPACE. areas. x += SPACE. setFocusable(true).w < x) { this. } else if (item == '@') { soko = new Player(x.') { a = new Area(x. } else if (item == '$') { b = new Baggage(x. } } } h = y. } x = OFFSET.charAt(i).h. baggs. } else if (item == '#') { wall = new Wall(x.+ " + " ## #########\n" ########\n". if (this. x += SPACE. 670 . } else if (item == '.length(). x += SPACE. Area a. } public int getBoardHeight() { return this. Wall wall.add(wall). y). y). } public int getBoardWidth() { return this. walls. x += SPACE. public Board() { addKeyListener(new TAdapter()). int y = OFFSET. y). getKeyCode(). g.size(). 170)). ArrayList world = new ArrayList(). 671 . if (key == KeyEvent.drawImage(item.get(i). } this).drawString("Completed". item. 0)).getImage().getHeight()). 240. this.getImage().drawImage(item. this).x(). buildWorld(g). this. item. } int key = e.y() + 2.setColor(new Color(250.addAll(baggs). if ((item instanceof Player) || (item instanceof Baggage)) { g. 0. for (int i = 0. LEFT_COLLISION)) { return. world. 0.getWidth().y().fillRect(0. } if (checkBagCollision(LEFT_COLLISION)) { return. world. } } } @Override public void paint(Graphics g) { super.public void buildWorld(Graphics g) { g.addAll(areas). i < world. } class TAdapter extends KeyAdapter { @Override public void keyPressed(KeyEvent e) { if (completed) { return. g. } if (completed) { g. world.x() + 2. 25.addAll(walls).setColor(new Color(0. } else { g. i++) { Actor item = (Actor) world.VK_LEFT) { if (checkWallCollision(soko.add(soko). item. world. item. 20).paint(g). } else if (key == KeyEvent. } } return false. } if (checkBagCollision(RIGHT_COLLISION)) { return. } repaint(). } else if (key == KeyEvent. } } private boolean checkWallCollision(Actor actor. SPACE). 0). i < walls.VK_R) { restartLevel(). TOP_COLLISION)) { return. } if (checkBagCollision(TOP_COLLISION)) { return. } soko.VK_RIGHT) { if (checkWallCollision(soko. 0). -SPACE). i++) { Wall wall = (Wall) walls.move(0. if (actor.size().get(i).soko. 672 .VK_DOWN) { if (checkWallCollision(soko. } else if (key == KeyEvent. BOTTOM_COLLISION)) { return. RIGHT_COLLISION)) { return. } soko.move(SPACE.VK_UP) { if (checkWallCollision(soko. } else if (key == KeyEvent.isLeftCollision(wall)) { return true.move(-SPACE.move(0. } if (checkBagCollision(BOTTOM_COLLISION)) { return. } soko. int type) { if (type == LEFT_COLLISION) { for (int i = 0. size(). if (soko.size(). i < baggs.isTopCollision(wall)) { return true.get(i). j++) { Baggage item = (Baggage) baggs.isLeftCollision(item)) { return true. i++) { Wall wall = (Wall) walls.isBottomCollision(wall)) { return true. private boolean checkBagCollision(int type) { if (type == LEFT_COLLISION) { for (int i = 0. i < walls. isCompleted(). } } return false.size(). if (actor. } } return false.get(i).get(i). LEFT_COLLISION)) { return true. i++) { Wall wall = (Wall) walls. if (actor. if (!bag. 0).size().size(). } } if (checkWallCollision(bag.get(i).isRightCollision(wall)) { return true.get(j). i < walls. if (actor. i++) { Wall wall = (Wall) walls.move(-SPACE. } else if (type == BOTTOM_COLLISION) { for (int i = 0. } else if (type == TOP_COLLISION) { for (int i = 0. i < walls. } } return false. } } return false.} else if (type == RIGHT_COLLISION) { for (int i = 0. i++) { Baggage bag = (Baggage) baggs.equals(item)) { if (bag. j < baggs. } else if (type == RIGHT_COLLISION) { 673 . } } return false. } } bag.isLeftCollision(bag)) { for (int j=0. if (!bag. } } return false. TOP_COLLISION)) { return true. isCompleted().for (int i = 0.get(j). } } bag. j++) { Baggage item = (Baggage) baggs.equals(item)) { if (bag. -SPACE). isCompleted(). } else if (type == BOTTOM_COLLISION) { for (int i = 0.size(). 0). i++) { Baggage bag = (Baggage) baggs. } } bag. if (soko.get(i).isBottomCollision(bag)) { for (int j = 0. i < baggs. RIGHT_COLLISION)) { return true. } } if (checkWallCollision(bag.size(). if (soko. j++) { Baggage item = (Baggage) baggs. i < baggs.isTopCollision(bag)) { for (int j = 0.isTopCollision(item)) { return true. j < baggs.get(i).size(). if (soko.isBottomCollision(item)) { 674 . j++) { Baggage item = (Baggage) baggs. i < baggs.get(j). } } return false.get(j).isRightCollision(bag)) { for (int j=0. if (!bag.move(SPACE. } else if (type == TOP_COLLISION) { for (int i = 0.isRightCollision(item)) { return true. if (!bag. j < baggs. i++) { Baggage bag = (Baggage) baggs. i++) { Baggage bag = (Baggage) baggs.equals(item)) { if (bag. } } if (checkWallCollision(bag. j < baggs.size().size().get(i).size().move(0.equals(item)) { if (bag. clear(). SPACE = 20. i++) { Baggage bag = (Baggage) baggs. } } public void restartLevel() { areas. } public void isCompleted() { int num = baggs. int compl = 0.x() && bag. LEFT_COLLISION = 1.} return true. private private private private private private final final final final final final int int int int int int OFFSET = 30. The game has one level.x() == area. isCompleted(). initWorld(). i < num. RIGHT_COLLISION = 2. SPACE). if (bag. TOP_COLLISION = 3.clear(). walls.size().move(0.get(i). } bag. BOTTOM_COLLISION)) { return true. j < num. for (int j = 0.clear(). baggs. BOTTOM_COLLISION = 4.get(j). } } } if (compl == num) { completed = true. repaint(). 675 .y()) { compl += 1. The code is than easier to understand. j++) { Area area = (Area) areas. It only provides the very basic functionality. for (int i = 0. } } if (checkWallCollision(bag. if (completed) { completed = false.y() == area. } } } return false. } } } The game is simplified. The dollar ($) represents the box to move. private String level = " ######\n" + " ## #\n" + " ##$ #\n" + " #### $##\n" + " ## $ $ #\n" + "#### # ## # ######\n" + "## # ## ##### . The OFFSET is the distance between the borders of the window and the game world. Except for the space. The initWorld() method initiates the game world. int y = OFFSET. y). And finally the new line character (\n) starts a new row of the world. The hash (#) stands for a wall..addAll(areas).. 676 . ArrayList world = new ArrayList().#\n" + "## $ $ .addAll(walls).. x += SPACE. The x variable is increased accordingly.add(b). The object is appended to the baggs list. } else if (item == '$') { b = new Baggage(x. In case of the dollar character. This is the level of the game. The at character (@) is the sokoban. private ArrayList baggs = new ArrayList(). The buildWorld() method draws the game world on the window. world. which will hold all the walls.. baggs.. public final void initWorld() { int x = OFFSET.The wall image size is 20x20px. we create a Baggage object. The walls. The dot (. There are four types of collisions.. This reflects the SPACE constant.. private ArrayList walls = new ArrayList(). there are five characters.#\n" + "###### ### #@## .) character represents the place where we must move the box. baggs and areas are special containers. world.#\n" + " ## #########\n" + " ########\n". It goes through the level string and fills the above mentioned lists. public void buildWorld(Graphics g) { . private ArrayList areas = new ArrayList(). world. Each one is represented by a numerical constant. baggs and areas of the game.addAll(baggs). . item. } else if (key == KeyEvent.move(-SPACE.x() + 2. we check if the sokoban collides with a wall or with a baggage. 25. if (key == KeyEvent.drawString("Completed".y(). We control the sokoban object with the cursor keys. i++) { Wall wall = (Wall) walls. soko.size(). We add 2px to their coordinates to center them.getImage(). i < walls..drawImage(item.size(). } } 677 . this). if (completed) { g.VK_LEFT) { if (checkWallCollision(soko. g.. i++) { Actor item = (Actor) world. } . If it does not. if we press the R key.drawImage(item. We iterate through the world container and draw the objects. for (int i = 0.add(soko). we move the sokoban to the left. this).world.getImage(). } if (checkBagCollision(LEFT_COLLISION)) { return. } We restart the level. if (type == LEFT_COLLISION) { for (int i = 0. if ((item instanceof Player) || (item instanceof Baggage)) { g. if (actor.y() + 2. item. } If the level is completed. 20). } else { g. 0.VK_R) { restartLevel(). LEFT_COLLISION)) { return. 0)). } . item. 0).. we check what keys were pressed. Inside the keyPressed() method.get(i)..setColor(new Color(0. If we press the left cursor key.x(). The player and the baggage images are a bit smaller.get(i).isLeftCollision(wall)) { return true. we draw "Completed" in the upper left corner of the window. i < world. We create a world list which includes all objects of the game. item. when the completed variable equals the number of bags in the game. public class Actor { 678 .. The baggage can be moved only if it collides with a sokoban and does not collide with another baggage or a wall. private boolean checkBagCollision(int type) { } The checkBagCollision() is a bit more involved. it is time to check. The above lines check for the left collision.clear(). for (int j = 0. initWorld().clear(). for (int i = 0. Actor.get(j). walls.. return false.y() == area. y coordinates of all the bags and the destination areas. j++) { Area area = (Area) areas. that the sokoban or a baggage don't pass the wall. if (completed) { completed = false.get(i).x() == area. i++) { Baggage bag = (Baggage) baggs. if the level is completed by calling the isCompleted() method. There are four types of collisions.x() && bag. if the level is completed.y()) { compl += 1. public void restartLevel() { areas.Image. j < num.java package sokoban. } The game is finished. When the baggage is moved. repaint(). if (bag. } } If we do some bad move. We delete all objects from the important lists and initiate the world again. The completed variable is set to false. We compare the x. baggs. we can restart the level.clear(). i < num. The checkWallCollision() method was created to ensure. A baggage can collide with a wall. if (compl == num) { completed = true.. with a sokoban object or with another baggage. import java. We get the number of bags. } } } The isCompleted() method checks.awt. y())) { return true. int y) { this.x()) && (this. } public void setY(int y) { this.x()) && (this. } } public boolean isRightCollision(Actor actor) { if (((this.x() . } public int y() { return this.y.SPACE) == actor. public Actor(int x.x())) { return true. this.x() == actor.y() == actor.y()) && (this.y() == actor. } else { return false. private int x.y() .x = x. } public Image getImage() { return this. } else { return false.y())) { return true. } public void setX(int x) { this.SPACE) == actor. } public void setImage(Image img) { image = img.x() + SPACE) == actor. private Image image.y = y. } else { return false. } public int x() { return this.image.x = x.x. } } public boolean isTopCollision(Actor actor) { if (((this. private int y. } 679 . } public boolean isLeftCollision(Actor actor) { if (((this.private final int SPACE = 20.y = y. y() + SPACE) == actor.Image.getImage(). public boolean isLeftCollision(Actor actor) { if (((this.setImage(image).y())) { return true.} public boolean isBottomCollision(Actor actor) { if (((this.net. import javax.Image.net. ImageIcon iia = new ImageIcon(loc). URL loc = this. It encapsulates the basic functionality of an object in the Sokoban game.java package sokoban. } } This is the Wall class. public class Wall extends Actor { private Image image. Wall.x() == actor. The class is a base class for other actors in the game. baggage. 680 .x()) && (this.ImageIcon.x())) { return true. } } This method checks.y() == actor. it loads a wall image from the filesystem. y). } } } This is the Actor class.swing. if the actor collides with another actor (wall. import java.getClass().SPACE) == actor.java package sokoban.URL.URL. } else { return false.awt. import javax.awt.png"). import java.y()) && (this. public Wall(int x. } else { return false.x() . int y) { super(x. Player. import java.swing. Upon construction. image = iia.ImageIcon. this. It inherits from the Actor class. sokoban) to the left. import java.getResource("wall. ImageIcon iia = new ImageIcon(loc).ImageIcon.java package sokoban. public void move(int x. y). which moves the object inside the world.getClass(). } } This is the class for the Baggage object.URL. import javax. Image image = iia. public class Baggage extends Actor { public Baggage(int x. ImageIcon iia = new ImageIcon(loc). this. URL loc = this.y() + y.getImage(). this.setX(nx).y() + y.Image.x() + x. } public void move(int x.x() + x.net. 681 . int ny = this. import java.setY(ny). int ny = this. This object is movable.png").getResource("baggage.setX(nx).getResource("sokoban.getImage(). this. this.getClass().setY(ny).setY(ny).awt. It is the class to create the sokoban object.x() + x. this. Image image = iia. this.y() + y.setImage(image). int y) { int nx = this. } This class has a move() method.swing.png"). int y) { super(x. y). int y) { int nx = this. so it has the move() method also. this. int y) { super(x. import java. this. } public void move(int x. } } This is the Player class. URL loc = this.setX(nx).public class Player extends Actor { public Player(int x.setImage(image). Baggage. int ny = this. int y) { int nx = this. awt.Image. Sokoban. this.Area.ImageIcon.swing.setVisible(true).getBoardWidth() + OFFSET. } } The Area class.getResource("area. on which we try to place the baggages.java package sokoban. setDefaultCloseOperation(JFrame.getBoardHeight() + 2*OFFSET).png").getClass(). add(board).getImage(). } } This is the main class. setSize(board. board. 682 . ImageIcon iia = new ImageIcon(loc).java package sokoban. Image image = iia. setLocationRelativeTo(null). int y) { super(x.swing.setImage(image). } public static void main(String[] args) { Sokoban sokoban = new Sokoban(). import javax. import javax. It is the object.JFrame. URL loc = this.EXIT_ON_CLOSE). setTitle("Sokoban"). import java.URL. import java. sokoban. public Sokoban() { InitUI(). } public void InitUI() { Board board = new Board().net. y). public final class Sokoban extends JFrame { private final int OFFSET = 30. public class Area extends Actor { public Area(int x. Figure: Sokoban This was the Sokoban game. 683 .
Copyright © 2024 DOKUMEN.SITE Inc.