Certification Objective Declare Class Members (Objectives 1.3 and 1.4)


Certification Objective —Declare Class Members (Objectives 1.3 and 1.4)

1. 3 Develop code that declares, initializes, and uses primitives, arrays, enums, and objects as static, instance, and local variables. Also, use legal identifiers for variable names.

1.4 Develop code that declares both static and non-static methods, andif appropriateuse method names that adhere to the JavaBeans naming standards. Also develop code that declares and uses a variable-length argument list.

We've looked at what it means to use a modifier in a class declaration, and now we'll look at what it means to modify a method or variable declaration.

Methods and instance (nonlocal) variables are collectively known as members. You can modify a member with both access and nonaccess modifiers, and you have more modifiers to choose from (and combine) than when you're declaring a class.

Access Modifiers

Because method and variable members are usually given access control in exactly the same way, we'll cover both in this section.

Whereas a class can use just two of the four access control levels (default or public), members can use all four:

  • public

  • protected

  • default

  • private

Default protection is what you get when you don't type an access modifier in the member declaration. The default and protected access control types have almost identical behavior, except for one difference that will be mentioned later.

It's crucial that you know access control inside and out for the exam. There will be quite a few questions with access control playing a role. Some questions test several concepts of access control at the same time, so not knowing one small part of access control could blow an entire question.

What does it mean for code in one class to have access to a member of another class? For now, ignore any differences between methods and variables. If class A has access to a member of class B, it means that class B's member is visible to class A. When a class does not have access to another member, the compiler will slap you for trying to access something that you're not even supposed to know exists!

You need to understand two different access issues:

  • Whether method code in one class can access a member of another class

  • Whether a subclass can inherit a member of its superclass

The first type of access is when a method in one class tries to access a method or a variable of another class, using the dot operator (.) to invoke a method or retrieve a variable. For example:

 class Zoo {   public String coolMethod() {     return "Wow baby";   } } class Moo {   public void useAZoo() {     Zoo z = new Zoo();     // If the preceding line compiles Moo has access     // to the Zoo class     // But... does it have access to the coolMethod()?     System.out.println("A Zoo says, " + z. coolMethod());     // The preceding line works because Moo can access the     // public method   } } 

The second type of access revolves around which, if any, members of a superclass a subclass can access through inheritance. We're not looking at whether the subclass can, say, invoke a method on an instance of the superclass (which would just be an example of the first type of access). Instead, we're looking at whether the subclass inherits a member of its superclass. Remember, if a subclass inherits a member, it's exactly as if the subclass actually declared the member itself. In other words, if a subclass inherits a member, the subclass has the member.

 class Zoo {   public String coolMethod() {     return "Wow baby";  } } class Moo extends Zoo {   public void useMyCoolMethod() {     // Does an instance of Moo inherit the coolMethod()?     System.out.println("Moo says, " + this.coolMethod());     // The preceding line works because Moo can inherit the     // public method     // Can an instance of Moo invoke coolMethod() on an     // instance of Zoo?     Zoo z = new Zoo();     System.out.println("Zoo says, " + z.coolMethod());     // coolMethod() is public, so Moo can invoke it on a Zoo     //reference   } } 

Figure 1-2 compares a class inheriting a member of another class, and accessing a member of another class using a reference of an instance of that class.

image from book
Figure 1-2: Comparison of inheritance vs. dot operator for member access.

Much of access control (both types) centers on whether the two classes involved are in the same or different packages. Don't forget, though, if class A itself can't be accessed by class B, then no members within class A can be accessed by class B.

You need to know the effect of different combinations of class and member access (such as a default class with a public variable). To figure this out, first look at the access level of the class. If the class itself will not be visible to another class, then none of the members will be either, even if the member is declared public. Once you've confirmed that the class is visible, then it makes sense to look at access levels on individual members.

Public Members

When a method or variable member is declared public, it means all other classes, regardless of the package they belong to, can access the member (assuming the class itself is visible).

Look at the following source file:

 package book; import cert.*;  // Import all classes in the cert package class Goo {   public static void main(String[] args) {     Sludge o = new Sludge();     o.testIt();   } } 

Now look at the second file:

 package cert; public class Sludge {   public void testIt() { System.out.println("sludge"); } } 

As you can see, Goo and Sludge are in different packages. However, Goo can invoke the method in Sludge without problems because both the Sludge class and its testIt() method are marked public.

For a subclass, if a member of its superclass is declared public, the subclass inherits that member regardless of whether both classes are in the same package:

 package cert; public class Roo {   public String doRooThings() {     // imagine the fun code that goes here     return "fun";   } } 

The Roo class declares the doRooThings() member as public. So if we make a subclass of Roo, any code in that Roo subclass can call its own inherited doRooThings() method.

 package notcert;   //Not the package Roo is in import cert.Roo; class Cloo extends Roo {   public void testCloo() {     System.out.println(doRooThings());   } } 

Notice in the preceding code that the doRooThings() method is invoked without having to preface it with a reference. Remember, if you see a method invoked (or a variable accessed) without the dot operator (.), it means the method or variable belongs to the class where you see that code. It also means that the method or variable is implicitly being accessed using the this reference. So in the preceding code, the call to doRooThings() in the Cloo class could also have been written as this.doRooThings(). The reference this always refers to the currently executing object—in other words, the object running the code where you see the this reference. Because the this reference is implicit, you don't need to preface your member access code with it, but it won't hurt. Some programmers include it to make the code easier to read for new (or non) Java programmers.

Besides being able to invoke the doRooThings() method on itself, code from some other class can call doRooThings() on a Cloo instance, as in the following:

 class Toon {   public static void main(String[] args) {     Cloo c = new Cloo();     System.out.println(c.doRooThings()); //No problem; method                                          // is public   } } 

Private Members

Members marked private can't be accessed by code in any class other than the class in which the private member was declared. Let's make a small change to the Roo class from an earlier example.

 package cert; public class Roo {   private String doRooThings() {     // imagine the fun code that goes here, but only the Roo     // class knows     return "fun";   } } 

The doRooThings() method is now private, so no other class can use it. If we try to invoke the method from any other class, we'll run into trouble:

 package notcert; import cert.Roo; class  UseARoo {   public void testIt() {     Roo r = new Roo(); //So far so good; class Roo is public     System.out.println(r.doRooThings()); //Compiler error!   } } 

If we try to compile UseARoo, we get a compiler error something like this:

 cannot find symbol symbol  : method doRooThings() 

It's as if the method doRooThings() doesn't exist, and as far as any code outside of the Roo class is concerned, it's true. A private member is invisible to any code outside the member's own class.

What about a subclass that tries to inherit a private member of its superclass? When a member is declared private, a subclass can't inherit it. For the exam, you need to recognize that a subclass can't see, use, or even think about the private members of its superclass. You can, however, declare a matching method in the subclass. But regardless of how it looks, it is not an overriding method! It is simply a method that happens to have the same name as a private method (which you're not supposed to know about) in the superclass. The rules of overriding do not apply, so you can make this newly-declared-but-just-happens-to-match method declare new exceptions, or change the return type, or anything else you want to do with it.

 package cert; public class Roo {    private String doRooThings() {     // imagine the fun code that goes here, but no other class     // will know     return "fun";    } } 

The doRooThings() method is now off limits to all subclasses, even those in the same package as the superclass:

 package cert;             //Cloo and Roo are in the same package class  Cloo extends Roo {  //Still OK, superclass Roo is public   public void testCloo() {     System.out.println(doRooThings()); //Compiler error!   } } 

If we try to compile the subclass Cloo, the compiler is delighted to spit out an error something like this:

 %javac Cloo.java Cloo.java:4: Undefined method: doRooThings()       System.out.println(doRooThings()); 1 error 

On the Job 

Although you're allowed to mark instance variables as public, in practice it's nearly always best to keep all variables private or protected. If variables need to be changed, set, or read, programmers should use public accessor methods, so that code in any other class has to ask to get or set a variable (by going through a method), rather than access it directly. JavaBean-compliant accessor methods take the form get<propertyName> or, for booleans, is<propertyName> and set<propertyName>, and provide a place to check and/or validate before returning or modifying a value.

Without this protection, the weight variable of a Cat object, for example, could be set to a negative number if the offending code goes straight to the public variable as in somecat. weight = -20. But an accessor method, setweight (int wt), could check for an inappropriate number. (OK, wild speculation, but we're guessing a negative weight might be inappropriate for a cat. Or not.) Chapter 2 will discuss this data protection (encapsulation) in more detail.

Can a private method be overridden by a subclass? That's an interesting question, but the answer is technically no. Since the subclass, as we've seen, cannot inherit a private method, it therefore cannot override the method—overriding depends on inheritance. We'll cover the implications of this in more detail a little later in this section as well as in Chapter 2, but for now just remember that a method marked private cannot be overridden. Figure 1-3 illustrates the effects of the public and private modifiers on classes from the same or different packages.

image from book
Figure 1-3: Effects of public and private access

Protected and Default Members

The protected and default access control levels are almost identical, but with one critical difference. A default member may be accessed only if the class accessing the member belongs to the same package, whereas a protected member can be accessed (through inheritance) by a subclass even if the subclass is in a different package.

Take a look at the following two classes:

 package certification; public class OtherClass {   void testIt() {   // No modifier means method has default                     // access     System.out.println("OtherClass");   } } 

In another source code file you have the following:

 package somethingElse; import certification.OtherClass; class AccessClass {   static public void main(String[] args) {     OtherClass o = new OtherClass();     o.testIt();   } } 

As you can see, the testIt() method in the first file has default (think: package-level) access. Notice also that class OtherClass is in a different package from the AccessClass. Will AccessClass be able to use the method testIt()? Will it cause a compiler error? Will Daniel ever marry Francesca? Stay tuned.

 No method matching testIt() found in class certification.OtherClass.   o.testIt(); 

From the preceding results, you can see that AccessClass can't use the OtherClass method testIt() because testIt() has default access, and AccessClass is not in the same package as OtherClass. So AccessClass can't see it, the compiler complains, and we have no idea who Daniel and Francesca are.

Default and protected behavior differ only when we talk about subclasses. If the protected keyword is used to define a member, any subclass of the class declaring the member can access it through inheritance. It doesn't matter if the superclass and subclass are in different packages, the protected superclass member is still visible to the subclass (although visible only in a very specific way as we'll see a little later). This is in contrast to the default behavior, which doesn't allow a subclass to access a superclass member unless the subclass is in the same package as the superclass.

Whereas default access doesn't extend any special consideration to subclasses (you're either in the package or you're not), the protected modifier respects the parent-child relationship, even when the child class moves away (and joins a new package). So, when you think of default access, think package restriction. No exceptions. But when you think protected, think package + kids. A class with a protected member is marking that member as having package-level access for all classes, but with a special exception for subclasses outside the package.

But what does it mean for a subclass-outside-the-package to have access to a superclass (parent) member? It means the subclass inherits the member. It does not, however, mean the subclass-outside-the-package can access the member using a reference to an instance of the superclass. In other words, protected = inheritance. Protected does not mean that the subclass can treat the protected superclass member as though it were public. So if the subclass-outside-the-package gets a reference to the superclass (by, for example, creating an instance of the superclass somewhere in the subclass' code), the subclass cannot use the dot operator on the superclass reference to access the protected member. To a subclass-outside-the-package, a protected member might as well be default (or even private), when the subclass is using a reference to the superclass. The subclass can see the protected member only through inheritance.

Are you confused? So are we. Hang in there and it will all become clear with the next batch of code examples. (And don't worry; we're not actually confused. We're just trying to make you feel better if you are. You know, like it's OK for you to feel as though nothing makes sense, and that it isn't your fault. Or is it? <insert evil laugh>)

Protected Details

Let's take a look at a protected instance variable (remember, an instance variable is a member) of a superclass.

 package certification; public class Parent {    protected int x = 9; // protected access } 

The preceding code declares the variable x as protected. This makes the variable accessible to all other classes inside the certification package, as well as inheritable by any subclasses outside the package. Now let's create a subclass in a different package, and attempt to use the variable x (that the subclass inherits):

 package other; // Different package import certification.Parent; class Child extends Parent {    public void testIt() {       System.out.println("x is " + x); // No problem; Child                                        // inherits x    } } 

The preceding code compiles fine. Notice, though, that the Child class is accessing the protected variable through inheritance. Remember, any time we talk about a subclass having access to a superclass member, we could be talking about the subclass inheriting the member, not simply accessing the member through a reference to an instance of the superclass (the way any other nonsubclass would access it). Watch what happens if the subclass Child (outside the superclass' package) tries to access a protected variable using a Parent class reference.

 package other; import certification.Parent; class Child extends Parent {    public void testIt() (       System.out.println("x is " + x); // No problem; Child                                        // inherits x       Parent p = new Parent(); // Can we access x using the                                // p reference?       System.out.println("X in parent is " + p.x); // Compiler                                                    // error!    } } 

The compiler is more than happy to show us the problem:

 %javac -d . other/Child.java other/Child.java:9: x has protected access in certification.Parent System.out.println("X in parent is " + p.x);                                         ^ 1 error 

So far we've established that a protected member has essentially package-level or default access to all classes except for subclasses. We've seen that subclasses outside the package can inherit a protected member. Finally, we've seen that subclasses outside the package can't use a superclass reference to access a protected member. For a subclass outside the package, the protected member can be accessed only through inheritance.

But there's still one more issue we haven't looked atwhat does a protected member look like to other classes trying to use the subclass-outside-the-package to get to the subclass' inherited protected superclass member? For example, using our previous Parent/Child classes, what happens if some other class—Neighbor, say—in the same package as the Child (subclass), has a reference to a Child instance and wants to access the member variable x ? In other words, how does that protected member behave once the subclass has inherited it? Does it maintain its protected status, such that classes in the Child's package can see it?

No! Once the subclass-outside-the-package inherits the protected member, that member (as inherited by the subclass) becomes private to any code outside the subclass, with the exception of subclasses of the subclass. So if class Neighbor instantiates a Child object, then even if class Neighbor is in the same package as class Child, class Neighbor won't have access to the Child's inherited (but protected) variable x. The bottom line: when a subclass-outside-the-package inherits a protected member, the member is essentially private inside the subclass, such that only the subclass and its subclasses can access it. Figure 1-4 illustrates the effect of protected access on classes and subclasses in the same or different packages.

image from book
Figure 1-4: Effects of protected access

Whew! That wraps up protected, the most misunderstood modifier in Java. Again, it's used only in very special cases, but you can count on it showing up on the exam. Now that we've covered the protected modifier, we'll switch to default member access, a piece of cake compared to protected.

Default Details

Let's start with the default behavior of a member in a superclass. We'll modify the Parent's member x to make it default.

 package certification; public class Parent {   int x = 9; // No access modifier, means default              // (package) access } 

Notice we didn't place an access modifier in front of the variable x. Remember that if you don't type an access modifier before a class or member declaration, the access control is default, which means package level. We'll now attempt to access the default member from the Child class that we saw earlier.

When we compile the child file, we get an error something like this:

 Child.java:4: Undefined variable: x       System.out.println("Variable x is " + x); 1 error 

The compiler gives the same error as when a member is declared as private. The subclass Child (in a different package from the superclass Parent) can't see or use the default superclass member x! Now, what about default access for two classes in the same package?

 package certification; public class Parent{   int x = 9; // default access } 

And in the second class you have the following:

 package certification; class Child extends Parent{   static public void main(String[] args) {     Child sc = new Child();     sc.testIt();   }   public void testIt() {     System.out.println("Variable x is " + x); // No problem;   } } 

The preceding source file compiles fine, and the class Child runs and displays the value of x. Just remember that default members are visible to subclasses only if those subclasses are in the same package as the superclass.

Local Variables and Access Modifiers

Can access modifiers be applied to local variables? NO!

There is never a case where an access modifier can be applied to a local variable, so watch out for code like the following:

 class Foo {   void doStuff() {     private int x = 7;     this.doMore(x);   } } 

You can be certain that any local variable declared with an access modifier will not compile. In fact, there is only one modifier that can ever be applied to local variables—final.

That about does it for our discussion on member access modifiers. Table 1-2 shows all the combinations of access and visibility; you really should spend some time with it. Next, we're going to dig into the other (nonaccess) modifiers that you can apply to member declarations.

Table 1-2: Determining Access to Class Members

Visibility

Public

Protected

Default

Private

From the same class

Yes

Yes

Yes

Yes

From any class in the same package

Yes

Yes

Yes

No

From a subclass in the same package

Yes

Yes

Yes

No

From a subclass outside the same package

Yes

Yes, through inheritance

No

No

From any non-subclass class outside the package

Yes

No

No

No

Nonaccess Member Modifiers

We've discussed member access, which refers to whether code from one class can invoke a method (or access an instance variable) from another class. That still leaves a boatload of other modifiers you can use on member declarations. Two you're already familiar with—final and abstract—because we applied them to class declarations earlier in this chapter. But we still have to take a quick look at transient, synchronized, native, strictfp, and then a long look at the Big One—static.

We'll look first at modifiers applied to methods, followed by a look at modifiers applied to instance variables. We'll wrap up this section with a look at how static works when applied to variables and methods.

Final Methods

The final keyword prevents a method from being overridden in a subclass, and is often used to enforce the API functionality of a method. For example, the Thread class has a method called isAlive() that checks whether a thread is still active. If you extend the Thread class, though, there is really no way that you can correctly implement this method yourself (it uses native code, for one thing), so the designers have made it final. Just as you can't subclass the String class (because we need to be able to trust in the behavior of a String object), you can't override many of the methods in the core class libraries. This can't-be-overridden restriction provides for safety and security, but you should use it with great caution. Preventing a subclass from overriding a method stifles many of the benefits of OO including extensibility through polymorphism. A typical final method declaration looks like this:

 class SuperClass{   public final void showSample() {     System.out.println("One thing.");   } } 

It's legal to extend Superclass, since the class isn't marked final, but we can't override the final method showsample(), as the following code attempts to do:

 class Subclass extends SuperClass{   public void showSample() { // Try to override the final                              // superclass method     System.out.println("Another thing.");   } } 

Attempting to compile the preceding code gives us something like this:

 %javac FinalTest.java FinalTest.java:5: The method void showsample() declared in class Subclass cannot override the final method of the same signature declared in class SuperClass. Final methods cannot be overridden.      public void showSample() { } 1 error 

Final Arguments

Method arguments are the variable declarations that appear in between the parentheses in a method declaration. A typical method declaration with multiple arguments looks like this:

 public Record getRecord(int fileNumber, int recordNumber) {} 

Method arguments are essentially the same as local variables. In the preceding example, the variables fileNumber and recordNumber will both follow all the rules applied to local variables. This means they can also have the modifier final:

 public Record getRecord(int fileNumber, final int recNumber) {} 

In this example, the variable recordNumber is declared as final, which of course means it can't be modified within the method. In this case, "modified" means reassigning a new value to the variable. In other words, a final argument must keep the same value that the parameter had when it was passed into the method.

Abstract Methods

An abstract method is a method that's been declared (as abstract) but not implemented. In other words, the method contains no functional code. And if you recall from the earlier section "Abstract Classes," an abstract method declaration doesn't even have curly braces for where the implementation code goes, but instead closes with a semicolon. In other words, it has no method body. You mark a method abstract when you want to force subclasses to provide the implementation. For example, if you write an abstract class Car with a method goUpHill(), you might want to force each subtype of Car to define its own goUpHill() behavior, specific to that particular type of car.

 public abstract void showSample(); 

Notice that the abstract method ends with a semicolon instead of curly braces, It is illegal to have even a single abstract method in a class that is not explicitly declared abstract! Look at the following illegal class:

 public class IllegalClass{   public abstract void doIt(); } 

The preceding class will produce the following error if you try to compile it:

 IllegalClass.java:1: class IllegalClass must be declared abstract. It does not define void doIt() from class IllegalClass. public class IllegalClass{ 1 error 

You can, however, have an abstract class with no abstract methods. The following example will compile fine:

 public abstract class LegalClass{    void goodMethod() {       // lots of real implementation code here    } } 

In the preceding example, goodMethod() is not abstract. Three different clues tell you it's not an abstract method:

  • The method is not marked abstract.

  • The method declaration includes curly braces, as opposed to ending in a semicolon. In other words, the method has a method body.

  • The method provides actual implementation code.

Any class that extends an abstract class must implement all abstract methods of the superclass, unless the subclass is also abstract. The rule is this:

The first concrete subclass of an abstract class must implement all abstract methods of the superclass.

Concrete just means nonabstract, so if you have an abstract class extending another abstract class, the abstract subclass doesn't need to provide implementations for the inherited abstract methods. Sooner or later, though, somebody's going to make a nonabstract subclass (in other words, a class that can be instantiated), and that subclass will have to implement all the abstract methods from up the inheritance tree. The following example demonstrates an inheritance tree with two abstract classes and one concrete class:

 public abstract class Vehicle {   private String type;   public abstract void goUpHill();  // Abstract method   public String getType() {         // Non-abstract method     return type;   } } public abstract class Car extends Vehicle {   public abstract void goUpHill();  // Still abstract   public void doCarThings() {     // special car code goes here }   } } public class Mini extends Car {   public void goUpHill() {     // Mini-specific going uphill code   } } 

So how many methods does class Mini have? Three. It inherits both the getType() and doCarThings() methods, because they're public and concrete (nonabstract). But because goUpHill() is abstract in the superclass Vehicle, and is never implemented in the Car class (so it remains abstract), it means class Mini—as the first concrete class below Vehicle—must implement the goUpHill() method. In other words, class Mini can't pass the buck (of abstract method implementation) to the next class down the inheritance tree, but class Car can, since Car, like Vehicle, is abstract. Figure 1-5 illustrates the effects of the abstract modifier on concrete and abstract subclasses.

image from book
Figure 1-5: The effects of the abstract modifier on concrete and abstract subclasses

Look for concrete classes that don't provide method implementations for abstract methods of the superclass. The following code won't compile:

 public abstract class A {   abstract void foo(); } class B extends A {   void foo(int I) { } } 

Class B won't compile because it doesn't implement the inherited abstract method foo(). Although the foo(int I) method in class B might appear to be an implementation of the superclass' abstract method, it is simply an overloaded method (a method using the same identifier, but different arguments), so it doesn't fulfill the requirements for implementing the superclass' abstract method. We'll look at the differences between overloading and overriding in detail in Chapter 2.

A method can never, ever, ever be marked as both abstract and final, or both abstract and private. Think about it—abstract methods must be implemented (which essentially means overridden by a subclass) whereas final and private methods cannot ever be overridden by a subclass. Or to phrase it another way, an abstract designation means the superclass doesn't know anything about how the subclasses should behave in that method, whereas a final designation means the superclass knows everything about how all subclasses (however far down the inheritance tree they may be) should behave in that method. The abstract and final modifiers are virtually opposites. Because private methods cannot even be seen by a subclass (let alone inherited), they too cannot be overridden, so they too cannot be marked abstract.

Finally, you need to know that the abstract modifier can never be combined with the static modifier. We'll cover static methods later in this objective, but for now just remember that the following would be illegal:

 abstract static void doStuff(); 

And it would give you an error that should be familiar by now:

 MyClass.java:2: illegal combination of modifiers: abstract and static   abstract static void doStuff(); 

Synchronized Methods

The synchronized keyword indicates that a method can be accessed by only one thread at a time. We'll discuss this nearly to death in Chapter 11, but for now all we're concerned with is knowing that the synchronized modifier can be applied only to methods—not variables, not classes, just methods. A typical synchronized declaration looks like this:

 public synchronized Record retrieveUserInfo(int id) { } 

You should also know that the synchronized modifier can be matched with any of the four access control levels (which means it can be paired with any of the three access modifier keywords).

Native Methods

The native modifier indicates that a method is implemented in platform-depen dent code, often in C. You don't need to know how to use native methods for the exam, other than knowing that native is a modifier (thus a reserved keyword) and that native can be applied only to methods—not classes, not variables, just methods. Note that a native method's body must be a semicolon (;) (like abstract, methods), indicating that the implementation is omitted.

Strictfp Methods

We looked earlier at using Strictfp as a class modifier, but even if you don't declare a class as Strictfp, you can still declare an individual method as strictfp. Remember, strictfp forces floating points (and any floating-point operations) to adhere to the IEEE 754 standard. With strictfp, you can predict how your floating points will behave regardless of the underlying platform the JVM is running on. The downside is that if the underlying platform is capable of supporting greater precision, a strictfp method won't be able to take advantage of it.

You'll want to study the IEEE 754 if you need something to help you fall asleep. For the exam, however, you don't need to know anything about strictfp other than what it's used for, that it can modify a class or method declaration, and that a variable can never be declared strictfp.

Methods with Variable Argument Lists (var-args)

As of 5.0, Java allows you to create methods that can take a variable number of arguments. Depending on where you look, you might hear this capability referred to as "variable-length argument lists," "variable arguments," "var-args," "varargs," or our personal favorite (from the department of obfuscation), "variable arity parameter." They're all the same thing, and we'll use the term "var-args" from here on out.

As a bit of background, we'd like to clarify how we're going to use the terms "argument" and "parameter" throughout this book.

  • arguments The things you specify between the parentheses when you're invoking a method:

     doStuff("a", 2);  // invoking doStuff, so a & 2 are arguments 

  • parameters The things in the method's signature that indicate what the method must receive when it's invoked:

     void doStuff(String s, int a) { } // we're expecting two                               // parameters: String and int 

We'll cover using var-arg methods more in the next few chapters, for now let's review the declaration rules for var-args:

  • Var-arg type When you declare a var-arg parameter, you must specify the type of the argument(s) this parameter of your method can receive. (This can be a primitive type or an object type.)

  • Basic syntax To declare a method using a var-arg parameter, you follow the type with an ellipsis (), a space, and then the name of the array that will hold the parameters received.

  • Other parameters It's legal to have other parameters in a method that uses a var-arg.

  • Var-args limits The var-arg must be the last parameter in the method's signature, and you can have only one var-arg in a method.

Let's look at some legal and illegal var-arg declarations:

Legal:

 void doStuff(int... x) { }  // expects from 0 to many ints                             // as parameters void doStuff2(char c, int... x)  { }  // expects first a char,                                       // then 0 to many ints void doStuff3(Animal... animal) { }   // 0 to many Animals 

Illegal:

 void doStuff4(int x...) { }             // bad syntax void doStuff5(int... x, char... y) { }  // too many var-args void doStuff6(String... s, byte b) { }  // var-arg must be last 

Constructor Declarations

In Java, objects are constructed. Every time you make a new object, at least one constructor is invoked. Every class has a constructor, although if you don't create one explicitly, the compiler will build one for you. There are tons of rules concerning constructors, and we're saving our detailed discussion for Chapter 2. For now, let's focus on the basic declaration rules. Here's a simple example:

 class Foo {   protected Foo() { }           // this is Foo's constructor   protected void Foo() { }      // this is a badly named,                                 // but legal, method } 

The first thing to notice is that constructors look an awful lot like methods. A key difference is that a constructor can't ever, ever, ever, have a return type ever! Constructor declarations can however have all of the normal access modifiers, and they can take arguments (including var-args), just like methods. The other BIG RULE, to understand about constructors is that they must have the same name as the class in which they are declared. Constructors can't be marked static (they are after all associated with object instantiation), they can't be marked final or abstract (because they can't be overridden). Here are some legal and illegal constructor declarations:

 class Foo2 {   // legal constructors   Foo2() { }   private Foo2(byte b) { }   Foo2(int x) { }   Foo2(int x, int... y) { }   // illegal constructors   void Foo2() { }             // it's a method, not a constructor   Foo() { }                   // not a method or a constructor   Foo2(short s);              // looks like an abstract method   static Foo2(float f) { }    // can't be static   final Foo2 (long x) { }     // can't be final   abstract Foo2 (char c) { }  // can't be abstract   Foo2 (int... x, int t) { }  // bad var-arg syntax  } 

Variable Declarations

There are two types of variables in Java:

  • Primitives A primitive can be one of eight types: char, boolean, byte, short, int, long, double, or float. Once a primitive has been declared, its primitive type can never change, although in most cases its value can change.

  • Reference variables A reference variable is used to refer to (or access) an object. A reference variable is declared to be of a specific type and that type can never be changed. A reference variable can be used to refer to any object of the declared type, or of a subtype of the declared type (a compatible type). We'll talk a lot more about using a reference variable to refer to a subtype in Chapter 2, when we discuss polymorphism.

Declaring Primitives and Primitive Ranges

Primitive variables can be declared as class variables (statics), instance variables, method parameters, or local variables. You can declare one or more primitives, of the same primitive type, in a single line. In Chapter 3 we will discuss the various ways in which they can be initialized, but for now we'll leave you with a few examples of primitive variable declarations:

 byte b; boolean myBooleanPrimitive; int x, y, z;                   // declare three int primitives 

On previous versions of the exam you needed to know how to calculate ranges for all the Java primitives. For the current exam, you can skip some of that detail, but it's still important to understand that for the integer types the sequence from small to big is byte, short, int, long, and that doubles are bigger than floats.

You will also need to know that the number types (both integer and floating point types) are all signed, and how that affects their ranges. First, let's review the concepts.

All six number types in Java are made up of a certain number of 8-bit bytes, and are signed, meaning they can be negative or positive. The leftmost bit (the most significant digit) is used to represent the sign, where a 1 means negative and 0 means positive, as shown in Figure 1-6. The rest of the bits represent the value, using two's complement notation.

image from book
Figure 1-6: The Sign bit for a byte

Table 1-3 shows the primitive types with their sizes and ranges. Figure 1-6 shows that with a byte, for example, there are 256 possible numbers (or 28). Half of these are negative, and half -1 are positive. The positive range is one less than the negative range because the number zero is stored as a positive binary number. We use the formula -2(bits-1) to calculate the negative range, and we use 2(bits-1)-l for the positive range. Again, if you know the first two columns of this table, you'll be in good shape for the exam.

Table 1-3: Ranges of Numeric Primitives

Type

Bits

Bytes

Minimum Range

Maximum Range

byte

8

1

-27

27-1

short

16

2

-215

215-1

int

32

4

-231

231-1

long

64

8

-263

263-1

float

32

4

n/a

n/a

double

64

8

n/a

n/a

The range for floating-point numbers is complicated to determine, but luckily you don't need to know these for the exam (although you are expected to know that a double holds 64 bits and a float 32).

For boolean types there is not a range; a boolean can be only true or false. If someone asks you for the bit depth of a boolean, look them straight in the eye and say, "That's virtual-machine dependent." They'll be impressed.

The char type (a character) contains a single, 16-bit Unicode character. Although the extended ASCII set known as ISO Latin-1 needs only 8 bits (256 different characters), a larger range is needed to represent characters found in languages other than English. Unicode characters are actually represented by unsigned 16-bit integers, which means 216 possible values, ranging from 0 to 65535 (216)-1, You'll learn in Chapter 3 that because a char is really an integer type, it can be assigned to any number type large enough to hold 65535 (which means anything larger than a short. Although both chars and shorts are 16-bit types, remember that a short uses 1 bit to represent the sign, so fewer positive numbers are acceptable in a short).

Declaring Reference Variables

Reference variables can be declared as static variables, instance variables, method parameters, or local variables. You can declare one or more reference variables, of the same type, in a single line. In Chapter 3 we will discuss the various ways in which they can be initialized, but for now we'll leave you with a few examples of reference variable declarations:

 Object o; Dog myNewDogReferenceVariable; String s1, s2, s3;               // declare three String vars. 

Instance Variables

Instance variables are defined inside the class, but outside of any method, and are only initialized when the class is instantiated. Instance variables are the fields that belong to each unique object. For example, the following code defines fields (instance variables) for the name, title, and manager for employee objects:

 class Employee {   // define fields (instance variables) for employee instances   private String name;   private String title,   private String manager;   // other code goes here including access methods for private   // fields } 

The preceding Employee class says that each employee instance will know its own name, title, and manager. In other words, each instance can have its own unique values for those three fields. If you see the term "field," "instance variable," "property," or "attribute," they mean virtually the same thing. (There actually are subtle but occasionally important distinctions between the terms, but those distinctions aren't used on the exam.)

For the exam, you need to know that instance variables

  • Can use any of the four access levels (which means they can be marked with any of the three access modifiers)

  • Can be marked final

  • Can be marked transient

  • Cannot be marked abstract

  • Cannot be marked synchronized

  • Cannot be marked strictfp

  • Cannot be marked native

  • Cannot be marked static, because then they'd become class variables.

We've already covered the effects of applying access control to instance variables (it works the same way as it does for member methods). A little later in this chapter we'll look at what it means to apply the final or transient modifier to an instance variable. First, though, we'll take a quick look at the difference between instance and local variables. Figure 1-7 compares the way in which modifiers can be applied to methods vs. variables.

Local Variables

Variables (non-local)

Methods

final

final public protected private static transient volatile

final public protected private static

abstract synchronized strictfp native


Figure 1-7: Comparison of modifiers on variables vs. methods

Local (Automatic/Stack/Method) Variables

Local variables are variables declared within a method. That means the variable is not just initialized within the method, but also declared within the method. Just as the local variable starts its life inside the method, it's also destroyed when the method has completed. Local variables are always on the stack, not the heap. (We'll talk more about the stack and the heap in Chapter 3). Although the value of the variable might be passed into, say, another method that then stores the value in an instance variable, the variable itself lives only within the scope of the method.

Just don't forget that while the local variable is on the stack, if the variable is an object reference, the object itself will still be created on the heap. There is no such thing as a stack object, only a stack variable. You'll often hear programmers use the phrase, "local object," but what they really mean is, "locally declared reference variable." So if you hear a programmer use that expression, you'll know that he's just too lazy to phrase it in a technically precise way. You can tell him we said that—unless he knows where we live.

Local variable declarations can't use most of the modifiers that can be applied to instance variables, such as public (or the other access modifiers), transient, volatile, abstract, or static, but as we saw earlier, local variables can be marked final. And as you'll learn in Chapter 3 (but here's a preview), before a local variable can be used, it must be initialized with a value. For instance:

 Class TestServer {   public void logIn() {     int count = 10;   } } 

Typically, you'll initialize a local variable in the same line in which you declare it, although you might still need to reinitialize it later in the method. The key is to remember that a local variable must be initialized before you try to use it. The compiler will reject any code that tries to use a local variable that hasn't been assigned a value, because—unlike instance variables—local variables don't get default values.

A local variable can't be referenced in any code outside the method in which it's declared. In the preceding code example, it would be impossible to refer to the variable count anywhere else in the class except within the scope of the method logIn(). Again, that's not to say that the value of count can't be passed out of the method to take on a new life. But the variable holding that value, count, can't be accessed once the method is complete, as the following illegal code demonstrates:

 class TestServer {   public void logIn() {     int count = 10;   }   public void doSomething(int i) {     count = i;  // Won't compile! Can't access count outside                 // method login()   } } 

It is possible to declare a local variable with the same name as an instance variable. It's known as shadowing, as the following code demonstrates:

 class TestServer {    int count = 9;  // Declare an instance variable named count    public void logIn() {       int count = 10;  // Declare a local variable named count       System.out.println("local variable count is " + count);    }    public void count () {       System.out.println("instance variable count is " + count);    }    public static void main(String[] args)      new TestServer().logIn();      new TestServer().count();    } } 

The preceding code produces the following output:

 local variable count is 10 instance variable count is 9 

Why on earth (or the planet of your choice) would you want to do that? Normally, you won't. But one of the more common reasons is to name a parameter with the same name as the instance variable to which the parameter will be assigned.

The following (wrong) code is trying to set an instance variable's value using a parameter:

 class Foo {    int size = 27;    public void setSize(int size) {       size = size; // ??? which size equals which size???    } } 

So you've decided that—for overall readability—you want to give the parameter the same name as the instance variable its value is destined for, but how do you resolve the naming collision? Use the keyword this. The keyword this always, always, always refers to the object currently running. The following code shows this in action:

 class Foo {  int size = 27;  public void setSize(int size) {     this.size = size;  // this.size means the current object's                        // instance variable, size. The size                        // on the right is the parameter     }   } 

Array Declarations

In Java, arrays are objects that store multiple variables of the same type, or variables that are all subclasses of the same type. Arrays can hold either primitives or object references, but the array itself will always be an object on the heap, even if the array is declared to hold primitive elements. In other words, there is no such thing as a primitive array, but you can make an array of primitives.

For the exam, you need to know three things:

  • How to make an array reference variable (declare)

  • How to make an array object (construct)

  • How to populate the array with elements (initialize)

For this objective, you only need to know how to declare an array, we'll cover constructing and initializing arrays in Chapter 3.

On the Job 

Arrays are efficient, but many times you'll want to use one of the Collection types from java.util (including HashMap, ArrayList, and TreeSet). Collection classes offer more flexible ways to access an object (for insertion, deletion, reading, and so on) and unlike arrays, can expand or contract dynamically as you add or remove elements. There's a Collection type for a wide range of needs. Do you need a fast sort? A group of objects with no duplicates? A way to access a name-value pair? Chapter 7 covers them in more detail.

Arrays are declared by stating the type of elements the array will hold (an object or a primitive), followed by square brackets to either side of the identifier.

Declaring an Array of Primitives

 int[]  key;  // Square brackets before name (recommended) int key  []; // Square brackets after name (legal but less              // readable) 

Declaring an Array of Object References

 Thread[] threads;   // Recommended Thread threads  []; // Legal but less readable 

On the Job 

When declaring an array reference, you should always put the array brackets immediately after the declared type, rather than after the identifier (variable name). That way, anyone reading the code can easily tell that, for example, key is a reference to an int array object, and not an int primitive.

We can also declare multidimensional arrays, which are in fact arrays of arrays. This can be done in the following manner:

 String[][][] occupantName; String[] ManagerName []; 

The first example is a three-dimensional array (an array of arrays of arrays) and the second is a two-dimensional array. Notice in the second example we have one square bracket before the variable name and one after. This is perfectly legal to the compiler, proving once again that just because it's legal doesn't mean it's right.

image from book
Exam Watch

It is never legal to include the size of the array in your declaration. Yes, we know you can do that in some other languages, which is why you might see a question or two that include code similar to the following:

 int[5] scores; 

The preceding code won't compile. Remember, the JVM doesn't allocate space until you actually instantiate the array object. That's when size matters.

image from book

In Chapter 3, we'll spend a lot of time discussing arrays, how to initialize and use them, and how to deal with multi-dimensional arrays stay tuned!

Final Variables

Declaring a variable with the final keyword makes it impossible to reinitialize that variable once it has been initialized with an explicit value (notice we said explicit rather than default). For primitives, this means that once the variable is assigned a value, the value can't be altered. For example, if you assign 10 to the int variable x, then x is going to stay 10, forever. So that's straightforward for primitives, but what does it mean to have a final object reference variable? A reference variable marked final can't ever be reassigned to refer to a different object. The data within the object can be modified, but the reference variable cannot be changed. In other words, a final reference still allows you to modify the state of the object it refers to, but you can't modify the reference variable to make it refer to a different object. Burn this in: there are no final objects, only final references. We'll explain this in more detail in Chapter 3.

We've now covered how the final modifier can be applied to classes, methods, and variables. Figure 1-8 highlights the key points and differences of the various applications of final.

image from book
Figure 1-8: Effect of final on variables, methods, and classes

Transient Variables

If you mark an instance variable as transient, you're telling the JVM to skip (ignore) this variable when you attempt to serialize the object containing it. Serialization is one of the coolest features of Java; it lets you save (sometimes called "flatten") an object by writing its state (in other words, the value of its instance variables) to a special type of I/O stream. With serialization you can save an object to a file, or even ship it over a wire for reinflating (descrializing) at the other end, in another JVM. Serialization has been added to the exam as of Java 5, and we'll cover it in great detail in Chapter 6.

Volatile Variables

The volatile modifier tells the JVM that a thread accessing the variable must always reconcile its own private copy of the variable with the master copy in memory. Say what? Don't worry about it. For the exam, all you need to know about volatile is that, as with transient, it can be applied only to instance variables. Make no mistake, the idea of multiple threads accessing an instance variable is scary stuff, and very important for any Java programmer to understand. But as you'll see in Chapter 11, you'll probably use synchronization, rather than the volatile modifier, to make your data thread-safe.

On the Job 

The volatile modifier may also be applied to project managers :)

Static Variables and Methods

The static modifier is used to create variables and methods that will exist independently of any instances created for the class. In other words, static members exist before you ever make a new instance of a class, and there will be only one copy of the static member regardless of the number of instances of that class. In other words, all instances of a given class share the same value for any given static variable. We'll cover static members in great detail in the next chapter.

Things you can mark as static:

  • Methods

  • Variables

  • A class nested within another class, but not within a method (more on this in Chapter 8)

  • Initialization blocks

Things you can't mark as static:

  • Constructors (makes no sense; a constructor is used only to create instances)

  • Classes (unless they are nested)

  • Interfaces

  • Method local inner classes (we'll explore this in Chapter 8)

  • Inner class methods and instance variables

  • Local variables

Declaring Enums

As of 5.0, Java lets you restrict a variable to having one of only a few pre-defined values—in other words, one value from an enumerated list. (The items in the enumerated list are called, surprisingly, enums.)

Using enums can help reduce the bugs in your code. For instance, in your coffee shop application you might want to restrict your size selections to BIG, HUGE, and OVERWHELMING. If you let an order for a LARGE or a GRANDE slip in, it might cause an error. Enums to the rescue. With the following simple declaration, you can guarantee that the compiler will stop you from assigning anything to a CoffeeSize except BIG, HUGE, or OVERWHELMING:

 enum CoffeeSize { BIG, HUGE, OVERWHELMING }; 

From then on, the only way to get a CoffeeSize will be with a statement something like this:

 CoffeeSize cs = CoffeeSize. BIG; 

It's not required that enum constants be in all caps, but borrowing from the Sun code convention that constants are named in caps, it's a good idea.

The basic components of an enum are its constants (i.e., BIG, HUGE, and OVERWHELMING), although in a minute you'll see that there can be a lot more to an enum. Enums can be declared as their own separate class, or as a class member, however they must not be declared within a method!

Declaring an enum outside a class:

 enum CoffeeSize { BIG, HUGE, OVERWHELMING }  // this cannot be                                          // private or protected class Coffee {    CoffeeSize size; } public class CoffeeTest1 {    public static void main(String[] args) {       Coffee drink = new Coffee();       drink.size = CoffeeSize.BIG;        // enum outside class    } } 

The preceding code can be part of a single file. (Remember, the file must be named CoffeeTest1.Java because that's the name of the public class in the file.) The key point to remember is that the enum can be declared with only the public or default modifier, just like a non-inner class. Here's an example of declaring an enum inside a class:

 class Coffee2 {   enum CoffeeSize  {BIG, HUGE, OVERWHELMING  }   CoffeeSize size; } public class  CoffeeTest2 {   public static void main (String[] args)  {     Coffee2 drink = new Coffee2();     drink.size = Coffee2.CoffeeSize.BIG;   // enclosing class                                            // name required   } } 

The key points to take away from these examples are that enums can be declared as their own class, or enclosed in another class, and that the syntax for accessing an enum's members depends on where the enum was declared.

The following is NOT legal:

 public class CoffeeTestl {   public static void main(String[] args) {     enum CoffeeSize { BIG, HUGE, OVERWHELMING } // WRONG! Cannot                                                 // declare enums                                                 // in methods     Coffee drink = new Coffee();     drink.size = CoffeeSize.BIG;   } } 

To make it more confusing for you, the Java language designers made it optional to put a semicolon at the end of the enum declaration:

 public class CoffeeTest1  {   enum CoffeeSize { BIG, HUGE, OVERWHELMING }; // <--semicolon                                              // is optional here   public static void main(String[] args) {     Coffee drink = new Coffee();     drink.size = CoffeeSize.BIG;   } } 

So what gets created when you make an enum? The most important thing to remember is that enums are not Strings or ints! Each of the enumerated CoffeeSize types are actually instances of CoffeeSize. In other words, BIG is of type CoffeeSize. Think of an enum as a kind of class, that looks something (but not exactly) like this:

 // conceptual example of how you can think // about enums class CoffeeSize {     public static final CoffeeSize BIG =                              new CoffeeSize("BIG", 0);     public static final CoffeeSize HUGE =                              new CoffeeSize("HUGE", 1);     public static final CoffeeSize OVERWHELMING =                              new CoffeeSize("OVERWHELMING", 2);     public CoffeeSize(String enumName, int index) {            // stuff here         }        public static void main(String[] args) {          System.out.println(CoffeeSize.BIG);        }     } 

Notice how each of the enumerated values, BIG, HUGE, and OVERWHELMING, are instances of type CoffeeSize. They're represented as static and final, which in the Java world, is thought of as a constant. Also notice that each enum value knows its index or positionin other words, the order in which enum values are declared matters. You can think of the CoffeeSize enums as existing in an array of type CoffeeSize, and as you'll see in a later chapter, you can iterate through the values of an enum by invoking the values() method on any enum type. (Don't worry about that in this chapter.)

Declaring Constructors, Methods, and Variables in an Enum

Because an enum really is a special kind of class, you can do more than just list the enumerated constant values. You can add constructors, instance variables, methods, and something really strange known as a constant specific class body. To understand why you might need more in your enum, think about this scenario: imagine you want to know the actual size, in ounces, that map to each of the three CoffeeSize constants. For example, you want to know that BIG is 8 ounces, HUGE is 10 ounces, and OVERWHELMING is a whopping 16 ounces.

You could make some kind of a lookup table, using some other data structure, but that would be a poor design and hard to maintain. The simplest way is to treat your enum values (BIG, HUGE, and OVERWHELMING), as objects that can each have their own instance variables. Then you can assign those values at the time the enums are initialized, by passing a value to the enum constructor. This takes a little explaining, but first look at the following code:

 enum CoffeeSize {      BIG(8), HUGE(10), OVERWHELMING(16);      // the arguments after the enum value are "passed"      // as values to the constructor      CoffeeSize(int ounces) {        this.ounces = ounces; // assign the value to                              // an instance variable      }     private int ounces;      // an instance variable each enum                              // value has     public int getOunces() {       return ounces;     } } class Coffee {    CoffeeSize size;    // each instance of Coffee has-a                        // CoffeeSize enum    public static void main(String[] args) {       Coffee drink1 = new Coffee();       drinkl.size = CoffeeSize.BIG;       Coffee drink2 = new Coffee();       drink2.size = CoffeeSize. OVERWHELMING;       System.out.println(drinkl.size.getOunces()); // prints 8       System.out.println(drink2.size. getOunces()); // prints 16    } } 

The key points to remember about enum constructors are

  • You can NEVER invoke an enum constructor directly. The enum constructor is invoked automatically, with the arguments you define after the constant value. For example, BIG(8) invokes the CoffeeSize constructor that takes an int, passing the int literal 8 to the constructor. (Behind the scenes, of course, you can imagine that BIG is also passed to the constructor, but we don't have to know—or care—about the details.)

  • You can define more than one argument to the constructor, and you can overload the enum constructors, just as you can overload a normal class constructor. We discuss constructors in much more detail in Chapter 2. To initialize a CoffeeType with both the number of ounces and, say, a lid type, you'd pass two arguments to the constructor as BIG(8, "A"), which means you have a constructor in CoffeeSize that takes both an int and a string.

And finally, you can define something really strange in an enum that looks like an anonymous inner class (which we talk about in Chapter 8). It's known as a constant specific class body, and you use it when you need a particular constant to override a method defined in the enum.

Imagine this scenario: you want enums to have two methods—one for ounces and one for lid code (a String). Now imagine that most coffee sizes use the same lid code, "B", but the OVERWHELMING size uses type "A". You can define a getLidcode() method in the CoffeeSize enum that returns "B", but then you need a way to override it for OVERWHELMING. You don't want to do some hard-to-maintain if/then code in the getLidCode() method, so the best approach might be to somehow have the OVERWHELMING constant override the getLidCode() method.

This looks strange, but you need to understand the basic declaration rules:

 enum CoffeeSize {      BIG(8),      HUGE(10),      OVERWHELMING(16)  {    // start a code block that defines                             // the "body" for this constant        public String getLidCode() {   // override the method                                       // defined in CoffeeSize          return "A";        }      };    // <-- the semicolon is REQUIRED when you have a body      CoffeeSize(int ounces) {        this.ounces = ounces;      }     private int ounces;     public int getOunces() {       return ounces;     }     public String getLidCode() {  // this method is overridden                                   // by the OVERWHELMING constant       return "B";   // the default value we want to return for                     // CoffeeSize constants     } } 




SCJP Sun Certified Programmer for Java 5 Study Guide Exam 310-055
SCJP Sun Certified Programmer for Java 5 Study Guide (Exam 310-055) (Certification Press)
ISBN: 0072253606
EAN: 2147483647
Year: 2006
Pages: 131

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net