Data Types
Objective #1: List the primitive data types and their characteristics.
- Java requires you to formally declare variables before they are used in a method or class. Variables can more formally be called primitive variables. A declaration statement such as the following examples is used to declare a variable.
int num = 0;
double grade = 89.555;
boolean isFinished = false;
When declaring a variable you should assign it a legal and logical name and you must assign a specific data type and an initial value. By the way, a data type is sometimes more simply called a type. For the AP exam, you are ony responsible for using three of Java's primitive data types: int, double, and boolean. However, it is a good idea to familiar with the other 5 primitive types as well.
- The following statement is a declaration statement because it declares the variable num to be a variable of the int data type
int num = 0;
- You should initialize a variable to a specific value in its declaration statement since Java does not automatically initialize local variables to zero like the programming language Visual Basic The line of code
int num;
is dangerous since an error would occur later if you tried to print out or make use of an unitialized variable with statements like
System.out.println(num);
What would Java display in the statement above if the programmer did not previously store a value in num? Remember that zero is not automatically stored in local variables.
- Later in a program after you have declared a variable you can store a value in that variable with an assignment statement as in
num = 15;
or
num = otherNum + 10;
- Two or more variables may be declared in the same statement but it is not considered to be good style. The following statements will not cause compile errors:
int num1, num2;
double grade1 = 35.45, grade2 = 9.99;
- String and other types are not primitive data types but are actually classes from which you can instantiate objects. In other words, a primitive variable is not considered to be an object.
- Primitive variables of the int data type can only store whole numbers and cannot store floating-point values (i.e. decimal numbers.) This data type is often referred to as the "integer" data type. An int variable has a size of 4 bytes.
The statement int num = 4.0; causes an error since num cannot store the .0 part of 4.0 even though it is equivalent to 4 mathematically.
- Primitive variables of the double data type can store whole numbers or floating-point values. Computer scientists refer to decimal numbers as "floating-point values". A double variable has a size of 8 bytes. The letter e can be used in a number to represent exponential (i.e. scientific) notation.
The statements double num = 1200; and double num = 1.2e3; and double num = 1.2E3; are equivalent.
The statements double num = 0.0012; and double num = 1.2e-3; and double num = 1.2E-3; are equivalent.
- Variables of the boolean data type can either store the boolean values true or false. A boolean variable has a size of 1 bit.
- Here are all 8 of the Java primitive data types. However the only ones that you have to know for the AP exam and unit tests are boolean, int, and double.
Type |
Stores |
Default Value |
Size |
Range |
boolean |
true or false |
false |
1 bit |
true or false only |
char |
Unicode character |
\u0000 |
2 bytes |
0 to 2^16 - 1 (\u0000 to \uFFFF) |
byte |
signed integer |
0 |
1 byte |
-128 to 127 |
short |
signed integer |
0 |
2 bytes |
-32768 to 32767 |
int |
signed integer |
0 |
4 bytes |
-2147483648 to 2147483647 |
long |
signed integer |
0 |
8 bytes |
-9223372036854775808 to 9223372036854775807 |
float |
floating-point value |
0.0f |
4 bytes |
+- 1.4E-45 to +-1.797693134823157E+308 |
double |
floating-point value |
0.0 |
8 bytes |
+- 439E-324 to +-1.7976931348623157E+308 |
- Besides the familiar operators, *, /, +, and - for
multiplication, division, addition and subtraction, Java supports the use
of the % for modulus. Modulus
computes the remainder of dividing one integer by another. For example, num
= 8 % 3; would result with the variable num storing the value 2 since
2 is the remainder of 8 divided by 3. With the statement, num
= 3 % 8; would
cause 3 to be stored in num since 3 is the remainder of 3 divided by 8. In the order of operations, modulus ( % ) is performed along with multiplication and division,
from left to right. That is, the expression 11 % 3 * 4 - 9 % 4 evaluates as 7 since 11 % 3 is performed first and then 2 * 4 and then 9 % 4 and finally 8 - 1.
- The % symbol can also be used in Java to find the decimal part of a decimal value although this is not tested on the AP exam. For example
System.out.println(1.234 % 1); // displays .234
- Java also allows the use of the compound operators *=, /=, %=, +=, and
-=. That is instead of typing out the assignment statement
num = num + 2;
you can type the simpler
num += 2;
- Java allows the use of the incrementing and decrementing operators. The
incrementing operator ++ can be used to add one to a variable as in
num++;
Notice there is no space typed between the variable name and the ++ symbol.
This statement is equivalent to
num = num + 1;
and can even be rewritten as
++num;
The decrementing operator -- can be used to
subtract one from a variable as in
num--;
Objective #2: Explain how primitive types are copied and be able to trace their values.
- The data types, int, double,
and boolean are primitive
data types. They store numeric values (except boolean which stores true's
and false's) and are built-in to the Java
language.
- When you assign a variable with a primitive data type
to another one, there are two independent variables. For example, after the
following code executes
int num1 = 5;
int num2 = num1;
the variable num1 is located somwhere in the
memory of the computer and stores the value 5 while the separate variable num2 is
located somewhere else in the memory of the computer and it stores the value
5.
- It is illegal to redeclare a variable with a primitive data type a second time in a method. For example:
int num = 0;
num = 10;
System.out.println(num); // 10
int num = 20; // illegal to redeclare num with int typed again in front of num
The code should be written as
int num = 0;
num = 10;
System.out.println(num); // 10
num = 20;
where the assignment statement
num = 20;
is used rather than the declaration statement
int num = 20;
Objective #3: Explain the problem of overflow.
- A compile error is generated if you initialize or assign
a value to an int variable that would overflow the limitation
of the int data type. The maximum
value that can be stored in an int variable
is 2147483647 (i.e. roughly 2 billion) and the smallest value that can be stored in an int variable
is -2147483648.
- These limits are due to
the fact that an int variable occupies 4
bytes of memory. Since 4 bytes is equivalent to 32 bits. If you use the first bit as the sign bit that determines whether the value is positive or negative, you are left with 31 bits to represent the number itself. If all 31 of those bits were 1's then the number 0111 1111 1111 1111 1111 1111 1111 1111 translates to the decimal number 2147483647 which is 231-1. Using the two's complement method for storing numbers then the smallest value that you can store in 4 bytes is represented by thirty-two 1's. That decimal value is -2147483648 which is equivalent to -231.
- The constants Integer.MAX_VALUE and Integer.MIN_VALUE from
the Integer class can be used to refer to
these limits. Because zero can be stored in an int variable
and the two's complement method that is used to store binary values, the
last digit in the positive upper limit (i.e. 7) is not the same as the last
digit (i.e. 8) in the negative lower limit. MAX_VALUE and MIN_VALUE are
legal to use because they are already declared as public constants in the Integer class
as in
public static final int MAX_VALUE = 2147483647;
The Integer class does not need to be imported
at the top of a Java program because it is found in the java.lang package
which is automatically imported to all Java projects. We will eventually
study why the word static is used in this
declaration statement.
- Note that if you add two integers whose sum is greater than 2147483647 and
store that resulting value into an int variable, an error will not occur.
The error occurs only if you directly initialize or assign such a value to
the variable. In the following code segment
int num = 2147483647 + 1;
System.out.println(num);
the value
-2147483648 displays since Java "wraps around" the number line.
- Technically, you could overflow a double variable if you attempted to store a number greater than 1.7976931348623157E+308 or less than -1.7976931348623157E+308. But it is unrealistic that you would need to work with values that extreme.
Objective #4: Explain the problem of loss of precision and round-off errors that can occur and be able to work around it.
- Since a double variable is stored in 8 bytes of memory, there is a limit though to the overall number of digits, both before or after the decimal point, that can be stored without a loss of precision. About 15 significant digits can be stored at most in a double variable. The statements
grade = 0.123456789012345678901234567890;
System.out.println(grade);
will not cause a compile error. But the actual value that is displayed will be 0.12345678901234568. Note in the example above a 7 digit was changed to an 8 at the end of the number.
Here is an example that illustrates the loss of precision
double a = 1.1;
double b = 1.2;
System.out.println(a + b); // 2.3
System.out.println(a - b); //should be -0.1 but -0.09999999999999987 displays instead
System.out.println((a + b)*(a - b)); //-0.22999999999999968
System.out.println(a * a);//1.2100000000000002
System.out.println(b * b);//1.44
System.out.println((a * a)- (b * b));//-0.22999999999999976
if ((a + b) * (a - b) != (a * a) - (b * b))
{
System.out.println("Math error"); // this displays even though the expressions should equal each other
}
- In some ways, the int type is easier for the computer to work with than the double type. An int is only 4 bytes as opposed to 8 so it uses less memory per variable. Also, it can be argued that mathematical computations with int's work faster on the computer's processor than double's. Also, it is occasionally safer to use an int since loss of precision and rounding errors affect double's in ways that int's are not affected. However, even with the int type a roundoff error (and not a lack of precision) can cause errors. As our author demonstrates on p. 104, the code segment
double f = 4.35;
int n = (int) (100 * f);
System.out.println(n);
causes the value 434 to be printed out and not 435
as you might expect. It is safer in this situation to use the round static
method from the Math class
as in
int n = (int) Math.round(100 * f);
Notice that the round method
is a static method and not an instance method. It is called by typing
the name of
its class in front of it and the dot
operator. It
is not
necessary
to
declare
a
Math object
and call the round method
on that object. The use of (int) is called
casting and is explained below.
- Decimal numbers are referred to as floating-point
values by computer scientists. An int variable
cannot store a floating-point value in Java. Unlike Visual Basic which
rounds a floating-point value to the nearest integer value, a "possible
loss of precision" compile error is generated if you try to initialize
or assign a value to an int variable
as in
int num = 1.2;
Be careful not to even assign a floating-point value to an int variable;
the compiler will generate an error. The following statement is not allowed in
Java even though there aren't any nonzero digits stored as decimal places in myDouble:
double myDouble = 1;
int myInt = 3 + myDouble;
- You use casting (aka typecasting)
to purposefully but temporarily turn a double variable
into an int variable.
In the following example, no compile error occurs since myDouble is
casted to an int before
it is assigned to the int variable myInt.
double myDouble = 1.99;
int myInt = 3 + (int) myDouble;
System.out.println(myInt); //
the value 4 prints, not 4.99 or 5
In this code segment, int is
called a cast operator. In Java, casting a double to
an int does not
cause the floating-point value to be rounded. In the code segment, the 1.99 is
truncated to 1 before it is added to the value 3. But the
variable myDouble is
treated as a double variable
later
in the program and still stores the value
1.99.
You cannot cast an int or
a double into
a String or vice
versa.
- In the example below,
grade = 96.55555555555555;
System.out.println(grade);
the value that will be displayed is 96.55555555555554 where the last 5 is changed to a 4. Because of the translation of decimal values to binary values, the number in the last decimal place may increase, decrease, or stay the same. You should be aware that some numbers that terminate in base 10 (0.1 and 0.2 for example) translate to non-terminating values in binary. This can confusion in some situations even though there are IEEE and other standards on how hardware and software should handle this issue.
- An interesting type of computer crime relating to roundoff issues in computer program computations is called salami fraud. This article from http://www.networkworld.com/newsletters/sec/2002/01467137.html explains it.
Objective #5: Explain integer division and how to avoid it when desired.
- Be wary of integer division which is what we call the result of dividing one integer by another integer. This result is truncated to an integer even if the result would technically be a decimal number. The code segment
int num = 9;
double result = num / 2;
System.out.println(result);
causes the value 4 to be displayed even though 9 divided
by 2 is 4.5 to a mathematician and even though the variable result is a double. To avoid integer division, you can cast either one of the two division operands to a double. The other divisor is then automatically promoted to a double value as well. Therefore in the code segment
int num = 9;
double result = (double) num / 2;
System.out.println(result);
the programmer is casting the int variable
to a double to
avoid the problem of integer division. The value 2 is promoted by the Java
compiler to the double value
2.0 so that two double's
are being divided on the computer's processor.
Another way to avoid integer division in the example above is to type a decimal point along with the number 2 as in
int num = 9;
double result = num / 2.;
System.out.println(result);
causes the value 4.5 to be displayed since 2. is considered by the computer to be a double while 2 without a decimal point is considered to be an int.
- Integer division and % can be used to isolate a specific digit within a number. This is called parsing an integer. For example, you can parse the integer 1234 to determine that a 3 is in the tens place. Remember that the according to the Java order of operations % (modulus) is tie with multiplication and division.
The following algorithms apply to integers in Java:
modding by 10 gives you the last 1 digit of an integer (e.g. 1234 % 10 simplifies to 4)
modding by 100 gives you the last 2 digits of an integer (e.g. 1234 % 100 simplifies to 34)
modding by 1000 gives you the last 3 digits of an integer (e.g. 1234 % 1000 simplifies to 234)
and so on
integer dividing by 10 takes off the last 1 digit of an integer (e.g. 1234 / 10 simplifies to 123)
integer dividing by 100 takes off the last 2 digits of an integer (e.g. 1234 / 100 simplifies to 12)
integer dividing by 1000 takes off the last 3 digits of an integer (e.g. 1234 / 1000 simplifies to 1)
and so on
THIS IS THE ESPECIALLY USEFUL SET OF ALGORTHMS TO PARSE INDIVIDUAL DIGITS:
a number % 100 / 10 is the tens digit of that number (e.g. 1234 % 100 / 10 simplifies to 3)
a number % 1000 / 100 is the hundreds digit of that number (e.g. 1234 % 1000 / 100 simplifies to 2)
a number % 10000 / 1000 is the thousands digit of that number (e.g. 1234 % 10000 / 1000 simplifies to 1)
and so on
We can write statements such as
int num = 123;
int onesDigit = num % 10;
int tensDigit = num % 100 / 10;
int hundredsDigit = num % 1000 / 100;
int thousandsDigit = num % 10000 / 1000;
and so on
- Be wary think about this
1/3 + 2/3 = 1 in your math class but
1/3 + 2/3 = 0 + 0 = 0 in Java since integer division occurs while
1./3 + 2./3 = 0.3333333333333333 + 0.6666666666666667 = 1.0 in Java
- There is a curious side effect from using the addition compound operator +=.
int total = 0;
total = total + 5.4; // causes loss of precision compiler error
total += 5.4; // compiles and executes with x = 5 afterwards
The latter statement total += 5.4; is the same as total = (int) (total + 5.4);
- Note that while it is legal to store the result of dividing a floating-point value (i.e. double) by an int into an int variable as in
int result = 9 / 2; // 4
it is not legal to directly assign a floating-point value to an int variable as in
int result = 4.5; // invalid
Nor can you evaluate an expression that results in a floating-point value and store that value to an int
int result = 4 + 0.5; // invalid
int result = 4 - 0.5; // invalid
int result = 5 * 0.5; // invalid
Objective #6: Use the Integer and Double wrapper classes.
- The makers of Java created a class that allows you to work with integers and floating-point values (i.e. decimal numbers) in a more efficient manner than using the int and double primitive variables. The Integer and Double classes are called wrapper classes because an Integer object "wraps" itself around a primitive int variable and a Double object "wraps" itself around a double variable. Technically, Integer and Double are not primitive data types but are classes.
- The Integer class allows you to work with Integer wrapper objects that can store whole numbers. The statement
Integer num = new Integer(5);
declares and instantiates an Integer object that contains the whole number 5. The other constructor for the Integer class is passed the parameter value 5 which is stored in the private property for the num Integer object variable.
- However in the early 2000's, a Java upgrade now allows the simpler statement
Integer num = 5;
to be used to declare and instantiate an Integer object as well. This is called autoboxing. You can think of it as Java automatically wrapping (or "boxing up") an int value into an Integer object. Autoboxing is a feature of Java that allows a programmer to be lazy in this case and avoid typing the keyword new and the full constructor call statement. Java automatically invokes the constructor and stores the value 5 in the property of the num object variable. As of 2016-17, you do not have to know autoboxing or auto-unboxing for the AP Computer Science A exam although it is useful to understand.
- The Double class allows you to work with Double wrapper objects that can store floating-point values such as 5.43. The statement
Double num = new Double(5.43);
declares and instantiates a Double object that contains the whole number 5. With autoboxing, the statement
Double num = 5.43;
does the same thing.
- The feature in the Java language called auto-unboxing does the opposite of autoboxing. It allows you to treat an Integer object as if it were a primitive int value or a Double object as if it were a primitive double value. In the old days, the second statement would have caused an error since you could not directly add the primitive int value of 1 to the Integer object num.
Integer num = 5;
int sum = 1 + num; // num is auto-unboxed here
System.out.println(sum); // 6
The value 6 will be displayed. The object variable num is auto-unboxed from an Integer wrapper object into a primitive int that is then added to the value 1 and correctly stored in the int variable sum. If it weren't for the auto-unboxing feature of Java, this would cause an error since an object variable could not be mathematically added to a primitive int value without using the Integer class' intValue method.
- The Integer class has a method named intValue that returns an Integer object's int value. To be safe, in case your compiler uses an older version of Java, you could use the intValue method as in the following example,
Integer num = 5;
int sum = 1 + num.intValue();
System.out.println(sum); // 6
- The Integer and Double classes are immutable because there are no modifier methods in these classes. An immutable class is one which does not contain any modifier methods and therefore doesn't allow a client programmer to change any properties of an object variable.
For example, in the following example, the object variable num1 is unchanged by what happens to num2 in the vegas method.
public class IntegerExample
{
public static void vegas(Integer num2)
{
num2 = num2.intValue() + 1;
System.out.println(num2); // 6 is displayed
}
public static void main(String[] args)
{
Integer num1 = new Integer(5);
vegas(num1);
System.out.println(num1); // 5 is displayed
}
}
- You can convert a string value into a primitive int using the parseInt method from the Integer class. This is similar to the Val and CInt functions in Visual Basic. The statement
int num = Integer.parseInt("5");
converts the string value "5" into a primitive int. An exception error is created if the string value is not in the correct format to be a valid number. We will later see that this method is particularly useful when getting user input from the keyboard.
- You can convert a string value into a primitive double using the parseDouble method from the Double class. The statement
double num = Double.parseDouble("5.43");
converts the string value "5.43" into a primitive double. An exception error is created if the string value is not in the correct format to be a valid number. We will later see that this method is particularly useful when getting user input from the keyboard.
- On the AP exam, you probably will not have to work with Integer and Double objects. As of 2016-17, you do not have to know the parseInt and parseDouble methods for the AP Computer Science A exam however they are very useful in real world Java applications and it is handy to use these wrapper classes. For example, the Integer class has a reverse method that allows you to reverse the digits of a number in one line rather than the work that is involved with parsing the individual digits of an int like explained above in these lecture notes. In a programming competition, you should definitely be familiar with the helper methods such as parseInt and reverse found in Integer and Double.