I won't be diving deep into OO concepts (there are numerous books on the subject that are far better explaining the OO concepts than me) but I will cover the basic OO concepts and any rules that you need to know
Object Oriented Programming (OOP) encapsulates data (attributes) and methods (behaviors) into objects, the data and methods are intimately tied together, implementation details are hidden within the objects themselves. In Java the unit of programming is the Class from which objects are eventually instantiated. Methods and data are encapsulated within the walls of the class. A class contains the data and the methods that access the data contained within the class.
When you run a Java program the JVM looks for a method called main(), this is the entrypoint from where the program starts to execute, its possible to have multiple main() methods but they must be overloaded, something I will discuss in another section.
I have a separate section on discussing inner classes, which covers Inner Class, Method-Local Inner Class, Anonymous Inner Class, Static Nested Class
A Class is a blueprint on which an Object is created, Objects are constructed (basically live in memory of the computer/server), you cannot make a new object without invoking a constructor (Java will proivde one even if you don't). Constructors are code that runs whenever you use the keyword new. Constructors cannot specify a return type or return values.
Every class even abstract classes must have a constructor, if you don't specify a constructor the Object class will provide a default one and as every object inheriates class Object, you will regardless get at least a default constructor. Interfaces do not have a constructor you cannot instantiate a interface.
Classes have the following rules
There are two types of modifiers that can be used for class declaration
Every class you declare has an access control, whether you explicity type one or not. When a class has access to another class it means he can do one of 3 things
Access means visibility, if you do not have access to a class you cannot use any methods or variables of that class.
Class Access Modifiers |
|
Default Access | When no modifier is present then default access is applied, think of default access as package access, a class with default access can be seen only by classes within the same package |
Public Access | When declared with public access, it gives all classes from all packages access to the public class, although you still need to import the class |
Class non-Access Modifers |
|
Final Classes | When used with classes this means that the class cannot be subclassed (cannot extend). Many of classes in the Java library are final classes. |
Abstract Classes | An abstract class can never be instantiated, its sole purpose is to be extended (subclassed). If a single method in a class is abstract the whole class must be declared abstract. You can put non-abstract methods in a abstract class (also known as a concrete method), you will be unable to override the non-abstract method. An abstract class can extend another abstract class. You cannot mark a class as both abstract and final as they mean the opposite, you will get a compiler error (illegal combination of modifiers). The first concrete class to extend an abstract class must implement all abstract methods. Abstract classes can have constructors. |
Strictfp Classes | The effect of strictfp is to restrict floating-point calculations to ensure portability across platforms |
Below are some example of class declarations
Class Declarations | public class Test public class Test extends SuperTest public final class Test public abstract class Test extends SuperTest // you can only extend one superclass public class Test implements AInterface public class Test implements AInterface, BIterface // you can implement many interfaces public class Test extends SupperTest implements AInterface |
Class initializer and code block ordering, one note to make is the ordering of variables and if they are used in the blocks, make sure they are initialized before the block otherwise you get a illegal forward reference error
Class initializer and ordering | public class ClassTest1 { { // this wont work as class is not initialized, entry point is the main method System.out.println("Hello from ClassTest1"); } public static void main(String[] args) { System.out.println("ClassTest1: NewClass static x: " + NewClass.x); // we can access the static variable x, even though we have not created NewClass object NewClass nc1 = new NewClass(); System.out.println("ClassTest1: nc1 A: " + nc1.a); System.out.println("ClassTest1 has now finished!!!!"); } } class NewClass { static int x = 101; int a = 5; int b; // Initializer block { System.out.println(" NewClass (initializer) A: " + a); } // Initializer block { System.out.println(" NewClass (initializer) B: " + b); } // Initializer block { System.out.println(" NewClass (initializer) X: " + x + "\n"); } // static blocks are always called first, when the class gets loaded static { System.out.println(" NewClass static block"); } // Constructor public NewClass() { this.b = 10; // we can change the value of b using this keyword, b is part of the object x++; // we can change the value of x without new keyword, you dont need to initialize object to get x, its static System.out.println(" NewClass (constructor) A: " + this.a); System.out.println(" NewClass (constructor) B: " + this.b); System.out.println(" NewClass (constructor) Static X: " + this.x); } } NOTE: output would be below: NewClass static block ClassTest1: NewClass static x: 101 NewClass (initializer) A: 5 NewClass (initializer) B: 0 NewClass (initializer) X: 101 NewClass (constructor) A: 5 NewClass (constructor) B: 10 NewClass (constructor) Static X: 102 ClassTest1: nc1 A: 5 ClassTest1 has now finished!!!! |
Illegal forward reference error | class Test1 { static { System.out.println("A: " + a); // You are using variable a before its been referenced } static int a = 5; } |
Below are some example on declarations and instantiations, the new operator dynamically allocates memory and returns a reference to the memory location (it's address in memory).
Declarations and Instantiations | Object a = new Object(); // The default object constructor will be used Object a = new Object(), Object b = new Object; // you can create multiple Objects on oneline String s = new String("Paul"), Object a = new Object(); // ERROR: You cannot declare different types on oneline |
Constructors and Instantiation
As I mentioned at the start Objects are constructed, you cannot make a new Object without invoking a constructor.
Constructors have the following rules
Below is are some examples of a simple constructors
Example Constructor | class Test { Test() { ... } // The constructor for the Test class } Note: there is no return type and the constructors name must match exactly the class name |
Example constructor using varargs | public class Test2 { public static void main(String[] args) { Test2 t1 = new Test2(); t1.test(1, 2); t1.test(1, 2, 3); t1.test(1, 2, 3,4 ,5 ,6, 7, 8, 9, 10); } } class Test2 { void Test2(int a, int... paulArgs) { // you can name the varargs anything senible and it must be the last argument System.out.println("test.constructor normal variable a: " + a); System.out.print("test.constructor with varargs: "); for(int i: paulArgs) { // you can loop through the args like an array System.out.print(i + " "); } System.out.println("\n"); } } |
Constructors are normally used to initialize instance variables, I talk about the this keyword later
Initialize instance variables using the constructor | class Test { int size; String name; Test(String name, int size) { this.name = name; // the this keyword refers to the classes instance variables, see below for the this keyword this.size = size } } Note: see below for more information on this |
When you invoke a constructor using the new keyword you can create a constructor chain
Constructor Chaining | What happens when you create a new object in this case Horse extends Animal Horse h = new Horse();
|
The below tables details what the compiler will generate for your class regardless if you have typed it or not.
Class Code (What you type) | Compiler-Generated Constructor Code (in Bold type) |
class Foo { } | class Foo { Foo() { super(); } } |
class Foo { Foo() { ... } } |
class Foo { Foo() { super(); } } |
public class Foo { } | class Foo { public Foo() { super(); } } |
class Foo { Foo(String s) { ... } } |
class Foo { Foo(String s) { super(); } } |
class Foo { Foo(String s) { super(); } } |
Nothing - compiler does not need to insert anything |
class Foo { void Foo() { ... } } |
class Foo { void Foo() { ... } Foo(){ super(); } } Note: void Foo is a method because of the return type |
You can overload constructors just the same as methods, I will discuss overloading in the methods section
Constructor overloading | class Foo { Foo() { ... } Foo(String s) { ... } Foo(String s, int t) { ... } } |
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; } |
Java's garbage collector provides an automatic solution to memory management, in most cases it frees you from having to add memory management logic to your code, the only downside is that you have no control when it runs and when it doesn't.
The purpose of the garbage collector is to find and delete any objects that are no longer in use (Java calls them reachable) and free the memory so that it can be used again. The garbage collector is under the control of the JVM, only the JVM decides when it runs. You can only ask the JVM to run the garbage collector but it decides if it runs now of later, generally the JVM will run the garbage collector when it's low on memory.
Every Java program will at least run one thread (main()), but can have many threads running. A thread can be either alive or dead so an object is eligible for garbage collection when no live thread can access it. So when the JVM discovers an object that cannot be reached by any live thread it will consider that object eligible for deletion and it might even delete it at some point (it might never delete it).
So a reachable object is a object that is available to a live thread.
Here are some examples on how to make an object eligible for garbage collection
nulling a reference | public class GarbageTruck { // object sb is not eligible for garbage collection // object is now available for garbage collection |
Reassigning a Reference | public class GarbageTruck { // object sb is not eligible for garbage collection // Now the StringBuffer "Hello" is eligible for collection |
Isolating a reference | public class Island { i2.i = i3; |
The first thing to remind you about is that you cannot force a garbage collection, you politely ask the JVM to collect the garbage but it may fall on deaf ears.
Asking to garbage collect | import java.util.Date; public class stringTest { public static void main(String[] args) { Runtime rt = Runtime.getRuntime(); System.out.println("Total JVM memory: " + rt.totalMemory()); System.out.println("Before JVM memory: " + rt.freeMemory()); Date d = null; for (int i = 0; i < 10000000; i++) { d = new Date(); d = null; } System.out.println("After JVM memory: " + rt.freeMemory()); rt.gc(); // politely ask to collect garbage // Runtime.getRuntime().gc(); // other way to run the gc System.out.println("After GC memory: " + rt.freeMemory()); } } |
There are a number of garbage collector options you can pass to the java command and the types of garage collector
Java GC Options | -Xlog:gc // Display the Garbage collector information -verbose:gc // Displays information about each garbage collection (GC) event (less info than above) -XX:+UseSerialGC // serial collector uses a single thread to perform the garbage collection -XX:+UseParallelOldGC // parallel collector uses multiple threads to collect the garbage -XX:+UseG1GC // server-type collector for multi-processor machines with large memory (default as of Java 11) -XX:+UseZGC // scalable low latency GC, performing all work concurrently without stopping other threads -XX:+UseConcMarkSweepGC // concurrent GC, used for shorted GC pauses and could afford to share processor resources with GC (no longer used from Java 9) |
Java provides you a mechanism to run some code just before the object is deleted, this code is located in the method finalize() (inherited from class Object) this sounds like a good idea but you cannot count on the garbage collector to ever delete an object (so the finalize method might not run). It is recommended that you do not override the finalize() method at all, if you do overide it then it should be protected so that subclasses have access to the method.