Sep 18: Classes and Objects
Code discussed in lecture
The code for the Counter
class is here: Counter.java.
Last class we did a quick overview - class declaration, variable declarations, method declarations,
return types, parameters. Now we will go through things in more detail:
Java Types
Probably the biggest change for Python programmers in moving to Java is that you have to declare your variables and say what type each will hold. It isn't that Python didn't have types. It was that Python checked them at run time. Java checks them statically at compile time, and knows that at run time that everything is OK. By saving all of those run time checks Java code is able to run significantly faster than Python code.
So what is a type, and what is it for? At the basic level computer memory is a long sequence of 1's and 0's, each called a bit (BInary digiT). It is broken into eight bit chunks (called bytes), each with its own address. There are may things that you might want to store in a computer: numbers, characters, colors, sounds, wildebeast objects, etc. Even computer instructions are stored in memory. Each "type" of thing has its own encoding as bits. When I was teaching the computer architecture course one of my favorite test questions was to give a 32 bit sequence of 0's and 1's and ask how it would be interpreted if it were a computer instruction, an integer, a string of characters, a color, etc.
When programming in machine language the programmer has to remember what each byte is supposed to represent. If he or she starts treating computer instructions as if they were integers, or integers as if they were computer instructions, bad things happen. (That is what many computer viruses do - they store a bit pattern as data, and then trick the program into running that pattern as code.)
The two things that a type tells us are:
- How to interpret the bits as a representation of the particular data.
- Which operations are valid on that data.
Smalltalk, an early OO languge, has a very elegant solution to the type problem. Everything is an object, and the operations that are valid on the object are precisely those defined by its methods. The methods understand how to interpret the bit patterns. It is elegant, but having to ask a number to add itself to another number via a method call is a lot slower than using the hardware "add" instruction. Smalltalk is slow.
Java therefore has a compromise. Most things are objects whose
types are defined by the methods that they implement and the data that they store.
The object data
is stored somewhere in memory, and variables hold references
to the object that tell how to find it. The variables c1
and c2
in the Counter
class are examples.
(In practice the reference is
a memory address, but Java treats the reference abstractly and uses it to communicate
with the object. Unlike C and C++, Java has
no way to manipulate
the memory addresses.) Method calls are messages sent
to the object via the reference. This has the advantage
that all references are the same size, no matter how big the object is.
However, for speed's sake there are eight primitive types that are not objects. These are:
- four types for storing integers of in different numbers of bytes:
byte
(1),short
(2),int
(4), andlong
(8). We will almost always useint
, which has a range between just under negative 2 billion and to just over 2 billion.byte
is -128 to 127.short
is basically plus or minus 32,000.long
is huge: -263 to (263 - 1). (263 - 1 = 9,223,372,036,854,775,807.) - two types for storing numbers with fractions in scientific notation:
float
(4 bytes) anddouble
(8 bytes).float
has 7 or 8 significant decimal digits,double
has about 15 significant decimal digits. char
, a type for storing characters in a system called Unicode (2 bytes).boolean
, which stores true or false.
Primitives are actually stored in the variable itself, so these primitives have different lengths in memory. This means that there are two kinds of Java variables. If a variable is of a primitive type, the variable contains the actual data itself (the bit pattern representing the data). If a variable is of type reference to an object, then it contains a reference, and the data itself is stored elsewhere.
Why is this distinction important? My wife and I have a joint checking account. We each have an ATM card. The cards are different and have different names on them, but the refer to the same checking account. If I withdraw money with my ATM card, there is less money in the account, and if my wife then looks at the balance it will be smaller even though she did nothing with her ATM card.
In this analogy, the account is the object (and bank account objects are a common example in textbooks). The ATM cards are the variables, each holding a reference to the same account. Any changes made by either of us to the account via our ATM card are seen by both. On the other hand, if my wife has her ATM card re-programmed so that it refers to her personal account (changes the reference stored in the variable), that won't affect my card or the account. She just will no longer be able to use that ATM card to withdraw money from our account, because it no longer refers to our account.
Some tricky details:
Division of two ints (or other integer values) truncates to an integer, so 11/3 = 3. % is the remainder operation. 11%3 = 2. ONLY for integers. Demonstrate using DrJava.
Integer literals (e.g. 123) are assume to be int, unless followed by a L or l, in which case they are long.
Floating-point literals (e.g. 3.14159) contain a decimal point and are assumed to be double, unless followed by an f or F, in which case they are float. Also, there is a literal form for floating point numbers that resembles scientific notation:
6.022e23
. "E" or "e" replaces "10 to the".Character literals are represented between single quotes. So
'a'
. Note that'1'
is very different than1
. So how do we represent a single quote? Need to "escape" it using a backslash:'\''
. How do we represent a backslash? Escape it:'\\'
. Also,'\t'
is tab,'\n'
is newline.Strings. Java has strings. A
String
is an object rather than a primitive type, and we will look at strings later. However, there areString
literals built into Java. They are surrounded by double quotes:"This is a string"
. Strings can be concatenated using the+
operator. To include a double quote in a string, escape it with backslash:"Strings are enclosed by \" characters"
. Strings cannot extend over multiple lines.Type conversions. Some type conversions happen automatically. Others must be specifically requested via a process called casting. The distinction is whether the conversion can result in the loss of significant information. If an
int
is used where adouble
is expected, it is safe to do the conversion and it happens automatically. (Everyint
can also be represented as adouble
.) If adouble
is used where anint
is expected, we have a problem. There is no good integer representation for 3.3, for instance. So we have to make a specific cast, which trucates the fractional part:int n = (int) 3.3;
Going in the "safe" direction is called widening; going the other way is called narrowing. The order of primitive types from narrowest to widest is:
byte => short => int => long => float => double
It is also widening to go from a
char
to anint
or adouble
- the Unicode is stored as an integer value. Demonstrate all of these using DrJava.
Java Classes
You have played with classes in Python (or Java), so I won't go over a lot of details. I will instead walk you through a specific example of a class, and will mention things as I go. Read the book for more details!
One important thing to remember: In Java, a class is a template or blueprint. An
object is a thing created from the blueprint, usually by calling new
.
Although Java does not explicitly require us to, we observe various conventions for capitalization styles of various types of identifiers.
- Class names conventionally use title case: the first letter of each word
in the name is capitalized (e.g.
ArrayList
). - Variables and method names conventionally start with a lowercase
letter and then capitalize successive words (e.g.
poundsOfBarbecue
). - Constants (final variables) are all upper case, with words separated
by underscores (e.g.
LONG_CONSTANT_NAME
).
The class that we will look at is Counter
, which we skimmed over
earlier. An object of this class
represents a counter that starts at 0. Each time the method
tick()
is called, one is added to the counter. When the counter reaches a
pre-defined limit it "wraps," going back to 0. Such an object could be used as part of
a digital timer or digital clock class. The code for this class is here:
Counter.java.
Things to note about the code:
Comments: Three types:
- End-line comments. Everything following
//
is a comment and is ignored by the compiler. - Block comments. Everything between
/*
and*/
is a comment, no matter how many (or few) lines separate them. - JavaDoc comments. Special block comments that begin with
/**
. These can be automatically be converted into nicely formatted HTML files that document your program. There are special tags that help this process. In this class we will use:@author
to give an author's name.@param
to give the name of a parameter for a method and its description.@return
to give a description of what a method returns.
Counter
class in DrJava. The JavaDoc for theCounter
class can be found here: Counter.html.
- End-line comments. Everything following
Visibility: For now,
public
andprivate
. Public things can be seen by any class; private things can only be seen by objects of the same class. In this course variables will generally be private and methods will generally be public. NOTE: things are private to the class, not to the object. So one object can see another object's private variables, if they are in the same class.Types of variables. There are three distinct types of variables in Java.
- Instance variables (or fields, to use the Java term). The way that an object stores its personal data. One copy is each instance variable is created each time an object is created. It continues to exist as long as the object exists.
- Class variables (or fields). These belong to the class, not to any particular object. So one per class, no matter how many objects of the class there are. These variables exist before any objects are created and never go away. If any object changes the value of a class variable the value is changed for all objects of that class, because there is only one.
- Local variables. These are "scratch paper" created within a method. They are created when the execution reaches the delaration and go away no later than when the method returns. (Actually, when the program reaches the end of the "block" where the variable is declared. Blocks are delimited by curly braces. If you re-enter a block, a new copy of the variable gets created. Any previous value is gone.)
You would expect there to be declarations like "instance variable" or "local variable". Unfortunately, that is not what you get. Show examples from
Counter
.Any variable declared within a method is a local variable. (Look at
c1
andc2
inmain
.) Note no visibility specifier (public, private), because it is only visible within the block where it is declared.Any variable declared outside of any method is an instance variable, unless the declaration includes the word
static
. If static is included, then it is a class variable. Is this unnecessarily confusing? I think so.It is important to include visibility specifiers (public, private) on class and instance variable declarations. If you leave them off, you get a visibility called "package", which is seldom what you want! I would have been happier if leaving off the visibility specifier were an error!
Note that the three variable types are used for very different things. A common error is to be writing a method, realize that you need a temporary local variable, and declare it outside of the method. This makes it an instance variable, which remains in existence as long as the object exists. It runs correctly, but it is wrong to do this. It is like realizing you need a piece of scratch paper, and then going to a file cabinet and creating a new file folder and permanently filing the piece of scratch paper, which you will never use again.
One other note about variables - any of the three categories can be declared
final
. That means that the variable cannot be changed after it is assigned. It is a constant. This can be a very useful thing to do, and allows you to give meaningful names to "magic values".While it is legal to declare a final instance variable, does it make sense to do so? Not really. If you have a thousand objects, does each need its own copy of the constant? One for the whole class is enough, because it will be the same for every object in the class. So if you make an instance variable final, make it static as well, so it becomes a class variable.
-
Constructors. When you create an object you create its instance variables. Java will initialize these (to 0 for numbers, false for boolean, null for references to objects). However, these are seldom what you want. Java provides a special type of method called a constructor. It has the same name as the class, and is called by saying
new
. It is reponsible for giving all of the instance variables appropriate values. -
Overloading. Note that there are two constructors delared, and both have the same name
Counter
. How can this be? Their parameter sequences are different. One takes no parameters; the second take anint
. You can have multiple constructors (or methods) with the same name, as long as their signatures are different. The signature for a method is the list of parameter types in the order that they appear. The compiler can tell from the arguments which version to use. (Note - this can combine with automatic type conversions in very confusing ways. If the parameter types are an exact match for one of the signatures it is simple, but if not the order that you do type conversions can change which version you get. Avoid doing this!) -
Methods are called on and instance variables are associated with a particular object. (The object reference is the thing before the dot.) In the methods in
Counter
it is not clear that this is the case. If no object is specified, then it is the "current object", which has the namethis
. (Python uses "self".) Python requires that you always specify an object, using "self" if that is appropriate. Java does not requirethis
. However, it allows it. I considered requiring that you always usethis
, but for better or worse most Java code that you will read does not use it. However, if it is clearer, by all means usethis
.There is an exception - when the method or class variable is declared
static
. This makes the method or variable associated with a class, not an object. In this case the class name is normally used before the dot. (An example is the constant (static final class variable)Math.PI
. Another example is the class methodMath.sqrt
. The Math class declares the square root method to be static, so it is not associated with an object.) In fact, a static method is not allowed to use an instance variable in its code, because there is no object associated with the call which contains a copy of that instance variable. -
toString()
. Every object has associated with it atoString
method. The default you get is rather uninteresting - the class name and a reference value. But you can redefine it to do something more useful. ForCounter
this is to create a string with at least two digits, padding with a left 0 if necessary. (TheDecimalFormat
class does this. Look it up. But note that we create aDecimalFormat
object, and then send it the message to formatmyValue
.) ThistoString
is used automatically to do output. -
System.out.println
.System
is a Java class that handles all kinds of "systems" things.out
is a static class variable that refers to an object that knows how to print to the console. (Another variable isin
, which knows how to read from the keyboard.)print
andprintln
are two methods that this class understands. They assume aString
parameter and print it (with a newline at the end forprintln
). When you+
aString
with a non-string Java converts the non-string to a string (usingtoString
for objects) and appends them. -
Java control statements. This program contains
if
statements (with and withoutelse
) and afor
statement. The book explains in detail how these work. -
Operators. This program uses
&&
, the operator for boolean "and", in theset
method.||
is boolean or.!
is boolean not. Also it usesmyValue++
. This is the increment function, which adds 1 to the variable. There is also an decrement function--
that subtracts 1 from the variable. The book does a good job of explaining how to use these in the middle of expressions. You can use these to create very clever unreadable code. Please avoid doing so. -
public static void main(String args[])
. The way to run a Java program is to write amain
function. That is the function that will be run. It clearly should bepublic
, so that it can be seen outside of the class.static
says that it is a class method, so it can be run before any objects of the class are created.void
says return nothing. The parameter is an array of strings. Some operating systems (Linux) allow a command to be followed by a series of arguments. These will be put into an array and passed tomain
. We will not use this method of passing information to the main program. We will also be talking about arrays soon.You are not required to write a main method for each class. However, it is a very useful way to create a test program. Then if you modify the class you can re-run the test program to make sure that everything still works.