Introduction to Java Programming

Example 1

Disclaimer: Although I am a TA at Brock, this website is in no way funded or associated with the university. It is something I do in my spare time for fun.

Before we begin, I want to say that if you are serious about learning programming, now is the time to put on your critical thinking hat. You cannot expect someone to hold your hand through every programming problem, so it’s best to start learning how to think about programs now. If something here doesn’t make sense, stop and think it through. Programming is 90% debugging, which comes down to learning how to think critically, modify your code, and repeat the process. If you still don’t understand, feel free to leave a comment or send me an email.

In addition to critical thinking, learning how to Google is an essential skill. You don’t have to be great at it – just like you don’t have to be great at programming right away. It’s okay to fail and struggle; the important part is putting in the effort to improve. I don’t avoid using proper terminology in this article, and if you come across a term you don’t recognize, there’s a glossary at the bottom of the page. If that still doesn’t help, it’s the perfect opportunity to practice your Google-Fu.

Programming languages are highly structured. Like human languages, they follow a set of rules, or a grammar. Unlike English, however, a compiler enforces that grammar strictly. Why? Think about it this way: if you wrote a program to do heart surgery and the program hit a line of code it didn’t understand, would you rather it stop immediately or guess what you meant? Exactly.

The main function

In Java, all programs start with a main function. The main function always takes an array of Strings, which represent arguments passed to the program.

If you’ve never used the command line and have no idea what that means, don’t worry—you can ignore it for now. Just know that all Java programs must have a function named exactly like this, with this exact signature, in order to run:

Java
// In java all classes start with a captial letter
public class Example1 {
	public static void main(String[] args){
	
	}
}

The version of Java we are using doesn’t allow free-standing functions (functions outside of classes), so we must create a class to house our main function.

BlueJ abstracts away the main function, but I think this leads to a counterintuitive view of how the program actually runs. More on this later.

Variables

Before we can run, we must first learn how to walk.

In Java, all variable declarations follow the same general syntax:

Java
// Variable declarataion, no value assigned
Type identifer;
// what value is stored by default? depends on the type (see table below)

// Variable declaration, new object created.
Type identifer = new Type(...); 

// Variable declaration, with assignment to a primitive
primitive_type identifer = 0;

Nearly all variables in Java will follow one of these three styles. Notice that they all share the same underlying pattern: type followed by identifier.

A variable identifier is nothing more than a name for a location in memory. For primitives, this is often stored on the stack. For objects, it’s stored on the heap. When you use a variable, Java doesn’t care about the variable’s name—it cares about the value stored there. For primitives, Java copies the value. For objects, it passes around what’s called a reference copy. More on that shortly.

Java Primitives

Type NameDefault Value*Size (bytes)
booleanfalse1
byte01
char02
short02
int04
long08
float04
double08
(any object)nulldepends

Some of you may have noticed that a boolean takes 1 byte, even though it could theoretically fit in a single bit. The reason is that computers are optimized to work with bytes, not individual bits. On most systems, the smallest addressable unit is 1 byte, so storing a boolean as a single bit would actually take more work.

This also means you can store multiple booleans in a single int (up to 32). This is done using bitwise operators, which I may cover in a future post. If you’re curious, look up bitsets.

References & Reference-Copies

When I say “reference copies,” I mean that all objects in Java are passed by reference. However, reassigning the reference inside a method does not change the variable in the outer scope. On the other hand, modifying the object itself (e.g., by calling its methods) does change the underlying memory, and any reference to that object will see the update.

In other words: the underlying object is the same, but the variables pointing to it are not.

If that still doesn’t make sense, consider the following example.

Java
// This is a pattern you will see a lot in Java.
// It is often useful to store similar data together
// along with functions which modify or use that data. 
class MyExampleObject {
	private String myStr;
	private int myVal = 0;
	
	public MyExampleObject(String myStr) {
		this.myStr = myStr;
	}	
	
	public void print(){
		System.out.println("I have been changed " + myVal + " times.");
		System.out.println("My string contains '" + myStr + "'");
	}
	
	public void set(String replacementStr){
		this.myStr = replacementStr;
		myVal++;
	}
}

class Example {
	public static void setReference(MyExampleObject str){
		str = new MyExampleObject("Goodbye World!");
	}
	
	public static void setMethod(MyExampleObject str){
		str.set("Goodbye World!");
	}

	public static void main(String[] args) {
		MyExampleObject strObj = new MyExampleObject("Hello World!");
		
		// prints:
		// I have been changed 0 times.
		// My string contains 'Hello World!'
		strObj.print();
		
		// What do you think this will print?
		setReference(strObj);
		strObj.print();
		
		// How about this?
		setMethod(strObj);
		strObj.print();
		
		// Answer at the bottom of this page.		
	}
}

The new operator

You may have noticed the new operator used in the previous example. The reason for this is that all objects in Java must be allocated using new. This tells Java that you are requesting an object of that type.

When you write new, Java:

  1. Allocates memory for the object.
  2. Finds the constructor for the class.
  3. Calls the constructor with the parameters you provided.
  4. Returns a reference to the new object.

You can think of new as a special keyword used to invoke the class constructor and hand you back a reference.

Quiz

Let’s connect this to Lab 1. Here’s the basic setup code we used to create a turtle canvas:

Java
import brock.*;

class Example {
	public static void main(String[] args) {
		// What are these lines doing, exactly?
		TurtleDisplayer display = new TurtleDisplayer();
		Turtle yertle = new Turtle();
		
		// How about this line? What is it doing?
		// What about the variable, what kind is it?
		// Why would we want that?
		display.placeTurtle(yertle);
		display.waitForUser();
	}
}

Answers are at the bottom of the page. Hint: even if you don’t know exactly what the function does, you can infer from its name and location.

Scopes and For-loops

I’ve noticed some people struggling with the concept of for loops. Since it’s hard to explain them well in plain English, I’ll walk through scope first and then connect it to for loops with examples.

Scope

In Java, every control-flow structure (loops, if statements, etc.) introduces its own scope, called a block-level scope. A scope is defined by curly braces { ... }. You can nest scopes inside each other, and the nesting creates a hierarchy of visibility.

Java
// File-level scope: everything here belongs to the file.
// Usually, the file contains a single class named the same as the file.

class Example {
	// Class-level scope: everything declared here belongs to the class.
	
	public static void main(String[] args) {
		// Function-level scope: everything here belongs to the main function.
		
		{
			// Nested block-level scope.
		}
	}
}

A variable declared in an outer scope is accessible inside inner scopes, but not the other way around:

Java
public static void scopesAreFun(){
	int i = 2;// outer scope
	
	{
		// one indent more!
		int j = 10; // inner scope
	}
	
	// what happens here?
	System.out.println(i + j);
}

This fails to compile because j only exists inside its inner block.

  • Outer → inner: allowed
  • Inner → outer: not allowed

This lets you limit variable access and even reuse variable names safely outside that scope.

Variables declared inside a function disappear once the function ends.

Java
public static void scope1(){
	int j = 20;
}

public static void scope2(){
	// compile failure: j is not accessible in this scope.
	System.out.println(j);
}

Variables declared in a class belong to objects of that class. Functions inside the class can access them—but there’s a catch.

Java
class Example {
	public int i = 10;
	
	void print(){
		// perfectly fine.
		System.out.println(i);
	}
	
	public static void main(String[] args){
		// Compile error: i does not exist in static scope
		System.out.println(i);
	}
}

Why the error? Because static methods belong to the class, not an instance of the class. To access i, you need an object:

Java
class Example {
	public int i = 10;
	
	void print(){
		System.out.println(i);
	}
	
	public static void main(String[] args){
		Example e = new Example();
		System.out.println(e.i); // works
		e.print();               // works
	}
}

For loops

Now, let’s connect scope to loops.

A for loop is really just a common while loop pattern written in shorthand.

Java
class Example {
	// the following two functions are equivalent.
	public static void whileLoop(){
		int i = 0;
		while (i < 10){
			System.out.println(i);
			i++;
		}
	}
	
	public static void forLoop() {
		for (int i = 0; i < 10; i++){
			System.out.println(i);
		}
	}
}
  • In the while loop, you write initialization, condition, and increment separately.
  • In the for loop, all three parts are bundled together at the top, but the meaning is the same.

for loops also introduce their own scope. In the example above, the variable i only exists inside the loop. Once the loop ends, i goes out of scope and cannot be used.

That’s all for now. I have to head to the lab. In the next post, I will talk about classes, nested for loops, and probably more!

Glossary

Function

A function is a block of code with a well defined calling interface (signature), and can be called multiple times. They allow us to take a large complicated problem and break it down into smaller more manageable parts, often representing one task or aspect of a program.

Signature

A signature is the defined inputs and outputs to a function. In Java, the inputs to functions are called parameters and the output is called a return value. Java functions only allow one return value, thus if you want to return multiple objects you must package it into some kind of container class.

Initializers

An initlaizer sets a variable to a specific value. Specifically variable assignment is called variable initalization if it is set when the varaible is created.

Java
// Initalization
int i = 10;

// Assignment
i = 20;

Operator

Answers

Example MyExampleObject

In the above paragraph, I asked the question of what each method would do to the object. Here are the answers.

Java
// ...
	public static void main(String[] args) {
		MyExampleObject strObj = new MyExampleObject("Hello World!");
		
		// prints:
		// I have been changed 0 times.
		// My string contains 'Hello World!'
		strObj.print();
		
		// I have been changed 0 times.
		// My string contains 'Hello World!'
		setReference(strObj);
		strObj.print();
		
		// I have been changed 1 times.
		// My string contains 'Goodbye World!'
		setMethod(strObj);
		strObj.print();
		
	}
// ...

If you were thinking about what I said, then this answer doesn’t surprise you. I wanted to make it clear the distinction that, although the object represented by the variable is the same, the variables themselves are not the same.

Example Turtle Canvas

Java
// What are these lines doing, exactly?
TurtleDisplayer display = new TurtleDisplayer();
Turtle yertle = new Turtle();
// A:
// Constructs a TurtleDisplayer class and keeps a local reference to it
// Constructs a Turtle class and keeps a local reference to it
// There are no arguements passed to either class.
// Thus, we can infer that the object is in a valid state without any input from us.
// In the case of TurtleDisplayer, it will create a graphical window on your computer.
		
// How about this line? What is it doing?
// What about the variable, what kind is it?
// Why would we want that?
display.placeTurtle(yertle);
display.waitForUser();

// A:
// We notice that it is a function called "placeTurtle" which takes a turtle object. Since we can call commands directly on a turtle and have it draw to our screen, we know that this function must link the two classes together somehow. 
// Why would we want that? Well, by coupling state together we can simplify the interface to use it. Instead of passing the display every time we do turtle.forward(10); we can just call it like that.
// This is a concept which you sould understand, as it will come up when you have to make your own functions and classes.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *