You could use RMI.
RMI hides a lot of the 'complexity' of using sockets (but believe me, if you ever had to program sockets in c/c++ you would consider Java sockets to be laughably easy).
If you are looking at the doco for RMI, please keep in mind that (assuming you are using a recent JDK) you only need to worry about the most recent version of RMI.
*Completely ignore* anything about the previous versions!!! That way you keep it real simple. Trying to juggle multiple different versions of RMI is too much like hard work. In that respect the RMI documentation is some of the worst documentation I've ever seen from Sun (the only thing that comes close is the description of metadata for J2SE 1.5).
On the other hand, you may not be able to use RMI without a network connection to get an address for the machine (might be why you want to avoid sockets?) (I don't remember if 127.0.0.1 works without an internet connection some versions of windows)
In that case, you have a multitude of options available to you. Some are better than others.
(1) Have them communicate by piping the output of one to the other (very unixy)
(2) Have a shared directory that they can do things in (use timestamps on files to see when things have changed)
(3) Have a common 'controller' which starts both of the applications, and hands references to them to each other, eg:
public class Bootstrapper {
public static void main (String[] args) {
MyApp1 myApp1 = new MyApp1();
MyApp2 myApp2 = new MyApp2();
myApp1.setMyApp2(myApp2);
myApp2.setMyApp1(myApp1);
}
}
With this you have to be careful not to use the internal reference to the other app until after they have gotten the proper reference. eg you might do this:
...
MyApp1 myApp1 = new MyApp1();
MyApp2 myApp2 = new MyApp2();
myApp1.setMyApp2(myApp2);
myApp2.setMyApp1(myApp1);
myApp1.begin();
myApp2.begin();
...
Though that implies that they start up their own threads, otherwise myApp2 won't start till myApp1 finishes!
If they implement runnable, and the run method is where they start doing all their work, you could do this:
....
myApp1.setMyApp2(myApp2);
myApp2.setMyApp1(myApp1);
Thread t = new Thread(myApp1);
t.start();
t = new Thread(myApp2);
t.start();
...
(4) You might be tempted to take #3 a step further, and have one of them take the other as a parameter to a constructor - but be careful, if you refer to something or start trying to call methods on an object that isn't itself fully constructed yet you can have problems.
(5) Connect the two applications with streams - when they want to send messages to each other they put them in the stream
(6) Register them with each other to listen for events (you define the events and event handling)
etc.