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

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:

  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

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:

class Foo extends Bar {

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

Contents | Prev | Next