La questione fondamentale è che l'input dalla console è "bufferizzato". Cioè il testo digitato sulla console viene fornito alla applicazione solo quando si preme INVIO.

Prova questo:
codice:
public class Prova
{
    public static void main (String[] args)
        throws IOException
    {
        int b = System.in.read ();

        System.out.println ((char) b);
    }
}
Lancialo, digita 'a' poi 'b' poi 'c' poi INVIO. Solo quando hai premuto INVIO viene mostrato il primo carattere digitato 'a'.

Chiaro fin qui?

Uno Scanner creato passando System.in non riceve l'input finché non si è premuto INVIO (per ciò che abbiamo detto prima). Scanner per default, se non viene impostato altro, usa come delimitatore un "whitespace", cioè spazi, tab, carriage return, line feed, ecc...

Prova ora questo:
codice:
import java.util.*;

public class Prova4
{
    public static void main (String[] args)
    {
        Scanner sc = new Scanner (System.in);

        while (true)
        {
            try
            {
                int n = sc.nextInt ();
                System.out.println ("n = " + n);
            }
            catch (InputMismatchException e)
            {
                System.out.println (e);
                System.out.println ("token scaricato: " + sc.next ());
            }
        }
    }
}
Lancialo e digita i caratteri: ab 12 34 INVIO
Solo quando hai premuto INVIO allora stampa:

java.util.InputMismatchException
token scaricato: ab
n = 12
n = 34

Tra i token c'è appunto un whitespace e questo permette di spezzarli. Il primo token non è un numero, quindi lancia InputMismatchException. Il token però rimane lì. Deve essere il programmatore a leggerlo/saltarlo in altro modo. Un metodo che non ha problemi di formato è next() perché prende un token completo e basta.

Più chiaro ora?