General information about constructors
Конструктор
is a structure similar to a method, the purpose of which is to create an instance of a class. Characteristics of the designer:
- The name of the constructor must match the name of the class (by convention, the first letter is capitalized, usually a noun);
- There is a constructor in any class. Even if you don't write one, the Java compiler will create a default constructor, which will be empty and do nothing other than call the superclass constructor.
- A constructor is similar to a method, but it is not a method, it is not even considered a member of the class. Therefore, it cannot be inherited or overridden in a subclass;
- Constructors are not inherited;
- There can be several constructors in a class. In this case, the constructors are said to be overloaded;
- If a class does not define a constructor, the compiler automatically adds a parameterless constructor to the code;
- A constructor does not have a return type; it cannot even be a type
void
; if a type is returned void
, then it is no longer a constructor but a method, despite the coincidence with the class name.
- The operator is allowed in the constructor
return
, but only empty, without any return value;
- The constructor allows the use of access modifiers; you can set one of the modifiers:
public
, protected
, private
or without a modifier.
- A constructor cannot have the modifiers
abstract
, final
, native
, static
or synchronized
;
- The keyword
this
refers to another constructor in the same class. If used, the call to it must be the first line of the constructor;
- The keyword
super
calls the constructor of the parent class. If used, the reference to it must be the first line of the constructor;
- If the constructor does not make a call to the
super
ancestor class's constructor (with or without arguments), the compiler automatically adds code to call the ancestor class's constructor without arguments;
Default constructor
There is a constructor in any class. Even if you don't write one, the Java compiler will create a default constructor. This constructor is empty and does nothing other than call the superclass constructor. Those. if you write:
public class Example {}
then this is equivalent to writing:
public class Example
{
Example()
{
super;
}
}
In this case, the ancestor class is not explicitly specified, and by default, all Java classes inherit the class,
Object
so the class constructor is called
Object
. If a class defines a parameterized constructor, but there is no overloaded parameterless constructor, then calling the parameterless constructor is an error. However, in Java since version 1.5, it is possible to use constructors with variable length arguments. And if there is a constructor that has a variable length argument, then calling the default constructor will not be an error. It won't because the variable length argument can be empty. For example, the following example will not compile, but if you uncomment the constructor with a variable length argument, it will compile and run successfully and result in a line of code running
DefaultDemo dd = new DefaultDemo()
; the constructor will be called
DefaultDemo(int ... v)
. Naturally, in this case it is necessary to use JSDK 1.5. File
DefaultDemo.java
class DefaultDemo
{
DefaultDemo(String s)
{
System.out.print("DefaultDemo(String)");
}
public static void main(String args[])
{
DefaultDemo dd = new DefaultDemo();
}
}
The result of the program output with the constructor uncommented:
DefaultDemo(int ...)
However, in the common case where the class does not define any constructor at all, calling the default constructor (without parameters) will be necessary, since default constructor substitution occurs automatically.
Object creation and constructors
When creating an object, the following actions are performed sequentially:
- The object class is searched among the classes already used in the program. If it is not there, then it is searched in all catalogs and libraries available to the program. Once a class is discovered in a directory or library, the class's static fields are created and initialized. Those. For each class, static fields are initialized only once.
- Memory is allocated for the object.
- The class fields are being initialized.
- The class constructor executes.
- A link to the created and initialized object is formed. This reference is the value of the expression that creates the object. An object can also be created by calling a
newInstance()
class method java.lang.Class
. In this case, a constructor without a parameter list is used.
Overloading constructors
Constructors of the same class can have the same name and different signature. This property is called combination or overloading. If a class has multiple constructors, then constructor overloading is present.
Parameterized Constructors
The signature of a constructor is the number and types of parameters, as well as the sequence of their types in the list of constructor parameters. The return type is not taken into account. The constructor does not return any parameters. This statement explains, in a sense, how Java distinguishes between overloaded constructors or methods. Java distinguishes overloaded methods not by their return type, but by the number, types, and sequence of types of input parameters. A constructor cannot even return a type
void
, otherwise it will turn into a regular method, even though it is similar to the class name. The following example demonstrates this. File
VoidDemo.java
class VoidDemo
{
VoidDemo()
{
System.out.println("Constructor");
}
void VoidDemo()
{
System.out.println("Method");
}
public static void main(String s[])
{
VoidDemo m = new VoidDemo();
}
}
As a result, the program will output:
Constructor
This once again proves that a constructor is a method without return parameters. However, the constructor can be given one of three modifiers
public
, ,
private
or
protected
. And the example will now look like this: File
VoidDemo2.java
class VoidDemo2
{
public VoidDemo2()
{
System.out.println("Constructor");
}
private void VoidDemo2()
{
System.out.println("Method");
}
public static void main(String s[])
{
VoidDemo2 m = new VoidDemo2();
}
}
It is allowed to write an operator in a constructor
return
, but only an empty one, without any return value. File
ReturnDemo.java
class ReturnDemo
{
public ReturnDemo()
{
System.out.println("Constructor");
return;
}
public static void main(String s[])
{
ReturnDemo r = new ReturnDemo();
}
}
Constructors parameterized with variable-length arguments
Java SDK 1.5 introduced a long-awaited tool - variable-length arguments for constructors and methods. Previously, a variable number of documents were processed in two inconvenient ways. The first of them was designed to ensure that the maximum number of arguments is limited to a small number and is known in advance. In this case, it was possible to create overloaded versions of the method, one for each version of the list of arguments passed to the method. The second method is designed for something unknown in advance and a large number of arguments. In this case, the arguments were placed in an array, and this array was passed to the method. Variable length arguments are most often involved in subsequent manipulations with variable initializations. It is convenient to replace the absence of some of the expected constructor or method arguments with default values. The variable length argument is an array, and is treated as an array. For example, the constructor for a class
Checking
with a variable number of arguments would look like this:
class Checking
{
public Checking(int ... n)
{
}
}
The character combination ... tells the compiler that a variable number of arguments will be used, and that these arguments will be stored in an array whose reference value is contained in the variable n. The constructor can be called with a different number of arguments, including no arguments at all. The arguments are automatically placed in an array and passed through n. If there are no arguments, the length of the array is 0. The list of parameters, along with variable length arguments, can also include mandatory parameters. In this case, a parameter containing a variable number of arguments must necessarily be the last in the list of parameters. For example:
class Checking
{
public Checking(String s, int ... n)
{
}
}
A very obvious limitation concerns the number of variable length parameters. There must be only one variable-length parameter in the parameter list. Given two variable-length parameters, it is impossible for the compiler to determine where one parameter ends and the other begins. For example:
class Checking
{
public Checking(String s, int ... n, double ... d)
{
}
}
File
Checking.java
For example, there is equipment capable of recognizing car license plates and remembering the numbers of the squares of the area where each of the cars visited during the day. It is necessary to select from the total mass of recorded cars those that during the day visited two given squares, say 22 and 15, according to the area map. It is quite natural that a car can visit many squares during the day, or maybe only one. Obviously, the number of squares visited is limited by the physical speed of the car. Let's create a small program where the class constructor will take as arguments the car number as a mandatory parameter and the numbers of visited squares of the area, the number of which can be variable. The constructor will check whether a car has appeared in two squares; if it has, then display its number on the screen.
Passing parameters to the constructor
There are mainly two types of parameters in programming languages:
- basic types (primitives);
- references to objects.
The term call by value means that the constructor receives the value passed to it by the calling module. In contrast, call by reference means that the constructor receives the address of the variable from the caller. Java uses call by value only. By parameter value and by parameter link value. Java does not use call by reference for objects (although many programmers and the authors of some books claim this). When passing objects to Java, parameters are passed
not by reference , but
by the value of the object reference ! In either case, the constructor receives copies of the values of all parameters. The constructor cannot do with its input parameters:
- the constructor cannot change the values of the input parameters of the main (primitive) types;
- the constructor cannot change input parameter references;
- the constructor cannot reassign input parameter references to new objects.
The constructor can do with its input parameters:
- change the state of the object passed as an input parameter.
The following example proves that in Java, input parameters to a constructor are passed by object reference value. This example also reflects that the constructor cannot change the references of the input parameters, but actually changes the references of copies of the input parameters. File
Empoyee.java
class Employee
{
Employee(String x, String y)
{
String temp = x;
x = y;
y = temp;
}
public static void main(String args[])
{
String name1 = new String("Alice");
String name2 = new String("Mary");
Employee a = new Employee(name1, name2);
System.out.println("name1="+name1);
System.out.println("name2="+name2);
}
}
The output of the program is:
name1=Alice
name2=Mary
If Java used call by reference to pass objects as parameters, the constructor would swap
name1
and in this example
name2
. The constructor will not actually swap the object references stored in the
name1
and variables
name2
. This suggests that the constructor parameters are initialized with copies of these references. Then the constructor swaps the copies. When the constructor completes its work, the x and y variables are destroyed, and the original variables
name1
continue
name2
to refer to the previous objects.
Changing the parameters passed to the constructor.
The constructor cannot modify the passed parameters of basic types. However, the constructor can modify the state of the object passed as a parameter. For example, consider the following program: File
Salary1.java
class Salary1
{
Salary1(int x)
{
x = x * 3;
System.out.println("x="+x);
}
public static void main(String args[])
{
int value = 1000;
Salary1 s1 = new Salary1(value);
System.out.println("value="+value);
}
}
The output of the program is:
x=3000
value=1000
Obviously, this method will not change the main type parameter. Therefore, after calling the constructor, the value of the variable
value
remains equal to
1000
. Essentially three things happen:
- The variable
x
is initialized with a copy of the parameter value value
(i.e., a number 1000
).
- The value of the variable
x
is tripled - it is now equal to 3000
. However, the value of the variable value
remains equal to 1000
.
- The constructor terminates and the variable
x
is no longer used.
In the following example, the employee's salary is successfully tripled because the value of an object reference is passed as a parameter to the method. File
Salary2.java
class Salary2
{
int value = 1000;
Salary2()
{
}
Salary2(Salary2 x)
{
x.value = x.value * 3;
}
public static void main(String args[])
{
Salary2 s1 = new Salary2();
Salary2 s2 = new Salary2(s1);
System.out.println("s1.value=" +s1.value);
System.out.println("s2.value="+s2.value);
}
}
The output of the program is:
s1.value=3000
s2.value=1000
The value of the object reference is used as a parameter. When executing the line
Salary2 s2 = new Salary2(s1)
; the constructor
Salary2(Salary x)
will be passed the value of a reference to the variable object
s1
, and the constructor will effectively triple the salary for
s1.value
, since even the copy
(Salary x)
created inside the constructor points to the variable object
s1
.
Constructors parameterized by primitives.
If the parameters of an overloaded constructor use a primitive that can be narrowed (for example
int <- double
), then calling a method with a narrowed value is possible, despite the fact that there is no method overloaded with such a parameter. For example: File
Primitive.java
class Primitive
{
Primitive(double d)
{
d = d + 10;
System.out.println("d="+d);
}
public static void main(String args[])
{
int i = 20;
Primitive s1 = new Primitive(i);
}
}
The output of the program is:
d=30.0
Despite the fact that the class
Primitive
does not have a constructor that has a type parameter
int
, a constructor with an input parameter will work
double
. Before calling the constructor, the variable
i
will be expanded from type
int
to type
double
. The opposite option, when the variable
i
would be of type
double
, and the constructor would only have a parameter
int
, in this situation would lead to a compilation error.
Constructor call and operatornew
The constructor is always called by the operator
new
. When a constructor is called with the operator
new
, the constructor always generates a reference to a new object. It is impossible to force the constructor to form a reference to an already existing object instead of a reference to a new object, except by substituting the object being deserialized. And with the new operator, instead of a reference to a new object, it is impossible to form a reference to an already existing object. For example: File
Salary3.java
class Salary3
{
int value = 1000;
Salary3()
{
}
Salary3(Salary3 x)
{
x.value = x.value * 3;
}
public static void main(String args[])
{
Salary3 s1 = new Salary3();
System.out.println("First object creation: "+s1.value);
Salary3 s2 = new Salary3(s1);
System.out.println("Second object creation: "+s2.value);
System.out.println("What's happend with first object?:"+s1.value);
Salary3 s3 = new Salary3(s1);
System.out.println("Third object creation: "+s3.value);
System.out.println("What's happend with first object?:"+s1.value);
}
}
The output of the program is:
First object creation: 1000
Second object creation: 1000
What's happend with first object?: 3000
Third object creation: 1000
What's happend with first object?: 9000
First, using the line
Salary3 s1 = new Salary3()
; a new object is created. Next, if using the line
Salary3 s2 = new Salary3(s1)
; or strings
Salary3 s3 = new Salary3(s1)
; it would be possible to create a link to an already existing object, then
s1.value s2.value
they
s3.value
would store the same value
1000
. Actually in the line
Salary3 s2 = new Salary3(s1)
; a new object for the variable will be created
s2
and the state of the object for the variable will change
s1
by passing its reference value to the object in the constructor parameter. This can be verified by the output results. And when executing the line
Salary3 s3 = new Salary3(s1)
; a NEW object for the variable will be created
s3
and the state of the object for the variable will change again
s1
.
Constructors and initialization blocks, sequence of actions when calling a constructor
The section
Creating an Object and Constructors lists the general actions that are performed when creating an object. Among them are the processes of initializing class fields and working out the class constructor, which in turn also have an internal order:
- All data fields are initialized to their default values (0, false, or null).
- All field initializers and initialization blocks are executed in the order they are listed in the class declaration.
- If another constructor is called on the first line of a constructor, then the called constructor is executed.
- The body of the constructor is executed.
The constructor is related to initialization because in Java there are three ways to initialize a field in a class:
- assign a value in the declaration;
- assign values in the initialization block;
- set its value in the constructor.
Naturally, you need to organize the initialization code so that it is easy to understand. The following class is given as an example:
class Initialization
{
int i;
short z = 10;
static int x;
static float y;
static
{
x = 2000;
y = 3.141;
}
Initialization()
{
System.out.println("i="+i);
System.out.println("z="+z);
z = 20;
System.out.println("z="+z);
}
}
In the example above, the variables are initialized in the following order: static variables are initialized first
x
with
y
default values. Next, the static initialization block is executed. Then the variable is initialized
i
to the default value and the variable is initialized
z
. Next, the designer gets to work. Calling class constructors should not depend on the order in which fields are declared. This may lead to errors.
Constructors and inheritance
Constructors are not inherited. For example:
public class Example
{
Example()
{
}
public void sayHi()
{
system.out.println("Hi");
}
}
public class SubClass extends Example
{
}
The class
SubClass
automatically inherits the method
sayHi()
defined in the parent class. At the same time, the constructor
Example()
of the parent class is not inherited by its descendant
SubClass
.
Keyword this
in constructors
Constructors are used
this
to refer to another constructor in the same class, but with a different list of parameters. If the constructor uses the keyword
this
, then it must be on the first line; ignoring this rule will result in a compiler error. For example: File
ThisDemo.java
public class ThisDemo
{
String name;
ThisDemo(String s)
{
name = s;
System.out.println(name);
}
ThisDemo()
{
this("John");
}
public static void main(String args[])
{
ThisDemo td1 = new ThisDemo("Mary");
ThisDemo td2 = new ThisDemo();
}
}
The output of the program is:
Mary
John
In this example there are two constructors. The first one receives a string argument. The second one does not receive any arguments, it simply calls the first constructor using the default name "John". Thus, you can use constructors to initialize field values explicitly and by default, which is often necessary in programs.
Keyword super
in constructors
Constructors are used
super
to call a superclass constructor. If the constructor uses
super
, then this call must be on the first line, otherwise the compiler will throw an error. Below is an example: File
SuperClassDemo.java
public class SuperClassDemo
{
SuperClassDemo()
{
}
}
class Child extends SuperClassDemo
{
Child()
{
super();
}
}
In this simple example, the constructor
Child()
contains a call
super()
that creates an instance of the class
SuperClassDemo
, in addition to the class
Child
. Because
super
it must be the first statement executed in a subclass constructor, this order is always the same and does not depend on whether
super()
. If it is not used, then the default (no parameters) constructor of each superclass, starting with the base class, will be executed first. The following program demonstrates when constructors are executed. File
Call.java
class A
{
A()
{
System.out.println("Inside A constructor.");
}
}
class B extends A
{
B()
{
System.out.println("Inside B constructor.");
}
}
class C extends B
{
C()
{
System.out.println("Inside C constructor.");
}
}
class Call
{
public static void main(String args[])
{
C c = new C();
}
}
Output from this program:
Inside A constructor.
Inside B constructor.
Inside C constructor.
Constructors are called in order of class subordination. This makes some sense. Since the superclass has no knowledge of any subclass, any initialization it needs to perform is separate. If possible, it should precede any initialization performed by the subclass. That's why it should be done first.
Customizable constructors
The run-time type identification mechanism is one of the powerful core principles of the Java language that implements polymorphism. However, such a mechanism does not protect the developer from incompatible type casting in some cases. The most common case is the manipulation of a group of objects, the various types of which are unknown in advance and are determined at run time. Since errors associated with type incompatibility can only appear at the runtime stage, this makes them difficult to find and eliminate. The introduction of custom types in Java 2 5.0 moves some of these errors from runtime to compile time and provides some of the missing type safety. There is no need for explicit type casting when moving from a type
Object
to a concrete type. It should be kept in mind that type customization tools work only with objects and do not apply to primitive data types that lie outside the class inheritance tree. With custom types, all casts are performed automatically and behind the scenes. This allows you to protect against type mismatches and reuse code much more often. Custom types can be used in constructors. Constructors can be custom even if their class is not a custom type. For example:
class GenConstructor
{
private double val;
<T extends Number> GenConstructor(T arg)
{
val = arg.doubleValue();
}
void printValue()
{
System.out.println("val: "+val);
}
}
class GenConstructorDemo
{
public static void main(String args[])
{
GenConstructor gc1 = new GenConstructor(100);
GenConstructor gc2 = new GenConstructor(123.5F);
gc1.printValue();
gc2.printValue();
}
}
Because the constructor
GenConstructor
specifies a custom type parameter that must be a derived class from class
Number
, it can be called from any
GO TO FULL VERSION