DEV Community

Cover image for Lessons from a Month Learning Java
Alex Ortiz
Alex Ortiz

Posted on

Lessons from a Month Learning Java

I started learning Java a month ago. Here are five simple programs I've written. I'll use these as hooks to share what I've learned in that time.

Program Table Of Contents
Program 1: Two Languages
Program 2: Even Odd While Loop
Program 3: The Nth Letter of the Alphabet
Program 4: Combine Two Arrays
Program 5: Prime Checker

Program 1: Two Languages

This program prints one of two sentences, depending on which programming language I specify: "python" or "java".

/*
A program that prints a different sentence depending on which of two predefined programming languages the user inputs.
*/

import java.util.Scanner; // import the Scanner class

class TwoLanguages {
  public static void main(String[] args) { // program execution begins

    String python = new String(); // declare the python variable
    python = "Python"; // define the python variable

    String java = new String(); // declare the java variable
    java = "Java"; // define the java variable

    Scanner keyboardInput1 = new Scanner(System.in); // declare a Scanner object to accept user input

    System.out.println("Which language are you asking about?"); //  prompt the user for input

    String i = keyboardInput1.nextLine(); // declare a variable for storing the user's input

    // An if-else-if ladder to determine the program's response.
    if (i.equalsIgnoreCase("Python")) {
      System.out.println("Python is a great language for newcomers to programming! It's easy to pick up and has a huge developer community surrounding it.");
    }

    else if (i.equalsIgnoreCase("Java")) {
      System.out.println("Java is a language after my own heart; my brain `gets` it! I think it will serve me well in the 2020s for the scientific work I want to do.");
    }

    else {
        System.out.println("The program doesn't understand. Please try again.");
      }
    }
}

The code above is in a .java file named TwoLanguages.java. That code is uncompiled, so I can't run it on my computer. The computer doesn't understand words written in English, it understands machine code. So I need to compile my program into something the computer can work with.

To do that, I run the command javac TwoLanguages.java from my Terminal. This compiles the Java code into bytecode, which is one step removed from the machine code my computer can understand. Then I can run my program with the command java TwoLanguages.


There are really three things going on here:

(1) The Java compiler (javac) takes .java files as input and converts them to .class files as output. The .java file is the uncompiled code. The .class file is the compiled (byte)code.

(2)(3) When I run java TwoLanguages, what I'm doing is telling my machine (via the java interpreter) to convert the bytecode into machine code and then execute that machine code on the Java Virtual Machine installed on my computer. So there are two conversion steps and one execution step. Only then does my computer do what I expect it to do based on my program.


My Expected Output

When I run java TwoLanguages from my terminal, I expect it to ask me a question:

"Which language are you asking about?"

I'll then be able to type in one of two words, which I already know are "python" and "java". Depending on which word I enter, the program will print a different statement. If I enter something other than "python" or "java", my program will tell me that it doesn't understand and ask me to try again.

Example Outputs

Example 1:

java TwoLanguages
Which language are you asking about?
python
Python is a great language for newcomers to programming! It's easy to pick up and has a huge developer community surrounding it.

Example 2:

java TwoLanguages
Which language are you asking about?
java
Java is a language after my own heart; my brain `gets` it! I think it will serve me well in the 2020s for the scientific work I want to do.

Example 3:

java TwoLanguages
Which language are you asking about?
blah
The program doesn't understand. Please try again.

Awesome!

Notes

Workflow

  • I write my programs in Atom. I've tried IDEs, like IntelliJ. But for where I am in my learning process, Atom is perfect
  • I'm using my command line for compilation and execution commands. So javac TwoLanguages.java, java TwoLanguages, and my user input all go through my Terminal
  • When I need to rapidly prototype a line of code, I use the ever-handy jshell

Code

  • The line Scanner keyboardInput1 = new Scanner(System.in); is critical to how this program works. It declares an object (keyboardInput1) of the Scanner class. The Scanner class lets the program read text input by the user or stored in a file. In this case, I use System.in to inform the program that my Scanner object is going to read from standard input
  • The line String i = keyboardInput1.nextLine(); tells the program to read the string the user enters (when pressing Enter) and "store" that string in an object named i
  • nextLine() isn't strictly necessary here; I could also have used next() for this particular scenario
  • When I enter my word, the case doesn't matter. I can type with lowercase, uppercase, sentence case, camelCase, or any other case. So "java", "JAVA", "Java", and "jAva" all work. That's because my code uses the equalsIgnoreCase() method: i.equalsIgnoreCase("Java"). Whatever I type into the terminal is stored in i, and the equalsIgnoreCase() method tells the program to evaluate the word regardless of its case. Had I used equals() instead of equalsIgnoreCase(), the program would have only worked as intended if I typed "Java" exactly

Program 2: Even Odd While Loop

This program prints all integers from zero to ten (inclusive) and tells me if each number is even or odd.

/*
A program that prints even and odd numbers from zero to ten using a while loop with a nested if statement.
*/

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

    int x = 0; // a variable to store the starting point, the integer 0

    while (x < 11) { // set a max of x = 10

      if (x % 2 == 0) { // if dividing by two leaves no remainder
        System.out.println(x + " is an even number.");
        x++; // increment the current value of x by 1
      }

      else {
        System.out.println(x + " is an odd number.");
        x++; // increment the current value of x by 1
      }
    }
  }
}

My Expected Output

When I run my EvenOddWhileLoop program, I expect it to print the numbers "0" to "10" and tell me if each number is an even number or an odd number.

Example Output

java EvenOddWhileLoop
0 is an even number.
1 is an odd number.
2 is an even number.
3 is an odd number.
4 is an even number.
5 is an odd number.
6 is an even number.
7 is an odd number.
8 is an even number.
9 is an odd number.
10 is an even number.

It worked!

Notes

Workflow

  • I found it useful to think about what I want the program to do, before I sat down to code the program. Visualizing the code that I'd type, before I wrote it in Atom, was a fun way to engage my brain on what problem I was actually trying to solve. The coding itself was then faster. This is a habit I'll keep as I learn more Java

Code

  • I could have also set the max value with while (x <= 10). But while (x < 11) is more economical
  • This combo of an if statement nested within a while loop is versatile. The inner if portion tells the program what action to take on every number. The outer while portion tells the program when to stop attempting further action (when x = 10, in this case)
  • I use the modulus operator % along with the relational operator equal to (==) as a proxy for the condition "is even". So x % 2 == 0 is how I tell the program that if x has no remainder when divided by 2, then x is an even number. (Read aloud, the full if statement if (x % 2 == 0)...{System.out.println(x + " is an even number."); is "If x modulo 2 is equal to zero, print the statement. x " is an even number.")
  • Each value falls into one of only two possible categories: even or odd. So the else clause is simpler than the if clause and doesn't require a condition within parentheses. The reason is that if the current value of x fails to qualify as even, then it must be odd. With this insight, there's probably a way to use the boolean data type, but I didn't think of that when writing the program
  • With the lines int x = 0; ...while (x < 11)..., my program predefines the half-open interval [0,11). A more flexible, hands-on approach is to use the Scanner class to read values from standard input via System.in like with Program #1 (TwoLanguages). Below is a modified version of EvenOddWhileLoop that allows the user to enter minimum and maximum values to define a custom closed interval, [a,b]:
/*
A program that accepts user input for the minimum and maximum values in an arbitrary integer interval, then prints the numbers and specifies if they are even or odd.
*/

import java.util.Scanner;

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

    Scanner lowerBound = new Scanner(System.in);
    System.out.println("Enter a minimum:");
    int a = lowerBound.nextInt(); // a variable to store the lower bound of the interval

    Scanner upperBound = new Scanner(System.in);
    System.out.println("Enter a maximum:");
    int b = upperBound.nextInt(); // a variable to store the upper bound of the interval

    while (a <= b) { // i.e., set a max of a = b

      if (a % 2 == 0) { // if dividing by 2 leaves no remainder
        System.out.println(a + " is an even number.");
        a++;
      }

      else {
        System.out.println(a + " is an odd number.");
        a++;
      }
    }
  }
}

This modified program will prompt me to enter a minimum value and enter a maximum value, then print the results:

java CustomEvenOddWhileLoop
Enter a minimum:
2
Enter a maximum:
6
2 is an even number.
3 is an odd number.
4 is an even number.
5 is an odd number.
6 is an even number.

As expected, this program prompted me to enter a minimum value. I entered 2. Then it prompted me to enter a maximum value. I supplied 6. The program then ran as intended.

I noticed that I didn't need to specify a starting point in my code this time, in contrast to int x = 0; in the original, EvenOddWhileLoop. Instead, my lowerBound variable serves as the de facto starting point here: e.g., 2.


There are two gotchas with these programs, EvenOddWhileLoop and CustomEvenOddWhileLoop:

  1. If I input a decimal value, such as 2.0 or 6.0, there will be an error. The program will halt with an InputMismatchException exception. I don't yet know enough to add a try/catch block to my program, but I can picture that being useful here to redirect the user with a custom error statement such as, "You entered a decimal value; try again with an integer."

  2. With the CustomEvenOddWhileLoop program, it would seem that the user can enter arbitrary integer values, but that's not quite the case. The program uses the data type int (for integer). Integer data takes up a certain amount of space in memory (32 bits, or 4 bytes, to be specific). In Java, integers can range from -2,147,483,648 to 2,147,483,647. So any integer value smaller than -2,147,483,648 or larger than 2,147,483,647 is disallowed for the int type. That's because such values take up more than 32 bits of space in memory. So if I input the value 3,333,333,333, I will get an InputMismatchException exception too. A value of that size requires the data type long, which can handle 64-bit integer values. If I wanted to do that, the fix would be simple enough: replace type int with type long and the .nextInt() method with the .nextLong() method in my code. The altered program would then accept any value sized 64 bits or smaller, and 3,333,333,333 would work just fine


Program 3: The Nth Letter of the Alphabet

This program prints the letters a through z. The program also specifies the numerical order of the letters.

/*
A program that prints the letters of the English alphabet along with their ordinals, using a while loop with a nested if-else-if ladder.
*/

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

    // An array to store the letters of the alphabet.

     String alphabet[] = {"a","b","c","d","e","f","g","h","i","h","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"};

    int n = 1; // to track which letter the program will print

    while (n < 28) { // i.e., until the 27th letter is reached
      //
      if (n == 1 || n == 21) {
        System.out.println("The " + n + "st letter of the alphabet is " + alphabet[n-1] + ".");
      }

      else if (n == 2 || n == 22) {
        System.out.println("The " + n + "nd letter of the alphabet is " + alphabet[n-1] + ".");
      }

      else if (n == 3 || n == 23) {
        System.out.println("The " + n + "rd letter of the alphabet is " + alphabet[n-1] + ".");
      }

      else {
        System.out.println("The " + n + "th letter of the alphabet is " + alphabet[n-1] + ".");
      }

      n++;
    }
  }
}

My Expected Output

When I run java TheNthLetterOfTheAlphabet, I expect to see a printout of twenty-seven sentences, one for each letter, a to z, in order. Each sentences will specify the ordinal number and ordinal indicator corresponding with that letter (i.e., "The 1st letter of the alphabet is...", etc).

Example Output

java TheNthLetterOfTheAlphabet
The 1st letter of the alphabet is a.
The 2nd letter of the alphabet is b.
The 3rd letter of the alphabet is c.
The 4th letter of the alphabet is d.
The 5th letter of the alphabet is e.
The 6th letter of the alphabet is f.
The 7th letter of the alphabet is g.
The 8th letter of the alphabet is h.
The 9th letter of the alphabet is i.
The 10th letter of the alphabet is h.
The 11th letter of the alphabet is j.
The 12th letter of the alphabet is k.
The 13th letter of the alphabet is l.
The 14th letter of the alphabet is m.
The 15th letter of the alphabet is n.
The 16th letter of the alphabet is o.
The 17th letter of the alphabet is p.
The 18th letter of the alphabet is q.
The 19th letter of the alphabet is r.
The 20th letter of the alphabet is s.
The 21st letter of the alphabet is t.
The 22nd letter of the alphabet is u.
The 23rd letter of the alphabet is v.
The 24th letter of the alphabet is w.
The 25th letter of the alphabet is x.
The 26th letter of the alphabet is y.
The 27th letter of the alphabet is z.

I was amazed by TheNthLetterOfTheAlphabet the first time I ran the program. It's simple yet delightful. But this delight required some prep work.

Notes

Workflow

  • The key ingredient in this program is the combo of two things:
    • the if-else-if ladder, and
    • the logical or operator (||)
  • This combo makes it possible for the program to select the correct ordinal statement corresponding with each letter. For example,
    • the first and twenty-first letters have the "-st" ordinal indicator as a suffix
    • the second and twenty-second letters have "-nd"
    • the third and twenty-third letters have "-rd"
    • The remaining 21 letters have "-th" (i.e., 4th, 5th, 6th...26th)
  • It would have been tedious to specify 27 individualized print statements in my program. By combining the nested if-else-if ladder, the ||s, and a few simple conditions, the program can handle that mapping for me. This required a little bit of upfront thinking to get the code just right

Code

  • I create an array, alphabet[], in which to store the letters of the alphabet as String variables
  • Arrays are zero-indexed, meaning that the position of the first object in the array is position [0], not position [1]. For example, the first letter, a, is in position alphabet[0]. This becomes important in the next few lines of code
  • The variable n lets my program keep track of which letter to make a decision on when the program reaches the if-else-if ladder. The n represents the ordinal of each letter, not the index of the letter in the array: i.e., n = 1 refers to letter a; n = 2 represents letter b; and so on
  • The array itself is 0-indexed, but my code is more human-readable if I initialize n as n = 1 rather than n = 0. The trade-off is that in my print statements, I have to subtract 1 from n to access the correct variable: so alphabet[n-1] is alphabet[0], which returns letter a. This trade-off is worthwhile, though: the conditions in the if and else if statements are easier to understand this way
  • The print statements concatenate three things: sentence fragments, letter ordinals, and array indices, like so: "The " + n + "st letter of the alphabet is " + alphabet[n-1] + "."

Program 4: Combine Two Arrays

This program will combine two arrays into one and print its contents as a list.

/*
A program that combines two arrays into a third array and prints its elements.
*/

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

    String array1[] = {"a","b","c"}; // declare and define the first array
    String array2[] = {"d","e","f"}; // declare and define the second array

    // A third array created by the program using the first two arrays as input.  
    String array3[] = {array1[0], array1[1], array1[2], array2[0], array2[1], array2[2]};

    int x = array1.length + array2.length; // for use in the while loop condition below
    int y = 0; // a starting point for the while loop condition below

    while (y < x) {
      System.out.println(array3[y]); // print the members of array3
      y++;
    }
  }
}

My Expected Output

When I run java CombineTwoArrays, I expect the program to create an array containing the combined elements of the first two arrays. I also expect the program to print the elements a through e, i.e., print the six elements now stored inside the third array.

Example Output

java CombineTwoArrays
a
b
c
d
e
f

Nice.

Notes

Workflow

  • Writing this program took some trial-and-error
    • Initially, I thought that .length was a method, but it's not. It's just a variable. So array1.length() would be incorrect. All I have to do is add .length to the name of the array, and that will retrieve the array's length (in the case of array1, that would be 3)
    • Another learning for me was that to access an element of an array, I'm supposed to use the format array[n], where n is the index of the element I want to retrieve. At first, I tried the format array.index[n], and this threw the error cannot find symbol. After a while, I learned why. Jshell was indispensible as I tried out different statements before correcting the code in my CombineTwoArrays.java file

Code

  • I don't yet know if there's a way or how to print all the members of an array without specifying the indices I want printed. So I rigged the program to print all members indirectly. For this to work, I had to set up the following:
    • the variable x, to store the combined length of arrays 1 and 2
    • the variable y, to use as a counter for my while loop
    • the while loop itself with the condition y < x, to coordinate the control flow for println
    • array3[y] as the parameter for println
  • The statement int x = array1.length + array2.length wasn't strictly necessary. I could have used int x = array3.length also. Or done away with x altogether, replacing the condition with while (y < array3.length). That would have been simpler and would have produced the same output. But I took this learning opportunity to verify these three things:
    1. array1.length and array2.length are indeed of type int,
    2. array1.length and array2.length can indeed be summed without an intermediary variable
    3. array1.length and array2.length do indeed lead to the same result as 3 + 3 = 6

I find it amazing that I can piggyback off of existing objects to create new ones. I'm getting strong "g(x) = f(f(x))" vibes here. :)

Program 5: Prime Checker

My final program checks if a whole number input by the user is a prime number or not.

/*
A program to check if an integer number supplied by the user is or is not a prime number.
The maximum input value is 2_147_483_647.
*/

import java.util.Scanner;

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

    Scanner userInput = new Scanner(System.in);
    System.out.println("Enter an integer to check if it is a prime number:"); // ask the user for an integer to check
    int i = userInput.nextInt(); // store the user's integer in the i variable

    if (i <= 0) { // prime numbers can't be negative or zero
      System.out.println("The number "+ i + " is not a prime number.");
    }

    else if (i == 2 || i == 3 || i == 5 || i == 7){ // single-digit prime numbers
      System.out.println("The number "+ i + " is a prime number.");
    }

    // The number "1" or any number divisible by "2", "3", "5", "7", or "9" cannot be prime.
    else if (i != 1 // the number "1" is by definition not a prime
                    && i % 2 != 0
                    && i % 3 != 0
                    && i % 5 != 0
                    && i % 7 != 0
                    && i % 9 != 0) { // a redundant condition that does not harm the program

      System.out.println("The number "+ i + " is a prime number.");
    }

    else {
      System.out.println("The number "+ i + " is not a prime number.");
    }
  }
}

My Expected Output

When I run java PrimeDetector on my terminal, I expect this prompt:

"Enter an integer to check if it is a prime number:"

So long as I type an integer value allowable for type int, the program will tell me if the number is a prime number or if the number is not a prime number.

Example Outputs

Example 1:

java PrimeDetector
Enter an integer to check if it is a prime number:
11
The number 11 is a prime number.

Example 2:

java PrimeDetector
Enter an integer to check if it is a prime number:
55
The number 55 is not a prime number.

Example 3:

java PrimeDetector
Enter an integer to check if it is a prime number:
2147483647
The number 2147483647 is a prime number.

Notes

Workflow

  • I tried but couldn't find a way to exclude the condition else if (i == 2 || i == 3 || i == 5 || i == 7) from my code. It's a verbose condition. But without it, the program evaluates that "2", "3", "5", and "7" are not prime numbers, because of the way I structure the follow-up modulus conditions. This would be correct program execution but be incorrect factually. What I need is a condition that translates to "if i modulo 2 is zero, then i is not a prime number, except if i itself is 2." So I have more to learn

Code

  • As with Program #1 (TwoLanguages) and Program #3 (TheNthLetterOfTheAlphabet), the if-else-if ladder proves to be, yet again, so useful! With just a handful of rules, this program can check the prime number status for every single integer from "0" to "2,147,483,647", which is outright bedazzling

With some modifications, the program could in fact check the prime status of numbers "0" through "2,147,483,647":

  1. remove the Scanner package, Scanner object, and prompt;
  2. replace int i = userInput.nextInt(); with int i = 0;;
  3. add a while loop with the condition while (i <= 2_147_483_647), and move the if-else-if ladder inside the while loop; and
  4. add the counter i++; to the bottom of that while loop

With those changes, running PrimeDetector checks the prime status of every integer from 0 to 2,147,483,647, inclusive. It would take the program over an hour to evaluate all 2.1 billion values, but it could do it. Thank goodness for Control + C to terminate long-lived programs!


Code (continued)

  • IDEs like IntelliJ have something called "chained method calls". It's a simple indentation technique to neatly display code when a line of code has a lot of method calls for the same object. Analogously, I chained my logical ands (&&) above, indenting them so that my code appeared neater. I could have left all my && operators on one line for the second if else statement, but I preferred the visual cleanliness of indenting them
  • In retrospect, the condition && i % 9 != 0 is redundant and can be removed, because any number divisible by "9" is also divisible by "3" and would already satisfy the condition i % 3 != 0. I realized that while writing this, so I left the "9" conditional in as a harmless quirk
  • I suspect that there's a way to write this program with fewer lines of code, so I'll have to practice working with logical operators with Java to see if that illuminates the way
  • As with Program #2 (EvenOddWhileLoop), there's an opportunity to add a try/catch block to this program with a custom statement to handle InputMismatchException errors. For example, if I type a decimal value into the command line, e.g., 11.0, instead of 11, Java will rightfully complain, and the program won't complete. Without the try/catch block, the user might not realize what mistake they've made.

Wrap-up

And that's that for my five programs:

  • TwoLanguages
  • EvenOddWhileLoop (and CustomEvenOddWhileLoop)
  • TheNthLetterOfTheAlphabet
  • CombineTwoArrays
  • PrimeDetector

I wrote the programs towards the end of my first month learning Java. It was a signal that my brain had begun to make sense of what I've learned in the last four weeks about Java syntax, packages, classes, variables, data types, arrays, relational and logical operators, conditional statements, control flow, print statements, Scanner objects, compilation, execution, bugs, and writing code. Writing about them has been a great way to review all this.

Next Up: Entering Month Two of My Learnings

I'm entering my second month of learning. It's exciting to know that I've only learned 0.001% of what there is to learn about Java. For example, I know enough to have written the five programs above in the last week. I knew enough to troubleshoot the many errors that came up as I was coding these. I knew enough to research solutions in my books and online when I got stuck.

But I don't even yet have a foundation in Java fundamentals! Herbert Schildt's 700-page Java: A Beginner's Guide is staring me in the face waiting for me to really read it, as I've only read about 20 pages' worth of it. And that book doesn't even cover all there is to know (there's nothing in it about ArrayLists, as just one example).

Zooming out further, the Java ecosystem is immense: there's Spring for app development, Maven for dependency management, Hibernate for dealing with data, Spring Boot for making microservices, J2EE and its many APIs for enterprise app development...and that is still not all there is to learn about Java!

But I'm on my way.


Till next time.

Top comments (3)

Collapse
 
dowenrei profile image
wenrei

Great article here! Iā€™m curious to know why (x < 11) is more economical than I (x <= 10), to me it appears the same

Collapse
 
alexlearnsjava profile image
Alex Ortiz • Edited

Hey what's up @wenrei, it's more economical visually. That's because x < 11 uses six characters and x <= 10 uses seven (including spaces). But from a memory standpoint, I think x < 11 takes up a little less space too; I don't know enough yet about how Java allocates memory for strings to say. But the <= has one more character than <, so my initial hunch is that the program with x <= 10 inside takes up slightly more space in memory than does the program with x < 11.

Collapse
 
dowenrei profile image
wenrei

Both x< 11 and x<= 10 evaluates to boolean, so the memory footprint should be the same