Chapter 2

Data Types & Math

Storing and Manipulating Basic Numerical Data

Subsections of Data Types & Math

The World as Data

Binary World Binary World1

Everything in the world can be represented using data. Think about that for a minute. Does it make sense?

Picture a tree. That tree can be described by its width, height, age, number of leaves, and even its color or texture. What about a person? A person has a name, age, height, and many other attributes. Could we simply represent trees, humans, and other entities in our computer programs using their attributes?

That’s the next big step in learning how to write a computer program: dealing with data. Many of the programs we’ll write are simply built to help us manipulate data in some fashion. Think back to the Turing Machine example from the first chapter. All a Turing Machine does is manipulate data on a tape, and it is capable of running any possible computer program.

In this chapter, we’ll learn all about the different ways we can work with data in our programs.

Variables

YouTube Video

Video Materials

First, let’s talk about variables. A variable is a placeholder for another value in a computer program. Most students first learn about variables in a mathematics course, such as Algebra. For example, in math, we might see the equation $ x + 5 = 10 $, where $ x $ is a variable, or placeholder, for a value. Using the algebraic rules, we can solve that equation to find that $ x = 5 $.

Variables in a computer program share some similarities with variables in math, but have some differences as well. First, variables in a computer program are also placeholders that represent a value. However, in programming, instead of representing an unknown value, variables in a computer program represent a stored value, one which we can always access.

Cardboard Box Labelled Height Cardboard Box Labelled Height1

One way to think about variables in a computer program is to imagine them as a cardboard box. When we declare a variable, we are making a new box and giving it a name, which is the identifier of the variable. In the picture above, we’ve declared a variable named height.

Then, we can store a value in that variable, using a statement such as height = 42, which will put the number 42 inside of the height variable. This is an assignment statement, which we’ll cover in detail in this chapter.

Later in our program, we can then access the value stored in that variable and use it for calculations or output. For example, if we have additional variables named width and area, and have already stored the value 10 in width using width = 10, we can calculate the area of rectangular object using a statement such as area = width * height. This would use the current values in the height and width variables, and then store the result in the variable named area.

Behind the scenes, each time we declare a variable, we are telling the computer to set aside a small piece of memory in which to store something. We give the variable a name when we declare it, and then we can use that name to store or retrieve data from the location in memory our computer gave us.

Using variables effectively is a essential to writing good computer programs. This chapter will introduce many of the concepts related to dealing with variables and data in our code.

Subsections of Variables

Basic Data Types

In a computer, all data, even the instructions, is stored in a binary format. Binary data consist entirely of 1s and 0s, or, in the case of a computer, it could be represented as “on” and “off” for current in a circuit, or “positive” and “negative” charges stored on a magnetic storage device. It could even be “open” and “closed” for physical memory gates. There are many different ways to represent it, but it all boils down to just 1s and 0s.

Of course, we need a way to convert between the binary 1s and 0s that a computer understands and the actual data we’d like to represent. On this page, we’ll cover some of the common types of data and how they are handled by most computer programming languages.

Binary

Working with binary data can be a complex topic. In this program, we won’t deal directly with binary data very often. If you’d like to learn more about the binary numerical system and how it works, check out the Wikipedia article on Binary Numbers.

You may also want to review the List of Types of Numbers article as well, since we’ll refer to several of those types.

Binary literals are often prepended with 0b thus 10 represents the decimal number 10, whereas 0b10 represents the binary number 10 and the decimal number 2. Recall that a literal represents the actual typed value.

Common Numerical data

Numbers, and thus numerical literals, are used everywhere in computer programs. This goes beyond obvious arithmetic and accounting applications. Modern graphics, artificial intelligence and simulation programs use numerical representations of data to quickly perform their operations. Thus, it is unsurprising that there are many ways to write and store numeric data.

Integers

The first type of data we’ll deal with in our computer program is whole numbers. These are numbers such as 1, 0, -1, 25, -186, 12852, and more. In both mathematics and programming, we refer to these numbers as integers. An integer is any whole number, or a real number without a fraction or decimal component. We sometimes refer to these numbers as counting numbers.

To store these numbers in our computer program, we typically use a signed integer data type. This data type allows us to store both positive and negative numbers in binary in our computer’s memory. We won’t go too far into the details of how that works in this course, but there is more information on how that works on Wikipedia

Floating Point

Next, we’ll also need to handle numbers that include a fraction or decimal component. These include rational numbers such as 1.5, -2.98, 3 1/3, and even irrational numbers such as $ e $ and $ \pi $.

Our computer uses a special representation known as floating point to store these numbers. Floating point is very similar to scientific notation, where a number such as 1,230,000 is represented as $ 1.23 * 10^{6} $. In this example, 1.23 is the significand and 6 is the exponent of the number. It is known as floating point because the decimal point “floats around” the number based on the exponent. We could represent the same number as $ 12.3 * 10^{5} $ or $ 0.123 * 10^{7} $ just by adjusting the exponent, causing the decimal point to move within the significand.

Modern computers use the IEEE 754 standard for encoding floating-point numbers into binary. Again, we won’t go into the specifics here, but the graphic below gives us a good idea of how a floating point number can be broken up and stored in binary.

Double Precision Floating Point Double Precision Floating Point1

Here we can see that a 64 bit space in memory is divided into three parts, one for the sign (to denote either a positive or negative number), another for the exponent, and a third for the fraction or significand of the number.

Floating Points are Approximations

Just like there are rational numbers (such as 1/3) which cannot be exactly represented in the base 10 decimal number system, there are numbers which cannot be exactly exactly represented in base 2 (the underlying binary system used by a computer). In fact, there are many more rational numbers that cannot be exactly represented by binary numbers using floating-point than there are in a base 10 system.

In addition to this, because floats have a finite (set) number of bits, there is a limit to the number of significant bits we can use (typically 16 for a 64-bit float). This is generally only a concern in scientific computing (where we are dealing with either very big or very small numbers). However, many programs use common graphic processing units, which use only 32-bits (7 significant digits) to speed up calculations. This much lower accuracy can cause problems with statistical simulations.

Boolean

Boolean values, named for George Boole represent true and false in a computer program. While it may be as simple as storing a single bit, with 0 and 1 representing true and false, most programming languages provide a special way to deal with these values as they are very important in our computer programs. We’ll spend most of the next several chapters discussing how to work with Boolean values, but for now they are just another type of data our program could store.

Text as Data

Character

Many high-level programming languages have a character data type. A character represents a single letter in a written language such as English. Most programming languages use a special code called ASCII, or the American Standard Code for Information Interchange. It defines a numerical value for each character in the English language, as well as several special characters such as the newline or \n character we’ve already seen. Below is a table showing the entire ASCII code.

ASCII Table ASCII Table2

So, to store the character c, our computer would store the number 99 in binary. We should also notice that the capital and lowercase letters are separate, so C is 67.

Modern computer programming languages also support the use of Unicode characters. We won’t cover that in this course, but it is important to remember later on when working with languages other than English.

String

Sometimes we want to store entire sentences in our computer programs. A sentence is just a String of characters, object-oriented languages use a string class for this purpose and we’ll cover everything we need to know about strings in a later chapter.


  1. File:IEEE 754 Double Floating Point Format.svg. (2015, January 21). Wikimedia Commons, the free media repository. Retrieved 20:50, December 18, 2018 from https://commons.wikimedia.org/w/index.php?title=File:IEEE_754_Double_Floating_Point_Format.svg&oldid=147276375↩︎

  2. File:ASCII-Table-wide.svg. (2018, March 6). Wikimedia Commons, the free media repository. Retrieved 21:51, December 18, 2018 from https://commons.wikimedia.org/w/index.php?title=File:ASCII-Table-wide.svg&oldid=291044172↩︎

Data Types in Detail

A variable in a high level language is just a memory address. It tells the compiler/interpreter where to start looking for the binary string of data.

Decoding the binary

All high-level programming languages use a type system to tell the system how to decode the binary data it finds there. All types have a size and a semantic which describes how many bits are in the data and how to interpret each bit. Consider the 2-byte (16-bit) binary sequence 1100 0110 1110 0100, it can be interpreted as many values based on its type.

TYPE value
16-bit floating point -6.891
16-bit unsigned integer 50916
16-bit 2’s complement integer -14620
8-bit ASCII Æ ä

Performing Operations

Operations and methods are only defined for specific types. For example, we can divide two integers, but not two characters. Likewise, integer division works differently than floating point division, which we’ll cover later in this chapter. It is up to the programmer to select the appropriate type for a variable and keep track of it.

Implications for Object-Oriented Programming (OOP)

All objects are created from a class definition. For example, an object created from a class named Cat is itself a new data type. A key part of object-oriented programming is that objects modify their own data. So, understanding the type of each variable is vital to understanding program state, which allows us to verify correct behavior and troubleshoot buggy code.

Typing Systems

Static (i.e. Java)

One typing system, often used by compiled languages, is the static type system. In this scheme the programmer explicitly tells the compiler the type of each variable when the is declared or defined. The statement int x = 5; tells the compiler that:

  1. it should reserve a memory address for an integer variable
  2. that the programmer is going to use the variable name x for that memory address
  3. it should always interpret the bits stored in x as an integer
  4. it should put the binary string which, when evaluated as an int is equal to the decimal number 5 into the reserved memory address

Dynamic (i.e. Python)

Another typing system, often used by interpreted languages, is the dynamic type system. In this system, the interpreter infers (guesses) the type of the variable based on the literal or variable in the definition. The statement x = 5 tells the interpreter:

  1. to infer a type based on the literal 5
  2. to reserve a memory address for the inferred type
  3. that the programmer is going to use the variable name x for that memory address
  4. to put the binary string which, when evaluated as the inferred type is equal to the decimal number 5 into the reserved memory address

Converting Between Types

High-level languages typically have operations or methods to convert from one data type to another.

For example, in Java, to print a line to the terminal, we use the System.out.println() method. As input, this method expects to receive a value that is a String data type. So, many languages include methods such as the toString() method built into all Java classes, as well as the str() method provided by Python, to convert most data types into a string. When a programmer explicitly tells the computer to convert one variable Type to another, the process is called casting.

Thankfully, many languages also allow some data conversions to happen automatically through a process known as coercion. In most modern high-level languages, when a method is supplied the wrong type of parameter, the language checks to see if there is an automatic conversion from the supplied type to the required type. If so, the language first converts the incorrect parameter to the correct type, then calls the method. The same procedure is used for operators. Depending on coercion for simple types is a common practice. Depending on it for more complex types or programmer developed classes can be more difficult and result in errors.

In practice, we generally should prefer explicitly casting variables to convert their types instead of relying on coercion. Casting ensures the program does exactly what we expect it to do.

Arithmetic

Once we have data stored in our programs, there are a number of ways we can manipulate that data. Let’s look at a few of them here.

Basic Operations

At its core, a computer is capable of performing all of the basic arithmetic operations, so we can write programs that can add, subtract, multiply and divide numbers. However, there is one big caveat that must be dealt with.

As discussed on the previous page, computer programs can store numbers as both integers and floating point numbers. What happens when we perform operations on two different types of numbers? For example, if an integer is added to a floating point number, would the resulting number be an integer or a floating point number, as in $ 1 + 1.5 $?

As our intuition suggests, performing the operation gives us $ 1 + 1.5 = 2.5 $, which is a floating point number. In general, when performing an operation on either two integers or two floating point numbers, the result will match the type of the two operands. However, when performing an operation on both an integer and a floating point number, the result is a floating point number.

There are two notable exceptions to this rule:

  1. In a strongly-typed programming language, such as Java, the result may be converted to match the type of the variable it is being stored in, if allowed by the language.
  2. When dividing two integers, the result may be either a floating point number or an integer, depending on the language.

We’ll discuss both of these exceptions later in this chapter when we dig into the details for each programming language.

New Operations

YouTube Video

Video Materials

In addition to the basic operations listed above that we are all familiar with from our mathematics class, there are a few new operations we should be aware of as well.

Modulo

The first new operation, the modulo operation, finds the remainder after dividing two numbers. It is typically written as $ 9 \bmod 5 $ when printed, but most programming languages use the percent sign %, as in 9 % 5 to refer to this operation.

To calculate the result of the modulo operation, we must look back to long division. In the example above, we can calculate $ 9 \bmod 5 $ by first calculating $ 9 / 5 $, which is $ 1 $ with a remainder of $ 4 $. So, the result of $ 9 \bmod 5 $ is $ 4 $.

Another way to think about the modulo operation is to simply find the largest multiple of the second operand that is smaller than the first, and then subtract the two, just like you do when performing long division. So, we can find $ 42 \bmod 13 $ by first calculating the largest multiple of $ 13 $ that is smaller than $ 42 $, which is $ 39 $, or $ 13 * 3 $. Then, we can subtract $ 42 - 39 = 3 $, so $ 42 \bmod 13 = 3 $.

The modulo operation is very important in many areas of programming. In fact, it is one of the core operations for most modern forms of encryption!

Modulo & Negative Numbers

The modulo operation is not consistently defined when applied to negative numbers. Therefore, each programming language may return a slightly different result when performing this operation on a negative number. Make sure you carefully consider how this operation is used, and consult the official documentation for your programming language or test the operation if you aren’t sure how it will work.

Truncated & Floor Division

As discussed a bit earlier, one of the stranger things in programming is dealing with division. When dividing two integers, it is possible to end up with a result that is a floating point number, as in $ 9 / 8 = 1.125 $. So, how should we handle this?

It turns out that each programming language may handle it a bit differently. For example, Java would truncate the result by removing everything after the decimal point to make the result an integer. So, in Java, the statements 99 / 100 and -99 / 100 would both usually evaluate to 0. However, we can force the result to be a floating point number by making sure that at least one of the operands is a floating point number, as well as the variable we are storing the result in.

Python, on the other hand, would store the result as a floating point number by default if needed. However, Python also includes a special operator, known as floor division or //, that would round the result down. For positive numbers, this means that the result would be rounded toward 0, while negative numbers would be rounded away from 0. So, in Python, the statement 99 // 100 would evaluate to 0 since it is rounded toward 0, while -99 // 100 would evaluate to -1 since it is rounded away from 0.

In short, we just need to pay special attention when we use the division operation to make sure we get the result we expect.

Assignment & Equality

Lastly, we should briefly discuss the assignment operator. We’ve already come across it earlier in this chapter. In mathematics, we usually use the equals sign = to denote equality, as in $ x + 5 = 10 $.

In programming, we use the single equals sign = to perform assignment. This allows us to store a value into a variable, as in x = 5. This would store the value $ 5 $ into the variable x. We could also say that we assign the value $ 5 $ to x.

However, it is very important to note that the variable we are assigning a value to goes first, on the left side of the equals sign. So, we cannot say 5 = x in most programming languages.

Equality

Most programming languages use a double equals sign, or == to denote equality. We’ll learn more about equality and other comparison operators in a later chapter.

Subsections of Arithmetic

More on Operations

Operation

Computer Science, and therefore programming, has its roots in mathematics and uses a lot technical terms and jargon from math. Unfortunately this means that most programmers need to be familiar with some of this jargon.

An operation is a fancy math term for “do something”, but carries with it its own set of vocabulary. An operation is composed of:

  1. an operator (one or more symbols)
  2. operands (one or more pieces of data)
  3. a semantic meaning (i.e. what the operator does)

Binary Operations

Binary operations operate on two inputs. This class of operations should be familiar from arithmetic. Its format is

    operand1 operator operand2
        2     +        4
        x     +        y

where the operator is typically some type of symbol.

Binary Operations

Note that in this case the term binary refers to the fact that the operation is performed on exactly 2 operands, and not that the data is stored in a base-2 (binary) data format.

This can lead to some confusion among programmers when referring to bitwise operators, which are operators that perform actions on individual bits in a piece of data. Many programmers mistakenly call these “binary” operators since they operate on data at the binary level, but the proper term is “bitwise” operators.

Operator symbols are typically overloaded in programming languages, which means that they can do different things based on the types of the operands. For example, a typical language might do any of the following operations when the plus sign + is used as the operator, depending on the types of the individual operands:

Type Operand 1 Type Operand 2 Result Type
int
2
int
4
int
6
int
2
float
4.0
float
6.0
float
1.1
float
3.7
float
4.8
String
“2”
String
“4”
String
“24”
String
“2”
int
4
Error operation not defined for an operand of type String and int

We will have to refer to our language’s documentation for how each operator functions. Operator behavior, particularly in the presence of mixed-type operands varies from language to language.

Infix vs. Prefix

The form operand1 operator operand2 is called infix notation. However, there is another construct called prefix, that looks like operator operand1 operand2 or + 2 4. For technical reasons it is easier to write programming languages that use prefix notation. In OOP, we may see prefix notations used for methods and classes, which were adapted from a functional programming language where prefix notation is more common.

Unary Operations

There are some operations that have just one operand, these are called unary operations. The typical form is operator operand or operand operator. A fairly common unary operator is ! for the logical operation “not”. !True is the same as False. We’ll introduce more of these operators in a later chapter.

User Input

Most programs process some type of user input, either text input by the user, mouse clicks on the screen, or many other methods. In this course, we’ll typically deal with three different types of user inputs:

  1. Command-line arguments
  2. Reading from a file stream
  3. Reading from the keyboard stream

For the first few chapters, we’ll go ahead and provide the code needed to handle and process user inputs. As we learn more, we’ll go deeper into detail about each method and discuss the nuances involved with reading and processing user input.

Hard Coding

Consider the code

 int x = 3
 int y = 4
 int z = x + y
 print (z)

Here the programmer has explicitly coded the values of x, y, and z. Because the user has no way to directly assign values to any of these variables, they are referred to as “hard coded.” However, programs that contain only hard-coded values are not very useful since they don’t respond to any input from the user. So, in most cases, our programs will include some sort of user input instead of hard-coded values.

Command Line Arguments

Command-line arguments are pieces of data that are provided to the program when executed as part of the initial command. For example, we learned that we can run a basic Hello World program using a command similar to these:

# Java
java HelloWorld
# Python
python3 HelloWorld

We can provide a command-line argument to these programs by including them after the command. For example, we could re-write our program to make user of the user’s name when provided as the first command-line argument. In that case, we can use the following command to run our program:

# Java
java HelloWorld Willie
# Python
python3 HelloWorld Willie

In this example, Willie is the input provided as the first command-line argument. We can change that to be any value we want.

Common Properties of Command Line Arguments

There are some common properties of command line arguments we should be aware of

  1. Arguments are passed in as strings. Programmers must convert them to the appropriate data types.
  2. Arguments are passed in order from left to right. If a problem specification asks for a positive number then a negative number, we must provide them in that order.
  3. Arguments are separated by one or more spaces.
  4. There are ways for a program to tell how many arguments there are.

That’s pretty much it. User input can be provided on the command line in the form of string arguments (values). When the command is run, these arguments are packed up by the operating system and given to the program. When command line arguments are required (or optional), the problem statement will tell us their order and purpose. The decision on whether or not to use command line arguments is typically a design consideration.

Streams

Both file and keyboard input are handled as streams. Streams are a computer science abstraction for data that is organized in a queue. Data (usually bytes) are ordered and read from front to back. We can see how this works for the keyboard. When we type “dog” the keyboard stream (called stdin for “standard input”) receives the bytes [100, 111, 103]. Files from a disk drive are similarly read first byte to last.

Streams as Objects

Object oriented high level programming like Java and Python have built in classes that read values from a stream. Thus, with some minor set up differences, the commands to read from a file are typically exactly same as the commands to read from the keyboard. This is the power of abstraction – the details of how to read from these different streams are hidden from the programmer who only wants read them.

Streams are literally bytes of data. We will use methods that interpret those byes as text, and then convert the text to the type of variable we need.

We’ll learn more about reading data from the keyboard and from a file later in this course.

Chapter 8.J

Java Data Types

Data Types in Java

Subsections of Java Data Types

Types

The Java programming language is a statically typed language. This means that each and every variable we use in our programs must be declared with a name and a type before we can use it. The Java compiler can then perform a step known as type checking, which verifies that we are using the proper data types in our program.

The major advantage of this approach is that many errors in our computer programs can be discovered by the compiler, well before we ever try to run the program directly. It can also help us determine what the cause of the error is by stating that it is a type error, giving us a clue toward how it could be solved.

One downside to this approach is that it makes our programs a bit more complex, as we must think ahead about what types of data we’ll be storing in each variable, and we’ll need to write our programs carefully to avoid type errors. However, it may also make our programs easier to manage later on, as we’ll know exactly what type of data is stored in each variable at any given point in the program’s execution.

To make dealing with types a bit easier, Java will automatically coerce data from a “narrower” data type to a “wider” data type without any additional code. For example, an integer in Java can be stored in a floating point variable.

However, the opposite case is not true. In Java, we cannot store a floating point number in an integer without explicitly converting the data type. We’ll see how to do that later in this chapter.

Numbers

Most of the computer programs we’ll write must deal with numbers in some way. So, it makes perfect sense to start working with the numerical data types, since we’ll use them very often. Let’s dive in and see how we can use these numerical data types in Java!

Java has built in primitive types for various numeric, text and logic values. A variable in Java can refer to either a primitive type or a full fledged object.

Integers

The first data types we’ll learn about in Java are the ones for storing whole numbers. There are actually 4 different types that can perform this task, each with different characteristics.

Name Type Size Minimum Maximum
Byte byte 8 bits $ -128 $ $ 127 $
Short short 16 bits $ -32\,768 $ $ 32\,767 $
Integer int 32 bits $ -2\,147\,483\,648 $ $ 2\,147\,483\,647 $
Long long 64 bits $ -2^{63} $ $ 2^{63} - 1 $

As we can see, each data type in this list has a different size, and can store numbers within a different range. So, if we know the minimum and maximum values that could possibly be stored in a particular variable, we can use the smallest corresponding data type that can store that value. This would allow us to conserve the amount of memory used in our programs.

However, in practice, most modern computers have more than enough memory available to handle our programs, so this is typically not a concern for most developers. Instead, it is best to use the largest possible data type, to avoid errors in the future as the program is updated and data values may become larger than initially anticipated.

In this program, and most of the code in this book, we’ll typically use the integer, or int data type for all whole numbers. Even though it isn’t the largest data type for storing whole numbers, it is generally large enough. In addition, the int data type is supported universally across many different programming languages, so learning how to use it will make it easier to switch between languages later on.

Floating Points

The next data types we’ll learn about in Java are the ones for storing rational and irrational numbers. There are actually 2 different types that can perform this task, each with different characteristics.

Name Type Size Range
Float float 32 bits $ \pm 10^{\pm 38} $
Double double 64 bits $ \pm 10^{\pm 308} $

Unlike the data types for whole numbers, it is more difficult to discuss the minimum and maximum values for these data types, as it requires a thorough understanding of how they are stored in binary and interpreted by the processor in a computer. In general, each one can handle large numbers as well as small numbers extremely well.

However, just like scientific notation, the numbers it stores at best can only be as accurate as the number of digits it holds. So, when storing an extremely large number, there will be some rounding error.

In this program, and most of the code in this book, we’ll typically use the Double, or double data type for all decimal numbers.

Using Numbers in Code

Now that we’ve discussed the various data types available in Java, let’s look at how we can actually create variables that can store data in our programs.

Getting Started

Before working with the code examples in the rest of this chapter, we’ll need to add a class declaration and a main method declaration to a file named Types.java. In Codio, you can open it by clicking on the file in the tree to the left. If you don’t recall how to do that, now is a great time to review the material in Chapter 1 before continuing.

Setting up a new file each time is great programming practice!

Declaring

In Java, we can declare a variable using this syntax:

<type> <identifier>;

So, to declare a variable of type int named x, we would write:

int x;

We can also do the same for each of the types listed above:

byte b;
short s;
int i;
long l;

float f;
double d;

As with any other part of our program, we must first declare it before we can use it.

Identifier Rules & Conventions

Java has rules about the allowable names with can be used as identifiers, you can find them in the Java Documentation.

By convention, variable names should be descriptive and use camelCase to aid in reading.

Assigning

Once a variable has been declared, we can give it a value using an assignment statement. Assignment uses this syntax:

<destination> = <expression>

In that example above, <destination> is the identifier of the variable we’d like to store data in, and <expression> is any valid Java expression that produces a value. It could be a number, another variable, a mathematical operation, or even a method call, which we’ll learn about in a later chapter. The variable we are storing the value in must always be on the left side of the equals = sign.

For example, if we want to store the value $ 5 $ in an int variable named x, we could write the following:

int x;
x = 5;

We can even combine the declaration and assignment statements into a single statement, like this:

int x = 5;

The same syntax applies to all of these types in Java:

byte b = 1;
short s = 2;
int i = 3;
long l = 4;

float f = 1.2f;
double d = 3.4;

When assigning values from one variable to another using primitive data types, the value is copied in memory. So, changing the value of the first variable would not affect the others, as in this example:

int x = 5;
int y = x;
x = 6;

At the end of that code, the value of x is $ 6 $, but y still contains $ 5 $. This is important to remember.

Try It!

See if you can create a variable using each of the six data types listed above in Types.java. What happens when you assign a value at that is too big or too small for the variable’s data type? Can you assign the value from an int variable into a byte variable?

Doubles are Default

Notice in the code that the float variable f is assigned using the value 1.2f instead of just 1.2. This is because decimal values in Java are interpreted as double values by default, so when assigning a double to a float there is a possible loss of precision that the Java compiler will complain about. To avoid that, we can explicitly state that the value is a float by appending the letter f to it. We won’t see this very often outside of this lesson.

Printing

We can also use the System.out.println and System.out.print methods we learned earlier to output the current value of a variable to the screen. Here’s an example:

int x = 5;
System.out.println(x);

Notice that the variable x is not in quotation marks. This way, we’ll be able to print the value stored in the variable x to the screen. If we place it in quotation marks, we’ll just print the letter x instead.

Later, in the project for this chapter, we’ll learn how to combine multiple variables into a single output.

Casting

When writing our programs, sometimes we need to change the data type that a particular value is stored as, especially when we want to store it in a new variable. Ideally, we would construct our programs to avoid this issue, but in the real world we aren’t always so lucky.

To change the data type of a value, we can cast that value to a different data type. To cast a value to a different data type, we put the data type we’d like it to be in parentheses in front of the value.

Let’s look at the example below:

int x = 120;
byte y = x;
System.out.println(y);

In this example, we’ve created an int variable x, and stored $ 120 $ in that variable. We then create a byte variable y, and try to store the value from x into y.

Follow Along

Try to run these examples by placing each one in Types.java and seeing what happens. Does it work? Try it before reading the answers below.

When we try to compile that example, we should get the following compiler error:

Incompatible Types Error Incompatible Types Error

Since the int data type is larger than the byte data type, the compiler will give us an error stating that we might lose data when we perform that conversion. Of course, if we reverse the int and byte data types, and try to assign a byte to and int, it will work just fine.

In general, we should try to avoid this problem by redesigning our program to eliminate the need to store a variable in a smaller type, but sometimes it is necessary. To do this, we’ll need to cast the value to the correct data type. Let’s update the example above:

int x = 120;
byte y = (byte) x;
System.out.println(y);

In this example, we have added a (byte) in front of the variable x when we are assigning it to y. This tells the compiler that we would like to convert the data type of x to byte before storing it in y. Now, when we try to compile and run this program, it will act as we expect.

Successful Type Cast Successful Type Cast

However, let’s look at one final example to see why the compiler would warn us about converting to a smaller data type:

int x = 128;
byte y = (byte) x;
System.out.println(y);
Cast vs. Convert

When we say “cast” we really mean convert. Sometimes this results in a change in the the binary representation. Conversion preserves the semantics (meaning) but over writes the binary. In addition to the “casting” syntax (desired type) value ... (int)2.0, you will be exposed to many ConvertTo() methods later in this course.

Casting nearly always preserves the original binary but may result in gibberish; you lose the meaning.

int x = (int)'2';
// results in x equal to 50 not 2
// '2' binary 00110010
// int value of 00110010 is 50

However, “casting” is nearly always used to mean “convert”. It comes from the origins of programming, where languages supported fewer types and the binary had the same semantic meaning across multiple data types.

typebytesbinary for the value of 2
byte100000010
short20000000000000010
int400000000000000000000000000000010

From the above table you can see how casting might work for various sized integer values. Leading “bits” were ignored when casting to a byte-wise smaller type. Leading 0s were added when casting to larger type.

We will use cast and convert interchangeably in this course to mean convert to the desired data type. In Java casting between primitive numeric-types often preserves the semantics (value) of the cast. (int) 2.5 is the integer 2, and (double)-4 is the double -4.0.

In this example, instead of storing $ 120 $ in x, we have instead stored $ 128 $. When we compile and run this program, we get this unexpected output:

Overflow Error Overflow Error

We expect it to output $ 128 $, but instead it outputs $ -128 $. That’s strange, isn’t it?

What’s happening is an error known as integer overflow. Since $ 128 $ is too large to fit in a byte variable, the computer will truncate, or remove, the bits that are at the front of the number that won’t fit. This could cause data to be lost or misinterpreted, which is what happens here.

So, we must always be careful and not try to cast a variable to a smaller data type if it is too large to fit in that data type. This is why the compiler will always warn us when we try to do so, unless we add an explicit cast to our code.

Simplify our Code

To make things simpler, we typically just use a single data type for whole numbers, and a single data type for decimal numbers. By using the same type consistently throughout our programs, we can avoid many issues related to data types and casting.

As stated above, most of the examples in this book will use the int data type for whole numbers, and the double data type for decimal numbers. These choices are consistent with the majority of official Java code.

Other Types

Beyond numbers, there are a few other primitive data types in Java. Let’s take a quick look at them and see how they can be used in our programs.

Boolean

Java supports a primitive data type named boolean that can only store two values: true and false. As we might expect, we can use these boolean variables to store answers to questions in our program, such as “Is x greater than y?” At this point, we won’t use boolean variables for much in our programs, but in a later module we’ll cover the basics of boolean logic and learn how to use these variables to control how our program runs.

Character

Java also has a char data type, which can be used to store a single character of text. It uses the 16-bit Unicode format to store the data, which is the same as ASCII for simple characters and commands.

Declaring & Assigning

To declare a variable using these data types, the format is the same as the numerical data types:

boolean t;
char c;

We can also assign values to each variable. For example, to assign a value to a boolean variable, we could do this:

boolean t;
t = true;
boolean f = false;

Notice that both true and false are keywords in Java, which means that we won’t have to put them in quotation marks to use them. These values can be used to directly represent the boolean values True and False, respectively. Some programming languages, most notably C, do not support boolean values in this way, but Java includes them as keywords.

For characters, the process is similar:

char c;
c = 'a';
char d = 'B';

Notice that we use a single quotation mark ' around character values in our code. This is very important to remember! In our Hello World example, we used double quotation marks " around strings, or sentences of text. For a single character, however, we only use a single quotation mark.

Characters & Strings

Thankfully, the Java compiler will catch this problem pretty quickly. For example, take a look at this code, and see if you can spot the errors:

char c = "a";
System.out.println("c");

When we first try to compile the program, we should get the following error:

Compilation Error Compilation Error

As we can see, the compiler will not allow us to assign the string "a" to the variable c, which is of type char. So, to fix this error, we’ll need to modify our code to be the following:

char c = 'a';
System.out.println("c");

However, there might be another error in this code. Can you spot it? What will be printed to the screen when we run this program?

Let’s find out:

Program Output Program Output

You might have expected the program to output a, but instead it output c. This is because we have "c" inside of our System.out.println method. To print the value stored inside a variable, we shouldn’t put it in quotation marks. Otherwise, it will interpret that as a string, and print the name of the variable (or whatever is in quotation marks) instead.

Operators

Now that we’ve learned how to create our own variables, we can use them to perform a wide variety of mathematical operations. For most of these, we’ll use the same operator symbols that we’ve used before in math, but a few of them are unique to programming languages like Java.

For each of the examples below, we’ll assume that we have already created two int variables, x and y, as follows:

int x = 5;
int y = 8;

Addition +

Using the plus + symbol, we can add two numbers together and find their sum:

int z = x + y;
System.out.println(z); // 13

Since $ 5 + 8 = 13 $, this program would output 13.

When writing our code, it is always good practice to put spaces between operators and operands unless otherwise noted, just to make it easier to read our code.

The plus symbol can also be use to concatenate or link multiple strings together. We’ll learn how to do that in the project for this chapter.

Code Comments

We can add comments to our code to help us describe it to others or simply understand it better ourselves. These comments are simply text that is added to the source code, but they are marked in a way that tells the compiler to ignore it when compiling the program.

In Java, we can use two forward slashes // to tell the compiler that everything on that line following those symbols should be treated as a comment, as we can see in the examples on this page.

We can also use a forward slash followed by an asterisk /* to start a multiline comment in Java. The compiler will ignore everything after that symbol until a closing asterisk followed by a forward slash */ is found.

Finally, Java also uses a forward slash followed by two asterisk /** to denote special comments that can be used to automatically generate documentation for the code. Again, this comment section is closed by an asterisk followed by a forward slash */.

Traditionally, each additional line in these multiline comments will be prefixed by an asterisk to make them easier to read. In fact, the Codio editor will do that for us!

Here are some examples in code showing how those comments can be used.

/**
 * This is a comment at the beginning of a class that gives
 * information about the class contained in this file
 * 
 * @author Username
 * @version 1.0
 */

public class Example{
  
  /**
   * This is a comment at the beginning of a method. 
   * It can give information about the inputs and outputs 
   * of that method
   * 
   * @param args - the command-line arguments
   */
  public static void main(String[] args){
    /*
     * This is a simple multi-line comment
     * It can have multiple lines
     * and ends with the slash below
     */
    
    int x = 0;
    
    //This is a single line comment
    int y = 0; //Comment after code on the same line
  }
}

Feel free to use comments anywhere in your source code to help you understand what the code does! It is always better to include too many comments than too few, especially in larger programs.

Subtraction -

Likewise, we can use the minus - symbol to subtract two values and find their difference:

int z = x - y;
System.out.println(z); // -3

This program would output -3, since $ 5 - 8 = -3 $. It is important to remember that these operations can result in negative numbers, so the order of operands we use matters when subtracting.

We can also use the minus symbol to find the additive inverse of a number, or change the sign of a variable from positive to negative and vice-versa:

int z = -x - y;
System.out.println(-z); // 13

In this example, we use -x to denote the inverse of x. In this case, we don’t include a space between the minus symbol and the variable it is attached to. So, this expression will calculate $ -5 - 8 = -13 $ and store that value in z. Then, when we print that variable, we invert it again, so it will actually print 13 to the screen instead of the value $ -13 $ stored in z.

Multiplication *

We can also use the asterisk * or star symbol for multiplication to find the product of two numbers:

int z = x * y;
System.out.println(z); // 40

We know that $ 5 * 8 = 40 $, so this program would output 40 as expected.

Division /

Division is a bit different. Most programming languages use the forward slash / symbol for division, making the statement look like a fraction. That is probably the easiest way to think about division in a programming language - it makes a fraction of the dividend and the divisor.

Earlier in this chapter we discussed that strange things may happen when we try to divide two whole numbers. Let’s look at a few examples:

int z = x / y;
System.out.println(z); // 0

This first example will calculate $ 5 / 8 $ but store it in an int variable. We expect the answer to be $ 0.625 $, but since an int variable can only store a whole number, the result is truncated by removing the decimal portion. So, our program just outputs 0.

Here’s another example:

int z = -x / y;
System.out.println(z); // 0

We know that $ -5 / 8 = -0.625 $, but once again the result is truncated and the decimal part is removed. So, this program will also output 0.

Let’s look at another division example:

double z = x / y;
System.out.println(z); // 0.0

Here we are storing the result in a double variable. We’d expect the output to be 0.625, but we actually just get 0.0 when we run this program. That’s strange, isn’t it?

Earlier we discussed some of the rules for how a programming language calculates numbers. One of those rules states that an operation performed on two whole numbers will result in a whole number, which is exactly what is happening here. The processor calculates $ 5 / 8 = 0 $ as a whole number using truncated division, then converts the value $ 0 $ to a floating point value before storing it in z. So, even though it appears we should be storing $ 0.625 $, we aren’t able to due to the data types of our two operands. This is a very common mistake made by programmers of all skill levels, so it is very important to understand what is going on.

So, how can we solve this issue? The answer lies in the use of a cast operation to convert the data type of one of our operators to a floating point value. Here’s an example showing how to accomplish that:

double z = (double) x / y;
System.out.println(z); // 0.625

In this example, we are casting the value stored in x to be of type double. Then, the division operation is performed, this time between a floating point number and a whole number, which will result in a floating point number. Finally, that result will be stored in z, and the program will output 0.625, which is the correct answer.

In short, we need to make sure we pay special attention when dividing numbers in our programs, just to make sure we are getting the result we expect.

Modulo %

The modulo operator uses the percent sign % to calculate the remainder of a division operation. Let’s look at an example:

int z = x % y;
System.out.println(z); // 5

Here, we are calculating the remainder of the division $ 5 / 8 $. Since $ 8 $ does not go into $ 5 $ at all, we are left with $ 5 $ as the remainder of that division.

If we reverse the operands, we can get a different result:

int z = y % x;
System.out.println(z); // 3

Here, we are calculating the remainder of $ 8 / 5 $. Since $ 5 $ will go into $ 8 $ once, we can perform $ 8 - 5 = 3 $ to find the remainder of that division.

Modulo & Negative Numbers

In Java, the modulo operation is treated like a true remainder operation. So, when applied to negative numbers, it will perform the expected division and return the remainder, which may be a negative number, depending on the signs of the operands. It follows the same sign rule as multiplication and division.

Here are some examples:

System.out.println(-8 % 5);  // -3
System.out.println(-8 % -5); // -3
System.out.println(8 % -5);  // 3

We already know that $ 8 % 5 = 3 $. In this case, the negative sign is applied if just one operand is negative. Otherwise, the result is positive if both operands are negative.

Increment ++ & Decrement --

Increment ++ and decrement -- are two special operators in Java. They are used to add or subtract $ 1 $ from an existing variable. Let’s look at a couple of examples:

int z = x++;
System.out.println(x); // 6
System.out.println(z); // 5

Here, the increment operator will increase the value stored in x by $ 1 $. In this case, we don’t include a space between the operator and the variable it is attached to. However, since the operator is after the variable, it will happen at the end of the operation. So, the value $ 5 $ will be stored in z first, then x will be incremented by $ 1 $ afterwards. So, the program will output 6 as the value of x, and 5 as the value of z.

By placing the increment operator in front of the variable, we can see a different outcome:

int z = ++x;
System.out.println(x); // 6
System.out.println(z); // 6

As we’d expect, here the increment operation will be performed first, and then the new value stored in x will also be stored in z, so both variables will contain the value $ 6 $ in this program.

The decrement operator works similarly, subtracting $ 1 $ instead of adding $ 1 $.

In practice, these operators are only used in a few scenarios, most notably within loops to increment or decrement a counter variable. In general, they can make our code more complex and difficult to read due to the variety of different ways these operators can be used.

Assignment Shortcuts

Finally, many operators in Java also have combined operation and assignment operators. These will perform the operation specified and store the value in the first variable, all in a single statement.

Operator Example Equivalent
+= x += y; x = x + y;
-= x -= y; x = x - y;
*= x *= y; x = x * y;
/= x /= y; x = x / y;
%= x %= y; x = x % y;

As with the increment and decrement operators, some of these shortcuts are rarely used in practice since they can make our code more difficult to read and understand. However, in some scenarios they can be quite helpful.

Order of Operations

Video Error

There is a typo in the video below. The order of operations given on the slide is incorrect. The correct order is shown below the video.

YouTube Video

Video Materials

We are already familiar with the order of operations in mathematics, using mnemonics such as “PEMDAS” to refer to the following order:

  1. Parentheses
  2. Exponents
  3. Multiplication & Division
  4. Addition & Subtraction

Programing languages work very similarly, but there are many more operators to deal with. Here’s a quick list of the order of operations, or operator precedence of the operators we’ve covered so far:

  1. Parentheses
  2. Postfix: Increment ++ & Decrement -- after variable*
  3. Prefix: Inverse -, Increment ++ & Decrement -- before variable*
  4. Multiplicative: Multiplication *, Division /, and Modulo %
  5. Additive: Addition +, Subtraction -
  6. Assignment: =, +=, -=, *=, /=, %=

* The increment and decrement operations will be calculated either before or after the rest of the operation is performed, depending on where they are placed in relation to the associated variable. This lists what order they will be analyzed by the compiler to determine which variable they are attached to.

Here’s a quick example to show how this works:

int x = 1;
int y = ++x + x - x / x * x % x--;
System.out.println(x); 
System.out.println(y); 

Can we determine what the output should be? Let’s try to break it down!

Based on the operator precedence table above, we know that the x-- will be evaluated first. However, since it is after the variable, it won’t be calculated until after the rest of the operation is complete. So, we can just remember that step for now, and we’ll actually perform that calculation last.

Next, we know that ++x will be evaluated. This step must be performed before any other operation since it is before the variable, so we must immediately increment the value of x by $ 1 $. So, x is now $ 2 $.

Now we can perform all of the multiplicative operations, going from left to right. So, by adding the appropriate parentheses, we’d calculate the following:

$$ x + x - x / x * x \% x $$ $$ 2 + 2 - 2 / 2 * 2 \% 2 $$ $$ 2 + 2 - (2 / 2) * 2 \% 2 $$ $$ 2 + 2 - ((2 / 2 * 2) \% 2 $$ $$ 2 + 2 - (((2 / 2) * 2) \% 2) $$ $$ 2 + 2 - ((1 * 2) \% 2) $$ $$ 2 + 2 - (2 \% 2) $$ $$ 2 + 2 - 0 $$

Following that, we can perform any additive operations. Once again, we can add parentheses as appropriate to find the following:

$$ 2 + 2 - 0 $$ $$ (2 + 2) - 0 $$ $$ ((2 + 2) - 0) $$ $$ (4 - 0) $$ $$ 4 $$

Once we’ve calculated all of the operations on the right-hand side of the assignment operator, we can then perform the assignment operation to store the final value $ 4 $ in y.

Finally, we must remember to go back and decrement the value in x by $ 1 $, so x once again is just $ 1 $.

So, the final output from this program would be:

Operators Example Operators Example

However, as any good math teacher will tell us, it is always better to use additional parentheses to make sure our operations are performed in the correct order instead of relying on the order of operations. So, we should definitely avoid writing code as ambiguous as the example given above.

Subsections of Operators

Command-Line Arguments

The Linux command line consists of the command and the arguments.

linux command line for java linux command line for java

In this example, Linux will see the first part of the command, which is java, and that will tell it what program to execute. Everything else is packaged up as string-values and sent to the Java Virtual Machine. The JVM takes the name of the program, SomeProgram, from the command, and then starts the program with that file name, passing the remaining items (arguments) to the program.

Accessing Command-line Arguments in a Object Oriented Program

By convention, the command line arguments are sent to the program’s main method. Java handles this automatically.

linux command line for java linux command line for java

Inside Java, the command line arguments are mapped (assigned) to the parameter variable in the main method called args. In main args is an array of Strings.

Java map of cmd line arguments Java map of cmd line arguments

Accessing arguments

Arrays are ordered collections of data - we might think of it as a numbered list. We access elements of the array using the indexing operator [], the indexes start at 01. The syntax is variableName[indexNumber].

The parameter args is always an array of strings, in the order they appeared on the command line.

Converting arguments

Since args is always an array of Strings, programmers must convert those arguments (values in the args array) to the appropriate type. For numbers, this is typically done using various parse methods that are provided as part of the Java language.

Syntax Semantics
int x = Integer.parseInt(args[0]) Take the string in args at index 0
Convert it to an int
Assign it to x
double x =Double.parseDouble(args[0]) Take the string in args at index 0
Convert it to a double
Assign it to x

If we try and convert something that does not make sense, Double.parseDouble("act") or Integer.parseInt("3.1415"), we’ll get an error when we run our program. Later in this class we’ll learn about how to detect and handle those errors, but for now they will simply crash our program.

Example

Consider the code below, which can be stored in a file named SomeProgram.java if we want to execute it.

public class SomeProgram{
    public static void main(String[] args){ 
        System.out.println("args[0] is a String"); 
        System.out.println("args[0]'s value is \""+ args[0] + "\"");
        int x = Integer.parseInt(args[0]);
        System.out.println("x is a int");
        System.out.println("x's value is " + x);
        int y = Integer.parseInt(args[1]);
        int z = x + y;
        System.out.println(x + " + " + y + " = " + z);
    }
}

A proper command line for running SomeProgram.java would be java SomeProgram <int> <int>, since it requires to command-line arguments that will be converted to integers. An example would be java SomeProgram 2 3 which will store "2" as args[0] and "3" as args[1]. At this point, let’s trace the execution SomeProgram.java if we execute it using the command java SomeProgram 2 3.

  1. When we run this program we start on line 2, the beginning of the main method.
  2. The array {"2", "3"} is assigned to args
  3. This line prints that args[0] is a String. We know this because args is declared to be an array of Strings (String[] args, line 2)
  4. This line prints the value of the string at index 0 of the array args
  5. This will convert the string args[0] to an int, assign that integer value to x
  6. This will print that x is an int. We know this because x is declared to be an int (int x, line 5)
  7. Next we’ll print the value of x
  8. Now we convert the string args[1] to an int, assign that integer value to y
  9. Add x and y, assign that sum to the int z
  10. Print the result of the computation.

Feel free to experiment with SomeProgram.java to explore more ways to use command-line arguments in code. What happens if we give too few arguments? Or too many? What about the wrong types?


  1. Staring at 0 saves memory and simplifies memory access. Nearly all languages start at 0. ↩︎

Subgoals - Evaluating Expressions

One of the unique parts of this course will be the inclusion of subgoals to help us better understand what is going on in our code. Subgoals are designed to help us structure our thinking process in a way that matches what an experienced developer might have.

A great example is learning how to read. Fluent readers can read whole words at a time, while processing and retaining information from several sentences at once. Novice readers have to sound out words one letter at a time, and can store and retain a few words at a time in memory before being overwhelmed by information. Learning a new language can quickly make this difference very apparent, even for readers who are fluent in many languages.

Writing computer code is very similar. An experienced developer can fluently read and write code, seeing and understanding entire code structures at a glance. Novice developers often have to look at a single line at a time, and break it down into individual parts before it is apparent what the code says. That’s just part of the learning process!

To make this process easier, this course will include many pages that describe subgoals we can use to help analyze and write code. In essence, we can treat these subgoals as a set of steps to follow mentally when performing these tasks. Over time, we’ll become more comfortable with each task, and the subgoals will quickly become ingrained knowledge. Just like we can easily read both “cat” and “catastrophe” at a glance, with practice we’ll be able to read code just as quickly!

More Information

For more information on Subgoals, see the work of Dr. Briana Morrison published through the ACM and elsewhere. Link

Analyzing Expressions

To analyze an expression involving variables and mathematical operators in code, here are some mental subgoals we can use:

1. Check Types

First, we must determine whether the data type of the expression is compatible with the data type of the variable we want to store it in. In Java, we must be very careful to make sure we are only storing whole numbers in the integer data type, and floating point values in the double data type.

2. Perform Prefix Operations

Next, we should look at the expression to determine if there are any prefixed operations that must occur first. In Java, for example, we could find a prefixed increment operator like ++x, so we’ll need to update the value of x before moving to the next step.

3. Solve Arithmetic Equation

At this point, we can solve the arithmetic equation using the order of operations for our language. This simply involves the process of substituting values for variables and performing the requested operations. However, once again we must be careful to make sure that the operands provided to each operator are valid and produce the correct data type. The example we saw earlier for handling a large equation showed us a great way to work through this process.

4. Confirm Data Type of Result & Store It

Once we’ve solved the arithmetic equation, we should be left with a variable on the left side of the equals sign and a single value on the right side. So, once again, we should confirm that the value on the right can be stored in the data type of the variable on the left.

5. Perform Postfix Operations

Finally, if the original expression included any postfix operations, such as a postfixed decrement like x-- in Java, we’ll need to update the value of x before moving on to the next line.

Examples

Let’s walk through an example showing how to use these subgoals to evaluate a few expression statements.

int x = 3 + 5;

We’ll break this down by subgoals:

  1. We can easily confirm that the data type of the variable is int, and that the numbers on the right side of the equation are all integers, so we shouldn’t have any issues with data types initially.
  2. This reminds us to look for prefix operations. Since there aren’t any, we can move on.
  3. We can now solve the arithmetic equation on the right side of the expression. So, we can easily find that 3 + 5 can be replaced with 8.
  4. This tells us to once again confirm that the result is a compatible value for the data type of the variable on the left. Since 8 can be stored in an int variable, we are just fine. So, we can update the value of x to 8.
  5. Finally, we must look for any postfix operations in the original equation. Since there aren’t any, we can skip this step.

That’s all there is to it! While this may seem like quite a bit of information for handling a simple statement, it becomes much more useful as the statements get more complex.

Here’s a second example:

int x = 5;
double y = 3.5;
double z = ++x + -y--;

Once again, let’s break the last line in this code example down by subgoals to evaluate the expression:

  1. This is a bit tricky, since the statement includes several variables. However, we can look at the earlier code to see that x is an integer type, and y is a double type. Since this expression is adding them together, the result should be a floating point number, which can be stored back in the variable z with type double. So, we should be fine here.
  2. Next, we’ll see that there is a prefix expression ++x, so we’ll increment the value of x by $ 1 $ to $ 6 $. We also see a prefix operator -y, so we’ll need to remember that we are dealing with the inverse of the value stored in y.
  3. Now we can solve the equations. To do this, we’ll replace each variable with its value, then calculate the result. So, x + -y becomes 6 + -3.5 which results in 2.5.
  4. Once we have the result, we must confirm that it can be stored in the variable to the left. Since 2.5 is a floating point number, it can be stored in the double data type. So, we’ll update the value of z to $ 2.5 $.
  5. Finally, we’ll need to look for any postfix operators in the original equation. We’ll see y--, so we must decrement the value of y by $ 1 $ to $ 2.5 $

So, at the end of this short code example, we’ll see that x is $ 6 $, y is $ 2.5 $, and z is also $ 2.5 $.

Finally, let’s look at one more example:

int x = 5;
double y = 8.5;
int z = x + y;

Here’s how we can break down the process of evaluating the last line in this example:

  1. First, just like the example above, we see that x is an integer and y is a double, so the sum of those values should also be a double. However, we’ll see that z is an integer. So, what will happen here? As it turns out, the Java compiler will give us an error when we try to compile this code, because we are assigning a double value to an integer which could result in the loss of data.

As we can see, by carefully following these subgoals, we can even find errors in our code, just by evaluating it step by step.

Subgoals - Writing Expressions

We can also use subgoals to help us write new expressions in our code. These subgoals help us understand each part of the process of building a new expression, and they also help us avoid many common errors.

Writing Expressions

Here are the subgoals for writing a new expression:

1. Find Variable in Problem Statement

The first step is to determine which part of the problem statement will be represented by a variable. Sometimes this is obvious, and other times it is not. This may be a new variable that we are creating, or it could be an update to an existing variable.

2. Determine Name and Data Type of Variable

Once we’ve found a variable to work with, we must also determine the variable’s name and data type. Once again, this may be obviously found in the problem statement, but other times we must think a bit more about what type of data will be stored in the variable. If we are working with an existing variable, we’ll need to make sure that the data type of that variable is compatible with the type of data we’d like to store in it. Of course, we should also make sure the variable has a descriptive and memorable name if we are creating a new one.

3. Build Arithmetic Equation with Operators

Now that we’ve isolated our variable, we must build an arithmetic equation and operators required to produce the desired value. This may involve using additional variables in our equations as well.

4. Build Expression

Once we have our arithmetic equation, we can then build the overall expression. This usually consists of three parts: the variable on the left, an assignment operator in the middle, and the arithmetic equation on the right.

5. Make sure Operators and Operands are Compatible

Finally, once we’ve constructed the overall expression, we should check and make sure that all operators and operands are compatible. This means making sure that we aren’t trying to assign a floating point value to an integer, or dividing two integers and expecting a floating point number as a result.

Example

Here’s a quick example to show how this process works. Consider the following problem statement:

Given two whole numbers, find the remainder of dividing the first number by the second number.

Let’s use the subgoals listed above to build an expression for this problem statement

  1. First, we must identify our variables. Looking at this problem statement, we see that it expects us to be given two whole numbers. So, we’ll probably need two integer variables to represent those two numbers. Then, we’ll need a third variable to store the remainder. In total, we can easily identify three variables that we’ll need to create for this program.
  2. Next, we must determine the name and type of each of these variables. To make it very clear, I’m going to label the two input variables inputA and inputB, and the remainder variable remainder. We can also determine that each of them should be the int type, since we are only dealing with whole numbers. So, our code might begin with these three variable declarations:
int inputA;
int inputB;
int remainder;
  1. Now we can assemble our arithmetic equation. We need to calculate the remainder of dividing inputA by inputB, so we can use the modulo operation % to find this value. So, our arithmetic equation could be inputA % inputB.
  2. Then, we can build the overall expression itself. Since we want to store the result of the arithmetic equation in remainder, we’ll build the line remainder = inputA % inputB as our entire expression. So, we can add that to our code:
int inputA;
int inputB;
int remainder;
remainder = inputA % inputB;
  1. Finally, we must make sure our operators and operands are all compatible. Since we know that both inputA and inputB are integers, the result of the modulo operation would also be an integer. So, this line is valid.

Of course, the part we are missing is the values for inputA and inputB. At this point, we haven’t covered how to accept user input yet, so we can assume that those values are hard-coded into the program. So, our final program might look something like this:

int inputA;
int inputB;
int remainder;

inputA = 5;
inputB = 8;
remainder = inputA % inputB;

Using these subgoals is a very great way to learn how to build programs one step at a time. As we learn more about programming and get more experience, many of these steps will become automatic. This is very similar to how we write sentences by hand. We typically don’t think about each letter as we write it once we are fluent with the language, but instead we think of entire words and our brain is able to send the correct commands to produce the desired output. With time and practice, writing code will be very similar.

Printing Text & Numbers

Printing Text with Variables

Now that we’ve learned how to run our program and provide it some input, we must also learn how to provide output that includes our variables as well as text all on the same line. There are a few ways to accomplish this.

First, we can use System.out.print() to print text without a newline character at the end, so that the next output will begin on the same line. So, we could use that to print text and variables on the same line. Here’s an example:

int x = 1;
int y = 2;
System.out.print("Variable x: ");
System.out.print(x);
System.out.print(", Variable y: ");
System.out.println(y);

will produce this output:

Variable x: 1, Variable y: 2

In Java, we can also concatenate multiple outputs together using the + symbol. So, we could produce similar output using this example:

int x = 1;
int y = 2;
System.out.println("Variable x: " + x + " , Variable y: " + y);

which should output:

Variable x: 1 , Variable y: 2

Notice that we needed to include an extra space after 1 in the output. This is because Java does not add a space between strings when concatenating them, so we must be careful to add the spaces ourselves where needed.

There are many ways that we can use Java to output text and variables as desired. We’ll use some of these methods to complete this project.

Summary

We’ve now learned a lot about variables and data types in our programming language. We can use this information to build programs that store and manipulate large amounts of information.

We still have quite a bit to learn before we have the basics of programming covered. As we continue, we’ll see the usefulness of variables and the various data types we’ve learned.