Guaranteed Initialization with The Constructor
In Java, the class designer can guarantee initialization of every object by providing a constructor.
Constructor is an unusual type of method because it has no return value. This is distinctly different from a void return value. The new expression does return a reference to the newly created object, but the constructor itself has no return value.
Method Overloading
Each overloaded method must take a unique list of argument types. And it's sufficient to distinguish among the methods with the same name.
Overloading with Primitives
public class Demotion {
void f1(long x) {
// ...
}
void test() {
int x = 5;
f1(x); // x will be promoted to long
}
If you passed a value which is smaller than the arguments in the method, that data type will be automatically promoted.
If your argument is wider, then you must performa narrowing conversion with a cast. Otherwise it will get an error in compile time.
Default Constructors
Default constructors is one without arguments that is used to create a "default object". If you create a class without constructors, the compiler will automatically create one for you. But you do write a constructor, then compiler will not create the default one.
The this Keyword
Suppose you are inside a method and you would like to get the reference to the current project. Since the reference is passed secretly by the compiler, there is no identifier for it.
// Demo 1
public class Leaf {
int i = 0;
Leaf increment() {
i++;
return this; // return reference to the current object
}
}
// Demo 2
public class Demo {
void pick() {/* ... */}
void pit() {
pick(); // this.pick();
}
}
The this keyword is created for this purpose (reference the current object), can be used only inside a non-static method. It produces the reference to the object that the method has been called for.
If you are calling a method of your class from within another method of your class, you don't need to use this.
class Peeler {
static Apple peel(Apple apple) {
// .. remove peel
return apple; // peeled
}
}
class Apple {
Apple getPeeled() {
return Peeler.peel(this); // pass itself as an argument
}
}
Calling Constructors from Constructors
When there are several constructors for a class, calling one constructor from another is to avoid duplicating code.
public class Flower {
int petal = 0;
String s = "initial value";
Flower(int petals) { /* ... */ }
Flower(String s) { /* ... */ }
Flower(String s, int petals) {
this(petals); // be in the first line
//! this(s); can't call twice
this.s = s; // another use of this
}
Flower() {
this("hi", 7);
}
}
Within a constructor, you can only call another constructor by using this once, not twice.
And it must be in the first line.
The Meaning of static
There is no this for the static method.
You cannot call non-static methods from inside static methods (the reverse is possible).
Putting static method inside a class allows it access to other static methods and to static fields.
Member Initialization
Java guarantees that variables are properly initialized before they are used.
- Member variable: each primitive type is guaranteed to get an initial value; each object reference without initializing will be given a special value of null.
- Method's local variable: if not initialized, then error occurs at compile time.
Constructor Initialization
Order of Initialization
- Within a class, the order of initialization is determined by the order that the variables are defined within the class.
- Variables are initialized before any methods can be called (even the constructor).
static Data Initialization
- There is only a single piece of storage for a static, regardless of how many objects are created.
- You cannot apply the static keyword to local variables, so it only applies to fields.
The Process of Creating an Object
- Consider a class called Dog
- Though it doesn't explicitly use the static keyword, the constructor is actually a static method. The first time an object of type Dog is created, or the first time a static method or static filed of class Dog is accessed, the Java interpreter must locate the Dog.class by searching through the classpath.
- As Dog.class is loaded, all of its static initializers are run. Thus static initialization takes only once.
- When you create a new Dog(), the construction process for a Dog object first allocates enough storage for a Dog object on the heap. The storage is wiped to zero, automatically setting all the primitives in that Dog object to their default values and the references to null. (Any initializations that occur at the point of field definition are executed).
- Constructors are executed.
Array Initialization
- For an array of primitive type elements, elements are automatically initialized to default values.
- While for an array of object, only initializing the array is not enough (actually it's only an array of references at this step), you have to initialize the reference itself by creating the object. If you forget to create the object, you will get an exception at rum time when you try to use the empty array location.
Variable Argument Lists
"varargs" - known as C's variable argument lists: used for unknown quantities of arguments as well as unknown types. Since all classes are ultimately inherited from the common root class Object, you can create a method that takes an array of Object and call it as follows:
public class VarArgs {
static void printArray1(Object[] args) { } // call it must passing an array of object
static void printArray2(Object... args) { } // can call it without passing arguments
}
Enumerated Types
Use the key word "enum" when you need to group together and use a set of enumerated types.
/**
Following creates an enumerated type called Spiciness
with 5 named values. Because the instances of enumerated
types are constants, they are in all capital letters by convention.
*/
public enum Spiciness {
NOT, MILD, MEDIUM, HOT, FLAMING
}
/**
To use an enum, create a reference of that type and assign it to an instance.
*/
public class SimpleEnumUse {
public static void main(String[] args) {
Spiciness howHot = Spiciness.MEDIUM;
System.out.println(howHot);
}
}
// Output:
// MEDIUM
Summary
- In Java, constructors allow you to guarantee proper initialization and cleanup (the compiler will not allow an object to be created without the proper constructor calls), you get complete control and safety.
- In C++, destruction is quite important because objects created with new must be explicitly destroyed.
- In Java, the garbage collector automatically releases the memory for all objects, so the equivalent cleanup method in Java isn't necessary much of the time (but when it is, you must do it yourself).