Você está na página 1de 9

Java Objects and Classes

Bob Dickerson
March 2006
1 Objects and classes
An object is a group of data values and functions stored together. Its normal purpose is to represent some
more or less complicated data component. An object is said to have state, in other words the current values
of its data elds. The components of an object are known as its attributes or features, they are of two sorts:
data attributes
methods which is what the function attributes of an object are often called,
So an object is a grouping of code and data.
1.1 Classes, type, variables and new
All objects belong to some class which denes their attributes. The class is a type, so:
String a,b,c;
declares three variables all of type String, which is a class. It means that those variables can only hold
objects of type String. The class name is used as a generator for objects:
a = new String("goosander");
which creates a new object of type String and assigns a pointer to it to the variable a.
1.2 Accessing an attribute
A string is an object because it has attributes, it contains data values and functions:
b = a.substring(3,7);
(the indices are the start, counting from 0, and the position after the last character) which builds a new
String "sand" with characters extracted from the string object pointed to by a and assigns it to b. So all
Strings have a function inside called substring (in fact Strings contain dozens of different functions).
Attributes of an object, like substring, are accessed (in this case called) by following the name of the
variable holding the object by . and the attribute name.
1.3 Changing state
Nearly all objects can be changed. Unfortunately String objects are unusual, they cant be altered. So to
see how objects can be altered try a StringBuffer
1
:
StringBuffer dck = new StringBuffer("mall");
System.out.println(dck);
dck.append("ard");
System.out.println(dck);
this builds a single object and assigns it to dck, this is printed and produces "mall", the object is altered,
its state is changed, and then when it is printed again it will be seen to contain "mallard".
1
Why not alter Strings? Why have a separate very similar type that can be altered? Maybe Java is just stupid.
1
2 DEFINING YOUR OWN CLASSES AS A GROUP OF DATA ATTRIBUTES 2
1.4 Accessing data attributes
It is not possible to directly access the data attributes of String or StringBuffer, they are invisible, ie.
code using the object is prevented from accessing them. The only access is via the functions substring,
append and many more. However some classes do permit code using objects of their type to access and alter
the data attributes. The class Dimension does. Dimension has a width and a height and is used to specify
preferred sizes for graphical user interface components such as buttons, panels etc. Here is an example:
Dimension ps = new Dimension(400,80);
System.out.println(ps.toString());
ps.height = ps.height + 20;
System.out.println(ps.toString());
will produce the following output:
java.awt.Dimension[width=400,height=80]
java.awt.Dimension[width=400,height=100]
The issue of why and how access to data attributes is prevented will be dealt with later in section 3.1 on
writing classes.
1.5 Big and small
Some classes like Dimension dene small objects, just a couple of integers and a few functions, there are
other classes that dene very large objects with lots of functions and lots of functionality. An example
of a complicated class is JFrame, it has all the functionality necessary to produce a whole window on a
graphical user display and hold, position and display a variable number graphical components. However
it is in essence no different from Dimension, it is a class, it has data and function attributes, and it can be
used to create objects.
JFrame win = new JFrame();
Container pane = win.getContentPane();
pane.add(new JLabel("hello"));
win.setPreferredSize(new Dimension(100,100));
win.pack();
win.setVisible(true);
Given all the functionality inside a JFrame the above code is enough to create a window, add a message
to it, set the size and get it displayed. Notice how pervasive classes and objects are, this tiny example uses
four different classes: JFrame, Container, JLabel and Dimension.
2 Dening your own classes as a group of data attributes
Historically objects developed from the idea of a record which is to enable different types of data to be
grouped together in one storage structure. For example if it is required to read a le where each line is a
students name, registration number and mark and to store these it might be necessary to have three different
arrays for the names, numbers and marks, this would be complicated and hard to use. Instead if each triple
could be grouped together in an object it will be possible to store all the objects in one array.
2.1 Example AboveAverage01.java
Here is an example that reads a le of such records and then works out the average mark and only prints
records where the mark is greater than the average:
import java.io.*;
import java.util.*;
class Student {
public String name;
public String srn;
public int mark;
public Student(String n, String s, int m) {
name=n; srn=s; mark=m;
2 DEFINING YOUR OWN CLASSES AS A GROUP OF DATA ATTRIBUTES 3
}
}
public class AboveAverage01 {
public static void main(String[] args) throws Exception {
Scanner in = new Scanner(new FileInputStream(args[0]));
int count = 0;
Student students[]=new Student[200];
double tot=0.0, mean;
while(in.hasNext()) {
String name=in.next();
String srn=in.next();
int mark=in.nextInt();
students[count]=new Student(name,srn,mark);
tot = tot + mark;
count = count + 1;
}
mean = tot / count;
System.out.println("mean="+mean);
for(int i=0; i<count; i++) {
if(students[i].mark >= mean) {
System.out.println(String.format("%1$-20s",students[i].name)
+ " " + students[i].srn + " " + students[i].mark);
}
}
}
}
The program is in the le AboveAverage01.java. This program reads the le, line by line in a loop.
Each line contains a name, number and mark, these are used to create an object of type Student. The
newly created object is assigned to the next free position in an array. Also as the lines are read a total is
accumulated. When all the records are read the program calculates the mean and then loops through all the
objects in the array only printing out those that are above the average. If the program is compiled and run
and given the following data le, marks:
M.J.Brennan 04059120 37
M.J.Christie 04053477 76
S.K.Hussain 04052898 61
G.D.Knapp 04050895 70
S.N.Leonard 04058673 80
M.McAdam 04053183 22
C.R.Osborn 04054945 57
A.Patel 04050688 15
H.Patel 04058722 49
C.A.Snellgrove 04053303 56
A.Stewart 04051837 9
S.Travis 04052890 19
S.Tsang 04053058 55
W.A.Wilson 04058358 61
then the output will be only those records where the mark is greater than the average:
mean=47.643
M.J.Christie 04053477 76
S.K.Hussain 04052898 61
G.D.Knapp 04050895 70
S.N.Leonard 04058673 80
C.R.Osborn 04054945 57
H.Patel 04058722 49
C.A.Snellgrove 04053303 56
S.Tsang 04053058 55
W.A.Wilson 04058358 61
3 OBJECTS AS ABSTRACT ENTITIES 4
2.2 How the class Student is used
The program works by dening a very simple class called Student that will hold the three values, in
addition there is a constructor which is strange function (it must have no result) with the same name as the
class. The constructor is executed whenever an object is created using new, it initialises the object. In the
case of Student objects we want it to take three values and assign the values to the elds (data attributes)
of the object.
Having dened the class it is now possible, in the main program, to declare an array of students:
Student students[]=new Student[200];
where every element of the 200 can hold one Student object. Note that this doesnt create 200 Student
objects, just 200 empty variables that can refer to that type of object if ever any are created. Later in the
program, as it reads the le, the objects will be created:
students[count]=new Student(name,srn,mark);
and the objects will be assigned to consecutive elements of the array.
In this program the class has been used as a record, just a group of data elds. This means that all the
manipulation of the data elds is done by code in the program declaring and using the object (in this case
main). Consequently every data attribute in the class denition must be preceded by the keyword public
to allow code outside the class to access attributes inside. As will be shown in the next section this is not
always done.
3 Objects as abstract entities
In the previous example the class was simple with public attributes so it could be manipulated by its client
code, the client of a class means any code that declares an object of that class type and uses it. However
this means that client code can do anything to the attributes whether those actions are safe or sensible.
Another way to use classes is to use them to dene abstract data types, what this means is that objects
of the class can only be treated and used in ways appropriate to the type they represent. For example the
String type in Java should only be used as a string of characters, but there is probably an array inside
which with suitable ddling could probably be manipulated directly to do very un-stringlike things if it
wasnt prevented by the design of the class String.
3.1 private not public data attributes
How is this protection achieved? Data abstraction is achieved by making all data attributes private
not public and providing functions (also called methods) to manipulate the data in safe appropriate ways.
This makes objects of class black-boxes where the internal data attributes are hidden from client code;
they are hidden in the sense that they cannot be directly accessed, the object can only be manipulated by
calling the appropriate functions. Another name for this wrapping up of functions and data together and
only allowing access to the data via the functions is data encapsulation.
Here is a simple example of a class to dene a Point which is supposed to represent information about
the position of a pixel in a window:
public class Point {
private int xCoord, yCoord;
public Point(int x, int y) { xCoord = x; yCoord = y; }
public int x() { return xCoord; }
public int y() { return yCoord; }
public void move(int dx, int dy) {
xCoord = xCoord + dx;
yCoord = yCoord + dy;
}
public String toString() { return "Point(" + x() + "," + y() + ")" ;}
}
The data attributes are private which means attempts to access them from outside (in client code) are
illegal:
3 OBJECTS AS ABSTRACT ENTITIES 5
class TryPoint2 {
public static void main(String args[]) {
Point p1 = new Point(6,6);
p1.xCoord = -1000000;
}
}
will, when compiled, produce the error message:
freckles(1286)$ javac TryPoint2.java
TryPoint2.java:4: xCoord has private access in Point
p1.xCoord = -1000000;
^
1 error
That was an error example, here is a very tiny test program to show succesful creation and use of a Point:
class TryPoint1 {
public static void main(String args[]) {
Point p1 = new Point(6,6);
System.out.println("p1 = " + p1.toString() );
p1.move(5,0);
System.out.println("p1 moved by 5,0 = " + p1.toString() );
}
}
The program is in the le TryPoint1.java. The output is:
tink(1419)$ java TryPoint1
p1 = Point(6,6)
p1 moved by 5,0 = Point(11,6)
The point is created, it is printed, moved and printed again. Do notice that there is a slight disadvantage
to using data hiding which is that in order to retrieve a data value it is necessary to have a function just to
retrieve a data value. To retrieve the xCoord it is necessary to call the function x():
public int x() { return xCoord; }
this ensures that client can get the value if necessary but not directly alter it.
So, in a way, the class Point is abstract, it is a black-box, it is not possible to directly access its internal
data from outside, this means all access must be by using the public functions which allow the designer
of the class to provide only forms of access and alteration that he or she thinks appropriate and if necessary
can include code to check what is being done.
3.2 The roles of methods (functions in a class)
In general the functions in a class have distinct types of job, they allow the object to be altered, or values to
be retrieved. These roles are often classied as follows:
constructors of creation methods, these are used to initialize the state of a newly created object. They
dont actually create the object or allocate storage for its attributes, that is done by create or new
so contructor is a slightly misleading name. Point has one constructor, Point(int x, int y) that
requires both coordinates when it is created and initialised,
accessors these retrieve values from objects on behalf of client code. Normally they get a data
attribute that is private and therefore not directly readable by client code. Point has the functions
int x() and int y() that are accessors. Also String toString() is an sort of accessor that
produces a printable representation of the object rather than retrieve an attribute,
updaters or mutators, methods that alter the data attributes of an object. Clearly the function void
move(int dx, int dy) is an updater in Point since it alters the state of objects.
object generators methods which return new object values. Point hasnt got a generator however it
could be given one:
4 OBJECTS AS BUILDING BRICKS 6
public Point add(Point op) {
return new Point(x()+op.x(), y()+op.y());
}
this is a function that will, when called inside one Point and given another Point as argument, create
a completely new Point based on adding the coordinates. This would be a object generator,
methods that have a combination of jobs. Usually having a method that updates and returns a value
is not a good idea because it is harder to use safely, but sometimes it is useful,
3.3 Other advantages of protecting data attributes
There are supposed to be other advantages to using data hiding in class denitions. The following is a very
brief summary of a couple of them:
Programs can be easier to debug. If the data in an object gets incorrect values then if the only code
that can change those values is inside the functions of the class then it might be easier to determine
how and when the values become corrupt. But if the data attributes are public then any code in the
whole program might be responsible for altering the data and it will be harder to determine how and
when it occurred.
Easier program maintenance. Much of the programming effort on software comes after it is initially
written, a lot of time is spent producing newversions, xing bugs and adapting it to newrequirements.
If the data attributes and data structures inside a class are private then it will be easier to change it
without having to change all the client code that uses it. If all the accesses are via functions then the
internal data can be changed and only the function internals will need changing, but if access is to
public data attributes then a change might require searching through lots of client code and making
lots of changes.
4 Objects as building bricks
This section has a stupid title but what it is meant to indicate is that it is about how objects can be larger and
contain a lot of the functionality of the program so that large programs are often constructed out of a few
large objectsthe objects are the building blocks of the program.
The need for small objects is often obvious or forced because of the requirement to group separate data
together, identifying larger abstract data types can be harder and is often only learnt by experience and by
learning about object oriented programming. However it is not the purpose of these notes to teach object
oriented programming, just to introduce the idea and the language features that support it so you will have
to make do with a few sentences and an example.
4.1 An example: WordFreq01.java
This program reads a le of text and counts the frequency od occurrence of all distinct words. Here is an
example of running it, if it is given the data le:
moses supposes his toeses are roses,
but moses supposes erroneously,
for nobodies toeses are poses of roses,
as moses supposes his toeses to be,
then its output will be:
tink(1428)$ java WordFreq01 moses
moses 3
supposes 3
his 2
toeses 3
are 2
roses 2
but 1
erroneously 1
for 1
nobodies 1
4 OBJECTS AS BUILDING BRICKS 7
poses 1
of 1
as 1
to 1
be 1
Notice that the output isnt sorted alphabetically or by highest frequency. Also note that punctuation is
ignored. Here is the program:
import java.io.*;
import java.util.*;
class WordCounter {
private String wordf;
private int countf;
public WordCounter(String w) { wordf=w; countf=1; }
public String word() { return wordf; }
public int count() { return countf; }
public void inc() { countf=countf+1; }
}
class WordFreqTab {
private WordCounter table[];
private int nwords;
public WordFreqTab() {
table=new WordCounter[4096]; nwords=0;
}
public void count(String target) {
for(int i=0; i<nwords; i++) {
if( target.equals(table[i].word() ) ) {
table[i].inc(); return;
}
}
table[nwords]=new WordCounter(target); nwords=nwords+1;
}
public String getWord(int n) { return table[n].word(); }
public int getCount(int n) { return table[n].count(); }
public int size() { return nwords; }
}
class WordFreq01 {
public static void main(String[] args) throws IOException {
Scanner in = new Scanner(new FileInputStream(args[0]));
in.useDelimiter("[\\s\\p{Punct}]+");
WordFreqTab freqs=new WordFreqTab();
while(in.hasNext()) {
freqs.count(in.next());
}
for(int i=0; i<freqs.size(); i++) {
System.out.println(freqs.getWord(i) + " " + freqs.getCount(i));
}
}
}
The program is in the le WordFreq01.java.
4.1.1 Scanning the input
Before discussing how classes and objects are used consider the following:
4 OBJECTS AS BUILDING BRICKS 8
Scanner in = new Scanner(new FileInputStream(args[0]));
in.useDelimiter("[\\s\\p{Punct}]+");
which declares a Scanner for reading the input le, but then changes it. The change means it will accept
any amount of whitespace (spaces, tabs and newlines) OR punctuation as separators between the words
it reads. If you use a plain Scanner it will only use white space so that the words: Java and Java,
would be treated as separate. Changing the delimiter pattern to include punctuation means they will both
be read as Java, the comma will be ignored.
You do not need to understand how to write delimiter patterns for Java Scanners, it is fairly obscure,
just understand why it is needed and then copy it and use it if you ever need it. If you really do want to read
more about it see the standard Java API specication for Scanner.
4.1.2 Using the class WordFreqTab
Before considering the classes it is useful to have an idea of the overall method of operation of the program:
The programreads the le word by word, it looks up every word in a table, if it is there already
a counter for it is incremented, if it has not already been seen they a new entry is added to the
table with a count of one. When the whole le has been read the program goes through every
entry in the table and prints out the word and its associated count.
Now consider the main programs main loops:
WordFreqTab freqs=new WordFreqTab();
while(in.hasNext()) {
freqs.count(in.next());
}
for(int i=0; i<freqs.size(); i++) {
System.out.println(freqs.getWord(i) + " " + freqs.getCount(i));
}
Note:
rst an object is created of type WordFreqTab and assigned to freqs, this is going to be used to do
all the work of maintaining the required lookup table,
the main loop continues while there are more words: in.hasNext(), each time it reads a word:
in.next() and immediately passes that word to the count function inside freqs. It is the job of this
function to do all the work of checking if the word has already been seen, and counting it or creating
a new entry. Notice how dramatically this simplies the work of the client code, in main,
lastly the programloops through all the entries in the table: up to freqs.size(), each time retrieving
the word and its count: freqs.getWord(i) and freqs.getCount(i), and prints them.
This means we have identied what we require of the class WordFreqTab, we can write a skeleton version
of it even before we implement it:
class WordFreqTab {
// the constructor
public WordFreqTab() { }
// count finds a target string and counts it
// or adds it if it is not there
public void count(String target) { }
// getWord returns the nth word
public String getWord(int n) { }
// getCount returns the count of the nth word
public int getCount(int n) { }
// size returns the number of words in the table
public int size() { }
}
4 OBJECTS AS BUILDING BRICKS 9
In many ways this is the hardest bit in program design, working exactly what functionality to include in
a class. Despite having tried to do it for years Im never condent that I get it right. Maybe thats partly
because there is never a right way, just some ways that are not as bad as other ways to do it. Everybody
nds it hard, if you consider some of the standard classes in the Java API you will realise that programmers
at Sun make some odd designs sometimes.
4.2 Implementing WordFreqTab
How does the new table class work? It uses another class WordCounter that just contains a string and an
integer, in this case though, the data elds are private and access to the elds is via functions:
class WordCounter {
private String wordf;
private int countf;
public WordCounter(String w) { wordf=w; countf=1; }
public String word() { return wordf; }
public int count() { return countf; }
public void inc() { countf=countf+1; }
}
you can create a WordCounter object with a word and an initial count of one, then you can retrieve both
data elds with count() and word(), and lastly, the counter is increased with inc(). Now WordFreqTab
contains a private array to hold these and also an integer, nwords containing a count of the number of
elements in the table. The most important function is count which searches the table for a string target,
if it nds it then it increments it and returns, if it goes through all the elements in the array without nding
the target then it creates a new WordCounter and puts it in the array.

Você também pode gostar