In C++, I can define an accessor member function that returns the value of (or reference to) a private data member, such that the caller cannot modify that private
It's up to the returned object to prevent modification. Java doesn't provide declarative/compile-time checking of unmodifiable objects except to the extent that the type lacks mutators.
There is some support in the JDK: methods like Collection.unmodifiableCollection will create an object that will throw runtime exceptions if the client calls collection mutator methods.
If you're truly motivated, you can get compile-time checks by defining read-only interfaces (or classes) that only expose/implement accessor methods on your own. Keep in mind that merely declaring that a method returns a read-only interface will not prevent runtime modification if the client uses introspection and the object provides mutators (that don't throw UnsupportedOperationException).