| CS 535: Object-Oriented Programming & Design |
|
---|
Fall Semester, 1997
Doc 4, Classes
To Lecture Notes Index
San Diego State University -- This page last updated 27-Sep-97
Contents of Doc 4, Classes
- References
- Classes
- Basic Terms
- Fields
- Initializing Fields
- this
- Access Levels for Fields and Methods
- Object Variables are References
- Parameter Passing - By value only
- Static Fields and Methods
- Constants
- Class Names, Packages and Import
The Java Programming Language, Arnold, Gosling, 1996 Chapter 2, 10
Sun Java Compiler V 1.1.3
Language Level Definition
Conceptual Level Definition
Java | field | method |
C++ | data member | member function |
Smalltalk | instance variable | method |
C | ??? | function |
Class member refers to either a field or method
class BankAccount
{
public float balance = 0.0F; // field
public void deposit( float amount ) // method
{
balance += amount ;
}
}
Comparisons to C++
Similar class syntax and structure
No multiple inheritance, uses interfaces instead
Functions are virtual by default
Constructors are much simpler
Destructors are not need
Packages provide separate name spaces for classes
class BankAccount
{
public float balance = 0.0F;
}
class RunBank
{
public static void main( String args[] )
{
System.out.println( " Start main " );
BankAccount richStudent = new BankAccount( );
richStudent.balance = (float) 100000;
BankAccount poorInstructor = new BankAccount( );
poorInstructor.balance = 5.10F;
System.out.println( "Student Balance: " + richStudent.balance );
System.out.println( "Prof Balance: " + poorInstructor.balance );
}
}
Output
Start main
Student Balance: 100000
Prof Balance: 5.1
Running Above Program
Put classes BankAccount and RunBank in file BankExample.java
rohan 50-> ls
BankExample.java
rohan 51-> javac BankExample.java
rohan 52-> ls
BankAccount.class BankExample.java RunBank.class
rohan 53-> java RunBank
Start main
Student Balance: 100000
Prof Balance: 5.1
Note
- Each class compiles to its own .class file
- Execute the class with the main program
Multi-File Programs
Put class BankAccount in file BankAccount.java
Put class RunBank in file RunBank.java
Compile top level class - javac will compile needed classes
Example
rohan 13-> ls
BankAccount.java RunBank.java
rohan 14-> java -cs RunBank
Start main
Student Balance: 100000
Prof Balance: 5.1
rohan 15-> ls
BankAccount.class RunBank.class
BankAccount.java RunBank.java
Methods
class BankAccount
{
public float balance = 0.0F;
public void deposit( float amount )
{
balance += amount ;
}
public String toString()
{
return "Account Balance: " + balance;
}
}
class RunBank
{
public static void main( String args[] )
{
BankAccount richStudent = new BankAccount( );
BankAccount poorInstructor;
poorInstructor = new BankAccount( );
richStudent.deposit( 10000F);
poorInstructor.deposit( 5.10F );
System.out.println( "Student Balance: " + richStudent.balance );
System.out.println( "Prof: " + poorInstructor.toString() );
}
}
Output
Student Balance: 10000
Prof: Account Balance: 5.1
The toString() Standard
- When required Java sends toString() message to objects to convert them to
strings
- This happens in println() and when adding to strings
class RunBank
{
public static void main( String args[] )
{
BankAccount richStudent = new BankAccount( );
BankAccount poorInstructor = new BankAccount( );
richStudent.deposit( 10000F);
poorInstructor.deposit( 5.10F );
String profBalance = "Prof: " + poorInstructor;
System.out.println( profBalance );
System.out.println( "Prof: " + poorInstructor );
System.out.println( richStudent );
}
}
Output
Prof: Account Balance: 5.1
Prof: Account Balance: 5.1
Account Balance: 10000
Three ways:
- Direct Assignment
- Instance Initializer Block
- Constructors
Direct Assignment
class BankAccount
{
public float balance = 0.0F;
public void deposit( float amount )
{
balance += amount ;
}
public String toString()
{
return "Account Balance: " + balance;
}
}
Initializing FieldsInstance Initializer Block
New to Java 1.1
class TaxAccount
{
public float balance;
{ //Instance Initializer block
float baseTaxRate = .33F;
float companySize = 1.24;
balance = baseTaxRate * companySize -
Math.sin( baseTaxRate ) + .013f;
}
public static void main( String[] args ) //Test method
{
TaxAccount x = new TaxAccount();
System.out.println( x.balance);
}
}
Output
0.09815697
Initializing Fields - Constructors
Constructors
have the same name as their class
Constructors have no return type
Constructors have zero or more arguments
Constructors are not methods!
Constructors are called when objects are created
Constructor Example
class BankAccount
{
public float balance;
public BankAccount( float initialBalance ) //Constructor
{
balance = initialBalance;
}
public void deposit( float amount )
{
balance += amount ;
}
public String toString()
{
return "Account Balance: " + balance;
}
}
class RunBank
{
public static void main( String args[] )
{
BankAccount richStudent = new BankAccount( 10000F );
BankAccount poorInstructor = new BankAccount( 5.10F );
System.out.println( "Prof: " + poorInstructor );
System.out.println( "Student: " + richStudent );
}
}
Output
Prof: Account Balance: 5.1
Student: Account Balance: 10000
Multiple Constructors
class ConstructorExample {
public ConstructorExample( ) {
System.out.println( "In constructor - no argument" );
};
public ConstructorExample( int size) {
System.out.println( "In constructor - one argument" );
};
public void ConstructorExample( ) {
System.out.println( "return type means it is ");
System.out.println( "not a constructor " );
};
}
class TestConstructor {
public static void main( String args[] ) {
System.out.println( " Start main " );
ConstructorExample test = new ConstructorExample( );
ConstructorExample x = new ConstructorExample(5);
System.out.println( " Done with Constructors " );
test.ConstructorExample ();
}
}
Output
Start main
In constructor - no argument
In constructor - one argument
Done with Constructors
return type means it is
not a constructor
Implicit Constructors
If a class has no constructor compiler generates an implicit constructor with
no arguments
class ImplicitConstructorOnly {
int size = 5;
}
class OneConstructor {
OneConstructor( String message ) {
System.out.println( message );
}
}
class TwoConstructors {
TwoConstructors ( String message ) {
System.out.println( message );
}
TwoConstructors ( ) {
System.out.println( "No argument Constructor" );
}
}
class Constructors {
public static void main( String args[] ) {
ImplicitConstructorOnly ok = new ImplicitConstructorOnly();
TwoConstructors alsoOk = new TwoConstructors();
OneConstructor compileError = new OneConstructor();
}
}
Order of Class Elements
Layout
The compiler will let you order the element of a class nearly any order!
Fields must be declared before they are used in an initializer block
Human readers require some consistency
Guidelines suggest placing all field declaration in one location
- place all fields at the beginning of the class
or
- place all fields at the end of the class
Layout Example
class OrderExample
{
public OrderExample()
{
a = 8; // OK
}
{
a = 4; // Compile Error
}
int a = 1;
public int getC()
{
return c;
}
int c = 3;
{
a = 4;
}
}
Order of Initialization
Direct assignment of fields and instance initializer blocks are done in order
from top to bottom of class, then the constructor is run
class OrderExample
{
int aField = 1;
{
System.out.println( "First block: " + aField );
aField++;
}
public OrderExample()
{
System.out.println( "Start Constructor: " + aField );
aField++;
System.out.println( "End Constructor: " + aField );
}
{
System.out.println( "Second block: " + aField );
aField++;
}
public static void main( String args[] )
{
OrderExample test = new OrderExample();
}
}
Output
First block: 1
Second block: 2
Start Constructor: 3
End Constructor: 4
Overloading Methods
The signature of a methods is its name with number, type and order of its
parameters.
The return type is not part of the signature of a method.
Two methods in the same class can have the same name if their signatures are
different.
class OverLoad
{
public void same() {
System.out.println( "No arguments" );
}
public void same( int firstArgument ) {
System.out.println( "One int arguments" );
}
public void same( char firstArgument ) {
System.out.println( "One char arguments" );
}
public int same( int firstArgument ) { // Compile Error
System.out.println( "One char arguments" );
return 5;
}
public void same( char firstArgument, int secondArgument) {
System.out.println( "char + int arguments" );
}
public void same( int firstArgument, char secondArgument ) {
System.out.println( "int + char arguments" );
}
}
(not that)
this
- Refers to the object on which the method operates
-
- Used to return self from method and pass self as a parameter
-
- Java's this differs from C++'s this
-
- The difference occurs with inheritance
class BankAccount
{
public float balance;
public BankAccount( float initialBalance )
{
this.balance = initialBalance;
}
public void deposit( float amount )
{
balance += amount ;
}
public String toString()
{
return "Account Balance: " + balance;
}
}
Returning this
class BankAccount
{
public float balance;
public BankAccount( float initialBalance )
{
this.balance = initialBalance;
}
public BankAccount deposit( float amount )
{
balance += amount ;
return this;
}
}
class RunBank
{
public static void main( String args[] )
{
BankAccount richStudent = new BankAccount( 10000F );
richStudent.deposit( 100F ).deposit( 200F ).deposit( 300F );
System.out.println( "Student: " + richStudent.balance );
}
}
Output
Student: 10600
this as Parameter
A Convoluted Contrived Example
class CustomerList
{
public BankAccount[] list = new BankAccount[ 100 ];
public int nextFreeSlot = 0;
public void add( BankAccount newItem )
{
list[ nextFreeSlot++ ] = newItem;
}
}
class BankAccount
{
public float balance;
public BankAccount( float initialBalance )
{
this.balance = initialBalance;
}
public void badBalanceCheck( CustomerList badAccounts )
{
if ( balance <= 0F ) badAccounts.add( this );
}
}
class RunBank
{
public static void main( String args[] )
{
BankAccount richStudent = new BankAccount( 10000F );
CustomerList customersToDrop = new CustomerList();
richStudent.badBalanceCheck( customersToDrop );
}
}
this and Chaining Constructors
this with argument list as first line of a constructor will call another
constructor of the same class
class ThisAndConstructors
{
public ThisAndConstructors()
{
this( 5 );
System.out.println( "No argument");
}
public ThisAndConstructors( int a)
{
this( a, 10 );
System.out.println( "One argument");
}
public ThisAndConstructors( int a, int b)
{
System.out.println( "Two arguments");
}
public static void main( String args[] )
{
new ThisAndConstructors();
}
}
Output
Two arguments
One argument
No argument
Finalize - Destructor of Sorts
Automatic storage management handles reclaiming objects and arrays that are no
longer needed by a running program
When an object is determined to no longer be needed it may be reclaimed, unless
it has a finalize method
If a class has a finalize method, the method is executed. The object is
reclaimed the next time it is determined the object is no longer needed
The finalize method is never called more than once for an object
In Java 1.0.2 it was hard to insure that finalize would be called
Java 1.1 introduced the method:
- System.runFinalizersOnExit(boolean)
-
- default value is false - do not run finalize on exit
You can now force finalize to run when your program exits
Finalize Example
class Death
{
int myId;
public Death ( int sequenceNumber)
{
myId = sequenceNumber;
}
public void finalize( )
{
System.out.println( myId );
}
}
class Finalization
{
public static void main( String args[] )
{
Death sampleObject;
for ( int k = 0; k < 5; k++ )
sampleObject = new Death( k );
}
}
No Output
Finalize Examples
class FinalizationOnExit
{
public static void main( String args[] )
{
System.runFinalizersOnExit(true);
Death sampleObject;
for ( int k = 0; k < 5; k++ )
sampleObject = new Death( k );
}
}
Output
0
1
2
3
4
class FinalizationForced
{
public static void main( String args[] )
{
Death sampleObject;
for ( int k = 0; k < 5; k++ )
sampleObject = new Death( k );
System.gc();
System.runFinalization();
}
}
Output
0
1
2
3
public
- Members declared public are accessible anywhere the class is accessible
-
- Inherited by all subclasses
protected
- Members declared protected are accessible to an inherited by subclasses, and
accessible by code in the same package
private
- Members declared private are accessible only in the class itself
If no access level is given
-
- Members declared with on access modifier are accessible only to code in the
same package
Public, Protected, Private
class AccessLevels
{
public int publicObjectVariable ;
protected float protectedObjectVariable = 10;
private int[] privateObjectVariable;
int noExplicitAccessLevel = publicObjectVariable;
public AccessLevels ( int startValue )
{
System.out.println( " Start Constructor" );
privateObjectVariable = new int[ startValue ];
}
public void sampleMethod( int value )
{
System.out.println( " In method" );
privateObjectVariable[ 1 ] = value;
}
}
class TestAccessLevels
{
public static void main( String args[] )
{
AccessLevels test = new AccessLevels ( 11 );
test.publicObjectVariable = 100; // Ok
test.protectedObjectVariable= 100; // Ok
test.privateObjectVariable[ 1 ] = 100; // Compile Error
test.noExplicitAccessLevel = 100; // Ok
}
}
Look Ma, Hidden Methods
class Hide
{
public void publicAccess()
{
System.out.println( "Start public access " );
internalWorker();
realPrivateWorker();
}
protected void internalWorker()
{
System.out.println( "In internal worker " );
}
private void realPrivateWorker()
{
System.out.println( "In Private worker " );
}
public static void main( String[] args )
{
Hide me = new Hide();
me.publicAccess();
}
}
Output
Start public access
In internal worker
In Private worker
Better BankAccount
class BankAccount
{
private float balance;
public BankAccount( float initialBalance )
{
balance = initialBalance;
}
public void deposit( float amount )
{
balance += amount ;
}
public String toString()
{
return "Account Balance: " + balance;
}
}
Modular Design Rules
Measure Twice, Cut Once
Object-Oriented programming requires more early planning then procedural
programming
Supports:
Decomposability Composability Protection Continuity Understandability
Poker Rule: Hide Your Cards
or
Information Hiding
All information about a module should be private unless it is specifically
declared public
Supports:
Decomposability Composability Continuity Understandability
Two Views on Hidden fields
- All fields should be protected or private to outside access
- All fields should be hidden from methods in same class
class BankAccount
{
private float balance;
protected float getBalance()
{
return balance;
}
protected void setbalance( float newBalance)
{
balance = newBalance;
}
public BankAccount( float initialBalance )
{
setbalance( initialBalance );
}
public void deposit( float amount )
{
setbalance( getBalance() + amount );
}
public String toString()
{
return "Account Balance: " + getBalance();
}
}
class Student
{
public char grade;
}
class PointerTest
{
public static void main( String args[] )
{
Student sam = new Student();
sam.grade = 'A';
Student samTwin = sam;
samTwin.grade = 'C';
System.out.println( sam.grade );
}
}
Output
C
class Parameter
{
public void noChangeArgument( char grade )
{
grade = 'A';
};
};
class TestParameter
{
public static void main( String args[] )
{
char samGrade = 'C';
Parameter Test = new Parameter();
Test.noChangeArgument( samGrade );
System.out.println( samGrade );
};
};
Output
C
How to get Functions to return values
- Use return
- Use references (arrays or objects)
class TestParameter
{
public static void main( String args[] )
{
char samGrade = 'C';
samGrade = inflateGrade( samGrade );
System.out.println( samGrade );
};
public static char inflateGrade( char grade )
{
switch ( grade )
{
case 'A':
case 'B':
return 'A';
case 'C':
return 'B';
case 'D':
case 'F':
return 'C';
};
return 'A';
};
};
Output
B
How to get Functions to return values
Using References - Array
class TestParameter
{
public static void main( String args[] )
{
char[] classGrades = new char[ 30 ];
classGrades[ 1 ] = 'B';
TestParameter.changeArgument( classGrades );
System.out.println( classGrades[ 1 ] );
};
static public void changeArgument( char[] grade )
{
grade[1] = 'A';
};
};
Output
A
How to get Functions to return values
Using References - Class
class Student
{
public char grade;
};
class Parameter
{
public void changeArgument( Student lucky )
{
lucky.grade = 'A';
};
};
class TestParameter
{
public static void main( String args[] )
{
Student sam = new Student();
sam.grade = 'B';
Parameter Test = new Parameter();
Test.changeArgument( sam );
System.out.println( sam.grade );
};
};
Output
A
Static fields are class variables
- The same static field is shared between all object of the class
-
- Static fields exist before creating an object of the class
-
- Same as C++ static data members
class StaticFields
{
public static int size = 10;
public void increaseSize( int increment )
{
size = size + increment;
}
}
class DemoStaticFields
{
public static void main( String[] arguments )
{
System.out.println( "Size " + StaticFields.size );
StaticFields top = new StaticFields();
StaticFields bottom = new StaticFields();
top.size = 20;
System.out.println( "Size " + bottom.size );
}
}
Output
Size 10
Size 20
Static Methods
class StaticMethods
{
public static int size = 10;
public static void increaseSize( int increment )
{
size = size + increment;
}
}
class DemoStaticMethods
{
public static void main( String[] arguments )
{
StaticMethods.increaseSize( 30 );
System.out.println( "Size " + StaticMethods.size );
StaticMethods top = new StaticMethods();
top.increaseSize( 20 );
System.out.println( "Size " + StaticMethods.size );
}
}
Output
Size 40
Size 60
More Static
class StaticFields
{
public static int size = 10;
protected static int[] classArray = new int [ size ];
public int localInt;
static // Run when class is first loaded
{
size = 20;
};
public static void classMethod( int value )
{
System.out.println( " In class method" );
size = value;
localInt = value; // Compile Error
}
}
class TestStaticFields
{
public static void main( String args[] )
{
StaticFields test = new StaticFields ( );
StaticFields secondObject = new StaticFields ( );
StaticFields.size = 100;
System.out.println( test.size ); // Print 100
StaticFields.localInt = 99; // Compile Error
test.localInt = 99;
}
}
Main revisited
class Top
{
public static void main( String[] arguments )
{
System.out.println( "In top main" );
Bottom.main( arguments );
}
public static void print()
{
System.out.println( "In top" );
}
}
class Bottom
{
public static void main( String[] arguments )
{
System.out.println( "In bottom main" );
Top.print( );
OddMain( arguments );
}
}
class OddMain
{
public static int main( String[] arguments )
{
System.out.println( "In odd main" );
Top hat = new Top();
String[] message = { "Hi", "Mom" };
hat.main( message );
return 5;
}
}
final variables are constants
class Constants
{
protected final int SIZE = 10;
protected final int[] CLASS_ARRAY = new int [ SIZE ];
protected final int NO_VALUE_YET; // blank final
public void aMethod( int input, final float FIXED)
{
final int NEW_FEATURE = 123;
final int ALSO_FIXED = input;
CLASS_ARRAY[ 3 ] = input;
}
public Constants( int aValue )
{
NO_VALUE_YET = aValue;
}
public static void main( String args[] )
{
Constants test = new Constants( 5);
test.aMethod( 13, 2.2f);
System.out.println( test.NO_VALUE_YET ); // Prints A
}
}
Blank Final Rules
A blank final is a final variable declaration which lacks an
initializer
A blank final must be assigned exactly once
A blank final class (static) variable must be assigned by one static
initializer block
- blank final class variable can not be assigned in more than one static
initializer block
A blank final instance variable be assigned by one non-static initializer block
or else by every constructor in the class
- A blank final instance variable can not be assigned in a non-static
initializer block and a constructor
Blank Final Rules - Examples
class StaticConstants
{
protected static final int SIZE;
static
{
SIZE = 123;
}
}
class Constants
{
protected final int SIZE;
{
SIZE = 123;
}
}
Blank Final Rules - A Slight Problem
class Constants
{
protected final int SIZE;
public Constants()
{
this( 5 );
SIZE = 123;
}
public Constants( int newSize )
{
SIZE = newSize;
}
public static void main( String args[] )
{
Constants whichOne = new Constants();
System.out.println( whichOne.SIZE );
}
}
Output
123
Initialization Order
A class is initialized when it is first "actively" used, i.e.
- A method defined in the class is invoked
-
- A constructor in the class is invoked
-
- A non-constant field in the class is accessed
A class is initialized by performing direct assignments of static fields and
static initializer blocks are done in order from top to bottom of the class
When an object is created, after the class in initialized, all instance field
are initialized by:
- performing direct assignments of non-static fields and instance initializer
blocks are done in order from top to bottom of the class, then the constructor
is executed
Initialization Order and Forward References
Don't Mix
class ForwardReferenceAndInitialization
{
public static int first = 1;
public static int second = first * 2;
public static int third = fourth - 1; // Compiler error
public static int fourth = 4;
public int fifth = 5;
public int sixth = fifth + 1;
public int seventh = eighth - 1; // Compiler error
public int eighth = 8;
}
Function Calls & Forward Reference
class ForwardReferenceAndFunctions
{
public int fifth = getSixth();
public int sixth = fifth + 1;
public int seventh = getSixth();
public int getSixth()
{
return sixth;
}
}
class Test
{
public static void main( String[] arguments )
{
ForwardReferenceAndFunctions works;
works = new ForwardReferenceAndFunctions();
System.out.println( "fifth " + works.fifth );
System.out.println( "sixth " + works.sixth );
System.out.println( "seventh " + works.seventh );
}
}
Output
fifth 0
sixth 1
seventh 1
Each class belongs to a "package"
A package creates a name space
A package defines the full name of a class
Some Standard packages
java.applet | java.awt.peer | java.net |
java.awt | java.io | java.util |
java.awt.image | java.lang | sun.tools.debug |
Example - PrintStream
PrintStream is in the java.io package
The full name of the class is java.io.PrintStream
class Output {
public static void main( String args[] ) {
java.io.PrintStream myOut = System.out;
myOut .print( "Look Mom, No System.out" );
}
}
Import Statement
The import statement allows you to shorten class names
import java.io.PrintStream;
class Output {
public static void main( String args[] ) {
PrintStream myOut = System.out;
myOut.print( "Look Mom, No System" );
}
}
import java.io.*;
class Output {
public static void main( String args[] ) {
PrintStream myOut = System.out;
myOut.print( "Look Mom, No System" );
}
}
Default Import
All classes in the java.lang are imported in all programs by default
Placing a class in a package
package sdsu.roger;
public class Sample {
public void hello() {
System.out.println( "Hello for package sdsu.roger" );
}
}
Place
program in file named "Sample.java"
Place file "Sample.java" in directory called "roger"
Place directory "roger" in directory called "sdsu"
Place directory "sdsu" in "~whitney/java/classes"
Make sure that "~whitney/java/classes" in the CLASSPATH environment variable
- setenv CLASSPATH
-
- '.:/opt/java/classes:/home/ma/whitney/java/classes'
Place the following class anywhere you like and compile
import sdsu.roger.Sample;
class TestPackage {
public static void main( String args[] ) {
Sample me = new Sample();
me.hello();
}
}
Name Collision
File SearchTree/Leaf
package SearchTree;
public class Leaf
{
public Leaf()
{
System.out.println( "Leaf in a binary search tree" );
}
}
File Botany/Leaf
package Botany;
public class Leaf
{
public Leaf()
{
System.out.println( "Leaf in a real tree" );
}
}
First Main
class Test
{
public static void main( String args[] )
{
Botany.Leaf green = new Botany.Leaf();
SearchTree.Leaf node = new SearchTree.Leaf();
}
}
Second Main
import SearchTree.Leaf;
class Test
{
public static void main( String args[] )
{
Botany.Leaf green = new Botany.Leaf();
Leaf node = new Leaf();
}
}
Third Main
import SearchTree.Leaf;
import Botany.Leaf; // Compile error
class Test
{
public static void main( String args[] )
{
Botany.Leaf green = new Botany.Leaf();
Leaf node = new Leaf();
}
}
What Should this do Main
import SearchTree.Leaf;
import Botany.*;
class Test
{
public static void main( String args[] )
{
Botany.Leaf green = new Botany.Leaf();
Leaf node = new Leaf();
}
}
Class Access Levels
Public
- Accessible to code in and outside a package
Package
- Accessible to code in package only
- No subclasses outside package
package Botany;
public class Leaf
{
public Leaf()
{
System.out.println( "Leaf in a real tree" );
}
}
package Botany;
class BotanyHelper
{
// Only code in package Botany can use this class
}
Package Notes
If a class has no declared package, it is in the unnamed package
CLASSPATH needs to point to the root directory containing the binaries of
packages
There is no requirement to have both binaries and source code of a package in
the same location.