The point of object-orientation is not to count the user-defined classes and judge via "more == better".
Instantiating an object is useful if you really do have multiple things in your problem field that have identical behaviour but a distinct identity. You saw how that works with streams: one reads from one source, the next one from another; reading and writing streams are different but closely related, so it makes sense that they both inherit from the same class, etc. Those are all good reasons to use OOP idioms; it is better to write the logic for doing something once and then reuse it, and if the logic is bound up with instance-specific data (e.g. the source file name), then the logical way of bundling them is to create a class out of the data and the methods that work on it.
However, if your problem is so small that you don't repeat anything, i.e. if you really read a couple things from one place, perform one transformation and then write them to another place, there's little use in defining objects and classes to do that when it's really just a list of steps to be performed. (Separating the phases into smaller methods, even if each is called only once, is still a good idea, but that isn't strictly related to OOP - it's a good idea even in non-object languages.)
Briefly, don't beat yourself up about not having created your own class. Once you've met our first problem class where it does make sense to define a class with multiple instances, it becomes easier to judge when this makes sense and when it would just be window dressing for a fundamentally non-dynamic problem solution.
String[]
? or do you have something that looks like a strut with public fields in a class?