Association,Aggregation,Composition

Association

Association is a relationship between two objects. In other words, association defines the multiplicity between objects. You may be aware of one-to-one, one-to-many, many-to-one, many-to-many all these words define an association between objects. Aggregation is a special form of association. Composition is a special form of aggregation.

Example: A Student and a Faculty are having an association.

Aggregation

Aggregation is a special case of association. A directional association between objects. When an object ‘has-a’ another object, then you have got an aggregation between them. Direction between them specified which object contains the other object. Aggregation is also called a “Has-a” relationship.

Composition

Composition is a special case of aggregation. In a more specific manner, a restricted aggregation is called composition. When an object contains the other object, if the contained object cannot exist without the existence of container object, then it is called composition.

Example: A class contains students. A student cannot exist without a class. There exists composition between class and students.

Difference between aggregation and composition

Composition is more restrictive. When there is a composition between two objects, the composed object cannot exist without the other object. This restriction is not there in aggregation. Though one object can contain the other object, there is no condition that the composed object must exist. The existence of the composed object is entirely optional. In both aggregation and composition, direction is must. The direction specifies, which object contains the other object.

Example: A Library contains students and books. Relationship between library and student is aggregation. Relationship between library and book is composition. A student can exist without a library and therefore it is aggregation. A book cannot exist without a library and therefore its a composition. For easy understanding I am picking this example. Don’t go deeper into example and justify relationships!

Generalization and Specialization

Generalization is a mechanism for combining similar classes of objects into a single, more general class. Generalization is a bottom-up process. Generalization and inheritance are powerful abstractions for sharing similarities among classes while preserving their differences.

Generalization identifies commonalities among a set of entities. The commonality may be of attributes, behavior, or both. Generalization/Specialization represents the is a relationship set , an essential element of the object oriented paradigm. The main idea in Generalization/Specialization is that one object class (the specialization) is a subset of another (the generalization). The direction of the is-a relationship goes from the specialization to the generalization, that is, it may be stated as, “Specialization Class is-a Generalization Class.”

It indicates that a superclass (generalization class) and subclass (specialization class) have common attributes, operations, and relationships. A superclass has the most general attributes, operations, and relationships that may be shared with subclasses. A subclass is a specialization of a superclass. A subclass may have more specialized attributes and operations. For example, a Shape superclass defines common attributes, operations, and relationships for a square, circle , and triangle, subclasses.

Interpreter Vs Compiler :

We generally write a computer program using a high-level language. A high-level language is one which is understandable by us humans. It contains words and phrases from the English (or other) language. But a computer does not understand high-level language. It only understands program written in 0's and 1's in binary, called the machine code. A program written in high-level language is called a source code. We need to convert the source code into machine code and this is accomplished my compilers and interpreters. Hence, a compiler or an interpreter is a program that converts program written in high-level language into machine code understood by the computer.

The difference between an interpreter and a compiler is given below:

 

Difference between Compiler and Interpreter

No Compiler Interpreter
1 Compiler Takes Entire program as input Interpreter Takes Single instruction as input .
2 Intermediate Object Code is Generated No Intermediate Object Code is Generated
3 Conditional Control Statements are Executes faster Conditional Control Statements are Executes slower
4 Memory Requirement : More (Since Object Code is Generated) Memory Requirement is Less
5 Program need not be compiled every time Every time higher level program is converted into lower level program
6 Errors are displayed after entire program is checked Errors are displayed for every instruction interpreted (if any)
7 Example : C Compiler Example : BASIC

String intern

When we use new keyword to create the String object in heap, it doesn’t go into the String Pool. When we call intern method, then it checks if any String with same value is already present in the pool or not. If there is already a String with same value, it returns the reference or else it places the String into the pool.

How to write an immutable Class?

Immutable objects are instances whose state doesn’t change after it has been initialized. For example, String is an immutable class and once instantiated its value never changes.

Immutable objects are good for caching purpose because you don’t need to worry about the value changes. Other benefit of immutable class is that it is inherently thread-safe, so you don’t need to worry about thread safety in case of multi-threaded environment.

Here I am providing a way to create immutable class via an example for better understanding.

To create a class immutable, you need to follow following steps:

  1. Declare the class as final so it can’t be extended.
  2. Make all fields private so that direct access is not allowed.
  3. Don’t provide setter methods for variables
  4. Make all mutable fields final so that it’s value can be assigned only once.
  5. Initialize all the fields via a constructor performing deep copy.
  6. Perform cloning of objects in the getter methods to return a copy rather than returning the actual object reference.

To understand points 4 and 5, let’s run the sample Final class that works well and values doesn’t get altered after instantiation.

FinalClassExample.java
package com.journaldev.java;
import java.util.HashMap;
import java.util.Iterator;
public final class FinalClassExample {
    private final int id;
    
    private final String name;
    
    private final HashMap<String,String> testMap;
    
    public int getId() {
        return id;
    }
    public String getName() {
        return name;
    }
    /**
     * Accessor function for mutable objects
     */
    public HashMap<String, String> getTestMap() {
        //return testMap;
        return (HashMap<String, String>) testMap.clone();
    }
    /**
     * Constructor performing Deep Copy
     * @param i
     * @param n
     * @param hm
     */
    
    public FinalClassExample(int i, String n, HashMap<String,String> hm){
        System.out.println("Performing Deep Copy for Object initialization");
        this.id=i;
        this.name=n;
        HashMap<String,String> tempMap=new HashMap<String,String>();
        String key;
        Iterator<String> it = hm.keySet().iterator();
        while(it.hasNext()){
            key=it.next();
            tempMap.put(key, hm.get(key));
        }
        this.testMap=tempMap;
    }
    
    
    /**
     * Constructor performing Shallow Copy
     * @param i
     * @param n
     * @param hm
     */
    /**
    public FinalClassExample(int i, String n, HashMap<String,String> hm){
        System.out.println("Performing Shallow Copy for Object initialization");
        this.id=i;
        this.name=n;
        this.testMap=hm;
    }
    */
    
    /**
     * To test the consequences of Shallow Copy and how to avoid it with Deep Copy for creating immutable classes
     * @param args
     */
    public static void main(String[] args) {
        HashMap<String, String> h1 = new HashMap<String,String>();
        h1.put("1", "first");
        h1.put("2", "second");
        
        String s = "original";
        
        int i=10;
        
        FinalClassExample ce = new FinalClassExample(i,s,h1);
        
        //Lets see whether its copy by field or reference
        System.out.println(s==ce.getName());
        System.out.println(h1 == ce.getTestMap());
        //print the ce values
        System.out.println("ce id:"+ce.getId());
        System.out.println("ce name:"+ce.getName());
        System.out.println("ce testMap:"+ce.getTestMap());
        //change the local variable values
        i=20;
        s="modified";
        h1.put("3", "third");
        //print the values again
        System.out.println("ce id after local variable change:"+ce.getId());
        System.out.println("ce name after local variable change:"+ce.getName());
        System.out.println("ce testMap after local variable change:"+ce.getTestMap());
        
        HashMap<String, String> hmTest = ce.getTestMap();
        hmTest.put("4", "new");
        
        System.out.println("ce testMap after changing variable from accessor methods:"+ce.getTestMap());
    }

Output of the above program is:

Performing Deep Copy for Object initialization
true
false
ce id:10
ce name:original
ce testMap:{2=second, 1=first}
ce id after local variable change:10
ce name after local variable change:original
ce testMap after local variable change:{2=second, 1=first}
ce testMap after changing variable from accessor methods:{2=second, 1=first}
Now lets comment the constructor providing deep copy and uncomment the constructor providing shallow copy. Also uncomment the return statement in getTestMap() method that returns the actual object reference and then execute the program once again.
Performing Shallow Copy for Object initialization
true
true
ce id:10
ce name:original
ce testMap:{2=second, 1=first}
ce id after local variable change:10
ce name after local variable change:original
ce testMap after local variable change:{3=third, 2=second, 1=first}
ce testMap after changing variable from accessor methods:{3=third, 2=second, 1=first, 4=new}
As you can see from the output, HashMap values got changed because of shallow copy in the constructor and providing direct reference to the original object in the getter function.

Why String is immutable or final in Java?

String is one of the most used classes in any programming language. As we know that String is immutable and final in java and java runtime maintains a String pool that makes it a special class.

String immutable Benefits

1. String pool is possible only because String is immutable in java, this way Java Runtime saves a lot of java heap space because different String variables can refer to same String variable in the pool. If String would not have been immutable, then String interning would not have been possible because if any variable would have changed the value, it would have been reflected to other variables also.

2. If String is not immutable then it would cause severe security threat to the application. For example, database username, password are passed as String to get database connection and in socket programming host and port details passed as String. Since String is immutable it’s value can’t be changed otherwise any hacker could change the referenced value to cause security issues in the application.

. Since String is immutable, it is safe for multithreading and a single String instance can be shared across different threads. This avoid the usage of synchronization for thread safety, Strings are implicitly thread safe.

4. Strings are used in java classloader and immutability provides security that correct class is getting loaded by Classloader. For example, think of an instance where you are trying to load java.sql.Connection class but the referenced value is changed to myhacked.Connection class that can do unwanted things to your database.

5. Since String is immutable, its hashcode is cached at the time of creation and it doesn’t need to be calculated again. This makes it a great candidate for key in a Map and it’s processing is fast than other HashMap key objects. This is why String is mostly used Object as HashMap keys.

Above are some of the reasons I could think of that shows benefits of String immutability. It’s a great feature of Java String class and makes it special.

What is Java String Pool?

As the name suggests, String Pool is a pool of Strings stored in Java heap memory. We know that String is special class in java and we can create String object using new operator as well as providing values in double quotes.

Here is a diagram which clearly explains how String Pool is maintained in java heap space and what happens when we use different ways to create Strings.

String-Pool-Java

String Pool is possible only because String is immutable in java and it’s implementation of String interning concept. String pool is also example of Flyweight design pattern.

String pool helps in saving a lot of space for Java Runtime although it takes more time to create the String.

When we use double quotes to create a String, it first looks for String with same value in the String pool, if found it just returns the reference else it creates a new String in the pool and then returns the reference.

However using new operator, we force String class to create a new String object and then we can use intern() method to put it into the pool or refer to other String object from pool having same value.

Here is the java program for the String Pool image:

StringPool.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.journaldev.util;
 
public class StringPool {
 
    /**
     * Java String Pool example
     * @param args
     */
    public static void main(String[] args) {
        String s1 = "Cat";
        String s2 = "Cat";
        String s3 = new String("Cat");
        
        System.out.println("s1 == s2 :"+(s1==s2));
        System.out.println("s1 == s3 :"+(s1==s3));
    }
 
}

Output of the above program is:

1
2
s1 == s2 :true
s1 == s3 :false