It does sort of make sense that A is restricted for working with things in B but not the other way around. Because you have to remember that a class loader is a namespace or scope. Since A is B's parent, B has access to A but not vice versa.
By way of illustration they work (a little) like this
{ // A scope
int i = 5;
{ // B scope
int j = 6;
i = 7; // OK, B inherits A's scope
}
j = 8; // no good - j out of scope - not visible
}
What is also really strange is the following. Given the original scenario above, B calls A and passes A a parameter C. C's classloader (assuming C is some non-trivial object) will be B's classloader which is not visible to A, yet the parameter gets passed and A can see it and use it even though it is from a completely different and unaccessible classloader. How does that work?
I'm not sure I get it - but IIUC - when you pass C - what is the type of the reference that A sees and where is that type defined (not the concrete type of C - but its apparent type to A)? That is sort of the key. If the apparent type (Object for instance), is visible to A then it can use it (which is why you can do reflection tricks). Its kind of a hole in the system I think.