Ocp The Basics
OCP - The basics
Declaration vs Definition
Declaration just means that something exists. Where as Definition describes exactly what the something is. For example: you declare a class or a method, then you define what the class and/or method does.
class aClass // Declaring the class.
{ // The start of defining what the class does.
private int aVariable; // All the information required to define a variable is included within the declaration. However, Java does make the distinction between variable **declaration** and **initialization**.
private int anotherVariable = 100; // This variable is both **declared** and **initialized**
public void aMethod() // Declaring a method.
{ // The start of defining what this method does.
} // The end of the method definition.
} // The end of the class definition.
Interfaces
Interfaces did used to only contain method declarations. But since Java 8, interfaces could also contain method definitions in the form of default
methods.
Classes
A class
is the definition of what it’s object
instances will be able to do. It is the “blue print” or the “cookie-cutter”. These objects are stored in memory (the heap) and a reference to their localation must be declared in a variable, so we can keep track of where the object is, to be able to call methods on it.
References
A object is created when it is initialized. The declaration initialiazes a reference
variable which points to the location in memory where the object resides. This variable is known as a pointer
because it’s value is the address of the location in memory of the object.
Many references can exist that point to the same location in memory where an object resides. For example:
[...]
private String variable1 = "value";
private String variable2;
public constructor() {
this.variable2 = variable1; // This is
}
[...]
A reference variable can not be assigned to a memory location directly. It can only ever be assigned a reference to the location in memory where the object resides.
Primitives
Primitive types are different to object types in that the value is the actual value and not a reference to an object stored in the heap. Although, whether a primitives value is stored in the heap or on the stack depends on whether the primitive is declared as a member or class variable, or a local variable or method parameter. More on this later.
[...]
private int primitive1 = 1;
[...]
Null objects
Objects can be declared without referencing an object in memory. In this case they are assigned the value null
. Objects can also be de-referenced, or have their value changed from a reference to a location in memory to null
, or not referencing any location in memory.
Primitives, on the other hand, can never be null, and can either be intialized explicitly with a value or not. In this case they are initialized with a default value for the type specified in the decalaration. For example, the default value for integer
is 0
. A primitive type declaration cannot be assigned the value null.
Static vs instance
In Java static
is a keyword used when declaring fields or methods that belong to the class
. These fields and methods cannot be called on instances of the class
and can only be called on the class itself. For example:
class AClass() {
private static String classVariable = "value";
}
[...]
var variable1 = AClass.classVariable;
Static fields and methods can be called within member
(or instance) methods. For example:
class AClass() {
private static String classVariable = "value";
public String giveMeTheClassVariable() {
return classVariable;
}
}
If a method or variable is not declared as static
then it is an instance or variable member. The values of instance fields may be different.
Stack vs Heap
All programs require Random Access Memory (RAM) in order to store data during runtime. This memory is allocated to a program by the operating system provided the system has enough to allocate. Once allocated, the program is responsible for managing the memory that was allocated to it. It may ask the system for more memory, or it may release memory (that it does not need) back to the operating system.
The difference between the stack
memory and the heap
memory essentially boils down to short term vs long term storage. But more specifically, the stack
is used to store the order of method invokations and local reference variables within a single thread during runtime, and the heap
is managed by the Java Virtual Machine (JVM) to store ALL
objects that are referenced from variables in the stacks.
The stack
is named because of the way it stores it’s data in “Last In First Out (LIFO)” order. This means that memory is released from the top of the stack as the most locally accessed methods and variables become out-of-scope and are no longer being accessed.
The heap
, on the other hand, is managed by the JVM which uses a Garbage Collector (GC) to dynamically manage memory allocation. It does not store objects in the order they are declared, like the stack. Whenever an object is created on any stack, it is stored in the heap, but the reference variables are stored on the stack allocated to the thread within which it was instantiated.
A Java program does not ever explicitly release memory from the stack, but it is automatically released from the top of the stack when method invokation is complete. In the heap, the GC will identify “garbage” objects as those objects which no longer have any references on any stacks pointing to them.
-
Stacks are created for specific threads and the data stored in them is only accessible from the threads they are created for.
-
The heap is shared amongst threads, and is used to store ALL objects. This means that an object that was created in one thread could be accessed from another thread.
-
Primitive values could be stored on the stack or in the heap. This depends on where it is declared.
-
The stack space is limited and fixed. A
StackOverflowException
will be thrown if the stack space is exceeded. By default the stack space is 64KB, but this can be changed. The heap space is only limited by the available system memory that can be allocated to the JVM.
Compile time vs run time
In Java, all code must be compiled into class files by a compiler. The compiler converts the Java code into intermediary code that the JVM can execute.
The Java compiler is smart enough to identify syntactical errors during compilation and will generate
relevant errors
if it identifies any syntax errors. It will also try to identify logical errors, but can not be relied on soley for this. For example, it will correctly identify when a developer tries to assign a value greater than the bounds of the type used (e.g: byte b = 200
) but it will not throw an error for int i = 10/0
.
The JVM, on the other hand, will identify these logical mistakes and will throw
a relevant Exception
during run time.
Successful compilation is not a guarantee of successful execution.
The compiler can know the value of some types. These are known as compile-time constants. For example: final int x = 20
. The value of x
will never change so it is compile time constant
. The same goes for literal
values such as 1
, 2
, true
, false
and characters like 'a'
.
Identifiers
Java has the following rules when naming classes, variables and methods:
- The first character of an identifier must be a
uppercase
orlowercase
letter, a_
, or a$
- An identifier can be an unlimited length of letters and digits
- An identifier cannot have the same spelling as a
keyword
or aliteral
Reserved keywords and literals
abstract | continue | for | new | switch |
assert | default | goto | package | synchronized |
boolean | do | if | private | this |
break | double | implements | protected | throw |
byte | else | import | public | throws |
case | enum | instanceof | return | transient |
catch | extends | int | short | try |
char | final | interface | static | void |
class | finally | long | strictfp | volatile |
const | float | native | super | while |
_ |
var
var
is a special kind of keyword. It is not a keyword in and of itself, but is replaced by the compiler with a derived type based on the value that is being assigned.