Java's AWT components can be divided into three categories:  

The visual components are the ones that users can actually see and interact with. Label is the simplest "widget". It does not respond to user input and does not send out any events.

Button implements a push button. Generates Action events.
Checkbox - a two-state button (true or false).   Generates Item events.
CheckboxGroup - a group of Checkboxes that demonstrate mutually exclusive selection (i.e. radio buttons). CheckboxGroup does not control physical layout, only logical association (the programmer must do physical layout with a Layout manager).
Generates Item events.
Choice implements a pull-down list.   Generates Item events.
List - a list of text items, arranged vertically. If its physical window is smaller than its list of items, a vertical scroll bar is added. Can allow multiple selections, or enforce a single selection. Single clicking generates an Item event. Double clicking generates an Action event.
Scrollbar - a "slider" widget. Allows the following parameters to be specified: min value, max value, initial value, slider size, and orientation. Generates Adjustment events.

ScrollPane - contains a single component. If the contained component is larger than the ScrollPane, then the latter acquires horizontal and/or vertical Scrollbars based on the "policy" specified at creation (SCROLLBARS_AS_NEEDED, SCROLLBARS_ALWAYS, SCROLLBARS_NEVER). Generally, you do not handle events on a ScrollPane, but, it can generate Mouse and MouseMotion events.
TextField - a single row text entry widget. Does not support scroll bars, but the "arrow" keys can be used to browse input that is larger than the component. Routinely broadcasts Text events. Generates Action events on receipt of an "Enter" key.  

TextArea - a multiple row text entry widget. If the text is larger than the physical window, then scroll bars will appear. Same events as TextField.

Canvas is a component that has no default appearance or behavior. It can be subclassed to create custom drawing regions and work areas. Canvases are one of the rare components that setSize() calls will work on (the Layout manager will not override their "preferred size"). They generate Mouse, MouseMotion, and Key events.

FileDialog - a modal "file open" or "file save" dialog window. The dialog is automatically removed when the user selects a file or the "Cancel" button.

   FileDialog fd = new FileDialog( parentFrame, "title" );
   fd.setVisible( true );     // block here until OK selected
   String s = fd.getFile();

Containers are components capable of holding other components. [ScrollPane also fits this description, but it does not present the same number of issues.]

The top-most component of a Java executable hosted by an HTML page. An Applet may only exist in a browser.
An independent window, decorated by the underlying windowing system, and capable of being moved, resized, iconified, and destroyed.
Allows the top-most Applet or Frame to be subdivided into additional layers or groups.
A pop-up window that accepts user input. A Dialog may optionally be made modal.

The visual and container components both inherit from java.awt.Component and share many common features. The menu components extend from java.awt.MenuComponent, so they do not inherit the same superclass functionality. You cannot add menus to ordinary containers and have them laid out by layout managers. You can only start a menu by putting a MenuBar into a Frame, using the setMenuBar() method.

   Frame   f  = new Frame( " title" );
   MenuBar mb = new MenuBar();
   f.setMenuBar( mb );
   Menu fm = new Menu( "File" );
   Menu em = new Menu( "Edit" );
   Menu hm = new Menu( "Help" );
   mb.add( fm );
   mb.add( em );
   mb.setMenuBar( hm );
   MenuItem mis = new MenuItem( "Save" );
   MenuItem mil = new MenuItem( "Load" );
   MenuItem miq = new MenuItem( "Quit" );
   fm.add( mis );
   fm.add( mil );
   fm.add( miq );
The menu bar for a Frame window.
A pull-down or pop-up list of selectable items. If a Menu appears within another Menu, it functions as a "cascading" menu item.
A "button" that lives in a Menu. Generates Action events.
A "check box" that lives in a Menu. Generates Item events.
A horizontal bar used for visually dividing a menu into sections.

GUI code

The minimum program to say "Hello world" in Java's AWT (Abstract Windowing Toolkit) is:
    1  import java.awt.*;

    2  class Widgets {
    3     public static void main( String[] args ) {
    4        Frame frame = new Frame( "Window title" );
    5        Label lab = new Label( "Hello world" );
    6        frame.add( lab );
    7        frame.pack();
    8        frame.setVisible( true );
    9     }
   10  }
This example creates an instance of class Frame. A frame is an independent window (decorated by the underlying window system) that can be moved, resized, iconified, or destroyed by the user. Any application that requires a GUI must use one or more frames to contain the desired components.

A Label is then created and added to the frame. pack() causes subcomponents of the Frame to be laid out at their preferred size. The window is then displayed by calling setVisible(true).

An alternate approach uses inheritance instead of the "aggregation" (I use the word loosely) strategy above. Below, class Widgets subclasses from class Frame, so it "is a" Frame - it inherits all of Frame's functionality and simply does "stuff" to itself. main() creates an instance of Widgets, and Widgets's constructor does all the rest.

    1  import java.awt.*;

    2  class Widgets extends Frame {
    3     public Widgets() {
    4        super( "Window title" );
    5        Label lab = new Label( "Hello world" );
    6        add( lab );
    7        pack();
    8        setVisible( true );
    9     }
   10     public static void main( String[] args ) {
   11        new Widgets();
   12     }
   13  }
The first line of the subclass's constructor invokes the superclass's constructor (line 4). This results in the window title being communicated to the parent Frame. add(), pack(), and setVisible() are now called on "self" (since Widgets "is a" Frame).

To control the appearance of a Label, several options exist.

    1  Label lab = new Label( "Hello world" );
    2  lab.setBackground( Color.yellow );
    3  lab.setForeground( );
    4  lab.setAlignment( Label.CENTER );
    5  lab.setFont( new Font( "SansSerif", Font.BOLD, 32 ) );
Background and foreground are self-explanatory. Labels have left-justification by default, but can be changed with setAlignment(). Fonts expect to be instantiated with: font, style, and point size. Your choices for font are: Serif (formerly TimesRoman), SansSerif (Helvetica), Monospaced (Courier), Dialog, and DialogInput. Your choices for style are: Font.PLAIN, Font.ITALIC, Font.BOLD, Font.BOLD + Font.ITALIC. Point size is an integer value.

Responding to user interaction

To create something a little more interactive, ask for a Button. But, to actually receive programmatic feedback when the user pushs the button requires an understanding of Java's event model. [See the Events section for a detailed discussion of event handling.] The example below demonstrates a minimal implementation for receiving notification when a button is pressed.
    1  import java.awt.*;
    2  import java.awt.event.*;

    3  class BL implements ActionListener {
    4     private int  count = 1;
    5     public  void actionPerformed( ActionEvent e ) {
    6        System.out.println( e.getActionCommand() + " - " + count++ );
    7     }
    8  }

    9  class Widgets {
   10     public static void main( String[] args ) {
   11        Frame  frame = new Frame( "Window title" );
   12        Button btn   = new Button( "press me" );
   13        BL     bl    = new BL();
   14        btn.addActionListener( bl );
   15        frame.add( btn );
   16        frame.pack();
   17        frame.setVisible( true );
   18     }
   19  }

   20  // press me - 1
   21  // press me - 2
   22  // press me - 3
A "listener" object (an instance of a class that promises to fulfill the role of interface xxxListener must be registered with widgets that expect to receive user interaction. In the example above, an independent class (BL [button listener]) has been defined (lines 3-8), an instance has been created (line 13), and the association has been registered (line 14). Whenever the button is pressed, an actionPerformed() message will be sent to the registered listener object. The implementation has chosen to report the value of a local count attribute.

An alternate way of doing the same thing is to create a totally "self-contained" component that inherits from Frame and declares an "inner" listener class. This design creates an abstraction that can be "plucked" out of one application and "dropped" in another.

    1  class Widgets extends Frame {
    2     public Widgets() {
    3        super( "Window title" );
    4        Button btn   = new Button( "press me" );
    5        BL     bl    = new BL();
    6        btn.addActionListener( bl );
    7        add( btn );
    8        pack();
    9        setVisible( true );
   10     }
   11     public class BL implements ActionListener {
   12        private int  count = 1;
   13        public  void actionPerformed( ActionEvent e ) {
   14           System.out.println( e.getActionCommand() + " - " + count++ );
   15        }
   16     }
   17     public static void main( String[] args ) {
   18        new Widgets();
   19     }
   20  }
The code that previously existed in main() now resides in a constructor (lines 3-9). main() is only a "testbed" that "launches and leaves" an instance of class Widgets.

Inner classes are a new feature of JDK 1.1. An inner class can use both class and instance variables of enclosing classes and local variables of enclosing blocks. Inner classes that are declared static automatically become top-level classes, and lose their ability to access the members of the enclosing class. Inner classes cannot declare any static members. One advantage of this approach is that byte-code size will shrink on the order of 60% as multiple classes are packaged into a single class.
A third way of doing the same thing has the Widgets class performing double duty. Instead of delegating event handling to an external class, or an internal class, Widgets is accepting that responsibility itself. Notice below that: Widgets implements ActionListener (line 1), Widgets registers itself as the listener (line 6), the "count" attribute is now local to Widgets, and the actionPerformed() method resides in Widgets (lines 11-13).
    1  class Widgets extends Frame implements ActionListener {
    2     private int count = 1;

    3     public Widgets() {
    4        super( "Window title" );
    5        Button btn   = new Button( "press me" );
    6        btn.addActionListener( this );
    7        add( btn );
    8        pack();
    9        setVisible( true );
   10     }
   11     public void actionPerformed( ActionEvent e ) {
   12        System.out.println( e.getActionCommand() + " - " + count++ );
   13     }
   14     public static void main( String[] args ) {
   15        new Widgets();
   16  }  }
I think the first approach is the most intuitive and straight-forward. Bruce Eckel thinks the second approach (inner listener classes) is the only way to go. And he thinks the last approach is bad, hard, and ugly. His argument is the second approach allows a separate listener object to be defined and registered for each event of interest on each component of interest, whereas the third approach requires that a single object (the parent) must register for all events and service all components. This will result in large "case" statements to sort out the events coming back from many different sources. The second approach uses lots of separate listener objects that each service a single source.

Responding to the "X" icon on the title bar

Handling events from the platform's underlying windowing system requires an understanding of Java's event model (see the Events section). The example below demonstrates a minimal implementation for receiving (and responding to) a notification when the title bar's "X" icon is pressed.

When you are finished with a frame, you need to recycle its non-memory resources (memory will be automatically harvested by the garbage collector). Non-memory resources vary from system to system. On a Unix/Motif platform, for example, a frame's non-memory resources include at least one file descriptor and one X window. To release these resources, call the frame's dispose() method. [Roberts, p265]
    1  import java.awt.*;
    2  import java.awt.event.*;
    3  //////////////// Handle exit window requests ////////////////
    4  class WL extends WindowAdapter {
    5     Frame frame;
    6     public WL( Frame f ) {
    7        frame = f;
    8     }
    9     public void windowClosing( WindowEvent e ) {
   10        System.out.println( "window disposing and exiting" );
   11        frame.dispose();
   12        System.exit( 0 );
   13     }
   14  }
   15  /////////////////// Handle Button presses ///////////////////
   16  class BL implements ActionListener {
   17     private int  count = 1;
   18     public  void actionPerformed( ActionEvent e ) {
   19        System.out.println( e.getActionCommand() + " - " + count++ );
   20     }
   21  }
   22  class Widgets {
   23     public static void main( String[] args ) {
   24        /////////////// Reuse the Frame base class ///////////////
   25        Frame  frame = new Frame( "Window title" );
   26        Button btn   = new Button( "press me" );
   27        WL     wl    = new WL( frame );
   28        BL     bl    = new BL();
   29        frame.addWindowListener( wl );
   30        btn.addActionListener( bl );
   31        frame.add( btn );
   32        frame.pack();
   33        frame.setVisible( true );
   34     }
   35  }
   36  // push me - 1
   37  // push me - 2
   38  // push me - 3
   39  // window disposing and exiting
A new class has been added - WL (window listener). It extends WindowAdapter (line 4). [See the end of this section for a discussion of why the relationship is not implements WindowListener.] windowClosing() is the message that is sent when the "X" icon is pressed. We are intercepting that message and interposing application functionality.

Because the WL object needs to "call back to" the original Frame object, it must have a "handle" to that object. Line 27 is passing that handle to WL's constructor, and the handle is being remembered (line 7) in the attribute declared on line 5. The handle is then used in line 11.

IF, the "inner class" approach described above had been used (instead of the "outer" class choice actually implemented), then the handle to the Frame object would not need to be passed to WL's constructor. The Frame object reference could be made a private data member of class Widgets, and inner class WL would have ready access to it.

Just as the second example in this section demonstrated that "aggregation" OR inheritance could be used to reuse the functionality of the Frame class, the same is true for event handling. The example below has the class Widgets fulfilling the "roles" of: Frame (master window), ActionListener (button event handler), and WindowListener (window event handler).

    1  class Widgets extends    Frame
    2                implements ActionListener, WindowListener
    3  {
    4     private int count = 1;

    5     /////////////////// Handle button presses ///////////////////
    6     public void actionPerformed( ActionEvent e ) {
    7        System.out.println( e.getActionCommand() + " - " + count++);
    8     }

    9     //////////////// Handle exit window requests ////////////////
   10     public void windowClosing( WindowEvent e ) {
   11        System.out.println( "window disposing and exiting" );
   12        dispose();
   13        System.exit( 0 );
   14     }
   15     public void windowActivated(WindowEvent e) { }
   16     public void windowClosed(WindowEvent e) { }
   17     public void windowDeactivated(WindowEvent e) { }
   18     public void windowDeiconified(WindowEvent e) { }
   19     public void windowIconified(WindowEvent e) { }
   20     public void windowOpened(WindowEvent e) { }

   21     public Widgets() {
   22        /////////////// Reuse the Frame base class /////////////// 
   23        super( "Window title" );
   24        setBounds( 50, 100, 300, 100 );
   25        addWindowListener( this );
   26        Button btn = new Button( "push me" );
   27        btn.addActionListener( this );
   28        add( btn );
   29        setVisible( true );
   30     }

   31     public static void main( String[] args ) {
   32        new Widgets();
   33     }
   34  }
In lines 25 and 27, the class Widgets registers itself as the window event handler and the button event handler. All the "empty" window event methods are required (lines 15-20) because Widgets implements WindowListener.
Recall from the interface discussion that all the abstract methods declared in an interface must have definitions supplied by classes promising to fulfill the "contract" of the interface.
For this very reason, Java has graciously defined "adapter" classes that provide an "empty" implementation for all the abstract methods in a Listener interface. Having these adapters available allows programmers to simply override the methods they are particularly interested in. In the previous example, we did just that. See the java.awt.event section for more on "interfaces" and "adapters".

Layout managers

To accomodate more than one widget in a window, you need to start dealing with "layout managers". The FlowLayout manager places and centers the add()'ed widgets horizontally. If insufficient real estate exists, it will wrap widgets to another "line" and continue centering. When the user resizes the window, FlowLayout adjusts its widgets' positioning for the new width.
    1  import java.awt.*;

    2  class Widgets {
    3     public static void main( String[] args ) {
    4        Frame frame = new Frame( "Window title" );
    5        frame.setLayout( new FlowLayout() );
    6        Button one = new Button( "first" );
    7        Button two = new Button( "second" );
    8        Button thr = new Button( "third" );
    9        frame.add( one );
   10        frame.add( two );
   11        frame.add( thr );
   12        frame.pack();
   13        frame.setVisible( true );
   14  }  }
Note: the "tab order" of components (moving focus from one widget to the next by using the keyboard's tab key) is set by the order in which they are add()'ed to the containing component.
The user of a GridLayout manager specifies how many rows and columns are to be supported, and then as widgets are add()'ed to the Frame, the GridLayout fills up each row horizontally before moving vertically to the next row.
    5        frame.setLayout( new GridLayout( 3, 1 ) );
The BorderLayout manager will format up to 5 widgets - one on each side of the Frame, and one in the center. Widgets are "anchored" to a specific position by specifying a second parameter in the add() method.
    5        frame.setLayout( new BorderLayout() );

    9        frame.add( one, BorderLayout.NORTH );
   10        frame.add( two, BorderLayout.CENTER );
   11        frame.add( thr, BorderLayout.SOUTH );
The GridBag is by far the most complicated layout manager. It divides its container into rows and columns of cells, and each individual cell can "span" multiple rows and/or columns. GridBagConstraints is a helper class used to specify all layout criteria.

The Card layout manager organizes its components in time rather than in space. At any moment, a container using the Card manager is displaying a single component; all others are unseen. The effect is similar to a tabbed panel without the tabs.

The FlowLayout's capability to do horizontal "line fill and word wrap" is nice, but it won't perform the same role vertically. To do vertical layout, you're generally stuck with GridLayout. But its autocratic policy of stretching all components so that they are the same size as the largest component is routinely inconvenient. "To avoid that, you can stick the component inside a panel and add the panel inside the grid." [Horstmann, vol1, p424]

    1  import java.awt.*;

    2  class Widgets {
    3     public static void main( String[] args ) {
    4        Frame frame = new Frame( "Window title" );
    5        frame.setLayout( new GridLayout( 3, 1 ) );
    6        Button one = new Button( "top" );
    7        Button two = new Button( "long in the middle" );
    8        Button thr = new Button( "the bottom" );
    9        Panel p1 = new Panel();   p1.add( one );
   10        Panel p2 = new Panel();   p2.add( two );
   11        Panel p3 = new Panel();   p3.add( thr );
   12        frame.add( p1 );
   13        frame.add( p2 );
   14        frame.add( p3 );
   15        frame.pack();
   16        frame.setVisible( true );
   17  }  }
While this technique preserves the default size of each component, it still causes all components to be layed-out in a regular grid that is predicated on the size of the largest component. If one component is extra tall, all other components incur extra "white space" between themselves and their neighbor beneath them. So the goal of performing "flow layout" vertically is not entirely realized.

FlowLayout is the default for panels and applets. BorderLayout is the default for frames.

If you don't want a layout manager, and want to place a component at a fixed location with a fixed size; then set the layout to null (line 2), and use setBounds(), setLocation(), and/or setSize() to specify the x, y, width, and height of each component. Line 3 is setting both position and size for the Frame. Line 5 is setting both for the first Button. Lines 7 and 8 together are setting both for the second Button. Note that pack() is not necessary here, because no default real estate management is being computed.

    1  Frame f = new Frame( "title" );
    2  f.setLayout( null );
    3  f.setBounds( 50, 50, 120, 190 );
    4  Button first = new Button( "first" );
    5  first.setBounds( 30, 45, 60, 40 );
    6  Button second = new Button( "second" );
    7  second.setLocation( 30, 115 );
    8  second.setSize( 60, 40 );
    9  f.add( first );
   10  f.add( second );
   11  f.setVisible( true );
"People who set a container's layout manager to null find that they have to write code to detect when the container resizes, and more code to do the right thing when resizing occurs. This ends up being more complicated than creating your own layout manager." [Roberts, p300]