I am assuming the world as flat.
Regarding water - you could decide on a sea level. When generating the terrain you place it on a distance from the center of the round world or from bottom of a flat world so that X % is above sea level and the rest below sea level. Then you fill all "air" spaces below sea level with water. With your current generation that would give a lot of tiny lakes, you probably want larger and smoother variations to get larger lakes (and hills).
If you want to have mountain lakes etc, you could try to randomly test blocks in the air and test to "flood fill" horizontally and downwards from that block and see if the size of the resulting lake would be of a good size (most attempts will result in flooding the earth).
For caves, you can:
1. Pick a random starting position (x, y).
2. "Dig" - replace that block with air
3. Generate a trajectory (dx, dy) and a delta trajectory (ddx, ddy).
4. Proceed in the trajectory (x = x + dx, y = y+dy). Have a small probability of changing the trajectory delta (ddx, ddy). Also have a small probability of stop digging, and a small probability of branching (MUST be smaller than the probability of stop digging). For branches, RECURSIVELT start from step 1 on the branched block.
5. If we decided to stopped dig, or if the block we end up on is air, we have reached the surface again or reached a different cave. Stop looping. Otherwise, start over from #2.
If you wanted the cave to start on top, let the starting block be at the surface. If you want a subterranean block, start anywhere and run the process twice for opposite directions of the starting point.
If you want the block to be more than one block wide, "expand" it during or after the digging.
For trees, I guess you want them lumped in forests, you could do something like this:
1. Randomize a forest size (effectively length): l
2. Randomize a location: x
3. Check if the location x, x+1, x+2, ..., x+l is suitable for a forrest: are all ground pieces (not water), check other criteria (maybe not put forrest on a steep slope, or above tree level, etc). Start over from step 1 if the spot was not suitable for a forest.
4. Within the forest area, randomize the trees, for a certain probability of a tree on each ground block on the surface (but do not allow trees on adjacent surface blocks). Near the edges of the forest area, have a gradient probability of trees to hide the edge of the forest area.
In a flat world this should be fairly simple, question is how it translates to circular world. You may want to create many common functions so that you can get things like:
* distance of block from center of earth
* is the block a surface block
* get adjacent surface block
* get gravitationally adjacent surface block
* get angle of block relative to north pole (needed to know in what angle to grow a tree, once you have picked a block suitable for a tree)
For the caves you should be able to disregard that the earth is round, except if you want to avoid having too steep caves etc in which case you need to account for angle of the block when determining angle.