|
JavaFAQ Home » Java Lectures by Anatoliy Malyarenko

Classes
by: Anatoliy Malyarenko
Abstract
Contents of the lecture.
- Creating classes.
- The class declaration.
- The class body.
- Providing constructors for your classes.
- Declaring member variables.
- Implementing methods.
- Controlling access to members of a class.
- Understanding instance and class members.
Creating classes
Consider a small example that implements a last-in-first-out (LIFO) stack. The following
diagram lists the class and identifies the structure of the code.

This implementation of a stack uses another object, a Vector, to store its elements.
Vector is a growable array of objects and does a nice job of allocating space for new objects
as space is required. The Stack class makes use of this code by using a Vector to store its
elements. However, it imposes LIFO restrictions on the Vector -- that is, you can only add
elements to and remove elements from the top of the stack.
The class declaration
The left side of the following diagram shows the possible components of a class
declaration in the order they should or must appear in your class declaration. The right side
describes their purposes. The required components are the class keyword and the class
name and are shown in bold. All the other components are optional, and each appears on a
line by itself (thus "extends Super" is a single component). Italics indicates an identifier such
as the name of a class or interface. If you do not explicitly declare the optional items, the Java
compiler assumes certain defaults: a non-public, non-abstract, non-final subclass of Object
that implements no interfaces.

The following list provides a few more details about each class declaration component.
public
By default, a class can be used only by other classes in the same package. The public
modifier declares that the class can be used by any class regardless of its package.
abstract
Declares that the class cannot be instantiated.
final
Declares that the class cannot be subclassed.
class NameOfClass
The class keyword indicates to the compiler that this is a class declaration and that the
name of the class is NameOfClass.
extends Super
The extends clause identifies Super as the superclass of the class, thereby inserting
the class within the class hierarchy.
implements Interfaces
To declare that your class implements one or more interfaces, use the keyword
implements followed by a comma-separated list of the names of the interfaces implemented
by the class.
The class body
The class body contains all of the code that provides for the life cycle of the objects
created from it: constructors for initialising new objects, declarations for the variables that
provide the state of the class and its objects, methods to implement the behaviour of the class
and its objects, and in rare cases, a finalize method to provide for cleaning up an object
after it has done its job.
Variables and methods collectively are called members.
Note: Constructors are not methods. Nor are they members.
The Stack class defines one member variable in its body to contain its elements -- the items Vector. It also defines one constructor -- a no-argument constructor -- and three methods: push, pop, and isEmpty.
Providing constructors for your classes
All Java classes have constructors that are used to initialise a new object of that type.
A constructor has the same name as the class. For example, the name of the Stack class's
constructor is Stack, the name of the Rectangle class's constructor is Rectangle, and the
name of the Thread class's constructor is Thread. Stack defines a single constructor:
public Stack() {
items = new Vector(10);
}
Java supports name overloading for constructors so that a class can have any number
of constructors, all of which have the same name. Following is another constructor that could
be defined by Stack. This particular constructor sets the initial size of the stack according to
its parameter:
public Stack(int initialSize) {
items = new Vector(initialSize);
}
Both constructors share the same name, Stack, but they have different parameter lists.
The compiler differentiates these constructors based on the number of parameters in the list
and their types.
Typically, a constructor uses its arguments to initialise the new object's state. When
creating an object, choose the constructor whose arguments best reflect how you want to
initialise the new object.
Based on the number and type of the arguments that you pass into the constructor, the
compiler can determine which constructor to use. The compiler knows that when you write the
following code, it should use the constructor that requires a single integer argument:
new Stack(10);
Similarly, when you write the following code, the compiler chooses the no-argument
constructor or the default constructor:
new Stack();
When writing your own class, you don't have to provide constructors for it. The default
constructor is automatically provided by the runtime system for any class that contains no
constructors. The default provided by the runtime system doesn't do anything. So, if you want
to perform some initialisation, you will have to write some constructors for your class.
The constructor for the following subclass of Thread performs animation, sets up some
default values, such as the frame speed and the number of images, and then loads the images:
class AnimationThread extends Thread {
int framesPerSecond;
int numImages;
Image[] images;
AnimationThread(int fps, int num) {
super("AnimationThread");
this.framesPerSecond = fps;
this.numImages = num;
this.images = new Image[numImages];
for (int i = 0; i <= numImages; i++) {
. . .
// Load all the images.
. . .
}
} . . .
}
Note how the body of a constructor is like the body of a method; that is, it contains local
variable declarations, loops, and other statements. However, one line in the AnimationThread
constructor that you wouldn't see in a method is the second line:
super("AnimationThread");
This line invokes a constructor provided by the superclass of AnimationThread,
namely, Thread. This particular Thread constructor takes a String that sets the name of
Thread. Often a constructor wants to take advantage of initialisation code written in a class's
superclass. Indeed, some classes must call their superclass constructor in order for the object
to work properly. If present, the superclass constructor must be the first statement in the
subclass's constructor: An object should perform the higher-level initialisation first.
You can specify what other objects can create instances of your class by using an access
specifier in the constructors' declaration:
private
No other class can instantiate your class. Your class may contain public class methods
(sometimes called factory methods), and those methods can construct an object and return it,
but no other classes can.
protected
Only subclasses of the class and classes in the same package can create instances of
it.
public
Any class can create an instance of your class.
no specifier gives package access
Only classes within the same package as your class can construct an instance of it.
Declaring member variables
Stack uses the following line of code to define its single member variable:
private Vector items;
This declares a member variable and not some other type of variable (like a local
variable) because the declaration appears within the class body but outside of any methods
or constructors. The member variable declared is named items, and its data type is Vector.
Also, the private keyword identifies items as a private member. This means that only code
within the Stack class can access it.

This is a relatively simple member variable declaration, but declarations can be more
complex. You can specify not only type, name, and access level, but also other attributes,
including whether the variable is a class or instance variable and whether it's a constant. Each
component of a member variable declaration is further defined below:
accessLevel
Lets you control which other classes have access to a member variable by using one of
four access levels: public, protected, package, and private. You control access to methods in
the same way.
static
Declares this is a class variable rather than an instance variable. You also use static
to declare class methods.
final
Indicates that the value of this member cannot change.
The following variable
declaration defines a constant named AVOGADRO, whose value is Avogadro's number (6.022 ·
1023) and cannot be changed:
final double AVOGADRO = 6.022e23;
It's a compile-time error if your program ever tries to change a final variable.
By
convention, the name of constant values are spelled in uppercase letters.
transient
The transient marker is not fully specified by The Java Language Specification but is
used in object serialisation to mark member variables that should not be serialised.
volatile
The volatile keyword is used to prevent the compiler from performing certain
optimisations on a member. This is an advanced Java feature, used by only a few Java
programmers.
type
Like other variables, a member variable must have a type. You can use primitive type
names such as int, float, or boolean. Or you can use reference types, such as array, object,
or interface names.
name
A member variable's name can be any legal Java identifier and, by convention, begins
with a lowercase letter. You cannot declare more than one member variable with the same
name in the same class, but a subclass can hide a member variable of the same name in
its superclass. Additionally, a member variable and a method can have the same name. For
example, the following code is legal:
public class Stack {
private Vector items;
// a method with same name as a member variable
public Vector items() {
. . .
}
}
Implementing methods
This figure shows the code for Stack's push method. This method pushes an object,
the one passed in as an argument, onto the top of the stack, and returns it.

Like a class, a method has two major parts: method declaration and method body. The
method declaration defines all of the method's attributes, such as access level, return type,
name, and arguments, as illustrated here:
The method body is where all the action takes place. It contains the Java instructions
that implement the method.
Details of a method declaration
A method's declaration provides a lot of information about the method to the compiler,
to the runtime system, and to other classes and objects. Included is not only the name of the
method, but also such information as the return type of the method, the number and type of the
arguments required by the method, and which other classes and objects can call the method.

While this may sound like writing a novel rather than simply declaring a method,
most method attributes can be declared implicitly. The only required elements of a method
declaration are the method's name, its return type, and a pair of parentheses (). This figure
shows the elements of a method declaration.
Each element of a method declaration is further defined below:
accessLevel
As with member variables, you control which other classes have access to a method
using one of four access levels: public, protected, package, and private.
static
As with member variables, static declares this method as a class method rather than
an instance method.
abstract
An abstract method has no implementation and must be a member of an abstract class.
final
A final method cannot be overridden by subclasses.
native
If you have a significant library of functions written in another language such as C,
you may wish to preserve that investment and use those functions from Java. Methods
implemented in a language other than Java are called native methods and are declared as
such using the native keyword.
synchronized
Concurrently running threads often invoke methods that operate on the same data.
These methods may be declared synchronised to ensure that the threads access information
in a thread-safe manner.
returnType
Java requires that a method declare the data type of the value that it returns. If your
method does not return a value, use the keyword void for the return type.
methodName
A method name can be any legal Java identifier. You need to consider several issues in
regards to Java method names.
(paramlist)
You pass information into a method through its arguments.
[throws exceptions]
If your method throws any checked exceptions, your method declaration must indicate
the type of those exceptions.
Returning a value from a method
You declare a method's return type in its method declaration. Within the body of the
method, you use the return operator to return the value. Any method that is not declared
void must contain a return statement. The Stack class declares the isEmpty method,
which returns a boolean:
public boolean isEmpty() {
if (items.size() == 0)
return true;
else
return false;
}
The data type of the return value must match the method's return type; you can't return
an Object type from a method declared to return an integer. The isEmpty method returns
either the boolean value true or false, depending on the outcome of a test. A compiler error
results if you try to write a method in which the return value doesn't match the return type.
The isEmpty method returns a primitive type. Methods also can return a reference type.
For example, Stack declares the pop method that returns the Object reference type:
public synchronized Object pop() {
int len = items.size();
Object obj = null;
if (len == 0)
throw new EmptyStackException();
obj = items.elementAt(len - 1);
items.removeElementAt(len - 1);
return obj;
}
When a method returns an object such as pop does, the class of the returned object must
be either a subclass of or the exact class of the return type. This can be a source of confusion,
so let's look at this more closely. Suppose you have a class hierarchy where ImaginaryNumber
is a subclass of java.lang.Number, which is, in turn, a subclass of Object, as illustrated here:

Now suppose you have a method declared to return a Number:
public Number returnANumber() {
. . .
}
The returnANumber method can return an ImaginaryNumber but not an Object.
ImaginaryNumber "is a" Number because it's a subclass of Number. However, an Object
is not necessarily a Number -- it could be a String or some other type. You also can use
interface names as return types. In this case, the object returned must implement the specified
interface.
A method's name
Java supports method name overloading so that multiple methods can share the same
name. For example, suppose you are writing a class that can render various types of data
(strings, integers, and so on) to its drawing area. You need to write a method that knows
how to render each data type. In other languages, you have to think of a new name for each
method, for example, drawString, drawInteger, drawFloat, and so on. In Java, you can
use the same name for all of the drawing methods but pass a different type of parameter to
each method. So, in your data rendering class, you can declare three methods named draw,
each of which takes a different type of parameter:
class DataRenderer {
void draw(String s) {
. . .
}
void draw(int i) {
. . .
}
void draw(float f) {
. . .
}
}
Overloaded methods are differentiated by the number and type of the arguments passed
into the method. In the code sample, draw(String s) and draw(int i) are distinct and
unique methods because they require different argument types. You cannot declare more than
one method with the same name and the same number and type of arguments because the
compiler cannot differentiate them. So, draw(String s) and draw(String t) are identical
and result in a compiler error.
A class may override a method in its superclass. The overriding method must have the
same name, return type, and parameter list as the method it overrides.
Passing information into a method
When you write your method, you declare the number and type of the arguments
required by that method. You declare the type and name for each argument in the method
signature. For example, the following is a method that computes the monthly payments for
a home loan based on the amount of the loan, the interest rate, the length of the loan (the
number of periods), and the future value of the loan (presumably the future value of the loan
is zero because at the end of the loan, you've paid it off):
double computePayment(double loanAmt,
double rate,
double futureValue,
int numPeriods) {
double I, partial1, denominator, answer;
I = rate / 100.0;
partial1 = Math.pow((1+I), (0.0-numPeriods));
denominator = (1 - partial1) / I;
answer = ((-1 * loanAmt) / denominator)
- ((futureValue * partial1) / denominator);
return answer;
}
This method takes four arguments: the loan amount, the interest rate, the future value
and the number of periods. The first three are double-precision floating point numbers, and
the fourth is an integer.
As with this method, the set of arguments to any method is a comma-separated list of
variable declarations where each variable declaration is a type/name pair:
type name
As you can see from the body of the computePayment method, you simply use the
argument name to refer to the argument's value.
Argument types
In Java, you can pass an argument of any valid Java data type into a method.
This includes primitive data types such as doubles, floats and integers as you saw in the
computePayment method, and reference data types such as objects and arrays. Here's an
example of a factory method that accepts an array as an argument. In this example, the
method creates a new Polygon object and initialises it from a list of Points (Point is a class
that represents an x, y coordinate):
static Polygon polygonFrom(Point[] listOfPoints) {
. . .
}
Unlike some other languages, you cannot pass methods into Java methods. But you can
pass an object into a method and then invoke the object's methods.
Argument names
When you declare an argument to a Java method, you provide a name for that argument.
This name is used within the method body to refer to the item.
A method argument can have the same name as one of the class's member variables.
If this is the case, then the argument is said to hide the member variable. Arguments that hide
member variables are often used in constructors to initialise a class. For example, take the
following Circle class and its constructor:
class Circle {
int x, y, radius;
public Circle(int x, int y, int radius) {
. . .
}
}
The Circle class has three member variables: x, y and radius. In addition, the
constructor for the Circle class accepts three arguments each of which shares its name
with the member variable for which the argument provides an initial value.
The argument names hide the member variables. So using x, y or radius within the
body of the constructor refers to the argument, not to the member variable. To access the
member variable, you must reference it through this -- the current object:
class Circle {
int x, y, radius;
public Circle(int x, int y, int radius) {
this.x = x;
this.y = y;
this.radius = radius;
}
}
Names of method arguments cannot be the same as another argument name for the
same method, the name of any variable local to the method, or the name of any parameter to
a catch clause within the same method.
Pass by value
In Java methods, arguments are passed by value. When invoked, the method receives
the value of the variable passed in. When the argument is of primitive type, pass-by-value
means that the method cannot change its value. When the argument is of reference type,
pass-by-value means that the method cannot change the object reference, but can invoke the
object's methods and modify the accessible variables within the object.
This is often the source of confusion -- a programmer writes a method that attempts to
modify the value of one its arguments and the method doesn't work as expected. Let's look
at such method and then investigate how to change it so that it does what the programmer
originally intended.
Consider this series of Java statements which attempts to retrieve the current colour of
a Pen object in a graphics application:
. . .
int r = -1, g = -1, b = -1;
pen.getRGBColor(r, g, b);
System.out.println("red = " + r +
", green = " + g +
", blue = " + b);
. . .
At the time when the getRGBColor method is called, the variables r, g, and b all have
the value -1. The caller is expecting the getRGBColor method to pass back the red, green
and blue values of the current colour in the r, g, and b variables.
However, the Java runtime passes the variables' values (-1) into the getRGBColor
method; not a reference to the r, g, and b variables. So you could visualise the call to
getRGBColor like this: getRGBColor(-1, -1, -1).
When control passes into the getRGBColor method, the arguments come into scope
(get allocated) and are initialised to the value passed into the method:
class Pen {
int redValue, greenValue, blueValue;
void getRGBColor(int red, int green,
int blue) {
// red, green, and blue have been created
// and their values are -1
. . .
}
}
So getRGBColor gets access to the values of r, g, and b in the caller through its
arguments red, green, and blue, respectively. The method gets its own copy of the values to
use within the scope of the method. Any changes made to those local copies are not reflected
in the original variables from the caller.
Now, let's look at the implementation of getRGBColor within the Pen class that the
method signature above implies:
int redValue, greenValue, blueValue;
. . .
// this method does not work as intended
void getRGBColor(int red, int green,
int blue) {
red = redValue;
green = greenValue;
blue = blueValue;
}
}
This method will not work as intended. When control gets to the println statement in
the following code, which was shown previously, getRGBColor's arguments, red, green, and
blue, no longer exist. Therefore the assignments made to them within the method had no
effect; r, g, and b are all still equal to -1.
. . .
int r = -1, g = -1, b = -1;
pen.getRGBColor(r, g, b);
System.out.println("red = " + r +
", green = " + g +
", blue = " + b);
. . .
Passing variables by value affords the programmer some safety: Methods cannot
unintentionally modify a variable that is outside of its scope. However, you often want a method
to be able to modify one or more of its arguments. The getRGBColor method is a case in
point. The caller wants the method to return three values through its arguments. However, the
method cannot modify its arguments, and, furthermore, a method can only return one value
through its return value. So, how can a method return more than one value, or have an effect
(modify some value) outside of its scope?
For a method to modify an argument, it must be of a reference type such as an object or
array. Objects and arrays are also passed by value, but the value of an object is a reference.
So the effect is that arguments of reference types are passed in by reference. Hence the
name. A reference to an object is the address of the object in memory. Now, the argument in
the method is referring to the same memory location as the caller.
Let's rewrite the getRGBColor method so that it actually does what you want. First, you
must introduce a new type of object, RGBColor, that can hold the red, green and blue values
of a colour in RGB space:
class RGBColor {
public int red, green, blue;
}
Now, we can rewrite getRGBColor so that it accepts an RGBColor object as an
argument. The getRGBColor method returns the current colour of the pen by setting the
red, green and blue member variables of its RGBColor argument:
class Pen {
int redValue, greenValue, blueValue;
void getRGBColor(RGBColor aColor) {
aColor.red = redValue;
aColor.green = greenValue;
aColor.blue = blueValue;
}
}
And finally, let's rewrite the calling sequence:
. . .
RGBColor penColor = new RGBColor();
pen.getRGBColor(penColor);
System.out.println("red = " + r +
", green = " + g +
", blue = " + b);
. . .
The modifications made to the RGBColor object within the getRGBColor method affect
the object created in the calling sequence because the names penColor (in the calling
sequence) and aColor (in the getRGBColor method) refer to the same object.
The method body
In the code sample that follows, the method bodies for the isEmpty and pop methods
are shown in bold:
class Stack {
static final int STACK_EMPTY = -1;
Object[] stackelements;
int topelement = STACK_EMPTY;
. . .
boolean isEmpty() {
if (topelement == STACK_EMPTY)
return true;
else
return false;
}
Object pop() {
if (topelement == STACK_EMPTY)
return null;
else {
return stackelements[topelement--];
}
}
}
Besides regular Java language elements, you can use this in the method body to refer
to members in the current object. The current object is the object whose method is being
called. You can also use super to refer to members in the superclass that the current object
has hidden or overridden. Also, a method body may contain declarations for variables that are
local to that method.
this
Typically, within an object's method body you can just refer directly to the object's
member variables. However, sometimes you need to disambiguate the member variable name
if one of the arguments to the method has the same name.
For example, the following constructor for the HSBColor class initialises some of an
object's member variables according to the arguments passed into the constructor. Each
argument to the constructor has the same name as the object's member variable whose initial
value the argument contains.
class HSBColor {
int hue, saturation, brightness;
HSBColor (int hue, int saturation,
int brightness) {
this.hue = hue;
this.saturation = saturation;
this.brightness = brightness;
}
}
You must use the this keyword in this constructor because you have to disambiguate
the argument hue from the member variable hue (and so on with the other arguments). Writing
hue = hue; makes no sense. Argument names take precedence and hide member variables
with the same name. So to refer to the member variable you must do so through the current
object -- using the this keyword to refer to the current object -- explicitly.
Some programmers prefer to always use the this keyword when referring to a member
variable of the object whose method the reference appears. Doing so makes the intent of the
code explicit and reduces errors based on name sharing.
You can also use the this keyword to call one of the current object's methods. Again
this is only necessary if there is some ambiguity in the method name and is often used to make
the intent of the code clearer.
super
If your method hides one of its superclass's member variables, your method can refer to
the hidden variable through the use of the super keyword. Similarly, if your method overrides
one of its superclass's methods, your method can invoke the overridden method through the
use of the super keyword.
Consider this class:
class ASillyClass {
boolean aVariable;
void aMethod() {
aVariable = true;
}
}
and its subclass which hides aVariable and overrides aMethod:
class ASillierClass extends ASillyClass {
boolean aVariable;
void aMethod() {
aVariable = false;
super.aMethod();
System.out.println(aVariable);
System.out.println(super.aVariable);
}
}
First aMethod sets aVariable (the one declared in ASillierClass that hides the one
declared in ASillyClass) to false. Next aMethod invoked its overridden method with this
statement:
super.aMethod();
This sets the hidden version of the aVariable (the one declared in ASillyClass) to
true. Then aMethod displays both versions of aVariable which have different values:
false
true
Local variables
Within the body of the method you can declare more variables for use within that method.
These variables are local variables and live only while control remains within the method. This
method declares a local variable i that it uses to iterate over the elements of its array argument.
Object findObjectInArray(Object o,
Object[] arrayOfObjects) {
int i; // local variable
for (i = 0; i < arrayOfObjects.length; i++) {
if (arrayOfObjects[i] == o)
return o;
}
return null;
}
After this method returns, i no longer exists.
Controlling access to members of a class
One of the benefits of classes is that classes can protect their member variables and
methods from access by other objects. Why is this important? Well, consider this. You're
writing a class that represents a query on a database that contains all kinds of secret
information, say employee records or income statements for your startup company.
Certain information and queries contained in the class, the ones supported by the
publicly accessible methods and variables in your query object, are OK for the consumption
of any other object in the system. Other queries contained in the class are there simply for
the personal use of the class. They support the operation of the class but should not be used
by objects of another type -- you've got secret information to protect. You'd like to be able to
protect these personal variables and methods at the language level and disallow access by
objects of another type.
In Java, you can use access specifiers to protect both a class's variables and its methods
when you declare them. The Java language supports four distinct access levels for member
variables and methods: private, protected, public, and, if left unspecified, package.
The following chart shows the access level permitted by each specifier.
| Specifier |
class |
subclass |
package |
world |
| private |
v |
-- |
-- |
-- |
| protected |
v |
v* |
v |
-- |
| public |
v |
v |
v |
v |
| no specifier |
v |
-- |
v |
-- |
The first column indicates whether the class itself has access to the member defined by
the access specifier. As you can see, a class always has access to its own members. The
second column indicates whether subclasses of the class (regardless of which package they
are in) have access to the member. The third column indicates whether classes in the same
package as the class (regardless of their parentage) have access to the member. The fourth
column indicates whether all classes have access to the member.
Private
The most restrictive access level is private. A private member is accessible only to the
class in which it is defined. Use this access to declare members that should only be used
by the class. This includes variables that contain information that if accessed by an outsider
could put the object in an inconsistent state, or methods that, if invoked by an outsider, could
jeopardise the state of the object or the program in which it's running. Private members are
like secrets you never tell anybody.
To declare a private member, use the private keyword in its declaration. The following
class contains one private member variable and one private method:
class Alpha {
private int iamprivate;
private void privateMethod() {
System.out.println("privateMethod");
}
}
Objects of type Alpha can inspect or modify the iamprivate variable and can invoke
privateMethod, but objects of other types cannot. For example, the Beta class defined here:
class Beta {
void accessMethod() {
Alpha a = new Alpha();
a.iamprivate = 10; // illegal
a.privateMethod(); // illegal
}
}
cannot access the iamprivate variable or invoke privateMethod on an object of type Alpha
because Beta is not of type Alpha.
When one of your classes is attempting to access a member variable to which it does
not have access, the compiler prints an error message similar to the following and refuses to
compile your program:
Beta.java:9: Variable iamprivate in class Alpha
not accessible from class Beta.
a.iamprivate = 10; // illegal
^
1 error
Also, if your program is attempting to access a method to which it does not have access,
you will see a compiler error like this:
Beta.java:12: No method matching privateMethod()
found in class Alpha.
a.privateMethod(); // illegal
1 error
New Java programmers might ask if one Alpha object can access the private members
of another Alpha object. This is illustrated by the following example. Suppose the Alpha
class contained an instance method that compared the current Alpha object (this) to another
object based on their iamprivate variables:
class Alpha {
private int iamprivate;
boolean isEqualTo(Alpha anotherAlpha) {
if (this.iamprivate ==
anotherAlpha.iamprivate)
return true;
else
return false;
}
}
This is perfectly legal. Objects of the same type have access to one another's private
members. This is because access restrictions apply at the class or type level (all instances of
a class) rather than at the object level (this particular instance of a class).
Protected
The next access level specifier is protected, which allows the class itself, subclasses
and all classes in the same package to access the members. Use the protected access level
when it's appropriate for a class's subclasses to have access to the member, but not unrelated
classes. Protected members are like family secrets -- you don't mind if the whole family
knows, and even a few trusted friends but you wouldn't want any outsiders to know.
To declare a protected member, use the keyword protected. First, let's look at how the
protected specifier affects access for classes in the same package. Consider this version of
the Alpha class which is now declared to be within a package named Greek and which has
one protected member variable and one protected method declared in it:
package Greek;
public class Alpha {
protected int iamprotected;
protected void protectedMethod() {
System.out.println("protectedMethod");
}
}
Now, suppose that the class Gamma was also declared to be a member of the Greek
package (and is not a subclass of Alpha). The Gamma class can legally access an Alpha
object's iamprotected member variable and can legally invoke its protectedMethod:
package Greek;
class Gamma {
void accessMethod() {
Alpha a = new Alpha();
a.iamprotected = 10; // legal
a.protectedMethod(); // legal
}
}
That's pretty straightforward. Now, let's investigate how the protected specifier affects
access for subclasses of Alpha.
Let's introduce a new class, Delta, that derives from Alpha but lives in a different
package -- Latin. The Delta class can access both iamprotected and protectedMethod,
but only on objects of type Delta or its subclasses.
The Delta class cannot access
iamprotected or protectedMethod on objects of type Alpha. accessMethod in the following
code sample attempts to access the iamprotected member variable on an object of type
Alpha, which is illegal, and on an object of type Delta, which is legal. Similarly, accessMethod
attempts to invoke an Alpha object's protectedMethod which is also illegal:
package Latin;
import Greek.*;
class Delta extends Alpha {
void accessMethod(Alpha a, Delta d) {
a.iamprotected = 10; // illegal
d.iamprotected = 10; // legal
a.protectedMethod(); // illegal
d.protectedMethod(); // legal
}
}
If a class is both a subclass of and in the same package as the class with the protected
member, then the class has access to the protected member.
Public
The easiest access specifier is public. Any class, in any package, has access to a class's
public members. Declare public members only if such access cannot produce undesirable
results if an outsider uses them. There are no personal or family secrets here; this is for stuff
you don't mind anybody else knowing.
To declare a public member, use the keyword public. For example,
package Greek;
public class Alpha {
public int iampublic;
public void publicMethod() {
System.out.println("publicMethod");
}
}
Let's rewrite our Beta class one more time and put it in a different package than Alpha
and make sure that it is completely unrelated to (not a subclass of) Alpha:
package Roman;
import Greek.*;
class Beta {
void accessMethod() {
Alpha a = new Alpha();
a.iampublic = 10; // legal
a.publicMethod(); // legal
}
}
As you can see from the above code snippet, Beta can legally inspect and modify the
iampublic variable in the Alpha class and can legally invoke publicMethod.
No specifier
The package access level is what you get if you don't explicitly set a member's access
to one of the other levels. This access level allows classes in the same package as your class
to access the members. This level of access assumes that classes in the same package are
trusted friends. This level of trust is like that which you extend to your closest friends but
wouldn't trust even to your family.
For example, this version of the Alpha class declares a single package-access member
variable and a single package-access method. Alpha lives in the Greek package:
package Greek;
class Alpha {
int iampackage;
void packageMethod() {
System.out.println("packageMethod");
}
}
The Alpha class has access both to iampackage and packageMethod. In addition, all
the classes declared within the same package as Alpha also have access to iampackage
and packageMethod. Suppose that both Alpha and Beta were declared as part of the Greek
package:
package Greek;
class Beta {
void accessMethod() {
Alpha a = new Alpha();
a.iampackage = 10; // legal
a.packageMethod(); // legal
}
}
Beta can legally access iampackage and packageMethod as shown.
Understanding instance and class members
When you declare a member variable such as aFloat in MyClass:
class MyClass {
float aFloat;
}
you declare an instance variable. Every time you create an instance of a class, the runtime
system creates one copy of each class's instance variables for the instance.
Instance variables are in contrast to class variables (which you declare using the static
modifier). The runtime system allocates class variables once per class regardless of the
number of instances created of that class. The system allocates memory for class variables
the first time it encounters the class. All instances share the same copy of the class's class
variables. You can access class variables through an instance or through the class itself.
Methods are similar: Your classes can have instance methods and class methods.
Instance methods operate on the current object's instance variables but also have access to
the class variables. Class methods, on the other hand, cannot access the instance variables
declared within the class (unless they create a new object and access them through the
object). Also, class methods can be invoked on the class, you don't need an instance to
call a class method.
By default, unless otherwise specified, a member declared within a class is an instance
member. The class defined below has one instance variable -- an integer named x -- and
two instance methods x and setX -- that let other objects set and query the value of x:
class AnIntegerNamedX {
int x;
public int x() {
return x;
}
public void setX(int newX) {
x = newX;
}
}
Every time you instantiate a new object from a class, you get a new copy of each of the
class's instance variables. These copies are associated with the new object. So, every time
you instantiate a new AnIntegerNamedX object from the class, you get a new copy of x that is
associated with the new AnIntegerNamedX object.
All instances of a class share the same implementation of an instance method; all
instances of AnIntegerNamedX share the same implementation of x and setX. Note that both
methods, x and setX, refer to the object's instance variable x by name. "But", you ask, "if
all instances of AnIntegerNamedX share the same implementation of x and setX isn't this
ambiguous?" The answer is "no." Within an instance method, the name of an instance variable
refers to the current object's instance variable, assuming that the instance variable isn't hidden
by a method parameter. So, within x and setX, x is equivalent to this.x.
Objects outside of AnIntegerNamedX that wish to access x must do so through a
particular instance of AnIntegerNamedX. Suppose that this code snippet was in another
object's method. It creates two different objects of type AnIntegerNamedX, sets their x values
to different values, then displays them:
. . .
AnIntegerNamedX myX = new AnIntegerNamedX();
AnIntegerNamedX anotherX = new AnIntegerNamedX();
myX.setX(1);
anotherX.x = 2;
System.out.println("myX.x = " + myX.x());
System.out.println("anotherX.x = " + anotherX.x());
. . .
Notice that the code used setX to set the x value for myX but just assigned a value
to anotherX.x directly. Either way, the code is manipulating two different copies of x: the
one contained in the myX object and the one contained in the anotherX object. The output
produced by this code snippet is:
myX.x = 1
anotherX.x = 2
showing that each instance of the class AnIntegerNamedX has its own copy of the instance
variable x and each x has a different value.
You can, when declaring a member variable, specify that the variable is a class variable
rather than an instance variable. Similarly, you can specify that a method is a class method
rather than an instance method. The system creates a single copy of a class variable the first
time it encounters the class in which the variable is defined. All instances of that class share
the same copy of the class variable. Class methods can only operate on class variables --
they cannot access the instance variables defined in the class.
To specify that a member variable is a class variable, use the static keyword. For
example, let's change the AnIntegerNamedX class such that its x variable is now a class
variable:
class AnIntegerNamedX {
static int x;
public int x() {
return x;
}
public void setX(int newX) {
x = newX;
}
}
Now the exact same code snippet from before that creates two instances of
AnIntegerNamedX, sets their x values, and then displays them produces this, different, output.
myX.x = 2
anotherX.x = 2
The output is different because x is now a class variable so there is only one copy of the
variable and it is shared by all instances of AnIntegerNamedX, including myX and anotherX.
When you invoke setX on either instance, you change the value of x for all instances of
AnIntegerNamedX.
You use class variables for items that you need only one copy of and which must be
accessible by all objects inheriting from the class in which the variable is declared. For
example, class variables are often used with final to define constants; this is more memory
efficient than final instance variables because constants can't change, so you really only need
one copy.
Similarly, when declaring a method, you can specify that method to be a class method
rather than an instance method. Class methods can only operate on class variables and
cannot access the instance variables defined in the class.
To specify that a method is a class method, use the static keyword in the method
declaration. Let's change the AnIntegerNamedX class such that its member variable x is once
again an instance variable, and its two methods are now class methods:
class AnIntegerNamedX {
int x;
static public int x() {
return x;
}
static public void setX(int newX) {
x = newX;
}
}
When you try to compile this version of AnIntegerNamedX, the compiler displays an
error like this one:
AnIntegerNamedX.java:4: Can't make a static reference to
nonstatic variable x in class AnIntegerNamedX.
return x;
^
This is because class methods cannot access instance variables unless the method
created an instance of AnIntegerNamedX first and accessed the variable through it.
Let's fix AnIntegerNamedX by making its x variable a class variable:
class AnIntegerNamedX {
static int x;
static public int x() {
return x;
}
static public void setX(int newX) {
x = newX;
}
}
Now the class will compile and the same code snippet from before that creates two
instances of AnIntegerNamedX, sets their x values, and then prints the x values produces this
output:
myX.x = 2
anotherX.x = 2
Again, changing x through myX also changes it for other instances of AnIntegerNamedX.
Another difference between instance members and class members is that class
members are accessible from the class itself. You don't need to instantiate a class to access
its class members. Let's rewrite the code snippet from before to access x and setX directly
from the AnIntegerNamedX class:
. . .
AnIntegerNamedX.setX(1);
System.out.println("AnIntegerNamedX.x = "
+ AnIntegerNamedX.x());
. . .
Notice that you no longer have to create myX and anotherX. You can set x and retrieve
x directly from the AnIntegerNamedX class. You cannot do this with instance members, you
can only invoke instance methods from an object and can only access instance variables from
an object. You can access class variables and methods either from an instance of the class or
from the class itself.
Initialising instance and class members
You can use static initialisers and instance initialisers to provide initial values for class
and instance members when you declare them in a class:
class BedAndBreakfast {
static final int MAX_CAPACITY = 10;
boolean full = false;
}
This works well for members of primitive data type. Sometimes, it even works when
creating arrays and objects. But this form of initialisation has limitations, as follows:
- Initialisers can perform only initialisations that can be expressed in an assignment statement.
- Initialisers cannot call any method that can throw a checked exception.
- If the initialiser calls a method that throws a runtime exception, then it cannot do error recovery.
If you have some initialisation to perform that cannot be done in an initialiser because
of one of these limitations, you have to put the initialisation code elsewhere. To initialise
class members, put the initialisation code in a static initialisation block. To initialise instance
members, put the initialisation code in a constructor.
Using static initialisation blocks
Here's an example of a static initialisation block:

The errorStrings resource bundle must be initialised in a static initialisation block.
This is because error recovery must be performed if the bundle cannot be found. Also,
errorStrings is a class member and it doesn't make sense for it to be initialised in a
constructor. As the previous example shows, a static initialisation block begins with the static
keyword and is a normal block of Java code enclosed in curly braces {}.
A class can have any number of static initialisation blocks that appear anywhere in the
class body. The runtime system guarantees that static initialisation blocks and static initialisers
are called in the order (left-to-right, top-to-bottom) that they appear in the source code.
Initialising instance members
If you want to initialise an instance variable and cannot do it in the variable declaration
for the reasons cited previously, then put the initialisation in the constructor(s) for the class.
Suppose the errorStrings bundle in the previous example is an instance variable rather
than a class variable. Then you'd use the following code to initialise it:
import java.util.ResourceBundle;
class Errors {
ResourceBundle errorStrings;
Errors() {
try {
errorStrings = ResourceBundle.
getBundle("ErrorStrings");
} catch (java.util.MissingResourceException e) {
// error recovery code here
}
}
}
The code that initialises errorStrings is now in a constructor for the class.
Sometimes a class contains many constructors and each constructor allows the caller
to provide initial values for different instance variables of the new object.
For example,
java.awt.Rectangle has these three constructors:
Rectangle(); Rectangle(int width, int height);
Rectangle(int x, int y, int widt Printer Friendly Page
Send to a Friend
..
Search here again if you need more info!
|