When you use a variable name in a method call, it _____ as the variable in the method header.

Try the new Google Books

Check out the new look and enjoy easier access to your favorite features

full-member-definition (note package and class are keywords).

    package-declaration <=>package package-name;
    class-body                 <=>full-member-definition}

    class-definition         <= >package-statement]
                                           {import-declaration}
                                            access-modifiers class identifier {
                                              class-body
                                            }

The braces in the last rule stand for themselves (in the previous rules they stand for standard EBNF repetition). Most classes in Java are defined in their own .java file (although we can and will later use one file to define multiple classes).

Let's examine the three major parts of a class definintion. First, the package-statement. Every class is defined in one package, specified in the package-statement; if this option is omitted, the class is said to be defined in the anonymous package (the name of the anonymous packages has no characters in it). Whatever package this class is in, all other classes in that package are automatically imported for use in it. Second, if this class must refer to any classes in other packages, they must be imported explicitly in an import-declarations. Finally, the class itself is defined: it specifies its own access modifiers (almost always jut public) and includes any number of full-member-definitions.

Here is a trivial but complete class named Application. It is defined in the anonymous package, imports a neccessary class from the course library, and has a main method that performs some trivial I/O on the console.

import edu.cmu.cs.pattis.cs151xx.Prompt; public class Application { public static void main(String[] args) { int input = Prompt.forInt("Enter positive n"); System.out.println("You entered: " + n); } } Typically, such a class is stored in a file with the same first name as the class: Application.java. After discussing main methods, will see how to define complete classes for simple Java programs and libraries that define other methods.

The main Method Any Java class can define a special main method as one of its members; but, a method with this name is special only if it has exactly the following access modifiers and header (This method specifies an array of String as its parameter, although we will not use this parameter until we study how to use Java from a command line; we will see below how to tell the Eclipse IDE which special main method to execute.)

    public static void main(String[] args)

We can direct Java to start our program (a collection of one or more classes) automatically in any special main method. In fact, any project can include multiple classes, and each class can have its own special main method (this is actually quite useful, and we will discuss this feature when we discuss testing classes in more detail). In such a situation, we must tell Java WHICH special main method to start with.

In Eclipse, we specify the class whose main method is to be run by selecting the class either in the Package Explorer or in the Editor.

Methods in Applications We have seen how to declare one special static method in a class and have Java execute that method. Now we will learn how to define and call other static methods in a class. All we must do is place these other method definitions inside the class, along with the main method. Then, we can call these method from main or from each other.

Any static method in a class can call any other static method in that same class, just by using its name and supplying arguments that match its signature (or, if overloaded, one of its signatures). We can also be a bit more consistent (and verbose) and call a static method by prepending the class name to the method's name. The following Application class shows a simple example of such code.

import edu.cmu.cs.pattis.cs151xx.Prompt; public class Application { public static int factorial (int n) { int answer = 1; for (int i=2; i<=n;> Date Calculator #1

program. This program defines and used five static methods (and twelve static fields).
Definition Order Java uses a multi-pass compiler, which means that the methods and fields in a program can be defined in any order: Java first reads all the method headers and fields in the file, then in reads all the bodies, checking that they all use the types of these methods and fields correctly.

One standard way to write methods is in the "natural" order: if the body of method b calls method a, then method a is defined before method b. For example, we might have the following program form

method a's header {...no method calls...} method a's header {...call to a...} method c's header {...no method calls...} method d's header {...calls to b and c...} main methods' header {...calls to d and a...}

In fact, there may be many natural orders: e.g., in this example we could also meet the natural criteria by defining method c before method b or even before method a. The main method calls lots of other methods, so it typically appears last in the file.

In the "reverse natural" order: if the body of method a calls method a, then method a is defined after method b. In this case, the main method calls lots of other methods, so it typically appears first in the file.

main methods' header {...calls to d and a...} method d's header {...calls to b and c...} method c's header {...no method calls...} method b's header {...call to a...} method a's header {...no method calls...} In this way, the most powerful methods appear at the top; we can read the details of how they work aftward. Because Java uses a multi-pass compiler, these two orderings, or any others, are all legal. When we discuss mutually recursive methods, we will return to this topic again.

Now, some words on divide and conquer, and program complexity. Up until now, we have been putting all of our code in the main method, some of which have been a hundred or more lines of code. This practice is stopping here! From now on, we will be distributing complexity by writing methods, and placing them in the application program, or in class libraries. We can write, test, and debug each method (and each class) by idependently.

Each method, including main, should not comprise more than one or two dozen statements; when a method gets too complicated (it does "this" and "that") then write a "this" method and a "that" method, and have the original method call these two new methods to get its job done. Another rule for keeping the complexity of each method small it to prohibit more than one loop (the most complex Java statement to think about) per method -or allow multiple loops, but not nested loops.

Notice how the complexity has been distibuted in the date calculator program, in which each method, even main contains only a small number of statements.

Throwing Exceptions
(an introduction)
We have already discussed how to handle thrown exceptions with try-catch statements. Now is an appropriate time to begin discussing the other end of exception processing: how to throw them them after detecting a problem. The EBNF rule for throwing an exception (using the keyword throw) is trivial:

    throw-statement <=>throw expression;

where there is a syntax constraint that expression must refer to an object constructed from a class descended from the Throwable class. We will discuss class hierarchies later; for now we have seen the names of a variety of classes descended from Throwable: EOFException, NumberFormatException, and most important for our current needs, IllegalArgumentException, and IllegalStateException.

Exceptions are represented by classes; so, throwing an exception requires us to construct a new instance of the class, typically initializing its state by a String that describes the problem; this String can be further examined and printed when the exception is caught.

Given that our factorial method only works for non-negative integers, we might modify it as follows, to detect a bad argument and throw IllegalArgumentException with an appropriate message (rather than just returning 1). Notice how throws IllegalArgumentException now appears in factorial's signature.

public static int factorial (int n) throws IllegalArgumentException { if (n < 0) throw new IllegalArgumentException ("factorial: n ("+n+") must be non-negative"); int answer = 1; for (int i=2; i<=n;> A simple if statement, the first in the method, determines whether or not the argument is bad, and if so throws an exception. It is common to check all the necessary preconditions on arguments at the start of a method's body, grouping such code together and separating it from the code that actually performs the method's task (which executes only after all the preconditions on the parameters have been checked). In this example, if the argument matching parameter n is a negative value, Java constructs an instance of the IllegalArgumentException class (initialized with an appropriate error message), and throws that exception.

When a statement throws an exception, Java abandons sequential execution and tries to locate a catch clause to handle the exception, first inside the method in which it was thrown; if the method itself doesn't have one, Java goes back to the call site for the method (which is the body of some other method) and repeats this process there. If by repeating this process, Java eventually gets back to the special main method, and if there is no matching catch clause to handle the exception, Java prints the exception name, the exception's message (the String argument to the exceptions constructor), and a trace of all the methods that were called, leading up to the problem.

We will use throw statements as we continue to learn about writing constructors and methods in classes. We will come back to the topic of throw statements and try-catch statements, and exception classes at least once more (in the context of class hierarchies), to help us further understand this complex error detection and recovery mechanism. There we will learn how to write new exception classes and the difference between checked and unchecked exceptions.

Methods in Library Classes Although some static methods might be useful in just one application, many are general enough to be used in other (similar) applications. In Java, we can easily collect these methods into a class of related methods (all the source code in the same file), which we can easily import and use in other programs.

The Math class in the standard Java library serves exactly this purpose, as doe the Prompt class in the course library: each collects together a group of math-related or console i/o related methods. For example, we ccould easily group together all of the static methods and fields from the date calculator program into a DateUtility class as is shown below. Then, we could use such a class library in any program that must deal with dates. Examine the Date Calculator #2 program to see exactly how this mechanism works in a project.

public class DateUtility { //Returns whether year is a leap year? public static boolean isLeapYear (int year) {return (year%4 == 0 && year%100 != 0) || year%400 == 0;} //Returns the number of days in month (in year) public static int daysIn (int month, int year) throws IllegalArgumentException { if (year < 1) throw new IllegalArgumentException ("daysIn: year ("+year+") not positive"); if (month < JANUARY || month > DECEMBER) throw new IllegalArgumentException ("daysIn: month ("+month+") not in range [1,12]"); //Thirty days hath September, April, June and November... if (month == APRIL || month == JUNE || month == SEPTEMBER || month == NOVEMBER) return 30; //...all the rest have thirty one... else if (month == JANUARY || month == MARCH || month == MAY || month == JULY || month == AUGUST || month == OCTOBER || month == DECEMBER) return 31; //...except February (must be FEBRUARY in else: see possible exception) else /* if (month == FEBRUARY) */ return 28 + (isLeapYear(year) ? 1 : 0); } //Returns the ordinal (1st, 2nd, 3rd, etc) representing month, day, year public static int ordinalDate (int month, int day, int year) { int ordinal = 0; //Scan every earlier month, summing the # of days in that month... for (int m=JANUARY; m < month; m++) ordinal += daysIn(m, year); //...and add day in the current month return ordinal + day; } //Returns a date as an American or European String //e.g., for February 10, 1954 these return "2/10/1954" and "10/2/1954" public static String americanFormat (int month, int day, int year) {return month + "/" + day + "/" + year;} public static String europeanFormat (int month, int day, int year) {return day + "/" + month + "/" + year;} //Fields: all public static final (constants supplied by class) //These could be private, for use only in this class, // but what the heck, let programmers use them from this class // (with final, there is nothing a programmer can do to change them) public static final int JANUARY = 1; public static final int FEBRUARY = 2; public static final int MARCH = 3; public static final int APRIL = 4; public static final int MAY = 5; public static final int JUNE = 6; public static final int JULY = 7; public static final int AUGUST = 8; public static final int SEPTEMBER = 9; public static final int OCTOBER = 10; public static final int NOVEMBER = 11; public static final int DECEMBER = 12; }
  Recall that final variables (constants) in Java are written as upper-case identifiers. If their name consists of multiple words, separate them by underscores: e.g., MAX_CLASS_SIZE.

Given the use of a library class, the main method in the Application class must refer to its members by using both their class name and member name: e.g., int ordinal = DateUtility.ordinalDate(month, day, year);

Again, observe that inside this class, we refer to each member by just its name. Outside the class (in the Application class) we must refer to each static member by its class name followed by its member name.

Finally, note that there are no constructors for this class (and likewise no instance variables). We do not construct objects from this class; we just use the class name directly to refer to the methods that we want to call from this class.

Methods/Fields and the Eclipse IDE Methods are so common in programming, various parts of the Eclipse IDE have been built to deal with them easily. Here we will examine mechanisms in the Java and Debugger views that help us use methods in our programs.

The editor includes a mechanism to locate and display a method easily in a program or library class. When a class is active in the editor, the Outline window lists all the methods in the class. We can easily view a method in the editor by clicking its name in the Outline window. As the number of methods in a class grows, this mechanism becomes more and more useful for quickly navigating files.

To the left of each method header is small shaded circle, containing either a minus sign or a plus sign. The minus sign means the method is fully disclosed; the plus sign means the method body is non-disclosed/elided (we see only its header). Clicking the circle toggles between disclosed and elided method bodies.

We can also use the debugger to better understand methods and debug methods that we have written. The options displayed when we are stepping through a program appear as

SimpleDiceEnsemble and Rational.

Instance Variables Let's start by looking at the implementation details for two sample classes. The SimpleDiceEnsemble class must store information characterizing the ensemble (number of dice and sides per die) and information about its current state (number of rolls, pip sum, whether all die show the same numer of pips). It declares its instance variables as follows. private int numberOfDice; private int sidesPerDie; private int rollCount; private int pipSum; private boolean allSame;

The Rational class is much simpler: it must store only the numerator and denominator of the rational number (fraction). It declares its instance variables as follows.

private int numerator; private int denominator;

Classes typically group the declarations of all their fields at the top or bottom (although there are no rules requiring this placement) Recall that Javadoc pages show fields first, so declaring them at the top is reasonable. Another perspective is that instance variables are private details, so declaring them at the bottom (out of the way) is reasonable.

Whenever new constructs an object, the first thing that it does is process all the field declarations in the class, which includes reserving space for all these field and initializing them. Unlike local variables, ALL FIELDS ARE INITIALIZED when they are declared: if we do not explicitly initialize them in their declarations, then Java implicitly initializes them: for the primitive types, it uses 0 for int, 0. for double, false for boolean, and the null character for char; for all reference types it uses null (meaning that they do not refer to any object).

In the examples above, all instance variables are initialized to 0 and false; in SimpleDiceEnsemble it is as if we had explicitly written

private int numberOfDice = 0; private int sidesPerDie = 0; private int rollCount = 0; private int pipSum = 0; private boolean allSame = false; We will soon see that constructors can (and often do) store more appropriate values in these variables, based on the arguments that we supply to the constructor. So technically, when a constructor stores a value into an instance variable, it is reinitialization, not initialization, because an initial value has already been stored there by Java, when it executes its declaration. Still, we will speak about initializing instance variables in constructors (and reinitialization if we want to be precise).

Constructors The main purpose of any constructor is to ensure that all the instance variables of the object being constructed are initialized correctly. This is done in the body of the constructor, which contains exactly the same statements that can appear inthe body of a void method.

For some instance variables a constructor may do nothing special: it leaves them with the initial values they received when declared. In other cases it initializes (actually reinitializes, given the discussion above) instance variables using the arguments passed to the constructor's parameters; the constructor often validates these arguments first (throwing IllegalArgumentException if they are incorrect).

There are classes, some quite complicated, in which constructors take no arguments and reinitialize no fields. In these cases, the fields are initialized correctly in their declarations (either explicitly or implicitly). The Timer class is one example of this kind of class. Its constructor looks like

public Timer () {} In fact, if we fail to define any constructor for a class, Java will automatically supply one that looks like this one (with the appropriate class name). But, if we define even one constructor for a class, Java will not overload the constructor(s) by defining this one.

Most classes define at least one constructor (and many overload the constructor). These constructors always have parameter that help reinitialize instance variables.

SimpleDiceEnsemble The first constructor defined in the SimpleDiceEnsemble class is public SimpleDiceEnsemble (int numberOfDice, int sidesPerDie) throws IllegalArgumentException { if (numberOfDice < 1) throw new IllegalArgumentException ("SimpleDiceEnsemble constructor: " + "Number of dice ("+numberOfDice+") < 1"); if (sidesPerDie < 1) throw new IllegalArgumentException ("SimpleDiceEnsemble constructor: " + "Sides per die ("+sidesPerDie+") < 1"); this.numberOfDice = numberOfDice; this.sidesPerDie = sidesPerDie; //rollCount: see declaration for implicit initializaton to 0 //pipCount and allSame indeterminate until roll } It first validates the values of its two parameters: if either does not make sense (we must have at least one die, and it must have at least one side), the constructor throws an IllegalArgumentException with an appropriate message. If the parameters do make sense, it copies them into two of the instance variables (reinitializing them). The other three instance variables are not reinitialized: the initial values they received when decared are correct: rollCount should always start at zero, and pipSum and allSame, although they store zero/false, really represent nothing, because the dice haven't been rolled yet (so any values would work for these).
Interlude: Variable Name Conflicts and Resolving them with "this" We must take a briefly diversion to discuss variable name conflicts and how to resolve them with the keyword this. There are three kinds of variable names in Java.
  1. The name of a parameter (defined in the constructor/method header)
  2. The name of a local variable (defined in the constructor/method body)
  3. The name of a field (defined in its class)
The Java compiler automatically implements a syntax constraint that prohibits defining a parameter with the same name as a local variable. So, the compiler would detect and report an error in following code public static int returnIt (int a) { int a = 1; return a; } In fact, Java points at the local variable declaration of a and says, "Variable 'a' is already defined in this method".

But, Java does allow instance variables to have the same names as parameters or local variables. When this happens, it is called a variable name conflict, because when we use that common name, there is a conflict as to what it means. Whenever there is a variable name conflict, the name by itself NEVER refers to the instance variable; it ALWAYS refers to the parameter or local variable. If instead we want to refer to the instance variable, we must preface its name with this. (this is a keyword). In a constructor, this is a reference to the object being constructed; and this.numberOfDice refers to the numberOfDice instance variable defined inside the class. In fact, writing this.numberOfDice is always a legal way to refer to the numberOfDice instance variable in the object being constructed, whether or not there is a variable name conflict.

So, in the constructor above, both parameter variables have a name conflict with two of the instance variables. The if statements, which check numberOfDice and sidesPerDie, are testing the parameter variables; the statements

this.numberOfDice = numberOfDice; this.sidesPerDie = sidesPerDie; store the values of the parameter variables (which disappear when the constructor finishes executing) into the instance variables (which exist so long as the object they are in exists). If we wrote numberOfDice = numberOfDice; then Java would just store the parameter's value back into the parameter variable: it stores nothing into the instance variable! Such a statement can cause a very hard to locate bug!

Another way around this whole "name conflict" problem is to change the parameter names; e.g. use number and sides. With no name conflicts, so we can write just numberOfDice = number; and sidesPerDie = sides. But, it is often the case that a well chosen name for an instance variable is replicated as a parameter name, because it captures exactly the right description; in such cases we must understand name conflicts and use this to resolve them.

To help avoid confusion, some style guidelines for Java specify that every access to an instance variable should be prefixed by this. to indicated explicitly it that is accessing a field. I'm still on the fence about this style rule.

Back to Discussing Constructors The second SimpleDiceEnsemble constructor has a much different form. First, it has no parameters; second, it does not throw any exceptions (this information is all specified in the constructor's header). We could have written this constructor as public SimpleDiceEnsemble () { numberOfDice = 2; sidesPerDie = 6; } which initializes the two instance variables so that the object represents two, six-sided dice. Note that because there are no parameter names in this constructor, so there are no name conflicts; therefore, we can use the instance variables directly with the this. prefix (although we could include this prefix for stylistic reasons).

But Java provides an even simpler way to define this constructor (even if it requires us to learn a new language feature: a different context in which to use this). The actual constructor appears as

public SimpleDiceEnsemble () {this(2,6);} In the constructor above this says "to initialize the instance variables, use another constructor from this class, one taking two int arguments. This is a common pattern, where one general constructor (with many parameters) is used by one or more special constructors (with fewer parameters) to do the initializations. Note that if we needed, we could add more statements to the constuctor AFTER this one (here, none are needed).

In fact, another way to handle all the initialization in this class is to declare

private int numberOfDice = 2; private int sidesPerDie = 6; private int rollCount; private int pipSum; private boolean allSame; The first constructor would work as before, reinitializing numberOfDice and sidesPerDie using the parameters. But the second constructor could be simplified to contain nothing in its body, because now when the instance variables are declared, they correctly represent two, six-sided dice.

Thus, constructors act as middlemen: they accept arguments, check these values for correctness, and ultimately use them to (re)initialize instance variables (if they are correct). Because instance variables are private, they can be initialized only in the declaration themselves, and reinitialized by a constructor defined inside the class.

Rational The first and most general constructor defined in the Rational class is public Rational (int numerator, int denominator) throws IllegalArgumentException { if (denominator == 0) throw new IllegalArgumentException ("Rational Construtor: - denominator 0"); if (numerator == 0) denominator = 1; //Ensure non-negative denominator; if a rational is // negative, its numerator is negative if (denominator < 0) { denominator = -denominator; numerator = -numerator; } //call gcd (greatest commmon divisor) // a private static method defined in this class int common = gcd(numerator,denominator); //or ...Rational.gcd(...) //name conflict this.numerator = numerator /common; this.denominator = denominator/common; } This constructor ultimately stores very special values into its two instance variables, carefully checking/altering its parameters before doing so. First, it cannot construct a rational value with a denominator or zero, is if the parameter has this values, it throws an exception. For all other numerators and denominators, it stores values according to the following rules.
  • Zero is always stored as 0/1
  • The denominator is always stored as a positive value
  • The numerator and denominator are reduced to have no common factors
So, if we declare Rational x = new Rational(2,-4); then x refers to an object that stores -1 for the numerator and 2 for the denominator (try some other examples). The parameters are examined and changed as needed in all but the last two statements; at the end, this. is used to resolve the name conflicts. Note the call to the method gcd, which is a static method defined in this class. Any non-static method can call a static method.

The following more special constructors create new objects by using this (in the sense of using another constructor in this class to initialize the instance variables)

public Rational (int numerator) {this(numerator, 1);} public Rational () {this(0, 1);}

In the first of these constructors, we specify a only a numerator parameter and by using this construct a rational with that value over 1; in the case of a parameterless constuctor, we construct a rational with the value 0 over 1; we could also have written this(0);

Blank Final Recall that we can declare blank final local variables. We can also declare blank final instance variables, but we must follow and additional constraint. Java allows us to declare an intance variable final and not initialize it in its declaration (the definition of blank final) But, we must initialize this variable in EVERY constructor that we write, otherwise the Java compiler will detect and report an error. Of course, the Java compiler will ensure that we never try to assign a second value to any final variable, including final instance variables.
Methods Method bodies follow the same rules as constructor bodies, in terms of their use of parameter variables, local variables, and instance variables (and in terms of this, variable name conflicts, etc). In fact, when we illustrate the call frame for a non-static method, it will show an implicit parameter named this and we will see how this parameter gets initialized by an implicit argument when such a method is called.

Recall that methods are divided into two categories

  • Mutator/command methods can access and store into instance variables declared in the class; they change the state of the object they are called on.

  • Accessor/query methods can access instance variables declared in the class, but not store into them; they do not change the state of the object they are called on.
We cannot tell just by looking at a method header whether it defines an accessor or a mutator (we must look at the method body or Javadoc). Yet, this is a fundamentally important piece of information about any method.

Often, one can tell which it is by the name of the method: accessor method names often begin with get. Also, void methods are almost always mutators: if they don't return a result, the only interesting thing they can do is change the state of the object they were called on. Some methods, like nextToken in the StringTokenizer clas act as both a mutator/command and accessor/query: changing an object's state and returning some value.

SimpleDiceEnsemble The SimpleDiceEnsemble class defines the roll method to be both a mutator/command and accessor/query (it is the only mutator in the class). It is defined as follows. public SimpleDiceEnsemble roll () { this.rollCount++; int firstThrow = this.randomDie(); this.pipSum = firstThrow; this.allSame = true; for (int i=2; i<=numberofdice;> Here, for clarity in the discussion to come, I have prefaced each instance variable by this. (even though there are no name conflicts). The roll method has no parameters; it declares two local variables (firstThrow and nextThrow) that it uses to change the rollCount, pipSum, and allSame instance variables

Methods often have few or no parameters, because they primarily operate on the instance variables of an object. The pips showing for each die are computed by the randomDie method, which We will examine later.

Let us see how to hand simulate a call to this method by using a call frame. Pay close attention to how this, the implicit parameter, is initialized by the implicit argument. Assume that we have declared

SimpleDiceEnsemble dice = new SimpleDiceEnsemble(2,6); and now we execute the statement dice.roll(); We illustrate the call of this method by the call frame below (assume that we roll a 3 on the first die and a 5 on the second).

Javadoc of Course API.

/** * Objects constructed from the <code>DiceEnsemble</code> * class act as collections of dice. * The number of dice in an ensemble (and the number of sides of * each die) can be controlled by the programmer. * The class models the basic operations need to roll the dice and * determine the number of pips showing -both for individual dice * and for the entire ensemble. * * @author Richard E. Pattis */ Javadoc copies the contents of this comment into the web page that it builds for this class; it appears near the top, right before the first Summary table. I write such comments in the .java file a special style, for ease of editing; each line is a sentence, with sentences longer than one line indented. The web browser renders this text as a nice paragraph.

Note that I said that Javadoc copies the contents of the message... and the web browser renders the text... This means that the comment can us embedded HTML markup tags; these tags are copied into the web page and rendered by the browser, just like normal HTML tags in text. Notice the use of <code>DiceEnsemble</code> to render the name of this class in the code font; in a multi-paragraph description, we use <p> to separate the paragraphs. Generally, use what HTML markup tags you are familiar with to format your documentation.

Finally, note the special Javadoc markup tag @author; Javdoc makes special use of these tag, often creating special HTML for them.

A typical constructor or method is documented by a Javadoc comment of the following form; the comment appear in the

Postingan terbaru

LIHAT SEMUA