In the first example (both methods within one single class), no overloading can take place as the signatures of both methods are the same (only name and parameter types count, return value is ignored). This results in a compiler error.
In the second example, the method add with return type string
hides the inherited method. When you have something like
a myB = new b();
myB.add(1,2);
Then the implementation of the base class will be called even if the actual type is b. For
b myB = new b();
myB.add(3,4);
the implementation of b gets called (look at the variable type of myB, it is important!).