Contact Us Sitemap
Main Menu
Home
Who Are You Like?
- - - - - - -
Books
Movies
Music
Restaurants
Games
- - - - - - -
Hiking
Articles
More Articles
Blog
- - - - - - -
Java
PHP
PSP
Joomla!
CafePress Designs
- - - - - - -
Free Downloads
Web Links
Galleries
Paypal Donate
Subscribe to RSS
RSS Feed
Who's Online
We have 9 guests online
Statistics
Visitors: 1125112
Login Form





Lost Password?


Hwy777.com
Blog Directory & Search engine
Home arrow Java arrow Studying for the Java Programmer certification

Studying for the Java Programmer certification Print
Written by Mike Noel   
Thursday, 11 August 2005
Page 3 of 4

Chapter 7: Nested Classes and Interfaces

This chapter covers the complexities of static and non-static nested classes in a lot of detail. A nested class is simply a class defined inside another class. When applied to a nested class the static or non-static label has the same sort of semantics that is has when applied to other members of a class. Java also allows for nested interfaces, local classes (basically, nested within a block of code), and anonymous nested classes (classes without a name). The topics listed below explain some of the details concerning these topics.

Constructing non-static member classes

A non-static member class is a class defined within the context of another class. Since the classes are non-static they must have an associated encapsulating object. The standard class constructor syntax:
MyClass myClass = new MyClass();
Doesn't work because there is no way to specify the encapsulating object. Suppose that MyClass is a non-static member of the TopClass class. Since it's non-static there must be a TopClass object associated with any instance of a MyClass object. The constructor shown above doesn't enforce that requirement.
Doesn't work because there is no way to specify the encapsulating object. Suppose that MyClass is a non-static member of the TopClass class. Since it's non-static there must be a TopClass object associated with any instance of a MyClass object. The constructor shown above doesn't enforce that requirement.
A new form of a class constructor is required for non-static member classes. The basic form of this constructor is:
InnerClass innerClassRef = outerClassRef.new InnerClass();
Notice that an OuterClass object, referred to here by outerClassRef is required, not just the outer class name. When the constructor is called from within an outer class method the this keyword can be used in place of the outerClassRef. This examples shows this:
class OuterClass {

  // ...

  public InnerClass makeInnerObject() {

    InnerClass innerRef = this.new InnerClass();
    return(innerRef);
  }

  class InnerClass {
    // ... inner class details
  }
}
The makeInnerObject() method is not static. It must be called within the context of an OuterClass object. When this method is called a new InnerClass object is created and a reference to it is returned. As a shorthand the this keyword can be omitted. This results in:
  // ...

  public InnerClass makeInnerObject() {

    InnerClass innerRef = new InnerClass();
    return(innerRef);
  }

  // ...
With the this keyword dropped this constructor call doesn't look any different than any other class constructor invocation.

Accessing members of sibling nested classes

It seems intuitive that a nested class would have access to all of the fields and methods of the enclosing class. What doesn't seem intuitive is the fact that member classes have access to all of the fields and methods, regardless of access specifier, of all other nested classes. There is no access hierarchy when it comes to accessing sibling nested classes.

Multiple non-static member class objects in a single top-level object

Non-static member classes are instantiated in the context of an enclosing object (see the section above for a description of this). It is tempting to think of a class as a "type" and then assume that there can exist only one nested class instance for a given outer class. This isn't true. It is possible to have any number of inner class objects associated with the same outer class object. The states of each of the internal objects are distinct (but they have access to each other). They are completely different objects.

Local classes and formal parameters

It is hard to explain this issue simply but I'll try. A local class is a class declared in a block. Typical blocks in a program are method bodies and if/for/while statement bodies. Just as local variables can be declared in any of these blocks, classes can be declared in this blocks. The scope of these classes extends from the point of declaration through the end of the block. Again, just as with local variables. A typical local class might look like this:
class TopLevel {

  // ... other class stuff

  public Object myMethod(String p) {
    class LocalClass {
      // ... local class stuff
      public void toString() {
        return("Value = " + p);
      }
    }
    return(new LocalClass());
  }
}
This example can look complicated so I'll explain it a bit. First of all, there is a top level class which contains a method myMethod. This method takes a String parameter and returns an instance of LocalClass. The LocalClass type is not visible outside of the myMethod method so only an Object ref can be passed back to the client. For this example the toString() method is overridden in LocalClass. So far this is confusing but reasonable. Pay attention to the parameter p. Notice that when myMethod is called a value for p is supplied. It is not used anywhere in the method but it is referenced by the toString() method of the local class. By the time the toString() method is called the myMethod() call will be completed. However, the value of p when the class was declared will be preserved and used.

Accessing hidden enclosing object fields

An inner class can hide a field from the enclosing class by declaring a new field by the same name. A special form of the this syntax is required to access the hidden field.
class TopLevel {
  int f = 3;

  class Inner {
    int f = 5;  // hides the top-level field

    void func() {
      System.out.println(f);               // (1)
      System.out.println(TopLevel.this.f)  // (2)
    }
  }
}
The code at (1) prints the value of the visible field. That is, it prints "5". The code at (2) accesses the hidden field. It prints "3".

Chapter 8: Object Lifetime

Static fields can be initialized before declared

It is common to see a field declaration combined with an initial value assignment. This is typically of the form:
class MyClass {
 
  int x = 10;

  // methods follow this
}
The member x is given an initial value when a MyClass object is constructed. There is a similar form for static members. In those cases the static field is initialized when the class is initialized. An unexpected form of static field initializatoim allows the static field to be assigned a value before it is declared. The following example illustrates this:
class MyClass {

  static int x = y = 10;  // (1)
  static int y;           // (2)
}
Notice the use assignment of the value "10" to the variable "y" at (1) before the static field "y" is declared at (2). This same behavior can be seen when using a static initializer block.
class MyClass {

  static {  
    y = 10;               // (1)
  }

  // Other methods 

  static int y;           // (2)
}

Chapter 9: Threads

A important feature of the Java language is the built-in support for threads. Constructs in the language make it relatively easy to create threads and manage their execution. This chapter covers these language features and explains some of the issues involved with multi-threaded Java programs.

Using the yeild(), sleep(), and wait() methods

There are several ways that a running thread can become paused (not stopped, just paused). The scheduler can decide to pause a thread in order to enforce it's policies, an IO resource can block a thread and put it in a paused state, or a thread can try to access a locked object and end up paused while it waits for the lock to be freed. These are mostly outside the control of the thread object and occur at unpredictable points in the execution of the thread. There are three methods that the thread can use to pause itself directly. These are the yeild(), sleep(), or wait() methods.
The yeild() method is the simplest of these. This method simply pauses the thread and puts it back in the ready-to-run state. The scheduler can decide when to re-run this thread. If there are no other waiting thread it may decide to run thread immediately.
The sleep(t) method is not much more complicated. This method is called with a time value specified in milliseconds. The thread will pause for that period of time and then be put back in to the ready-to-run state. After that point the scheduler can re-run the thread at any time. Since the scheduler may not run the newly awakened thread immediately it is likely that the elapsed time from the time the sleeping start till the thread is running again will be greater than the specified time value. Because of this the sleep() method alone it not a good means of controlling time-synchronous activity (such as animating graphics).
The wait() method is more involved and is only useful when dealing with synchronized methods (object locks). In fact, the wait() method can only be called on an object that is locked by the calling thread. When a thread calls the wait() method it releases it's lock on the object and pauses. The fact that it releases the lock is one of the important distinctions between this method and the other pausing methods. Neither yeild() or sleep() release any locks.

start() runs in the current thread

Once a new thread is created it is started by calling the start() method. The start method will eventually end up calling the run() method. There is a crucial difference between these two methods. The start() method is executed in the same thread as the caller. The run() method is executed in a newly created thread. Here's an example.
public class Main {
  
  public static void main(String args[]) {

    Thread ct = Thread.currentThread();
    System.out.println("The main thread is " + ct.toString();

    Thread t = new Thread {
      public void start() {
        System.out.println("Calling start with thread " + this.toString());
      }
      public void run() {
        System.out.println("Calling run with thread " + this.toString());
      }
    }  
    t.start();
  }
}
This program starts by getting the current (main) thread and printing out it's name. Next it creates an anonymous class derived from Thread. This class overrides both the start() and run() methods. In each case the method prints a short message including the thread id. The message printed from start() shows that it's thread is the same as the main thread. The message printed from run() shows that it's thread is new.


Last Updated ( Tuesday, 11 July 2006 )
 

Copyright 2004 - 2008 Mike Noel. All rights reserved.
This Site is powered by Joomla!.