Java access specifiers

Bruce Eckel’s Thinking in Java Contents | Prev | Next

“Friendly”

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:

  1. Make
    the member
    public.
    Then everybody, everywhere, can access it.
  2. 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.
  3. 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.
  4. 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

//: 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”

class
Foo extends Bar {

The
rest of the class definition looks the same.

//: 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.)

More by Author

Must Read