Spring Semester 2002


Lecture Notes Five: Syntax. The structure of main.
What is a number? That much we know.

Let's say: an integer or a floating point number. We know all about integers and floating point numbers, I'd say.

What is an operator? You want a long answer or a short one?

Short. +, -, *, / are operators.

What is an expression? A number, is a (very simple) expression. A symbolic name is also a (very simple, or atomic) expression.

What is an expression followed by an operator followed by another expression? That would also be an expression, wouldn't it?

Yes, indeed. What do we have so far? We have this table:
Term defined Composition
expression number
variable
(expression operator expression)

Do we need the parentheses? Not really, but we want to emphasize the structure.

Correct. Now, is
((3 + 4) * a)
an expression?
It is. Do you want to show why with a diagram?

I sure do. Very well, there you go:

That's interesting that we can define a concept (such as expression) in terms of itself. This is called a recursive definition. An important part of a recursive definition is specifying a set of fixed points,

... without which defining something in terms of itself could go on for ever. Exactly. Thank goodness for numbers and variable names without which our definition would be irreversibly circular.

What is an assignment statement? Its structure is as follows: variable name, followed by an equal sign, followed by an expression, and then by a semicolon.
a = 3 + 5;
a = b;
r = new Rectangle(2, 2, 10, 10); 

What are we trying to get at? The structure of all Java programs.

What is a declaration? It is a type, followed by a variable name and a semicolon...
int m;
Rectangle r; 
double x, y, z; 
I see you can declare several variables at the same time.
Yes, that's relatively obvious by now, I hope.

... and it can also be a type, followed by an assignment statement (as defined above). You should be building a few examples following these definitions, or take some statements and analyze them the way we analyzed the simple expression a few lines above.
int m = 25; 
int n = 34, p; 
Rectangle r = new Rectangle (5, 10, 8, 16); 

OK, I see what you're getting at, let's move on. Are we really going to be extremely precise and cover all possible cases?

Not really. For that we'd have to sacrifice some of the intuitive structure of all these. But we'll go far enough for you to get a good grasp of the general structure.

Are you ready for something really deep? Go ahead.

What is a function call? A function name followed by open parenthesis, followed by zero or more arguments separated by commas, followed by closed parenthesis.
System.out.println(); 
System.out.println("Wow!");
Math.sqrt(2);
r.translate(3, 3);

What is an argument? I suppose any expression would work as one.
System.out.println("tom" + "ate".substring(0, 2) + "a dog".charAt(3));

I've seen length() and substring() being invoked on Strings, but what's charAt()? Exactly what you're thinking that it might be.

Based on its name? Yes. And we'll talk more about chars tomorrow.

OK, let's summarize what we have so far:
Term defined Composition
expression number
variable
(expression operator expression)
funCall funName ( )
funName ( arguments )
arguments expression
expression, arguments

Any questions about function names? Not really, I suppose they're basically of the "absolute path" kind, like
System.out.println

Good assumption. Are function calls expressions themselves?

I'm glad you asked. The answer is "yes" if the function returns a value. Then we are now dealing with an even bigger infinity of expressions.

Note though that not all functions return values. Yes, println from System.out does not return a value, but sqrt from class Math returns the square root of the argument, so it can be used in an expression.

Let's update our table. We'll put a star (*) next to remind ourselves that it's a logic error to use a function call in an expression if the function does not return a value.
Term defined Composition
expression number
variable
(expression operator expression)
funCall *
funCall funName ( )
funName ( arguments )
arguments expression
expression, arguments
The update is minimal, but I think I need to look at more examples before I become too dizzy.

You've already seen examples of expressions, as defined by the table above. This table is a table of syntactic categories described in terms of other syntactic categories,

... an enterprise aimed at describing the (grammatical structure of our simple) language.

Here are some more examples of expressions.

Take them apart, fit them in the table, don't just accept them and then move on.
Don't worry. Let's take a look:
Math.sqrt(a + Math.sqrt(b))
This is easy:

Math.sqrt(Math.sqrt(Math.sqrt(Math.sqrt(a))))
Just as easy:

Math.pow(a, (1.0 / 16.0))
Same as above:

Math.sqrt(Math.pow(Math.sqrt(Math.pow(Math.sqrt(2)), 2)), 2)
A bit messy, perhaps, but easy:

(6 + (5 + (4 + (3 + (2 + 1)))))

Neat.


What is a statement?
A statement is a
  • declaration or
  • an assignment statement,
Or a function call, as in:
System.out.println("Hello, world!"); 

Exactly.
What is this?
public class Template 
{ public static void main(String[] args) 
  { 
    <methodBody>
  }
}

It's the template we're using, and methodBody is composed of one or more statements. That is, declarations and statements in any order, with the following final table describing the entire structure of the language (so far).
Term defined Composition
statement declaration
assignment
funCall
declaration type variable ;
type variable = assignment ;
assignment variable = expression ;
expression number
variable
(expression operator expression)
funCall *
funCall funName ( )
funName ( arguments )
arguments expression
expression, arguments

This table covers the syntax of the Java programs that we are going to be writing for a while. Note that not all syntactically correct programs are logically correct.

For example declaring a variable a twice is a semantic error, although having two declarations in a program is not a syntactic error... ... but if the variable is one and the same the semantic part of the compiler will signal that.

Suppose you compile and run the program below. What's its output?
class One {
  public static void main(String[] args) {
    int m = 2; 
    System.out.println(m); 
    int m = 3; 
    System.out.println(m); 
  }
}

The program won't compile. One cannot declare a variable twice.

All right, let's move on. Have you heard of the latest late policy on assignments and such? There's no new policy, everything is still the same: you need to turn everything on time. Try your best to meet the deadlines.

Just trying to get your attention. OK. Let's move on.

Fine. What is an int? It's an integer number between -231 (which is about -2 billion) and (231 - 1). If you need to refer to these boundaries in your program, use the constants Integer.MIN_VALUE and Integer.MAX_VALUE

... which are defined in a class called Integer like Math.PI is defined in the Math class. Convention says: name your constants using all caps for the name of the constant.

How do you define constants? Mark them final when you declare them.

Here's a program, what does it produce: Overflow.
public class Test 
{ public static void main(String[] args)
    { int n = Integer.MAX_VALUE; 
      System.out.println(n); 
      n = n + 1; 
      System.out.println(n); 
    }
} 

Have you run it? Yes, it's an eye-opening experience.

Why does it happen? Representation is finite.

What if we need bigger integers? Use long.

What's a long? It's a type that allows for the representation of bigger integers. The range is now -263 (which is about -9 billion billions) to (263 - 1).

What if we need bigger integers? Then work with objects of class BigInteger.

How do I do that? We'll see that in a minute. What is a floating-point number?

A double or a float. float spans the range from -3.4E38 to 3.4E38. double is much wider: from -1.7E308 to 1.7E308 but both suffer from the same problem:

... precision. Yes, what's the output of this program?
public class Test 
{ public static void main(String[] args)
    { double a = 30000000000000000000000.0; 
      System.out.println(a - (a - 0.5)); 
    }
} 

It should be 0.5 by algebra. Yes, but is that what the program prints?

Try it. Also, try to initialize the double variable with a value that doesn't contain the decimal point.

In that case the compiler catches the overflow before it happens. Overflow happens even for double type.

The following program produces Infinity. You're kidding me!

Try it:
public class Test 
{ public static void main(String[] args)
    { double a = 1.5E308; 
      System.out.println(a * 10); 
    }
} 

For most of the programming projects in this book, the limited range and precision of int and double are acceptable. Just bear in mind that overflow or loss of precision occur.

Another kind of loss of precision occurs in what is known as a roundoff error. What's that?

In the processor hardware, numbers are represented in the binary number system, not in decimal. You get roundoff errors when binary digits are lost, they just may crop up at different places than you might expect.

Here's an example: Another eyeopener.
System.out.println(4.35 * 100);

What do we do? Keep a cool head. For example in this last case first round then cast to an int...

... if you want an int, ... ... and especially if you want the right one.

How about Slightly off...
System.out.println(Math.pow(Math.sqrt(2), 2)); 

That's different, but related. Yes, and we'll discuss how floating-point numbers should be tested for equality soon.

How do you use big numbers? If you want to compute with really large numbers, you can use big number objects.

Big number objects are objects of the BigInteger and BigDecimalclasses in the java.math package. Unlike int or double, big numbers have essentially no limits on their size and precision,

... however, computations with big number objects are much slower than those that involve primitive types. To perform operations you need to use methods
  • add
  • subtract
  • multiply and
  • divide

... that big number objects have. Here's
a + b * c
with big numbers.
import java.math.*;

public class Test 
{ public static void main(String[] args)
  { BigInteger a = new BigInteger("100000000000000000000000000000000000000");
    BigInteger b = new BigInteger("200000000000000000000000000000000000000");
    BigInteger c = new BigInteger("300000000000000000000000000000000000000");
    BigInteger d = a.add(b.multiply(c)); 
    System.out.println(d); 
  }
} 

This is good exercise in object notation. You bet. We've seen rectangles, strings, and now big numbers. We're getting even better.

So what should we have achieved by now? I think we understand integer and floating point numbers.

We're able to write arithmetic expressions in Java. We can appreciate the importance of comments and good code layout.

We can define and initialize variables and constants. We know of the limitations of int and double types,

... and the overflow and roundoff errors that result. We can read program input with ConsoleReader.

We know how to print program output. We understand the structure of a method body,

... which contains statements, such as declarations, assignments, and function calls. We've also talked about expressions.

We have used the String class to define and manipulate strings.
By the way can you draw a picture for me for the following situation:
String a = "Hello!"; 
String b = a.toUpperCase(); 

I sure can: Thanks. It is the same with substring, isn't it?
I know it is.

String objects are called immutable objects.


That's right: toUpperCase on a String works like add on a big number... ... or like intersection on a Rectangle.

Last updated: Jan 19, 2002 by Adrian German for A201