Два класса не являются взаимоисключающими - вы можете использовать оба из них, если вам это нужно.
Как вы поняли, BufferedInputStream - это чтение блоков данных, а не одного байта за раз. Он также предоставляет удобный метод readLine (). Однако он также используется для просмотра данных далее в потоке, а затем при необходимости отката к предыдущей части потока (см. Методы mark () и reset ()).
DataInputStream / DataOutputStream предоставляет удобные методы для чтения / записи определенных типов данных. Например, у него есть метод для записи / чтения строки UTF. Если бы вы делали это самостоятельно, вам нужно было бы решить, как определить конец строки (т. Е. С помощью байта терминатора или путем указания длины строки).
Это отличается от readLine () в BufferedInputStream, который, как звучит метод, возвращает только одну строку. writeUTF () / readUTF () имеет дело со строками - эта строка может иметь столько строк, сколько ей нужно.
BufferedInputStream подходит для большинства целей обработки текста. Если вы делаете что-то особенное, например, пытаетесь сериализовать поля класса в файл, вам следует использовать DataInput / OutputStream, поскольку он обеспечивает больший контроль над данными на двоичном уровне.
Надеюсь, это поможет.