The last option is to create a BufferedImage the same size as the screen and then use a succession of function calls to get the underlying byte array containing the display data. You can then write to this in the same way you would write to a chunk of display memory. The drawback is you have to write your own drawing primitives.
I have two comments on this. Firstly, I prefer to use a BufferedImage backed by an int[] rather than a byte[]. Secondly, BufferedImage allows you to create a Graphics object, so you can use this approach to mix standard Graphics calls with custom processing. My Java4k 2009 game did this - I drew almost everything directly to the int[], but used the Graphics object for text, before finally blitting the BufferedImage to the applet's Graphics object.