Java Objects
- General Object-Oriented concepts
- To declare object classes, defining methods and properties
- To create instances of objects and use them in a program
- To access objects using reference variables
- To define different protection levels for elements
- To define constructors
- To overload methods and constructors
- To use the this self-reference
- To declare and use static elements
- To create packages of classes
Objects
In general, the concept of an object is: a collection of data along with the functions to work with that data
- as opposed to purely procedural languages where the data and functions are separate, and the data is passed to the function to work with
In Java, all code must be written inside object definitions
- a class is an object definition, containing the data and function elements necessary to create an object
- an instance of an object is one object created from the class definition (the process is known as instantiation)
- the data and function elements are known as members
- data members are also known as fields, properties, or attributes, and function members as methods
Methods can work with any properties of an object, as well as any other methods
A constructor is a function that defines the steps necessary to instantiate one object of that class
- if you do not explicitly create one, a default constructor that does nothing will be created automatically
Each class should generally be defined in its own file if it is to be used by other classes
- the file name must match the name of the class, plus a .java extension
The data members of a class may be any type of data, objects or primitives
It is traditional in Java that only class names begin with capital letters
- variable names, both object and primitive, begin with lowercase letters, as do method names
- multi-word names, like first name, are done in Camel-case (as in firstName)
Object-Oriented Languages
For a whirlwind tour of OOP, an object-oriented language is supposed to include three major concepts:
Encapsulation - that data and the functions to work with that data are contained within the same entity; encapsulation includes another concept: data hiding or protection - where direct access to data is restricted in favor of methods that manipulate the data
Polymorphism - that there can be different forms of the same thing, so that you could have one variable that could store several different types of objects; or a method that accepts no arguments and another method with the exact same name that accepts an integer argument (and maybe another that accepts a string and a double-precision argument, etc.).
Inheritance - that an object definition can use another object definition as a starting point and build upon that base object (in which case the original object definition is called the base class, parent class, or superclass and the new class is called the derived class, the child class, or subclass, respectively) - note that a derived class can be used as a base for further inheritance, creating a chain
Java uses all three of these concepts
Java does not allow any code that is not part of an object - all code in your program must be inside a class definition
Java forces inheritance on every class - if you do not explicitly inherit from a class, then by default Java assumes that you are inheriting from the class called Object (which does not do much, but does have several useful methods)
- if you think about this, it implies that every class is descended from Object, since whatever class you inherit from must have inherited from something, which would be either Object or something that inherited from something, etc.
- the concept of polymorphism implies that you could store any type of object in a variable whose type was Object
Object-Oriented Programs
An object-oriented program replaces the concept of a linear sequence of steps with steps that create instances of defend types of objects, connect them together, and set them to communicating with each other
For example, in an HR/Payroll application, we might have object classes that represent employees, dependents, checks, departments, financial accounts, etc
The act of creating a check for an employee could trigger a number of actions:
- update the employee's accumulated pay, etc., fields - done in an employee object
- update accumulated department totals - performed in a department object
- debit a financial account - done in an account object
- cause a physical check to be printed - a check object would be created encapsulating the information, and sent to a printing module (in some sort of object dedicated to similar generic operations)
- upon successful printing of the check, notifications would be sent to the above objects to finalize their operations
Encapsulation is the concept that the data and operations that work on or with that data are bundled together into one unit - the object
Objects are said to have state and behavior
- the state is the current set of values for all the data values
- terms commonly used to describe the state values are:
- data members
- fields
- properties
- attributes
- the behavior is the set of operations that the object
can perform (the functional code for the object)
- commonly called methods, but also called functions sometimes
Encapsulation
In a procedural program, the executable code is usually kept in a separate area from the data values
- in general, the programmer will tell the code for an entity where it's data is
In an object-oriented program, it will appear to the programmer that the data values are kept with the executable code
- each object has a memory area, which can be viewed as having a data block and a code block (note that this is a dramatic simplification - in practice the data is indeed kept separate from the data, but the compiler and/or runtime environment handles the details necessary to match an object's code with its data)
- an entity's code automatically knows where its data is
Encapsulation is often said to include the concept of data hiding, where the data elements of an object are not directly invisible to the outside world. Instead, methods are provided to retrieve and set the values of the data elements
- forcing external code to go through code inside the class allows for validation of data; for example, if a field must have a value that is greater than or equal to zero, the method to set that field can perform a test on incoming data to ensure that it is valid
Exercise: Object Definition
We would like to develop a simple employee database system that will include the ability to pay an employee (print a check for them, and processing necessary for bookeeping)
- As a first step, we will define a type of object to
represent an employee
- what data fields would we be likely to need (names and the type of data they would hold)
- what methods might be associated with an employee's data?
Creating and Using an Instance of an Object
This discussion applies to object types that have already been defined; we will look at how to define object types later
When an object is actually created and stored, that is known as an instance of the class
- the process is often called instantiating an object
The keyword new is almost always used to instantiate an object:
StringBuffer sb = new StringBuffer("Hello World");
- the variable sb now holds an instantiated StringBuffer object (StringBuffer is a class from the Java API)
- creating a String does not require the new keyword, text in quotes automatically becomes a String object:
String s = "Hello World";
To use an object, we need an expression that provides a reference to the object
- the dot operator ( . ) following an object reference gives access to the elements of that object
- so, if we know that the String class has a method toUpperCase() that returns an uppercase version of the text, we can now call that method to get our message in uppercase:
s.toUpperCase();
- we could use this in our Hello.java program as follows:
System.out.println(s.toUpperCase());
- go ahead and modify Hello.java to print the message in uppercase
References
When you create an object, it occupies some memory location that the JVM allocates at runtime
If you then have a reference and set it equal to that object, the variable’s memory location stores the numeric address of the memory location where the object is stored (often it is said that it points to the object’s memory location; it is more appropriate to say that it references the object)
If you set one object variable equal to another, you do not have two copies of the object - you have one object and two variables that point to that object
If you had the following:
MyClass myObject = new MyClass(); MyClass yourObject = myObject;
You would not end up with two copies of the same data, you would end up with two references to the same object (since all you really did was store another copy of the original object’s memory address)
So, if you then changed the properties of myObject, the properties as seen through yourObject would change as well
Note: there is a clone() method available to copy an object's contents to a new object
Although this is a technicality, it is an important one: certain Java classes are immutable, or final, that is, they cannot be changed once they are created
- String objects are immutable, as are the wrapper classes that hold primitive data types inside objects
- you can still set a String reference variable to a new value, but what you are doing is setting the reference to point to a different object
- the important point to note is that if there were other references to the original String, they would be unchanged - this follows from how references work, but is counter to how some languages work
Reference Example
In the following code the new keyword creates an object, in this case a new StringBuffer object
Code Sample: Java-Objects/Demos/References.java
public class References {
public static void main(String[] args) {
StringBuffer sb1 = new StringBuffer("Hello");
StringBuffer sb2 = null;
sb2 = sb1;
sb2.append(" World");
System.out.println(sb1);
}
}
- the first line creates a StringBuffer object containing the text Hello, then creates a reference variable called sb1 that points to that new object
- the second line creates a reference variable called sb2 that points to nothing (it is set to null)
- the third line then sets sb2 to point to the same object that sb1 points to
- the last line changes sb2; the effect of this change is seen via sb1 as well
Reference Expressions
A reference is a simple form of expression. References from more complex expressions may be used as well. For, example, with an array of String references called names, a method for one element can be called in this manner:
names[1].toUpperCase()
Similarly, if a method returns a reference, a method may be called directly on the returned value
Code Sample: Java-Objects/Demos/ReferencesChained.java
public class ReferencesChained {
public static void main(String[] args) {
StringBuffer sb1 = new StringBuffer("Hello");
StringBuffer sb2 = null;
sb2 = sb1;
sb2.append(" World").append(" Again");
System.out.println(sb1);
}
}
The StringBuffer.append method returns a reference to the same StringBuffer object, so that another call to append or any other method can be chained to the result.
Defining a Class
The basic syntax for defining a class is:
[modifiers] class ClassName {
(property definitions, constructors, and methods go here)
}
Example (for overall format only, we will examine the pieces later):
Code Sample: Java-Objects/Demos/BookBasic.java
public class BookBasic {
private int itemCode;
private String title;
public int getItemCode() {
return itemCode;
}
public void setItemCode (int newItemCode) {
if (newItemCode > 0) itemCode = newItemCode;
}
public String getTitle() {
return title;
}
public void setTitle (String newTitle) {
title = newTitle;
}
public void display() {
System.out.println(itemCode + " " + title);
}
}
Normally the access term for the class should be public, which means that the class can be used in other classes
- a public class definition must be in a file with a name that matches the class name, but classes with other access levels do not have that requirement
Access to Data
Class definitions may have different access levels - this determines where an object of the class may be instantiated or otherwise used
- class definitions are usually made freely available to all code, defined as public
All data and function members in a class are available to any code written for that class
- note that the normal scope rules for local variables apply to Java - that is, a method's internal variables are never available to other methods
There are four possible access states, three are declared with access keywords
- public - the member may be freely accessed by code from any other class
-
protected - the
member may not be accessed by code from any other class, except:
- classes that inherit from this class
- classes in the same package (a package is a collection of classes - basically all the classes stored in the same directory)
- private - the member may not be accessed by code in any class, including classes that inherit from this one
-
there is a fourth possibility, the default access used if you use
no access word at all
- any code in the same package (the same directory) can access the member
- this type of access is often called package access, and is also sometimes called default or friendly access
Example - the example from the previous page defines a class that will be publicly available, so that any class could instantiate one
- the class listed below can use that class in its code, but cannot directly access the title or itemCode properties, so it must call the setTitle and setItemCode methods to set properties, and getTitle and getItemCode to retrieve properties:
Code Sample: Java-Objects/Demos/UseBookBasic.java
public class UseBookBasic {
public static void main(String[] args) {
BookBasic b = new BookBasic();
b.setItemCode(5011);
b.setTitle("Fishing Explained");
System.out.println(b.getItemCode() + " " + b.getTitle());
System.out.print("From display: ");
b.display();
}
}
More on Access Terms
What the access terms control is where the name of the item may be written in your code
- if the item is public, then the name of the item may be written within code for any other class
- if the item is private, then the name of the item may not be written within the code for any other class
- package access is the default if no access term is specified; this allows the name of the item to be written within code for any class in the same package (which maps to the same directory)
- if the item is protected, then the name of the item may not be written within the code for any other class, except one that extends this class, or a class in the same package
For example:
- if a class definition is public, then you can write the name of the class in code in some other class (so you can use the class)
- if it is private, then you can't write the name in any other class, so you couldn't use it (in fact, a private class definition can only be used in one very special situation, to be covered later)
For another example:
- if the class is public, and contains a public element, you can write the name of the class in another class, and you can write the name of the element in that class as well - and if the element is private, then you can't write its name, so you can't use it
- so, the UseBookBasic class code, which has a BookBasic variable b, can use b.getItemCode() and b.setItemCode(5011), but not b.itemCode
Note: this does not mean that a private item cannot be affected by the code in another class; it just can't have its name written in that other class
- the example on the previous page shows how public methods can provide controlled access to a private element - in this case to ensure that the value of itemCode is never set to a negative value
Adding Data Members to a Class
Data members are added using an access specifier, a data type keyword, and the property name
[modifiers] class ClassName {
[modifiers] dataType propertyName1, propertyName2, . . . ;
[modifiers] dataType propertyName3 = value, . . . ;
(etc.)
}
- note that multiple members of the same type may be defined with one statement
- variables may be given initializing values when declared
- primitive data elements are initialized to 0 unless explicitly set to a value (and remember that for a boolean value a 0 bit means false)
- member elements that are object reference variables are automatically set to null if not given a specific initial value (null is a keyword)
Revisiting our BookBasic example:
class BookBasic {
private int itemCode;
private String title;
. . .
}
- this class has integer variable itemCode and a reference to a String object called title
- neither of these can be directly accessed from outside the class definition, since they are private
Adding Function Members (Methods) to a Class
Methods may be added in a similar manner
- note that in Java, unlike C++, the method body must be defined when the function is declared - you can't postpone that until later
[modifiers] class ClassName {
[modifiers] dataType propertyName1, propertyName2, . . . ;
[modifiers] dataType propertyName3 = value, . . . ;
[modifiers] returnType methodName(paramType paramName, . . . ) {
(method body code goes here)
}
(more methods here)
}
Methods may freely access all data and function members of the class
Standard Practices for Properties and Methods
It is considered a good practice to have properties be private, with access provided by "set and get" methods that are public
Java has a naming convention that, for a property named value, there should be methods setValue (accepting a parameter of the same type as value) and getValue() which would return a value whose type is the same as value)
- get methods do not take any parameters; set methods take one parameter whose type matches the type of the property
- note that for boolean true/false values, the convention also uses is and has as prefixes for the get methods (as in isEnabled() for a property called enabled)
It is also considered a good practice to reuse existing methods for any steps that work with data in an way other than simple retrieval
- for example, if we had a method that accepted both itemCode and title, we would call the setItemCode() and setTitle() methods rather than access those properties directly
- that way if our approach to setting a value changes, the effect is automatically applied through all methods that do that
public void setItemCodeAndTitle (int newItemCode, String newTitle) {
setItemCode(newItemCode);
setTitle(newTitle);
}
Java Beans
If you follow the standard naming conventions, you are most of the way to creating a Java Bean class
- a bean is an object that can be created and used by various tools, both at development time and at run time
- Java has a mechanism known as reflection, inspection or introspection that allows for classes to be instantiated and used without knowing at
compile time what the class name is
- a bean can be instantiated using a String variable containing the class name
- or a bean can be delivered to an application, for example, across a network connection
- at runtime, for any object, its methods and fields can be determined by the JVM, as it inspects the object
- tools can then match field names to get and set methods and treat the fields as properties (to this point, we have used the terms fields and properties more or less interchangeably - in bean-speak fields are data values and properties are private fields that can be accesses by set and/or get methods)
- example uses of beans include:
- Java-based GUI development tools can create instances of GUI component beans such as text fields, radio buttons, etc., and create a properties table where the programmer can enter values into text fields for values such as background color, foreground color, text value, etc. (keep in mind that the environment's JVM can inspect the component objects)
- Java Server Pages can instantiate a bean and set and retrieve properties to display in a dynamic web page
- to be a bean, a class must:
- have a constructor that takes no parameters (the object will be created empty, and populated from the set methods - if there was a Rectangle class, for example, with height and width properties that were int, the tool wouldn't know how to call a constructor that took both parameters (which is the height and which is the width?)
- follow the naming convention stated above
- a bean can also have additional methods that code with advance knowledge of the class can call
Bean Properties
Properties are defined by public get and set methods, and usually map to private fields
- a read-only property would have a get method, but no method to set the
value - the property might or might not be backed by
a field
Code Sample: Java-Objects/Demos/Rectangle.java
public class Rectangle { private int height; private int width; public Rectangle() { } public Rectangle(int height, int width) { setHeight(height); setWidth(width); } public int getHeight() { return height; } public void setHeight(int height) { this.height = height; } public int getHeight() { return height; } public void setWidth(int width) { this.width = width; } public int getArea() { return height * width; } }Code ExplanationThe class has a constructor that takes no parameters, plus methods to get and set the height and width. The area is a read-only property that is not backed by a field - it is calculated each time it is needed.
A Java Server Page might instantiate a Rectangle bean, populate the height and width from values submitted from a form, and send back a page showing the area of the rectangle.
- less often, you might encounter a write-only property - a simplistic example might be a security bean that uses a key to encrypt and decrypt messages: you could set the key, but not retrieve it (and other methods would set an incoming encrypted message and retrieve the decrypted version, or set an outgoing message and retrieve the encrypted result)
Exercise: Payroll01: Creating an Employee Class
After review with management, it is decided that we will store the following information for each employee:
- employee ID (an integral number, automatically assigned when a new employee is added)
- first name and last name
- department number (an integral value, call it dept)
- pay rate (a floating-point value, using a double)
In addition, we would like:
- a method called getPayInfo() that will return a sentence with the employee's name, id, department, and check amount
- a method called getFullName() that will return the first and last names separated by a space
- Define a class called Employee with these characteristics, using standard practices for limiting data access and for method naming
- In order to be useful, there should be methods to set and get all properties (except setting the employee id, which will happen automatically in a manner to be determined later; for now, just let it default to 0)
- Create a class called Payroll with a main method, it should:
- instantiate an Employee object
- set values for all properties
- call the getPayInfo() method to see the results
Constructors
Every class should have at least one method: the constructor
- this specifies code that must run to instantiate (usually to initialize) a new object from the class definition
The constructor is a function with the same name as the class
[modifiers] class ClassName {
[modifiers] ClassName(paramType paramName, . . . ) {
(code goes here)
}
}
- the name of the constructor is the the same as the name of the class
- constructors have no return type and do not return a value
- they make take input parameters, usually to populate the fields for the new object
- they may use the standard access keywords - usually they are public (but in some circumstances involving inheritance they are sometimes protected or private)
The methods in an object are available at construction time. While a constructor could set properties directly, it is considered a better practice to call the set methods to store any values. That way, any validation necessary on the data can be accomplished without duplicating the validating code.
Code Sample: Java-Objects/Demos/BookWithConstructor.java
public class BookWithConstructor {
private int itemCode;
private String title;
public BookWithConstructor(int newItemCode, String newTitle) {
setItemCode(newItemCode);
setTitle(newTitle);
}
public int getItemCode() {
return itemCode;
}
public void setItemCode (int newItemCode) {
if (newItemCode > 0) itemCode = newItemCode;
}
public String getTitle() {
return title;
}
public void setTitle (String newTitle) {
title = newTitle;
}
public void display() {
System.out.println(itemCode + " " + title);
}
}
Note how the constructor can make use of the existing methods.
Code Sample: Java-Objects/Demos/UseBookWithConstructor.java
public class UseBookWithConstructor {
public static void main(String[] args) {
BookWithConstructor b =
new BookWithConstructor(5011, "Fishing Explained");
b.display();
}
}
Instantiating Objects Revisited
You instantiate an object from a class using the new keyword
objectReferenceVariable = new ClassName(parameters);
- note that the type of the object reference variable must match the type of the class being instantiated (or, for later, match a parent of that class)
- you can pass parameters as long as the parameter list matches a constructor for that class
- note that while the object reference is often stored in a variable, it does not have to be - an object can be instantiated in any situation where an object reference could be used, for example:
new BookWithConstructor(1234, "Help is on the Way").display();
When a new instance is created, the following sequence of events takes place (note that this includes only concepts we have covered thus far - later we will expand the sequence as we cover additional topics)
- memory is allocated in an appropriately sized block
- the entire block is set to binary zeros (so every property is implicitly initialized to 0)
- explicit initializations of properties take place
- the constructor runs
- the expression that created the instance evaluates to the address of the block
Important Note on Constructors
If you create no constructors at all, you will get a default constructor that accepts no arguments and does nothing (but at least you can use it to instantiate an object)
If you write a constructor that accepts arguments, then you lose the default constructor
- if you still want to have a no-argument constructor that does nothing, you must explicitly write it
- try modifying UseBookWithConstructor.java to add line like Book c = new Book();
Exercise: Payroll02: Adding an Employee Constructor
- Modify your Employee class to use a constructor that accepts parameters for the first and last names, department, and pay rate
- In Payroll, modify main to add another Employee variable that receives an instance created using this constructor
- Also add a constructor that takes no parameters and does
nothing (as we discussed above)
public Employee() { }
Method Overloading
Methods can be overloaded to have several versions, all with the same name
- the difference is in the parameter lists, also known as the function signature
- differences in return types are not enough to create a second version of a function, the parameter lists must be different
[modifiers] returnType functionName(paramlist) { . . . }
[modifiers] returnType functionName(differentParamList) { . . . }
Constructors are often overloaded, so that an object can be instantiated from different combinations of parameters
This is an example of polymorphism - the concept that the same name may have several forms. Method overloading produces a functional polymorphism, since the same method name can be used in different ways. While polymorphism is mainly used to describe having one variable that can store different but related types due to inheritance, the concept also applies to overloaded methods at a smaller scale.
Continuing our example:
Code Sample: Java-Objects/Demos/BookMultiConstructor.java
public class BookMultiConstructor {
private int itemCode;
private String title;
public BookMultiConstructor(int newItemCode, String newTitle) {
setItemCode(newItemCode);
setTitle(newTitle);
}
public BookMultiConstructor(String newTitle) {
setItemCode(0);
setTitle(newTitle);
}
public int getItemCode() {
return itemCode;
}
public void setItemCode (int newItemCode) {
if (newItemCode > 0) itemCode = newItemCode;
}
public String getTitle() {
return title;
}
public void setTitle (String newTitle) {
title = newTitle;
}
public void display() {
System.out.println(itemCode + " " + title);
}
}
Code Sample: Java-Objects/Demos/UseBookMultiConstructor.java
public class UseBookMultiConstructor {
public static void main(String[] args) {
BookMultiConstructor b = new BookMultiConstructor(5011, "Fishing Explained");
b.display();
// note the immediate call to a method on a new instance
new BookMultiConstructor("Dogs I've Known").display();
}
}
Exercise: Payroll03: Overloading Employee Constructors
-
Add more Employee constructors:
- one that takes values for first and last names only
- one that takes values for first name, last name, and department
- one that takes values for first name, last name, and pay rate
- note that, in practice, you can use the parameter lists for constructors to help enforce what you would consider valid combinations of properties - for example, if you would not want an employee to exist in a state where they had name and department information, but no pay rate, then the absence of that particular constructor would help ensure that
- judicious use of copy-paste-edit can speed up this process, but be careful to make every necessary edit if you do this!
- Create and pay additional instances using these constructors
- You will find yourself writing somewhat repetitive code, setting the same values the same way in several different constructors - we will address this in a few pages
The this Keyword
The this keyword this provides a reference to the current object
Used to resolve name conflicts
-
if you have a method that receives an input parameter String s, but s is already a member variable String reference
class X { String s; public void setS(String s) { this.s = s; } }- within the function, the parameter s is a local variable that will disappear when the function ends
- it hides the existence of the s member field
- but, we can pass the local variable's value into the field s by using the this reference to resolve s as the one that belongs to this object
- some programmers always use this approach with constructor functions and set methods - the benefit is that the code is more self-documenting, since it would use the most appropriate variable name in both the class definition and in the method argument
Used when the object's code needs to pass a reference to itself to the outside
- say you are creating MyClass
- some other class, YourClass, has a public method called useMyClass that needs a reference to a MyClass object; in other words, something like:
public void useMyClass(MyClass x) { . . . }
- your code for MyClass could do:
YourClass yC = new YourClass(); yC.useMyClass(this);
The this keyword is also used in a constructor function to call another constructor from the same class
Using this to Call Another Constructor
As we have seen, to avoid duplicating code in different functions, any function in a class may call other functions in that class
- a function that is not a constructor can only call other functions that are not constructors (although it can still cause a constructor to run by instantiating an object)
- a constructor may only call one other constructor, and it must be done as the first line in the function
- the this keyword is used instead of the name of the constructor, then arguments are passed in a normal fashion
The following example uses this to provide the object with a reference to itself, as well as to chain to another constructor:
Code Sample: Java-Objects/Demos/BookUsingThis.java
public class BookUsingThis {
private int itemCode;
private String title;
public BookUsingThis(int itemCode, String title) {
setItemCode(itemCode);
setTitle(title);
}
public BookUsingThis(String title) {
this(0, title);
}
public int getItemCode() {
return itemCode;
}
public void setItemCode (int itemCode) {
if (itemCode > 0) this.itemCode = itemCode;
}
public String getTitle() {
return title;
}
public void setTitle (String title) {
this.title = title;
}
public void display() {
System.out.println(itemCode + " " + title);
}
}
The constructor that only accepts the title calls the other constructor, passing 0 as the item code. It would also be possible to run set up the chain of constructors in the reverse direction, as in:
public BookUsingThis(int itemCode, String title) { this(title); setItemCode(itemCode); } public BookUsingThis(String title) { setTitle(title); }This approach is good if the default values of the fields (either zero or as set by an initializer) are acceptable - note that the constructor that accepts only title never sets the item code
Code Sample: Java-Objects/Demos/UseBookUsingThis.java
public class UseBookUsingThis {
public static void main(String[] args) {
BookUsingThis b = new BookUsingThis(5011, "Fishing Explained");
b.display();
new BookUsingThis("Dogs I've Known").display();
}
}
The second book uses the constructor that accepts only the title. Since the new operator results in a reference to the book, we can chain a call to display to that result.
Exercise: Payroll04: Using the this Reference
- Now we can clean up our Employee class definition
- Modify the set methods to use the same name for each parameter as the associated property
- Modify the constructors to eliminate redundant code - for example:
public Employee(String firstName, String lastName, int dept) { this(firstName, lastName); setDept(dept); }
static Elements
The keyword static states that one and only one of this element should be available at all times - whether there is one object of the class instantiated, many objects, or none at all
[modifiers] static dataType propertyName; [modifiers] static returnType methodName(paramType paramName, . . . )
If a data member is static, there is one memory location that is shared among all instances of a class
- this allows the separate instances of one class a way of sharing data with each other
- that memory location exists even if no objects of the class exist - this is useful for creating constants
- any initialization of this value occurs just once, when the class is loaded into the JVM
If a method is static and public, then it is available even when no objects have been instantiated
- this is used in the Java API for math functions like the random
number function and trigonometric functions - they have to be
part of a class, but you wouldn't want to have to instantiate some
special object just to generate a single random number
or calculate a cosine
- for more sophisticated random number generation, where repeatability of a sequence of random values might be desirable, there is a Random class
- one caveat with this approach is that the function cannot access any of the other elements (data members or methods) that are not also static, since it is not guaranteed that they will exist when the function is called
- such an element is referenced using the name of the class, a dot, then the element name:
double x = Math.random(); double y = Math.sqrt(x);
The main Method
Now we can see that main, as a static method, can be run without any instance of the class. In fact, no instance is created automatically - the JVM only needs to load the class into memory.
If you create a class that has properties or methods not marked as static, and try to access them from main, you will get a rather cryptic error message about a non-static element referenced from a static context. The problem is that there isn't necessarily an instance of your class in memory, or there may be more than one. To call a non-static element, you must first create an instance and access the element for that instance.
The following example draws from the GUI world, where the JFrame class has a setVisible method that this class inherits. The main method creates an instance of its own class to display.
import javax.swing.*;
public class MyGUI extends JFrame{
public static void main(String[] args) {
MyGUI gui = new MyGUI();
gui.setVisible(true);
}
}
Exercise: Payroll05: A static Property in Employee
- Add a private and static integer property called nextId to the Employee class (give it an initial value of 1)
- Modify the declaration of the id property as follows:
private int id = nextId++;
- What happens when each new Employee gets instantiated?
Garbage Collection
Java takes care of reclaiming memory that is no longer in use
Your program is not making memory-allocation calls directly to the operating system - the JVM requests a fairly large block at start time, and handles memory allocation from that block
- when necessary, it can request additional blocks from the OS
- there are command line options to set the initial, increment, and maximum sizes
When an object is no longer reachable through your code, it becomes subject to garbage collection
- the JVM has a low-priority thread that checks the graph of objects to find orphaned objects
- those that are found are marked as available to be collected
- a separate process will run when necessary to reclaim that memory
- if your program has small memory requirements, it is possible that garbage collection will never run
You can request garbage collection by calling System.gc()
- note that this may not have any effect; it is not guaranteed to run when you ask - the call is merely a request
You can specify code to run when an object is collected by writing a finalize() method in a class
- note that there is no guarantee that this method will ever run
- when the program ends, any objects still reachable will not be collected, nor will any objects marked for collection but not yet collected - thus finalization will not occur for those objects
Java Packages
As you have seen, Java programs may involve a large number of files
Packages help organize files within a program, as well as to collect objects into groups to improve reusability of code
A package structure is a tree structure that maps to a folder/directory structure
- package names correspond to directory names
- a dot is used between levels of a multilevel structure
- searching for files starts at the root level of the structure during compiling or class-load at runtime
- you can't use packages or directory structures without explicitly placing a class in a package via your code
- classes are always referenced from the root of the structure, and the fully-qualified name of the class contains the relative path elements to reach the class from the root point, separated by dot characters
To assign a class to a package, use a package statement as the first non-blank, non-comment line of code
| package test; |
puts this class in the package called test in order to be found, the class file must be in a directory named test that directory must be located in one of the classpath locations |
| package test.util; |
puts this class in the package called test.util in order to be found, the class file must be in a subdirectory of the test directory named util; test must be located in one of the classpath locations |
The accepted convention is that package names be all lowercase
Example
package test;
public class XYZ {
. . .
}
- XYZ.java should be in a directory called test that is located within a directory listed in our CLASSPATH
- the fully-qualified name of the class is test.XYZ
When the compiler and JVM look for the files, they will search every CLASSPATH entry for a directory called test containing XYZ.java (or XYZ.class, if that is what is needed)
Compiling and Executing with Packages
The default behavior of the compiler in JDK 1.4 and later is to compile source code in a directory structure into class files in the same structure
-
you can use the
-d option with javac to put the class files into a separate, parallel
directory structure
Syntax
javac -d rootofstructure *.java
- it will start the structure at the directory called rootofstructure
- it will build the directories if they don't already
exist
- in earlier versions of the JDK, all the source code, regardless of package, had to be in the same directory for that to work
To run an executable Java class that belongs to a package, your command prompt should be at the directory level that matches the root of the package structure
- you would then treat the class name as packagename.ClassName
Example - the directory C:\MyProject is the root level of a package structure, and the main class, XYZ.class, is in the package called test (therefore test is a subdirectory of MyProject)
To run XYZ.class, the command line would look like the following:
C:\MyProject>java test.XYZ
Working with Packages
Once a package structure is created, all classes in it must be referenced with respect to the root of the structure. You have two choices:
- explicitly use the fully-qualified name of the class
- import either that specific class or the entire package
Say your directory C:\Bookstore is the root of your project
- it contains BookstoreWithPackage.java (the main class)
- within that is a directory called types, which contains Book.java
Declaring a package
The BookWithPackage class would start like this:
package types;
public class BookWithPackage {
. . .
}
The package statement must be the first non-comment, non-blank line in the file.
Referencing Packaged Classes - Importing and Fully-Qualified Names
In BookstoreWithPackage.java, you could import the entire types package, or just the class(es) you need
import types.*;
or
import types.BookWithPackage;
then
public class BookstoreWithPackage {
public static void main(String[] args) {
BookWithPackage b = new BookWithPackage("My Favorite Programs");
. . .
}
}
Or, instead of importing, you could explicitly reference the class by its full name, which precedes the class name with the package structure (using a dot character as the directory separator)
public class BookstoreWithPackage {
public static void main(String[] args) {
types.BookWithPackage b = new types.BookWithPackage("My Favorite Programs");
. . .
}
}
This approach is necessary if your program uses two classes with the same name (such as java.sql.Date and java.util.Date)
All classes in the Java API are in packages. We have not had to deal with imports to this point because the package java.lang is special - it does not need to be imported. (This is where System, String, the numeric wrapper classes like Integer, and a few more generally useful classes reside.)
The package at the root level is called the default package.
When the main Class is in a Package
If your main class is in a package, then it must be compiled and run from the root of the structure
For example, say your directory C:\Bookstore is the root of your project, but it contains no classes
- it contains a subdirectory called bookstore, which contains Bookstore.java (the main class)
package bookstore; public class Bookstore { . . . }
- if that directory contained the types directory, the remainder of the code would be as before
To compile the program from the C:\Bookstore directory, use the standard operating system directory separator character:
javac bookstore\Bookstore.java
To run the program, you must be in the C:\Bookstore directory, use the dot character as the separator character:
java bookstore.Bookstore
Note that the preferred practice is to have no classes in the default package.
- since the package has no name, there is no way to access it or any of its classes from any other package!
- since no other class needs to reference the main class, for now we will leave that in the default package.
Other Package Considerations
It is a recommended convention that class created for external distribution use a package structure that begins with the creating organization's domain name in reverse order (for example, there are a few non-standard classes in the API provided by Sun that are in the com.sun package)
Sun recommends that package names be in all lowercase
Package access does not necessarily limit you to working in the same directory; you could have the same directory structure from a different root point in the classpath
Exercise: Payroll06: Creating an employees package
- Create a package called employees; put the Employee class into this package (leave Payroll where it is)
- This will require not only creating the directory and moving the file, but also adding an import statement to Payroll and a package statement to Employee
- To compile, start in the project root directory (the directory containing
Payroll). If you compile Payroll as usual, it will also find and compile
Employee. To compile just Employee, you would still work in the project
root directory, but execute
javac employees\Employee.java
- Run Payroll in the usual fashion
Dealing with Keyboard Input
Java input is very "low-level" - it deals with raw bytes of data, but, we can use classes designed to make input easier
Also, keyboard input is error-prone, requiring our program to handle exceptions, situations requiring special attention
Since it would be nice to have interactive programs, the following code gives an example of reading data from the keyboard
Code Sample: Java-Objects/Demos/InputTest.java
import java.io.*;
public class InputTest {
public static void main(String[] args)
throws Exception {
BufferedReader in =
new BufferedReader(
new InputStreamReader(System.in));
System.out.println("Enter a string");
String s = in.readLine();
System.out.println("Enter an integer");
int i = Integer.parseInt(in.readLine());
System.out.println("Enter a character");
char c = (char) in.read();
System.out.println("s=" + s + " i=" + i + " c=" + c);
}
}
Briefly:
- the import statement enables you to use classes not in the default set (in this case the input-related classes) - the API documentation lists the package for each class
- the raw input class System.in is filtered using an InputStreamReader, which allows it to recognize input as text characters (the basic InputStream is plugged into a more complex object that does additional processing)
- then, the resulting InputStreamReader is plugged into a BufferedReader that buffers keyboard input until it recognizes the Enter keystroke
- lines can now be read using readLine(), and characters can be read using read() (note that they are read as integers, thus the typecast)
- a sequence of digits can be parsed using a static method of the Integer class
- since the whole input and parsing process is error-prone, we will avoid issues by having main throw an exception (much more on exceptions later)
A Keyboard Reader Class
The following class, KeyboardReader.java, can be used to read from the keyboard:
Code Sample: Java-Objects/Demos/util/KeyboardReader.java
package util;
import java.io.*;
public class KeyboardReader {
private BufferedReader in;
public KeyboardReader() {
in = new BufferedReader(new InputStreamReader(System.in));
}
public String getPromptedString(String prompt) throws IOException {
System.out.print(prompt);
return in.readLine();
}
public char getPromptedChar(String prompt) throws IOException {
System.out.print(prompt);
return in.readLine().charAt(0);
}
public int getPromptedInt(String prompt) throws IOException {
System.out.print(prompt);
return Integer.parseInt(in.readLine());
}
public float getPromptedFloat(String prompt) throws IOException {
System.out.print(prompt);
return Float.parseFloat(in.readLine());
}
public double getPromptedDouble(String prompt) throws IOException {
System.out.print(prompt);
return Double.parseDouble(in.readLine());
}
}
Exercise: Payroll07: Using KeyboardReader in Payroll
-
Add another employee to your payroll, this time using
one KeyboardReader object to prompt the user for all data;
for example, to read a double-precision value for a width:
KeyboardReader kbr = new KeyboardReader(); double width = kbr.getPromptedDouble("Please enter the width: "); -
note: you will need change the declaration of main as
follows:
public static void main(String[] args throws Exception {
String, StringBuffer, and StringBuilder
Java has three primary classes for working with text
The String class is optimized for retrieval of all or part of the string, but not for modification (it is assumed to be written once and read many times)
- this comes at the expense of flexibility
- once created, a String object is immutable, that is, its contents cannot be changed
- methods like toUpperCase() that seem like they would change the contents actually return a brand-new String object with the new contents
The StringBuffer and StringBuilder classes are optimized for changes
- they are essentially the same as far as available methods, but StringBuilder is not safe for multithreaded applications while StringBuffer is
- they are useful for building a long string from multiple pieces, or for text-editing applications
- among the useful methods are:
- append(String s) and other forms accepting various primitive data types and a form that accepts another StringBuffer object
- insert(int position, String s) to insert text at (in front of) the specified position; again, various forms for different types of data
- delete(int start, int end) to delete characters from the start position up to but not including the end position
- toString() will convert the contents of the object to a String object
-
note that the modifying methods listed above modify the
contents of the object, but also return a this reference,
(a reference to the same object) - this enables chaining of method calls:
StringBuffer sb = new StringBuffer(); sb.append("Hello").append(" World"); System.out.println(sb.toString());
Creating Documentation Comments and Using javadoc
Java documentation can be created as part of the source code. If you embed javadoc comments in your code the javadoc documentation engine supplied with the jdk will create a set of interlinked HTML pages, one for each class.
Javadoc Comments
A javadoc comment is a block comment beginning with /**, and ending with */
- they may be used before any documentable item: (classes, fields, constructors, and methods - if used in other places they will be ignored by the documentation engine )
- use them to provide descriptions of the item
- many HTML tags are valid within the comment - in particular, formatting tags like <em> and <code> are often used, as well as lists
- there are a number of special block tags that begin with @, such
as:
- @param documents a method or constructor parameter
- @return documents a return value
- {@link BookWithJavadoc} creates a hyperlink to the specified class page
javadoc options filelist
Command line options include:
- -d destinationdirectory to produce the output in a specific directory, which need not already exist (default is current directory)
- options for access levels to document (each option generates documentation
for that level and every more accessible level - e.g., protected documents
public, package, and protected elements)
- -private
- -package
- -protected
- -public
The file list can use file names, package names, wildcards, like *.java (separate multiple elements with spaces)
The following command places the output in a subdirectory docs of the current directory, and documents the "default package" and the employees package (use this in the upcoming exercise)
javadoc -d docs -private employees *.java
See Sun's How To reference for more information
Code Sample: Java-Objects/Demos/BookWithJavadoc.java
/**
* Represents a Book in inventory,with an item code and a price
*/
public class BookWithJavadoc {
/**
* The book's item code
*/
private int itemCode;
/**
* The title of the book
*/
private String title;
/**
* Creates a book instance. It is expected that the value will
* be non-negative; a negative value will be rejected.
* @param itemCode the book's item code
* @param title the title of the book
*/
public BookWithJavadoc(int itemCode, String title) {
setItemCode(itemCode);
setTitle(title);
}
/**
* Creates a book instance, The item code willl be set to 0.
* @param title the title of the book
*/
public BookWithJavadoc(String title) {
setItemCode(0);
setTitle(title);
}
/**
* Retrieves the item code for the book.
* @return the book's item code
*/
public int getItemCode() {
return itemCode;
}
/**
* Sets the item code for the book. It is expected that the value will
* be non-negative; a negative value will be rejected.
* @param itemCode the book's item code
*/
public void setItemCode (int itemCode) {
if (itemCode > 0) this.itemCode = itemCode;
}
/**
* Retrieves the title of the book.
* @return the title of the book
*/
public String getTitle() {
return title;
}
/**
* Sets the title of the book.
* @param title the title of the book
*/
public void setTitle (String title) {
this.title = title;
}
public void display() {
System.out.println(itemCode + " " + title);
}
}
Even though some of the documentation will often be trivial, parameters and return values should always be documented. Any restrictions on parameter values, such as the non-negative parameters, should be mentioned
Code Sample: Java-Objects/Demos/UseBookWithJavadoc.java
/**
* Tests the {@link BookWithJavadoc} class.
*/
public class UseBookWithJavadoc {
/**
* Tests the {@link BookWithJavadoc} class.
*/
public static void main(String[] args) {
BookWithJavadoc b = new BookWithJavadoc(5011, "Fishing Explained");
b.display();
}
}
Note the use of the {@link} tag, since nothing else (parameter or return types) would mention the BookWithJavadoc class otherwise
Exercise: Payroll08: Creating and Using javadoc Comments
- Add javadoc comments to all the properties and methods in Employee. Don't worry about making them completely descriptive, but do document parameters and return values for the methods. Note that judicious use of copy and paste can speed up the process.
- Run javadoc and view index.html in a browser.
Java Objects Conclusion
In this section we have learned how to define and use classes, using:
- fields
- methods
- constructors
- public and private access
- overloading
- the this reference
- packages
- javadoc comments