'0' — это не число 0, а код символа «0». Во всех машинах, кроме совсем уж мамонтов, используются надмножества кодировки ASCII, и '0' = 48.
И в ASCII, и в EBCDIC, и во внутренних кодировках, скажем, ЖК-индикаторов коды цифр идут последовательно от 0 до 9 — так что, отняв от кода цифры код символа «0», получаем числовое значение цифры.
То есть ,
'0' - '0' = 48 - 48 = 0
'1' - '0' = 49 - 48 = 1
...
'9' - '0' = 57 - 48 = 9
(цифры справедливы для ASCII и его надмножеств, но в EBCDIC аналогично)
В компьютере вообще нет понятия «символ», и что такое 48 — сорок восемь попугаев, X-координата на экране или код символа '0' — программист или компилятор должен думать сам. Так что Си (который, по сути, кроссплатформенный ассемблер), а ним и Java ставят очень эфемерную грань между одним и другим. В отличие от Паскаля, где надо писать
Ord(c) - Ord('0')
.