Title: access modifiers
Course: CPS 261: Advanced Java Concepts ~ DL & MM MASTER

Access Modifiers:

Summary of Access Levels

Note that the following table only applies if the class is available to the user. In otherwords, if the class isn't marked as public and you are in another package, then you have no access even if the variable or method is labeled as public.

The following chart shows the access level permitted by each specifier.

Specifier | class | subclass | package | world
private | X |  |  |
protected | X | X | X |
public | X | X | X | X
default | X |  | X |

Package Example and Access Levels

dessert package


//: Cookie.java
// Creates a library
package dessert;

public class Cookie {
  public Cookie() {
   System.out.println("Cookie constructor");
  }

// The following call can only be accessed by classes
//  in the same package (dessert)

  void foo() { System.out.println("foo"); }

// The following call can be accessed by classes derived
//  from Cookie

  protected void fooProtected() { System.out.println("fooProtected");}
}
// **********************************************************
 //: DessertCookie.java
// Because we are in the same package as Cookie.java, we CAN access the foo
//   member function.


package dessert;
//Don't need:   import  dessert.*;

public class DessertCookie {
  Cookie theCookie = new Cookie();
  public DessertCookie() {
   System.out.println("DessertCookie constructor");
  }
  public void foo() {
    theCookie.foo(); // CAN access because we are in the same package
  }
}
// ***************************************


Using the dessert package


//: Dinner.java
// Uses the library:  This demonstrates the fact that we can't access
//   "Default" member functions if we aren't in the same package.
//   However, we can access the foo interface indirectly through a public
//   interface made available inside the desert package in the DesertCookie class


import dessert.*;

public class Dinner {
  public Dinner() {
   System.out.println("Dinner constructor");
  }
  public static void main(String[] args) {
    Cookie x = new Cookie();
    DessertCookie y = new DessertCookie();
    MyCookie z = new MyCookie();

    //! x.foo(); // Can't access  - - Only available inside of dessert package
    y.foo(); // CAN access - - indirect access through
                //  public interface inside of dessert package

    z.myfoo();
  }
}
class MyCookie extends Cookie{

    void myfoo()
    {
        // fooProtected is available to a derived class since
        // it's access is protected

        fooProtected();
    }
}






- In the normal directory where you do your Java work, create a directory by the name of dessert and download the following files into it:
Cookie.java - This class lives in the dessert package and contains a package routine foo and a protected routine fooProtected
DessertCookie.java - This class also lives in the dessert package, and creates an instance of the Cookie class. DessertCookie has a public foo routine. Note that because DessertCookie is in the same package(dessert) as Cookie, that the package routine foo in Cookie can be called.
- In the normal directory where you do your Java work, download the following file:
Dinner.java - This class is not in the dessert package, but imports the dessert package. In the Dinner class, you should observe the following:
  - Dinner cannot call the package foo routine in the Cookie class
  - A class MyCookie derived from Cookie can get to the fooProtected routine
  - Dinner can access the public foo routine in DessertCookie. Inside of the DessertCookie foo routine, we can then access the Cookie package foo routine.
- Compile and execute Dinner.java

Default Package Example

If you don't specify a package, then the default package consists of all files in the working directory.

Create the following Files:

Cake.java


//: Cake.java
// Accesses a class in a separate
// compilation unit. Note that the Pie.class file needs to be in this
// directory to qualify as being in the "Default Package".  The Pie
// constructor needs to either be public or "Default".

class Cake {
  public static void main(String[] args) {
    Pie x = new Pie();
    x.f();
  }
}


PieWrongName.java



//  This Class is used in the Cake.java class.

// Java source files are allowed to have at most 1 public class.
// The name of that class then dictates the name of the file as we
// have seen all through our class so far.

// However, in this file we have no public class, and
// we aren't forced to name the file after any class currently in this
// file.  Note that compiling this file will create a Pie.class file.
// If you think about it, you can see how this yields some compiler and
// loader efficiencies.

class Pie {
  void f() { System.out.println("Pie.f()"); }
}



- In your current directory download, create the files displayed above:
Cake.java and PieWrongName.java
- Try compiling the Cake.java file. The Cake class tries to access the Pie class. When the compiler tries to compile Cake.java, it will first look for Pie.class. If it doesn't exist, then the compiler will look for a Pie.java file to compile.
- You will need to explicitly compile PieWrongName.java. If the Pie class had been put in a file named Pie.java, then the compiling Cake.java would automatically compile Pie.java, because Cake.java needs a class named Pie. Compile Cake.java and execute it.
- The Pie class is defined in a file named PieWrongName.java. This is OK as long as the Pie class is not defined as public.
- Java source files are allowed to have at most 1 public class. The name of the one public class then dictates the name of the file as we have seen all through our class so far. Note that compiling PieWrongName.java will create a Pie.class file. If you think about it, you can see how this yields some compiler and loader efficiencies.

Keyword Definitions:

Some miscellaneous Keyword concepts:

static - variables(covered earlier) : static variables are global to all class instances.

static -methods :( covered earlier ): static methods are like global methods that don't require a class instance to execute

final - variable :

A final variable can only be set once. After being initialized it becomes a constant:


final double PI=3.1416; // Normal way final variables are declared
final int sidesInHexagon;

sidesInHexagon = 6; // However, this is OK

// At this point the following would be illegal statements:
PI = 3.14; // Compiler error would occur here
sidesInHexagon = 6 ;// Compiler error would occur here


final - method :

A final method cannot be overridden. The following will generate a compilation error:


class Rectangle
{
    final double getArea()
    {
        return 1.0;
    }
}
class Square extends Rectangle
{
// Compiler will complain about the following method overriding
// a final method in Rectangle.
    double getArea()
    {
        return 1.0;
    }
}


final - class :

A final class can not be extended. A class that can't be extended tends to be a bit more efficient. Some of the key java classes are final for this reason. Look in online help at: String, and Math

final class Rectangle
{
    final double getArea()
    {
        return 1.0;
    }
}

// Compiler won't be happy with the following
// statement because we are extending a final class

class Square extends Rectangle
{
}


synchronized Methods -

The synchronized keyword indicates that a method may be accessed by only one thread at a time. This will be covered in the Threads section. The syntax for a synchronized method is:
synchronized int retrieve()
{
}

native Methods -

The native keyword is used to mark methods written in another language like C. We won't use this in this class.

Return Types

It would be good for you to review the casting section because it is very much related to this section.

Returning Primitives

If a method returns a primitive data type, then it can also return a "smaller" datatype without a caste. If the method wants to return a "bigger" datatype a caste is needed. Consider the following example which demonstrates valid Java possibilites.


class RetValues
{
    long retPrimitive(int type)
    {
        byte b=1;
        short s=1;
        char c='a';
        int i=1;
        long l=1;
        float f=1;
        double d=1;

        switch (type)
        {
        // For byte, short, char, int, long you need no caste
        // because a long is "bigger" than these datatypes
        case 0:
            return b;
        case 1:
            return s;
        case 2:
            return c;
        case 3:
            return i;
        case 4:
            return l;

        // Need a caste for float and double because these
        // datatypes are considered "bigger" than long
        case 5:
            return (long)f;
        case 6:
            return (long)d;
        }
        return 1;
    }
}


Returning Objects

You can return Objects that have a different type as the declared return type as long as the actual value returned is extended from the desired Return Type.

If this can't be determined by the values declaration, then a caste is needed. This will keep the compiler happy. However, at runtime an invalid caste will throw an exception and halt your program.

You might question the fact that the objects are created inside of a method and then returned. Primitives created inside of a method go out of scope when a method returns. The key to understanding this is the fact that objects are created from a global heap of memory in java. The objects would be garbage collected if no pointer is returned. However, as long as some piece of java code has a pointer to an object, it won't be garbage collected.


class Rectangle
{
}
class Square extends Rectangle
{
}
class RetObjValues
{
    Rectangle retObjects(int type)
    {
        Object obj = new Square();
        Object obj2 = new Object();
        Rectangle r = new Rectangle();
        Square s = new Square();

        switch (type)
        {
        // It's OK to return a class extended from Rectangle, or
        // a Rectangle --- no casting needed in these cases
        case 0:
            return s;
        case 1:
            return r;

        // obj is declared as an Object, so we need to
        // caste at least up to a Rectangle.  We can also
        // caste obj to Square, because Square extends Rectangle
        case 2:
            return (Square)obj;
        case 3:
            return (Rectangle)obj;

        // The following will compile, but will throw
        // an exception at Run Time when Java discovers
        // that obj2 can't be caste to a Rectangle
        default:
            return (Rectangle)obj2;

        }
    }
}


Returning Arrays of Objects

Not only can you return a single object, but you can return a variable lengthed array of objects.


class Test
{
    static String[] retArray(int num)
    {
        String [] retArr = new String[num];
        for (int i=0; i < retArr.length; i++)
        {
            retArr[i] = new String("num = " + i);
        }
        return retArr;
    }
    public static void main(String[] args)
    {
        String [] arr;
        arr = retArray(13);
        for (int i=0; i < arr.length; i++)
            System.out.println(arr[i]);
    }
}


Polymorphism


//: Shapes.java
// Polymorphism in Java
// We create an array of Shape's that were actually extended Shape's.
// Our program didn't know the actual specific type (Circle,
//    Square, etc), but the run-time system knew.  Note that the actual
//    specific object type was not known at compile time.
//

class Shape {
  void draw() {}
  void erase() {}
}

class Circle extends Shape {
  void draw() {
    System.out.println("Circle.draw()");
  }
  void erase() {
    System.out.println("Circle.erase()");
  }
}

class Square extends Shape {
  void draw() {
    System.out.println("Square.draw()");
  }
  void erase() {
    System.out.println("Square.erase()");
  }
}

class Triangle extends Shape {
  void draw() {
    System.out.println("Triangle.draw()");
  }
  void erase() {
    System.out.println("Triangle.erase()");
  }
}

public class Shapes {
  public static Shape randShape() {
    switch((int)(Math.random() * 3)) {
      default: // To quiet the compiler
      case 0: return new Circle();
      case 1: return new Square();
      case 2: return new Triangle();
    }
  }
  public static void main(String[] args) {
    Shape[] s = new Shape[9];
    // Fill up the array with shapes:
    for(int i = 0; i < s.length; i++)
      s[i] = randShape();

  // Make polymorphic method calls:
    for(int i = 0; i < s.length; i++)
      s[i].draw();
  }
}

Compile and execute the above.

In this example, Shape, Circle, Square and Triangle all have draw methods. Shape is the base class for Circle, Square, and Triangle.

We create an array of random objects such that at compile time it would be impossible to know which objects will be Circles, Squares, or Triangles.

Then we call all of the draw methods for the random collection of objects. Java knows at run time which objects are circles, squares, and triangles.

This is the essence of Polymorphism. At compile time it is impossible to know which draw routine should be called, but at run time Java must hang on to enough information to call the appropriate draw routine.

All created classes in Java work in a Polymorphic way. If you are a C++ programmer, you get Polymorphic classes if you include the key word virtual, otherwise the compiler determines which play routine is actually called.

Note that polymorphism is more expensive than non-polymorphic approaches.

Abstract Classes and Interfaces

Abstract Class -

An abstract class is a java class that can't be created with the new operator. To mark an abstract class, we use the keyword abstract. To use an abstract class, you must extend the abstract class with a regular class. Usually(but this isn't required), an abstract class has at least one method which has not been completed. The incomplete method is marked with the keyword abstract. Here is an example of an abstract class:


abstract class Shape
{
   Shape()
   {
   }
   abstract double getPerimeter();
   abstract double getArea();

   double getAPRatio()
   {
   	return getArea()/getPerimeter();
   }

   public static void main (String arguments[])
   {
   	Shape s = new Shape(); // This can't be executed
   }


}


Consider the Following Example of Deriving from an Abstract Class (Remove the package exercise statement at the top if packages are uncomfortable to you ---- we will cover this topic later)

Interfaces

An interface is a nonfunctional class that can contain constants and method declarations but no functional methods. An Interface is like an Abstract class where all of the methods are abstract.




interface Fabric {
     public Image getSample();
}


Class Using the Fabric Interface




import java.awt.Image;
class Chair extends Furniture implements Fabric {
     Image sampleImage;
     public Image getSample() {
          return sampleImage;
     }
// Additional code goes here
}


When a class implements an interface, its almost like it signs a contract that says that all of the methods in the interface will be implemented in the class.

If an interface contained 50 methods, then a class which implements the interface would be obligated to have all 50 methods included in the class.

An interface can consist of constants (i.e. variables that have initialized values that can't be changed). And variables declared in an interface are implicitly public, static and final. Consider:




interface ServerCodes {
     public static final int MAXCONNECTIONS = 20;
     public static final int MAXUSERS= MAXCONNECTIONS * 5;
}


Change the previous example to use interfaces instead of an Abstract class (Remove the package exercise statement at the top if packages are uncomfortable to you ---- we will cover this topic later)

The interface definition to use is defined below:

Class Derivation (Extends), Abstract Classes, and Interfaces

Concepts:

We have covered examples of deriving (extends) from regular classes. Now we are going to introduce you to some related concepts that are very important for understanding some of the java building blocks.

Abstract Class

- An Abstract class is very similar to a regular class except for a couple of things:
1. An Abstract class has the keyword abstract in its declaration
2. An Abstract class probably has at least one member function declared with the abstract keyword. This method has no implementation and must be implemented in any class derived from the abstract class
3. You can't create an instance of an abstract class
4. An abstract class can provide implementations for some of its member functions
5. Classes derived from an abstract class look just like regular classes that can have instances. Note that any class derived from an abstract class must implement any of the abstract member functions in the abstract class.

interfaces

- An Interface is a little like an Abstract class with no implementations for its member functions. However, you never derive a class from an interface, instead you add its member functions to your class and add the implements clause on your class definition. More specifically:
1. Interfaces don't have any implementation code
2. Interfaces in effect give java something like a multiple class inheritance capability. You derive your class from some other class but you add a clause that says something like:
implements xyzinterface
3. If a class implements an interface, it MUST have all of the interface member functions show up as defined in the class implementing the interface.
4. You can declare a class that has the type of the interface. See the Music examples that follow:

Polymorphism

Execute each of the following 3 examples and note the Polymorphic result you get from:
Instrument.play();

Music3:
This program is the first of a three-some of examples demonstrating standard Java Object capabilities.

Music4:
This program is similar to the previous example, except that the base class is an abstract class.

Music5:
This program is similar to the previous example, except that the base class is an Interface. A Singer class is shown that implements the interface and is derived from a Person class. Hence we effectively have a Singer class that is "sort of" derived from 2 classes.

Exercises:

access modifiers: public, protected, default

Consider the following classes found in the exercise package:

Shape, Rectangle, Square, Circle classes in the exercise package

Remove the public keyword from the getPerimeter method in the Rectangle class. Where can the Rectangle.getPerimeter method be accessed. Where can't it be accessed. Create some Java code to demonstrate all of these principles.

Change the public keyword to protected for the getPerimeter method in the Rectangle class. Where can the Rectangle.getPerimeter method be accessed. Where can't it be accessed. Create some Java code to demonstrate all of these principles.

You can make these changes in the classes directory in your home directory.

Rework the Rectangle, Square, and Circle classes to implement the following Shape2 Interface.


interface Shape2
{
   double getPerimeter();
   double getArea();
   double getAPRatio();
}


Zoo, Animal class with Abstract classes and an interface


Consider the following classes:


import tools.*;


class Animal
{
   private int weight = 0;
   private String name = null;


   Animal(int w, String n)
   {
      weight = w;
      name = n;
   }

   int getWeight()
   {
      return weight;
   }

   void setWeight(int w)
   {
      weight = w;
   }

   String getName()
   {
      return name;
   }

   void setName(String n)
   {
      name = n;
   }
}

public class Zoo
{
   Animal [] zoo_arr;
   int num_animals = 0;

   Zoo (int num)
   {
      zoo_arr = new Animal[num];
   }

   void addAnimal(Animal a)
   {

      if ((zoo_arr != null) && (num_animals < zoo_arr.length))
         zoo_arr[num_animals++] = a;
   }

   int totalWeight()
   {
      int total_weight = 0;
      int i = 0;

      for (i = 0; i < num_animals; i++)
      {
         total_weight += zoo_arr[i].getWeight();
      }
      return total_weight;
   }

   void printList()
   {
      int i = 0;

      for (i = 0; i < num_animals; i++)
      {
         System.out.println("Animal: " + zoo_arr[i].getName());
         System.out.println("Weight: " + zoo_arr[i].getWeight());
      }
   }



   public static void main (String[] args)
   {

      String aName = "";
      String aType = "";
      int aWeight = 0;
      MyInput in = new MyInput();
      String input;
      int zooSize = 50;
      Zoo zoo_arr = new Zoo(zooSize);
      Zoo zoo2 = null;
      boolean contFlag = true;

      while(contFlag)
      {
         System.out.println("Select from the following");
         System.out.println("    a: add animal");
         System.out.println("    p: print the list of animals");
         System.out.println("    c: compute the total weight of the current animals");
         System.out.println("    e: exit");

         input = in.get();

         switch (input.charAt(0))
         {
         case 'A': case 'a':

               System.out.println("Enter the animal name:  ") ;
               aName = in.get() ;

               System.out.println("Enter the animal type:  ") ;
               aType = in.get() ;

               System.out.println("Enter the animal weight:  ") ;
               aWeight = in.getInt() ;

               zoo_arr.addAnimal(new Animal(aWeight, aName));
           break;

           case 'P': case 'p':
              if (zoo_arr != null)
                 zoo_arr.printList();
              else
                 System.out.println("No animals exist");
           break;
           case 'C': case 'c':
              if (zoo_arr != null)
                 System.out.println("Total Weight = " + zoo_arr.totalWeight());
              else
                 System.out.println("No animals exist");
           break;
           case 'E': case 'e':
              contFlag = false;
              break ;
         }
      }
   }
}

// ***************************************************


interface Living
{
    void born();
    void die();
}

Implement the Living interface in the Animal class.

Make the Animal class into an abstract class and add an
abstract method called SoundsLike()

Add Reptile ,  Mammal , Snake, Monkey, Horse

The Reptile class has a layEggs routine, and the Mammal class
has a nursing routine.  Snake, Monkey, and Horse all have
SoundsLike routines.

In the Zoo class, create a method called listenToZoo().
Create another routine that finds out how old each of the animals
is.
The Date class has some useful capabilities for this.



Last Updated: September 22, 2004 1:16 PM