Being Classy


· and (you will also need the Scanner.class file)


Objects are the basic units of programming in object-oriented languages. In Java, new types of objects are defined by designing and implementing classes. A class is the blueprint from which objects can be created. This laboratory introduces designing and working with your own classes. In particular, the laboratory has you build a program by creating two classes. In building these classes you will explore information hiding and data abstraction. The laboratory also considers the different kinds of methods that classes can have.

Key Concepts

·         Class design, data abstraction, and information hiding

·         Instance variables

·         Accessor, mutator, and facilitator methods

·         Constructors

·         Graphics.drawOval() and Graphics.fillOval() methods

4.1           Getting started

·         Copy the laboratory files to your PC.

4.2           Design

·         For this lab, we are going to design and create a class SmileWindow and a helping class SmileyFace. Together, the two classes enable a program to display a "smiley face" in a window, as in the figure below.

·         For example, the following method main() will display a smiley face in a window.

public static void main(String[] args) {


      System.out.print("Window size for smiley (pixels): ");


      Scanner stdin = new Scanner (;


             int size = stdin.nextInt();


      SmileWindow myWindow = new SmileWindow();







For SmileWindow, the size of the smiley face is dependent upon the size of the JFrame containing the smile. That is, the the object displaying the face will automatically scale and position the smiley face to fit properly in whatever size window is requested.

·         The first step in creating a program to satisfy the specified requirements is the design phase. We need to develop the overall framework for the program. We must think about how the user will interact with the program.

·         The user should be able to enter the size of a window and have a window of that size appear with a smiley face in the window.

·         The user is not expected to provide the features of the face or the positions of these features.

·         The rendering (i.e., drawing and painting) will be produced automatically by SmileWindow and a helping class SmileyFace.

·         A SmileWindow user does not need to have any understanding of how the smiley face is drawn or scaled in its window. Instead, a SmileWindow user should have access to a SmileWindow constructor that sets up a new SmileWindow object and SmileWindow methods that provide access to existing SmileWindow objects.

·         In order to preserve encapsulation, a SmileWindow user should not be able to access or change directly any of the instance variables of SmileWindow. The SmileWindow user should be constrained to use the constructors and methods when manipulating a SmileWindow object.

·         Therefore, the SmileWindow instance variables should be private and the constructors, accessors, and mutators should be public.

·         Although a SmileWindow user does not need to have any understanding of how the smiley face is drawn or scaled in its window, we – as the implementors of SmileWindow – must.

·         You may have wondered why we are going to implement two classes for our task. Yes, it is possible to create one class that does it all. However, in object-oriented programming, it is best to break tasks into easily managed pieces. Let's begin by creating a class SmileyFace that will do the actual rendering of the face. Our other class SmileWindow will make use of SmileyFace to produce a window with a smiley face.

·         By separating these two aspects of the problem, it makes it easier to make enhancements and reuse the classes for other purposes. For example, perhaps in a future program, we will want a button with a smiley face on it. We can create a button and then access the SmileyFace class to draw the face, instead of creating a completely new class from scratch.

4.3           More Design and Code Production

·         Let’s begin building the SmileyFace class. A SmileyFace object will compute the location of the eyes and smile from the diameter and the position of the face. Therefore, a SmileyFace needs to maintain only the diameter of the face and the location of the face.

·         Open the file

import java.awt.*;


public class SmileyFace {

   private int diameter;

   private int x;

   private int y;


   // accessor methods . . .


   // mutator methods . . .


   // constructors . . .


   // facilitator methods . . .



·         The SmileyFace class contains three instance variables: diameter, x, and y. Following normal information hiding practices, the instance variables are declared private.

·         It is good programming practice to create accessor and mutator methods for instance variables. An accessor method typically begins with get followed by the variable name. Its return type is the same as the variable type. Add accessor methods to your program for each instance variable. We have already supplied the first one for you:

public int getDiameter() {

          return diameter;



·         Mutator methods are methods that change the value of the variable. Mutator methods typically begin with set followed by the variable name. Mutator methods are passed a parameter of the same type as the variable. Mutator methods do not return a value, so their return type is void. Here is a mutator method for the diameter.

public void setDiameter(int d) {

       diameter = d;



·         Beginning programmers are sometimes tempted to name a formal parameter with the same name as an instance variable that they want to update. This practice does not produce the desired result. Suppose we had written our setDiamet mutator in the following way.

public void setDiameter(int diameter) {

       diameter = diameter;



When the method is invoked, the name diameter refers to a formal parameter. Therefore, the assignment assigns the formal parameter its own value. It does not change the instance variable with that same name.

·         Add mutator methods to your program for each instance variable.

·         Although providing these methods may seem to be unnecessary, the methods are important for achieving information hiding, an important software engineering principle.

·         Properly hiding information gives the program, not the user, control over the circumstances under which variables can be accessed or modified. You can also choose to modify the internal representation of the smiley face, but the user will not know the difference because the user will still call the same accessors and mutators to set the diameter and coordinates of the face. Separating the internal representation from the user interface, including the accessors and mutators, is another important software engineering principle.

·         Compile your program. Although there is nothing to run right now, compiling your program helps find syntax errors early. After every code revision, it is a good idea to compile the code to check for and to correct syntax errors, such as a missed semicolon or a misspelled variable name.

·         Now we will add two constructors to the code. In particular, add a constructor that initializes the diameter, x and y variables with user-supplied values. To preserve information hiding, make sure that your constructor calls the appropriate mutator methods rather than accessing the variables directly. We provide the outline for another constructor:

public SmileyFace(int d, int xValue, int yValue) {

   // configure a smiley face with the specified characteristics


·         Do we need any other constructors? We may need to create a SmileyFace object before we know the diameter and position of the object. In this case, we need a default constructor that has no parameters and simply initializes the object. Have the default constructor represent a smiley face with diameter 2 at position (0, 0).

public SmileyFace() {

   // configure a smiley face with the default characteristics


·         Now that we have created our accessor, mutator and constructor methods, we can concentrate on the facilitator methods. Facilitators are methods that help the object perform its intended task. In this case, we need to provide the means for a SmileyFace object to draw itself. The paint() method must take into account all of the current attributes of its SmileyFace object in rendering the face.

·         Let's now learn how to draw ovals. Open up the Java API specification at and find the method details for Graphics.fillOval() and  Graphics.drawOval() located in the java.awt package.

·         Review the SmileyFace methods drawFace(), drawEyes() and drawMouth(). Do you understand how the methods work? It may help to work through the code with pencil and paper.

·         You will notice that instead of calculating the facial feature coordinates and drawing them in a single method, we distributed the tasks across multiple methods. In programming, it is generally recommended that you break down a task into easily manageable pieces. We could have placed all the code to draw the face in the paint()method. But, it is easier to read and debug code that is broken down into manageable pieces, each represented by a separate method. Add the following lines to your object's paint() method:





·         Compile your SmileyFace class. If you get any compiler errors, try to determine where you made any mistakes. If you cannot determine what is wrong, seek help from an instructpr.

4.4           Putting it TOGETHER

·         Open the file As written, class SmileWindow defines a blueprint for square windows. The class defines a default constructor that creates a JFrame with the title “smile”. It also provides a mutator method setSize() that resizes the window and a facilitator makeVisible() method that makes the window visible.

import java.awt.*;

import javax.swing.*;


public class SmileWindow  {

       JFrame window ;


       public SmileWindow () {

              // create JFrame

              window  = new JFrame("Smile");

              // set program to end when JFrame closes

              window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE) ;

              // size our new window




       public void setSize(int size) {

              window.setSize(size, size);



       public void makeVisible() {


              Graphics g = window.getGraphics();



       public static void main(String[] args) {

              Scanner stdin = new Scanner (;

System.out.print("Window size for smiley (pixels): ");

           int size = stdin.nextInt();


              SmileWindow myWindow = new SmileWindow();








·         Compile and run the program. It should prompt you for a size and then display an empty window with width and height equal to the size which you provided as input. Do you understand how the program works?

·         Now we will add our SmileyFace to the window. Uncomment the private instance variable named face of type SmileyFace in your program.

private SmileyFace face;

·         When we call our SmileWindow  constructor, it initializes the JFrame for our window and sets its default close operation to EXIT_ON_CLOSE. Add a line of code to beginning of the constructor to initialize the face instancevariable by calling its default constructor.

face = new SmileyFace();


·         When the window size is set in the setSize() method, we need to set the size and position of our SmileyFace object. Add code to setSize() to perform the following:

·         Set the x value of the SmileyFace object to 1/8 of the window dimension.

·         Set the y value of the SmileyFace object to 1/8 of the window dimension.

·         Set the diameter of the SmileyFace object to 3/4 of the window dimension

·         Now our final step is to call SmileyFace.paint(). In the makeVisible() method, add the following lines of code:

      Graphics g = window.getGraphics();



·         We want face to draw itself in window, so we call window.getGraphics() to get the graphics context for window. We pass this graphics context to face.

·         Compile and run the program. If it does not compile or run correctly, try to determine what the problem is. If you cannot figure it out, ask your lab instructor for assistance.

·         When your program runs successfully, try entering different sizes to see if your face is drawn correctly. It is possible to enter sizes that are too small or sizes that are too large. In later chapters we will learn how to address this problem. 

·         Show your completed program to the lab instructor.

4.5           Finishing up

·         Copy any files you wish to keep.

·         Submit your files and

·         Delete any laboratory files that you copied or created.