Remember the fields (or variables–we’ll be using the terms interchangeably) in our HelloWorld class? Those are the entities named myInteger, myLong, maybe (later shouldPrint), myList, and helloWorld. Notice that each of them, whether or not they have access modifiers, whether or not they’re static, and whatever their names, have a type.
It helps to think of each variable as a box. The box has a name, and usually contains a value. Furthermore, the box has a shape, and only values of the right kind of “shape” can fit inside. It’s a lot like a game of Perfection where a variable of type int is a semicircle and only semicircles are allowed inside, and the same with other variable types. (The term for this is strongly typed. Scripting languages such as Python and Javascript are weakly typed–variables in these languages aren’t assigned types at all and might contain anything.)
Primitives, Objects, and null
Java has two kinds of variables: primitives, where the value in the box is the actual value assigned to the variable; and objects, where the value in the box is actually a pointer to a location where an Object is found. Notice that our game has Objects of two different shapes, which would actually be for Objects of two different classes.
A primitive variable cannot be valueless–the “box” cannot be empty. But an object variable can–the “box” can be empty, like the first Object box in the game above. We have a special word for the “value” (or rather, “valuelessness”) of the empty box: null. null is the default value for objects.
null is really useful to represent a piece of data that’s unavailable or unknown. Say you have a list of high temperatures for the month of May, and today is May 3. What’s the temperature for May 18? You can’t say zero, because that’s an actual number and probably isn’t true (except maybe in Patagonia). The correct answer is “I don’t know”–or in Java, null.
The Types of Primitives
There are eight primitive types in Java.
Type | Size | Description and range | Default value |
byte | 1 byte | Integer from -128 to 127 | 0 |
short | 2 bytes | Integer from -32,768 to 32,767 | 0 |
int | 4 bytes | Integer from -2,147,483,648 to 2,147,483,647 | 0 |
long | 8 bytes | Integer from -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807. | 0 |
float | 4 bytes | Fractional number between 6 and 7 digits | 0 |
double | 8 bytes | Fractional number of up to 15 digits | 0 |
boolean | 1 bit | true or false | false |
char | 2 bytes | A single character or ASCII value. The coding scheme for char, called unicode, allows for 4095 distinct possible values, which means you can represent the characters and symbols of nearly any language. | ‘\u0000’ |
Note that despite the fact that I like to initialize variables when they’re declared, it’s not required; you can let them assume the defaults in the table above–or, for an Object, the default of null.
The Wrapper Classes
Each of the primitive types has an associated wrapper class–a type of Object that does little more than hold an immutable (unchangeable) value of the type of the corresponding primitive. They are:
Primitive Type | Wrapper Class |
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
boolean | Boolean |
char | Character |
You’ve undoubtedly noticed that most of the wrapper classes’ names are identical to their corresponding primitives–except that they’re capitalized. This is an excellent example of how names in Java are case sensitive.
Variables of wrapper classes, of course, can be null, which isn’t true of the primitives. (That set of temperatures for May we talked about above might be a bunch of Float values which are null for the days we haven’t recorded yet.) Furthermore, the wrapper classes are chockablock with useful methods (such as parsing strings into numeric or boolean values), and constants (like the minimum and maximum possible values for the corresponding primitive.
The wrapper class names in the table above are links to Oracle’s documentation so you can take a few minutes to get acquainted with some of them.
Autoboxing
Strictly speaking, Java syntax demands that, for example, you initialize an Integer and change its value (didn’t I say that was impossible??) this way:
Integer myInteger = new Integer(1);
// Create a new Integer and put it in the box
// named "myInteger".
myInteger = new Integer(2);
But it’s equally valid to use these statements instead:
Integer myInteger = 1;
myInteger = 2;
In cases like this, the Java compiler takes the second set of code lines and “rewrites” them behind the scenes to look like the first one. This sleight-of-hand is called autoboxing, and gives you that much less to worry about.
Autoboxing also allows for transparent conversions between types, such as assigning an int value to a long.
Java Literals
As in any other computer language, you can use literals, which are values you can use to initialize variables or perform calculations and other operations. Here are the formats of literals for each of the primitive types and wrapper classes.
Primitive Type | Sample Literals |
byte, short, int, long | 30 (a decimal number with or without a sign) 030 (a number in octal–base 8. This value equals 24 decimal.) 0x30 (a number in hexadecimal–base 16. This value equals 48 decimal) (Strictly speaking, long values are suffixed with an “L” or “l” (lowercase L), but thanks to autoboxing this usually isn’t necessary.) |
float, double | 3.14159 (a decimal number with or without decimal point, and with or without a sign) (Again, a float should be suffixed with “F” or “f”, and a double with “D” or “d”, but again, autoboxing usually takes care of this.) |
boolean | true or false–the literal characters, in lowercase, no quotes |
char | ‘a’ |
In addition to ordinary character literals, there are nine escape sequences for representing special characters.
Escape sequence | Value |
\t | tab |
\b | backspace |
\n | newline |
\r | carriage return |
\f | form feed |
\” | double quote–needed to represent a quote with a quote-delimited string |
\’ | single quote (apostrophe)–needed to represent a single quote within a single-quote delimited string |
\\ | backslash |
\u | the escape sequence for a Unicode character, written as \uxxxx, where the xs are hexadecimal characters (0-9 and A-F). For example, a nonbreaking space is written as \u00A0 and the symbol for euros is ‘\u20AC’. |
The String Class
You may have noticed that the char primitive and the Character class, which hold human-readable characters, allow for one and only one character. When you need more (and you almost always do), there’s the String class. Like the wrapper classes, instances of String are immutable–i. e., what looks like replacing the value of a String variable is, thanks to autoboxing, actually creating a totally new instance of String and “putting it into the box” that the previous instance occupied. And like the wrapper classes, String provides a host of methods for manipulating strings, which you can read about here.
Method | Function |
char charAt(int index) | Returns the character at the specified index, 0 being the first character. |
int compareTo(String anotherString) int compareToIgnoreCase(String anotherString) | Compares this String to another; for compareToIgnoreCase, the comparison is case insensitive. If this String is lower, the return value is negative; if higher, the return value is positive; if equal, the return value is zero. |
boolean endsWith(String suffix) boolean startsWith(String prefix) | Returns true if the String ends/begins with the specified suffix or prefix. |
boolean isEmpty() | Returns true if the length is zero. |
int length() | Returns the length of the String. |
String replace(char oldChar, char newChar) | Returns a String in which each occurrence of oldChar is replaced by newChar. |
String replace(CharSequence target, CharSequence replacement) | Returns a String in which each occurrence of target is replaced by replacement. (String is an implementation of CharSequence (as are StringBuilder and StringBuffer), so usually the arguments are typically Strings.) |
String replaceAll(String regex, String replacement) String replaceFirst(String regex, String replacement) | Returns a String in which each occurrence of the regular expression regex is replaced by replacement. |
String[] split(String regex) | Returns an array of Strings split around matches of the regular expression regex. |
String substring(int beginIndex) String substring(int beginIndex, endIndex) | Returns a substring of this String, staring at position beginIndex and running to either the end of the String or to the position endIndex – 1. |
String toLowerCase() String toUpperCase() | Returns a String shifted to the desired case. |
String trim() | Returns a String stripped of leading and trailing whitespace (spaces, tabs, carriage returns, line feeds). |
String valueOf(?) | Returns a String representation of the value provided. The possible types of argument are: boolean char char[] double float int long Object In each case, the return value is the same as toString() for either the Object or the wrapper class of the primitive. |
Concatenating Strings
It’s a simple matter to concatenate Strings with not just other Strings, but with any type of variable, using the “+” operator like so:
String winner = "tortoise";
int secsLead = 3;
String newString = "And the winner is the " + winner
+ " who finished " + secsLead + " seconds ahead.";
StringBuffer and StringBuilder
Strings are immutable. You can’t change their values; you can only replace them with other Strings. But there are two similar classes that holds a String that can change, and they are StringBuffer and StringBuilder. The main differences between them is that StringBuffer is synchronized and thus preferable for multithreaded applications; StringBuilder is not, but is faster. We invite you to visit its documentation pages in the links above.
Field attributes
Fields have a number of possible attributes. Let’s examine them.
You’re already familiar with the access modifier, which controls who, other than where it’s defined, can get to the field. Access modifiers are used only on fields at the class level; fields defined within methods are known only within those methods. Fields have the same access modifiers as classes:
- public: Any other class can refer to this field.
- private: No class other than this one and those contained within this one can refer to this field.
- protected: Only this class, its subclasses (even in other packages), and other classes in the same package, can refer to this field.
- default (no modifier at all): This class, and other classes in the same package, can refer to this field.
Second is the modifier, static. This is used only at the class level, and when it appears in a variable declaration, it means there’s one instance of the variable shared by every instance of the class. Without it, each instance of the class has its own distinct copy of the variable.
Finally (not surprisingly) is the modifier final. When this is used, the field may be assigned a value once and once only.
static and final are often used together to create constants–values that never change. There are a couple of reasons to use constants. First, using the name of a constant instead of a literal value in executable code ensures that the same value is used consistently throughout your project. The other is that if the value of the constant ever needs to be changed, it can be done in a single place in your code rather than throughout your project. Example:
public static final float ROOM_TEMPERATURE_FAHRENHEIT = 72;
Scope
In any computer language, variables have scope–the fact that some executable code knows of their existence, and some doesn’t.
Restricting scope is an important tool, because executable code that can’t reach a field can’t go mucking it up to the detriment of whoever defined it.
We’ve already seen how access modifiers control whether a variable defined in one class is visible to another class: public, protected, and default access allows some such visibility for variables defined at the class level.
But what about private fields–those no other class can see? The simple rule is: a field is visible to all methods within the same pair of braces in which the field itself is defined.
Consider HelloWorldClass with some modifications to make fields private:
public class HelloWorldClass {
private int myInteger = 1;
private long myLong;
private static boolean shouldPrint = true;
private ArrayList myList;
public static void main(String[] args) {
...
}
private void greet() {
...
}
}
The fields myIntger, myLong, etc. are within the outermost set of braces which enclose the contents of HelloWorldClass. Therefore, these fields can be reached from elsewhere within the class: static variables can be reached from within the static main() method, and both static and instance variables from within instance method greet().
Now try adding fields within a method.
public class HelloWorldClass {
private int myInteger = 1;
private long myLong;
private static boolean shouldPrint = true;
private ArrayList myList;
public static void main(String[] args) {
...
}
private void greet() {
String motto = "E Pluribus Unum";
...
}
}
The field motto is known only within greet(). The main() method–in fact, no method other than greet(), knows of motto.
Now let’s take this one step further:
public class HelloWorldClass {
private int myInteger = 1;
private long myLong;
private static boolean shouldPrint = true;
private ArrayList myList;
public static void main(String[] args) {
...
}
private void greet() {
{
String motto = "E Pluribus Unum";
System.out.println(motto);
}
{
String motto = "One for all and all for one";
System.out.println(motto);
}
motto = "Hakuna matata";
...
}
}
In this case, the two println statements display “E Pluribus Unum” and “One for all and all for one” respectively. The two declarations of motto are totally separate, and the memory they use is freed when execution resumes outside the braces. The code inside one set of braces is unaware of the declaration of the variable of the same name in the other set. This fact lets you reuse variable names within a method.
Usually this is done within blocks of code (code within braces) associated with if or while statements, which you’ll learn about soon. But as the example shows, those aren’t necessary.
But the line ‘motto = “Hakuna matata”‘ leads to a compilation error. Why? Because the two declarations of motto are within braces, and the erroneous line is outside the braces.
If you declare motto before the first set of braces, the declarations within the braces become attempts to redefine a variable name, a compilation error. But if you declare motto after the last set of braces in which the name is declared, that’s perfectly okay; a name isn’t taken until its declaration appears in the source code.
What You Need to Know
- Field (aka variable) declarations establish what are essentially named boxes containing values.
- A field can contain only the kind of data in the declaration.
- There are two kinds of fields: primitive and object.
- The “boxes” for primitives contain the actual value of the field; e.g. an int set to 123 contains the integer value 123. Primitive fields cannot be valueless.
- The “boxes” for objects contain pointers to the objects. Object fields can be empty; in that case, we say they contain null.
- All fields have scope: the limits of which executable code can refer to them.
Exercise
Here’s an exercise to get your feet wet on defining and using variables.
- In your Eclipse workspace, create a new Java project. To do this, select File/New/Java Project from the menu. This brings up the Create a Java Project dialog.
- Name the project “Lesson-02-Exercise-01”. Accept all the defaults and click Finish.
- If necessary, expand the tree whose root is the new project folder in the Package Explorer. Right-click on the src folder and select New/Package from the popup menu to open the Java Package dialog. Name the package “org.hardknockjava.lesson02.exercise01” and click Finish to accept the defaults.
- Right-click on the package name and select New/Class from the popup menu. Name the class Lesson02Exercise01, check the box marked “public void static main(String[] args)”, and click Finish.
- The editor now shows your new class. Here’s what you need to do.
- Define an instance variable of type int at the class level. Name it “theDecimal” and give it an initial value of 23 in decimal.
- Define an instance variable of type int at the class level. Name it “theOctal” and give it an initial value of 23 in octal (i.e., the octal digits “23”). See above for how to write an octal value.
- Define an instance variable of type int at the class level. Name it “theHexadecimal” and give it an initial value of “23” in hexadecimal (i.e., the hexadecimal digits “23”). See above for how to write a hexadecimal value.
- Using the System.out.println() method you saw in the class HelloWorldClass on the previous page, display the values of the three variables in this format: “theDecimal=” followed by the value of theDecimal, and so on. Each caption and value pair must appear on a separate line.
Remember that theDecimal, theOctal, and theHexadecimal are instance variables. This means they’re not accessible from the main() method. Follow the example in HelloWorldClass on the previous page to see how to create an instance of your class so you can get at the variables.
When you run the code, you should see this result:
theDecimal=23
theOctal=19
theHexadecimal=35
Having trouble? Download the project here and import it into your workspace to see one possible solution. Click here for instructions on importing a project into your workspace.
Onward! to Java Expressions and Operators