- Introduction
- Packages
- Overloading
- Continuing the code
- Calling methods from Anywhere
- Access Modifiers
- Using Access Modifiers
- Many objects
Introduction
In this tutorial we'll finally look at Packages to organize our code, and also at Access Modifiers that will allow us to restrict access to parts of our Classes.
By this point you show have an understanding of how to create a new Class, create new objects of a class type, create variables and know that there are primitive types. If you don't, the please check the Tutorial 3 - Variables and Tutorial 4 - Classes, Constructors and Methods .
So, if you have IntelliJ Community Edition installed and would like to use it, just open it up and Create a New Project, of Java type, and select no other libraries nor Frameworks. Call it "BeginnerJava5". If you are using a text editor, just create a new Folder somewhere on your disc and call it "BeginnerJava5".
We'll develop a very rudimentary system that creates and stores data on Persons, much like we did on the previous tutorial. We'll do it from scratch, and now learn about Packages in Java.
Lets start with creating a class called Starter, so using IntelliJ on the left tree find the "src" folder, right click in it and select "new" -> "Java Class" . If you are using a the text editor, create a file called "Starter.java" on the root of the folder you previously created.
public class Starter {
}
This is our Starter class. Lets create a main method and make it print out something:
public class Starter {
public static void main(String[] args) {
System.out.println("Starting program");
}
}
Compile it and run it. If you are using IntelliJ simply press Ctrl+Shift-F10.
Everything should be fine to this point.
Now we'll go and implement the User class, but before we do, lets think a little about how we want to organize our Classes. For a project so small as this one it wouldn't be a problem to have all Classes in the same place, but imagine for a project that will have tens or hundreds or thousands of Classes? Having a large project with all its classes in the same place would be a nightmare to manage, and work with.
There is the concept of Packages in Java, which allows the programmers to organize their classes in a tree like fashion.
Packages
Every project in Java should have its package, the exception being that the project will never ever be used again in any sort of productive way. Lets call this a rule.
Because we are programming a Person management system, and because we want to do it right, well create a package for this project. We'll call it tutorials.personmanagement
.
There is a naming convention as to what the package name of a software or library should be and what rules should be follow:
- Package names are in all lowercase
- If the project is developed by a company, organization, or person that has an internet domain, the reverse of the domain is used to start the name of the package.
For example, if this was a project developed by JBay Solutions and this project is a Printing Money Software, then because JBay Solutions has the domain jbaysolutions.com the package could be called for example:
com.jbaysolutions.printingmoney
com.jbaysolutions.printmoneysoftware
com.jbaysolutions.lib.printingmoney
There is a bit of freedom to choose the name of the package, but its goal is to avoid conflicts! By following these rules, it is very unlikely that you'll find conflicts of Classes with the same name, inside packages with the same name, in different libraries.
So, our package is going to be called tutorials.personmanagement
.
If you are using IntelliJ just right-click on the "src" folder on the Project tree on the right, and select "new" -> "Package" . Just type in : tutorials.personmanagement .
If you are using the text editor, then just go to the folder of the project, and create a folder named "tutorials" and then inside that folder create another folder called "personmanagement" .
So now we have the Package for the project, we'll start with the creation of the Person class, inside the tutorials.personmanagement package . Just go ahead and create the Person class, with no methods and no variables.
If you use IntelliJ it should look like this:
package tutorials.personmanagement;
public class Person {
}
If you created the Class in a normal Text Editor, then you are probably missing the first line:
package tutorials.personmanagement;
Because this Person class "belongs" to a package, not only must the file be created inside that specific folder, the first statement of that Class must be the definition of the package it belongs to.
This would also be correct, because the Comments and Blank lines obviously don't count as statements:
// This is a comment
/*
This is also a comment
*/
package tutorials.personmanagement;
public class Person {
}
For our software, a Person will have the following attributes:
- A Name - String
- An Age - int
- A Salary - double
Lets implement this class then, with the Getter and Setter methods for all the properties, and also create 2 constructors, one that takes all 3 parameters and one that takes just the Name and Age, because under aged people will not have a salary.
We should end up with a class somewhat like this :
package tutorials.personmanagement;
public class Person {
String name;
int age;
double salary;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person(String name, int age, double salary) {
this.name = name;
this.age = age;
this.salary = salary;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
}
Notice that in this class we have two constructors. This is valid, because the constructors have different signatures.
Likewise we could have several methods with the exact same name, as long as input paramenters part of the signature is different. This is called method overloading , or in our particular case here, constructor overloading.
Overloading
Lets think about this "Overloading" thing for a second.
We have two Constructors like this:
public Person(String name, int age, double salary)
public Person(String name, int age)
Why is this allowed? The simple reason is : you call them in different ways!
Because you call them in different ways, the JVM, when it is executing, will know exactly which constructor to use based on the input you provide.
As an example, imagine you will be creating a Person object with the following code:
Person person = new Person( "Rui", 35 );
The JVM knows that you are trying to call a constructor that takes as input a String and an int in this particular order. It will then look at the class Person and see if there is a Constructor that matches this. It finds it and uses it:
public Person(String name, int age)
It will not be able to use the other Constructor :
public Person(String name, int age, double salary)
Likewise, if we try to create a Person object with the following code:
Person person = new Person( "Rui", 35 , 1000.0 );
It will only be able to use the second constructor.
Overloading works exactly in the same way for methods.
Also, if we look at one of the constructor, we will notice that the names of the parameters provided are exactly the same as the names of the Class level variables:
String name;
int age;
double salary;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
These are effectively two different variables, one that has a Class Level Scope, and one that has a Method Level Scope (check the previous Tutorial to understand more about variables scope Here) .
This is legal, because they are defined in different scopes.
But now, because we can access two variables with the same name from inside the method (or constructor in our case), there has to be a way to unequivocally specify which variable we are referring to, hence the use of the keyword this .
this Keyword
From inside a method, the keyword this is used to reference the Object the method belong to. In this particular case, inside the constructor, the this.name
specifies unequivocally that we are referring to the String name
variable defined outside the method.
If we have this Constructor instead:
String name;
int age;
double salary;
public Person(String name, int age) {
name = name;
age = age;
}
Because we are not using the this keyword, then we will be referring to the String name
variable that is passed as argument, and therefore we would not be affecting any of the Class Level variables that exist on the class.
Notice also that on the Setter methods of the class, we also use the same name for the input argument as we use for the Class variables, and therefore we need to use the this keyword:
public void setName(String name) {
this.name = name;
}
Also, keep in mind that the following method would be valid and would accomplish the same result as the previous method:
public void setName(String newName) {
this.name = newName;
}
In this particular case the use of the this
keyword is redundant, because there is no ambiguity as to which variables we are referring to, but it is legal and correct non the less.
Lets continue with the code.
Continuing
On our Starter class, which does not have a package, we only have the main method, which is at this point printing a String. Lets modify the main method to now create an object o type Person:
public class Starter {
public static void main(String[] args) {
System.out.println("Starting program");
Person person = new Person("Rui", 35);
}
}
If you are using IntelliJ or any other IDE, the source code should be showing some errors. This is because the Starter doesn't know what Person is, nor where it is. We must import the Class to let Starter know that when we refer to Person we are talking about a specific class:
import tutorials.personmanagement.Person;
public class Starter {
public static void main(String[] args) {
System.out.println("Starting program");
Person person = new Person("Rui", 35);
}
}
As you can see, in the first line of this class, we added an import statement:
import tutorials.personmanagement.Person;
This is how it works: If a programmer is writing some code in a Class that uses other Classes, then, if the other Classes are not in the same exact Package, the programmer needs to import either the specific class, or everything inside the Package of the other classes:
import tutorials.personmanagement.Person;
: Importing specific classimport tutorials.personmanagement.* ;
: Importing every class inside this package
Usually a programmer will import only the Class that is needed, unless there are several Classes inside a package that are going to all be needed, and in that case, the programmer imports all the Classes inside a Package using the *
notation.
Why is this important?
Imagine for a second that you are developing a big project that has a visual component that draws Squares on the screen, and a computational side that calculates areas of Squares. You will possibily have two classes called Square on two different packages :
com.project.render.Square
com.project.area.Square
When writting the code, depending of which part the programmer is now coding, he will need to import a different Square
class. This gets even more tricky when a project uses libraries that were developed by a 3rd party, and you don't know all the names of all the classes that exist inside that 3rd party library.
The import statements are always placed between the Package statement and the beginning of the class
Like this:
package com.project;
import com.project.render.Square;
public class Example {
[ETC ETC ETC]
}
If the specific class you will need to import stuff into does not belong to a Package, then the import statements should be the first statements of the file. (Obviously not counting the comments)
Compile and run what you have until now, and see if the import works ( Ctrl+Shift+F10 or Shift+F10 on IntelliJ) .
Calling methods from anywhere
In the previous part of this tutorial we created a Person class, and a Starter class where we create an instance of Person with some values. Lets now modify the Person object that was created and change the value of the name
variable for that object, and lets also modify the Person class to add a method for printing the values of the variables. We'll start with the method of the Person class:
public void printData() {
System.out.println("Value for name is : " + name);
System.out.println("Value for age is : " + age);
System.out.println("Value for salary is : " + salary);
}
Very simple, like we did on the previous tutorials.
Now, lets modify the main
method on the Starter class, to modify the name
property of the object and also print out the values of the object afterwards:
public static void main(String[] args) {
System.out.println("Starting program");
Person person = new Person("Rui", 35);
person.setName("Super Rui");
person.printData();
}
Compiling and running it we get :
Starting program
Value for name is : Super Rui
Value for age is : 35
Value for salary is : 0.0
The first thing to notice here is that the Constructor used for creating the Person object was the Constructor that only takes in String name
and int age
, and therefore salary
was never ever set! But when we print, the value of salary
is 0.0
.
This is because, for Class Level Scope primitive typed variables (yes... all these names we have just learned), when not assigned, they take their default value, which for a
double
is0.0
.
The second thing to notice is that the change of the name
was done through a Setter method, which is correct, but is not enforced here, which is bad. We'll see how to enforce that in a minute with Access Modifiers.
For our example of a Person Management System, we want that every time that a Person is created or properties are modified on a Person object, the Software shows some Logs. To do this, we will implement a method on the Person class that prints a provided message. We'll call that method logToConsole
and we define it like this :
public void logToConsole(String message) {
System.out.println("PERSON LOG - " + message);
}
So, every time this method is called with a message
, it will writen to the console with a PERSON LOG -
prefix.
Now, we'll modify every Setter method on the Person class to call this method before changing the value of a variable. We'll also modify the Constructors to call this method when they are called.
The final *Person class should look something like this now:
package tutorials.personmanagement;
public class Person {
String name;
int age;
double salary;
public Person(String name, int age) {
logToConsole("Creating Person with name: " + name + " age: " + age);
this.name = name;
this.age = age;
}
public Person(String name, int age, double salary) {
logToConsole("Creating Person with name: " + name + " age: " + age + " salary: " +salary);
this.name = name;
this.age = age;
this.salary = salary;
}
public void logToConsole(String message) {
System.out.println("PERSON LOG - " + message);
}
public void printData() {
System.out.println("Value for name is : " + name);
System.out.println("Value for age is : " + age);
System.out.println("Value for salary is : " + salary);
}
public String getName() {
return name;
}
public void setName(String name) {
logToConsole("name modified to: " + name );
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
logToConsole("age modified to: " + age);
this.age = age;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
logToConsole("salary modified to: " + salary );
this.salary = salary;
}
}
Looking, for example, at the setName
method we have:
public void setName(String name) {
logToConsole("name modified to: " + name );
this.name = name;
}
Which mean that before assigning the provided name
value to the class variable name
, we call the method logToConsole
and print the value out.
Compiling the project now and running it will return something like this:
Starting program
PERSON LOG - Creating Person with name: Rui age: 35
PERSON LOG - name modified to: Super Rui
Value for name is : Super Rui
Value for age is : 35
Value for salary is : 0.0
Notice that we have 2 LOGs on the output. The first which corresponds to the Constructor , which should the values used on the creation of the Person object, and the second one which if for the call of the setName
method.
Lets say now that we are very sneaky and lets try to call the logToConsole
method from the main
method in this way :
public static void main(String[] args) {
System.out.println("Starting program");
Person person = new Person("Rui", 35);
person.setName("Super Rui");
person.logToConsole("name modified to: MEGA MEGA RUI");
person.printData();
}
When we compile and run it we get:
Starting program
PERSON LOG - Creating Person with name: Rui age: 35
PERSON LOG - name modified to: Super Rui
PERSON LOG - name modified to: MEGA MEGA RUI
Value for name is : Super Rui
Value for age is : 35
Value for salary is : 0.0
We now have 3 LOGs on the output, we have the third LOG saying that the name was modified to "MEGA MEGA RUI"
but when we call the printData
method, we see that the value for name is still "Super Rui"
. We understand why, because we called the logToConsole
function, which simply prints things to the console, and not the setName
Setter Method which also prints things (through the use of logToConsole
) but also modifies the value of the name
variable. We know this, but other users, that might be looking at the LOGs will not get this, and will wonder why the name was not modified.
The reason why this can happen is because everyone can call logToConsole
.
Problems can even be worst, for example, with the salary
variable. Imagine that the Setter method for Salary has some other code that calculates Taxes, and other things that are related to money. It would be a very big security issue if a programmer would modify the value of the salary
of a person directly without using the Setter method, or print LOGs saying that the salary was modified, but nothing was actually changed on the Person object in question.
Enter the Access Modifiers.
Access Modifiers
Since the first tutorial of this series, every method and class we created started with the public
keyword. This is the Access Modifier of the Class or Method, which in all our examples was public
, which is the least restrictive Access Modifier that there is.
There are in total 4 Access Modifiers:
public
protected
- no modifier (package-protected)
private
Public
A method that is public is a method that can be called from anywhere in the code. You can call it from inside the same class, from a different class in the same package, from a different class on a different package, etc. The same goes for Class Level Scope variables, you can access these variables from anywhere.
Private
A method that is private is a method can can only be called from the same Class where it is declared. The same goes for Class Level Scope variables, these can only be accessed from inside the same Class where they are declared.
Package-Protected
The package-protected is a modifier which does not have a specific name, but is usually referred to as the Package-Protected access modifier, or Package Level access modifier, which is when a method or a variable have no Access Modifier defined. An example is :
public class Person {
String name; // <-- Package-Protected Access Modifier
int age; // <-- Package-Protected Access Modifier
double salary; // <-- Package-Protected Access Modifier
}
These three Class Level Scope variables have the Package-protected access modifier , because they don't have an Access Modifier defined.
What this means is that these variables can be accessed directly only from the Class they are declared in, or from other Class that belong to the same package.
Protected
The protected Access Modifier is almost the same as the Package Access Modifier ( or "no modifier" ) , which means that the method or variable can be accessed from Classes that belong to the same package, but also from Subclasses even if they are in other packages. What is a Subclass? Well get back to this in a few tutorials, but it is nothing to worry about.
All these Access Modifiers can be applied Methods and Class Level Scope Variables.
As for Classes, they can only be "public" or "Package level" . Private and protected make no sense to Classes, and therefore cannot be used.
Using Access Modifiers
Looking at our Person Class, we see that we have 3 variables that are package-protected:
String name;
int age;
double salary;
It makes no sense to have them as Package-Protected because they are only meaningful to the Object they belong to. Also, because they have Getter and Setter methods, it makes no sense to allow them to be accessed and modified from outside of this Class. We should make them private
, like so:
public class Person {
private String name;
private int age;
private double salary;
What about the methods? The Getter and Setter methods, because we want to be able to access them from other classes, even outside of the package this class belongs to, we leave them as public
.
What about the logToConsole
method? Because we only want this method to be called from inside the Person class, and avoid other developers ruining the LOGs we produce, we'll make the method private
:
private void logToConsole(String message) {
System.out.println("PERSON LOG - " + message);
}
Making this change to the logToConsole
method will make calling it from the main
method at Starter illegal and will generate an error if we try to Compile. Try it and see for yourself (really, try it) .
Lets now remove the call to logToConsole
method from the main
method (or comment it out), and give it a compile and run.
public static void main(String[] args) {
System.out.println("Starting program");
Person person = new Person("Rui", 35);
person.setName("Super Rui");
//person.logToConsole("name modified to: MEGA MEGA RUI");
person.printData();
}
What about Constructors?
Constructors are like methods and therefore can have any Access Modifier, even the "private" modifier. You'll see examples of it later on.
What about Method Level Scope Variables?
Method Level Scope Variables don't have an Access Modifier, because they are only accessible from inside the specific method they are created in. Also, it wouldn't make any sense to have them have an Access Modifier because they only exist during the execution of the method they belong to, and after the method execution they stop existing.
Many objects
As an exercise, lets now modify the main
method to make it create 2 objects of type Person , using the two different Constructors we have, and then print the data on both the objects using the printData
method.
If should look something like this :
public static void main(String[] args) {
System.out.println("Starting program");
Person person = new Person("Rui", 35);
person.setName("Super Rui");
Person person2 = new Person("Miguel", 90, 500.0);
person.printData();
person2.printData();
}
Compile and run it. The result should look like:
Starting program
PERSON LOG - Creating Person with name: Rui age: 35
PERSON LOG - name modified to: Super Rui
PERSON LOG - Creating Person with name: Miguel age: 90 salary: 500.0
Value for name is : Super Rui
Value for age is : 35
Value for salary is : 0.0
Value for name is : Miguel
Value for age is : 90
Value for salary is : 500.0
Wouldn't it be nice to be able to have a list of Person objects stored in someway, instead of having to programmatically define a variable for each of the objects we create? Yes , and we'll look into of doing it in the next tutorial.
Further Reading :
- Primitive Types in details
Lets jump to the next Tutorial : Tutorial 6 - Arrays