WALT (We Are Learning To): Designate access and visibility constraints to classes, data, constructors, and methods (MOD-2)
Classes represent either physical objects (e.g. a student) or abstract concepts (e.g. a loan) and define their attributes (instance and static variables) and behaviors (methods).
private
- restricts access to the declaring class (the usual designation for instance variables)public
- allows access from outside the declaring class (this is the usual scenario for classes and constructors)protected
- (not covered in the AP Java subset)Methods can be either internal (access designation - private
) or external (access designation - public
)
A technique where the implementation of the details of a class is kept secret from the user. This is done through access specifiers. Data can or cannot be accessible (read-only) and/or modifiable (R/W) from external classes.
Instance variables are typically encapsulated using the private
access specifier.
WALT (We Are Learning To): define instance variables for the attributes to be initialized through the constructors of a class (MOD-2)
An object's state refers to its attributes and their corresponding values at a given point in time. Attributes "live" in the instance variables of an object. So, an object HAS instance variables (a so called "has-a" relationship). E.g. object Andy
of class Student
has an instance variable called firstName
and its value is "Andy"
.
The constructor' job is to set the initial state for an object, this including the initial values for all its instance variables.
These are local variables and their job is to initialize the instance variables. When using a mutable object as a constructor parameter, I should use its copy, not an alias of the original object — this way I make sure that the object's methods cannot modify the state of the original object.
When no constructor is provided, Java uses the so called no-argument constructor and sets all instance variables to default values:
0
.null
.Notice that constructors don't have a return value.
Comments are ignored by the compiler and are not executed at the program run-time.
/* .......
.............
.............
*/
These comment out a block of code - i.e. multiple lines of code.
// ..............
These work for individual lines only.
/**
*......
*......
*/
These are used for creating API documentation. BlueJ supports these.
A precondition is a condition that needs to be met prior to the calling of the method in question in order for the code to work properly. E.g. the public double divide(int a, int b)
method would have a precondition that the second parameter is not a zero, so that the division by zero error doesn't occur. The method does not necessarily need to check whether the precondition was met, but the precondition needs to be stated explicitly in the documentation.
A postcondition is a condition that always needs to evaluate to true after the execution of the method/code segment. Postconditions stipulate what is being returned or in what state the object is after the execution of the method/code segment.
Methods should be written so that the postconditions are satisfied when all the preconditions were met - i.e. that the program outputs what it should so long it has been fed with correct input (the opposite of GIGO: garbage in — garbage out).
E.g. the aforementioned divide()
method should return the quotient of a
, the dividend and b
, the divisor. This quotient should be of the double data type.
In order to be able keep the object's instance or static variables private, one has to provide public methods that can read the contents of these variables outside of the object itself. Such methods are called "accessors" as they access the instance variables, read them and then return their content to whoever may be asking. They also go by the name "getters" as all they do is get some data without modifying anything within the object itself.
Methods that are non-void (i.e. they have a return type specified) return a value that must be compatible with the return type. Java first evaluates the return expression and then returns a copy of the value. They return exactly one value.
Example 1:
public static int add(int a, int b)
{
return a+b;
}
The method above is not returning a or b, it first evaluates the return expression and then sends a copy of the value, which is the value of a plus the value of b. This is called returning by value. The original values of a
and b
stay intact.
Example 2:
public int greater(int a, int b)
{
if (a>b)
return a;
else
return b;
}
Here neither primitive a
nor b
is returned - a copy of a
or b
is. This is another example of returning by value.
Example 3:
public String getName()
{
return objectName;
}
As the return type is String
- an object - in this case a copy of the memory reference to the objectName
instance variable is returned, not the actual value(s) stored in the object (which is a string in this case).
Whenever you try to "print" an object, the default toString()
method is called which displays its instance variables. It is very common to override this method with a class-specific version of the toString()
method. This method always returns a value, not a memory reference.
Consider the example below:
public class Student
{
private int studentID;
private String firstName;
private String lastName;
private int noOfCredits;
public Student(int ID, String fName, String lName, int credits)
{
studentID=ID;
firstName=fName;
lastName=lName;
noOfCredits=credits;
}
public String toString()
{
return "Student ID: "+studentID+"\n"+"First Name: "+firstName+"\n"+"Last Name: "+lastName+"\n"+"Number of Credits: "+noOfCredits;
}
}
public class Main
{
public static void main()
{
Student s1=new Student(001,"Andy","Daubner",55);
System.out.print(s1);
}
}
Instead of just merely printing the object reference, System.out.print(s1)
will call the class-specific toString()
method which we defined in the class above and which formats the output in a more useful way.
Printing s1 with the generic toString()
method (no class-specific toString()
method just prints the memory reference to the object:
Student@3e242412
Printing s1 with the class-specific toString()
method produces a more intelligible output:
Student ID: 1
First Name: John
Last Name: Smith
Number of Credits: 55
Here, the class-specific toString() method has overriden the generic toString() method.
return
keyword at the end of the method?void
keyword in the method signature. setXYZ()
, changeXYZ()
, updateXYZ()
, renameXYZ()
, etc. These are not mandatory, though... There will be situations where you will not use any of the above, such as in a bank account deposit()
method, which is a mutator method - it modifies the account balance. See the example below:Example of a setter method:
public class BankAccount
{
private double balance;
}
public void deposit(double amount)
{
balance+=amount;
}
balance
is a private variable readable and modifiable by the object itself only.balance
instance variable - it adds the amount deposited to the existing balance.Another example of a mutator method:
public class File
{
private String fileName;
}
public void changeFileName(String newFileName)
{
fileName=newFileName;
}
fileName
instance variableSetters:
void
)private
instance variablespublic void setName(String studentName)
Getters:
private
instance variablespublic double getBalance()
return
statement in the data type set in the method signature.The parameters defined in the method header are called formal parameters (such as String password
and double amount
in the example method header above. The actual parameters are the values passed on to the method when it's being called and are saved in the local variables declared in the method signature, such as in the example below:
public class Account
{
private double balance;
private int noOfOpenAccounts=0;
public Account()
{
balance=0.0;
Account.noOfOpenAccounts++;
}
public void withdraw(String password, double amount)
{ if password.equals("pass123")
balance-=amount;
}
public static void main()
{
Account account1 = new Account()
account1.withdraw("pass123",100.00);
}
Here the value "pass123
" got saved in the password
local variable and the value 100.00
got saved in the local variable called amount
.
These variables are local to the method - they cannot be used outside of it.
In short: the method header declares its local variables and the calling of the method with parameters initializes the local variables with concrete values that were sent as parameters.
When passing primitives as parameters, Java makes a copy of the value in the argument and sends that to the method. This is called calling by value. If you change the value that was passed on as parameter in the method, the original value will stay unaltered.
When sending an object as a parameter to a method, a copy of the reference to that object is made (an alias), which is then passed on to the method. See the example below:
In the example on your left we see that two objects were created but three references exist. The "MrKubes
" and the "MrMatej
" references both refer (point) to the same object. We call such references as aliases. When passing objects as arguments, a new alias is created and only the alias, not the entire object, is passed on to the method.
static
or not, its parameters, and the return value (void
or not).account1
in order to be able to call the withdraw()
method by stating account1.withdraw("pass123", 100.00)
having included the correct type and number of parameters.static
keyword in the method signature to indicate that a variable is static.public
or private
.className.variableName
. Example for the Account class (see the code above): System.out.println("The number of open accounts is: "+Account.noOfOpenAccounts)
The line of code above (if placed in the main method) reads the static variable noOfOpenAccounts
which holds the number of accounts open (whenever the Account()
constructor is called, this variable's value increases by one)
static
keyword in the method signature to indicate that a method is static.this
keyword cannot be used with static methods as there is no object reference.{ }
— this will be the variable's scope.{
and }
. These will only work within the scope of this block. An example would be the iterator in your for loop.Different scopes example:
public class Teacher
{
private String firstName;
private String lastName;
public void printName(int howManyTimes)
{ for (i=0;i<howManyTimes;i++)
{
System.out.println("Copy number :"+i);
System.out.println(firstName+" "+lastName;
}
}
}
<- declaring instance variables firstName
and lastName
- their scope is class-wide
<- parameter howManyTimes
has a method scope - it will only be usable within this method. It is also known as a local variable.
<- iterator i
has a block scope - it only works within the for
loop.
{ }
.public class Student
{
private String lastName;
public Student(String name)
{
lastName=name;
}
public void printStudent()
{
String lastName="Smith";
System.out.println(lastName);
}
}
public static void main()
{
Student s1=new Student("Shin");
s1.printStudent();
}
When we run the main method, the output will be "Smith
" and not "Shin
" as we have both a local and and instance variable called lastName
and it is the local one that "wins".
In Java IDEs the scope of variables can be usually identified by individual areas of scope (blocks) shaded in different colors for easier identification (this provided you've done your indentation properly). Here, BlueJ uses green, white, yellow and white again for different levels of scope.
this
keyword refers to the object whose method or constructor is being called.this
can only be used with non-static methods or constructorsthis
can be used to pass the current object as a parameter to a method or constructorConsider this example:
public class Parent
{
private String lastName;
public Parent(String name)
{
lastName=name;
}
public void printParent()
{
String lastName="Baker";
System.out.println(this.lastName);
}
}
public static void main()
{
Parent p1=new Parent("Smith");
p1.printParent();
}
<- We are using this
to identify the object (in this case it is p1
, hence this.lastName
refers to the instance variable lastName
, not the local variable lastName
.
When we run the main method, the output will be "Smith
" and not "Baker
" as we have clearly indicated, using the this
keyword, that we are interested in the instance variable and not the local or block variable.