Data Types & Math
Storing and Manipulating Basic Numerical Data
Storing and Manipulating Basic Numerical Data
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.
Image Credit: geralt on pixabay https://pixabay.com/en/binary-code-globe-africa-asia-1695475/ ↩︎
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.
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.
Orignial Image Credit: Karen Arnold on Public Domain Pictures.net https://www.publicdomainpictures.net/en/view-image.php?image=56626&picture=cardboard-box-white-background ↩︎
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.
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.
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.
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
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.
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.
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 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.
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.
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.
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.
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. ↩︎
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. ↩︎
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.
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 | Æ ä |
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.
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.
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:
x
for that memory addressx
as an integerint
is equal to the decimal number 5 into the reserved memory addressAnother 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:
5
x
for that memory addressHigh-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.
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.
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:
We’ll discuss both of these exceptions later in this chapter when we dig into the details for each programming language.
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.
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!
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.
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.
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.
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.
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:
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.
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.
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.
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.
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:
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.
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 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.
There are some common properties of command line arguments we should be aware of
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.
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.
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.
Data Types in Java
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.
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.
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.
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.
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.
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!
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.
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.
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.
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?
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.
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.
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
.
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:
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.
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);
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.
type | bytes | binary for the value of 2 | |
---|---|---|---|
byte | 1 | 00000010 | |
short | 2 | 0000000000000010 | |
int | 4 | 00000000000000000000000000000010 |
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:
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.
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.
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.
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.
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.
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.
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:
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:
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.
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;
+
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.
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.
-
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
.
*
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 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.
%
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.
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.
++
& 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.
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.
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.
We are already familiar with the order of operations in mathematics, using mnemonics such as “PEMDAS” to refer to the following order:
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:
++
& Decrement --
after variable*-
, Increment ++
& Decrement --
before variable**
, Division /
, and Modulo %
+
, Subtraction -
=
, +=
, -=
, *=
, /=
, %=
* 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:
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.
The Linux command line consists of the command and the arguments.
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.
By convention, the command line arguments are sent to the program’s main
method. Java handles this automatically.
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.
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.
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 0Convert it to an intAssign it to x |
double x =Double.parseDouble(args[0]) |
Take the string in args at index 0Convert it to a doubleAssign 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.
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
.
{"2", "3"}
is assigned to args
args[0]
is a String. We know this because args
is declared to be an array of Strings (String[] args
, line 2)args
args[0]
to an int
, assign that integer value to xx
is an int
. We know this because x
is declared to be an int (int x
, line 5)x
args[1]
to an int
, assign that integer value to yx
and y
, assign that sum to the int z
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?
Staring at 0 saves memory and simplifies memory access. Nearly all languages start at 0. ↩︎
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!
For more information on Subgoals, see the work of Dr. Briana Morrison published through the ACM and elsewhere. Link
To analyze an expression involving variables and mathematical operators in code, here are some mental subgoals we can use:
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.
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.
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.
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.
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.
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:
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.3 + 5
can be replaced with 8
.8
can be stored in an int
variable, we are just fine. So, we can update the value of x
to 8
.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:
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.++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
.x + -y
becomes 6 + -3.5
which results in 2.5
.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 $.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:
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.
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.
Here are the subgoals for writing a new expression:
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.
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.
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.
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.
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.
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
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;
inputA
by inputB
, so we can use the modulo operation %
to find this value. So, our arithmetic equation could be inputA % inputB
.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;
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.
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.
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.