Java couldn't allow this because of how its memory is laid out. There are four pools, each of increasing size. Every time an object is created, it is added to the smallest pool, in a contiguous manner. Objects that go out of scope don't immediately get deallocated, instead waiting for the next GC cycle. When that pool is full, all objects in the pool are checked for aliveness. If they are, they move to the next larger pool. The pool is then cleared at the end of the GC cycle. This happens for each larger pool, until the largest, which requires the most work to clean up, but rarely ever does. As such, a reallocation would require all members of the pool to potentially move to make space, or the object would have to be placed at the end of the pool, and everything else would move, and it may even trigger an early GC cycle in that pool if there's insufficient space.
C++ has a different memory layout, but the ideas are kind of the same. Memory is allocated contiguously, so increasing the size almost always requires a move operation. However, since we don't control the pointer, only references, and since multiple references can exist (which is also true for Java), that means that the language would have to specify that references are also updated automatically. This could be a major performance problem. One way to work around this problem is by using smart pointers. Your code would only ever interact with a pointer that is guaranteed to not be reallocated, and that pointer would point to the data that can move. This way, you maintain just one reference to the object, which improves performance.
Other strategies are possible, such as intentionally leaving space after every object, but this would trade the risk of movement in exchange for wasted memory usage. The language could also just keep track of the references in some kind of list, and therefore know how to update the references. Either way, such languages would likely be wasteful with memory or processing time. The fastest way to access data is direct access, and that typically means limiting what you can do with that memory once allocated.
realloc
is, essentially, a fancy wrapper aroundfree
andmalloc
with some corner cases. While it might resize in-place in favorable conditions, it gives no guarantees. Java and C++ still allow you to do the same thing manually, which is whatArrayList
andstd::vector
do. $\endgroup$