Exceptions Part 1 – Basics

if an application doesn’t behave as it supposes, there is a way to let outside world know. this way is to raise an exception.

below is the exception hierarchy in java
since Exception itself is an object and to be raised they should be thrown, the need to extends from Objects class and then Throwable interface.

Objects|
+- Throwable (interface)         
          |
+ ----------------------------------+
|                                    Exception
|                      ---------------+---------------------
|                      |                                    |
Error                  RunTimeException                     rest of the Exceoptions
[counted as Checked]   [Checked Exceptions]                [Unchecked Exceptions]

Checked & Unchecked Exceptions

Checked and unchecked exceptions are the two main categories that java separated all the unintended status of application into.

Unchecked Exceptions is the category that technically JVM will take care of that and when they raise the application terminated and won’t get into a situation that users left in a limbo with no information. so we are developer don’t need to take care of them in our application.

Checked exceptions are the unintentional behaviour of the application that might not be our preferable and they needed to be handled by developers.

Method Stack & Exception:

when an checked exception happenes, it needs to be handled somewhere in code. so the matter is where and who needs to handle it. this only means, when in memory stack methods will be pushed in top of each other from buttom of stack to the place that throws the exception, anyone of these methods can be responsible for handling that exception. the only factor is which one make sense more to handles that.

quick notes: try -finally – normally to handle closing files or streams. or network sockets.

 

you can use something like below snippet in any part of your code. even in try nad finally block, for example you are trying to handle file related exceptions so you can catch it or not catch it and let callers method handles it.


public void myMethod() throws IOException{

// doing something with file streams

try{

//doing the rest of stuff

}finally{

//doing some cleaning if I need.

}

}

Advertisements

Exception Part 2 – Customized Exceptions

Since all the exception in java are extended from exception class and counted as Objects, you can also extend the same way and generate your own exceptions, however most of them time existed standard exceptions are quite enough for application and normally it’s better to use the existed ones, but there are cases that you might need your own customized version of exceptions or currently existed ones might not address your need. so, in that case, you can simply extend from Exception class (and not from RunTimeException class) to make sure they could be unchecked Exceptions and also make sure you have a proper constructor for that.

about the constructors, you have below options …

  1. One without any parameters
  2. One with some details such as message
  3. one with some details such as message and the originally-thrown exception.

however you can define your own, but technically above constructors will satisfy most of the needs.

also Exception class itself provides almost everything you may need for further implementation, so remember to call super to use them, whenever you need.


public class MySpecialException extends Exception{

public MySpecialException(String reason, String additionalMessage){

super(reason + " = " +additionalMessage );}

}

public MySpecialException(String reason, String additionalMessage, Throwable e){

super(reason + " = " +additionalMessage, e );}

}

}

then inside your code you can easily call it like below snippet code.


public void myMethod() throws MySpecialException {

if(something happened wrong){
throw new MySpecialException("Reason of the issue","something really bad happened!");

}

 

try{

//opening a file for example

}catch(FileNotFoundException e){

throw new MySpecialException("Reason of the issue","something really bad happened!", e);

}

 

Working with Files JDK 1.6 or older

there is an easy way to work with files in JDK 8 but in older version when we need to read the files there was a quick coding line we had to do. in here I brought a simple example on how to open and read a text file in older versions of java.


import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
*
* @author Unknown_
*/
public class TextFile_OlderJDK {

public static void main(String[] args) {
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader("myFile.txt"));
String line = null;
while ((line = reader.readLine()) != null) {
System.out.println("" + line);
}
} catch (FileNotFoundException ex) {
Logger.getLogger(TextFile_OlderJDK.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
Logger.getLogger(TextFile_OlderJDK.class.getName()).log(Level.SEVERE, null, ex);
} catch (Exception e) {
e.printStackTrace();
} finally {

// this will run every time
try {
if (reader != null) {
reader.close();
}
} catch (IOException ex) {
Logger.getLogger(TextFile_OlderJDK.class.getName()).log(Level.SEVERE, null, ex);
}

}

}

}

 

Passing By-Value & By-Reference

for me there was a very big confusion when I tried to learn java at the beginning. so I will try to clear the situation for anyone who might have some questions about how Java treats with parameters inside its methods.

  • Method parameters as primitive type: the value will be copied into the methods scope and the result won’t affect outside the methods unless you return the result. so it’s by value.
  • Method parameters as class reference: the value will be copied into the methods scope and the result won’t affect outside the methods unless you return the result. so it’s by value.

so it shows, it doesn’t matter whether parameters of the methods are primitive types or class instance, the params will always be copied for methods and outside of the methods don’t have access to its internal changes.

if you want to take affect the by-reference you can use and update the content of the objects using references inside the method. that’s the only way I am using to change whatever I want and make that consistent outside of my methods body.

so since the parameters are immutable, any changes won’t affect outside, so for changing outside variables, the only way is to update and do the change inside the methods using the class variables itself, for example:

public void myChangerMethod(Person p1, Person p2){

// doing anything with p1 nad p2 won’t affect unless you can use these references to

Person p_temp = p1;

p1 = p2;

p2 = p_temp; // these lines won’t change anything outside of this method.

//change their object content itself. something like below:

String name_temp = p1.getName();

p1.setName(P2.getName()); // modifying content

p2.setName(name_temp); // modifying the first content.

}

 

Reflection – A Quick WrapUp :)

ok, I talked about reflection in couple posts specifically and indirectly I referred to them in a few others, but just to wrap up, Reflections are very useful and very powerful tool that every developers needed them in their toolbox. now, when you read and practice what I have explained so nfar, you can see the connections between that package with others and also you can see its footprints everywhere in Java frameworks and other utilities.

Reflection generally can be used to retrive the information about every Class Type.

you can load and reload Class definition into memory and you can trace everything inside classes, even inner classes (as I explained in my examples in Reflections)

Also Reflection will be used in Annotation processing behind the scene by JDK.

Also for Serialization, Java itself uses reflection to get the SerialVerUID fields to validate the classes.

to make that easy, I can just tell that wherever you saw something might use Class Type, you can now think of Reflection.

Serialization (Part 3) – Customized serialization and Transient Keyword

in some cases, we prefer to exclude a/ some member(s) of a class from being serialized. the way to do that is to use transient keyword in the declaration of that field. below is an example:

Why?

to decrease the space in the serialization process

Customization of serialization

make sure the Type that implement Serializable interface and you want it to be serialized, implement one or both of these two methods:


private void writeObject(ObjectOutputSteam out) throws IOException

private void readObject(ObjectInputSteam in) throws IOException, ClassNotFoundException

we can make them private since these methods could only be used by Serialization system through Reflections, so we don’t want anyone else outside of this scenario call them.

so in my example below,  I have added a new Char field into my type along with implementing these new methods into my User type and then re-ran the same test file that we have before ….


import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

/**
*
* @author Unknown_
*/
public class User implements Serializable {

private static final long serialVersionUID = -607661653725142657L;

private String username, password;
private int securityCode;
private char permissionCode;

public User() {
}

public User(String username, String password) {
this.username = username;
this.password = password;

}

public void updateUsername(String username) {
System.out.println("username's changed to ... " + username);
this.username = username;
}

public void setSecurityCode(int securityCode) {
this.securityCode = securityCode;
}

@Override
public String toString() {
return "Name: " + this.username + " - passwd: " + this.password + " - SecCode: " + this.securityCode + " permissionCode: " + permissionCode;
}

public void showcontent() {
System.out.println("" + this.toString());
}

public void setPermissionCode(char permissionCode) {
this.permissionCode = permissionCode;
}



/**
* customizing the write object process
* @param out
* @throws IOException
*/
private void writeObject(ObjectOutputStream out) throws IOException{

out.defaultWriteObject();
}

private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException{

ObjectInputStream.GetField readFields = in.readFields();
permissionCode = readFields.get("permissionCode", 'u'); // 'u' as undefined, instead of default compiler '\0' NULL
username = (String) readFields.get("username", null);
password = (String) readFields.get("password", null);
securityCode = readFields.get("securityCode", -1);
}

}

and the test scenario class:


 

import com.navid.practice.serialization.banking.User;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* we have a bank account and we want to save and then reload that into the
* object instance to see how serialization will work.
*
* @author Unknown_
*/
public class TestScenario1 {

/**
* saving objects into a file that is mentioned in the filename argument.
*
* @param u
* @param filename
*/
public static void saveUser(User u, String filename) {

try (ObjectOutputStream out = new ObjectOutputStream(Files.newOutputStream(Paths.get(filename),StandardOpenOption.APPEND))) {

out.writeObject(u);

} catch (IOException ex) {
Logger.getLogger(TestScenario1.class.getName()).log(Level.SEVERE, null, ex);
} catch (Exception ex) {
}
}

/**
* reading the object from the file mentioned as filename.
*
* @param filename
* @return
*/
public static User reloadUser(String filename) {

User u = null;
try (ObjectInputStream in = new ObjectInputStream(Files.newInputStream(Paths.get(filename), StandardOpenOption.READ))) {
u = (User) in.readObject();

} catch (IOException ex) {
Logger.getLogger(TestScenario1.class.getName()).log(Level.SEVERE, null, ex);
} catch (ClassNotFoundException ex) {
Logger.getLogger(TestScenario1.class.getName()).log(Level.SEVERE, null, ex);
}
return u;
}

public static void main(String[] args) {

User u = new User("MyOldUserName", "myPasswd1");
u.setSecurityCode(1200);
u.showcontent();
u.updateUsername("myNewUsername");
u.showcontent();

System.out.println("Saving Object ... ");
saveUser(u, "myAccounts.dat");
System.out.println("Reloading Object ... ");
reloadUser("myAccounts.dat").showcontent();

}

}

output will be something like this …

run:
Name: MyOldUserName - passwd: myPasswd1 - SecCode: 1200 permissionCode: 
username's changed to ... myNewUsername
Name: myNewUsername - passwd: myPasswd1 - SecCode: 1200 permissionCode: 
Saving Object ... 
Reloading Object ... 
Name: myNewUsername - passwd: myPasswd1 - SecCode: 1200 permissionCode: u
BUILD SUCCESSFUL (total time: 0 seconds)

since we didn’t set the default value for that recently added char in out readObject method we defined a default value. so whenever serialization system couldn’t find that field’s value in file, returned us the default value of that.

 

Serialization (Part 2)- Customization and Compatibility

Implementing customized version of serialization to save and reload the saved data back to the object instance.

this process can be done automatically just by implementing Serializable interface, however, if we have a different version of classes or different class pattern than what has been already serialized and saved, we have a problem in restoring.

how we can address this case:

by customizing the process of reloading and storing our object we can take control of how this process can be done and what needs to be serialized.

private static final long SerialVerUID = -122005682;

How to Maintain Compatibility between Different Type Versions?

just by using SerialVer utility on the first version and then use the same value of the long variable for the rest of different versions of the same type. by this way, we can make sure all of the future and old serialized and deserialized objects are using exact same amount and they are compatible with each other.

C:\Users\Unknown_>serialver 
use: serialver [-classpath classpath] [-show] [classname...]

or if you enjoy working with GUI, you can type:

C:\Users\Unknown_>serialver -show

which bring a UI tool to generate a specific id for the class. and by using the same amount for all our of class version we won’t face any issues with regards to reloading already change classes which have been stored and saved using serialization.

Upcoming Issues:

  1. future types might have introduced new fields which older serialized items didn’t have them, so how the serializable could address that?
  2. older types might have had new fields which newer serialized items don’t have them, so how the serializable could address that?

basically, both of the questions referring to a similar scenario, and the solution is very similar. we can customize the serialization process. we can make sure what fields are being fetched in deserialization process and then define some default value for new fields which do not exist in time of serialization of the older version, and then create an object and reload fields into that.

and in case that we have got some fields that newer versions don’t have then we can easily discard them.

we are checking these process in code and details below:

for example, we have a previous User class in Serialization (Part 1) – Basics, and we serialized, saved and retrieved objects successfully. but in future, we decided to add more fields to our User type. this introducing new fields will cause incompatibility, next time we try to retrieve previously serialized User objects, below is a new class along with exception stack trace.


import java.io.Serializable;

/**
*
* @author Unknown_
*/
public class User implements Serializable {

private String username, password;
private int securityCode;

public User() {
}

public User(String username, String password) {
this.username = username;
this.password = password;

}

public void updateUsername(String username) {
System.out.println("username's changed to ... " + username);
this.username = username;
}

public void setSecurityCode(int securityCode) {
this.securityCode = securityCode;
}

@Override
public String toString() {
return "Name: " + this.username + " - passwd: " + this.password + " - SecCode: " + this.securityCode;
}

public void showcontent() {
System.out.println("" + this.toString());
}

}

NOTE: I didn’t touch constructor to make the change easy, just by making the constructor exactly similar and adding new setter method to update the new field.

run:
Name: MyOldUserName - passwd: myPasswd1 - SecCode: 0
username's changed to ... myNewUsername
Name: myNewUsername - passwd: myPasswd1 - SecCode: 0
Saving Object ... 
Reloading Object ... 
Nov 26, 2016 9:58:00 PM com.navid.practice.serialization.TestScenario1 reloadUser
SEVERE: null
java.io.InvalidClassException: com.navid.practice.serialization.banking.User; 
local class incompatible: stream classdesc serialVersionUID = 2171997951914866539,
 local class serialVersionUID = -607661653725142657
    at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:621)
    at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1623)
    at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1518)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1774)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:371)
    at com.navid.practice.serialization.TestScenario1.reloadUser(TestScenario1.java:54)
    at com.navid.practice.serialization.TestScenario1.main(TestScenario1.java:74)

Exception in thread "main" java.lang.NullPointerException
    at com.navid.practice.serialization.TestScenario1.main(TestScenario1.java:74)
Java Result: 1
BUILD SUCCESSFUL (total time: 0 seconds)

as you can easily see the issue is referring to the Class incompatibility which we made it by intention in newer version of User class Type by just adding a new fields to the class. as a result the hashCode generated by serialVer is different since it depends on ClassName, methods and fields.

so the way, we can fix this is to take control of generating the SerialVersionUID and port that into out class as a private variable with the same name. in this way, whenever the serializable process will try to check the class versions, regardless of how many times we are changing that, it could still find the them compatible wioth what has been already serliaized and saved.

NOTE: just make sure you are opening the file in StandardOpenOption.APPEND, just to make sure you are not rewriting the existed files with the older serialized objects in it.we need them to prove only after adding the same serialVersionUID the incompatibility could be resolved.

just added the field which was mentioned in the exception to User class and the probelem was resolved.

import java.io.Serializable;

public class User implements Serializable {

private static final long serialVersionUID = 2171997951914866539L;

private String username, password;
private int securityCode;

public User() {
}
}

the output should be something like …

run:
Name: MyOldUserName - passwd: myPasswd1 - SecCode: 0
username's changed to ... myNewUsername
Name: myNewUsername - passwd: myPasswd1 - SecCode: 0
Saving Object ... 
Reloading Object ... 
Name: myNewUsername - passwd: myPasswd1 - SecCode: 0
BUILD SUCCESSFUL (total time: 0 seconds).

Serialization (Part 1) – Basics

Serialization means make the object transferable :

  1. between ip addresses over a network
  2. between memory addresses
  3. into a file
  4. into a database systems

Serializable interface in java provides serialization capabilities to any object type which is implement that and all of its content instances (all of its memebers). Object graph

Serializable interface is an marker interface = does’t hvae any menthod.


import java.io.Serializable;

/**
*
* @author Unknown_
*/
public class User implements Serializable {

private String username, password;

public User() {
}

public User(String username, String password) {
this.username = username;
this.password = password;
}

public void updateUsername(String username) {
System.out.println("username's changed to ... " + username);
this.username = username;
}

@Override
public String toString() {
return "Name: " + this.username + " - passwd: " + this.password;
}

public void showcontent() {
System.out.println("" + this.toString());
}

}

so far we have made this class serializable, but how we can save and persist and also reload it from our file system?

  • ObjectInputStream
  • ObjectOutputStream

by using above calsses you can just easily drop the serilized object into the input stream or fetch them from output stream without being worried about the rest of the process and the result will be provided. Check below examples:


import com.navid.practice.serialization.banking.User;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* we have a bank account and we want to save and then reload that into the
* object instance to see how serialization will work.
*
* @author Unknown_
*/
public class TestScenario1 {

/**
* saving objects into a file that is mentioned in the filename argument.
*
* @param ba
* @param filename
*/
public static void saveUser(User u, String filename) {

try (ObjectOutputStream out = new ObjectOutputStream(Files.newOutputStream(Paths.get(filename), StandardOpenOption.CREATE))) {

out.writeObject(u);

} catch (IOException ex) {
Logger.getLogger(TestScenario1.class.getName()).log(Level.SEVERE, null, ex);
} catch (Exception ex) {
}
}

/**
* reading the object from the file mentioned as filename.
*
* @param filename
* @return
*/
public static User reloadUser(String filename) {

User u = null;
try (ObjectInputStream in = new ObjectInputStream(Files.newInputStream(Paths.get(filename), StandardOpenOption.READ))) {
u = (User) in.readObject();

} catch (IOException ex) {
Logger.getLogger(TestScenario1.class.getName()).log(Level.SEVERE, null, ex);
} catch (ClassNotFoundException ex) {
Logger.getLogger(TestScenario1.class.getName()).log(Level.SEVERE, null, ex);
}
return u;
}

public static void main(String[] args) {

User u = new User("MyOldUserName", "myPasswd1");
u.showcontent();
u.updateUsername("myNewUsername");
u.showcontent();

System.out.println("Saving Object ... ");
saveUser(u, "myAccounts.dat");
System.out.println("Reloading Object ... ");
reloadUser("myAccounts.dat").showcontent();

}

}

output will be something like this …

run:
Name: MyOldUserName - passwd: myPasswd1
username's changed to ... myNewUsername
Name: myNewUsername - passwd: myPasswd1
Saving Object ... 
Reloading Object ... 
Name: myNewUsername - passwd: myPasswd1
BUILD SUCCESSFUL (total time: 0 seconds)

Reflection Example Part 6

in the previous example, the application will work however it’s tied to target object types and worker thread’s constructor. we can make it loosely coupled in a way that releases the application from these constraints and makes that flexible to decide later in running time about what Thread worker types might come in and also we can add different workerThread in future without changing the main logic. also, another good tip is reflection is Slower than regularly compiled programming, we need to decrease the use of reflections as much as possible. below is the solution:

Interface TaskWorker:


package com.navid.practice.reflection.newsrc.worker2;

/**
* by using this class
* 1. we can have different worker as much as we want
* 2. the worker's code doesn't need to have special constructor. -- more loosely coupled.
*
* @author Unknown_
*/
interface Taskworker extends Runnable{

/**
* do the actual task, so any thread could implement this.
* implement the threading triggering process.
* @param operation
* @param amount
*/
public void doWork();

/**
* setting a target for our worker thread implemenation. this target object will be an object reference to
* a BankAccount object or anything that is extended the bankAccount object.
* @param target
*/
public void setTarget(Object target);

}

then updating worker thread

AccountWorkerImproved


package com.navid.practice.reflection.newsrc.worker2;

import com.navid.practice.reflection.legacy.beans.BankAccount;
import com.navid.practice.reflection.legacy.beans.HighVolumeAccount;

/**
*
* @author Unknown_
*/
public class AccountWorkerImproved implements Taskworker {

private BankAccount bankAccount;

@Override
public void doWork() {

//decied if that's the BankAccount normal or HighVolume one, then trigger its thread.
Thread thread  = new Thread( HighVolumeAccount.class.isInstance(bankAccount)? (HighVolumeAccount)this.bankAccount : this);

thread.start();
}

@Override
public void setTarget(Object target) {

if(BankAccount.class.isInstance(target)){
this.bankAccount = (BankAccount) target;
}else
throw new IllegalArgumentException("Target object is not matching bankAccount class");
}

@Override
public void run() {
System.out.println("WorkerThreadImproved - start processing the account deposits ... ");
}

}

Executing the Scenario


package com.navid.practice.reflection.newsrc.worker2;

import com.navid.practice.reflection.legacy.beans.BankAccount;
import com.navid.practice.reflection.legacy.beans.HighVolumeAccount;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
*
* @author Unknown_
*/
public class ReflectionPractice2 {

public void startWork(String workerTypeName, Object workerTarget) {

try {

// providing the worker thread information.
Class<?> workerType = Class.forName(workerTypeName);

// since we don't have any additional constructor beside our worker thread default one, so we don;t need to get the target types
// and call it. insteda we cna use this ...
Taskworker taskWorker = (Taskworker) workerType.newInstance();

// the the rest is like reglar programming
taskWorker.setTarget(workerTarget);
taskWorker.doWork();

} catch (ClassNotFoundException | SecurityException | IllegalArgumentException | InstantiationException | IllegalAccessException ex) {
Logger.getLogger(ReflectionPractice2.class.getName()).log(Level.SEVERE, null, ex);
}

}

public static void main(String[] args) {

BankAccount bankAccount = new BankAccount("1234", 100);

new ReflectionPractice2().startWork("com.navid.practice.reflection.newsrc.worker2.AccountWorkerImproved", bankAccount);

HighVolumeAccount highVolumeAccount = new HighVolumeAccount();

new ReflectionPractice2().startWork("com.navid.practice.reflection.newsrc.worker2.AccountWorkerImproved", highVolumeAccount);
}

}

the output of our test is exactly the same as before, but we decrease the use of reflection and also make our design clearer and loosely coupled.

run:
BankAccount Object constructor 2
BankAccount Object constructor 1 
WorkerThreadImproved - start processing the account deposits ... 
HighVolumeAccount process all Daily Deposits.
HighVolumeAccount process all Daily Withrwals.
BUILD SUCCESSFUL (total time: 0 seconds)

Reflection Example (Part 5)

the main idea of this example is from one of the plural course from Mr. Jim Wilson.

Legacy Beans/Objects

Two beans of example, two types of BankAccount object.

BankAccount:


package com.navid.practice.reflection.legacy.beans;

/**
*
* @author Unknown_
*/
public class BankAccount {

private String accountNum;
private int balance;

public BankAccount(String accountNum, int balance) {
this.accountNum = accountNum;
this.balance = balance;
System.out.println("BankAccount Object constructor 2");
}

public BankAccount() {
System.out.println("BankAccount Object constructor 1 ");
}

public String getId() {
return this.accountNum;
}

public synchronized int getBalance() {
return this.balance;
}

public synchronized void deposit(int amount) {
balance += amount;
}

public synchronized void whithraw(int amount) {
balance -= amount;
}

}

 

HighVolumeBankAccount:


package com.navid.practice.reflection.legacy.beans;

/**
*
* @author Unknown_
*/
public class HighVolumeAccount extends BankAccount implements Runnable {

private int[] readDailyDeposit() {
System.out.println("HighVolumeAccount process all Daily Deposits.");
return null;
}

private int[] readDailyWithraw() {
System.out.println("HighVolumeAccount process all Daily Withrwals.");
return null;
}

@Override
public void run() {

this.readDailyDeposit();
this.readDailyWithraw();

}

}

 

 

Worker concepts:

WorkerThread is a good concepts, in this example we provide a simple form of threadworker nad then adding a better implemenation best practice to it to make the desing more robus and flexible and also loosely coupled.

AccountWorker


package com.navid.practice.reflection.newsrc.worker;

import com.navid.practice.reflection.legacy.beans.BankAccount;
import com.navid.practice.reflection.legacy.beans.HighVolumeAccount;

/**
*
* @author Unknown_
*/
public class AccountWorker implements Runnable {

private BankAccount bankAccount;
private HighVolumeAccount highVol;

public AccountWorker(BankAccount acc) {
this.bankAccount = acc;
}

public AccountWorker(HighVolumeAccount hva) {
this.highVol = hva;
}

/**
* do the actual work in here ...
*/
public void doWork() {

Thread thread;
thread = new Thread(this.highVol != null ? this.highVol : this);
//starting the thread instance in here.
thread.start();
}

@Override
public void run() {
// get this operation from BATCH file, or from other files, or from databases, or webservice, etc. any other none type related
//        //way to findout the operation.

System.out.println("Running Worker Thread in Run method - and target type is: " + (this.bankAccount == null ? this.highVol.getClass() : this.bankAccount.getClass()));
}

}

 

Testing the Scenario:

and for testing the application we can use below main class to trigger the whole process of worker thread.

package com.navid.practice.reflection.newsrc.worker;

package com.navid.practice.reflection.newsrc.worker;

import com.navid.practice.reflection.legacy.beans.BankAccount;
import com.navid.practice.reflection.legacy.beans.HighVolumeAccount;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
*
* @author Unknown_
*/
public class ReflectionPractice {

public void startWork(String workerTypeName, Object workerTarget) {

try {

// providing the worker thread information.
Class<?> workerType = Class.forName(workerTypeName);

// need to create a reference to the object that we have already
// in heap for BankAccount -- we need this to find proper constructor
Class<?> targetType = workerTarget.getClass();

//prepareing worker - specify what constructor you need
Constructor<?> workerConstructor = workerType.getConstructor(targetType);
// make an actual instance from worker class.
Object workerThread = workerConstructor.newInstance(workerTarget);
// get the proper methods to execute from worker
Method doWork_method = workerType.getMethod("doWork");

//then execute that method
doWork_method.invoke(workerThread);

} catch (ClassNotFoundException ex) {
Logger.getLogger(ReflectionPractice.class.getName()).log(Level.SEVERE, null, ex);
} catch (NoSuchMethodException ex) {
Logger.getLogger(ReflectionPractice.class.getName()).log(Level.SEVERE, null, ex);
} catch (SecurityException ex) {
Logger.getLogger(ReflectionPractice.class.getName()).log(Level.SEVERE, null, ex);
} catch (InstantiationException ex) {
Logger.getLogger(ReflectionPractice.class.getName()).log(Level.SEVERE, null, ex);
} catch (IllegalAccessException ex) {
Logger.getLogger(ReflectionPractice.class.getName()).log(Level.SEVERE, null, ex);
} catch (IllegalArgumentException ex) {
Logger.getLogger(ReflectionPractice.class.getName()).log(Level.SEVERE, null, ex);
} catch (InvocationTargetException ex) {
Logger.getLogger(ReflectionPractice.class.getName()).log(Level.SEVERE, null, ex);
}

}

public static void main(String[] args) {

BankAccount bankAccount = new BankAccount("1234", 100);

new ReflectionPractice().startWork("com.navid.practice.reflection.newsrc.worker.AccountWorker", bankAccount);

HighVolumeAccount highVolumeAccount = new HighVolumeAccount();

new ReflectionPractice().startWork("com.navid.practice.reflection.newsrc.worker.AccountWorker", highVolumeAccount);
}

}

output

run:
BankAccount Object constructor 2
BankAccount Object constructor 1 
Running Worker Thread in Run method - and target type is: class com.navid.practice.reflection.legacy.beans.BankAccount
HighVolumeAccount process all Daily Deposits.
HighVolumeAccount process all Daily Withrwals.
BUILD SUCCESSFUL (total time: 0 seconds)