Sunncity.com - Developer & I.T. Zone

 
Sunncity Web

Technology Menu

Developer Center
Delphi Online Tutorial
JAVA Online Tutorial
The Code Project
XML Code Library
FREE Download
Our Free Components
Our Free Software
Free Components
Delphi & Kylix Training
SMTC Training Course
About Delphi EXAM

Search Component


Search Software(s)


by oneNetwork

 

Java Online Tutorial
BASIC SYNTAX AND FUNDAMENTAL CONCEPTS - Part II

This chapter covers the following topics and is essential reading before tackling the other chapters.

MODIFIER

FLOW CONTROL

 

MODIFIER

Modifier Overview

The most common modifiers are the access modifiers: public, protected, and private. The access modifiers are covered in the next section. The remaining modifiers do not fall into clear categories. They are:

  • final
  • abstract
  • static
  • native
  • transient
  • synchronized
  • volatile

Each of these modifiers is discussed in its own section.

Back to TOP

The Access Modifiers

Access modifiers control which classes may use a feature. A class' features are

  • The class itself
  • Its class variables
  • Its methods and constructors

Note that, with rare exceptions, the only variables that may be controlled by access modifiers are class-level variables. The variables that you declare and use within a class' methods may not have access modifiers. This makes sense - a method variable can only be used within its method.

The access modifiers are

  • public
  • protected
  • private

The only access modifier permitted to non-inner classes is public. There is no such thing as a protected or private top level class.

A feature may have at most one access modifier. If a feature has no access modifier, its access defaults to friendly. Be aware that friendly is not a Java keyword. It is just the colloquial name that we humans use for the type of access a feature gets if no modifier is specified.

The following declarations are all legal, provided they appear in valid contexts.

	class Parser { ... }
	public class EightDimensionalComplex { ... }
	private int i;
	Graphics offScreenGraphics;
	protected double getChiSquared() { ... }
	private class Horse { .... }

The following declarations are illegal.


	public protected int x;// at most one access modifier
	friendly Button getBtn(){ ... }// 'friendly' is not a modifier

Back to TOP

public

The most generous access modifier is public. A public class, variable, or method may be used in any Java program without restriction. An applet (i.e. a custom subclass of java.applet.Applet) is declared as a public class so that it may be instantiated by browsers. An application declares its main() method to be public so that main() may be invoked from any Java runtime environment.

To summarize - public features may be accessed from anywhere by anyone. External classes, subclasses and classes in other packages all have access to public features without any restriction at all.

Back to TOP

private

The least generous access modifier is private. Top level classes may not be declared private. A private variable or method may only be used by an instance of the class that declares the variable or method.

As an example, consider the following code.


	class Complex {
	   private double real, imaginary;

	   public complex(double r, double i) {
		real=r;
		imaginary=i;
	   }
	
	   public Complex add(Complex c) {
          return new Complex(real+c.real, imaginary+c.imaginary);}
	}

	class Test {
	   void useThem() {
		Complex c1 = new Complex(1, 2);
		Complex c2 = new Complex(3, 4);
		Complex c3 = c1.add(c2);
		double d = c3.real;		// illegal !
	   }
	}

Here again, the last line causes a compiler error similar to the previous one: "Variable real in class Complex not accessible from SubComplex." The private nature of variable real prevents an instance of SubComplex from accessing one of its own variables !

The main purpose of the private access modifier is hiding data . Here the SubComplex class can be made to compile by accessing variable real through an instance of its class.

Friendly

Friendly is the name of the default access of classes, variables, and methods, if you don't specify an access modifier. A class' data and methods may be friendly, as well as the class itself. A class' friendly features are accessible to any class in the same package as the class in question.

Friendly is not a Java keyword. It is simply a name given to the access level that results from not specifying an access modifier.

To summarize: only classes that are in the package may access friendly features of the classes that are in the package. Friendly is sometimes also called package-access

Back to TOP

protected

The name protected is a bit misleading. From the sound of it, you might guess that protected access is extremely restrictive -- perhaps the next closest thing to private access. In fact, protected features are even more accessible than friendly features.

Only variables, methods and inner-classes may be declared protected. A protected feature of a class is available to all classes in the same package, just like a friendly feature. Moreover, a protected feature of a class is available to all subclasses of the class that owns the protected feature. This access is provided even to subclasses that reside in a different package from the class that owns the protected feature.

As an example consider the following source:


	package sportinggoods;
	class Ski {
		void applyWax() { ... }
	}

The applyWax() methods defaults to friendly access. Now consider the following subclass.


	package sportinggoods;
	class DownhillSki extends Ski {
		void tuneup() {
		   applyWax();
		}
	}

The subclass calls the inherited method applyWax(). This is not a problem as long as Ski and DownhillSki reside in the same package. However if one of the classes were to be moved to a different package, DownhillSki would no longer have access to the inherited method applyWax() and compilation would fail. The problem can be fixed by making applyWax protected like this.


	package adifferentpackage;
	class Ski {
	   	
	   protected void applyWax() { ... }
	}	   

Back to TOP

Subclasses and Method privacy

Java specifies that methods may not be overridden to be more private. For example, most applets provide an init() method, which overrides the do nothing version in java.applet.Applet. The original version is declared public so declaring the subclass version private, friendly or protected would result in the compiler error which gives a message like, "Methods cannot be overridden to be more private."

private ---> friendly ---> protected ---> public

The rules for overriding may be summarized as follows.

  • A private method may be overridden by a private, friendly, protected or public method.
  • A friendly method may be overridden by a friendly, protected or public method.
  • A protected method may be overridden by a protected or public method.
  • A public method may only be overridden by a public method.

Back to TOP

Other Modifiers

The rest of this section covers the other Java modifiers - final, abstract, static, native, transient, synchronized and volatile .

Java does not care about order of appearance of modifiers. Declaring a class to be public final is no different from declaring it as final public . Declaring a method to be protected static is the same as declaring it static protected .

Not every modifier can be applied to every feature. A table at the end of the section summarizes which modifiers apply to which features.

Back to TOP

final

The final modifier applies to classes, methods and variables. Final features may not be changed, in other words final has the same meaning as constant.

A final class may not be subclassed. For example the following statement will not compile, because the java.lang.Math class is final.

        class SubMath extends java.lang.Math { ... }

The compiler says, "Can't subclass final classes."

A final variable may not be modified once it has been assigned a value. For example java.lang.Math class has a final variable, of type double, called PI. Obviously pi cannot be changed during the execution of a program.

If a final variable is a reference to an object, it is the reference that must stay the same, not the object. You cannot alter the reference of a final object, but you can change the data it contains. For example:


	class Walrus {
	    int weight;
	    Walrus(int w) { weight = w; }
	}
	
	class Tester {
	    final Walrus w1 = new Walrus(1500);
	    void test() {
		w1 = new Walrus(1400);	// illegal
		w1.weight = 1800;		// legal
	    }
	}

A final method may not be overridden. For example the code below will not compile.


	class Mammal {
	   final void getAround() { .. }
	}
		
	class Dolphin extends Mammal {
	    void getAround() { ... }
	}

The compiler error one would get from attempting to compile the above code would be: "Final methods cannot be overridden."

Back to TOP

abstract

The abstract modifier can be applied to classes and methods. A class that is abstract may not be instantiated ( i.e. you may not call its constructor).

The signature of an abstract method looks like this:


	abstract void travel();

You will notice that the method declaration has no method body! The implementation of an abstract method is found in its subclasses. The superclass only provides the method name and signature.

If a class contains one or more abstract methods, the compiler insists that the class must be declared abstract. This is a great convenience to programmers who only have to look in one place (the class declaration) to find out if they are allowed to instantiate that class or if they have to build a subclass.

In fact, the compiler insists that a class must be declared abstract if any of the following conditions is true.

  • The class has one or more abstract methods
  • The class inherits one or more abstract methods (from an abstract parent) for which it does not provide implementations.
  • The class declares that it implements an interface but does not provide implementations from every method of that interface.

In a way, abstract is the opposite of final. A final class cannot be subclassed whereas an abstract class must be subclassed.

Back to TOP

static

The static modifier can be applied to variables, methods and even a strange kind of code that is not part of a method. You can think of static features as belonging to a class rather than to a specific instance of the class.

Static means there is only one copy of the variable, no matter how many specific instances of the class might exist at any particular moment. You can reference a static variable through its class name or through a reference to any instance of the class. Since there is only one copy, no matter how many specific instances of the class, it makes better sense to reference it via the class name rather than through a specific instance.

Methods as well as data can be declared static. Static methods are not allowed to call the non-static features of their class (although they are free to call the static methods and static data of their class).

Consider an example:


	class SomeClass {
	   static int i = 45;
	   int j = 2;

	   public static void main (String args[]) {
		i += 120;
		j *= 10;	//error: will not compile!
	   }
	}

Non-static methods have an implicit variable named this , which is a reference to the object executing the method. For example:


	class NoThisInStatic {
	   int w;
	   void bumpW() {
	      w++;
	   }
	}

On the last line, the programmer has not specified which object's w is to be incremented. The compiler assumes the line to be an abbreviation for

		this.w++;

For static methods, there is no this, If you try to access it you will get an error message which says, "Undefined variable: this." As mentioned earlier, the concept of a specific instance of a class executing the current method does not mean anything, static belongs to the whole class in general.

A static method may not be overridden to be non-static. The code below will not compile.


	class Cattle {
	   static void foo() { ... }
	}
	
	class Sheep extends Cattle {
	   void foo() { ... }		//compiler error !
	}

The compiler objects to the last line because a static method is overridden to be non-static. It can be made to compile if we change the signature of the over-ridden method to:

		static void foo() { ... } 

To summarize static methods.

  • A static method may only access the static data of its class. It may not access non-static data.
  • A static method may only call the static methods of its class. It may not call non-static methods.
  • A static method has no this.
  • A static method may not be overridden to be non-static.

Static Initializers

It is legal for a class to contain static code that does not exist within a method body. A class may have a block of initializer code that is simply surrounded by curly braces and labeled static. For example:


	public class StaticDemo {
	   static int i = 5;

	   static {
	       System.out.println("Static code: i = " + i++);
	   }
	   
           public static void main(String args[]) {
	   System.out.println("main: i = " + i++ );
	   }
	}

This kind of construction is perfectly legal. The code inside the curlies is executed exactly once, at the time the class is loaded. At class load time, all static initializer code are executed in order of appearance within the class definition.

Free floating initializer code should be used with caution as it can easily lead to obfuscated code. The compiler supports multiple initializer blocks within a class, but there is never a good reason to have more than one such block.

Back to TOP

native

The native modifier can refer only to methods. Like the abstract keyword, native indicates that the body of the method is to be found elsewhere. In the case of abstract methods, the body is in a subclass. With native methods, the body lies entirely outside the Java Virtual Machine, in a library.

Native code is written in a non-java language, typically C or C++ and compiled for single target machine type.

The signature of a native method is like this:


		native void doSomething();

Again, there is no method body! As mentioned above the implementation resides in a library which is made available by the statement:


		System.loadLibrary("SomeLibrary");

Native methods are called just like ordinary methods and callers needn't know that the method is native. Many common methods are native, including all the number crunching methods of the Math class and the clone() and notify() methods of the Object class.

Back to TOP

transient

The transient modifier applies to variables only. A transient variable is not stored as part of its object's persistent state.

Many objects (specifically those that implement either the Externalizable or Serializable interfaces) can have their state serialized and written to some destination outside the Java Virtual Machine. This is done by passing the object to the writeObject() method of the File Output Stream, then the object's state is written to a file. If the stream is chained to a socket's Output Stream, then the object's state is written to the network. In both cases, the object can be reconstituted by reading it from the Object Input Stream.

There will be times when an object will contain extremely sensitive information. Consider the following class:


     class WealthyCustomer extends Customer implements Serializable {
	private float wealth;
	private String accessCode;
     }

Once an object is written to a destination outside the JVM, none of Java's elaborate security mechanisms is in effect. If an instance of this class were to be written to a file or to the Internet, somebody could snoop the access code. To prevent this the variable should be made transient by marking it with the transient keyword like this.


     class WealthyCustomer extends Customer implements Serializable {
	private float wealth;
	private transient String accessCode;
     }

Now the access code will not be written out during serialization.

Note: Transient variables may not be final or static.

Back to TOP

synchronized

Only methods may be synchronized. The synchronized keyword means that only one thread can access the method at a particular time.

Back to TOP

volatile

Only variables may be volatile. Declaring them so indicates that such methods may be modified asynchronously, so the compiler takes special precautions. Volatile variables are of interest in multiprocessor environments.

Back to TOP

In Summary

pNot all modifiers can be applied to all features. Top level classes may not be private or protected. Methods may not be transient. Static is so general that you can apply it to free-floating blocks of code.

The table below summarizes the possible combinations of features and modifiers.

Modifier

Class

Variable

Method/Constructor

Free-floating block

public

yes

yes

yes

no

protected

no

yes

yes

no

friendly

yes

yes

yes

no

private

no

yes

yes

no

final

yes

yes

yes

no

abstract

yes

no

yes

no

static

no

yes

yes

yes

native

no

no

yes

no

transient

no

yes

no

no

synchronized

no

no

yes

no

Back to TOP


FLOW CONTROL

The Loop Constructs

Java provides three loop constructions. Taken from C and C++, these are the while(), do() and for() constructs. Each provide the facility for repeating execution of a block of code until some condition occurs. The while() loop is discussed first.

The while() Loop

The general form of the while() loop is:


	while(boolean_condition) {
		repeated_statement;
	}

The element boolean_condition can be any expression that returns a boolean result. The repeated_statement will be executed again and again until the boolean_condition becomes false. If the condition never becomes false, then the loop will repeat forever. In practice this means that the loop will repeat until the program is stopped or the machine is turned off.

When there is only one statement following a while() statement then it is not necessary to put the braces (the curly brackets), but it is a good idea to put the braces anyway because they make code more readable.

Observe that if the boolean_condition is already false when the loop is first encountered then the loop will never be executed. This is the main feature that distinguishes it from the do() loop, discussed next.

The do() Loop

The general form of the do loop is:


	do  {
	    repeated_statement(s)
	} while(boolean_condition);

Again, just like the while() loop, the execution terminates when the boolean_condition becomes false. The significant difference is that this loop always executes the body of the loop at least one, since the test is performed at the end of the body. The third type of loop, by far the most common is the for() loop discussed next.

The for() loop

The general form of the for() loop is shown below.


	for (init_statement ; boolean_condition ; iter_expression) {
	    statement_one;
	    statement_two;
	}

The keys to this loop are in the three parts contained in the brackets following the for keyword.

  • The init_statement is executed immediately before the loop is itself started. It is often used to set up starting conditions. You will see shortly that it can also contain variable declarations.
  • The boolean_condition is treated exactly the same as in the while() and do() loops. The body of the loop will be executed repeatedly until the condition ceases to be true. As in the while() loop, it is possible that the body of the for() loop might never be executed. This occurs when the boolean_condition is already false at the start of the loop.
  • The iter_expression (short for "iteration expression") is executed immediately after the execution of the loop, just before the test is performed again. Commonly this is used to increment a loop counter.

In fact, because for() loops commonly need a counting variable, you are allowed to declare variables in the init_statement part. The scope of such a variable is restricted to the statement or block following the for() statement. This results in code which looks like this:


	for(int x=0; x<10; x++) {
	   System.out.println("Value of x is:" + x);
	}

It might be useful to look at the equivalent of this code implemented using a while() loop.


	{
	   int x=0;
	   while (x<10) {
		System.out.println("Value of x is:" + x);
		x++;
	   }
	}

The for() loop and the Comma Separator

The init_statement and the iter_expression parts described previously can contain a sequence of expressions rather than a single one. The expressions should be separated by a comma, instead of a semi-colon. This is illustrated below.


	for ( j=3, k=6; j+k < 20; j++, k+=2) {
	    System.out.println("J is: " + j + "K is: " + k);
	}

Note that while you can use the comma to separate several expressions, you cannot mix expressions with variable declarations. So the following would be illegal:


	int i;
	for (i=7, int j=0; i < 20; j++) {  	// illegal !
	    System.out.println("i is: " + i + "j is: " + j);
	}

Back to TOP

The break and continue Statements in Loops

Sometimes you need to abandon the execution of the body of a loop - or perhaps a number of nested loops. The Java development team recognized this as a legitimate use for a goto statement. Java provides two statements, break and continue, which can be used instead of goto to achieve this effect.

Using continue

Suppose you have a loop that is processing an array of items that contains two String references. The first String is always non-null, but the second might not be present. To shift the control in a loop, we can use continue, like this:


	for (int i=0; i<array.length; i++)  {
	   //process first string
	   if (array[i].secondString == null) {
		continue;
	   }
	}

The real strength of continue is that it is able to skip out of multiple levels of loops. Consider another example. Suppose our example, instead of using two Strings, used two arrays of char values. We need to nest these loops like this:

	mainLoop:   for (int i=0; i<array.length; i++) {
	   // process first array 
	   for (int j=0; j<array[i].secondArray.length; j++) {
		if (array[i].secondArray[j] == '\u0000') {
		     continue mainLoop;
		}
	   }
	}

Notice that a label has been applied to the opening for() loop. Here, when the processing of the second array comes across a zero value, it abandons the whole processing not just for the second array, but for the current object in the main array. This is equivalent to jumping to the statement i++ in the first for() statement.

To achieve this without using continue is possible if we set a flag in the inner loop and use that to abandon the outer loop processing, which is rather messy.

Using break

The break statement, when applied to a loop, is somewhat similar to the continue statement. However, instead of prematurely completing the current iteration of a loop, break causes the entire loop to be abandoned. Consider this example.


	for (int j=0; j<array.length; j++ )  {
	    if (array[j] == null) {
		break;  	// breaks out of inner loop
	    }
	    // process array[i]
	}

In this case, instead of simply skipping processing for array[j] and proceeding directly to processing array[j+1], this version quits the entire inner loop as soon as the null element is found.

You can also use labels on break statements, and as before, you must place a matching label on one of the enclosing blocks.

Back to TOP

The Selection Statements

Java provides a choice of two selection constructs. These are the if()/else and switch() mechanisms. If there are two sub paths an execution can travel, you can use the if()/else statement which is controlled by a boolean expression. If you want multiple execution paths, then you can use the switch() construct.

The if()/else Construct

The most important thing to remember about an if()/else statement is that it takes a boolean argument. For example:


	if (x>5) {
	   System.out.println("x is more than 5");
	}
	else {
	   System.out.println("x is not more than 5");
	}  

The else part can be thought of as optional since an if() statement can exist by itself. And you can also use this in a nested fashion.

The switch() Construct

If you need to make a choice between multiple alternative execution paths, and the choice can be based on an int value, you can use the switch() construct. Consider this example:


	switch (x) {
	   case 1:
		System.out.println("Got a One");
		break;
	   case 2:
		System.out.println("Got a Two");
		break;
	   case 3:
	   case 4:
		System.out.println("Got a Four");
		break;	
	   default:
		System.out.println("Something other than 1,2,3 or 4");
		break;
	   }

There are a few important things to remember while using switch() and they are:

  • One of the important things to remember about switch() is that variable x must be either byte, short, char or int . It cannot be anything else.
  • Another is the arguments to the case labels must be constants, or at least constant expressions that can be fully evaluated at compile time. You cannot use a variable or an expression involving variables.
  • Each case label takes only a single argument, but when execution jumps to one of these labels, it continues downward until it reaches a break statement. If the break statement is not there at the end of each block of statements it would execute all the cases following the one that was matched. So it is essential to place the break statements.
  • The default statement can be compared to the else part of an if()/else construction. Execution jumps to the default statement if there are no matching cases in the switch(). And although the default statement is shown at the end of the switch construct there is no rule that requires this placement. Default can be placed anywhere in a switch().

Back to TOP

Now on to the next chapter Your first Java Applet and Applications


All questions and comments can be addressed to the author.
All material appearing within this website is copyright protected and may not be reproduced elsewhere without the express written permission of the author (Sanjeev Dasgupta)

 


Support this site BUY from Sunncity Gift Store