In this section I explore the basic fundamentals of Java.
The goal is to use the Java programming language to build software systems, cloud solutions and/or any systems that solve real world problems
I start from the very basics and build up to more complex projects.
Below is what is covered in the guide, with links directing to the specific sections.
We can print to the standard output using the System.out.print()
or System.out.println()
command.
System.out.print("Hello world");
System.out.println("This is another string");
To get the input from a user, we use the Scanner
class.
Scanner scanner = new Scanner(System.in);
System.out.println("Enter your string input:");
string_in = scanner.nextLine();
System.out.println(string_in);
In Java there are two classes of data types:
Below is a list of primitive data types
int x = 5; // This is an integer
byte w = 2; // This is a byte
short y = 100; // This is a short
long z = 10000000; // This is a long
float a = 12.34f; // This is a float. A float ends with an 'f' character
double b = 12345.6789d; // This is a double, it ends with a 'd' character
boolean bool = true; // This is a boolean
char character = 'a'; // This is a character
Reference data types have access to useful methods, which give them more functionalities:
Some Reference data types in Java are:
Some methods that can be used with strings are as shown below:
String test = "test";
test.toUpperCase();
test.toLowerCase()
test.length();
test.concat();
Wrapper classes are used to convert primitive data types to reference data types, which then allows them to access nethods for extra operations, or to be used in collections such as ArrayLists. The downside of using wrapper classes for reference data types is that they are slower compared to primitive data types, as they have extra steps to access data.
The conversion of promitive data types to reference classes is known as Autoboxing, while the inverse is known as unboxing.
Primitive data types can be used as reference data types as below:
Integer i = 2; // for int
Long l = 123451234L; // for long
Character c = 'c'; // for char
Boolean b = true; // for boolean
Float f = 1.231F; // for float
Double d = 12.123412; // for double
// You can now access the methods as below
i.equals(23);
l.hashCode();
c.toString();
b.booleanValue();
f.byteValue();
d.isInfinite();
If using Intellij IDE, you can see a list of methods for a wrapper class on entering the variable name followed buy a dot, ie in the example below, b.
final
is a Java keyword that is applied as a constant, and can be applied to:
On variables, the final keyword is used where a value is assigned to a variable once, after which the value can not be re-assigned. A very common case is in the use of scientific values such as Pi (3.142), Earth gravity (9.8).
final String Pi = 3.142;
On Methods, the final keyword is used where one does not want it to be overriden eg when defined in a class.
public final String testFunction(){
return "This is a test function";
}
On classes, the final keyword is used where one does not want a class to be inherited. You can therefore not create sub-classes from classes that are final.
public final class testClass {
public void testMethod() {
System.out.println("test method");
}
}
Arithmetic operators are used for mathematic operations such as addition, subtraction etc. Below is a list of the common arithmetic operations
Operator | Name |
---|---|
+ | Addition |
- | Subtraction |
* | Multiplication |
/ | Division |
% | Modulus |
++ | Increment |
-- | Decrement |
As the name suggests, the assignment operators are used to assign values to variables. Below is an example of the common assignment operators
= | x = 2 | |
---|---|---|
+ | x += 5 | Addition |
- | x -= 5 | Subtraction |
* | x *= 5 | Multiplication |
/ | x /= 5 | Division |
% | x %= 5 | Modulus |
Again as the name suggests, comparison operators are used to compare two variables or values. They return a boolean value ie, true
or false
== | Equal to |
---|---|
!= | Not equal to |
< | Less than |
> | Greater than |
<= | Less than or equal to |
>= | Greater than or equal to |
These operators check the logic between variables or values
&& | AND |
---|---|
|| | OR |
! | NOT |
if
statements are used to execute a block of code if a certain condition is met.
If statements in java are written as below:
if (condition) {
// statement 1
} else {
// statement 2
}
Example if an if
statement:
int x = 3, y = 5;
if (x > y) {
System.out.println("(If) x is bigger than y");
} else {
System.out.println("(if) y is bigger than x");
}
A switch
statement is used to test a variable against a list of values. Switch
statements can be used instead of if
statements if we have many comparison statements.
Switch statements are written as below:
switch (variable){
case comparison-1 :
// statement
// statement
break
case comparison-2 :
// statement
break
default :
// default statement if none of the comparisons are equal to the variable
break;
}
Below is an example:
int number = (int) (Math.random() * 40);
System.out.println("Number : " + number);
switch (number) {
case 1:
System.out.println("one");
break;
case 2:
System.out.println("two");
break;
case 3:
System.out.println("three");
break;
default:
System.out.println("none");
break;
}
break
vs continue
break
exits a loop or a statement while continue
skips a loop or statement.
A while loop executes a block of code continuously as long as a certain condition is met.
A while loop is written as:
while (condition) {
// statement
// statement
}
we also have a do-while
loop, which does what the while
loop does. The only difference is that the do-while
loop will execute the code block at least once before running the comparison/condition.
do {
// statement
// statement
} while (condition)
A good example for implementing a while
is a prompt that asks for an input, and will keep asking for an input if none is given. This is expressed programmatically as below:
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
System.out.println("Please enter your username to continue:");
String cli_input = input.nextLine();
// while loop
while (cli_input.isBlank()){
System.out.println("Please enter your username to continue:");
cli_input = input.nextLine();
}
System.out.println("Welcome to the program " + cli_input);
}
We can also implement the above code with a do-while
loop as below.
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
String cli_input = "";
do {
System.out.println("Please enter your username to continue:");
cli_input = input.nextLine();
} while (cli_input.isBlank());
System.out.println("Welcome to the program " + cli_input);
}
for-loops
are statements that executes a block of code over a set number of times.
The basic structure of a for-loop
is:
for (start-index ; end-index ; increment){
// statement
}
// eg
for (int i = 0 ; i <= 10 ; i++){
// statement
}
As an example, we can print out a string a set number of times as shown below, which increments in the first example, and decrements in the next example.
// incrementing from 0 to 20
for (int i = 0 ; i <=20 ; i++){
System.out.println(i);
}
// decrementing from 20 to 0 in intervals of 2
for (int i = 20 ; i >=0 ; i-=2){
System.out.println(i);
}
We can also have a loop within a loop, which is known as a nested loop
Methods are code blocks that are executed when called or invoked. This is useful as you do not have to repeat a block of code that is repetitive.
void testMethod(){
System.out.println("This is a test method");
}
An arrays
is a variable that is used to store multiple values.
A normal array would be declared as:
int number = 7;
While an array would be declared as:
int[] numbers = {1, 2, 3, 4};
We can alternatively declare an array by:
String letters = new String[90];
if we want to put in the values of the array later.
We can then fill in values in the array by:
letters[0] = "a";
letters[1] = "b";
letters[2] = "c";
Arrays can be accessed by the index of the value, where the first value has an index of 0. The first value will then be accessed by:
numbers[0];
which can be printed out to the standard output as
System.out.println(numbers[0]);
We can also loop through all the values of an array by using a for-loop
or a for-each
loop.
public static void main(String[] args) {
// declare an array "numbers"
int[] numbers = new int[5];
// fill in the values in the array
numbers[0] = 121;
numbers[1] = 39;
numbers[2] = 345;
// loop through the array with a forloop
for (int i = 0 ; i < numbers.length ; i++){
System.out.println("The number in index:" + i + " is -> " + numbers[i]);
}
// loop through an array with a for-each loop
for (int i : numbers){
System.out.println(i);
}
}
/*
Result:
The number in index:0 is -> 121
The number in index:1 is -> 39
The number in index:2 is -> 345
The number in index:3 is -> 0
The number in index:4 is -> 0
121
39
345
0
0
*/
Arrays can only be of one data type, therefore combining different data types would bring in an error.
2D arrays are basically arrays within arrays.
Considering a normal array is defined as:
int[] numbers = new int[4];
A 2D array is defined as:
int[][] numbers = new int[3][5];
A good way to remember the structure of 2D arrays is to consider the structure of a table, ie
int[][] => int[rows][columns]
Therefore : int[][] numbers = new int[3][5];
would have 3 rows and 5 columns
We can then loop through a 2D array using two for
loops. Consider a 2D array:
// we instantiate a 3 row, 2 column array
String countries[][] = new String[3][2];
// then enter the data into the array via the index
countries[0][0] = "Kenya";
countries[0][1] = "Ethiopia";
countries[0][0] = "Nigeria";
countries[0][1] = "Ghana";
countries[0][0] = "South Africa";
countries[0][1] = "Zambia";
// we can use a for loop to loop through the data as below
for (int i = 0 ; i < countries.length ; i ++){
System.out.println();
for (int j = 0 ; j < countries[i].length ; j ++) {
System.out.println(countries[i][j]);
}
}
// or use a for-each loop to run through all the data
for ( String[] x : countries){
System.out.println();
for ( String y : x){
System.out.println(y);
}
}
Array lists are arrays that are resizeable after compilation. In comparison to regular arrays, they do not take primitive data types, and only take in Reference data types. In the event that one wants to use primitive data types, they would have to be converted to reference data types using wrapper classes.
Below is the general operations of ArrayLists:
// create a String array list
ArrayList< String > cars = new ArrayList< String >();
// add entries to the array list
cars.add("Audi");
cars.add("Honda");
cars.add("Toyota");
// loop through an array list with a for loop
for (int i = 0 ; i < cars.size() ; i++){
System.out.println(cars.get(i));
}
// loop through an arraylist with a foreach loop
for (String i : cars){
System.out.println(i);
}
/*
Output from the for loop:
Audi
Honda
Toyota
*/
// edit the content of an arrayList
cars.set(2, "Range Rover"); // replaces "Toyota"
// print out the array list
System.out.println(cars);
/*
Output from the for loop:
Audi
Honda
Range Rover
*/
// remove an entry from the arrayList
cars.remove(1); // remove the entry on index 1, (Honda)
// print out the array list
System.out.println(cars);
/*
Output from the for loop:
Audi
Range Rover
*/
// clear the entire arrayList (removes all entries)
cars.clear();
Just like 2D Arrays, 2D ArrayLists are lists within lists.
Let's take the example of arrayLists of different countries from different continents. We first create the arrayLists of the countries in each continent, then add the seperate arraylists into another arraylist.
// create an arraylist for africa and add countries
ArrayList< String > Africa = new ArrayList< String >();
Africa.add("Kenya");
Africa.add("Uganda");
Africa.add("Ethiopia");
// create an arrayList for asia and add countries
ArrayList< String > Asia = new ArrayList< String >();
Asia.add("India");
Asia.add("Indonesia");
Asia.add("Malaysia");
Asia.add("Thailand");
// create an arrayList for europa and add countries
ArrayList< String > SouthAmerica = new ArrayList< String >();
SouthAmerica.add("Brazil");
SouthAmerica.add("Colombia");
SouthAmerica.add("Peru");
// create a 2D arrayList
ArrayList< ArrayList< String > > countries = new ArrayList< >();
// add the arraylists to the 2d arraylists
countries.add(Africa);
countries.add(Asia);
countries.add(SouthAmerica);
System.out.println(countries);
/*
* Result:
* [[Kenya, Uganda, Ethiopia], [India, Indonesia, Malaysia, Thailand], [Brazil, Colombia, Peru]]
* */
We can then loop through the arraylist with a for
loop as below
for (int i = 0 ; i < countries.size() ; i ++){
for (int j = 0 ; j < countries.get(i).size() ; j++){
System.out.println(countries.get(i).get(j));
}
}
Or with a for-each
loop as:
for (ArrayList i : countries){
for (String j : i){
System.out.println(j);
}
}
Object oriented programming (OOP) is the concept of using Objects and Classes, where classes are blueprints of classes, and classes are instances of objects.
Objects in Java would therefore be programatic representations of real life items/entities eg people, houses, cars, countries etc. This therefore means that each item would have attributes and behaviours/actions (methods) ie in the case of a car, it would have attributes such as color, engine, number of doors etc, while also having behaviours such as driving, braking etc.
We can therefore represent the car described above as below programatically:
public class Car {
// attributes
String name;
String engine;
int cc;
// constructor
public Car(String name, String engine, int cc){
this.name = name;
this.engine = engine;
this.cc = cc;
}
// Getters
public String getName(){
return name;
}
public String getEngine(){
return engine;
}
public int getCc(){
return cc;
}
// Setters
public void setName(String name){
this.name = name;
}
public void setEngine(String engine){
this.engine = engine;
}
public void setCc(int cc){
this.cc = cc;
}
public void startCar(){
System.out.println("starting the engine of " + name)
}
}
The Car class can then be instantiated and called as below:
public static void main(String[] args) {
// instantiate the class by creating an object
Car honda = new Car("Honda", "v8", 2000);
// getting the attributes
System.out.println(honda.getName());
System.out.println(honda.getEngine());
System.out.println(honda.getCc());
// setting the attributes
honda.setName("Honda CRV");
honda.setEngine("v8 Hybrid");
honda.setCc(2500);
// getting the attributes aging to see the changes made by the setters
System.out.println(honda.getName());
System.out.println(honda.getEngine());
System.out.println(honda.getCc());
// calling a method
honda.startCar();
}
A constructor is a special method that is used to create new objects. So as the name suggests, they construct new objects.
If we had a class Planet...
public class Planet {
String name;
int moons;
}
We would instantiate it (creating an object), as below:
Planet earth = new Planet();
From the above, Planet();
is actually calling a Planet constructor method, which creates a new earth object.
As we can see, we do not have a method defined in the Planet class. In this case Java has created the constructor method for you, and is usually defined as the default null args constructor. Therefore Java has created an object that is empty.
This means that after creation of an object earth, if we try earth.name
we get a null result.
System.out.println(earth.name); // result shall be null
System.out.println(earth.moons); // result shall be null
We can now create a custom constructor in the class as below:
public class Planet {
String name;
int moons;
public Planet(){
}
}
A constructor is like an ordinary method, but the name will always match the class name, i.e the Planet class has the Planet() constructor. Also note that constructors have no return types (no int, string or void)
We can move a step further and now create default values of the objects we create from classes. Now with the Planet class, we can create an object with custom values as below:
public class Planet {
String name;
int moons;
public Planet(String name, int moons){
this.name = name;
this.moons = moons;
}
}
or
public class Planet {
String name;
int moons;
public Planet(String newName, int newMoons){
name = newName;
moons = newMoons;
}
}
Let's take another example of Anime. An anime would have a name, genre, subtitles and episodes. We would also have methods such as watching, setSubtitles and removeSubtitles. With the above attributes and methods, we would have the class represented as below:
public class Anime {
// attributes
String name;
String genre;
String subtitles;
int episodes;
// methods + getters + setters
public void watching(boolean status){
if (status == true){
System.out.println("Currently watching the " + name + " anime.");
} else {
System.out.println("Currently NOT watching the " + name + " anime. On a break");
}
}
public void setSubtitles(String language){
subtitles = language;
System.out.println("The subtitles are now set to: " + language);
}
public void removeSubtitles(){
subtitles = "none";
}
public String getSubtitles(){
if (subtitles == "none"){
return "There are no subtitles set for the anime: " + name;
} else {
return "The subtitles for the anime " + name + " is : " + subtitles + " language";
}
}
}
On creating an object from the class as below, we get an unexpected result:
public static void main(String[] args) {
Anime bleach = new Anime();
bleach.watching(true);
}
/*
output : Currently watching the null anime.
*/
This is where we see the importance of constructors. The above result should have been "Currently watching bleach anime"
, but we get null instead of bleach
Creating an object from the above example, we use Anime bleach = new Anime();
. In this case Anime()
is our constructor (default constructor). However we have not passed any parameters, hence getting the null
result on creating the object (instantiating the class).
We can bypass this challenge by using the setters when we create the object. however this can be a challenge if we have many attributes to work with.
With constructors we can set default values for our objects while we create them. This is illustreted below with the Anime class:
public class Anime {
String name;
String genre;
String subtitles;
int episodes;
// class constructor
Anime( String name, String genre, String subtitles, int episodes){
this.name = name;
this.genre = genre;
this.subtitles = subtitles;
this.episodes = episodes;
}
public void watching(boolean status){
if (status == true){
System.out.println("Currently watching the " + name + " anime.");
} else {
System.out.println("Currently NOT watching the " + name + " anime. On a break");
}
}
public void setSubtitles(String language){
subtitles = language;
System.out.println("The subtitles are now set to: " + language);
}
public void removeSubtitles(){
subtitles = "none";
}
public String getSubtitles(){
if (subtitles == "none"){
return "There are no subtitles set for the anime: " + name;
} else {
return "The subtitles for the anime " + name + " is : " + subtitles + " language";
}
}
}
with the constructor being:
Anime( String name, String genre, String subtitles, int episodes){
this.name = name;
this.genre = genre;
this.subtitles = subtitles;
this.episodes = episodes;
}
Now so far we have learnt what constructors are and how to use them. Something extra to note, is that once we create a constructor with default parameters, we have to pass the required parameters when creating the object. However, even after having a constructor with the default values, we can still instantiate the class without any default values by having an empty constructor, or have a constructor with selected parameters. This concept is called constructor overloading.
class Smartphone {
String companyName;
String processor;
public Smartphone(){
}
public Smartphone(tring companyName){
this.companyName = companyName;
}
public Smartphone(String companyName, String processor){
this.companyName = companyName;
this.processor = processor;
}
public void details(){
System.out.println("The phone details are: company name: " + companyName +
", processor: " + processor);
}
}
public class Test {
public static void main(String[] args) {
Smartphone nokia = new Smartphone();
nokia.details(); // result: The phone details are: company name: null, processpr: null
Smartphone iphone = new Smartphone("Apple", "Snap dragon");
iphone.details(); // result: The phone details are: company name: Apple, processpr: Snap dragon
}
}
The above code will return The phone details are: company name: null, processpr: null for the nokia object, since we have created the object without any parameters.
The iphone object will however have no null values as we passed parameters while we creating the object.
Access modifiers are meant to control access of data from different files, classes, packages etc.
We have the following access modifiers:
We can summarise the above as the below:
Modifier | Class | Package | child-class | Global |
---|---|---|---|---|
Public | ||||
Protected | ||||
Default | ||||
Private |
The toString()
is a method that returns the textual representation/description of an object, which by default is the location of the object in memory.
Every object can call the toString
method, because all classes in Java extend another class called Object (which in turn has the toString method. In addition, one can also override the method to create a custom string.
// the toString method as definded in the Object class
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
To use the method, one can append the toString()
statement to a method.
Animal lion = new Animal();
System.out.println(lion.toString);
// alternatively
System.out.println(lion); // this will give the same result as the above
To override the default toString()
method, put in a custom method in the class as below.
public String toString(){
return "this is the default toString method";
}
Abstraction, Inheritance, Polymorphism and Abstraction are deemed to be the four main pillars in object oriented programming. I cover aspects of these four pillars in This section
© Copyright Kifaru Codes. All Rights Reserved.
Designed by KifaruCodes