I injected overridden method toString into Object.metaClass:
Object.metaClass.toString ={
   System.out.println("the string is $delegate")
}
and I thought that following code will execute this method:
1500.toString()
But it didn't not,nothing was printed to the console. That is what exactly confuses me: if something goes bad, then an error is to throw out; if Object.metaClass.toString is found and invoked, then the message will turn up, but why it is not working? What happened inside? 
This behavior is correct, because java.lang.Integer overrides Object.toString() with its own implementation. If your assumption was correct then it would mean that you can break overridden method by forcing to use an implementation from parent class. 
Consider following Groovy script:
Object.metaClass.toString = {
    System.out.println("the string is $delegate")
}
class GroovyClassWithNoToString {}
class GroovyClassWithToString {
    @Override
    String toString() {
        return "aaaa"
    }
}
new GroovyClassWithNoToString().toString()
new GroovyClassWithToString().toString()
1500.toString()
Runtime.runtime.toString()
When you run it you will see something like:
the string is GroovyClassWithNoToString@3a93b025
the string is java.lang.Runtime@128d2484
You can see that GroovyClassWithNoToString.toString()  called Object.toString() method and its modified version, also Runtime.toString() calls Object.toString() - I picked this class as an example of pure Java class that does not override toString() method.
As you can see overriding toString() method from Object level makes sense for classes that base on Object.toString() implementation. Classes that provide their own implementation of toString() wont use your dynamically modified method. It also explains why following code works:
Object.metaClass.printMessage = {
    System.out.println("Hello!")
}
1500.printMessage()
In this example we are adding a new method called printMessage() to Object class and all classes that don't override this method will use this dynamic method we just created. Integer class does not have method like that one so it's gonna print out:
Hello!
as expected.
Also keep in mind that toString() should return a String and it's better to not print anything to output inside this method - you can end up with nasty StackOverflowError caused by circular calls to toString() method.
toString() method is being picked by Groovy runtime?Let me show you under the hood what happens when we call following script:
Object.metaClass.toString = {
    System.out.println("Hello!")
}
1500.toString()
and let's see what does Groovy during the runtime. Groovy uses Meta Object Protocol (MOP) to e.g. invoke any method called in a Groovy code. In short, when you call any Java or Groovy method it uses MOP as an intermediate layer to find an execution plan for a method - call it directly or use e.g. a method that was injected dynamically.
In our case we use plain Java class - Integer. In this case Groovy will create an instance of PojoMetaMethodSite class to meta class implementation for Java class - an Integer. Every meta method is executed using one of the Groovy groovy.lang.MetaClass implementation. In this case groovy.lang.MetaClassImpl is being used. One of the last methods that picks a method to execute is MetaClassImpl.getMethodWithCachingInternal(Class sender, CallSite site, Class [] params). If you put a breakpoint in the beginning of this method and run a script with a debugger, you will see that this method is executed with following parameters:

In line 1331 you can see that helper method called chooseMethod(e.name, methods, params) is being used:
cacheEntry = new MetaMethodIndex.CacheEntry (params, (MetaMethod) chooseMethod(e.name, methods, params));
This method is responsible for picking the right method to execute when we try to invoke toString() on Integer object. Let's get there and see what happens. Here is what this method implementation looks like:
/**
 * Chooses the correct method to use from a list of methods which match by
 * name.
 *
 * @param methodOrList   the possible methods to choose from
 * @param arguments
 */
protected Object chooseMethod(String methodName, Object methodOrList, Class[] arguments) {
    Object method = chooseMethodInternal(methodName, methodOrList, arguments);
    if (method instanceof GeneratedMetaMethod.Proxy)
        return ((GeneratedMetaMethod.Proxy)method).proxy ();
    return method;
}
Source: https://github.com/apache/groovy/blob/GROOVY_2_4_X/src/main/groovy/lang/MetaClassImpl.java#L3158
Now let's see what parameters are received when we call our script:

What is most interesting in our case is the first element of methodOrList.data. It's a method object of:
public java.lang.String java.lang.Integer.toString()
which is the method toString() that Integer class overrides from its parent class. Groovy runtime picks this method, because it is the most accurate from the runtimes point of view - it is the most specific method for Integer class provided. If there is no toString() method overridden at the class level (e.g. Runtime class example I mentioned earlier) then the best candidate for invoking toString() method is a ClosureMetaMethod provided by us in Object.metaClass.toString = .... I hope it gives you a better understanding of what happens under the hood.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With