Keystrokes are good, but the main problem there is the binding.
Swing uses some frankly retarded abstraction:
map<KeyStroke, GlueObject> (inputmap) and
map<GlueObject, Action> (actionmap)
this was done for different keystrokes in the inputmap - different pressed keys - to be able to lead to the same action or not in the actionmap.
However it is retarded because when you're doing keybinding, that is changing the keystrokes that lead to a glue object, you need to guarantee that the
old keystroke is removed, and that the glue object is
not removed by accident.
The maps are prepared to have many keystrokes have the different glue objects that lead to many (or the same) actions, but they AREN'T prepared without a lot of pain to change the keystrokes themselves since invariants like - don't put the same keystroke in the map for different glueobjects since glueobjects are fixed and should always be in the map and overwriting is the same as deleting - aren't checked at all.
Basically swing has coding-time flexibility but doesn't help at all with some invariants.
BTW; if you use this design you probably want your keymappings GUI to prevent overwriting if the user configures tries to use the same keystroke for the different glueobjects (the old one would get deleted in the inputmap) and show a error and prevent saving (feedback) in your GUI otherwise.
If you're designing from scratch, the InputMap could probably be a 'InputMultiMap' if you don't think your users will get confused about the same keystroke activating two actions.
I ended up adapting this design to use a enum as the glue object and action container and that caused the other serialization problems.