1) That's no problem, but most of the buffer should be unused in the end. You need to cull (remove) faces that border to other solid blocks so only faces that can actually be visible are stored in VRAM. The size of vertexData should be enough to store even worse case scenarios, but the actual amount of data being uploaded should be pretty low per chunk in 99% of all cases.
2) glGenBuffers() creates a buffer ID used to refer to data stored on in VRAM on the GPU. You should create a vertex buffer per chunk.
3) Flipping sets the limit (Kind of like the size of an ArrayList. Note that size != capacity, the array length.) to the current position and resets the position to 0. Basically, you put data into the buffer with buffer.put() and when you're finished you flip. That way only the actual used part of the array is used thanks to the new limit. If you want to reuse the buffer later, just do a buffer.clear() which resets the limit to the capacity of the buffer.
4) That code uploads the vertexData buffer to VRAM and stores it there. First line creates a buffer handle. Second binds that buffer handle (they works like texture handles). Third line uploads the data in vertexData to the VBO. 4th line just unbinds the VBO.
How about just a VBO that holds the data for 1 cube that you render with different offsets 16*16*16 times.
No, you really need to batch the cubes together into chunks if you want to render more than a few thousand of them. You also really need to ignore faces that are inside walls/the floor/etc to reduce the number of vertices and triangles as much as possible.
View distance of 400m (1 cube = 1m^3) = Minecraft
With culling: 480 FPS, 150MB of VRAM used
Without: Instant out of VRAM crash
View distance of 100m:
With: 2200 FPS, 40MB of VRAM used
Without: Instant out of VRAM crash
View distance of 50m:
With: 2500 FPS, 30MB of VRAM used
Without: Instant out of VRAM crash
View distance of 25m:
With: 2500 FPS, 30MB of VRAM used
Without: 400 FPS, Crashes after loading around 5 16x16x16 chunks due to out of memory.