Methods and Variables within a class are collectively known as members. There are two types of modifiers that can be used for a member declaration
Every member you declare has an access control, whether you explicity type one or not. Members accessed using the dot operator (.) must belong to the same class, the this keyword is used to reference the currently executing object.
Member Access Modifiers |
||||||||||||||||
Public Access | When the member is declared protected it means that only the following can access the member
|
|||||||||||||||
Protected Access | When the member is declared protected it means that only the following can access the member
|
|||||||||||||||
Default Access | When the member has no modifier it has default access only the following can access the member
|
|||||||||||||||
Private Access | When the member is declared protected it means that only the following can access the member. Methods that are declared private are implicity final.
|
|||||||||||||||
Member non-Access Modifers |
||||||||||||||||
Final | When used this means that the member (method or variable) cannot be overridden. A variable in a interface is implicity marked as final. | |||||||||||||||
Abstract | An abstract method is a method that has been declared but not implemented, in other words the method contains no functional code. The abstract method must be implemented in either the subclass or any other subclass that has extend it. | |||||||||||||||
Transient | This modifier can only be applied to variables, you are telling the JVM to skip this variable when you attempt to serialize the object declaring it. | |||||||||||||||
Synchronized | When used this means that the method can be accessed by only one thread at a time. It can only be applied to methods. | |||||||||||||||
Native | Indicates that a method is implemented in a platform-dependent language such as C. | |||||||||||||||
Strictfp | Indicates that strict floating-point will be adhered too. | |||||||||||||||
Static | The best way I can describe a static variable or method is that one copy of the static member is contained in memory that allows all instances to access this copy, an example of this is like having a single counter variable that all classes can update. There is a rule that states that a static method of a class can't access a nonstatic member or variable of its own class. Static methods are also declared final implicity. You can Make Static: Methods, Variables and Top-Level nested Classes There is also no this reference when using static, also all static variables and methods are loaded first in order of appearance, you may find a simple static block of code to initialize variables or execute code.
|
|||||||||||||||
Volatile | this modifier tells the compiler that the variable can change unexpectedly, this is commonly used when using multi-threading and you want to keep a shared variable that all threads use. Threads can use CPU memory and thus could have different values in a shared variable, a volatile variable keeps the value in main memory and not in cpu memory, this makes sure that all threads have access to the same shared variable value. |
Quick lookup table to show the access to members permitted by each modifier
Modifier | Class |
Package |
Subclass |
World |
public | Y |
Y |
Y |
Y |
protected | Y |
Y |
Y |
N |
no modifier (default) | Y |
Y |
N |
N |
private | Y |
N |
N |
N |
Now for some examples
Method Examples |
|
Public Access | public void coolMethod() { /* any code that you want */ } |
Protected Access | protected int coolMethod() { /* any code that you want */ } |
Default Access | int coolMethod() { /* any code that you want */ } Note: no access modifier at the beginning of the statement |
Private Access | private String coolMethod() { /* any code that you want */ } |
Final | public final void superCoolMethod() { /* any code that you want */ } |
Abstract | public abstract int coolMethod(); Note: No curly braces at the end, code for this method must be in a sub-class |
Synchronized | public synchronized Record getUserInfo() { /* any code that you want */ } Note: synchronized can never be used with abstract but you can use the other access modifiers |
Static | public static void myStaticMethod() { /* any code that you want */ } Note: this method is shared by all objects within the class, there is only one copy in memory |
Local Variable Examples (contained in method or code block) |
|
No Modifier | int bonus = 1.1; Note: the default modifier is local to the block of code |
Final | final int bonus = 1.1; Note: the final non-access modifier is the only option for a local variable |
non-Local (instance) Variable Examples |
|
Public | public counter = 0; |
Protected | protected counter = 0; |
Private | private counter = 0; |
final | public final bonus = 1.5; Note: a final variable has to be initialized, it can be initialized in the constructor of a class, also a variable in an interface is implicity marked as final whether you declare it or not |
Static | public class VariableExample { public void increment() { var1.increment(); # This Java program will increase the static variable classCounter everytime a new class is created, the instanceCounter will only get affect everytime we call the increment method of that instance, the localCounter gets reset everytime we call the increment method |
A example using abstraction and using the keywords this and super
Abstraction, this and super | public class arrayTest { public static void main(String[] args) { test1 t1 = new test1(); // create the test1 object which extends the abstract class test2 t1.testMethod(); // call the abstracted method t1.testMethod2(); // calling the normal inheriated method } } class test1 extends test2{ System.out.println("local testMethod variable count = " + count); // local variable abstract class test2 extend test3{ // the abstract class int getSuperCount() { class test3 { Note: there is lots going on in this program buts its a good refresher and not a difficult program to work out. By the way there is no such thing as super.super you must get the super class to access the so called super.super class. |
You have different rules on returns types depending if you overriding or overloading
Here are some examples regarding the return types
Return a subtype of a type | Number getNumber(){ return Integer.valueOf(1); } |
Return a null of a reference type return type | Number getNumber(){ return null; } |
Return a narrower primitive data type if the return type is a primitive | long getNumber() { int myInt =2; return myInt; } |
Return a primitive data type wrapper if the return type is primitive | long getNumber(){ return Long.valueOf(1); } |
Return a wrapper of a primitive to a primitive that is the same or wider | long getNumber(){ return Integer.valueOf(1); } |
Just have the return statement but only if modifier is void | public void returnNothing() { return; } |
When passing a variable or an object to a method two outcomes happen
Passing Variable | When you pass a variable to a method, you are passing a copy of the variable which means you can do whatever you want to the variable passed but you will not affect the source of the variable. |
Passing Object | When you pass a object to a method you are passing the reference to that object not the object itself, it represents a way to get to a specific object in memory, thus any changes to the object that occur inside the method are being made to the object whose reference was passed. |
Below are some examples of passing a variable and an Object to a method
Passing Variables and Objects | public class Test { public static void main(String[] args) { int i = 3; StringObject so = new StringObject("M"); // Create a new StringObject passing a string to the constructor System.out.println(passingVariableObject(i, so)); // will display 5Hello, i is passed by value, so is passed by reference System.out.println(" " + i + so.toString()); // will display 3Hello } static String passingVariableObject(int i, StringObject so){ i = 5; // will not change the underlying variable as its passed by value so.s = "Hello"; // will change the objects string to "Hello", as its passed by reference return " " + i + so; // so Object will use the toString() to displays it's value } } class StringObject { public String s; public StringObject(String s) { this.s = s; } @Override public String toString() { return s; } } |
Overloaded methods lets you reuse the same method name in a class, but with different arguments (and optionally a different return type), you help out other programmers by supplying different argument types so they don't have to perform conversions before invoke your method.
The rules for overloading are as follows
Overloaded Example | // Original Method public void changeSize(int size, String name, float pattern) { ... } // Overloaded Methods public void changeSize(int size, String name) { ... } public void changeSize(float pattern, String name throws IOException) { ... } public int changeSize(int size, float pattern) { ... } |
So how does Java determine what overloaded method to use, its uses a 3 phase check of the method signature as detailed below, note that once Java finds a match the other phases are disguarded, think of it as precedence, phase one having the higher precedence
Phase One | Performs overload resolution without permitting boxing or unboxing conversion, or the use of variable arity (var args) method invocation Within Phase 1, if multiple methods meet this criteria the best method is selected by
|
Phase Two | Allows boxing and unboxing, but ignores variable arity (var args) method invocation |
Phase Three | Allows overloading to be combined with variable arity (var args), boxing and unboxing Note: var args methods will be last to match if better options exist |
Here are some examples of the above
Examples of Java 3 phase method signature testing | public class Test { public static void main(String[] args) { int i = 1; Long l = 2L; char c = 3; T1 t1 = new T1(i); // int test, will use int constructor T1 t2 = new T1(l); // Long test, will use long constructor T1 t3 = new T1(c); // char test, will use int constructor byte b = 4; long l2 = 5; short s = 6; float f = 5.6f; double d = 5.6; Character c2 = 7; T1 t4 = new T1(b); // byte test, will use int constructor T1 t5 = new T1(l2); // long test, will use long constructor T1 t6 = new T1(s); // short test, will use int constructor // T1 t7 = new T1(f); // No Constructor for float, compile error T1 t8 = new T1(d); // double test, will use Double constructor T1 t9 = new T1(c2); // Character test, will use char constructor } } class T1 { T1(int a){ System.out.println("int constructor"); } T1(long a){ System.out.println("long constructor"); } T1(char a){ System.out.println("char constructor"); } T1(Character a){ System.out.println("char constructor"); } T1(Double d) { System.out.println("Double constructor"); } } |
Overridden Methods
Methods can be overridden and overloaded, constructors can only be overloaded. Overload methods and constructors let you use the same name but different argument lists, overriding lets you redefine a method in a subclass when you need new sub-class behavior unless the method is mark final. In the case of a abstract method you have no choice you must override it.
The rules for overriding are as follows
The key benefit of overriding a method is to define behavior that is specific to the class
Overriding example | public class Animal { // This will be the super class class Horse extends Animal { // Horse is a sub-class of Animal public void printAnimal { class Lion extends Animal { // Lion is a sub-class of Animal Note: to call methods from the superclass use the super keyword. |
Difference between Overloaded and Overriding
Overloaded |
Overridden |
|
Argument list |
Must change | Must not change |
Return type |
Can Change | Must not change |
Exceptions |
Can Change | Can reduce or eliminate, must not throw new or broader checked exception |
Access |
Can Change | Must not make more restrictive (can be less restrictive) |
Invocation |
Reference type determines which overloaded version (based on declared argument types) is selected. Happens at compile time. The actual method that's invoked is still a virtual method invocation that happens at runtime, but the compiler will always know the signature of the method that is to be invoked. So at runtime, the argument match will have already been nailed down, just not the actual class in which the method lives | Object type (in other words, the type of the actual instance on the heap) determines which method is selected Happens at runtime. |
Command-line Arguments to Main
When passing command-line arguments to your Java program it creates a String array to hold the arguments, this array is just like any other array. Note that the array name args can be anything you want but it must be of type String.
Examples of a main method | // Mostly commonly used main signature public static void main(String[] args) // Other valid main method signatures public static void main(String args[]) public static void main(String []args) public static void main(String ... vargs) // a variable-length argument called vargs public static void main(Boolean ... vargs) // you can overload main, overloading the above public static void main(String...vargs) public static void main(int i, String...vargs) // the vargs must be the last argument public static void main(final String[] args) public static void main(String[] hello) // you can use whatever name you like, here I use hello public static void main(String[] vars) public static void main(String[] args) throws Exception public static void main(String[] args) throws RunTimeException public static void main(String[] args) throws Throwable public static void main(String[] args) throws Error |
Using main method | public class commandLine { System.out.println("You passed " + args.length + " arguments"); for(int i = 0; i < args.length; i++) # Run the command line Note: arrays args is just like any other array. |
Using variable-length arguments | public class VarArgs { public static void main(String ... vargs) { // notice the 3 periods, this means a varible-length argument is being passed for(String s : vargs){ System.out.println(s); } } } Note: you can pass any number of string arguments, icluding passing no arguments |
The ability to make changes in your implementation code without breaking the code of others who use your code is a key benefit of encapsulation. You want to hide (mask) implementation details behind a public programming interface. By interface we mean the set of accessible methods your code makes available for other code to call, in other words, your codes API's. You can change the code without forcing a change in the code that calls your changed methods.
To provide maintainability, flexibility and extensibility you must encapsulate the code and to do this you
Set and Get methods | public class Football { public void setTeam(String t) { ... } Note: the only way to get to the instance variables is via the set and get methods |
When you create a new class that does something you will document only what access methods (get and set) are available to the public and this will not change, whoever implements your class can get updates and improved versions knowing that implementing this newer version will not break his/her program as the return types and passing variables to methods will be the same.
I have already covered access modifiers in the previous section creating and using methods, so I will cover the princples here, as we learned in the previous section Java provides two ways to hide class memembers
The objectives to hiding members is to protect from unintended changes, and secondly to protect consumers of the class from unintended consequences causing a minimum of disruption should the class need to change and as mentioned above we can use a special type of class called a bean to archive this.
To sum up encapsulation we have the following:
The this keyword has the following meaning
access attributes from an Object | String getname() { return this.name; } |
call the no args constructor | this(); |
use inside a constructor | class Test { Test() {} // no args constructor Test(String a) { this(); // this() must be on the first line of the constructor // do something } } |
assign a variable to this | Object o = this; |
return an Object | Object returnObject() { return this; } |
When a method of a class references another member of that class for a specific object of that class, how does Java ensure that the proper object is referenced, the answer is that each object has access to a reference to itself called the this reference. The this reference is implicity used to refer to both instance variables and methods of an object.
this example | public class thisTest { public static void main(String[] args) { myClass m = new myClass(17, 55, 59); mtClass m1; m.getTime(); m1 = m.getObject; // return the m object using this m1.getTime(); if ( m.equals(m1) ) // they should be the same object System.out.println("m and m1 are the same object"); } } class myClass { public myClass getObject() { |
You can use a bit of Java tricky to chain methods (any methods)
General Form | result = method1().method2().method3(); |
Example using String | String x = "hello have a nice day "; x = x.trim().concat(" and a jolly good evening").replace('hello'.'Hello'); Note: the statement is processed from left to right, so the trim method is called first, etc |