Java OOPs- Encapsulation

We can create a fully encapsulated class in java by making all the data members i.e attributes of the class private and provide getters and setters to modify or view the same.

public class Student {

private int studentId;

private String name;

private String gender;

private int age;

private String department;

public int getStudentId() {
return studentId;
}

public void setStudentId(int studentId) {
this.studentId = studentId;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getGender() {
return gender;
}

public void setGender(String gender) {
this.gender = gender;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public String getDepartment() {
return department;
}

public void setDepartment(String department) {
this.department = department;
}

}

By declaring the member variables as private , a class can have total control over what is being stored in its fields. Other classes can only access the data members of above class through their getters and setters, the former dont have the right to manipulate the data members.

Consider, a public field in a class which can be directly accessed or modified by other classes. Now suppose later on, you want to add any extra logic while getting and setting the variable. This will impact all the other classes that uses the above declared class. So any changes to this public field will require change to each class that refers it. On the contrary, with an accessor or setter method, one can easily add some logic like cache some data(There are times when you dont want to hit the database again and again to get some data and you just add @cache to the getter method so that the data is cached and your hits to database are saved) manipulate the value passed and then return or set the member value without impacting other classes using the members.

Advertisements

Deep vs Shallow Copy

package com.test;
public class User implements Cloneable{
private String name;
private Integer age;
private Address address;

public User(String name, Integer age, Address address) {
super();
this.name = name;
this.age = age;
this.address = address;
}
@Override
public Object clone() throws CloneNotSupportedException{
return super.clone();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public static void main(String[] args) {
Address add=new Address(“243″,”Ghaziabad road”,”Gujarat”);
User user=new User(“sunita”,50,add);
User userClone=null;
try {
userClone=(User)user.clone();
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(“Original user address–“+user.getAddress());
System.out.println(“Cloned user address–“+userClone.getAddress());

}
}

Output for above sysouts:
Original user address–com.test.Address@15db9742
Cloned user address–com.test.Address@15db9742

As you can see above in a shallow copy, userclone creates a copy of name and age primitive attributes, however for address it reuses the same address object for userclone. So, now the clone object has a copy of primitive values but the object references refer to the same objects as the original copy.

Shallow Copies have a significant drawback. As we saw above, cloned object and original copy refer to the same address object. Any change that cloned object makes in address object will also be reflected in original copy, which is an unwanted behaviour. What we really wanted is two separate copies of user object. Deep copying comes to our rescue for this kind of situation.

Deep copying not just clones the primitive values, it also creates copies of object references.

user and userClone have their own instances of empAddress. Any change done to user’s address will not have any affect on userClone’s address and vice-a-versa.

To implement deep copying – User will still need to implement Cloneable, and it will still override Object.clone() method. But inside the overridden clone() method, instead of calling super.clone(), a clone of an User object is constructed step-by-step using custom code as shown in the code below –

@Override
public Object clone() throws CloneNotSupportedException {
User userClone = (User) super.clone();
Address addressClone = new Address(this.address.getHouseNo(),
this.address.getStreet(),
this.address.getCity());
userClone.address(addressClone);
return userClone;
}

String,String constant pool in java

String grima = “grima”;
String gri = “grima”;
if (grima == gri)
System.out.println(“grima == gri”); // The message is displayed

Both grima,grima references refer to same “grima” in string constant pool.

String pndey = “pandey”;
String pan = “pande”;
pan = pan + “y”; // The value for pan will be resolved during runtime.
if (pndey == pan)
System.out.println(“pndey == pan”); // The 2 references are different

Now in the second case if i change

pan = pan + “y”;this line to –>pan=”pande” +”y”, System.out.println(“pndey == pan”); message will be displayed because the compiler resolves the literal during compilation time itself.

Now as per java docs on string:

•Literal strings within the same class (§8 (Classes)) in the same package (§7 (Packages)) represent references to the same String object (§4.3.1).

•Literal strings within different classes in the same package represent references to the same String object.

•Literal strings within different classes in different packages likewise represent references to the same String object.

•Strings computed by constant expressions (§15.28) are computed at compile time and then treated as if they were literals.

•Strings computed by concatenation at run time are newly created and therefore distinct.

•The result of explicitly interning a computed string is the same string as any pre-existing literal string with the same contents.