Java
access specifiers
The
Java access
specifiers public,
protected
and private
are placed in front of each definition for each member in your class, whether
it’s a data member or a method. Each access specifier controls the access
for only that particular definition. This is a distinct contrast to C++, in
which the access specifier controls all the definitions following it until
another access specifier comes along.
One
way or another, everything has some kind of access specified for it. In the
following sections, you’ll learn all about the various types of access,
starting with the default access.
“Friendly”
What
if you give no access specifier at all, as in all the examples before this
chapter? The default access has no keyword, but it is commonly referred to as
“friendly.” It means that all the other classes in the current
package have access to the friendly member, but to all the classes outside of
this package the member appears to be private. Since a compilation unit –
a file – can belong only to a single package, all the classes within a
single compilation unit are automatically friendly with each other. Thus,
friendly elements are also said to have package
access
. Friendly
access allows you to group related classes together in a package so that they
can easily interact with each other. When you put classes together in a package
(thus granting mutual access to their friendly members; e.g. making them
“friends”) you “own” the code in that package. It makes
sense that only code that you own should have friendly access to other code
that you own. You could say that friendly access gives a meaning or a reason
for grouping classes together in a package. In many languages the way you
organize your definitions in files can be willy-nilly, but in Java you’re
compelled to organize
them in a sensible fashion. In addition, you’ll probably want to exclude
classes that shouldn’t have access to the classes being defined in the
current package.
An
important question in any relationship is “Who can access my
private
implementation?” The class controls which code has access to its members.
There’s no magic way to “break in;” someone in another
package can’t declare a new class and say, “Hi, I’m a friend
of
Bob’s!”
and expect to see the
protected,
friendly, and
private
members of
Bob.
The only way to grant access to a member is to:
- Make
the member
public.
Then everybody, everywhere, can access it.
- Make
the member friendly by leaving off any access specifier, and put the other
classes in the same package. Then the other classes can access the member.
- As
you’ll see in a later chapter where inheritance is introduced, an
inherited class can access a
protected
member as well as a
public
member (but not
private
members). It can access friendly members only if the two classes are in the
same package. But don’t worry about that now.
- Provide
“accessor/mutator” methods (also known as “get/set”
methods) that read and change the value. This is the most civilized approach in
terms of OOP, and it is fundamental to Java Beans, as you’ll see in
Chapter 13.
public:
interface access
When
you use the
public
keyword, it means
that the member declaration that immediately follows
public
is available to everyone, in particular to the client programmer who uses the
library. Suppose you define a package
dessert
containing the following compilation unit: (See page
97
if you have trouble executing this program.)
//: Cookie.java
// Creates a library
package c05.dessert;
public class Cookie {
public Cookie() {
System.out.println("Cookie constructor");
}
void foo() { System.out.println("foo"); }
} ///:~
Remember,
Cookie.java
must reside in a subdirectory called
dessert,
in a directory under
C05
(indicating
Chapter 5 of this book) that must be under one of the CLASSPATH directories.
Don’t make the mistake of thinking that Java will always look at the
current directory as one of the starting points for searching. If you
don’t have a ‘
.’
as one of the paths in your CLASSPATH, Java won’t look there.
Now
if you create a program that uses
Cookie:
//: Dinner.java
// Uses the library
import c05.dessert.*;
public class Dinner {
public Dinner() {
System.out.println("Dinner constructor");
}
public static void main(String[] args) {
Cookie x = new Cookie();
//! x.foo(); // Can't access
}
} ///:~
You
can create a
Cookie
object, since its constructor is
public
and the class is
public.
(We’ll look more at the concept of a public class later.) However, the
foo( )
member is inaccessible inside
Dinner.java
since
foo( )
is friendly only within package
dessert.
The
default package
You
might be surprised to discover that the following code compiles, even though it
would appear that it breaks the rules:
//: Cake.java
// Accesses a class in a separate
// compilation unit.
class Cake {
public static void main(String[] args) {
Pie x = new Pie();
x.f();
}
} ///:~
In
a second file, in the same directory:
//: Pie.java
// The other class
class Pie {
void f() { System.out.println("Pie.f()"); }
} ///:~
You
might initially view these as completely foreign files, and yet
Cake
is able to create a
Pie
object and call its
f( )
method! You’d typically think that
Pie
and
f( )
are friendly and therefore not available to
Cake.
They
are
friendly – that part is correct. The reason that they are available in
Cake.java
is because they are in the same directory and have no explicit package name.
Java treats files like this as implicitly part of the “default
package” for that directory, and therefore friendly to all the other
files in that directory.
private:
you can’t touch that!
The
private
keyword
that means no one can access that member except that particular class, inside
methods of that class. Other classes in the same package cannot access
private
members,
so it’s as if you’re even insulating the class against yourself. On
the other hand, it’s not unlikely that a package might be created by
several people collaborating together, so
private
allows you to freely change that member without concern that it will affect
another class in the same package. The default “friendly” package
access is often an adequate amount of hiding; remember, a
“friendly” member is inaccessible to the user of the package. This
is nice, since the default access is the one that you normally use. Thus,
you’ll typically think about access for the members that you explicitly
want to make
public
for the client programmer, and as a result, you might not
initially
think you’ll use the
private
keyword
often since it’s tolerable to get away without it. (This is a distinct
contrast with C++.) However, it turns out that the consistent use of
private
is very important, especially where multithreading is concerned. (As
you’ll see in Chapter 14.)
Here’s
an example of the use of
private:
//: IceCream.java
// Demonstrates "private" keyword
class Sundae {
private Sundae() {}
static Sundae makeASundae() {
return new Sundae();
}
}
public class IceCream {
public static void main(String[] args) {
//! Sundae x = new Sundae();
Sundae x = Sundae.makeASundae();
}
} ///:~
This
shows an example in which
private
comes in handy: you might want to control how an object is created and prevent
someone from directly accessing a particular constructor (or all of them). In
the example above, you cannot create a
Sundae
object via its constructor; instead you must call the
makeASundae( )
method to do it for you.
[25] Any
method that you’re certain is only a “helper” method for that
class can be made
private
to ensure that you don’t accidentally use it elsewhere in the package and
thus prohibit you from changing or removing the method. Making a method
private
guarantees that you retain this option. (However, just because the handle is
private
doesn't mean that some other object can't have a
public
handle to the same object. See Chapter 12 for issues about aliasing.)
protected:
“sort of friendly”
The
protected
access specifier requires a jump ahead to understand.
First, you should be aware that you don’t need to understand this section
to continue through the book up through the inheritance chapter. But for
completeness, here is a brief description and example using
protected.
The
protected
keyword deals with a concept called inheritance,
which takes an existing class and adds new members to that class without
touching the existing class, which we refer to as the base
class.
You can also change the behavior of existing members of the class. To inherit
from an existing class, you say that your new class extends
an
existing class, like this:
The
rest of the class definition looks the same.
If
you create a new package and you inherit from a class in another package, the
only members you have access to are the
public
members of the original package. (Of course, if you perform the inheritance in
the
same
package, you have the normal package access to all the “friendly”
members.) Sometimes the creator of the base class would like to take a
particular member and grant access to derived classes but not the world in
general. That’s what
protected
does. If you refer back to the file
Cookie.java
on page
203,
the following class
cannot
access the “friendly” member:
//: ChocolateChip.java
// Can't access friendly member
// in another class
import c05.dessert.*;
public class ChocolateChip extends Cookie {
public ChocolateChip() {
System.out.println(
"ChocolateChip constructor");
}
public static void main(String[] args) {
ChocolateChip x = new ChocolateChip();
//! x.foo(); // Can't access foo
}
} ///:~
One
of the interesting things about inheritance is that if a method
foo( )
exists in class
Cookie,
then it also exists in any class inherited from
Cookie.
But since
foo( )
is “friendly” in a foreign package, it’s unavailable to us in
this one. Of course, you could make it
public,
but then everyone would have access and maybe that’s not what you want.
If we change the class
Cookie
as follows:
public class Cookie {
public Cookie() {
System.out.println("Cookie constructor");
}
protected void foo() {
System.out.println("foo");
}
}
then
foo( )
still has “friendly” access within package
dessert,
but it is also accessible to anyone inheriting from
Cookie.
However, it is
not
public.
[25]
There’s another effect in this case: Since the default constructor is the
only one defined, and it’s
private,
it will prevent inheritance of this class. (A subject that will be introduced
in Chapter 6.)