Lecture 4

In this lecture we will discuss the boolean type, relational and logical operators and expressions, and if/else statements that use boolean values to allow conditional execution of statements.  We will also look at how to define a class, and start discussing how to define and use static methods.

The boolean type

The Java boolean type is a primitive type that has only two possible values: true and false.  It is used to represent the concept of truth or falsehood.

Examples of declaring boolean variables and assigning literal boolean values:

boolean a, b;

a = true;
b = false;

boolean c = true;

The environment after executing these statements:

Type Name Value
boolean a true
boolean b false
boolean c true

Relational Operators and Expressions

The relational operators compare two values and evaluate to a boolean result: whether the assertion made by the expression is true or false.  These are the relational operators:

Operator Meaning
== Equality
!= Inequality
> Greater than
< Less than
>= Greater than or equal to
<= Less that or equal to

Say that we start with the following statements:

int a = 3;
int b = 5;

We can now compare the values of the variables a and b using the relational operators.  (Try typing these expressions into the interactions window in DrJava.)

Expression Result
a < 1 false
a == 3 true
a >= 3 true
a >= 4 false
a != b true
b != 5 false

The operands in a relational expression can be any numeric type.  The standard numeric promotion rules apply, so if the operands are different number types, the less precise operand is promoted to the type of the more precise operand.  For example, if x is a double and y is an int, then in the expression

x > y

the variable y will be promoted to a double before the operands are compared.

The equality and inequality operators == and != can be used with any type of operands, not just numeric types.  For example, if a and b are booleans, then the expressions a == b and a != b are both valid, and have the effect of comparing the operands to see if they contain the same value or different values.

Logical Operators and Expressions

The logical operators take boolean operands and use them to perform a logical function.

The && and || operators are the logical and and logical or operators.  The following truth table shows how they are evaluated:

a b a && b a || b
true true true true
true false false true
false true false true
false false false false

Logical and (&&) evalates to true if both of the operands are true, otherwise it evaluates to false.  Logical or (||) evaluates to true if at least one of its operands is true, otherwise it evaluates to false.

The logical and/or operators have a lower precedence than any of the relational operators.  Therefore, it is possible to combine relational expressions using logical operators without using parentheses, because the relational sub-expressions will be evaluated before the overall logical expression.  For example:

int a = 3;
int b = 5;
boolean p = a > 10 || b <= 5;

The value of the boolean variable p will be true: the expression a > 10 evaluates to false, but the expression b <= 5 evaluates to true, making the overall logical or expression evaluate to true.

The logical not operator negates a boolean value, chaning true to false and false to true.  Logical not is a unary (one operand) operator written as an exclamation point just in front of the expression whose value should be negated.  E.g.:

boolean p = true;
boolean q;

q = !p;

The boolean variable p has the value true.  The value of the expression !p is then assigned to the variable qq thus will contain the value false.

The logical not operator has a high precedence (higher than any relational operator), so if you want to negate the value of a relational expression you need to put it in parentheses: e.g.,

!(a < 3)

Java is not English

The way that logical and relational operators combine in Java is less flexible than the analagous constructs in English.  For example, in English we can say

a equals 3 or 6

By this, we really mean "a equals 3 or a equals 6".

In Java, if you try writing

a == 3 || 6

or

a == (3 || 6)

neither of these expressions will have the indended effect, or even pass the Java compiler.  The first expression will be parsed as

(a == 3) || 6

Because 6 is not a boolean value, it is illegal as an operand to a logical operator.  The second expression will be parsed as

a == (3 || 6)

The same problem occurs because neither 3 nor 6 is a boolean value, and may not be used in a logical or expression.

We can fix these problems by writing the expression

a == 3 || a == 6

This is legal and correct because the relational expressions (a == 3 and a == 6) are evaluated to boolean results before the logical or operator is applied.

Defining a class

Every Java program consists of one or more classes.  So, to be able to start writing complete Java programs, we need to know how to define classes.

Here is the general (simplified) syntax for defining a class:

class definition :==

      class modifiers identifier {

        method or variable*

      }

class modifiers :==

      optional visibility modifier

      optional final modifier

      optional abstract modifier

optional visibility modifier :==

      public | private | protected | nothing

optional abstract modifier :==

      abstract | nothing

The purple star (*) signifies a construct that may appear an arbitrary number of times, or not appear at all.  The vertical bar (|) implies a choice between several alternatives.  The symbol nothing indicates that the empty string of symbols is a valid choice.

The visibility modifier specifies whether or not the class can be directly accessed by other classes.  Almost all classes are public, meaning that they can be freely used by all parts of the program.

The abstract and final modifiers have to do with an advanced language feature, inheritance.  We'll learn more about inheritance later in the semester.  For now, we won't be using these modifiers.

The identifier specifies the name of the class, and can be any valid Java identifier.  By convention, class names start with a capital letter, and if there are multiple words in the class name, each word begins with a capital letter.  Some examples of class names:

PongGame
PlayingField
Player
Ball

Defining a variable in a class

Classes can have variables defined in them.  A variable defined in a class is often called a field.  Here is the syntax for defining a variable inside a class:

variable :==

      variable modifiers type identifier ;

variable modifiers :==

      optional visibility modifier

      optional final modifier

      optional static modifier

optional static modifier :==

      static | nothing

type :==

      primitive type | identifier

primitive type :==

      boolean | byte | char | short | int | long | float | double

The visibility modifiers have the same purpose as before: to determine whether the variable can be directly accessed by other classes.  Most variables will be defined as private, meaning that they may only be accessed within the class that defines them.  Occasionally, we will define a variable as public, meaning that any class is allowed to access the variable.  Restricting the visibility of class data (variables) is called encapsulation.  This is an important technique for writing larger programs because it allows each class to control how the details of its implementation are exposed to other classes.

The final modifier means that, once initialized, the value of the variable never changes.  Such variables are constants.

The static modifier means that the variable belongs to the class as a whole, not objects that are instances of the class.  This is an extremely important distinction:

For variables, the static keyword means that the variable belongs to the class as a whole, and is not associated with any specific object.

The type of the variable defines what type of values may be stored in the value.  The primitive types you are familiar with may all be used as the types of variables defined in a class.  In addition, you may use the name of a class as the type of a variable: that means that the variable refers to an object whose type is the class specified.  We'll be talking more about classes and objects in the next lecture.

The identifier gives a name to the variable.  By convention, variable names start with lower case letters, and if there are multiple words in the variable name, each word (other than the first) starts with a capital letter.  Some examples of variable names:

color
directionAndVelocity
firstPlayer
secondPlayer
ball

Choosing meaningful names for variables is the single most important thing you can do to make your programs easy to read and understand.  Give some thought to choosing good variable names: it will make writing programs much easier.

Defining methods

Methods define the behavior or operations of a class: basically, what the class (and objects that are instances of the class) can do when the program executes.  Here is the syntax for defining a method:

method :==

      method modifiers

      return type 

      identifier

      ( optional parameters )

      {

      statement*

      }

method modifiers :==

      optional visibility modifier

      optional static modifier

      optional abstract modifier

      optional final modifier

return type :==

      void | type

optional parameters :==

      nothing | parameters

parameters :==

      parameter | parameter , parameters

parameter :==

      type identifier

The visibility modifier and optional static modifier mean the same things for methods as they do for variables: the visibility modifiers define the extent to which the method may be accessed by other classes, and the static method specifies that a method belongs to the class as a whole rather than being associated with a particular object.  The abstract and final modifiers may also be specified for methods: however, they are related to inheritance, so we won't worry about them for now.

For methods, the static modifier means that the method belongs to the class as a whole, and is not associated with any particular object (instance of the class.)

The return type of a method indicates what kind of value the method returns.  In addition to the usual primitive and class/object types, methods may declare the special void return type, which means that the method does not return any value.

The identifier specifies a name for the method.  The conventions for method names are the same as for variables: first letter is lower-case, and any subsequent words start with a capital letter.  Some examples:

move
changeDirection
animateOneFrame

As with variables, choosing meaningful names for methods will help tremendously in making your programs more readable.  Because methods define behavior, they will generally be verb phrases.  Variables define data, and therefore their names are generally noun phrases.

Variable names are usually noun phrases and method names are usually verb phrases.

The parameters of a method define the values that the method will receive when it is invoked.  They are specified just like other variable declarations, and, in fact, you should think of them as being variables that are private to the environment in which the method is executed.  More about this in a moment.

The statements in a method are simply the statements (variable declarations, assignments, etc.) that define the method's code: in other words, what the method should do when it is invoked.

Static methods are like mathematical functions

So, what do methods actually do, and how do they work?  We'll start with static methods, since they are simpler.  A static method is like a mathematical function.  It takes one or more parameters, which are the input values of the function, and returns a single return value, which is the output value of the function.

Let's tie together everything we've seen so far and define an entire class with a single static method:

public class Demo {
    public static double add(double a, double b) {
        double result;
        result = a + b;
        return result;
    }
}

We are defining a single static method called add inside a class called Demo.  This method takes the values of the two parameters a and b, adds their values, and assigns the sum to a local variable called result.  The return statement then returns the computed sum as the return value of the method.  Try typing this class into DrJava and saving it in a file called "Demo.java".  (The name of a Java source file must be the same as the class defined in the file.)  Hit "Compile All" and then go to the interactions window.  Your DrJava window should look something like this:

Now let's try invoking this static method with different input values:

The expressions appearing in the DrJava interactions window are static method call expressions.  They have the following general form:

class name . method name ( arguments )

class name is an identifier specifying the name of the class containing the static method.  The period (".") is the "choice operator": given a class or an object that is an instance of a class, it selects a variable or method defined in the class.  method name is an identifier specifying a particular method defined in the class.  The parentheses ("(" and ")") indicate that the expression is a method call.  arguments are a comma-separated list of values that will be used as the values of the method's parameters when the method call is executed.  Note that the list of arguments can be empty (if and only if the method does not have any parameters).

Calling a static method

Each time a method is called (because a method call expression is evaluated), a private environment is created to store the values of the method's parameters and any variables defined inside the method.  This environment exists only for the duration of the method call.  Let's take a specific example: the method call

Demo.add(-4.0, 3.0)

Recall that this static method call expression means in the Demo class, call a static method called add, passing it the argument values -4.0 and 3.0.  Here is the private environment that will be created for this call:

Type Name Value
double a -4.0
double b 4.0

Note the following important point:

The values of the arguments in a method call are used to initialize the corresponding method parameters.

This rule tells you almost everything you need to understand about how method calls are evaluated.  The argument values are passed to the method via the method parameters.

Once the private environment for a method call has been created and the arguments have been used to initialize the method parameters, the statements inside the method are evaluated using the private method environment.  In the case of the Demo.add method, we have the following statements:

double result;
result = a + b;
return result;

The first statement declares a double variable called result:

Type Name Value
double a -4.0
double b 3.0
double result ?

Because the new variable hasn't been assigned a value yet, it value is indeterminate.  The next statement evaluates the expression a + b and assigns the resulting sum to result:

Type Name Value
double a -4.0
double b 3.0
double result -1.0

Finally, the return statement returns the value of the result variable to the place where the method was called, thus terminating the execution of the method.  In other words:

The value of a method call expression is the value used in the return statement which terminates the method call.

Try experimenting by writing some static methods of your own and using the DrJava interactions window to call them.  A solid understanding of the mechanics of defining and calling methods is crucial in order to become an effective Java programmer.

If/else statements

So far we've only considered linear sequences of statements where each statement is executed in order.  If/else statements allow us to execute blocks of statements conditionally depending on whether a particular condition is true or false.  The syntax of an if/else statement is as follows:

if/else statement :==

      if ( condition )

      {

      statement*
      
      }

      optional else clause

optional else clause :==

      else

      {

      statement*

      }

The condition is simply any expression that evaluates to a boolean value.  All relational and logical expressions, boolean variables, and the literal values true and false are valid conditions.

Here is a simple example of a static method that uses an if/else statement to return the sign of a double value (-1.0 for negative input and 1.0 for positive input):

public class Demo {
  public static double sign(double value) {
    if (value < 0.0) {
      return -1.0;
    } else {
      return 1.0;
    }
  }
}