Hello Java! with MacOS
Published on

Hello Java! with MacOS

Authors

Introduction

Java has been there for more than 20 years. If you are not sure what Java is? and you're thinking if its worth learning don't worry. Its going to be there for at least next 20 years. Most companies are still looking for Java developers as they need to migrate there code to the new cloud world. And there is still a lot of active development going on in Java.

Please check this post if you want to learn more about Java and its usage, etc.

To install Java into your MacOS we'll be using HomeBrew package manager.

Lets install Java 17 which is current LTS version. If you're thinking what's LTS its Long Term Support version. Because syntax of Java can change from version to version slightly its always good to start developing your application on LTS rather than the latest version as there may be some minor bugs.

brew install openjdk@17

Once Java is installed you’ll need to make sure you install package manager or build tool for Java i.e. Lets install maven which is widely used by many projects/companies.

brew install maven

Folder structure for your java projects

All Java projects needs to follow the below folder structure so that maven can build them correctly. If you don't follow this standard practice then you'll have to update a lot of configurations in pom.xml and some IDE's might not understand the project structure and then it will become hard for you to develop or share code.

java-project
-->src
---->main/java
------->.java project files
---->test/java
------->.java test files
-->pom.xml

src/main/java would hold all your project source codes and src/test/java would hold all your test classes. pom.xml is your maven configuration for the project, everything related building your project i.e. project lifecycle.

pom.xml example structure and explanation

Below is the minimum pom.xml configuration that must be used for any new project that you’re going to create. I’ll explain the properties in more detail in the below sections.

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.codingjump</groupId>
    <artifactId>hello-world</artifactId>
    <version>0.0.1</version>
    <name>Hello World Java Project</name>
    <description>Hello World Project</description>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>5.9.1</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>3.1.0</version>
                <configuration>
                    <archive>
                        <manifest>
                            <addClasspath>true</addClasspath>
                            <mainClass>HelloWorld</mainClass>
                        </manifest>
                    </archive>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

Every pom.xml would start with a <project> tag and it would contain multiple fields like <build>, <dependencies>, <properties>, <groupId>, <artifactId>, <version>, <description>, etc. Of which some are required fields and others are optional.

Lets go through these fields in more detail.

<groupId>

This is similar to package name where we put our company web url in reverse like com.codingjump and should be all lowercase, we might have many projects and you want to differentiate projects of each department then you add one more field with . as separator for example, com.codingjump.admin, com.codingjump.finance, etc.

<artifactId>

This is like a project name and it must not have any spaces between names. Suggested to use only lowercase letters. Lets take a look at above pom.xml example we have separated the words with hyphen - for hello-world.

<version>

This defines the version of the library or project so that we can differentiate between releases.

Most projects need one or more dependencies which are in-turn Java projects we refer to these projects with groupId, artifactId and version in <dependencies> section in our pom.xml file. Incase if there is any issue with a certain dependency then we can change its version if it got fixed in the latest release. So maintaining/changing versions is very important.

We have two types of versions SNAPSHOT and normal release version. Snapshot version is something we you don't release to production and it remains fixed i.e. whenever you change your code you don't have to keep creating new versions we can just make use of our same Snapshot version. The maven or IDE would pick the latest build.

Release version don't have -SNAPSHOT suffixed to it. If you change your code then you must make sure to change your version number otherwise your new code might not be picked by maven or IDE.

You have a lot of flexibility in terms of naming your versions but most follow version number of 3 dots i.e. MAJOR.MINOR.INCREMENT

<name>

We can provide the name of the project with spaces in a human-readable "friendly" name. It is not a required field.

<description>

Here we provide the description of what the project does so that the developer who is reading this would understand what this project is all about. We don’t have to give too much details but giving about 50 to 100 words of description would make sense.

<properties>

This section holds all the properties that are used within the pom.xml. This is used mostly for overriding the dependency version of parent pom or sometimes we use a number of artifacts from same groupId. Normally companies release their artifacts with same version. So it doesn’t make sense to hard code the version and change it in every place. Instead of that we can just change the variable with the new version, etc.

We can create custom tags in properties for example cj.version it can be anything just make sure that there are no spaces and also keep the text in lowercase. Now we can make use of this anywhere in the pom.xml by ${cj.version}

   <properties>
        <cj.version>1.0.1</cj.version>
    </properties>

<dependencies>

All your project dependencies are placed in this field. Each dependency is of the below format.

<dependency>
    <groupId>...</groupId>
    <artifactId>...</artifactId>
    <version>...</version>
    <scope>compile/provided/runtime/test/system/import</scope>
</dependency>

We have a test dependency to JUnit 5 for our test cases. As this is a sample hello-world application we'll have to add more dependencies as per our application needs. As this is just a building block for any application.

<build>

In this field you can specify a lot of other fields like sourceDirectory, testSourceDirectory, etc. But the most important field is plugins where you can configure your build steps for your project with multiple open-source plugins.

We have used the maven-jar-plugin which is used to create a executable jar file by defining our mainClass.

src/main/HelloWorld.java

This file just prints out Hello World!!! to the console.

src/main/HelloWorld.java
public class HelloWorld {
    public static String sayHello() {
        return "Hello World!!!";
    }
    public static void main(String[] args) {
        System.out.println(sayHello());
    }
}

In Java everything must start and end with a class. This is what we call Object Oriented Programming 😜. For reference we can assume that class is a template and then from that we create an object. There are many different modifiers for a class but just remember that in a static class there is only one object that gets initialized when your program starts. For other types of class we'll need to create object first before using the methods. Also static methods can be called directly using their class name.

So here we can see that we are calling sayHello() directly in our System.out.println function which will return Hello World!!! for standard output i.e. console.

You will have to understand Java terminology/syntax perfectly to really write useful code. I hope you have already done so if not put your learning hat on and goto youtube.com for learning the basics first.

Testing your code inside src/test folder

We can create multiple test classes in our src/test folder. These classes are used to test our code which is present inside src/main folder.

Many people don't know the importance of testing they think that it needs to done only after projects are built, but this is not true. Doing programming is like learning a new language i.e. you are not sure if your function or the code what you write would work. So after writing code you need to make sure if your code works as expected. But you can't do so unless you run your code, sometimes you need a lot of inputs to run your program which cannot be simulated unless your complete your project. Average lifespan or time taken to code any simple project would be at least weeks if not months/years. So if you're going to hold your onto testing code till project end, then you're going be in for lot of surprises and headache as you'll encounter many bugs.

The key to this is unit testing your code while development. For every function you write you'll need to write unit test cases. Doing so would give you more confidence in your coding and also final product will have less bugs as you know all the internal parts are working correctly.

Here is the sample test case code.

src/test/HelloWorldTest.java
import static org.junit.jupiter.api.Assertions.assertEquals;

import org.junit.jupiter.api.Test;

public class HelloWorldTest {
    @Test
    public void testHello() {
        assertEquals("Hello World!!!", HelloWorld.sayHello());
    }
}

All test classes must end with a suffix Test. Normally, when testing a particular class we name test class with the same name including the suffix Test.

A test class can have multiple test methods each method should be annotated with @Test, so that IDE and build tools can identify that it is a test method.

We are using JUnit5 for testing. So we'll need add this as a test dependency in our pom.xml

<dependency>
	<groupId>org.junit.jupiter</groupId>
	<artifactId>junit-jupiter-engine</artifactId>
	<version>5.9.1</version>
	<scope>test</scope>
</dependency>

To run our test cases in maven you can use mvn test in your project folder or if you want to just run a specific test you can do so with your IDE interface, but each IDE is slightly different so you'll need to look into how you can run the test cases from your IDE.

Conclusion

Hope now you have some clarity on how to create your first Hello World application in Java. I've just gone through the basic support code for running your java project. I wrote this post because I wanted to give you a glimpse of what actually Java project looks like. Many posts don't explain these basics and assume that you know everything about maven, pom.xml, etc.

Please refer to example application code present in GitHub for working example