Conforme declarado por outros, variáveis privadas são boas para evitar erros de uso que levam o objeto a um status inconsistente e difíceis de rastrear bugs e exceções imprevistas.
Mas, por outro lado, o que tem sido principalmente ignorado pelos outros é sobre campos protegidos.
Uma subclasse estendida terá acesso total aos campos protegidos, tornando o objeto tão frágil como se esses campos fossem públicos, mas essa fragilidade é limitada à própria classe de extensão (a menos que exponha ainda mais esses campos).
Portanto, os campos públicos são difíceis de serem considerados bons e, até a data, o único motivo para usá-los é nas classes usadas como parâmetro de configuração (uma classe muito simples, com muitos campos e nenhuma lógica, para que a classe seja passada apenas como um parâmetro para algum método).
Mas, por outro lado, os campos privados diminuem a flexibilidade do seu código para outros usuários.
Flexibilidade vs problemas, prós e contras:
Objetos instanciados pelo seu código na classe vanilla com campos protegidos são seguros e são de sua exclusiva responsabilidade.
Por outro lado, os objetos que estendem sua classe com campos protegidos, instanciados pelos usuários do seu código, são de responsabilidade deles, não sua.
Portanto, campos / métodos protegidos não bem documentados, ou se os usuários não entenderem realmente como esses campos e métodos devem ser usados, têm uma boa chance de causar problemas desnecessários a eles e a você.
Por outro lado, tornar a maioria das coisas privadas reduzirá a flexibilidade dos usuários e pode até afastá-los à procura de alternativas mantidas, pois eles podem não querer criar e manter uma bifurcação apenas para que as coisas aconteçam do seu jeito.
Portanto, um bom equilíbrio entre privado, protegido e público é o que realmente importa.
Agora, decidir entre privado e protegido é o verdadeiro problema.
Quando usar protegido?
Sempre que você entender que um campo pode ser altamente flexível, ele deve ser codificado como protegido. Essa flexibilidade é: de se tornar nulo (onde nulo é sempre verificado e reconhecido como um estado válido, sem gerar exceções), a ter restrições antes de ser usado por sua classe ex. > = 0, <100 etc, e corrigido automaticamente para valores acima / abaixo do fluxo, emitindo no máximo uma mensagem de aviso.
Portanto, para esse campo protegido, você pode criar um getter e usá-lo apenas (em vez de usar diretamente a variável de campo), enquanto outros usuários podem não usá-lo, caso desejem mais flexibilidade para seu código específico, no meu exemplo pode ser : se eles querem que valores negativos funcionem bem em sua classe estendida.