[Top bar]
[Bottom bar]
[Photo of the Author]
by Jose M. Fernández
<fgcia(at)correoweb.com>



Translated to English by:
Javier Cano <jcano(at)iti.upv.es>

Content:

 

Programming with Java, part II

[Ilustration]

Abstract:

This series of articles have as a model the classical books of structured programming. After a short introduction to Java language characteristics in the first article of the series, we now continue with the study of data types, variables, control statements, etc. This will lead us to the most important topic which is classes. The concept of classes is at the root of this programming language. Into account that classes are the basis of this programming language.

_________________ _________________ _________________

 

Lexical issues

Formally speaking, a Java program is formed by a set of comments, identifiers, literals, separators, blank spaces and key words. The compiler receives code written in a unique format, expanding the number of bits from 8 of ASCII to 16 and, consequently, also widening the keymap table to fit with those characters of the no latin languages. Blank spaces, tabs and carriage returns are erased by the compiler as they do not belong to the symbol set. This provides Java programs with free writing style.

Java comments can be in one of the next three styles:

  1. //comments
    Characters from // until end of line are ignored.
  2. /* comment */
    All characters between /* and */ are ignored. These comments can have an extension of several lines.
  3. /** comment **/
    Same as /* */ comments, but these should only be used before the declarations because the tool javadoc uses them to automatically create the documentation.

Identifiers are the names given to the variables, classes and methods to identify them to the Compiler. It is possible to use any string of letters (upper and lower case), numbers and the underline and dollar symbols. They can not begin with a number.

Java uses some special characters as separators. The most frequently used is the ; separator, but we will also find:

Symbol Description
() It holds parameter lists on the definition and call to the methods. It is also used to change the expressions precedence, or to contain expressions in a control sentence. and in type conversion.
{} To contain the values of the automatically initialized vectors. To define a code block, to use with classes, methods and local scope
[] To define matrix types. To reference values in a matrix
; Sentence separator.
, To separate consecutive identifiers in the variable declaration. To separate sentences in a FOR sentence.
. To separate packet, sub packet and classes names. To separate a variable or method from a referenced variable.
The keywords are identifiers used by the Java language and cannot be used in any other way from that defined by Java. The next table shows all the Java keywords:
abstract double int super
boolean else interface switch
break extends long synchronized
byte false native this
byvalue final new threadsafe
case finally null throw
catch float package transient
char for private true
class goto protected try
const if public void
continue implements return while
default import short
do instanceof static
 

Data types, Variables, Operators.

At this point I will insist on saying that Java is a hard typed language, each variable has a type and each type is tightly defined. All assignment type compatibility is tested, both explicitly or through parameter interchange in the calls to methods. The compiler checks all the expressions and parameters to ensure type compatibility.

In the previous article we mentioned that Java was completely object oriented, nevertheless, due to efficiency considerations, Java defines eight "simple" data types that are not objects. Besides, for portability reasons, all data types have a very defined range.

Simple data types can be distributed in four groups:

TYPE NAME SIZE RANGE
Integer long 64 -9.223.372.036.854.775.808
a
9.223.372.036.854.775.807
int 32 -2.147.483.648
a
2.147.483.647
short 16 -32.768 a 37.767
byte 8 -128 a 127
Floating point float 32 3.4 e-038 a 3.4 e+038
double 64 1.7 e-308 a 1.7 e+308
Character char 16 Unicode
Boolean boolean true o false

Variables:

A variable is defined by an identifier and a type. Alternatively, we can declare and initialize the variable at the same time. Every variable has a scope and a lifetime. Besides, they must be declared before they can be used. They can be declared at any place in the program, even at the same moment of being used for the first time.

Generally, a variable is declared in this way:

identifier type [=value] [,identifier [=value]......];


where "type" can be a basic Java type or a class, even an interface. If we initialize the variable, its expression must be of the same type or a compatible type with that specified at the variable declaration.

Examples:

int  a = 4, b, c=7;

char c;

myclass class;


As a rule, the variable defined inside a scope it is not accessible by the code outside (we define scope as the code portion between braces {}). A variable will not hold its value once exited from its scope.

Most of the programming languages define two scope categories, global and local. But this does not fit well with the Java object oriented model. On this model, the two main scopes are those defined by a class and a method.

Type conversion:

Java allows, although theory denies it, to assign a value of a specific type to a variable of another type. If the types are compatible, an automated conversion is carried out and if not, it is always possible to perform an explicit type conversion between incompatible types. In order to achieve an automatic conversion of types, it is required that: For example, an int type is big enough to store a byte type, so it doesn't need an explicit conversion. Number types are not char or boolean compatible, and these last are neither compatible with the rest of them. If we wanted to assign an int value to a byte, we should make use of an explicit conversion with the next format:
(type) value
where type indicates the type conversion target. Example:
int a;

char b;

a=(int) b;


We must take care with the automated type conversion, as this conversion can carry out a leak of information. For example:

At the conversion of a floating point type to an integer type, it loses the fraction component:

int a;

double d= 125.43;

a=(int) d;
the variable has the value 125.
byte   b;

int i=257;

b=(byte) i;


b has the value 1, that results from dividing 257 by 256 where 256 is the range of the byte type.
byte b;

double d= 340.123;

b=(byte) d;
where b will have a value of 84;
All these conversions are carried out with the compiler and the interpreter , which do not come up with any compile errors.

Operators:

There is a wide set of operators that can be divided into four groups: arithmetic, bit level, relationals and logic. As a general rule, they will work exactly the same as in other languages, but there are some little differences that will be discussed here.
Arithmetic Operators:
Operator Description
+ Addition
- Subtraction
* Multiplication
/ Division
% Modulo (division remaining)
++ Increment
+= Addition and assignment
-= Subtraction and assignment
*= Multiplication and assignment
/= Division and assignment
%= Modulo and assignment
-- Decrement
The modulo operator can be applied, both to integer and floating point values. For example:
int a=38;

double d=41.95;

int c=a%10;<

double  e=d%10;
The c variable has a 8. The e variable has a 1.95;

The operators with assignment are useful at constructions such as:

a = a + 4; equivalent to a+=4;

a = a % 2; equivalent to a%=2;

Normally, we can say that sentences like:

var=var op expression; can be replaced with var op= expression;

Bit level operators:
There are some defined bit level operators that can be applied to integer, long, int, short, char, byte types, modifying their operand bits.
Operator Description
~ Unary NOT at bit level
& AND at bit level
| OR at bit level
/\ Exclusive OR exclusive at bit level
>> Right shift
>>>> Right shift filled with zeroes
<< Left shift
&= AND at bit level and assignation
|= OR at bit level and assignation
/\= exclusive OR at bit level and assignation
>>= Right shift and assignation
>>>>= Right shift filled with zeroes and assignation
<<= Left shift and assignation
Relational operators:
They determine the relation between two operands, specifically testing if one is equal, greater or lower than the other. The result is a boolean value.
Operator Description
= = Equal to
!= Different from
> Greater than
< Lower than
>= Equal or greater than
<= Equal or lower than
Unlike other languages(C/C++), the boolean values are True and False, that is, they are not numeric values..
Boolean logic operators:
They only work with boolean operators and also produce another boolean operator.
Operator Description
& logic AND
| logic OR
/\ logic XOR(exclusive OR)
|| shortcut OR
&& shortcut AND
! unary logic NOT
&= AND assignment
|= OR assignment
/\= XOR assignment
= = Equal to
!= Not equal to
?: Ternary If-then-else ternary
The OR shortcut operator has as a result: true, when the first operator is true, whichever the second operator value. Equally, the AND shortcut operator is false when the first operator is false, whichever the other operator.

The general format of the ternary operator is:

Expession1 ? expession2 : expression3

If Expression1 is true, expresion2 will be executed; if it is false, expression3 will be executed.

Operators precedence:

Higher
( ) [ ] .
++ -- ~ !
* / %
+ -
>> >>>> <<
> >= < <=
= = !=
&
'
|
&&
||
?:
= Op=
Lower
 

Flow-Control sentences

They can be divided into three groups: Selection, Iteration and Jump.
Group Sentence Description
Selection if
if ( condition )

  sentence1;

else

  sentence2;
various if's
If (condition )

  sentence;

else if (condition)

  sentence;

else if (condition)

  sentence;

.

.

else

  sentence;
switch
switch (expression){

  case value1:

    sentence;

    break;

  case value2:

    sentence;

    break;

    .

    ...

  default :

    sentence;

}
Iteration while
while (condition) {

  sentence;

}
do while
do {

  sentences;

} while (condition)
for
for (initialization, condition, iteration) {



  sentences;

}
Jump break To exit from a switch.

To exit from a loop

continue Exits from the current loop iteration but continues in the same loop
return Return explicitly from a method
 

Classes, Methods, Inheritance

Java is a language that was designed from scratch, as we remarked in the previous article, and this is the reason for the language being a close, clear and useful implementation of an object oriented programming language. Consequently, all Java programs are object oriented. You must realize that you will not find any object oriented programming teaching in this series of articles. For that purpose you can find a lot of great literature, from the basics up to the most skillful programming. Because OOP (object oriented programming) is so fundamental to Java, you should understand it before beginning to program in this language. We are forced to use and handle some basic elements of this paradigm, although we always will follow the Java terminology and we will try to define the elements that make Java a complete object oriented language in the shortest and clearest way.

CLASSES :

It is the Java kernel. It defines the shape and nature of an object, and it represents the basis of object oriented programming. A new data type comes defined by a class, and this new type can be used to create objects of that type.

A class is a model (pattern) for an object, and an object is an instance of a class. Java does not support global functions or variables, so all program actions (methods) have to be defined into a class.

A class is defined using the reserved word "class". A common class definition could be:

Class class_name {

  Type instance1_of_variable;

  Type instance2_of_variable;

  .

  .

  ...

  type instance_variable;

  type method_name(parameter_list){

    //body of the method

  }

  .

  .

  ..

  type name_of_the_method(parameter_list){





    //body of the method

  }

}
The variables or data defined into a class are called variables of instances. The methods contain the code, and they define how the data of a class can be used.

Two steps are required in order to get the objects of a class:

  1. To declare a variable of the type of the class. This variable does not define an object, it is a variable which we can use to reference an object.
  2. To dynamically assign memory to the object and get a reference of it.
This second step it is done by means of the new operator. Its general aspect is:


variable = new name_of_the_class();
Where "variable" is a variable of the class that we want to create and name_of_the_class is the name of the class which is being instanciated. The creation of an object could be graphically represented as:

METHODS:

The general format of a method is:
type name_of_the_method (parameter_list) {



  //method_body

}
"type" is the returned type by the method; it can be any valid type, included class types, or it can even not return any value (void).

The parameter list is a pair sequence of type-identifier, separated by a colon. The parameters are variables that receive the value of the arguments given to the method. If the method has no parameters, then the list will be empty.

Methods returning a value different from "void" use:



return valor;
Where value is the returned value.

Java provides methods with plenty of flexibility and power, so from here, up to the end of the article, we will review each of the most important aspects of methods.

However, before going on, let's review all the previous concepts with a simple example.

Let's create a class to calculate the capacity of a rectangular box (like a pool):

Code Comments
class capacity {

    double length;

    double width;

    double height;

    void CalcVolume () {

        double volume ;

        volume = length*width*height;

        System.out.println("Volume: " +

                           volume);

    }

}
As you can see, we have defined a class named "capacity" which holds three instance variables: length, width and height. It is also defined a method that calculates the recipient volume. We will call capacity.java to the source file. When you compile it, a capacity.class class will be created.
This class on its own does not carry any action, like an applet, neither has the main() method to be executed from the command line. We have created a template that enables us to create objects (instances) of this class. To perform this target, we will create a class that can be executed from the command line.
Code Comments
class example {

  public static void main(String Arg[]){

    capacity p1=new capacity();

    capacity p2=new capacity();

    p1.length = 16;

    p1.width=3;

    p1.height=2;



     //

    p2.length = 25;

    p2.width=6;

    p2.height=3;



    //

    p1.CalcVolume();



    //

    p2.CalcVolume();



  }



}


Two variables of the capacity type are defined, p1, p2. With the new operator we create two objects of the capacity type, which can be referenced through the p1, p2 variables.

Afterwards, we assign values to each one of the variables of the created objects

We call the method CalcVolume() of the referenced object in p1, and as a result a:

"Volume: 96", will be shown on the screen.

The same to the object referenced in p2. A:

"Volume: 450", will be shown on the screen.

When p1.CalcVolume() is executed, the Java interpreter transfers the control to the code defined inside CalcVolume(). Once all control sentences have been executed it returns to the calling routine and the execution continues on the next line to the call.

Methods with parameters. Value return.

The majority of the methods are used with parameters that allow one to generalize them. Besides, methods can also return values, so we can build methods that can work with a wide range of data that can be used in different situations.

We will improve our example:

Code Comments
class capacity {

  double CalcVolume (double l,

                     double a,

                     double p) {

    double volume=l*a*p ;

    return volume;

  }

}
The CalcVolume method has been modified to receive three parameters. It is also defined to return a double type. This action is performed by the return volume instruction.
class example {

  public static void main(String Arg[]){

    capacity p1=new capacity();

    capacity p2=new capacity();



    double vol;



    vol=p1.CalcVolume(10,3,2);



    System.out.println("Volume: " + vol);



    //



    vol=p2.CalcVolume(25,5,2);



    System.out.println("Volume: " + vol);

  }

}


The call to the method is done sending the desired parameters. It returns the value of the method in the vol variable, which must be the same type that the method.
A main aspect of classes are constructors. These items define what happens when an object of a class is created. Most of them explicitily define its own constructors into the class definition. If not defined at this point, Java uses a default constructor (as in our example).

A constructor included in a class has exactly the same name as the class. Its syntax is similar to that of a method. It is automatically executed after creating the object, before the new operator finishes.

Constructors do not return any type, as implicitly they return the type of the class. The constructor carries on with the initialization of the all the object state. By this way, the code that creates an instance of the object has a ready object to make use of it. As default, instance variables are initialized by constructors. As it happens with methods, constructors can have parameters to make them more useful. Let's modify our example to contemplate all this new aspects.

Code Comments
Class capacity {

  double length;

  double width;

  double height;



  //

  capacity(double l,

           double a,

           double p){

    length=l;

    width=a;

    height=p;

  }



  //

  void CalcVolume () {

    double volume ;

    volume=large*wide*high;

    return volume;

  }

}


A constructor is added to the class, that has the aspect of a method with the same name that the class, but without any type. This constructor initializes the instances of the variables declared with the received arguments.
class example {

  public static void main(String Arg[]) {

    capacity p1=new capacity(10,5,2);

    capacity p2=new capacity(25,6,2);

    double vol;

    vol=p1.CalcVolume();

    System.out.println("Volume: " + vol);



    //

    vol=p2.CalcVolume();

    System.out.println("Volume: " + vol);

  }

}
The new operator creates the instances of the class, passing the needed parameters to the constructor of that class.
When no reference to an object exists, it is assumed that this object is not going to be used anymore, so the memory allocated to it gets freed. There is no explicit need to destroy the objects because this process is carried out in an automated way through the program execution.

Nevertheless, the method "finalize()" is used to add a finisher to a class. This method is executed when the interpreter destroys the object. In this method we will include the actions to get executed before destroying the object.

Method Overload.

Polymorphism (multiple methods for one interface) is one of the basic PILLARS in OOP. Java implements polymorphism by means of the method overload.

Different methods with the same name can be defined in a class, but they must have a different parameter list or at least different returned types. When an overloaded method is called the Java interpreter uses the type and/or the arguments passed with the call, to distinguish the version of the method to be used.

Each version of the overloaded method can perform different tasks, although this makes polymorphism completely impossible, so method overload should preferably imply some relation. Same way methods get overloaded, constructors can also be overloaded.

Argument passing.

Generally, programming languages allow two different ways to pass the arguments:

Java supports two means of passing parameters to the methods. If the argument has a simple type, it is passed by value, but when the argument is an object it is passed by reference.

Access Control.

Another basic PILLAR in OOP is ENCAPSULATION. It means linking data with the code that uses it. Moreover, ENCAPSULATION provides access control, that is to say, a part of the program has the ability to access the members of a class.

Access specifications in Java are 'public', 'private' and 'protected'.

When a method or a instance-variable is defined as 'public', it is accessible from any part of the program. If defined as 'private', it is only accessible by other methods of its own class. By default all methods or instance-variables (members) are 'public' (to its own packet¿?).

The 'protector' specifier is used when working with HERENCIAS(inheritances¿?), which is the next topic.

INHERITANCE.

The three elements that define OOP are polymorphism, encapsulation, and last of all inheritance. A general class (superclass) can be defined through this. This generic class, with a set of common characteristics, can be inherited by other more specific classes, each adding some other particular characteristic.

To achieve a class B inheriting the properties of an A superclass, we will put in the B definition:

Class B extends A {

    // class definition

}
Class B includes all members of A superclass, being this last class an AUTONOMOUS class with the ability to be used freely. Also, class B can be the superclass of any other class.

Java does not allow multiple inheritance from different superclasses to one subclass.

If there are members defined as 'private' in the superclass, they can not be accessed by the inherited classes. When a subclass needs to reference an immediate superclass, the reserved word 'super' can be used. In that way we can call the builder/constructor or members of the superclass that have been hidden by the subclass.

When a method of a subclass has the same name and type as the method of the superclass, it is said that the method is overwritten. This property establishes the base of one of the more powerful aspects of Java, named "Dynamic selection method". This means that the decision of which method should be used on each call, is performed at execution time in place to depend on the referenced variable type.

In the next article we will see all the power of inheritance, with abstract classes, interfaces, etc.

 

References


Webpages maintained by the LinuxFocus Editor team
© Jose M. Fernández, FDL
LinuxFocus.org
Translation information:
es --> -- : Jose M. Fernández <fgcia(at)correoweb.com>
es --> en: Javier Cano <jcano(at)iti.upv.es>

2002-11-03, generated by lfparser version 2.34