As an example of how new classes are written, we will set up a personnel system. To make things clear it will be very simple, and just handle employees and departments.
Code structure
Recall that Java code is split into separate source code files, with each public class defined in each file. Also there will be a method called
main somewhere where execution will start:
In an IDE this will be a new project which we will call 'personnel'. Classes are grouped into packages, but this simple example only needs one package, also called personnel. Each class definition will start with
package personnel;
which tells the compiler which package the class is in.
Class design
In this application we will have just 3 classes:
Start - to contain main
Department - to model a department Employee - to model an employee The department class
We will go through this first class in a lot of detail. The overall structure of this is
package..
class Department {
..data members
.. constructors and methods }
The package statement must come as the first line. That is where the compiler will look for it.
Then we have the class definition, which starts class Whatever { and ends with the matching } . Within that we have the data members, constructors and methods. The order of these does not matter. These will not be executed in sequence, so the order does not matter. It is a definition of what is in the class, not a program.
After the class header we have
private String name;
This is the only data member in the class - the name of the department. This is a String, and String is a class, hence the capital letter. It does not say what the name is, since this will be different for each Department object - each instance of the class. It just states that each Department object will have a name.
Then we have the constructor:
Department(String n) {
name=n;
}
We know it is a constructor because it has the same name as the class. It has one String argument, n, and a single statement, which assigns the field 'name' to this argument.
For example, a department is created by
dep1 = new Department("Production");
Here the argument value "Production" is passed into the constructor as the argument n, and this is assigned to the department's name field, so it becomes "Production". Distinguish between teh name of the variable which referencs the object (dep1), and the name field of teh object (Production).
Next we have a method definition
void display() {
System.out.println("Department : "+name);
}
The fragment display() means the method name is 'display', and the empty bracket shows it takes no arguments. The void means this method does not return a value - it sends nothing back to where it was invoked from - it just does something. What it does is to simply output the department name.
String getName() {
return name;
}
This does return a value, of type String. It returns the name of the department. This is an accessor method, a get, which is needed because of access control.
Encapsulation and access control
Remember that encapsulation means sealing up data within objects so that they cannot be corrupted by bugs. Three access levels are:
private - accessible only from this class
nothing - the default, accessible from this package.
public - accessible from anywhere
The data member is private - it is encapsulated within the class and cannot be accidentally changed from out of it.
The constructor and method are nothing - that is, package access level. That is what we need.
The data member is private - but sometimes we need to know what the department name is, from outside the class. We achieve this with an accessor method, a getter,
String getName()
Effectively this makes 'name' to be read-only - we can read it from the package outside the class, but nothing outside the class can change it.
The Employee class
private Department department;
Employee(String name, int salary, Department department) {
System.out.println("Name: "+name + " department: "+department.getName()+" Salary:
"+salary);
} }
Most of this is similar to Department - we just pick out the differences.
The constructor has three argument, which are values for the data members. But their names are the same as the names of the data members - which in one sense is logical, but might be confusing.
The resolution is to use the keyword this:
this.salary=salary;
this.salary means we are referring to the salary field of this object we are constructing. The other one, just 'salary', is the value of the argument with that name. This technique means we can avoid having to use argument names different from what they mean, such as
Employee(String n, int s, Department d) {
name=n;
salary=s;
department = d;
}
In the display method, we say
+.." department: "+department.getName()+..
We cannot say
+ " department: "+department.name+"
and access the department name directly, because the name field is private. We have to use the accessor method.
Counting the objects
The Employee class has a data field for a Department. Does that mean that when we create an Employee object, we also create a new Department object?
No. The keyword new does not appear in the class definition, so no new objects are being created.
The data field 'department' is a reference to another object which already exists, not a newly created one. The code to use these classes is like:
Department dep1 = new Department("Marketing");
..
Employee emp1 = new Employee("John Smith", 20000, dep1);
The first line creates a new Department object. Then we create a new Employee object, passing to it a reference to the already existing object called dep1.
Do not be confused by the idea of 'name'. In the line
Employee emp1 = new Employee("John Smith", 20000, dep1);
the name of the object is emp1. Objects of that class have a data field called name. In this instance, the value of that is "John Smith". But the object is called emp1, not "John Smith".
The Start class
package personnel;
public class Start {
public static void main(String[] args) {
This class is public, as is the main method. This has to be so, since main must be accessible from outside the package, when the runtime system calls it to start the application.
The main method just makes some Department and Employee objects, displays them, changes someone's salary and displays them again. The output is:
Department : Marketing Department : Production Department : R & D
Name: John Smith Department: Marketing Salary: 20000 Name: Jane Jones Department: Marketing Salary: 22000 Name: Joe Jeffs Department: Production Salary: 19000 Name: John Smith Department: Marketing Salary: 22000
Static and non-static What does 'static'mean?
Static means per class
Non-static is per object
Most data fields are per object. That means, there is a separate value for each object. For example, Employee object has a non-static field called 'name'. This is because each employee has their own name. Similarly for the non-static field salary - each employee has their own salary.
We refer to non-static fields as
<object>.<field>
We have to say which object, since different objects have different values. For example
emp1.name
is the name field of the emp1 object.
But sometimes it is useful to have some value which belongs to the class, as a whole, not to
individual objects. This is what static means. It is per class, not per object. We refer to a static field by
<Class>.<field>
All of this applies to methods as well as data fields.
Examples of static - 1
The purpose of the main method is to provide a starting point for the application. Obviously we can only have one starting point. We can achieve this by making main to be static. Then it is per class. It is only in one class, so we just have one of it. If it was non-static, then we would have a main method for each instance of the class, which does not make sense.
In our example, main is in the Start class. The full name of it is
Start.main
We have now explained all of the qualifiers of main:
public static void main(String[] args)
public so it can be accessed from outside the package, by the runtime system. static because it is per class, so there is just one of it. void because it does not return a result, just does something.
Static example 2 - object arrays
As we have the application at the moment, we make department and employee objects, but they just 'lie around' in main, where they are declared, one at a time. It would be better if we could refer to all the departments, or all the employees. We could do this if we put them into arrays. But where to put the arrays? We could declare them in main, like
static void main(String[] args) {
Department[] departments = new Department[10];
..
but this would mean the array was local to main, and could not be referred to anywhere else, which we might want to do.
If we declare it like
package personnel;
public class Start {
Department[] departments = new Department[10];
public static void main(String[] args) { ..
that means the array is non-static - that is there will be a different copy for each instance of the Start class. But in this version we do not instantiate the Start class - we nowhere say
package personnel;
public class Start {
static Department[] departments = new Department[10];
public static void main(String[] args) { ..
then the array is per-class - we have one version of it, linked to the class. The full name of the array is
Start.departments.
We will do this, for employees as well, and in their constructors put the new object into the correct array. Then we can have methods to output all departments and all employees. We need to keep track of how many departments and employees we have:
So now Start is:
package personnel;
public class Start {
static Department[] departments = new Department[10];
static int departmentCount = 0;
static Employee[] employees = new Employee[10];
static int employeeCount = 0;
public static void main(String[] args) {
Department dep1 = new Department("Marketing");
Department dep2 = new Department("Production");
Department dep3 = new Department("R & D");
Department.displayAll();
Employee emp1 = new Employee("John Smith", 20000, dep1);
Employee emp2 = new Employee("Jane Jones", 22000, dep1);
Employee emp3 = new Employee("Joe Jeffs", 19000, dep2);
Employee.displayAll();
} }
Employee, with a modified constructor and displayAll, is:
package personnel;
class Employee {
private String name;
private int salary;
private Department department;
Employee(String name, int salary, Department department) {
this.name=name;
this.salary=salary;
this.department = department;
Start.employees[Start.employeeCount]=this;
Start.employeeCount++;
System.out.println("Name: "+name + " Department: "+department.getName()+" Salary:
"+salary);
} }
and similarly with Department:
package personnel;
Start.departments[Start.departmentCount]=this;
Start.departmentCount++;
Static example 3 - class design
As much as possible, we want to 'de-couple' classes, which is to make them as independent as possible. This makes it easier to re-use them in other situatuions, and lets us develop and debug them in one place, not in a tangled web across the application.
In our example code, both Start and Employee deal to some extent with employee objects. It would be better if Employee dealt with all aspects of the employee objects, and similarly for Departments.
We can do this by switching the arrays to their appropriate classes. Now Start looks a lot tidier:
package personnel;
public class Start {
public static void main(String[] args) {
private Department department;
static Employee[] employees = new Employee[10];
static int employeeCount = 0;
Employee(String name, int salary, Department department) { this.name = name;
We no longer say, for example
Start.employees[i].display();
because the employees array is no longer a static member of Start. We could say
Employee.employees[i].display();
but because this code is in the Employee class, we can miss out the Employee.
Similarly Department is
package personnel;
class Department { private String name;
static Department[] departments = new Department[10];
static int departmentCount = 0;
Department(String n) { name = n;
departments[departmentCount] = this;
departmentCount++;
}
static void displayAll() {
for (int i = 0; i < departmentCount; i++) { departments[i].display();
} }
void display() {
System.out.println("Department : " + name);
}
String getName() { return name;
} }
Remember that static is per class
non-static is per object
Exercise
Write an application to do stock control (inventory control) in a supermarket. Do NOT write it all in one go them test it! Write a small section at a time and test as you go along. You should have different Sections (like tinned fruit, frozen and household) and Goods (like peaches, peas and dishwasher tablets). Each of the goods needs a price and a current stock level.
You should be able to -
Set up new sections and goods.
Display all sections and all goods.
Sell some items, and so reduce the stock level.
Reduce the price of all items in a given section by 10%