First, "passing by reference" refers to passing parameters to methods. This is not the correct terminology here.
Non-primitive variables store references to objects.
When you set A="john" you create a string, "john", and the variable A references that.
When you set B=A you set B to refer to the same object that A also refers to at that point in time.
When you set A="mary" you create a new string, "mary", and the variable A now references that. But this has no effect on that "john" string, which still exists, which B references.
It is exactly the same idea as with primitives:
int x = 10;
int y = x; // y now holds the value, 10
x = 11; // x now holds the value, 11, but y is still 10.
The reference to the string "john" or "mary" is the value of your variables. The value isn't the string itself, it's a reference to the existing string object (it's a memory location, really).
Note that a convenient feature of Java may be making this less obvious for strings; in Java String str = "john"; is essentially a shortcut for:
char data[] = {'j', 'o', 'h', 'n'};
String str = new String(data);
This is documented clearly here.
You really probably just want to watch the classic Binky's Pointer Fun video, Java version.