Java Language Basics
Java Language Basics
Why static?
Notice that if you do not make main() method static, there is no compilation error. You will runtime error.
Why void?
Then there is no use of returning any value to JVM, who actually invokes this method. It simply doesn’t need any returning value.
We write the Java source code in Simple.Java file using an editor or IDE (integrated
development environment) e.g. Eclipse or IntelliJ Idea.
Program has to be compiled into bytecode. Java compiler (javac) compiles the sourcecode
to Simple.class file.
This class file can be executed in any platform/OS by JVM (Java virtual machine).
JVM translates bytecode into native machine code which machines can execute.
In the real world, JVM is a specification that provides a runtime environment in which Java
bytecode can be executed. Different vendors provide different implementations of this
specification. For example, this wiki page lists down different JVM implementations.
The most popular implementation of JVM is Hotspot which is owned and provided by Oracle
Corporation. (Previously by Sun Microsystems, Inc.).
JVM delivers the optimal performance for Java applications using many advanced techniques,
incorporating a state-of-the-art memory model, garbage collector, and adaptive optimizer.
JVM comes in two different flavors – client and server. Although the Server and the Client VMs
are similar, the Server VM has been specially tuned to maximize peak operating speed. It is
intended for executing long-running server applications, which need the fastest possible
operating speed more than a fast start-up time or smaller runtime memory footprint. Developers
can choose which system they want by specifying -client or -server.
The JVM is called virtual because it provides a machine interface that does not depend on the
underlying operating system and machine hardware architecture. This independence from
hardware and the operating system is a cornerstone of the write-once-run-anywhere value of Java
programs.
JVM Architecture
2.1.1. Class Loader
The class loader is a subsystem used for loading class files. It performs three primary functions,
i.e. class loading, linking, and initialization.
Loading
To load classes, JVM has 3 kind of class loaders. Bootstrap, extension and application class
loader.
When loading a class file, JVM finds out a dependency for some arbitrary class XYZ.class.
First bootstrap class loader tries to find the class. It scans the rt.jar file in JRE lib folder.
If class is not found then extension class loader searches the class file in inside jre\lib\
ext folder.
Again if class is not found then application classloader searches all the Jar files and classes
in CLASSPATH environment variable of system.
If class is found by any loader then class is loaded by class loader;
else ClassNotFoundException is thrown.
Linking
After class is loaded by the classloader, linking is performed. A bytecode verifier will verify
whether the generated bytecode is proper or not. If verification fails we will get a verification
error. It also performs the memory allocation to static variables and methods found in the class.
Initialization
This is the final phase of class loading, here all static variable will be assigned with the
original values, and the static blocks will be executed.
Method Area stores class structures like metadata, the constant runtime pool, and the code for
methods.
Heap stores all objects that are created during application execution.
Stacks store local variables, and intermediate results. All such variables are local to the thread by
which they are created. Each thread has its own JVM stack, created simultaneously as the thread
is created. So all such local variable are called thread-local variables.
PC register store the physical memory address of the statements which is currently executing. In
Java, each thread has its separate PC register.
Java supports and uses native code as well. Many low level code is written in languages like C
and C++. Native method stacks hold the instruction of native code.
2.2. JVM Execution Engine
All code assigned to JVM is executed by an execution engine. The execution engine reads the
byte code and executes one by one. It uses two inbuilt interpreter and JIT compiler to convert
the bytecode to machine code and execute it.
Platform Specific Interpreters
With JVM, both interpreter and compiler produce native code. The difference is in how they
generate the native code, how optimized it is as well how costly the optimization is.
2.2.1. Interpreter
A JVM interpreter pretty much converts each byte-code instruction to corresponding native
instruction by looking up a predefined JVM-instruction to machine instruction mapping.
It directly executes the bytecode and does not perform any optimization.
The JIT compiler is enabled by default. You can disable the JIT compiler, in which case the
entire Java program will be interpreted. Disabling the JIT compiler is not recommended except
to diagnose or workaround JIT compilation problems.
3. What is JRE?
The Java Runtime Environment (JRE) is a software package that bundles the libraries (jars)
and the Java Virtual Machine, and other components to run applications written in Java. JVM is
just a part of JRE distributions.
To execute any Java application, you need JRE installed in the machine. It’s the minimum
requirement to run Java applications on any computer.
For example, you cannot install a 64-bit JRE distribution on 32-bit machine. Similarly, JRE
distribution for Windows will not work in Linux; and vice-versa.
4. What is JDK?
JDK is a superset of JRE. JDK contains everything that JRE has along with development tools
for developing, debugging, and monitoring Java applications. You need JDK when you need to
develop Java applications.
appletviewer – this tool can be used to run and debug Java applets without a web browser
apt – the annotation-processing tool
extcheck – a utility that detects JAR file conflicts
javadoc – the documentation generator, which automatically generates documentation from
source code comments
jar – the archiver, which packages related class libraries into a single JAR file. This tool also
helps manage JAR files
jarsigner – the jar signing and verification tool
javap – the class file disassembler
javaws – the Java Web Start launcher for JNLP applications
JConsole – Java Monitoring and Management Console
jhat – Java Heap Analysis Tool
jrunscript – Java command-line script shell
jstack – utility that prints Java stack traces of Java threads
keytool – tool for manipulating the keystore
policytool – the policy creation and management tool
xjc – Part of the Java API for XML Binding (JAXB) API. It accepts an XML schema and
generates Java classes
Same as JREs, JDKs are also platform dependent. So take care when you download the JDK
package for your machine.
The classes are the template that describes the state and behavior of its objects. A class can
be used to create multiple objects. which are similar in structure but can have different states.
}
A class declaration may have zero or more modifiers.
The keyword class is used to declare a class.
The <<class name>> is a user-defined name of the class, which should be a valid identifier.
Each class has a body, which is specified inside a pair of braces ({ … }).
The body of a class contains its different components, for example, fields, methods, etc.
For example,
}
2.2. Types of Classes
In Java, we can have two types of classes:
1. Abstract class – These classes are abstract. These are incomplete classes. It means you cannot
create an instance of this class. You can only extend these classes to complete their specification.
2. Non-abstract class – These classes define their full state and behavior. They are complete
classes. You can create objects of this class.
3. Components of a Java Class
In Java, classes are used as templates to create objects. A class in Java may consist of five
primary components. i.e.
Fields
Methods
Constructors
Static initializers
Instance initializers
Fields and methods are also known as class members. Constructors and both initializers are
used during the initialization of the class i.e. creating objects using the class template.
Constructors are used for creating objects of a class. We must have at least one constructor for a
class (if we don’t declare explicitly then JVM injects the default constructor for us).
Initializers are used to initialize fields of a class. We can have zero or more initializers of static
or instance types.
3.1. Fields
Fields of a class represent properties (also called state attributes) of objects of that class. The
fields are declared inside the body of the class. The general syntax to declare a field in a class is:
Suppose every object of the ‘Human‘ class has two properties: name and gender.
The Human class should include declarations of two fields: one to represent the name and one to
express gender.
}
Here the Human class declares two fields: name and gender. Both fields are of the String type.
Every instance (or object) of the Human class will have a copy of these two fields.
System.out.println("I am eating");
3.3. Constructors
A constructor is a named block of code that is used to initialize an object of a class immediately
after the object is created. The general syntax for a constructor declaration is:
}
A constructor can have its access modifier as public, private, protected, or default (no modifier).
The constructor name is the same as the simple name of the class.
The constructor name is followed by a pair of opening and closing parentheses, which may
include parameters.
Optionally, the closing parenthesis may be followed by the keyword throws, which in turn is
followed by a comma-separated list of exceptions.
Unlike a method, a constructor does not have a return type.
We cannot even specify void as a return type for a constructor. If there is any return type, then it
is a method.
Remember that if the name of a construct is the same as the simple name of the class, it could be
a method or a constructor. If it specifies a return type, it is a method. If it does not specify a return
type, it is a constructor.
3.4. Instance Initialization Block
We saw that a constructor is used to initialize an instance of a class. An instance initialization
block, also called instance initializer, is also used to initialize objects of a class.
An instance initializer is simply a block of code inside the body of a class, but outside of any
methods or constructors. An instance initializer does not have a name. Its code is simply placed
inside an opening brace and a closing brace.
Note that an instance initializer is executed in the instance context, and the keyword this is
available inside the instance initializer.
}
we can have multiple instance initializers for a class.
All initializers are executed automatically in textual order for every object we create.
Code for all instance initializers is executed before any constructor.
An instance initializer cannot have a return statement.
It cannot throw checked exceptions unless all declared constructors list those checked exceptions
in their throws clause.
public class Main {
//instance initializer
//constructor
public Main()
System.out.println("Inside constructor");
new Main();
}
Program output:
Inside constructor
static {
//constructor
public Main()
System.out.println("Inside constructor");
new Main();
}
The program output:
Inside constructor
//e.g.
The new operator is followed by a call to the constructor of the class whose instance is being
created. The new operator creates an instance of a class by allocating the memory in a heap.
5. The ‘null’ Reference Type
Java has a special reference type called null type. It has no name. Therefore, we cannot define a
variable of the null reference type. The null reference type has only one value defined by Java,
which is the null literal. It is simply null.
The null reference type is assignment compatible with any other reference type. That is, we can
assign a null value to a variable of any reference type. Practically, a null value stored in
a reference type variable means that the reference variable is referring to no object.
That’s all for this very basic tutorial about creating classes in java.
4. Java Data Types with Examples
1. How to Declare a Variable in Java?
In Java, typically datatypes are associated with variables. A variable declaration has three parts:
The data type of the variable determines the range of the values that the memory location can
hold. Therefore, the amount of memory allocated for a variable depends on its data type.
For example, 32 bits of memory is allocated for a variable of the 'int' data type.
Java is a statically-typed language. This means all variables MUST be declared before they
can be used.
In Java SE 7 and later, any number of underscore characters ('_') can appear anywhere between
digits in a numerical literal. e.g. 10_000_000 is a valid number in Java. Read More
2.1.1. Type Conversion between Primitives
Except boolean, we can assign a primitive value to another primitive type. Though, sometimes it
may result in data loss when a primitive of large memory capacity is assigned to a primitive with
a smaller memory capacity.
It’s just like you are transferring water from a large vessel and putting it in a smaller vessel, so
water loss is natural.
int counter = 20_000_000;
System.out.println(counter); //20000000
System.out.println(shortCounter); //11520
System.out.println(longCounter); //20000000
Notice that when Java detects that type conversion may result in data loss (bigger data type to
smaller one), then gives a type-mismatch error and explicitly asks for type casting (e.g. ‘int’ to
‘short’ assignment). It helps in detecting and resolving accidental data loss assignments.
For example, java.lang.String is a class defined in the Java library and you can use it to
manipulate text (sequence of characters). You declare a reference variable of type String as:
First, a memory block is allocated, and the name of the variable str is associated with that
memory location. This process is the same as declaring a primitive data type variable.
The second part of code creates a new String object in memory with text "Hello World !!" and
stores the reference (or memory address) of the String object into the variable 'str'.
2.2.1. Multiple variables can refer to the same object
You can also assign the reference of an object stored in one reference variable to another
reference variable. In such cases, both reference variables will refer to the same object in
memory.
String str1;
String str2;
str2 = str1;
3. Wrapper Classes
A wrapper class is a class whose object wraps or contains primitive data types. In other words,
we can wrap a primitive value into a wrapper class object.
Please note that Java has one wrapper class mapped to each primitive data type.
For example, java.lang.Integer class is the object version of int data type. Similarly, we have a
total of 8 wrapper classes for all 8 primitive data types.
The wrapper class names are the same as primitive data types, only starting with capital letters.
These wrapper classes are Boolean, Byte, Short, Character, Integer, Long, Float and Double.
4. Auto-boxing
In Java, you can assign a primitive type value to a wrapper class, directly. For example, you can
assign an int value to Integer class reference.
It’s worth mentioning that all wrapper class instances are immutable. They also maintain
an internal cache for performance reason.
The Java programming language uses both “fields” and “variables” as part of its
terminology. Fields refer to variables declared outside methods, and variables are
referred to declarations inside methods, including method arguments.
1. What is a Variable?
As the term suggests, a variable is a placeholder whose value can vary during the
runtime. In Java, a variable is a named reference to a memory area where the value
of the variable is stored.
How a
variable works
The given syntax explains how to declare a variable in Java. The left part of this
statement describes the variable, and the right part describes something that is
assigned to it.
int i = 1, j = 2;
int i;
i = 1;
1.2. Example
Once a variable has been declared, its value can be accessed and modified using the
name.
In the following example below, we declare two ‘int‘ variables and assign their addition
result to a third variable ‘sum‘.
int i = 10;
int j = 10;
int sum = i + j;
2. Type Inference
Since Java 10, you can write var instead of a specific type to force automatic type
inference based on the type of assigned value:
int i = 10;
long j = i;
System.out.println( i );
System.out.println( j );
Program output.
10
10
3.2. Narrowing
When a larger primitive type value is assigned in a smaller size primitive data type, this
is called the narrowing of the variable. It can cause some data loss due to fewer bits
available to store the data. It requires explicit type-casting to the required data type.
In the given example, int type variable is assigned to byte type variable with data loss.
int i=198;
byte j=(byte)i;
System.out.println( i );
System.out.println( j );
Program output.
198
-58
}
4.2. Static Variables
Also, known as class variables. It is any field declared with the static modifier. It means
that there is exactly one copy of this variable in existence, regardless of how many
times the class has been instantiated.
}
A variable declared as “public static” can be treated as global variable in java.
}
4.4. Method Arguments
An argument is a variable that is passed to a method when the method is called.
Arguments are also only accessible inside the method that declares them, although
a value is assigned to them when the method is called.
print( 40 );
System.out.println ( param );
}
5. Difference between Instance and Class Variables
Instance variables (non-static fields) are unique to each instance of a class.
Class variables (static fields) are fields declared with the static modifier; there is
exactly one copy of a class variable, regardless of how many times the class has been
instantiated.
To access the instance variable, you MUST create a new instance of the class. Class
variables are accessible through class reference, and do not require creating an object
instance. Take an example. We have a class Data with one instance variable and one
class variable.
public class Data
}
We can access both variables in a given way.
}
6. Variable Naming Conventions in Java
There are a few rules and conventions related to how to define variable names.
Java variable names are case-sensitive. The variable name employee is not the
same as Employee or EMPLOYEE.
Java variable names must start with a letter, or the $ or _ character.
After the first character in a Java variable name, the name can also contain
numbers, $ or _ characters.
Variable names cannot be reserved keywords in Java. For instance, the words break
or continue are reserved words in Java. Therefore you cannot name your variables to
them.
Variable names should written in lowercase. For instance, variable or apple.
If variable names consist of multiple words, then follow camelcase notation. For
instance, deptName or firstName.
Static final fields (constants) should be named in all UPPERCASE, typically using an
(underscore) _ to separate the words in the name. For
example LOGGER or INTEREST_RATE.
7. Summary
Variables in Java are used to store values of different data types. They need to be
declared with a type and a name. Variables can be modified after declaration, but the
new value must be of the same type as the old one. In Java 10, the var keyword was
introduced for automatic type inference.
6. How to Pass and Access Command
Line Arguments in Java
When a Java program is launched from the terminal or command line, the
arguments passed at the time of launching are called command-line arguments.
A Java program can be launched either from a console or an editor e.g. Eclipse.
To launch a program we use “java ClassName” command from the command
prompt or system console.
In the given example, we are passing 5 parameters to the Main class MyClass.
MyClass has the main() method which accepts these arguments in form of
a String array.
System.out.println( args[i] );
}
$ java Main 1 2 3 4
#prints
Since Java 5, we can use varargs in the main() method. Even though, accessing the
variables remains the same.
//...
}
3. Conclusion
Command line args can be used to specify configuration information while launching the
application.
There is no restriction on the maximum number of arguments. We can specify any
number of arguments.
Arguments are passed as strings.
Passed arguments are retrieved as the string array in the main() method argument.
A wrapper class wraps (encloses) around a primitive datatype and gives it an object
appearance. Wherever the primitive datatype is required as an object type, this type
wrapper can be used.
Wrapper classes include methods to unwrap the object and give back the data type.
The type wrappers classes are part of java.lang package.
Each primitive type has a corresponding wrapper class.
double Double
float Float
long Long
int Integer
short Short
byte Byte
char Character
boolean Boolean
When two methods wants to refer to the same instance of an primitive type, then pass
wrapper class as method arguments.
Java Generics works only with object types and does not support primitive types.
Java Collections deal only with objects; to store a primitive type in one of these
classes, you need to wrap the primitive type in a class.
When you want to refer null from data type, the you need object. Primitives cannot
have null as value.
3. Conversions
3.1. Converting Primitive Types to Wrapper Classes
There are two ways for converting a primitive type into an instance of the corresponding
wrapper class –
1. Using constrcutors
2. Using static factory methods
// 1. using constructor
Java wrapper classes use internal caching which returns internally cached values upto
a limit. This internal caching of instances makes the wrapper classes more efficient in
perfomance and memory unilization.
3.2. Converting Wrapper Class to Primitive Type
Converting from wrapper class to primitive type is simple. We can use the
corresponding instance methods to get the primitive type.
e.g. intValue(), doubleValue(), shortValue() etc.
Autoboxing
Auto-Unboxing
4.1. Autoboxing
Autoboxing is the automatic conversion of the primitive types into their
corresponding wrapper class.
For example, converting an int to an Integer, a char to a Character, and so on.
}
In given example, integerList is a List of Integers. It is not a list of primitive type int
values.
Here compiler automatically creates an Integer object from int and adds the object
to integerList. Thus, the previous code turns into the following at runtime:
integerList.add(Integer.valueOf(i)); //autoboxing
}
4.2. Unboxing
Unboxing happens when the conversion happens from wrapper class to its
corresponding primitive type. It means we can pass or assign a wrapper object to an
argument or reference accepting primitive type.
int sum = 0;
if (i % 2 == 0)
return sum;
}
In the above example, the remainder (%) and unary plus (+=) operators do not
apply on Integer objects. The compiler automatically converts an Integer to an int at
runtime by invoking the intValue() method.
Autoboxing and unboxing lets developers write cleaner code, make it easier to read.
8. Types of Statements in Java
A statement specifies an action in a Java program. For example, a statement may
tell the add of values of x and y and assign their sum to the variable z. It then prints a
message to the standard output or writes data to a file, etc.
Declaration statement
Expression statement
Control flow statement
1. Declaration Statement
A declaration statement is used to declare a variable. For example,
int num;
String str;
2. Expression Statement
An expression with a semicolon at the end is called an expression statement. For
example,
num++;
++num;
num--;
--num;
//Assignment expressions
num = 100;
num *= 10;
System.out.println("This is a statement");
someMethod(param1, param2);
All of these are possible in Java using flow control statements. The if statement, while
loop statement and for loop statement are examples of control flow statements.
if(condition) {
System.out.println("Condition is true");
} else {
System.out.println("Condition is false");
}
9. Block Statements in Java
A block statement is a sequence of zero or more statements enclosed in braces.
A block statement is generally used to group together several statements, so
they can be used in a situation that requires you to use a single statement.
var++;
var++;
}
// A compile-time error. var has been declared inside a block and
Syetem.out.println(var);
Similarly, you can also nest a block statement inside another block statement. All the
variables declared in the enclosing blocks (outer blocks) are available to the
enclosed blocks (inner blocks). However, the variables declared in the enclosed
inner blocks are not available in the enclosed outer blocks.
Please note that when block statements are declared in such a way, non-static
blocks will be executed every time an instance of the class is created.
The static blocks will be executed only once when the class is loaded by JVM
class loaders (Much like other static variables present at the class level).
public MyDemoAction(){
System.out.println("MyDemoAction Constructor");
static {
System.out.println("HowToDoInJava.com");
}
10. Java System Properties
Java maintains a Set of system properties that can be accessed in the runtime by
executing programs. Each system property is a key-value pair. For example, one
such system property is “java.version”=”1.7.0_09“.
Please note that access to system properties can be restricted by the Java security
manager and policy file. By default, Java programs have unrestricted access to all
the system properties.
1. Java System Properties List
The following is a list of important system properties in each category.
JRE library search path for search native libraries. It is usually but
java.library.path
not necessarily taken from the environment variable PATH.
java.class.path JRE classpath e.g., '.' (dot – used for current working directory).
java.runtime.versi
JRE version, e.g. 1.7.0_09-b05.
on
symbol for end-of-line (or new line). The default is "\r\n" for windows
line.separator
or "\n" for Unix/Mac OS X.
pros.list(System.out);
2.2. Get a system property value by its key
System.getProperty("java.home");
System.setProperty("custom_key", "custom_value");
Static Import Statements in
Java
In Java, the import statements imports classes from packages, so they can be used in
the current class without the package reference. Similarly, the static import statements
import the static members of the a class and allows to be used without class reference.
single-static import: imports one static member (field or method) from a class.
static-import-on-demand: imports all static members from a class. These are denoted
with an asterisk (*).
1.1. Syntax
The general syntax of static import statements is as follows:
1.2. Example
For example, we can print the messages in the standard output using
the System.out.println() method. System is a class in java.lang package that has
a static variable named out.
out.println("Hello World!");
Suppose there are two classes, package1.Class1 and package2.Class2. Both classes
have a static method called methodA. The following code will
use package1.Class1.methodA() method because it is imported using the single-static
import declaration:
}
}
package package1;
public class A {
System.out.println("package1.A.test()");
package package2;
import static package1.A.test;
test(); //package2.Test.test()
System.out.println("package2.Test.test()");
3. Conclusion
The static imports help us use simple names of static members to make the program
simpler to write and read. These are more of a syntactical sugar rather than providing
any execution benefits in runtime.
Whenever it is invoked on the same object more than once during an execution of a
Java application, the hashCode() must consistently return the same integer,
provided no information used in equals comparisons on the object is modified.
This integer need not remain consistent between the two executions of the same
application or program.
If two objects are equal according to the equals() method, then calling
the hashCode() on each of the two objects must produce the same integer result.
It is not required that if two objects are unequal according to the equals(), then
calling the hashCode() on each of the both objects must produce distinct
integer results.
However, the programmer should be aware that producing distinct integer results for
unequal objects may improve the performance of hash tables.
}
Above Employee class has some fundamental attributes and their accessor methods.
Now consider a simple situation where you need to compare two Employee objects.
Both employee objects have the same id.
e1.setId(100);
e2.setId(100);
System.out.println(e1.equals(e2)); //false
}
No prize for guessing. The above method will print “false.”
But is it correct after knowing that both objects represent the same employee? In a real-
time application, this should return true.
if(o == null)
return false;
if (o == this)
return true;
if (getClass() != o.getClass())
return false;
}
Employee e = (Employee) o;
Add this method to the Employee class, and EqualsTest will start returning "true".
So are we done? Not yet. Let’s test the above-modified Employee class again in a
different way.
import java.util.HashSet;
import java.util.Set;
e1.setId(100);
e2.setId(100);
//Prints 'true'
System.out.println(e1.equals(e2));
employees.add(e1);
employees.add(e2);
}
The above example prints two objects in the second print statement.
If both employee objects have been equal, in a Set which stores unique objects, there
must be only one instance inside HashSet because both objects refer to the same
employee. What is it we are missing??
@Override
int result = 1;
return result;
}
Once the above method is added in Employee class, the second statement starts
printing only a single object in the second statement and thus validating the true
equality of e1 and e2.
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
@Override
}
@Override
if (o == null)
return false;
if (o == this)
return true;
if (o.getClass() != getClass())
return false;
Employee e = (Employee) o;
append(getId(), e.getId()).
isEquals();
Right click on Java file -> Source -> Generate hashCode() and equals() …
Generate hashCode() and equals() In Eclipse
But if uses e1.getId() == e2.getId(), we can be sure even if the field is lazy-loaded,
calling the field getter will populate the field first.
Immutable Classes in Java
In Java, an immutable object is one whose state can not be changed once created.
Immutable objects are persistent views of their data without a direct option to change it.
To change the state, we must create a new copy of such an object with the intended
changes.
In this post, we will learn immutability in detail, creating an immutable object and its
advantages.
1. What is Immutability?
Immutability is a characteristic of Java objects that makes them immutable to future
changes once they have been initialized. Its internal state cannot be changed in any
way.
Take the example of java.lang.String class which is an immutable class. Once a String
is created, there is no way we can change the content of that String. Every public API
in String class returns a new String with the modified content. The original String always
remains the same.
2. Immutability in Collections
Similarly, for Collections, Java provides a certain degree of immutability with three
options:
Unmodifiable collections
Immutable collection factory methods (Java 9+)
Immutable copies (Java 10+)
Collections.unmodifiableList(recordList); //Unmodifiable list
List.copyOf(recordList); //Java 10
Note that such collections are only shallowly immutable, meaning that we can not
add or remove any elements, but the collection elements themselves aren’t guaranteed
to be immutable. If we hold the reference of a collection element, then we can change
the element’s state without affecting the collection.
In the following example, we cannot add or remove the list items, but we can change
the state of an existing item in the list.
list.get(0).setName("modified-value");
@Data
@NoArgsConstructor
@AllArgsConstructor
class Record {
long id;
String name;
To ensure complete immutability, we must make sure that we only add immutable
instances in the collections. This way, even if somebody gets a reference to an item
in the collection, it cannot change anything.
Do not provide setter methods. Setter methods are meant to change an object’s state,
which we want to prevent here.
Make all fields final and private. Fields declared private will not be accessible outside
the class, and making them final will ensure that we can not change them even
accidentally.
Do not allow subclasses to override methods. The easiest way is to declare the class
as final. Final classes in Java can not be extended.
Special attention to “immutable classes with mutable fields“. Always remember that
member fields will be either mutable or immutable. Values of immutable members
(primitives, wrapper classes, String etc) can be returned safely from the getter methods.
For mutable members (POJO, collections etc), we must copy the content into a
new Object before returning from the getter method.
Let us apply all the above rules to create an immutable custom class. Notice that we
are returning a new copy of ArrayList from the getTokens() method. By doing so,
we are hiding the original tokens list so no one can even get a reference of it and
change it.
this.id = id;
this.name = name;
this.tokens = tokens;
return name;
@Override
return "Record{" +
"id=" + id +
'}';
}
Now it’s time to test our class. We tried to add a new item to the tokens list, but the
original record and its list remain unchanged.
tokens.add("active");
record.getTokens().add("new token");
For example, we can rewrite the above Record class as follows. Note that records
generate the standard getters, so if we want to return a new copy of a mutable
reference, we must override the corresponding method.
}
}
tokens.add("active");
record.tokens().add("new token");
java.lang.String
Wrapper classes such as Integer, Long, Double etc
java.math.BigInteger and java.math.BigDecimal
Unmodifiable collections such as Collections.singletonMap()
java.lang.StackTraceElement
Java enums
java.util.Locale
java.util.UUID
Java 8 Date Time API – LocalDate, LocalTime etc.
record types
6. Advantages
Immutable objects provide a lot of advantages over mutable objects. Let us discuss
them.
Predictability: guarantees that objects won’t change due to coding mistakes or by 3rd
party libraries. As long as we reference a data structure, we know it is the same as at the
time of its creation.
Validity: is not needed to be tested again and again. Once we create the immutable
object and test its validity once, we know that it will be valid indefinitely.
Thread-safety: is achieved in the program as no thread can change immutable objects.
It helps in writing code in a simple manner without accidentally corrupting the shared
data objects.
Cacheability: can be applied to immutable objects without worrying about state changes
in the future. Optimization techniques, like memoization, are only possible with
immutable data structures.
7. Conclusion
This tutorial taught us to create an immutable java class with mutable objects and
immutable fields.
If you go to java download page, it lists various installation packages mentioning 32-bit
packages or 64-bit packages for various platforms such as Linux or windows. Many
times we worry that what package we are eligible to download and install in our systems
so that our java code runs fine. In this post, I will try to put some light on these different
terms and also I will try to answer some obvious questions.
You already read the basic difference between 64-bit and 32-bit
computing/architectures. Now let’s expand our understanding and go deeper into bits
and bytes.
Tech-savvy readers might know that modern chips support PAE, a processor
technology that allows the operating system to use a little bit more memory—up to 64
GB, but it also requires special application support that most applications don’t have or
necessarily need.
The 4 GB limit for Windows, at least, is also a factor of licensing. The home versions of
32-bit Windows, while technically being able to support PAE, have a hard limit of 4 GB
for licensing and driver compatibility reasons. I am pointing out “driver compatibility
reasons” because some specific applications which highly use native files (e.g. anti-
viruses) are built specifically for 32-bit/64-bit machines and native files are not
compatible with other machines.
The other thing to remember is that your BIOS and other device chips in the
motherboard such as video cards, also occupy some memory in same 4 GB space so
the actual memory available for use by your applications reduces further to around 1.5
GB only.
Windows 64-bit Home editions are still limited to 16 GB of RAM [ all because of
licensing reasons], but the Professional and Ultimate versions can use up to 192 GB of
RAM at present due to various compatibility issues.
The per-process limit for RAM is also greatly increased—on 64-bit Windows, instead of
a 2 GB limit, each application can access upto 8 TB of virtual memory without any
special configuration (besides it must be present in your system). It is a huge factor in
choosing your next machine when you consider applications like video editing or virtual
machines that may need to use enormous amounts of RAM.
So now we have a good understanding of 32-bit machines vs. 64-bit machines. Let’s
focus on stuff which is related mostly to java.
First of all – object headers are 12 bytes on 64-bit JVM. Secondly, object references
can be either 4 bytes or 8 bytes, depending on JVM flags and the size of the heap. This
definitely adds some overhead compared to the 8 bytes on headers on 32-bit and 4
bytes on references.
What it means in real life is that you have to be extra cautious when building heaps
larger than 12-16GB. Without fine-tuning and measuring you can easily introduce full
GC pauses spanning several minutes which can result in showstoppers.
If so, then how 32-bit applications run on 64-bit systems? The answer is that 64-bit
systems include a compatibility layer called WoW64, which actually switches the
processor back and forth between 32-bit and 64-bit modes depending on which
thread needs to execute; making 32-bit software run smoothly even in the 64-bit
environment.
Similarly, on 32-bit machine, the limit is 4 GB, and about only 1.5 GB is actually
available for user applications for the reasons stated above in the post.
There is a trick you can pull on 32-bit windows to reduce the kernel space and grow the
user space. You can use the /3GB parameter in your boot.ini. However, to actually use
this opportunity, the JVM must be compiled/linked using
the /LARGEADDRESSAWARE switch.
This unfortunately is not the case, at least with the Hotspot JVM. Until the latest JDK
releases the JVM is not compiled with this option. You are luckier if you are running on
a jRockit on post-2006 versions. In this case, you can enjoy up to 2.8-2.9 GB of the
heap size.
That’s all for this topic. Please drop a comment if something is not clear, or you simply
disagree with me.
Happy Learning !!
References:
https://community.oracle.com/thread/2497016?tstart=0
http://en.wikipedia.org/wiki/32-bit
http://en.wikipedia.org/wiki/64-bit_computing
Difference between Java.exe
and Javaw.exe
“java.exe” and “javaw.exe” are Java executables on the Windows platform. These
files are nearly identical versions of the Java Application Launcher utility. Both versions
of the launcher take the same arguments and options. The launcher is invoked with
“java” or “javaw” followed by launcher options, the class or Java archive (JAR) file name
and application arguments.
1. javaw.exe
This non-console version of the application launcher is used to launch java
applications usually with graphical user interfaces (GUIs). These applications have
windows with menus, buttons and other interactive elements. Essentially,
use javaw.exe when you don’t want a command prompt window to appear either to take
further input or show output.
The javaw.exe launcher will, however, display a dialog box with error information if a
launch of java application fails for some reason.
2. java.exe
The java.exe is very similar to javaw.exe. The console version of the launcher is
used for applications with text-based interfaces or that output text. Any application
launched with “java” will cause the command line waits for the application response till it
closes.
When launched using javaw, the application launches and the command line exits
immediately and is ready for the next command.
That’s the only noticeable difference between java.exe and javaw.exe. If you know of
any other noticeable differences, please share them with all of us.
Java View Bytecode of a Class
File
Many times, we need to understand what a compiler is doing under the hood. How the
java statements we are writing, will be reordered and executed. Also, we need to see
the byte code for learning purposes also, I do it seldom. In this tutorial, I am giving an
example of how to generate the byte code for a class file in java.
To demonstrate the example, I am using the java file created for my other tutorial
related to automatic resource management in java 7.
Folder view
A file bytecode.bc file will be generated at a given location. It will be something like this:
public com.howtodoinjava.java7.tryCatch.ResourceManagementInJava7();
Code:
0: aload_0
4: return
Code:
3: dup
7: dup
16: astore_1
17: aconst_null
18: astore_2
22: dup
26: dup
27: ldc #7 // String C:/temp/test2.txt
35: astore_3
36: aconst_null
37: astore 4
42: dup
46: dup
55: astore 5
57: aconst_null
58: astore 6
60: aload 5
65: aload 6
67: ifnull 90
70: aload 5
78: astore 7
80: aload 6
82: aload 7
90: aload 5
98: astore 8
100: aload 5
105: aload 6
110: aload 5
112: invokevirtual #9 // Method java/io/BufferedReader.close:()V
118: astore 9
120: aload 6
122: aload 9
130: aload 5
135: aload 8
137: athrow
138: aload_3
142: aload 4
147: aload_3
154: astore 5
156: aload 4
158: aload 5
166: aload_3
173: astore 5
175: aload 5
177: astore 4
179: aload 5
181: athrow
182: astore 10
184: aload_3
188: aload 4
193: aload_3
194: invokevirtual #9 // Method java/io/BufferedReader.close:()V
200: astore 11
202: aload 4
204: aload 11
212: aload_3
216: aload 10
218: athrow
219: aload_1
223: aload_2
227: aload_1
234: astore_3
235: aload_2
236: aload_3
243: aload_1
250: astore_3
251: aload_3
252: astore_2
253: aload_3
254: athrow
255: astore 12
257: aload_1
261: aload_2
265: aload_1
266: invokevirtual #9 // Method java/io/BufferedReader.close:()V
272: astore 13
274: aload_2
275: aload 13
283: aload_1
287: aload 12
289: athrow
293: astore_1
294: aload_1
298: return
Exception table:
70 75 78 Class java/lang/Throwable
98 100 98 any
}
Little-Endian and Big-Endian
in Java
We must have heard the terms Little-Endian and Big-Endian many times in your
engineering course. Let’s quickly recap the concept behind these words.
1. Little-Endian vs Big-Endian
These two terms are related to the direction of bytes in a word within CPU
architecture.
The “natural” order, where the less significant byte comes before the more
significant byte in memory, is called little-endian.
Many vendors like IBM, CRAY, and Sun preferred the reverse order that, of course, is
called big-endian.
2. Example of Ordering
For example, the 32-bit hex value 0x45679812 would be stored in memory as follows:
Address 00 01 02 03
-----------------------------------
Little-endian 12 98 67 45
Big-endian 45 67 98 12
3. Endian-ness in Java
The difference in endian-ness can be a problem when transferring data between two
machines.
The problem comes when you must exchange data files with some program not written
in Java that uses little-endian order, most commonly a program written in C. Some
platforms use big-endian order internally (Mac, IBM 390); some uses little-endian order
(Intel).
System.out.println(byteOrder); //LITTLE_ENDIAN
5. Conclusion
Java hides that internal endian-ness from us and gives us consistent results in all the
platforms.
But in programming languages, where code reads the data directly from the memory
areas using pointers, endian-ness could be an issue in cases where data is transferred
from one machine to another machine and both machines have different endian-ness.
Java Pass-by-Value or Pass-
by-Reference
Java is passed by value and not
pass-by-reference. If it had been
pass-by-reference, we should have
been able to C like swapping of
objects.
There has been a lot of debate on whether “java is pass by value or pass by
reference?“. Well, let us conclude last time, Java is passed by value and not pass-
by-reference. If it had been pass-by-reference, we should have been able to C like
swapping of objects, but we can’t do that in java. We know it already, right?
1. Java is Pass-by-value
When you pass an object to a method, its memory address is copied bit by bit to a new
reference variable, thus both pointing to the same instance. But if you change the
reference inside the method, the original reference will not change.
To prove it, let’s see how memory allocations happen in run time. It should solve the
slightest doubts if any. I am using the following program for demonstration of the
concept.
this.attribute = a;
return attribute;
this.attribute = attribute;
modifyReference(f); // It will change the object that the reference variable "f" refers to!
a = b;
}
public static void modifyReference(Foo c) {
c.setAttribute("c");
2. Analysis
Let us see what happens on runtime step by step when the above program runs:
This statement will create an instance of class Foo, with ‘attribute’ initialized to ‘f’. The
reference to this created instance is assigned to variable f;
When this executes then a reference of type Foo with a name a is declared and it’s
initially assigned to null.
changeReference(f);
As you call the method changeReference(), the reference a will be assigned to the
object which is passed as an argument.
This is an important point. Here, we have three reference variables, and when the
statement executes, a and b will point to the same instance created inside the method.
Note: f is unchanged, and it is continually pointing to instance, it was pointing originally.
NO CHANGE !!
modifyReference(Foo c);
Now when this statement executed a reference, c is created and assigned to the object
with attribute “f”.
c.setAttribute("c");
This will change the attribute of the object that reference c points to it, and the same
object that reference f points to it.
I hope that this explanation was clear enough to make your understanding better if it
was not already.
Happy Learning !!