Previous Page TOC Next Page



- 17 -
Modifiers, Access Control, and Class Design


Today, you'll start with advanced Java language concepts for organizing and designing individual classes:


Modifiers


The techniques for programming you'll learn today involve different strategies and ways of thinking about how a class is organized. But the one thing all these techniques have in common is that they all use special modifier keywords in the Java language.

You have already learned how to define classes, methods, and variables in Java. Modifiers are keywords you add to those definitions to change their meaning. Classes, methods and variables with modifiers are still classes, methods and variables, but the modifiers change their behavior or how Java treats those elements.

Modifiers are special language keywords that modify the definition (and the behavior) of a class, method, or variable.



You've already learned about a few of these modifiers earlier on in the book, but here we'll talk about them in detail so you can get the bigger picture of why modifiers work the way they do.

The Java language has a wide variety of modifiers, including the following:

Some modifiers, as you can see, can apply to only classes and methods or only to methods and variables. To use a modifier, you simply place it before the class, method, or variable definition, for example:




public class MyApplet extends Java.applet.Applet { ... }



private boolean engineState;



static final double pi = 3.141559265



protected static final int MAXNUMELEMENTS = 128;



public static void main(String args[]) { ...}

The order of modifiers is irrelevant to their meaning; your order can vary and is really a matter of taste. Pick a style and then be consistent with it throughout all your classes. Here is the usual order:




<access> static abstract synchronized volatile final native

In this definition, <access> can be public, protected, or private (but only one of them).

All the modifiers are essentially optional; none have to appear in a declaration. Good object-oriented programming style, however, suggests adding as many as are needed to best describe the intended use of, and restrictions on, the thing you're declaring. In some special situations (inside an interface, for example, as described tomorrow), certain modifiers are implicitly defined for you, and you do not need to type them—they will be assumed to be there.

Controlling Access to Methods and Variables


The most important modifiers in the language, from the standpoint of class and object design, are those that allow you to control the visibility of, and access to, variables and methods inside your classes.

Why Access Control Is Important


Why would you care about controlling access to methods and variables inside your classes? Consider a PC, which is made up of a number of different components, that, when combined, work together to create a useful, larger system.

Each component in a PC system works in a particular way, and has a specific way of interacting with the other components in the system. For example, a video card plugs into your motherboard using a standard socket and plug arrangement, as does your monitor to the back of the card. Your computer can then display information on the screen by using standard software interfaces.

The video card itself has a lot of other internal features and capabilities beyond the basic hardware and software interfaces. But as a user or consumer of the card, I don't need to know what every single chip does, nor do I need to touch them in order to get the card to work. Given the standard interfaces, the card figures everything out and does what it needs to do internally. And, in fact, the manufacturer of the card most likely doesn't want me to go in and start mucking with individual chips or capabilities of the card, because I'm likely to screw something up. It's best if I just stick to the defined interface and let the internal workings stay hidden.

Classes and objects are the same way. Although a class can define lots of methods and variables, not all of them are useful to a consumer of that class, and some might even be harmful if they're used in the wrong way from how they were intended to be used.

Access control is about controlling visibility. When a method or variable is visible to another class, its methods can reference (call or modify) that method or variable. Protecting those methods and instance variables limits the visibility and the use of those methods and variables. Therefore, as a designer of a class or an entire hierarchy of classes, it is a good idea to define what the external appearance of a class is going to be, which variables and methods will be accessible for other users of that class, and which ones are for internal use only. This is called encapsulation and is an important feature of object-oriented design.

Encapsulation is the process of hiding the internal parts of an object's implementation and allowing access to that object only through a defined interface.

You might note that, up to this point, not very much of this has been done in any of the examples; in fact, just about every variable and method you've created has been fairly promiscuous and had no access control whatsoever. The reason I approached the problem in this way is that it makes for simpler examples. As you become a more sophisticated programmer and create Java programs with lots of interrelated classes, you'll find that adding features such as encapsulation and protecting access to the internal workings of your classes makes for better designed programs overall.

The Four Ps of Protection


The Java language provides four levels of protection for methods and instance variables: public, private, protected, and package (actually, the latter isn't an explicit form of Java protection, but I've included it here because it's nicely alliterative). Before applying protection levels to your own code, you should know what each form means and understand the fundamental relationships that a method or variable within a class can have to the other classes in the system.



You can also protect entire classes using these modifiers. But class protection applies better once you know what packages are, so I'll postpone talking about that until tomorrow.


Package Protection

The first form of protection is the one you've been unconsciously using all this time: package protection. In C, there's the notion of hiding a name so that only the functions within a given source file can see it. Java doesn't have this kind of control; names will be happily found in other source files as long as Java knows where to find them. Instead of file-level protection, Java has the concept of packages, which are a group of classes related by purpose or function.

Methods and variables with package protection are visible to all other classes in the same package, but not outside that package. This is the kind of protection you've been using up to this point, and it's not much protection at all. Most of the time, you'll want to be more explicit when you define the protection for that class' methods and variables.

Package protection, the default level of protection, means that your methods and variables are accessible to all the other classes in the same package.

Package protection isn't an explicit modifier you can add to your method or variable definitions; instead, it's the default protection you get when you don't add any protection modifiers to those definitions.



You might not think you've been using packages at all up to this point, but actually, you have. In Java, if you don't explicitly put a class into a package, it'll be included in a default package that is essentially defined as all the classes in the same directory. So all the classes you put in a single directory are part of their own implicit package and can be used by other classes in that directory.


Private Protection

From the default protection you get with package protection, you can either become more restrictive or more loose in how you control the visibility and access to your methods and variables. The most restrictive form of protection is private, which limits the visibility of methods and instance variables to the class in which they're defined. A private instance variable, for example, can be used by methods inside the same class, but cannot be seen or used by any other class or object. Private methods, analogously, can be called by other methods inside that same class, but not by any other classes. In addition, neither private variables or private methods are inherited by subclasses.

private protection means that your methods and variables are accessible only to other methods in the same class.

To create a private method or instance variable, add the private modifier to its definition:




class  Writer {



    private boolean writersBlock = true;



    private String mood;



    private int income = 0;



    private void getIdea(Inspiration in) {



        . . .



    }



    Book createBook(int numDays, long numPages) {



      ...



    }



}

In this snippet of code example, the internal data to the class Writer (the variables writersBlock, mood, income, and the method getIdea()) are all private. The only method accessible from outside the Writer class is the createBook() method. createBook() is the only thing other objects (editors?) can ask the writer object to do; the other bits of data are implementation details that might affect how the book is written, but don't otherwise need to be visible or accessible from other sources.

The rule of thumb for private protection is that any data or behavior internal to the class, that other classes or subclasses should not be independently touching, should be private. Judicious use of private variables and methods is how you limit the functionality of a class to only those features you want visible outside that class—as with the example of the PC components. Remember that an object's primary job is to encapsulate its data—to hide it from the world and limit its manipulation. It separates design from implementation, minimizes the amount of information one class needs to know about another to get its job finished, and reduces the extent of the code changes you need to do if your internal implementation changes.

In addition to picking and choosing which methods you'll want to keep private and which will be accessible to others, a general rule of thumb is that all the instance variables in a class should be private, and that you should create special non-private methods to get or change those variables. You'll learn more about this rule and why it's important a little later in "Instance Variable Protection and Accessor Methods."

Public Protection

The diametric opposite of private protection, and the least restrictive form of protection, is public. A method or variable that is declared with the public modifier is accessible to the class in which it's defined, all the subclasses of that class, all the classes in the package, and to any other classes outside that package, anywhere in the entire universe of Java classes.

Public protection means that your methods and variables are accessible to other methods anywhere inside or outside the current class or package.

Indicating that a method or variable is public isn't necessarily a bad thing. Just as hiding the data that is internal to your class using private helps encapsulate an object, using public methods defines precisely what the interface to instances of your class is. If you expect your classes to be reused by other programmers in other programs, the methods that they'll be using in your class should be public.

In many ways, public protection is very similar to the default package protection. Both allow methods and variables to be accessed by other classes in the same package. The difference occurs when you create packages of classes. Variables and methods with package protection can be used in classes that exist in the same package. But if someone imports your class into their own program from outside your package, those methods and variables will not be accessible unless they have been declared public. Once again, you'll learn more about packages tomorrow.

Public declarations work just like private ones; simply substitute the word public for private.

Protected Protection

The final form of protection available in Java concerns the relationship between a class and its present and future subclasses declared inside or outside a package. These subclasses are much closer to a particular class than to any other outside classes for the following reasons:

To support a special level of visibility reserved for subclasses somewhat less restrictive than private, Java has an intermediate level of access between package and private called, appropriately enough, protected. Protected methods and variables are accessible to any class inside the package, as they would be if they were package protected, but those methods and variables are also available to any subclasses of your class that have been defined outside your package.

Protected protection means that your methods and variables are accessible to all classes inside the package, but only to subclasses outside the package.



In C++, the protected modifier means that only subclasses can access a method or variable, period. Java's meaning of protected is slightly different, also allowing any class inside the package to access those methods and variables.

Why would you need to do this? You might have methods in your class that are specific to its internal implementation—that is, not intended to be used by the general public—but that would be useful to subclasses for their own internal implementations. In this case, the developer of the subclass—be it you or someone else—can be trusted to be able to handle calling or overriding that method.

For example, say you had a class called AudioPlayer, which plays a digital audio file. AudioPlayer has a method called openSpeaker(), which is an internal method that interacts with the hardware to prepare the speaker for playing. openSpeaker() isn't important to anyone outside the AudioPlayer class, so at first glance you might want to make it private. A snippet of AudioPlayer might look something like the following:




class AudioPlayer {



  private boolean openSpeaker(Speaker sp)_ {



     // implementation details



  }



}

This works fine if AudioPlayer isn't going to be subclassed. But what if you were going to create a class called StereoAudioPlayer that is a subclass of AudioPlayer? This class would want access to the openSpeaker() method so that it can override it and provide stereo-specific speaker initialization. You still don't want the method generally available to random objects (and so it shouldn't be public), but you want the subclass to have access to it—so protected is just the solution.



In versions of Java up to 1.01, you could use private and protected together to create yet another form of protection that would restrict access to methods or variables solely to subclasses of a given class. As of 1.02, this capability has been removed from the language. Using this combination with Visual J++ will result in a compiler warning and the method or variable will default to protected only.


Modifier Icons in ClassView

The ClassView pane in the Visual J++ development environment has several icons to help you visually distinguish between public, private, and protected variables and methods. These icons are demonstrated in Figure 17.1.

Figure 17.1. Modifier Icons in ClassView.

The lock icon next to the DisplayImage method indicates that DisplayImage is private. The key icon next to the AProtectedMethod method indicates that it is protected. You can see that the other methods do not have any additional icons next to them and are therefore public. The same icons apply to the member variables. As you can see by the lock icon, all of the member variables except m_Animal is private.

A Summary of Protection Forms

The differences between the various protection types can become very confusing, particularly in the case of protected methods and variables. Table 17.1, which summarizes exactly what is allowed where, will help clarify the differences from the least restrictive (public) to the most restrictive (private) forms of protection.

Table 17.1. Different protection schemes.

Visibility public protected package private
From the same class yes yes yes yes
From any class in the same package yes yes yes no
From any class outside the package yes no no no
From a subclass in the same package yes yes yes no
From a subclass outside the same package yes yes no no

Method Protection and Inheritance


Setting up protections in new classes with new methods is easy; you make your decisions based on your design and apply the right modifiers. When you create subclasses and override other methods, however, you have to take the protection of the original method into account.

The rule in Java is that you cannot override a method and make the new method more private than the original method (you can, however, make it more public).


Instance Variable Protection and Accessor Methods


A good rule of thumb in object-oriented programming is that unless an instance variable is constant it should almost certainly be private. But, I hear you say, if instance variables are private, how can they be changed from outside the class? The answer is that they can't, which is precisely the point. Instead, if you create special methods that indirectly read or change the value of that instance variable, you can better control the interface of your classes and how those classes behave. You'll learn about how to do this in this section.

Why Non-Private Instance Variables Are a Bad Idea

In most cases, having someone else accessing or changing instance variables inside your object isn't a good idea. Take, for example, a class called Circle, whose partial definition looks like the following:




class Circle {



   int x, y, radius;



   Circle(int x, int y, int radius) {



      ...



   }



   void draw() {



      ...



   }



}

The Circle class has three instance variables for the x and y position of the center point and of the radius. A constructor builds the circle from those three values, and the draw() method draws the circle on the screen. So far, so good, right?

Now say you have a Circle object created and drawn on the screen. Then some other object comes along and changes the value of radius. Now what? Your circle doesn't know that the radius has changed. It doesn't know to redraw itself to take advantage of the new size of the circle. Changing the value of an instance variable doesn't in itself trigger any methods. You have to rely on the same random object that changed the radius to also call the draw() method, and that overly complicates the interface of your class, making it more prone to errors.

Another example of why it's better not to make instance variables publicly accessible is that it's not possible to prevent a non-constant instance variable from being changed. In other words, you could create a variable that you'd intended to be read-only, and perhaps your program was well-mannered and didn't go about changing that variable randomly—but because the variable is there and available, someone else might very well change it without understanding your methodology.

Why Accessor Methods Are a Better Idea

If all your instance variables are private, how do you give access to them to the outside world? The answer is to write special methods to read and change that variable (one for reading the value of the variable, one for changing it) rather than allowing it to be read and changed directly. These methods are sometimes called accessor methods, mutator methods (for changing the variable) or simply getters and setters.

Accessor methods are special methods you implement to indirectly modify otherwise private instance variables.

Having a method to change a given instance variable means you can control the value that that variable is set to (to make sure it's within the boundaries you expect) as well as perform any other operations that might need to be finished if that variable changes, such as redrawing the circle.

Having two methods for reading and changing the variable also allows you to set up different protections for each. The method to read the value, for example, could be public, whereas the method to change the value can be private or protected, effectively creating a variable that's read-only except in a few cases (which is different from constant, which is read-only in all cases).

Using methods to access an instance variable is one of the most frequently used idioms in object-oriented programs. Applying it liberally throughout all your classes repays you numerous times over with more robust and reusable programs.

Creating Accessor Methods

Creating accessor methods for your instance variables simply involves creating two extra methods for each variable. There's nothing special about accessor methods; they're just like any other method.

Consider the following example:




class Circle {



   private int x, y radius;



   public int getRadius() {



     return radius;



   }



   public int setRadius(int value) {



       radius = value;



       draw();



       doOtherStuff();



       return radius;



   }



    ....



}

In this modified example of the Circle class, the accessor methods for the instance variable radius have the names set and get appended with the name of the variable. This is a naming convention popular amongst many programmers for accessor methods, so you always know which methods do what and to which variable. To access or change the value of the instance variable, therefore, you'd just call the following methods:




radius = theCircle.getRadius(); //get the value



theCircle.setRadius(4); //set the value (and redraw, etc)

Another convention for naming accessor methods is to use the same name for the methods as for the variable itself. In Java, it is legal for instance variables and methods to have the same name; Java knows how to perform the right operation from how they are used. Although this does make accessor methods shorter to type, there are two problems with using this convention:

The convention you use is a question of personal taste. The most important thing is to choose a convention and stick with it throughout all of your classes so that your interfaces are consistent and understandable.

Using Accessor Methods

The point of declaring instance variables private and using accessor methods is so that external users of your class will be forced to use the methods you choose to modify your class' data. But the benefit of accessor methods isn't just for use by objects external to yours; they're also there for you. Just because you have access to the actual instance variable inside your own class doesn't mean you can avoid using accessor methods.

Consider that one of the good reasons to make instance variables private is to hide implementation details from outside your object. Protecting a variable with accessor methods means that other objects don't need to know about anything other than the accessor methods—you can happily change the internal implementation of your class without wreaking havoc on everyone who's used your class. The same is true of your code inside that class; by keeping variables separate from accessors, if you must change something about a given instance variable all you have to change are the accessor methods and not every single reference to the variable itself. In terms of code maintenance and reuse, what's good for the goose (external users of your class) is generally also good for the gander (you, as a user of your own class).

Class Variables and Methods


A class variable or method is one that exists only once, for the class. For example, a class variable is not created each time you create a new instance of the class. This is useful for variables that hold information that applies to the entire class.

To create a class variable or method, simply include the word static in front of the method name. The static modifier typically comes after any protection modifiers, such as this:




public class  Circle {



    public static float  pi = 3.14159265F;



    public float  area(float r) {



        return  pi * r * r;



    }



}


The word static comes from C and C++. Although static has a specific meaning for where a method or variable is stored in a program's runtime memory in those languages, it simply means that it's stored in the class in Java. Whenever you see the word static, remember to substitute mentally the word class.

Both class variables and methods can be accessed using standard dot notation with either the class name or an object on the left side of the dot. However, the convention is to always use the name of the class, to clarify that a class variable is being used, and to help the reader to know instantly that the variable is global to all instances. Here are a few examples:




float circumference = 2 * Circle.pi * getRadius();



float randomNumer = Math.random();


class variables, for the same reasons as instance variables can also benefit from being declared private and having accessor methods get or set their values.

Listing 17.1 shows a class that uses class and instance variables to keep track of how many instances of that class have been created:

Listing 17.1. The CountInstances example.




 1: public class  CountInstances {



 2:    private static int   numInstances = 0;



 3:



 4:     protected static int getNumInstances() {



 5:         return numInstances;



 6:    }



 7:



 8:     private static void  addInstance() {



 9:         numInstances++;



10:     }



11:



12:    CountInstances() {



13:         CountInstances.addInstance();



14:    }



15:



16:     public static void  main(String args[]) {



17:         System.out.println("Starting with " +



18:           CountInstances.getNumInstances() + " instances");



19:         for (int  i = 0;  i < 10;  ++i)



20:             new CountInstances();



21:       System.out.println("Created " +



22:           CountInstances.getNumInstances() + " instances");



23:    }



24:}

The following is the output:




Started with 0 instances



Creates 10 instances

This example has a number of features, so now go through it line by line.

In line 2, you declare a private class variable to hold the number of instances (called numInstances). This is a class variable (declared static) because the number of instances is relevant to the class as a whole, not to any one instance. And it's private so that it follows the same rules as instance variables accessor methods.

Note the initialization of numInstances to 0 in that same line. Just as an instance variable is initialized when its instance is created, a class variable is initialized when its class is created. This class initialization happens essentially before anything else can happen to that class, or its instances, so the class in the example will work as planned.

In lines 4 through 6, you created a get method for that private instance variable to get its value (getNumInstances()). This method is also declared as a class method, as it applies directly to the class variable. The getNumInstances() method is declared protected, as opposed to public, because only this class and perhaps subclasses will be interested in that value; other random classes are therefore restricted from seeing it.

Note that there's no accessor method to set the value. The reason is that the value of the variable should only be incremented when a new instance is created; it should not be set to any random value. Instead of creating an accessor method, therefore, you'll create a special private method called addInstance() in lines 8 through 10 that increments the value of numInstances by one.

In lines 12 through 14, you have the constructor method for this class. Remember, constructors are called when a new object is created, which makes this the most logical place to call addInstance() and to increment the variable.

And finally, the main() method indicates that you can run this as a Java application and test all the other methods. In the main() method, you create ten instances of the CountInstances class, reporting after you're finished the value of the numInstances class variable (which, predictably, prints 10).

Finalizing Classes, Methods, and Variables


Although it's not the final modifier discussed in this chapter, the final modifier is used to finalize classes, methods, and variables. Finalizing a thing effectively freezes the implementation or value of that thing. More specifically, it does the following:

Finalization (using the final modifier) freezes the implementation of a class, method, or variable.

Finalizing Classes


To finalize a class, add the final modifier to its definition. final typically goes after any protection modifiers such as private or public:




public final class  AFinalClass {



    . . .



}

You declare a class final for only two reasons:

The Java class library uses final classes extensively. Classes that have finalized for security reasons are java.lang.System, java.net.InetAddress, and java.net.Socket. A good example of the second reason is java.lang.String. Strings are so common in Java, and so central to it, that the runtime handles them specially (for security reasons as well).

In most cases, it will be a rare event for you to create a final class yourself because extendible classes are so much more useful than finalized classes, and the efficiency gains are minimal. You will, however, most likely have plenty of opportunity to be upset at certain system classes being final (making extending them more difficult).

Finalized Variables


A finalized variable means its value cannot be changed. This is effectively a constant. To declare constants in Java, use final variables with initial values:




public class  AnotherFinalClass {



    public static final int aConstantInt    = 123;



    public final String aConstantString = "Hello world!";



}

Local variables (those inside blocks of code surrounded by braces, for example, in while or for loops) can't be declared final.

Finalized Methods


Finalized methods are methods that cannot be overridden; that is, their implementations are frozen and cannot be redefined in subclasses.




public class  ClassWithFinalMethod {



    public final void  noOneGetsToDoThisButMe() {



        . . .



    }



}

The only reason to declare a method final is efficiency. Normally, method signatures and implementations are matched up when your Java program runs because that method can be found in the current class or in any subclass. When you call a method, therefore, Java checks the current class and each superclass in turn for that definition. Although this makes methods very flexible to define and use, it is also not very fast.

If you declare a method final, however, the compiler can then inline it (stick its definition) right in the middle of methods that call it, because it knows that no one else can ever subclass and override the method to change its meaning. Although you might not use final right away when writing a class, as you tune the system later, you might discover that a few methods have to be final to make your class fast enough. Almost all your methods will be fine, however, just as they are.

If you use accessor methods a lot (as recommended), changing your accessor methods to be final can be a quick way of speeding up your class. Because subclasses will rarely want to change the definitions of those accessor methods, there's little reason those methods should not be final.

The Java class library declares a lot of commonly used methods final so that you'll benefit from the speed-up. In the case of classes that are already final, this makes perfect sense and is a wise choice. The few final methods declared in non-final classes will annoy you—your subclasses can no longer override them. When efficiency becomes less of an issue for the Java environment, many of these final methods can be unfrozen again, restoring this lost flexibility to the system.



private methods are effectively final, as are all methods declared in a final class. Marking these latter methods final (as the Java library sometimes does) is legal, but redundant; the compiler already treats them as final.

It's possible to use final methods for some of the same security reasons you use final classes, but it's a much rarer event.



abstract Classes and Methods


Whenever you arrange classes into an inheritance hierarchy, the presumption is that higher classes are more abstract and general, whereas lower subclasses are more concrete and specific. Often, as you design hierarchies of classes, you factor out common design and implementation into a shared superclass. That superclass won't have any instances; its sole reason for existing is to act as a common, shared repository for information that its subclasses use. These kind of classes are called abstract classes, and you declare them using the abstract modifier.




public abstract class Fruit {



...



}

abstract classes can never be instantiated (you'll get a compiler error if you try), but they can contain anything a normal class can contain, including class and instance variables and methods with any kind of protection or finalization modifiers. In addition, abstract classes can also contain abstract methods. An abstract method is a method signature with no implementation; subclasses of the abstract class are expected to provide the implementation for that method. abstract methods, in this way, provide the same basic concept as abstract classes; they're a way of factoring common behavior into superclasses and then providing specific concrete uses of those behaviors in subclasses.

abstract classes are classes whose sole purpose is to provide common information for subclasses. abstract classes can have no instances.

abstract methods are methods with signatures, but no implementation. Subclasses of the class that contain that abstract method must provide its actual implementation.

Like abstract classes, abstract methods give you the capability to factor common information into a general superclass and then reuse that class in different ways.

The opposite of abstract is concrete: concrete classes are classes that can be instantiated; concrete methods are those that have actual implementations.

abstract methods are declared with the abstract modifier, which usually goes after the protection modifiers but before either static or final. In addition, they have no body. abstract methods can only exist inside abstract classes; even if you have a class full of concrete methods, with only one abstract method, the whole class must be abstract.

Listing 17.2 shows two simple classes. One, called appropriately, MyFirstAbstractClass, has an instance variable and two methods. One of those methods, subclassesImplementMe(), is abstract. The other, doSomething(), is concrete and has a normal definition.

The second class is AConcreteSubclass, which is a subclass of MyFirstAbstractClass. It provides the implementation of subclassesImplementMe(), and inherits the remaining behavior from MyFirstAbstractClass.

Listing 17.2. An example of using abstract.




public abstract class  MyFirstAbstractClass {



    int  anInstanceVariable;



    public abstract int  subclassesImplementMe(); // note no definition



    public void  doSomething() {



        . . .    // a normal method



    }



}



public class  AConcreteSubClass extends MyFirstAbstractClass {



    public int  subclassesImplementMe() {



        . . .    // we *must* implement this method here



    }



}

Here are some attempted uses of these classes:




Object  a = new MyFirstAbstractClass();    // illegal, is abstract



Object  c = new AConcreteSubClass();       // OK, a concrete subclass

Using an abstract class with no abstract methods—that is, one which provides nothing but a template for behavior—is better accomplished in Java by using an interface (discussed tomorrow). Whenever a design calls for an abstraction that includes instance state and/or a partial implementation, however, an abstract class is your only choice.

Summary


Today, you learned how variables and methods can control their visibility and access by other classes via the four Ps of protection: public, package, protected, and private. You also learned that, although instance variables are most often declared private, declaring accessor methods allows you to control the reading and writing of them separately. Protection levels allow you, for example, to cleanly separate your public abstractions from their concrete representations.

You also learned how to create class variables and methods, which are associated with the class itself, and how to declare final variables, methods, and classes to represent constants and fast or secure methods and classes.

Finally, you discovered how to declare and use abstract classes, which cannot be instantiated, and abstract methods, which have no implementation and must be overridden in subclasses. Together, they provide a template for subclasses to fill in and act as a variant of the powerful interfaces of Java that you'll study tomorrow.

Q&A


Q: Why are there so many different levels of protection in Java?

A: Each level of protection, or visibility, provides a different view of your class to the outside world. One view is tailored for everyone, one for classes in your own package, another for your class and its subclasses only, one combining these last two, and the final one for just within your class. Each is a logically well-defined and useful separation that Java supports directly in the language (as opposed to, for example, accessor methods, which are a convention you must follow).

Q: Won't using accessor methods everywhere slow down my Java code?

A: Not always. As Java compilers improve and can create more optimizations, the use of accessor methods should not slow your code significantly, but if you're concerned about speed, you can always declare accessor methods to be final, and they'll be just as fast as direct instance variable accesses.

Q: Are class (static) methods inherited just like instance methods?

A: No. static (class) methods are now final by default. How, then, can you ever declare a non-final class method? The answer is that you can't! Inheritance of class methods is not allowed, breaking the symmetry with instance methods.

Q: Based on what I've learned, it seems like private abstract methods and final abstract methods or classes don't make sense. Are they legal?

A: Nope, they're compile-time errors, as you have guessed. To be useful, abstract methods must be overridden, and abstract classes must be subclassed, but neither of these two operations would be legal if they were also private or final.

Q: I tried creating a private variable inside a method definition. It didn't work. What did I do wrong?

A: Nothing. All the modifiers in this chapter, when you can use them with variables, only apply to class and instance variables. Local variables—those that appear inside the body of a method or loop—cannot use any of these modifiers.

Previous Page Page Top TOC Next Page