I am not a big fan of Java, since in my experience it's difficult to transition into C, where we need to know for damn sure the difference between a pointer and actual memory. In Java, of course, if we have a class Foo, we can say Foo f = new Foo() and Foo g = f; We note that f sort of "owns" the memory of the actual object, and g also points at the object. But neither f or g are actually the object. They are references TO the object. In fact, they are pointers to the object. They hold the address of the object. Which is exactly what pointers in C are: addresses of the first byte in memory of whatever object.
The difficulty with pointers in my experience has been how do we interpret them? Suppose I have
How do I interpret this? I will often say str is a string holding "Hello, World!". In the strict sense, that's not true, it's only a valid interpretation of the fact that str is a byte address to the first character in the "string" (more properly a contiguous block of memory), which is 'H'. I think of str2 not as a string but as a marker marching along the string, because if I declare another pointer pointing at the same thing, I stop thinking "this is the object" and start thinking "this is a marker." A valid interpretation, but not correct in the strict sense. Finally, what is str3? I think of str3 as "the substring 5 characters into str." So then str3 can be THOUGHT of as ", World!" But it is not. If str = 0x450f0040, then str3 = 0x450f0044. That's all these are in the strict sense: byte addresses.
char* str = "Hello, World!";
char* str2 = str;
char* str3 = &str; // or str + 4 if you prefer