I see that this causes a cycle
ptr.nextNode = temp;
temp.nextNode = ptr.nextNode;
as temp.nextNode points to itself.
U need to interchange these lines
Answer from Aditya K on Stack OverflowLinked list without using collections in Java - Stack Overflow
How to create a linked list in java without importing linked lists - Stack Overflow
Creating linked lists in java without using in-built methods/importing util - Stack Overflow
Singly linked list implementation in Java without help of java.util.linkedlist - Code Review Stack Exchange
I've only glanced at your code, but two things stood out.
Immutable
In HeadNode you have a comment:
// immutable class representing head node of linked list
Either immutable doesn't mean what you think it does, or this comment is confused. Immutable classes don't change after they've been constructed. They typically have final members so that they can't be assigned to. You HeadNode class provides bother a getter and a setter for its only field nextNode. This isn't an immutable class.
Testing
Rather than rolling your own tests in main, consider looking into a testing framework like JUnit. It allows you to encapsulate tests for different functionality in a more expressive way, so that you can clearly tell from the test runs what is failing if you introduce bugs.
A few afterthoughts to add to @Jonas' answer.
Node
At the moment you have a distinction between nodes that have data (Node) and nodes that don't have data (HeadNode). Does it really make sense to have a Node that doesn't have a dataitem associated with it? Looking at your code, the answer is probably not, the first thing you do after creating a new Node is to call setDataItems. With that in mind, it would be better to add a constructor to your Node class that takes in a data item, rather than requiring the client to call a member function immediately after construction.
Efficiency
As @Janos pointed out, some of your methods are not very efficient. The one that most struck me was your searchKey method. It relies on your dataAtNodeIndex method which in turn relies on nodeAtIndex. Since your nodeAtIndex always starts from head, you end up searching the list over and over again from the start, going one item further each time until you find they key you're looking for.
Error Checking
Some of your methods can fail, but don't have a method for telling the client. For example, insertNodeAtIndex and deleteNodeAtIndex both return void. They also have ways that they can fail to deliver on their promises and fail silently. If a client calls insertNodeAtIndex(5) when the list only has 3 items, then you have a few options. At the moment, you effectively ignore the request (the client has no way to know that the item hasn't been added to the list). It would be better to at the item to the tail of the list (this fulfils the insert promise). However, it would be better still to throw an exception to indicate to the client that they've made an invalid request and that the item hasn't been inserted into the list.
Adding to forsvarir's Answer:
Code Style
Whitespace
Try to use whitespace more consistently.
Correct indentation can make code much more readable.
Sometimes you put a space before the opening braces after a method definition or around operators (=, +, etc.), sometimes you don't.
I won't tell you to put spaces there or to leave them out, that's mostly personal preference or convention (I prefer putting spaces), but when you decide to do one or the other, stick to it.
Parameter Names
All your method parameter's names start with an underscore, which doesn't look particularly nice. If you're already using the this-Keyword to access your object's properties, there is no need to give the parameter a different name than your attributes, since prefixing the name with this. will already tell Java to use the object's property instead of the parameter.
Method Names
insertNode gives the impression it inserts a Node at a freely specified point in the list.
Actually, it does neither: it adds the element at the front of the list (making it the new head) and it (from the user's perspective) doesn't add a Node, but a DataItems element to the list.
Maybe prependElement would be better suited here.
Same goes for deleteNode and generally any method with "node" in its name - of course it is a linked list and thus uses nodes underneath, but that detail should probably be hidden from the user.
Class Names
DataItems only represents a single item, it would be better to not use the pllural form for its name.
Architecture
Unnecessary HeadNode
Your LinkedList stores a reference to a HeadNode. HeadNodes only purpose seems to be to hold a reference to a Node, so why not remove the HeadNode-class altogether and directly use a Node in your LinkedList?
Coupling
Your LinkedList and Node implementations directly use the DataItems class for storing data.
The whole linked list-scenario seems like a perfect example for learning to use Java's Generics: That way you can not only use your LinkedList to store DataItems, but any object.
You can modify DataItems in the same way to use any type as key or value.
See Generic Types for more information on that topic.
If you implemented your list like this, you could also consider implementing Java's List<E> interface to make it completely interchangable with other List-type in Java.
While that violates not using the java.util package, it offers quite a range of new things to learn, while trying to implement all methods required by the interface.
Of course, when you use Generics instead of DataItems directly, your searchKey method cannot be implemented as it is now.
Instead, you'd need a method (let's call it find), which takes a predicate (i.e. a function taking the datatype stored in your list and returning a true if the item matches whatever you're looking for) as its parameter.
Implementation
Insertion and Deletion
Your insertNode and deleteNode methods both are called when executing insertNodeAtIndex(0, data) and deleteNodeAtIndex(0), respectively.
I recommend doing it the other way around, i.e. calling deleteNodeAtIndex(0) from deleteNode in order to keep the insertion/deletion code neatly in a single method instead of splitting it over two separate methods.
Also, I would move the method definitions closer together, so you don't have to search too much to find the related method.
Always try to keep methods with similar responsibilities closely together in the source file.
Reversing the List
Your implementation of reverseLinkedList is pretty inefficient.
For each node in your list, it calls the nodeAtIndex method, which itself iterates over the whole list, causing it to fall in the \$O(nยฒ)\$ group of algorithms.
Reversing the list could be done in \$O(n)\$ when using a Stack or recursion.
Calculating the List's Size
This implementation, too, is in \$O(nยฒ)\$ while it could be done in \$O(n)\$.
Instead of always calling nodeAtIndex, just put the first node in a variable and in the while loop, check if the element is null, incrementing count and replacing the node with the next node.