Потому, что:
А. в программе написано именно так делать, и
Б. эта программа выполняется под Виндой
(на Линуксе было бы только два раза).
Подробности
Метод
read(), сам по себе, читает только один байт из входного потока, если он там есть. Но этот поток к нему попадает только после завершения ввода в него символов, т.е. когда в нем оказывается признак конца потока, которым является конец строки.
Когда на клавиатуре вводят, например, "а" [
Enter], в поток попадают три байта:
"а" (=97)
[CR] (=13)
[LF] (=10)
Последние два, [
Возврат каретки] и [
Перевод строки] под Виндой и означают традиционно тот самый "конец строки", после которого метод начинает читать, a под Линуксом это был бы только один байт 10. А дальше программа сравнивает каждый из них (разумеется, предвaрительно приведенный по значению к char) с "q" (=113), и, т.к. ни один из них не равен этому значению, уходит на новую итерацию.
Вот так станет понятнее, что и почему там происходит:
public static void main(String[] args) throws Exception {
char ch;
do {
System.out.println("Press a key followed by ENTER: ");
ch = (char) System.in.read();
System.out.println("Entered: '" + ch + "' (=" + (int)ch + ")");
} while (ch != 'q');
}
Кстати, ничто не мешает пользователю ввести больше одного символа до того, как он нажмет
Enter... попробуйте сами, а потом подумайте, как исправить программу, чтоб она делала именно то, что от нее ожидается ))