Java Projects - Second Edition

By Peter Verhas
  • Instant online access to over 7,500+ books and videos
  • Constantly updated with 100+ new titles each month
  • Breadth and depth in over 1,000+ technologies
  1. Getting Started with Java 11

About this book

Java is one of the most commonly used software languages by programmers and developers. In this book, you’ll learn the new features of Java 11 quickly and experience a simple and powerful approach to software development. You’ll see how to use the Java runtime tools, understand the Java environment, and create a simple namesorting Java application. Further on, you'll learn about advanced technologies that Java delivers, such as web programming and parallel computing, and will develop a mastermind game. Moving on, we provide more simple examples, to build a foundation before diving into some complex data structure problems that will solidify your Java 11 skills. With a special focus on the features of new projects: Project Valhalla, Project Panama, Project Amber, and Project Loom, this book will help you get employed as a top-notch Java developer. By the end of the book, you’ll have a firm foundation to continue your journey toward becoming a professional Java developer.

Publication date:
August 2018
Publisher
Packt
Pages
524
ISBN
9781789131895

 

Chapter 1. Getting Started with Java 11

You want to learn Java and you have a good reason for it. Java is a modern and well-established application programming language, which is widely used in many industries, such as telecommunication, finance, and much more. Java developer positions are the most numerous and, probably, the best paid. This, among other things, makes the language lucrative for young professionals to learn.

On the other hand, this is not without reason. The Java language, the tools, and the whole infrastructure around it is complex and sophisticated. Becoming a Java professional does not happen in a day or a week; it is a work of many years. To be a Java expert, you need to know not only about the programming language but also about object-oriented programming principles, open source libraries, application servers, network, databases, and many other things. Nevertheless, learning the language is an absolute minimum. All other practices build on it. Throughout this book, you will learn Java version 18.9, also known as Java 11, and other things. You will learn not only the language but also the most important tools like maven, gradle, spring, Guice, SoapUI; protocols like http/2, SOAP, REST; how to work in an agile professional team; and what tools the team should use to cooperate. In the last chapter, you will even learn how you can plan your career that you intend to start as a Java developer.

In this chapter, you will be introduced to the Java environment, and you will be given step-by-step instructions on how to install it, edit the sample code, compile, and run Java. You will get acquainted with the basic tools that help in development, whether a part of Java or provided by other vendors. We will cover the following topics in this chapter:

  • Introduction to Java
  • Installing on Windows, Linux, and macOS
  • Executing jshell
  • Using other Java tools
  • Using an integrated development environment
 

Getting started with Java


It is like going through a path in a forest. You can focus on the gravel of the road, but it is pointless. Instead, you can enjoy the view, the trees, the birds, and the environment around you, which is more enjoyable. This book is similar, as I won't be focusing only on the language. From time to time, I will cover topics that are close to the road and will give you some overview and directions on where you can go after you finish this book. I will not only teach you the language but also talk a bit about algorithms, object-oriented programming principles, tools that surround Java development, and how professionals work. This will be mixed with the coding examples that we will follow. Lastly, the final chapter will be fully devoted to the topic, what to learn next, and how to go further to become a professional Java developer.

By the time this book gets into print, Java will have completed 22 years (http://www.oracle.com/technetwork/java/javase/overview/javahistory-index-198355.html). The language has changed a lot during this period and got better. The real question to ask is not how long it has been here, but how long will it stay? Is it still worth learning this language? There are numerous new languages that have been developed since Java was born (http://blog.takipi.com/java-vs-net-vs-python-vs-ruby-vs-node-js-who-reigns-the-job-market/). These languages are more modern and have functional programming features, which, by the way, Java has also had since version 8. Many say that Java is the past—the future is Scala, Swift, Go, Kotlin, JavaScript, and so on. You can add many other languages to this list, and for each, you can find a blog article that celebrates the burial of Java. There are two answers to this concern: one is a pragmatic business approach, and the other is more regarding engineering:

  • Considering that COBOL is still actively used in the finance industry and COBOL developers are perhaps better paid than Java developers, it is not too risky to say that, as a Java developer, you will find positions in the next 40 years. Personally, I would bet more than 100 years, but considering my age, it will not be fair predicting more than 20 to 40 years ahead.
  • Java is not only a language, it is also a technology that you will learn a bit about from this book. The technology includes the Java Virtual Machine (JVM), which is usually referred to as JVM, and gives the runtime environment for many languages; Kotlin and Scala, for example, cannot run without JVM. Even if Java will be adumbrated, JVM will still be a number one player in the enterprise scene.

To understand and learn the basic operation of JVM is almost as important as the language itself. Java is a compiled and interpreted language. It is a special beast that forges the best of both worlds. Before Java, there were interpreted and compiled languages.

Interpreted languages are read from the source code by the interpreter, and then the interpreter executes the code. In each of these languages, there is some preliminary lexical and syntax analysis steps; however, after that, the interpreter, which, as a program itself, is executed by the processor, and the interpreter continuously interprets the program code to know what to do. Compiled languages are different. In such a case, the source code is compiled to binary (.exefile on Windows platforms), which the operating system loads and the processor directly executes. Compiled programs usually run faster, but there is usually a slower compilation phase that may make the development slower, and the execution environment is not so flexible. Java combined the two approaches.

To execute a Java program, the Java source code has to be compiled to the JVM bytecode (.class file), which is loaded by JVM and is interpreted or compiled. Hmm…is it interpreted or compiled? The thing that came with Java is the Just in Time (JIT) compiler. This makes the phase of the compilation that is calculation-intensive and the compilation for compiled languages relatively slow. JVM first starts to interpret the Java bytecode and, while doing that, it keeps track of execution statistics. When it gathers enough statistics about code executions, it compiles to native code (for example, x86 code on an Intel/AMD platform) for direct execution of the parts of the code that are executed frequently and keeps interpreting the code fragments that are rarely used. After all, why waste expensive CPU time to compile some code that is hardly ever used? (For example, code that reads configuration during startup and does not execute again unless the application server is restarted.) Compilation to the bytecode is fast, and code generation is done only for the segments that pay off.

It is also interesting that JIT uses the statistics of the code execution to optimize the code. If, for example, it can see that some conditional branch is executed in 99% of the cases and the other branch is executed only in 1%, it will generate native code that runs fast, thus favoring the frequent branch. If the behavior of that part of the program changes by time and the statistic shows that the ratios changed, the JIT automatically recompiles the bytecode from time to time. This is all automatic and behind the scenes.

In addition to the automatic compilation, there is also an extremely important feature of JVM—it manages the memory for the Java program. The execution environment of modern languages does that, and Java was the first mainstream language that had an automatic garbage collection (GC). Before Java, I was programming in C for 20 years, and it was a great pain to keep track of all memory allocation and not to forget to release the memory when the program no longer needed it. Forget memory allocation at a single point in the code, and the long-running program eats up all memory slowly. Such problems practically ceased to exist in Java. There is a price that we have to pay for it—GC needs processor capacity and some extra memory, but that is something we are not short of in most of the enterprise applications. Some special programs, like real-time embedded systems that control the brakes of a heavy-duty lorry, may not have that luxury.

Those are still programmed in assembly or C. For the rest of us, we have Java, and though it may seem strange for many professionals, even almost-real-time programs, such as high-frequency trading applications, are written in Java.

These applications connect through the network to the stock exchange, and they sell and buy stock responding to market changes in milliseconds. Java is capable of doing that. The runtime environment of Java that you will need to execute a compiled Java code, which also includes the JVM itself, contains code that lets Java programs access the network, files on disks, and other resources. To do this, the runtime contains high-level classes that the code can instantiate, execute, and which do the low-level jobs. You will also do this. It means that the actual Java code does not need to handle IP packets, TCP connections, or even HTTP handling when it wants to use or provide a REST service in some microservices architecture. It is already implemented in the runtime libraries, and all the application programmer has to do is include the classes in the code and use the APIs they provide on an abstraction level that matches the program. When you program in Java, you can focus on the actual problem you want to solve, which is the business code and not the low-level system code. If it is not in the standard library, you will find it in some product in some external library, and it is also very probable that you will find an open source solution for the problem.

This is also a strong point of Java. There are a vast number of open source libraries available for all the different purposes. If you cannot find a library fitting your problem and you start to code some low-level code, then you are probably doing something wrong. There are topics in this book that are important, such as class loaders or reflection, not because you have to use them every day but because they are used by frameworks, and knowing them helps you understand how these frameworks work. If you cannot solve your problem without using reflection or writing your own class loader or program multithread directly, then you probably chose the wrong framework. There is almost certainly a good one; Apache project, Google, and many other important players in the software industry publish their Java libraries as open source.

This is also true for multithread programming. Java is a multithread programming environment from the very beginning. The JVM and the runtime support programs that execute the code. The execution runs parallel on multiple threads. There are runtime language constructs that support the parallel execution of programs. Some of these constructs are very low level, and others are at a high abstraction level. Multithread code utilizes the multicore processors, which are more effective. These processors are more and more common. 20 years ago, only high-end servers had multiple processors and only Digital Alpha processors had 64-bit architecture and CPU clock above 100 MHz. 10 years ago, a multiprocessor structure was common on the server side, and about 5 years ago, multicore processors were on some desktops and on notebooks; today, even mobile phones have them. When Java was started in 1995, the geniuses who created it had seen this future.

They envisioned Java to be a write once, run anywhere language. At that time, the first target for the language was applet running in the browser. Today, many think (and I also share this opinion) that applets were a wrong target, or at least things were not done in the right way. As of now, you will meet applets on the internet less frequently than Flash applications or dinosaurs. What's more, the applet interface was deprecated already in Java 9, creating the opinion that applets are not good officially.

However, at the same time, the Java interpreter was also executing server and client applications without any browser. Furthermore, as the language and the executing environment developed, these application areas became more and more relevant. Today, the main use of Java is enterprise computing and mobile applications mainly for the Android platform. For the future, the use of the environment is growing in embedded systems as the Internet of things (IoT) comes more and more into the picture.

Version numbers

Java versioning is constantly changing. It does not only mean that the version numbers are changing from one release to the other. That is kind of obvious; that is what version numbers are for, after all. In the case of Java, however, the structure of the version numbers is also changing. Java started with version 1.0 (surprise!) and then version 1.1 shortly followed. The next release was 1.2, and it was so much different from the previous versions that people started calling it Java 2. Then, we had Java 1.3 till Java 1.8. This was a stable period as far as we consider the structure of the version number. However, the next Java version was named Java 9 instead of 1.9 last year, in 2017. It makes sense, because after 22 years of development and nine releases, the 1. part of the version number did not really make much sense. Nobody was expecting a "real" Java 2.0 that is so much different from any other releases that it deserved the 2. version prefix. In reality, the Java versions were really 1, 2, 3 and so on; they were just named as 1.1, 1.2, 1.3, and so on.

You could expect that after this huge change in the release number format, the next release of Java would be Java 10. Not at all. Oracle decided to use date-based release numbers. The first part of the release number before the dot will be the two digit year, like 18 for versions released in 2018. The part after the dot is the number of the month, usually 3 for March and 9 for September. Thus, when you look at Java version number 18.3, you immediately know that this version was released March 2018, which is actually Java 10 when following the old nomenclature.

 

Installing Java


To develop, compile, and execute Java programs, you will need the Java execution environment. As the operating systems that we usually use for software development do not contain the language preinstalled, you will have to download it. Although there are multiple implementations of the language, I recommend that you download the official version of the software from Oracle. The official site for Java is http://java.com, and this is the site from where the latest release of the language can be downloaded. At the time of writing this book, the 11th version of Java has not yet been released. An early pre-release version is accessible via http://jdk.java.net/11/ to download. Later, the release versions will also be available from here:

What you can download from here is a so-called early access version of the code that is available only to experiment with it, and no professionals should use it for commercial purposes.

On the page, you have to click on the radio button to accept, but the license. After that, you can click on the link that directly starts the download of the installation kit. The license is a special early access license version that you, as a professional, should carefully read, understand, and accept only if you are agreeable to the terms.

There is a separate installation kit for Windows 32 and 64 bit systems, macOS, Linux 32, and 64-bit versions, Linux for ARM processor, Solaris for SPARC processor systems, and Solaris x86 versions. As it is not likely that you will use Solaris, I will detail the installation procedure only for Windows, Linux, and macOS. In the later chapters, the samples will always be macOS, but since Java is a write once, run anywhere language, there is no difference after the installation. The directory separator may be slanted differently, the classpath separator character is a semicolon on Windows instead of a colon, and the look and feel of the Terminal or command application is also different. However, where it is important, I will try not to forget to mention it.

To confuse you, the Java download for each of these operating system versions lists a link for the JRE and one for the JDK. JRE stands for Java Runtime Environment, and it contains all the tools and executables that are needed to run Java programs. JDK is the Java Development Kit that contains all the tools and executables needed to develop Java programs, including the execution of the Java program. In other words, JDK contains its own JRE. For now, all you need to do is download the JDK.

There is one important point of the installation that is the same on each of the three operating systems, which you have to be prepared for before the installation—to install Java, you should have administrative privileges.

Installation on Windows

The installation process on Windows starts by double-clicking on the downloaded file. It will start the installer and will present you with a welcome screen. Windows 10 may ask you the admin permission to install Java:

Pressing the Next button gets a window where you can select the parts you want to install, and also, we can change the location where Java will be installed:

Let's leave the default settings here, which means that we install all the downloaded parts of Java and press Next:

We get to a progress screen while Java is installing. This is a fairly fast process, no more than a 10-second process. After Java has been installed, we get a confirmation screen:

We can press Close. It is possible to press the Next Steps button, that opens the browser and brings us to a page that describes the next steps we can do with Java. Using the prerelease version results in an HTTP 404 error. This will hopefully be fixed when you read this book.

The last step is to set the environment variable JAVA_HOME. To do that, in Windows, we have to open the control center and select the Edit environment variables for your account menu:

This will open a new window that we should use to create a new environment variable for the current user:

The name of the new variable has to be JAVA_HOME, and the value should point to the installation directory of the JDK:

This value on most systems is C:\Program Files\Java\jdk-11. This is used by many Java programs and tools to locate the Java runtime.

Installation on macOS

In this section, we will take look at how to install Java step by step on a macOS platform. I will describe the installation process for the released version available at the time of writing this book. As of now, the Java 18.9 early access version is a bit tricky to install. It is probable that the release version of Java 18.9 will have similar or the same installation steps as Java 9.

The macOS version of Java comes in the form of a .dmg file. This is a packaging format of macOS. To open it, simply double-click on the file in the Download folder where the browser saves it, and the operating system will mount the file as a read-only disk image:

There is only one file on this disk—the installation image. Double-click on the filename or icon in the Finder application and the installation process will start:

The first screen is a welcome screen. Click on Continue, and you will see the Summary page that displays what will be installed.

It is not a surprise that you will see a standard Java installation. This time, the button is called Install. Click on it and you will see the following:

This is the time when you have to provide the login parameters for the administrative user—a username and password:

When provided, installation starts and, in a few seconds, you will see a Summary page:

Click on Close and you are ready. You have Java installed on your Mac. Optionally, you can dismount the installation disk and, sometime later, you can also delete the .dmg file. You will not need that, and in case you do, you can download it any time from Oracle.

The last thing is to check whether the installation was okay. The proof of the pudding is in eating it. Start a Terminal window and type java -version at the prompt; Java will tell you the version that's been installed.

In the following screenshot, you can see the output on my workstation and also the macOS commands that are handy to switch between the different versions of Java:

In the preceding screenshot, you can see that I have installed the Java 11 version and, at the same time, I also have a Java 18.9 early release installation, which I will use to test the new features of Java for this book.

Installation on Linux

There are several ways to install Java on Linux, depending on its flavor. Here, I will describe an installation method that works more or less the same way on all flavors. The one I used is Debian.

The first step is the same as in any other operating system—download the installation kit. In the case of Linux, you should select a package that has a tar.gz ending. This is a compressed archive format. You should also carefully select the package that matches the processor in your machine and the 32/64 bit version of the operating system. After the package is downloaded, you have to switch to root mode, issuing the su command. This is the first command you can see in the following screenshot, that shows the installation commands:

The tar command uncompressed the archive into a subfolder. In Debian, this subfolder has to be moved to /opt/jdk, and the mv command is used for this purpose. The two update-alternatives commands are Debian-specific. These tell the operating system to use this newly installed Java in case there is already an older Java installed. The Debian I was using to test and demonstrate the installation process on a virtual machine came with a 7-year-old version of Java.

The final step of the installation is the same as any other operating system—checking whether the installation was successful in issuing the java -version command. In the case of Linux, this is even more important. The installation process does not check that the downloaded version matches the operating system and the processor architecture.

Setting JAVA_HOME

The JAVA_HOME environment variable plays a special role in Java. Even though the JVM executable, java.exe or java, is on the PATH (thus, you can execute it by typing the name java without specifying the directory in the Command Prompt) (Terminal), it is recommended that you use the correct Java installation to set this environment variable. The value of the variable should point to the installed JDK. There are many Java-related programs, Tomcat or Maven for example, that use this variable to locate the installed and currently used Java version. In macOS, setting this variable is unavoidable.

In macOS, the program that starts to execute when you type java, is a wrapper that first looks at JAVA_HOME to decide which Java version to start. If this variable is not set, macOS will decide on its own, selecting from the available installed JDK versions. To see the available versions, you can issue the following command:

~$ /usr/libexec/java_home -V
Matching Java Virtual Machines (13):
    11, x86_64: "Java SE 11-ea" /Library/Java/JavaVirtualMachines/jdk-11.jdk/Contents/Home
    10, x86_64: "Java SE 10"    /Library/Java/JavaVirtualMachines/jdk-10.jdk/Contents/Home
    9.0.1, x86_64:      "Java SE 9.0.1" /Library/Java/JavaVirtualMachines/jdk-9.0.1.jdk/Contents/Home
    9, x86_64:  "Java SE 9-ea"  /Library/Java/JavaVirtualMachines/jdk-9.jdk/Contents/Home
    1.8.0_92, x86_64:   "Java SE 8"     /Library/Java/JavaVirtualMachines/jdk1.8.0_92.jdk/Contents/Home
    1.8.0_20, x86_64:   "Java SE 8"     /Library/Java/JavaVirtualMachines/jdk1.8.0_20.jdk/Contents/Home
    1.8.0_05, x86_64:   "Java SE 8"     /Library/Java/JavaVirtualMachines/jdk1.8.0_05.jdk/Contents/Home
    1.8.0, x86_64:      "Java SE 8"     /Library/Java/JavaVirtualMachines/jdk1.8.0.jdk/Contents/Home
    1.7.0_60, x86_64:   "Java SE 7"     /Library/Java/JavaVirtualMachines/jdk1.7.0_60.jdk/Contents/Home
    1.7.0_40, x86_64:   "Java SE 7"     /Library/Java/JavaVirtualMachines/jdk1.7.0_40.jdk/Contents/Home
    1.7.0_21, x86_64:   "Java SE 7"     /Library/Java/JavaVirtualMachines/jdk1.7.0_21.jdk/Contents/Home
    1.7.0_07, x86_64:   "Java SE 7"     /Library/Java/JavaVirtualMachines/jdk1.7.0_07.jdk/Contents/Home
    1.7.0_04, x86_64:   "Java SE 7"     /Library/Java/JavaVirtualMachines/1.7.0.jdk/Contents/Home

/Library/Java/JavaVirtualMachines/jdk-11.jdk/Contents/Home

You will then get the list of installed JDKs. Note that the command is lowercase, but the option is capitalized. If you do not provide any options and argument to the program, it will simply return the JDK it thinks is the newest and most appropriate for the purpose. As I copied the output of the command from my Terminal window, you can see that I have quite a few versions of Java installed on my machine.

The last line of the program response is the home directory of JDK, which is the default. You can use this to set your JAVA_HOME variable using some bash programming:

export JAVA_HOME=$(/usr/libexec/java_home)

You can place this file in your .bashrc file, which is executed each time you start a Terminal application, and thus JAVA_HOME will always be set. If you want to use a different version, you can use -v, with the lowercase option this time, to the same utility, as follows:

export JAVA_HOME=$(/usr/libexec/java_home -v 1.8)

The argument is the version of Java you want to use. Note that this versioning becomes the following:

export JAVA_HOME=$(/usr/libexec/java_home -v 11)

If you want to use the Java JDK Early Access version and not 1.11, there is not an explanation for the same—fact of life.

Note that there is another environment variable that is important for Java—CLASSPATH. We will talk about it later.

 

Executing jshell


Now that we have spent a lot of time installing Java, it's time to get your fingers burnt a bit. As we are using Java 18.9, there is a new tool that helps developers play around with the language. This is a Read-Eval-Print-Loop (REPL) tool that many language toolsets contain and there were also implementations from Java, but version 9 is the first that contains this feature off the shelf.

REPL is a tool that has interactive prompt and language commands that can be directly entered without editing some standalone file. The entered commands are executed directly and then the loop starts again, waiting for the user to type in the next command.

This is a very effective tool to try out some language constructs without the delay of editing, compiling, and loading. The steps are automatically and transparently done by the REPL tool.

The REPL tool in Java 18.9 is called jshell. To start it, just type its name. If it is not on the PATH, then type the full path to jshell that comes installed with Java 18.9, as shown in the following example:

$ jshell | Welcome to JShell -- Version 11-ea | For an introduction type: /help intro jshell>

The jshell starts up in an interactive way and the prompt it displays is jshell> to help you recognize that jshell is running. What you type is read by the program and not the operating system shell. As this is the first time you will start jshell, it tells you to type /help intro. Let's do it. It will print out a short text about what jshell is, as shown in the following code:

jshell> /help intro
|  
|                                   intro
|                                   =====
|  
|  The jshell tool allows you to execute Java code, getting immediate results.
|  You can enter a Java definition (variable, method, class, etc), like:  int x = 8
|  or a Java expression, like:  x + x
|  or a Java statement or import.
|  These little chunks of Java code are called 'snippets'.
|  
|  There are also the jshell tool commands that allow you to understand and
|  control what you are doing, like:  /list
|  
|  For a list of commands: /help

Okay, so we can type Java snippets and /list, but that is only one example of the available commands. We can hope for more information by typing /help, as demonstrated in the following code:

jshell> /help
|  Type a Java language expression, statement, or declaration.
|  Or type one of the following commands:
|  /list [<name or id>|-all|-start]
|       list the source you have typed
|  /edit <name or id>
|       edit a source entry
|  /drop <name or id>
|       delete a source entry
|  /save [-all|-history|-start] <file>
|       Save snippet source to a file
...

What you get is a long list of commands. Most of it is not presented here to save paper and your attention. We will use many of these commands on our journey through the next few pages. Let's start with a small Java snippet, that is, the ageless Hello World example:

jshell> System.out.println("Hello, World!")
Hello World!

This is the shortest ever Hello World program in Java. Till Java 9, if you wanted to do nothing more than print out Hello World!, you had to create a program file. It had to contain the source code of a class, including the public static main method, which contained the one line we had to type in with Java 9 jshell. It was cumbersome just for a simple printout of sample code. Now it is much easier, and jshell is also lenient. It forgives us the regarding missing semicolon at the end of the line.

The next thing we should try is declaring a variable, as follows:

jshell> var a = 13
a ==> 13

We declared a variable, named a, and assigned the value to it—13. The type of the variable is int, which is an abbreviation for integer types in Java. Now, we have this variable already in our snippet, so we can print it out if we want to, as shown here:

jshell> System.out.println(a)
13

It's time to write something more complex into jshell than a one-liner:

jshell> void main(String[] args){
   ...> System.out.println("Hello, World")
   ...> }
|  Error:
|  ';' expected
|  System.out.println("Hello, World")
|                                   ^

The jshell recognizes that this is not a one-liner and that it cannot process what we typed so far when we press Enter at the end of the first line, and it signals that it expects more characters from us, so it displays ...> as a continuation prompt. We type in the commands that make up the whole hello world main method.

 

However, this time, jshell does not let us miss the semicolon; that is allowed only in the case of one-line snippets. As jshell is interactive, it is easy to correct the mistake—press the up arrow key a few times to get back the previous lines and, this time, add the semicolon at the end of the second line:

jshell> void main(String[] args){
   ...> System.out.println("Hello, World");
   ...> }
|  created method main(String[])

This method was created for us as a snippet, and now we can call it:

jshell> main(null)
Hello, World

It works. You can list all the snippets that were created, as follows:

jshell> /list

   1 : System.out.println("Hello World!")
   2 : var a = 13;
   3 : System.out.println(a)
   4 : void main(String[] args){
       System.out.println("Hello, World");
       }
   5 : main(null)

Also, as we want to go on writing a full Java version of hello world, we can save our work from jshell to a file, as follows:

jshell> /save HelloWorld.java

Finally, we exit from jshell by typing /exit. As you get back to the system prompt, type cat HelloWorld.java (or type HelloWorld.java on Windows) to see the content of the file. It is as follows:

$ cat HelloWorld.java 
System.out.println("Hello, World!")
var a = 13;
System.out.println(a)
void main(String[] args){
System.out.println("Hello, World");
}
main(null)

The file contains all the snippets that we typed in, one after the other. If you think that you have messed up the shell with lots of variables and code snippets that you do not need anymore, you can issue the /reset command:

jshell> /reset
|  Resetting state.

After this command, the jshell is as clean as when it was started earlier:

jshell> /list

jshell>

Listing just does not produce anything, as we deleted it all. Fortunately, we saved the state of jshell to a file, and we can also loaded the content of the file by issuing the /open command:

jshell> /open HelloWorld.java
Hello, World!
13
Hello, World

It loads the line from the file and executes it, just as the characters were typed into the Command Prompt.

You may recall that the /list command printed a number in front of each snippet. We can use it to edit the snippets individually. To do so, issue the /edit command, followed by the number of the snippet:

jshell> /edit 1

You may recall that the first command we entered was the System.out.println system call that prints out the argument to the console. When you press Enter after the /edit 1 command, you do not get the prompt back. Instead, jshell opens a separate graphical editor that contains the snippet to edit, as shown:

Edit the text in the box so that it will look like this:

void printf(String format, Object... args) { System.out.printf(format, args); }
printf("Hello World!")

Click on Accept and then Exit. When you click on Accept, the Terminal will execute the snippet and display the following result:

| created method printf(String,Object...) Hello World!

The method that we used, printf, stands for formatted printing. This may be well-known from many other languages. It was first introduced by the C language and though cryptic, the name survived. This is also part of the standard Java class, PrintStream, just like println. In case of println, we had to write System.out in front of the method name. To avoid that, we defined the snipped in the editor, and it got executed and defined the printf method for us.

Jshell also defines a few snippets that are automatically loaded when jshell starts or resets. You can see these if you issue the /list command with the -start option, as follows:

jshell> /list -start

  s1 : import java.io.*;
  s2 : import java.math.*;
  s3 : import java.net.*;
  s4 : import java.nio.file.*;
  s5 : import java.util.*;
  s6 : import java.util.concurrent.*;
  s7 : import java.util.function.*;
  s8 : import java.util.prefs.*;
  s9 : import java.util.regex.*;
 s10 : import java.util.stream.*;

These predefined snippets help in the use of jshell. Most users will import these classes.

If you want to list all the snippets you entered as well as the predefined snippets, and also those that contained some error and thus were not executed, you can use the -all option on the /list command, as follows:

jshell> /list -all
  s1 : import java.io.*;
  s2 : import java.math.*;
  s3 : import java.net.*;
  s4 : import java.nio.file.*;
  s5 : import java.util.*;
  s6 : import java.util.concurrent.*;
  s7 : import java.util.function.*;
  s8 : import java.util.prefs.*;
  s9 : import java.util.regex.*;
 s10 : import java.util.stream.*;
   1 : System.out.println("Hello, World!")
   2 : var a = 13;
   3 : System.out.println(a)
   4 : void main(String[] args){
       System.out.println("Hello, World");
       }
   5 : main(null)
   6 : void printf(String format, Object... args) { System.out.printf(format, args); }
   7 : System.out.println("Hello, World!");

The lines that are preloaded are numbered with the s prefix. The snippets that contain an error have a number prefixed with e. (We have none in this printout.)

If you want to execute some of the snippets again, you only have to type /n, where n is the number of the snippet, as follows:

jshell> /1
System.out.println("Hello, World!")
Hello, World!

You cannot re-execute the preloaded snippets or snippets that contained errors. There is no need for any of those anyway. Preloaded snippets declare some imports; erroneous snippets do not execute because they are, well…erroneous.

You need not rely on the number of jshell when you want to re-execute a snippet. When you already have a lot of snippets in your jshell session, listing them all would be too cumbersome; there is a shortcut to re-execute the last n-th snippet. You have to write /-n. Here, n is the number of the snippet counting from the last one. So, if you want to execute the very last snippet, you have to write /-1. If you want to execute the one before the last one, you have to write /-2. Note that if you already typed /-1, the last one is the re-execution of the last snippet, and snippet number -2 will become number -3.

Listing all the snippets can also be avoided in other ways. When you are interested only in certain types of snippets, you can have special commands.

If we want to see only the variables that we defined in the snippets, we can issue the /vars command, as follows:

jshell> /vars
|    int a = 13

If we want to see only the classes, the /typescommand will do that:

jshell> class s {}
|  created class s

jshell> /types
|    class s

Here, we just created an empty class and then we listed it.

To list the methods that were defined in the snippets, the /methods command can be issued:

jshell> /methods
|    void main(String[])
|    void printf(String,Object...)

You can see in the output that there are only two methods, which are as follows:

  • main: Which is the main class of the program
  • printf: This, we defined when using the editor

If you want to see everything you typed, you have to issue the /history command for all the snippets and commands that you typed. (I will not copy the output here; I do not want to shame myself showing all of my typos and failures. You should try yourself and see your own history!)

Recall that we can delete all the snippets by issuing the /reset command. You can also delete snippets individually. To do so, you should issue the /drop n command, where n is the snipped number:

jshell> /drop 1

jshell> /list

   2 : var a = 13;
   3 : System.out.println(a)
   4 : void main(String[] args){
       System.out.println("Hello, World");
       }
   5 : main(null)
   6 : void printf(String format, Object... args) { System.out.printf(format, args); }
   7 : System.out.println("Hello, World!");
   8 : System.out.println("Hello, World!")

We can see that we dropped the first snippet:

jshell> /drop 2
|  dropped variable a

jshell> /drop 4
|  dropped method main(String[])

Note

The jshell error message asks us to see the output of the /types, /methods, /vars, or /list commands. The problem with this is that /types, /methods, and /vars do not display the number of the snippet. This is most probably a small bug in the jshell prerelease version and may be fixed by the time the JDK is released.

When we were editing the snippets, jshell opened a separate graphical editor. It may happen that you are running jshell using ssh on a remote server and where it is not possible to open a separate window. You can set the editor using the /set command. This command can set quite a few configuration options of the jshell. To set the editor to use the ubiquitous vi, issue the following command:

jshell> /set editor "vi"
|  Editor set to: vi

After this, jshell will open the snipped-in vi in the same Terminal window where you issue the /edit command.

It is not only the editor that you can set. You can set the startup file, and also the way jshell prints the feedback to the console after a command was executed.

If you set the startup file, the commands listed in the startup file will be executed instead of the built-in commands of jshell after the /reset command. This also means that you will not be able to use the classes that are imported by default directly, and you will not have the printf method snippet, unless your own startup file contains the imports and the definition of the snippet.

Create the sample.startup file with the following content:

void println(String message) { System.out.println(message); }

Starting up a new jshell, execute it as follows:

jshell> /set start sample.startup

jshell> /reset
|  Resetting state.

jshell> println("wuff")
wuff

jshell> printf("This won't work...")
|  Error:
|  cannot find symbol
|    symbol:   method printf(java.lang.String)
|  printf("This won't work...")
|  ^----^

The println method is defined, but the printf method, which we defined earlier, is not.

The feedback defines the prompt jshell prints and then waits for the input, the prompt for the continuation lines, and the message details after each command. There are predefined modes, which are as follows:

  • Normal
  • Silent
  • Concise
  • Verbose

Normal is selected by default. If you issue /set feedback silent, prompt becomes ->, and jshell will not print details about the commands. The /set feedback concise code prints a bit more information, and /set feedback verbose prints verbose information about the commands executed:

jshell> /set feedback verbose
|  Feedback mode: verbose

jshell> int z = 13
z ==> 13
|  created variable z : int

jshell> int z = 13
z ==> 13
|  modified variable z : int
|    update overwrote variable z : int

You can also define your own modes, giving a name to the new mode using the /set mode xyz command, where xyz is the name of the new mode. After this, you can set prompt, truncation, and format for the mode. When the format is defined, you can use it in the same way as the built-in modes.

Last but not least, the most important command of jshell is /exit. This will just terminate the program, and you will return to the operating system shell prompt.

Now, let's edit the HelloWorld.java file to create our first Java program. To do so, you can use vi, notepad, Emacs, or whatever is available on your machine and fits you. Later on, we will use some integrated development environment (IDE), NetBeans, Eclipse, or IntelliJ; however, for now, a simple text editor is enough.

Edit the file so that the content will be as follows:

public class HelloWorld { 
  public static void main(String[] args){ 
        System.out.println("Hello World"); 
       } 
  }

To compile the source code to bytecode, which is executable by JVM, we have to use the Java compiler named javac:

javac HelloWorld.java

This generates the java.class file in the current directory. This is a compiled code that can be executed as follows:

$ java HelloWorld
Hello World

With this one, you have created and executed your first full Java program. You may still wonder what we were doing; everything will be clear later. Here and now, I wanted you to get a feeling that it works.

The file we edited contained only the snippet, and we deleted most of the lines, except for the declaration of the main method, and inserted the declaration of the class around it.

In Java, you cannot have standalone methods or functions, like in many other languages. Every method belongs to some class, and every class should be declared in a separate file (well, almost, but for now, let's skip the exceptions). The name of the file has to be the same as the name of the class. The compiler requires this for public classes. Even for non-public classes, we usually follow this convention. If you renamed the file from HelloWorld.java to Hello.java, the compiler will display an error when you try to compile the file with the new name:

$ mv HelloWorld.java Hello.java
~/Dropbox/java_9-by_Example$ javac Hello.java
Hello.java:2: error: class HelloWorld is public, should be declared in a file named HelloWorld.java
public class HelloWorld {
       ^
1 error

So, let's move it back to the original name, that is, mv Hello.java HelloWorld.java.

The declaration of the class starts with the class keyword, then the name of the class, an opening curly brace, and lasts until the matching closing brace. Everything in-between belongs to the class.

For now, let's skip why I wrote public in front of the class and focus on the main method in it. The method does not return any value; therefore, its return value is void. The argument, named args, is a string array. When JVM starts the main method, it passes the command-line arguments to the program in this array. However, this time, we do not use it. The main method contains the line that prints out Hello World. Now, let's examine this line a little more.

In other languages, printing something to the console requires only a print statement, or a very similar command. I remember that some BASIC interpreters even allowed us to type ? instead of print, because printing to the screen was so common. This has changed a lot during the last 40 years. We use graphical screens, internet, and many other input and output channels. These days, it is not very common to write to the console.

Usually, in professional large-scale enterprise applications, there is not even a single line that does that. Instead, we will direct the text to log files, send messages to message queues, and send requests and reply with responses over TCP/IP protocol. As this is so infrequently used, there is no reason to create a shortcut for the purpose in the language. After the first few programs, when you get acquainted with the debugger and logging possibilities, you will not print anything directly to the console yourself.

Still, Java has features that let you send text directly to the standard output of a process the good old way, as it was invented originally for UNIX. This is implemented in a Java way, where everything has to be an object or class. To get access to the system output, there is a class named System, and it, among other things, has the following three variables:

  • in: This is the standard input stream
  • out: This is the standard output stream
  • err: This is the standard error stream

To refer to the output stream variable, because it is not in our class but in System, we will have to specify the class name, so we will refer to it as System.out in our program. The type of this variable is PrintStream, which is also a class. Class and type are synonyms in Java. Every object that is of the PrintStreamtype has a method named println that accepts a String. If the actual print stream is the standard output, and we are executing our Java code from the command line, the string is sent to the console.

The method is named main, and this is a special name in Java programs. When we start a Java program from the command line, JVM invokes the method named main from the class that we specify on the command line. It can do that because we declared this method public so that anyone can see and invoke it. If it was private, it would be seen and callable only from within the same class, or classes that are defined in the same source file.

The method is also declared as static, which means it can be invoked without an actual instance of the class that contains the methods. Using static methods is usually not seen as a good practice these days, unless they are implementing functions that cannot really ever be related to an instance, or have different implementations such as the functions in the java.lang.Math class. However, somewhere, the code execution has to start, and the Java runtime will not usually create instances of classes for us automatically.

To start the code, the command line should be as follows:

java -cp . HelloWorld

The -cp option stands for classpath. The classpath is a fairly complex idea for Java, but, for now, let's make it simple and say that it is a list of directories and JAR files that contain our classes. The list separator for the classpath is : (colon) on UNIX-like systems and ; (semicolon) on Windows. In our case, the classpath is the actual directory, as that is the place where the Java compiler created HelloWorld.class. If we do not specify classpath on the command line, Java will use the current directory as a default. That is the reason our program was working without the -cp option in the first place.

Both java and javac handle many options. To get a list of the options, type javac -help or java -help. We use the IDE to edit the code and, many times, to compile, build, and run it during development. The environment, in this case, sets the reasonable parameters. For production, we use build tools that also support the configuration of the environment. Due to this, we rarely meet these command-line options. Nevertheless, professionals have to understand their meanings at least and know where to learn their actual use, in case it is needed.

Looking at the bytecode

The class file is a binary file. The main role of this format is to be executed by the JVM and to provide symbolic information for the Java compiler when a code uses some of the classes from a library. When we compile our program that contains System.out.println, the compiler looks at the compiled .class files and not at the source code. It has to find the System class, the out field, and the println method. 

When we debug a piece of code or try to find out why a program does not find a class or method, we will need a way to look into the binary of the .class files. This is not an everyday task, and it takes some advanced knowledge.

To do so, there is a decompiler that can display the content of a .class file in a more or less readable format. This command is called javap. To execute it, you can issue the following command:

$ javap HelloWorld.class
Compiled from "HelloWorld.java"
public class HelloWorld {
  public HelloWorld();
  public static void main(java.lang.String[]);
}

The output of the program shows that the class file contains a Java class that has something called HelloWorld(); it seems to be a method having the same name as the class, and it also contains the method we have written.

The method that has the same name as the class is the constructor of the class. As every class in Java can be instantiated, there is a need for a constructor. If we do not give one, the Java compiler will create one for us. This is the default constructor. The default constructor does nothing special, but returns a new instance of the class. If we provide a constructor on our own, the Java compiler will not have bothered creating one.

The javap decompiler does not show what is inside the methods or what Java code it contains unless we provide the -c option:

$ javap -c HelloWorld.class
Compiled from "HelloWorld.java"
public class HelloWorld {
  public HelloWorld();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return
  public static void main(java.lang.String[]);
    Code:
       0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #3                  // String hali
       5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: return
}

It is very cryptic and is not for ordinary humans. Only a few experts who deal with the Java code generation can fluently read that. However, taking a look at it helps you get a glimpse of what bytecode means. It is something like a good old assembly. Although this is binary code, there is nothing secret in it: Java is open source, and the class file format is well documented and debuggable for the experts.

 

Packaging classes into a JAR file


When you deliver a Java application, usually the code is packaged into JAR, WAR, EAR, or some other packaged format. We learn something again that seems to be obscure at first sight, but in reality, this is not that complex. They are all ZIP files. You can open any of these files using WinZip or some other ZIP manager that you have a license for. The extra requirement is that, for example, in the case of a JAR file, the archive should contain a directory named META-INF and inside it a file named MANIFEST.MF. This file is a text file and contains meta information in the format, which is as follows:

Manifest-Version: 1.0 
Created-By: 11-ea (Oracle Corporation)

There can be a lot of other information in the file, but this is the minimum that the Java provided tool jar puts there if we package our class file into a JAR, issuing the following command:

jar -cf hello.jar HelloWorld.class

The -c option tells the JAR archiver to create a new JAR file and the foption is used to specify the name of the new archive. The one we specified here is hello.jar, and the file added to it is the class file.

The packaged JAR file can also be used to start the Java application. Java can read directly from JAR archives and load classes from there. The only requirement is that they are on the classpath.

Note

You cannot put individual classes on the classpath, only directories. As JAR files are archives with an internal directory structure in them, they behave like a directory.

Check that the JAR file was created using ls hello.jar, and remove the rm HelloWorld.class class file just to ensure that when we issue the command line, the code is executed from the JAR file and not the class:

$ java -cp hello.jar HelloWorld
Hello World

To see the content of the JAR file, however, it is recommended that you use the JAR tool and not WinZip, even though that may be cozier. Real professionals use the Java tools to handle Java files:

$ jar -tf hello.jar META-INF/ META-INF/MANIFEST.MF HelloWorld.class
 

Managing the running Java application


The Java toolset that comes with the JDK supports the execution and management of running Java applications as well. To have some program that we can manage while executing, we will need a code that runs not only for a few milliseconds but, while it runs, it also prints something to the console. Let's create a new program called HelloWorldLoop.java, with the following content:

public class HelloWorldLoop { 
  public static void main(String[] args){ 
       for( ;; ){ 
         System.out.println("Hello World"); 
         } 
       } 
  }

The program contains a for loop. Loops allow repeated execution of a code block, and we will discuss them in Chapter 2, The First Real Java Program - Sorting Names. The loop we created here is a special one that never terminates but repeats the printing method call, printing Hello World until we kill the program by pressing Ctrl + C or issuing a kill command on Linux or on macOS X, or terminate the program in the task manager under Windows.

Compile and start it in one window and open another Terminal window to manage the application.

The first command that we should get familiar with is jps. To get more familiar with jps, you can read some content here—http://docs.oracle.com/javase/7/docs/technotes/tools/share/jps.html, It lists the Java processes that run on the machine, which are as follows:

$ jps 
21873 sun.tools.jps.Jps 
21871 HelloWorldLoop

You can see that there are two processes: one is the program we execute and the other is the jps program itself. Not surprisingly, the jps tool is also written in Java. You can also pass options to jps, which are documented on the web.

There are many other tools, and we will examine one of them, which is a very powerful and easy-to-use tool—Java VisualVM:

VisualVM is a command-line graphical tool that connects to the running Java process and displays the different performance parameters. To start the VisualVM tool, you will issue the jvisualvm command without any parameters. Soon, a window will appear with an exploring tree on the left-hand side and a welcome pane on the right. The left shows all the running Java processes under the branch named Local. If you double-click on HelloWorldLoop, it will open the details of the process on the right pane. On the header tabs, you can select Overview, Monitor, Threads, Sampler, and Profiler. The first three tabs are the most important and give you a good view of what is happening in JVM regarding the number of threads, CPU usage, memory consumption, and so on.

 

Using an IDE


Integrated development environments are outstanding tools that help the development by offloading the mechanical tasks from the developer's shoulders. They recognize many of the programming errors as we type the code, help us find the needed library methods, display the documentation of the libraries, and provide extra tools for style checking, debugging, and such.

In this section, we will look at some IDEs and how to leverage the functions they provide.

To get an IDE, you will have to download and install it. It does not come with the Java development tools, because they are not part of the language environment. However, don't worry, they can be downloaded free of charge and are easy to install. They may be more complex to start up than a notepad editor, but even after a few hours of work, they will pay back the time you devote to learning them. After all, it is not without reason that no developer is coding Java in notepad or vi.

The three topmost IDEs are NetBeans, Eclipse, and IntelliJ. All are available in community versions, which means you need not pay for them. IntelliJ has a full version that you can also buy. The community edition will be used for learning the language. In case you do not like IntelliJ, you can use Eclipse or NetBeans. These are all free of charge. Personally, I use the IntelliJ community edition for most of my projects, and the screen samples that show an IDE in this book will feature this IDE. However, it does not necessarily mean that you have to stick to this IDE.

Note

In the developer community, there are topics that can be heavily debated. These topics are about opinions. Were they about facts, the debate would easily be soon over. One such topic is "Which is the best IDE?". It is a matter of taste. There is no definite answer. If you learn how to use one, you will like that, and you will be reluctant to learn another one unless you see that the other one is so much better. That is the reason developers love the IDE they use (or just hate, depending on their personality), but they keep using the same IDE, usually for a long time. There is no best IDE.

To download the IDE of your choice, you can visit either one of the following websites:

NetBeans

NetBeans is supported by Oracle and is continuously developed. It contains components, such as the NetBeans profiler, that became part of the Oracle Java distribution. You may note that when you start Visual VM and start the profiling, the Java process started has netbeans in its name.

Generally, NetBeans is a framework to develop rich client applications, and the IDE is only one application of the many that are built on top of the framework. It supports many languages, not only Java. You can develop PHP, C, or JavaScript code using NetBeans and have similar services for Java. For the support of different languages, you can download plugins or a special version of NetBeans. These special versions are available from the download page of the IDE, and they are nothing more than the basic IDE with some preconfigured plugins. In the C package, the developers configure the plugins that are needed when you want to develop C; in the PHP version, the developers configure for PHP.

Eclipse

Eclipse is supported by IBM. Similar to NetBeans, it is also a platform for rich-client application, and it is built around the OSGi container architecture, which itself is a topic that can fill a book like this. Most of the developers use Eclipse and, almost exclusively, it is the choice when developers create code for the IBM WebSphere application server. The Eclipse special version contains a developer version of WebSphere.

Eclipse also has plugins to support different programming languages and also has different variations that are similar to NetBeans. The variations are plugins prepackaged with the basic IDE.

IntelliJ

The last one in the preceding enumeration is IntelliJ. This IDE is the only one that does not want to be a framework. IntelliJ is an IDE. It also has plugins, but most of the plugins that you will need to download to use in NetBeans or Eclipse are preconfigured. When you want to use some more advanced plugins, it may, however, be something you have to pay for, which should not be a problem when you are doing professional, paid work, should it? These things are not that expensive. To learn the subjects in this book, you won't need any plugin that is not in the community edition. As in this book, I will develop the samples using IntelliJ, and I recommend that you follow me during your learning experience.

Note

I want to emphasize that the examples in this book are independent of the actual IDE to be used. You can follow the book using NetBeans, Eclipse, or even Emacs, notepad, or vi.

IDE services

Integrated development environments provide us with services. The most basic service is that you can edit files with them, but they also help build the code, find bugs, run the code, deploy to the application server in development mode, debug, and so on. In the following sections, we will look at these features. I will not give an exact and precise introduction on how to use one or the other IDE. A book like this is not a good medium for such a tutorial.

IDEs differ on menu placement, keyboard shortcuts, and they may even change as newer versions are released. It is best to look at the actual IDE tutorial video or online help. Their features, on the other hand, are very similar. IntelliJ has the video documentation at https://www.jetbrains.com/idea/documentation/.

IDE screen structure

The different IDEs look similar, and they have the same screen structure more or less. In the following screenshot, you can see an IntelliJ IDE:

On the left, you can see the file structure of a Java project. A Java project typically contains many files in different directories, which we will discuss in the next chapter. The simple HelloWorld application contains a pom.xml project description file. This file is needed for the Maven build tool, which is also a topic for the next chapter. For now, you should only know that it is a file that describes the project structure for maven. The IDE also keeps track of some administrative data for itself. It is stored in HelloWorld.iml. The main program file is stored in the src/main/java directory and named HelloWorld.java.

On the right, you can see the files. In the preceding screenshot, we have only one file opened. In case there is more than one file opened, there are tabs, one for each file. Now, the active file is HelloWorld.java, which can be edited in the source code editor.

Editing files

When editing, you can type in characters or delete characters, words, and lines, but this is something that all editors can do. IDEs offer extra—they analyze the source code and format it, which, in turn, automatically indents the lines. It also continuously compiles the code in the background while you edit it, and if there is some syntax error, it underlines that with a red waiving line. When you fix the error, the red underlining disappears:

The editor also automatically gives suggestions for further characters as you type. You can ignore the window that pops up and continue typing. However, many times, it is easier to stop after a character and use the up and down arrows to select the word that needs finishing before pressing Enter; the word will be inserted into the source code automatically.

In the preceding screenshot, you can see that I wrote System.o, and the editor immediately suggested that I wanted to write out. The other alternatives are the other static fields and methods that are in the Systemclass and which contain the letter o.

The IDE editor gives you hints, not only when it can type for you, but also when it cannot type instead of you. In the following screenshot, the IDE tells you to type some expression as an argument to the println() method that is boolean, char, int, and so on. The IDE has absolutely no idea what to type there. You have to construct the expression. Still, it can tell you that it needs to be of a certain type:

It is not only the built-in types that the editor knows. The editor integrated with the JDK continuously scans the source files and knows what classes, methods, and fields are there in the source code and which of those are usable at the place of editing.

This knowledge is also heavily used when you want to rename a method or variable. The old method was to rename the field or method in the source file and then do an exhaustive search for all references to the variable. Using the IDE, the mechanical work is done by it. It knows all the uses of a field or method and automatically replaces the old identifier with the new one. It also recognizes whether a local variable happens to have the same name as the one that we rename, and the IDE only renames those occurrences that are really referring to the one we are renaming.

You can usually do more than just renaming. There are more or less mechanical tasks that programmers call refactoring. These are supported by the IDEs using some keyboard shortcut and context-sensitive menu in the editor—right-click on the mouse and click on Menu:

The IDE also helps you read the documentation of the libraries and source code, as shown here:

Libraries provide Javadoc documentation for the public methods, and you should also write Javadoc for your own method. Javadoc documentation is extracted from special comments in the source code, and we will learn how to create those in Chapter 4, Mastermind - Creating a Game. These are located in comments in front of the actual method head. As creating compiled documentation is part of the compilation flow, the IDE also knows the documentation, and it displays as a hovering box over the method names, class names, or whatever element you want to use in the source file when you position the cursor on the element.

Managing projects

To the left of the IDE window, you can see the directory structure of the project. The IDE knows the different types of files and shows them in a way that is meaningful from the programming point of view. For example, it does not display Main.java as a filename. Instead, it displays Main and an icon that signals that Main is a class. It can also be an interface still in a file named Main.java, but, in that case, the icon will show that this is an interface. This is again done by the IDE continuously scanning and compiling the code.

The files are structured into subdirectories when we develop a Java code. These subdirectories follow the packaging structure of the code. Many times, in Java, we use compound and long package names, and displaying it as a deep and nested directory structure will not be so easy to handle.

Note

Packages are used to group the source files. The source files for classes that are related in some way should go into one package. We will discuss the notion of packages and how to use them in the next chapter.

The IDE is capable of showing the package structure instead of the nested directories for those directories of the project that contain source files:

 

When you move a class or an interface from one package to another, it happens in a similar to how renaming or any other refactoring action takes place. All references to the class or interface in the source files get renamed to the new package. If a file contains an import statement referring to the class, the name of the class in the statement is corrected. To move a class, you can open the package and use the good old drag and drop technique.

Package hierarchy is not the only hierarchy displayed in the IDE. The classes are in packages but, at the same time, there is an inheritance hierarchy. Classes may implement interfaces and can extend other classes. The Java IDEs help us by showing type hierarchies where you can navigate across a graphical interface along the inheritance relations.

There is another hierarchy that IDEs can show to help us with development—method call hierarchy. After analyzing the code, the IDE can show us the graph displaying the relations between the methods: which method calls which other methods. Sometimes, this call graph is also important in showing the dependencies of methods on each other.

Building the code and running it

The IDEs usually compile the code for analysis to help us spot syntax errors or undefined classes and methods on the fly. This compilation is usually partial, covering a part of the code, and as it runs all the time, the source code changes and is never actually complete. To create the deployable file, that is, the final deliverable code of the project, a separate build process has to be started. Most of the IDEs have some built-in tool for that, but it's not recommended to use these, except for the smallest projects. Professional development projects use Ant, Maven, or Gradle instead.

Here's an example of Maven:

The IDEs are prepared to use such an external tool, and they can help us start them. This way, the build process can run on the developer machine without starting a new shell window. IDEs can also import the settings from the configuration file of these external build tools to recognize the project structure, where source files are, and what to compile to support error checking while editing.

The building process usually contains the execution of certain checks on the code. A bunch of the Java source file may compile nice and smooth. Still, the code may contain a lot of bugs and may be written in a terrible style. Those things make the project unmaintainable in the long run. To avoid these problems, we will use unit tests and static code analysis tools. These do not guarantee error-free code, but the chances are much slimmer.

IDEs have plugins to run the static code analysis tools as well as unit tests. Being integrated into the IDE has a huge advantage. When there is any problem identified by the analysis tool, or by some unit tests, the IDE provides an error message that also functions like a link on a web page. If you click on the message, which is usually blue and underlined, exactly like on a web page, the editor opens the problematic file and places the cursor where the issue is.

Debugging Java

Developing code needs debugging. Java has very good facilities to debug code during development. JVM supports debuggers via the Java Platform Debugger Architecture. This lets you execute code in debug mode, and JVM will accept external debugger tools to attach to it via a network, or it will try to attach to a debugger depending on command-line options. JDK contains a client, the jdb tool, which contains a debugger; however, it is so cumbersome to use when compared to the graphical client built into the IDEs that I have never heard of anyone using it for real work.

To start a Java program in debug mode so that JVM will accept a debugger client to attach the options to it, execute the following command:

-Xagentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=7896

The Xagentlib option instructs the Java runtime to load the jdwp agent. The part of the option that follows -Xagentlib:jdwp= is interpreted by the debugger agent. These options are as follows:

  • transport: This should specify which transport to use. It can be a shared memory (dt_shmem) socket or a TCP/IP socket transport but, in practice, you will always use the latter. This is specified in the preceding dt_socket sample.
  • server: This specifies if the debugged JVM starts in server mode or client mode. When you start the JVM in server mode, it starts to listen on a socket and accepts the debugger to connect to it. If it is started in client mode, it tries to connect a debugger that is supposed to be started in server mode, listening on a port. The value of the option is y, meaning server mode, or n, meaning nonserver, which is client mode.
  • suspend: This can also be y or n. If JVM is started in suspend mode, it will not start the Java code until a debugger is attached to it. If it is started with suspend=n, the JVM starts and when a debugger attaches, it stops as soon as a break point is reached. If you start a standalone Java application, you will usually start the debugging with suspend=y, which is the default. If you want to debug an application in an application server or servlet-container environment, it is better to start with suspend=n; otherwise, the server does not start until the debugger attaches to it. Starting the Java process in the suspend=y mode in case servlet application is only useful when you want to debug the servlet static initializer code, which is executed when the server is starting up. Without the suspend mode, you will be required to attach the debugger very fast. It is better that JVM just waits for you in that situation.
  • address: This should specify the address that JVM communicates with. If the JVM started in client mode, it will start to connect to this address. If the JVM runs in server mode, it will accept connections from the debugger on that address. The address may specify only the port. In this case, the IP address is that of the local machine.

The other options the debugger agent may handle are for special cases. For the topics covered in this book, the preceding options are enough.

The following screenshot shows a typical debugging session where we debug the simplest program in IntelliJ IDE:

When you start a program from the IDE in debug mode, all of these options are automatically set for you. You can set a break point just by clicking on the source code in the editor. You can have a separate form to add, remove, and edit break points. Break points can be attached to specific lines or specific events, like when an exception is thrown. Break points attached to a specific line can also have conditions that tell the debugger to stop the execution of the code, but only when the condition is true; for example, if a variable has some predefined value.

 

Summary


In this chapter, we were introduced to each other with Java. We do not know too much about each other but we got acquainted. We installed the Java environment: Java, JDK, and integrated development environment. We wrote a small program and took a brief look at what can be done using the development tools. This is far from mastery, but even the longest journey starts with a first step, which is sometimes the hardest to take. We have done it in our Java journey. We started rolling and, for the enthusiasts that we are, nothing can stop us walking all the way along.

About the Author

  • Peter Verhas

    Peter Verhas is a senior software engineer and software architect with an electrical engineering and economics background from TU Budapest (MsC) and PTE Hungary (MBA), and he also studied at TU Delft and TU Vienna. He created his first programs in 1979, and since then he has authored several open source programs. He has worked in several positions in the telecommunications and finance industries.

    Peter works for EPAM Systems in Switzerland, participating in software development projects at various customer sites, and he supports talent acquisition by interviewing candidates, running training programs for developers, and internal mentoring programs. He regularly talks at various international conferences.

    Browse publications by this author

Recommended For You

Java Coding Problems

Develop your coding skills by exploring Java concepts and techniques such as Strings, Objects and Types, Data Structures and Algorithms, Concurrency, and Functional programming

By Anghel Leonard
Java 11 Cookbook - Second Edition

Solutions for modular, functional, reactive, GUI, network, and multithreaded programming

By Nick Samoylov and 1 more
Hands-On Design Patterns with Java

Understand Gang of Four, architectural, functional, and reactive design patterns and how to implement them on modern Java platforms, such as Java 12 and beyond

By Dr. Edward Lavieri
Java Fundamentals

Enhance your career options with this well-crafted object-oriented programming language that enjoys the support of an enormous ecosystem of tools and libraries

By Gazihan Alankus and 4 more