In this article, we will look in depth at Java Class Design. In this article we will review key elements of class design such as access modifiers, overriding, overloading, the use of the instanceof operator, virtual method invocation and how to override methods from the Java Object class. We will end by looking at the use of package and import statements. This is first one of a series of articles to help you prepare for the java programming exam. If you have not completed the Java Associate Exam and want to do the Java Programming Exam, either you will need to first complete the Java Associate Exam or to read or be knowledgeable of the topics of the articles of the Java Associate series to help you prepare for the Java Programming Exam.

Java Class and Class Design

A class is a structure that is used to define the data and the methods that work on that data. Programs written in Java have all program data wrapped in a class, whether it is a class you write or a class that you use from the Java platform API libraries. In object-oriented terms, in class design we can say that a Car is an instance of the class of objects known as Cars. A java class is the blueprint from which individual objects are created. The following Car class is one possible implementation of a Car:

Java Code: Declaration of Car Class
class Car {
    int speed = 0;
    int gear = 1;

    void changeGear(int newValue) {
         gear = newValue;
    }

    void speedUp(int increment) {
         speed = speed + increment;   
    }

    void applyBrakes(int decrement) {
         speed = speed - decrement;
    }

    void printStates() {
         System.out.println(" speed:" + 
             speed + " gear:" + gear);
    }
}
The syntax of the Java programming language may look new to some of you, but the design of this class is based on your conception of Car objects. The fields speed, and gear represent the object's state, and its methods (changeGear, speedUp etc.) define its interaction with the outside world.

Note that the Car class does not contain a main method because it's not a complete application. It is just the blueprint for Cars that can be used in an application. The responsibility for creating and using new instances of Car objects belongs to some other class in your application. Here's a CarDemo class that creates two separate Car objects and invokes their methods:

Java Code: Car Demo using Car Class
class CarDemo {
    public static void main(String[] args) {

        // Create two different 
        // Car objects
        Car car1 = new Car();
        Car car2 = new Car();

        // Invoke methods on 
        // those objects
        car1.speedUp(10);
        car1.changeGear(2);
        car1.printStates();

        car2.speedUp(10);
        car2.changeGear(2);
        car2.speedUp(10);
        car2.changeGear(3);
        car2.printStates();
    }
}
The output of this test prints the ending speed, and gear for the two Cars:

XML Code: Output from CarDemo
speed:50 gear:2
speed:70 gear:3
Access Modifiers

When you access a class, the code from one class, in this case class A has access to another class (i.e. class B). By consequence class A can do one of three things:
  • Create an instance of class B
  • Extend class B so that this new class becomes a subclass of class B
  • Access specific methods and variables within class B, dependent on the access control of those methods and variables.

Access is concerned with the visibility of a particular class. So if class A canít see class B, then the access level of the methods and variables within class B are not important since class A doesnít have any way to access those methods and variables.

Default Access for a Java Class

A java class with default access has doesnít have any modifier preceding it in the declaration. In other words, itís the access control you get when you donít type a modifier in the class declaration. Think of default access as package-level access, because a class with default access can only be seen by classes within the same package. So if class A and class B are in different packages, and class A has default access, class B wonít be able to create an instance of class A, or even declare a variable or return type of class A. In fact, class B has to pretend that class A doesnít even exist, or the compiler will complain. The listing below provides an example:

Java Code: Beverage Class
package refreshment;
class Beverage {

}
Now look at the second source file:

Java Code: Extension of Beverage Class
package demo.stuff;
import refreshment.Beverage;
class Tea extends Beverage {

}
This listing shows the superclass Beverage in a different package from the subclass Tea. The import statement at the top of the Tea file is used to import the Beverage class. The Beverage file compiles fine, but the listing below shows if you try to compile the Tea file:

XML Code: Output from Compiling the Class
>javac Tea.java
Tea.java:1: Can't access class refreshment.Beverage. Class or
interface must be public, in same package, or an accessible member 
class.
import refreshment.Beverage;
..
Tea wonít compile because its superclass, Beverage, has default access and is in a different package. You can do one of two things to make this work. You could put both classes in the same package, or declare Beverage as public, as the next section describes.

Public Access with a Java Class
A java class declaration with the public keyword gives all classes from all packages access to the public class. In other words, all classes in the Java have access to a public class. Note that if a public class youíre using is in a different package from the class youíre writing, youíll need to import the public class. In the listing below, as we may not want to place the subclass in the same package as the superclass, we need to add the keyword public in front of the superclass Beverage declaration:

Java Code: Output from Compiling the Class
package refreshment;
public class Beverage {

}
This modifies the Beverage class to make it visible to all classes in all packages. The class can now be instantiated from all other classes, and any class is now free to subclass or extend it unless the class is also marked with the non-access modifier final.

Other (Non-access) Class Modifiers

You can modify a class declaration using the keyword final, abstract, or strictfp in your class designs. These modifiers combined with whatever access control is on the class, so you could, for example, declare a class as both public and final. But you canít always mix non-abstract modifiers. Youíre free to use strictfp in combination with abstract or final, but you must never, ever, ever mark a class as both final and abstract. Youíll see why in the next two sections.

You only need to know that strictfp is a keyword and can be used to modify a class or a method, but never a variable. Marking a class as strictfp means that any method code in the class will conform to the IEEE754 standard rules for floating points. Without that modifier, floating points used in the methods might behave in a platform-dependent way. If you donít need to declare a class as strictfp, you can still get strictfp behavior on a method-by-method basis, by declaring a method as strictfp.

Final Classes

When used in a class declaration, the final keyword means the class canít be subclassed. In other words, no other class can ever extend (inherit from) a final class, and any attempts to do so will give you a compiler error. You should create a final class only if you need an absolute guarantee that none of the methods in that class will ever be overridden. If youíre deeply dependent on the implementations of certain methods, then using final gives you the security that nobody can change the implementation.

Youíll notice many classes in the Java core libraries are final. For example, the String class cannot be subclassed. So use final for safety, but only when youíre certain that your final class has indeed said all that ever needs to be said in its methods. Marking a class final means, in essence, your class canít ever be improved upon, or even specialized, by another programmer.

A benefit of having non-final classes is when you find a problem with a method in a class youíre using, but you donít have the source code. So you canít modify the source to improve the method, but you can extend the class and override the method in your new subclass, and substitute the subclass everywhere the original superclass is expected. If the class is final, you cannot modify it. For example, if we modify our Beverage class by placing the keyword final in the declaration:

Java Code: Beverage Class with Final Modifier
package refreshment;
  public final class Beverage{
     public void importantMethod() {
  } 
}
Now, if we try to compile the Tea subclass:

Java Code: Beverage Class with Final Modifier
package demo.example;
     import refreshment.Beverage;
     class Tea extends Beverage {
}
We get the following error:
XML Code: Beverage Class with Final Modifier
>javac Tea.java
                Tea.java:3: Can't subclass final classes: class
                refreshment.Beverage class Tea extends Beverage{
                1 error
Override Methods

Anytime there is a java class that inherits a method from a superclass, there is an opportunity to override the method unless the method is marked final. The key benefit of overriding is the ability to define behavior thatís specific to a particular subclass type. The listing below shows a Horse subclass of Animal overriding the Animal version of the eat() method:

Java Code: Base Class Animal and extended class Horse
public class Animal {
   public void eat() {
      System.out.println("Generic Animal Eating");
   }
}

class Horse extends Animal {
    public void eat() {
       System.out.println("Horse eating hay, oats, and horse treats");
   }
}
For abstract methods in your class design, you need to inherit from a superclass and implement the method(s) in the subclass unless the subclass is also abstract. Abstract methods are said to be implemented by the concrete subclass, but this is virtually the same as saying that the concrete subclass overrides the abstract methods of the superclass. Remember that abstract methods as methods youíre forced to override. In the listing above, the Animal class creator could have decided that for the purposes of polymorphism, all Animal subtypes should have an eat() method defined in a unique, specific way. Polymorphically, when someone has an Animal reference that refers not to an Animal instance, but to an Animal subclass instance, the caller should be able to invoke eat() on the Animal reference, but the actual runtime object (in our case, a Horse instance) will run its own specific eat() method. By declaring the eat() method as abstract, the Animal programmer has declared to all subclass developers, that it doesnít make any sense for your new subtype to use a generic eat() method, so you need to define your own implementation of the eat() method. The listing below shows an example of using polymorphism:

Java Code: Example of Polymorphism
public class TestAnimals {
      public static void main (String [] args) {
         Animal a = new Animal();
         Animal b = new Horse();  //Animal ref, but a Horse object
         a.eat(); // Runs the Animal version of eat()
         b.eat(); // Runs the Horse version of eat()
     } 
}

class Animal {
      public void eat() {
         System.out.println("Generic Animal Eating Generically");
      }
   }

class Horse extends Animal {
      public void eat() {
          System.out.println("Horse eating hay, oats, and horse treats");
     }
     public void buck() { }
}
In the listing above, the test class uses an Animal reference to invoke a method on a Horse object. The compiler only allows methods in class Animal to be invoked when using a reference to an Animal. The following would not be legal given the preceding code:

Java Code: Illegal Code
Animal c = new Horse();
c.buck();  // Can't invoke buck();
// Animal class doesn't have that method
The compiler looks only at the reference type, not the instance type. Polymorphism allows you to use a more abstract supertype including an interface reference to refer to one of its subtypes as well as interface implementations.

The overriding method cannot have a more restrictive access modifier than the method being overridden. So for example, you cannot ride override a method marked public and make it protected. If a subclass were allowed to change the access modifier on the overriding method, then suddenly at runtime when the JVM invokes the base objectís (i.e. Horse) version of the method rather than the reference typeís (i.e. Animal) version the program would abort. If the listing is modified, the polymorphic example would look like the following:

Java Code: Illegal Polymorphic Example
public class TestAnimals {
    public static void main (String [] args) {
        Animal a = new Animal();
        Animal b = new Horse();  //Animal ref, but a Horse object
        a.eat(); // Runs the Animal version of eat()
        b.eat(); // Runs the Horse version of eat()
     }
}
   
class Animal {
    public void eat() {
        System.out.println("Generic Animal Eating Generically");
     }
  }
  
 class Horse extends Animal {
     private void eat() {
        System.out.println("Horse eating hay, oats,
                             and horse treats");
      } 
  }
When you attempt to compile this code, the output would be the following at runtime:

XML Code: Output from Illegal Polymorphic Example
Animal b = new Horse();  
// Animal ref, but a Horse
// object , so far so good
b.eat();                 
// Meltdown!
The variable b is of type Animal. It has a public eat() method. Recall that at runtime, Java uses virtual method invocation to select dynamically the actual version of the method that will run, based on the actual instance. An Animal reference can always refer to a Horse instance, because Horse IS-A(n) Animal. What makes that superclass reference to a subclass instance possible is that the subclass is guaranteed to be able to do everything the superclass can do. Whether the Horse instance overrides the inherited methods of Animal or simply inherits them, anyone with an Animal reference to a Horse instance is free to call all accessible Animal methods. For that reason, an overriding method must fulfill the contract of the superclass. The rules for overriding a method are as follows:
  • The argument list must exactly match that of the overridden method.
The return type must exactly match that of the overridden method.
The access level must not be more restrictive than that of the overridden method. s The access level can be less restrictive than that of the overridden method.
  • The overriding method must not throw new or broader checked exceptions than those declared by the overridden method. For example, a method that declares a FileNotFoundException cannot be overridden by a method that declares a SQLException, Exception, or any other non-runtime exception unless itís a subclass of FileNotFoundException.
  • The overriding method can throw narrower or fewer exceptions. Just because an overridden method ďtakes risksĒ doesnít mean that the overriding subclassí exception takes the same risks. Bottom line: An overriding method doesnít have to declare any exceptions that it will never throw, regardless of what the overridden method declares.
  • You cannot override a method marked final.
  • If a method canít be inherited, you cannot override it. 
For example, the following code is not legal:

    Java Code: Legal Polymorphic Example
    public class TestAnimals {
         
       public static void main (String [] args) {
            
          Horse h =  new Horse();
            
          h.eat(); // Not legal because Horse didn't inherit eat() }
      
       }
      
       class Animal {
            
          private void eat() {
                 
             System.out.println("Generic Animal Eating Generically");
            
          }
      
       }
      
       class Horse extends Animal { }
    }


Invoking a Superclass Version of an Overridden Method

If you want to take advantage of some of the code in the superclass version of a method, yet still override it to provide some additional specific behavior. Itís like saying, ďRun the superclass version of the method, then come back down here and finish with my subclass additional method code.Ē (Note that thereís no requirement that the superclass version run before the subclass code.) Itís easy to do in code using the keyword super as follows:
Java Code: Example of Superclass
public class Animal {
	public void eat() { }
		public void printYourself() {
                      	// Useful printing code goes here
		}
	}
	class Horse extends Animal {
		public void printYourself() {
                      	// Take advantage of Animal code, then add some more
			super.printYourself();  // Invoke the superclass
			// (Animal) code
			// Then come back and do
			// additional Horse-specific
			// print work here
		} 
	}
Examples of Legal and Illegal Method Overrides

Letís take a look at overriding the eat() method of Animal:
Java Code: Overridding the eat() method
public class Animal {
		public void eat() { }
	}
The table below shows examples of illegal overrides of the Animal eat() method, given
the preceding version of the Animal class.

Tutorial:Review of Java Class Design for the Java Programming Exam I-d1-tblillegaloverrides.jpg
Figure: Illegal Overrides

Overload Constructors and Other Methods

Overloaded methods allow you reuse the same method name in a class design, but with different arguments and optionally, a different return type. Overloading a method often means your code takes on the burden of coping with different argument types rather than forcing the caller to do conversions prior to invoking your method. The rules are simple:
  • Overloaded methods must change the argument list.
  • Overloaded methods can change the return type.
  • Overloaded methods can change the access modifier.
  • Overloaded methods can declare new or broader checked exceptions.
  • A method can be overloaded in the same class or in a subclass.


Legal Overloads

If we overload the following method:

Java Code: Method to be Overridden
public void changeSize(int size, String name, float pattern) { }
The following methods are legal overloads of the changeSize() method:

Java Code: Methods that are legal Overloads of changeSize
public void changeSize(int size, String name) { }
  public int changeSize(int size, float pattern) { }
  public void changeSize(float pattern, String name)
                         throws IOException { }
public class Foo {
   public void doStuff(int y, String s) { }
   public void moreThings(int x) { }
}
class Bar extends Foo {
   public void doStuff(int y, float s) throws IOException { }
}
Invoking Overloaded Methods

When a method is invoked, more than one method of the same name might exist for the object type youíre invoking a method on. For example, the Horse class could have three methods with the same name but with different argument lists, which means the method is overloaded.

The way that the runtime engine engine determines which method to invoke is based on the arguments. If you invoke the method with a String argument, the overloaded version that takes a String is called. If you invoke a method of the same name but pass it a float, the overloaded version that takes a float will run. If you invoke the method of the same name but pass it a Foo object, and there isnít an overloaded version that takes a Foo, then the compiler will complain that it canít find a match. The listing below shows the invocation of overloaded methods:

Java Code: Methods that are legal Overloads of changeSize
class Adder {
	public int addThem(int x, int y) {
		return x + y;
	}

	// Overload the addThem method to add doubles instead of ints
	public double addThem(double x, double y) {
		return x + y; 
	}
  }
  // From another class, invoke the addThem() method
  public class TestAdder {
  	public static void main (String [] args) {
		Adder add = new Adder();
   		int b = 27;
   		int c = 3;
   		int result = a.addThem(b,c); // Which addThem is invoked?
   		double doubleResult = a.addThem(22.5,89.36);
   		// Which addThem?
	}
}
In the listing above, the first call to a.addThem(b,c) passes two ints to the method, so the first version of addThem() the overloaded version that takes two int arguments is called. The second call to a.addThem(22.5, 89.36) passes two doubles to the method, so the second version of addThem()óthe overloaded version that takes two double arguments is called.

Invoking overloaded methods that take object references rather than primitives
is a little more interesting. Say you have an overloaded method such that one version takes an Animal and one takes a Horse (subclass of Animal). If you pass a Horse object in the method invocation, youíll invoke the overloaded version that takes a Horse. Or so it looks at first glance:

Java Code: Polymorphism with Horse extension of Animal
class Animal { }
  class Horse extends Animal { }
  class UseAnimals {
	public void doStuff(Animal a) {
		System.out.println("In the Animal version");
	}
	public void doStuff(Horse h) {
		System.out.println("In the Horse version");
	}
	public static void main (String [] args) {
		UseAnimals ua = new UseAnimals();
		Animal animalObj = new Animal();
		Horse horseObj = new Horse();
		ua.doStuff(animalObj);
		 ua.doStuff(horseObj);
	} 
}
The output is what you expect:

XML Code: Output from UseAnimals
in the Animal version
in the Horse version
But what if you use an Animal reference to a Horse object?

Java Code: Animal Reference to Horse object
Animal animalRefToHorse = new Horse();
   ua.doStuff(animalRefToHorse);
Which of the overloaded versions is invoked? You might want to say, ďThe one that takes a Horse, since itís a Horse object at runtime thatís being passed to the method.Ē But thatís not how it works. The preceding code would actually print

XML Code: Output from UseAnimals using Animal reference
in the Animal version
Even though the actual object at runtime is a Horse and not an Animal, the choice of which overloaded method to call is not dynamically decided at runtime. Note that itís the reference type and not the object type determines which overloaded method is invoked. So the overridden method to call is decided at runtime based on object type, but which overloaded version of the method to call is based on the reference type passed at compile time.

Polymorphism in Overloaded and Overridden Methods

Note that it is not polymorphism that determines which overloaded version is called, but polymorphism does come into play when the decision is about which overridden version of a method is called. Sometimes a method is both overloaded and overridden. The listing below shows this for Animal and Horse classes:

Java Code: Overridden and Overloaded Method
public class Animal {
	public void eat() {
		System.out.println("Generic Animal Eating Generically");
	}
 }
 public class Horse extends Animal {
	public void eat() {
		System.out.println("Horse eating hay ");
	}
	public void eat(String s) {
		System.out.println("Horse eating " + s);
	}
}
In the above listing the Horse class has both overloaded and overridden the eat() method. The table below shows which version of the three eat() methods will run depending on how they are invoked.

Tutorial:Review of Java Class Design for the Java Programming Exam I-d1-tbloverloadedoverridden.jpg
Table: Overloaded and Overriden Method Invocations

The next table below summarizes the difference between overloaded and overridden methods.

Tutorial:Review of Java Class Design for the Java Programming Exam I-d1-tbldiffoverloadoverride.jpg
Table: Difference Between Overloaded and Overridden Methods

The Instanceof Operator and Casting

The instanceof operator is used for object reference variables only, and you can use it to check whether an object is of a particular type such as a class or interface type. If the object referred to by the variable on the left side of the operator passes the IS-A test for the class or interface type on the right side, then it will return true. The listing below shows an example of instanceOf operator:

Java Code: Example of instanceOf operator
public static void main (String [] args) {
	String s = new String("foo");
	if (s instanceof String) {
		System.out.print("s is a String");
	}
}
Even if the object being tested is not an actual instantiation of the class type on the right side of the operator, instanceof will still return true if the object being compared is assignment compatible with the type on the right. The listing below demonstrates testing an object using instanceof, to see if itís an instance of one of its superclasses:

Java Code: Example of instanceOf operator for superclass
class A { }
class B extends A { }
public static void main (String [] args) {
    	B b = new B();
	if (b instanceof A) {
		System.out.print("b is an A");
	}
}
The listing below shows that b is an a. The keyword instanceOf allows you to test an object reference against its own class type, or any of its superclasses. This means that any object reference will evaluate to true if you use the instanceof operator against type Object. The listing below demonstrates this:

Java Code: Example of instanceOf against class Object
B b = new B();
  if (b instanceof Object) {
     System.out.print("b is definitely an Object");
 }
The output of this code is the following:
Java Code: Output for instanceOf example
b is definitely an Object


The instanceof operator can be used on interface types as well. This is shown in the listing below:
Java Code: Example of instanceOf against interface
interface Foo { }
class Bar implements Foo { }
class TestBar {
	public static void main (String [] args) {
		Bar b = new Bar()
		if ( b instanceof Bar) {
			System.out.println("b is a Bar");
		}
		if (b instanceof Foo) {
			System.out.println("b is a Foo");
		}
	}
}
The output from executing the TestBar class in the listing above proves that the Bar object referenced by b is both a Bar and a Foo. This is shown below:

XML Code: Output from TestBar
b is a Bar
b is a Foo
Override Methods from Object Class

Everything in Java is an object. Not just an object, but Object with a capital ĎOí. Every exception, every event, every array extends from java.lang.Object. There are a number of methods that can be overridden from the Object.These are shown in the table below:

Tutorial:Review of Java Class Design for the Java Programming Exam I-d1-tblclassobjectmethods.jpg
Table: Methods of Class Object

Letís look at overriding the toString() Method. You override the toString() when you want to be able to read something meaningful about the objects of your class. Code can call toString() on your object when it wants to read useful details about your object. For example, when you pass an object reference to the System.out.println() method, the objectís toString() method is called, and the return of toString() is what you see displayed as follows:

Java Code: Example of HardToRead class
public class HardToRead {
      public static void main (String [] args) {
        HardToRead h = new HardToRead();
        System.out.println(h);
      }
}
Running the HardToRead class gives us the lovely and meaningful,
XML Code: Output of HardToRead class
% java HardToRead
   HardToRead@a47e0
The preceding output is what you get when you donít override the toString() method of class Object. It gives you the class name (at least thatís meaningful) followed by the @ symbol, followed by the unsigned hexadecimal representation of the objectís hashcode.
Seeing this perhaps motivates you to override the toString() method in your classes, for example,

Java Code: Example of invoking overloaded methods
public class JimTest {
      public static void main (String[] args) {
        Jim f = new Jim("GoJimGo", 19);
        System.out.println(f);
      }
}

class Jim {
      int shoeSize;
      String nickName;
      Jim(String nickName, int shoeSize) {
      this.shoeSize = shoeSize;
      this.nickName = nickName;
 }
 
  public String toString() {
      return ("I am a Jim, but you can call me " + nickName +
        ". My shoe size is " + shoeSize);
  }
}
This ought to be a bit more readable:

XML Code: Output of HardToRead class
% java JimTest
 I am a Jim, but you can call me GoJimGo. My shoe size is 19
Overriding equals()

When you need to know if the objects themselves and not the references are equal, use the equals() method. For each class you write, you must decide if it makes sense to consider two different instances equal. For some classes, you might decide that two objects can never be equal.

So letís say you decide to override equals() in your class. It might look something like this:

Java Code: Example of equals test
public class EqualsTest {
     public static void main (String [] args) {
        Moof one = new Moof(8);
        Moof two = new Moof(8);
        if (one.equals(two)) {
          System.out.println("one and two are equal");
        }
      } 
}

class Moof {
     private int moofValue;
     Moof(int val) {
       moofValue = val;
     }
    public int getMoofValue() {
       return moofValue;
     }
    public boolean equals(Object o) {
      if ((o instanceof Moof) && (((Moof)o).getMoofValue()
           == this.moofValue)) {
        return true;
       } else {
         return false;
       }
   } 
}
In the listing above, the main method of EqualsTest, we create two Moof instances, passing the same value of 8 to the Moof constructor. The Moof class uses the constructor argument to assign the value to the moof Value instance variable. Now imagine that youíve decided two Moof objects are the same if their moof Value is identical. So now you override the equals() method and compare the two moof Values. The listing below shows what happens in the equals() method:

Java Code: Internals of equals test
public boolean equals(Object o) {
      if ((o instanceof Moof) && (((Moof)o).getMoofValue()
             == this.moofValue)) {
            return true;
      } else {
      }
}
In the listing above, we first ensure that we observe all the rules of overriding, and declare a valid override of the equals() method inherited from Object. The next line is where we have to do two things in order to make a valid equality comparison. Be sure that the object being tested is of the correct type. It comes in polymorphically as type Object, so you need to do an instanceof test on it. Having two objects of different class types be considered equal is usually not a good idea, but thatís a design issue we wonít go into here. Youíd still have to do the instanceof test just to be sure that you could cast the object argument to the correct type so that you can access its methods or variables in order to actually do the comparison. Remember, if the object doesnít pass the instanceof test, then youíll get a runtime ClassCastException. The listing below shows what happen if you try to do this: 

Java Code: Example of equals test
public boolean equals(Object o) {
         if (((Moof)o).getMoofValue() == this.moofValue){

            // the preceding line compiles, but it's BAD!

            return true;
           } else {

             return false;
           }

}

The (Moof)o cast will fail if o doesnít refer to something that IS-A Moof.
Compare the attributes we care about (in this case, just moofValue). Only the developers can decide what makes two instances equal. In terms of performance youíre going to want to check the fewest number of attributes.

Using Package and Import Statements
Import statements come in two flavors, with a wildcard import or an explicit class import. An import statement is not an include statement. Import statements are little more than a way for you to save keystrokes when youíre typing your code. When you put a class in a package via the package declaration, you give the class a longer name, which is known as the fully qualified name. The fully qualified name of a class,in contrast to the class name, is like talking about the difference between your full name and your first name.

For example, if class Foo is in a package com.acme, the Foo class is still named Foo, but it also has a fully qualified name of com.acme.Foo. The package organization helps prevent name collisions in case other programmers build a class named Foo for example. But if a programmer from goose builds a Foo class, its fully qualified name will be com.goose.Foo or possibly even com.goose.projectx.Foo, while a programmer from Acme gives her Foo class the fully qualified name of com.acme.Foo. Once you put Foo in a package, if you refer to the Foo class in some other code, the compiler needs to know which Foo youíre talking about. If there is more than one Foo floating around, and that even within a single application you might want to use, say, two different Foo classes, you need a way to distinguish between them. The listing below shows what could happen if the class is not clearly identified:

Java Code: Example of ambiguity in class to use
class Bar {
   void doStuff() {
   Foo f = new Foo(); // Here you want the WickedlySmart version } 
   // But how will the    compiler know?
}
To eliminate the confusion, youíre required to do one of two things to help the compiler:
Use an import statement, 

Java Code: Using the import statement
import com.example.Foo;
   class Bar {
	void doStuff() {
	Foo f = new Foo(); // Now the compiler knows which one to use
	} 
}

or use the fully qualified name throughout your code: 

Java Code: The use of fully qualified name
class Bar {
      void doStuff() { 
      com.example.Foo f = new com. example.Foo() 
      // No doubts } 
   }
The best means of The import statement is almost always the way to go. You need to recognize that either option is legal, however. And using both together is legal as well. Itís not a problem, for example, to do the following:

Java Code: Use of fully qualified and import statement
import com.example.Foo; 
// Import class Foo class Bar {
   void doStuff() {
      com.example.Foo f = new com.example.Foo() 
      //OK; not needed
   } 
}
Summary

This covers the first part of class design. In the next article, we will cover advanced class design including abstract classes, static and final classes and subclasses.