In event driven programs I've done in the past in general you want each class that is going to do something and generate events have an internal list of listeners that can register / be registered with that class. Think of it as someone subscribing to your tweets on the internet. You respond to some type of stimuli and send out notifications to everyone subscribed/registered to your feed. Those listeners in turn can then take action and send out notifications to their own listeners etc.
A simple implementation would be to have a TweetListener interface and subscribing classes would implement that interface. You as the TweetProducer would have a set of register/unregister methods backed by an internal List of all the TweetListener instances that want to be notified when you send out an "event". When you're ready to send out a tweet, you would cycle through a defensive copy of that list and for each object in there, you'd call the method defined in your TweetListener interface, i.e. aListener.doTweetReceivedAction("Here's my tweet")
. (Defensive copy because one possible action for a listener is to unregister/stop following your tweets and you wouldn't want a concurrent list modification). Obviously you'd have to take great care in how follow on actions affect your program both from a concurrency/multi-threaded sense and from an order of operations sense.
As far as bootstrapping the whole thing, in your public void static main
method you would basically instantiate your various objects and wire them together as listeners etc.
Tweeter me = new Tweeter()
SomeDude dude = new SomeDude() // implements TweetListener interface
SomeDude bob = new SomeDude()
me.addListener(dude);
me.addListener(bob);
me.tweet("hi!");
Obviously you'd also need a KeyTyped listener that would sit & wait for keypress events, determine what they are, and send out the appropriate event notifications to the various listeners in your program. At some point that keypress listener would have to sit & block, waiting for input. Alternatively you could have that keypress listener & the other portions of your program run in different threads. This is actually the way the Swing threading model works in Java. You have a main program thread and a swing thread and you kick event notifications back and forth between the two (i.e. SwingUtilities.invokeLater();)
This is an older article but actually pretty good for the basics on how to do event driven programming in java. I think it would be applicable to what you are trying to do. https://community.oracle.com/docs/DOC-982960 It talks about things such as defensive copying, sending out immutable event objects and why you need to do & worry about things like this.