Thursday, December 31, 2015

Is ++i really faster than i++ in Java

Recently there was a question whether ++j is better than j++  in terms of performance. Typically this suggestion is applicable for C++ programming.

In below java code, let’s assume a value of 10 is passed as input to both the functions. After executing the line, in func1() the value of k will be 10 whereas value of j will be 11 and in func2(), the value of both k, j will be 11.

In C++, in order to return a value of 10 for variable k in func1(), the value of j is copied to a temp location, j is incremented and then the value of temp location is returned to k. For func2() this temp location is not necessary and hence one instruction is less for ++j when compared to j++.

Code
Java byte code
package com.test;

public class Test {
        public static void func1(int j) {
                int k = j++;
        }
        public static void func2(int j) {
                int k = ++j;
        }
}
public class com.test.Test extends java.lang.Object{
public com.test.Test();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

public static void func1(int);
  Code:
   0:   iload_0
   1:   iinc    0, 1
   4:   istore_1
   5:   return

public static void func2(int);
  Code:
   0:   iinc    0, 1
   3:   iload_0
   4:   istore_1
   5:   return

}

























To verify this in java, let’s examine the byte code and how this is handled.

func1():
                The passed input is stored on to the stack using iload and using iinc the value of j is incremented (so j= 11 now) and k is populated with value on the stack (which is 10) using istore

func2():
                First the value of j is incremented using iinc instruction and then incremented value is stored on to stack (which is 11) and then the same is returned to k using iload and istore respectively.

Hence the number of instructions is same in both the cases.

Now if the argument is that if no assignment is required then is ++j better? Let’s write a simple code and examine its byte code as shown in the below table:

Code
Java byte code
package com.test;
public class Test {
        public static void func1(int j) {
                j++;
        }
        public static void func2(int j) {
                ++j;
        }
}
public class com.test.Test extends java.lang.Object{
public com.test.Test();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

public static void func1(int);
  Code:
   0:   iinc    0, 1
   3:   return

public static void func2(int);
  Code:
   0:   iinc    0, 1
   3:   return

}

Compiler is smart to understand that the variable needs to be incremented irrespective of its position.
So what’s the conclusion? In case of java the number of instructions is same for the both the cases and hence we are free to choose our option while coding :-)

Friday, July 18, 2014

Finding java version from .class file

Below are simple steps to find on what java version a .class file is compiled upon.

Every compiled class has major minor version put in by the compiler. The first 4 bytes of the class file is the magic number used to identify that it is class file format of java and magic number is 0xCAFEBABE.
The next 4 bytes of the file contains minor version followed by major version (each of 2 bytes).

The below are values used for identifying each version:

Java version
HEXA Decimal value
Decimal value
8
0x34
52
7
0x33
51
6
0x32
50
5
0x31
49
1.4
0x30
48
1.3
0x2F
47
1.2
0x2E
46
1.1
0x2D
45

 
JVM checks whether the class being executed is supported on the current version of JRE or not. If an error occurs about version mismatch it prints the value in DECIMAL and not in HEXADECIMAL .

For example:
If a java file compiled on JDK 5.0 is executed on a JRE of version lower than 5.0 then always the below error will occur (meaning the version number won’t change)
Unsupported major.minor version 49.0

Most simple of way checking the HEXA values of the magic and version numbers of the class file is to open in a text editor which supports HEXA mode and check for the major/minor version as show in the below screen shot.

From the above image it is clear that the Version of java on which the java file is compiled is JAVA 7 (0033 corresponds to Java 7 from the above table).

Refer JVM Specs in order to understand the JAVA class file format.

Tuesday, May 13, 2014

Optional Type in Java 8

There are many new features like lambdas introduced in Java 8, but I thought of going through smaller ones. The current topic is one of such smaller changes but on the contrary quite interesting J

We all know that code is never expected to fail with a NullPointerException and proper handling of it is required. Doing NULL check is a very basic thing and expected out of every programmer. 

A new type Optional was introduced to indicate that the return value may be NULL and sort of enforces programmers to do a NULL check. I won't be discussing this feature in every minute detail for the reason there are many pretty good sources out there explaining the same. Blog Tired of Null Pointer Exceptions explains this very well.

The point that I want to emphasize here is about the programs which return NULL. One of the primary reason for introduction of Optional is because most of the programmers forget to do a NULL check but at the same time the code which returns NULL is forgiven. In my view returning NULL value is a bigger crime than not doing a NULL check and the reason being NULL shouldn't be used as an alternative for expressing nothing. Use an empty value instead of NULL; for example return an empty list instead of NULL. 

Also if you carefully go through the JAVA documentation for Optional.get() it says 'If a value is present in this Optional, returns the value, otherwise throws NoSuchElementException.' What this means? Now there are very good chances that programmers start facing more NoSuchElementException instead of NullPointerException.

In order to express the seriousness of the mistake in which the code returns a NULL value is presented very well in the blog post Empty !!!..

Sunday, December 15, 2013

Using TNSName - JDBC Thin client

The general practice in industry is to use TNSNames while fetching a database connection. Lets see a example on how to use TNSName while fetching the database connection.

package com.test;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class TestJDBCDriver {

private final boolean useTnsName;

public TestJDBCDriver(boolean useTnsName) {
this.useTnsName = useTnsName;
}

public void retrieveEmployeeSalary() throws SQLException {

Connection con = getConnection(useTnsName);

PreparedStatement ps = con.prepareStatement("SELECT SALARY FROM EMPLOYEE WHERE EMPLOYEE_ID = 395");
ResultSet rs = ps.executeQuery();
double salary = readResultSet(rs);

System.out.println(" Salaray :" + salary + "When useTnsName is set to :"+useTnsName );
}


private double readResultSet(ResultSet rs) throws SQLException {
double result = -2;

if (!rs.next ()) {
result = -1;
}
result = rs.getDouble(1);
return result;
}


private Connection getConnection(boolean useTnsName) throws SQLException {

String url = useTnsName?getDBUrl():getFullDBUrl();

DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
Connection conn = DriverManager.getConnection(url, getDBUsername(), getDBPassword());

return conn;
}

private String getDBUsername() {
return "USER";
}

private String getDBPassword() {
return "PASSWORD";
}

private String getFullDBUrl() {
return "jdbc:oracle:thin:@HOST:1521:DBINSTANCE";
}

private String getDBUrl() {
return "jdbc:oracle:thin:@DBINSTANCE";
}

public static void main(String[] args) throws SQLException {
String useTnsnameStr = System.getProperty("useTnsName");
TestJDBCDriver testJDBCDriver = new TestJDBCDriver(Boolean.parseBoolean(useTnsnameStr));
testJDBCDriver.retrieveEmployeeSalary();
}
}

Now the difference is in using the database URL, when TNSName is used, the corresponding database details exists in tnsnames.ora file and the details of this file need to be provided as JVM argument while invoking the main class. JVM argument is -Doracle.net.tns_admin

Now lets invoke the above code in both ways:

When there is no TNSName:

java com.testTestJDBCDriver 

When using TNSName:

java -DuseTnsName=true -Doracle.net.tns_admin=O:/Ora/network/admin/ com.testTestJDBCDriver 

I hope the above was useful

Sunday, July 7, 2013

Duplicate columns while using DatabaseMetaData.getColumns()

Want to post a potential pit fall in using JDBC API DatabaseMetaData.getColumns() where most of developers tend to do.

In some of the projects, there might be requirement to fetch the details of a column like its name etc during the run time. And the API in the subject of discussion perfectly does the job.

Below is the code snippet typically used:

String TABLE_NAME = "EMPLOYEE";
DatabaseMetaData meta = connection.getMetaData();
ResultSet columns = meta.getColumns(null, null, TABLE_NAME, null);


With above code, we faced issue of duplicate columns being returned. For example: Multiple times EMP_NAME is being returned by this API. 

When I further debugged the code to the level of DB, found that Connection.getMetaData() returns meta-data for the entire database, not just the schema you happen to be connected to. So when you supply null as the first two arguments to meta.getColumns(), the results are not filtered for your schema. You need to supply the name of the Oracle schema as second parameter of meta.getColumns() e.g., meta.getColumns(null, "DBSCHEMA_01", TABLE_NAME, null). At least this is the case when used with Oracle database (not sure of other providers).

If one carefully looks at the above code snippet, it is clearly visible that the DatabaseMetaData is fetched from the Connection. In short API returns all the columns (if no filtering is specified as above) of the specified table in the database which are visible to this connection.

Also the above issue is due to visibility of the tables provided to other schemas of the database. This may be a mistake or purposely configured by DBAs and unfortunately it was the later in our case. 

On a high level, the SQL used by ORACLE for fetching the results of this API is as below:

SELECT   *
    FROM all_tab_columns t
   WHERE t.owner LIKE '%' ESCAPE '/'
     AND t.table_name LIKE 'EMPLOYEE' ESCAPE '/'
     AND t.column_name LIKE '%' ESCAPE '/'
ORDER BY table_schem, table_name, ordinal_position


To avoid duplicates in the result returned by this API, below are two possible solutions that I could think of:
  1. Provide the schema name while calling this API.
  2. Avoid this API and use a SELECT SQL like select * from EMPLOYEE where rownum < 1. The metadata of the result set contains the details of the column. And the code will be something like: resultSet.getMetaData().getColumnCount() and resultSet.getMetaData().getColumnName(int columnIndex)
       I hope the above was useful. Please add your comments if any

Tuesday, July 2, 2013

Parsing text to create Date in Java

Recently came across a bug where the incorrect date text is getting parsed even though a valid format was specified. Java.text.SimpleDateFormat.java is used for parsing the date text.  

For example: the specified format is yyyyMMdd and the text given as input to parse is of 10252012. In such cases we expect java to through an error saying the specified text is not in expected/specified format.

After going through the java doc, found the mistake that I was doing.  The parser used in SimpleDateFormat.java may use heuristics to interpret inputs that do not precisely match specified format and if the given text fits into one of those formats then the respective Date object is created. To avoid parser to use heuristics and strictly use the format specified by user one need to call setLenient(false) method available in the SimpleDateFormat.java. By default parser is lenient (meaning set to true).


Sample Code:
------------------------------------------------------------------------------------------------------------
            String dateFormat = "yyyyMMdd";
            String correctDateText = "20121025";
            String inCorrectDateText = "10252012";
            SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
           
            Date correctdateObj = sdf.parse(correctDateText);
            System.out.println(" Date :" +correctdateObj);
           
            Date inCorrectDateObj = sdf.parse(inCorrectDateText);
            System.out.println(" Date :" +inCorrectDateObj);
           
            sdf.setLenient(false);
           
            correctdateObj = sdf.parse(correctDateText);
            System.out.println(" Date :" +correctdateObj);
           
            inCorrectDateObj = sdf.parse(inCorrectDateText);
            System.out.println(" Date :" +inCorrectDateObj);
------------------------------------------------------------------------------------------------------------
Output
------------------------------------------------------------------------------------------------------------
Date :Thu Oct 25 00:00:00 IST 2012
Date :Fri Aug 12 00:00:00 IST 1026
Date :Thu Oct 25 00:00:00 IST 2012
Exception in thread "main" java.text.ParseException: Unparseable date: "10252012"
       at java.text.DateFormat.parse(Unknown Source)
       at amdocs.ar.test.DummyTests.main(DummyTests.java:27)
------------------------------------------------------------------------------------------------------------

To continue further this is true for other cases as well.  Consider a leap year example, year 2011 is not a leap year and a date of 29th Feb 2011 should result in error if passed as input to SimpleDateFormat parser but that is not the case. It consider 29th Feb 2011 as 1st Mar 2011 and this may not be the expected business case in all scenarios. Setting the lenient to false stops this behavior.


Sample Code:
------------------------------------------------------------------------------------------------------------
            String dateFormat = "yyyyMMdd";
           
            String inCorrectDateText = "20110229";
            SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
           
            
            Date inCorrectDateObj = sdf.parse(inCorrectDateText);
            System.out.println(" Date :" +inCorrectDateObj);           
            sdf.setLenient(false);
                               
            inCorrectDateObj = sdf.parse(inCorrectDateText);
            System.out.println(" Date :" +inCorrectDateObj);
------------------------------------------------------------------------------------------------------------
Output
------------------------------------------------------------------------------------------------------------
Date :Tue Mar 01 00:00:00 IST 2011
Exception in thread "main" java.text.ParseException: Unparseable date: "20110229"
at java.text.DateFormat.parse(DateFormat.java:357)
at amdocs.test.Test.main(Test.java:19)
------------------------------------------------------------------------------------------------------------