I think you are seeing some Thread Entropy here, maybe what's needed is a little explanation of WHY you do some of these things, because absolutely it's a balancing act.
Keep things private (Information Hiding): This is a good principle, and I usually start by making everything in my class private, then you up the access modifier the minimum amount necessary to accomplish whatever needs to get done.
The advantage of keeping things private is that code which lives outside of your class doesn't know how the class works. If you go in and change something later, does it matter to any code outside of your class? Well, if you're only changing things that are private the answer is a resounding NO. It didn't know what the hell was going on in the first place, and you've only touched stuff that it didn't know about. Therefore, the more stuff that stays private, the more stuff that stays flexible. If you have to change something that's protected, let's say the name of a protected member, you now have to look through any code which subclasses your class to see if you find the old name anywhere and update that too. If you change the name of a public member... you are now looking through your entire code base for any place that you consumed that class. It's not that you CAN'T do that, in fact if your program is going to be upgraded it will have to be, it's just a pain in the ass. This kind of thing is simply accepted on long term iterative development projects. The key is that making things private MINIMIZES how often we need to do this. So hiding your implementation is an extremely flexible practice. The flip side is that a class with ALL private members quite literally does NOTHING. Again, the real idea is that you want to minimize the public interface (the members, properties, functions, etc.) that people can see from the outside. Give other classes the things they need. Don't give other classes additional information, just because it might be convenient at the moment; it will bite you when you have to modify your code later.
Minimize class communication (Decoupling): You can see how this is just the flip side of Information Hiding. Information Hiding allows your class to guarantee that other classes don't become dependent on things you would really prefer they don't use; so you literally use "private" to forbid them to use it. Decoupling means writing your class so that it doesn't USE of other classes, at least no more than necessary.
Keep in mind that if you're class doesn't call ANY other class, then it is stuck with doing all the implementation itself. Encapsulation is the most powerful tool in programming because it allows you to hide implementation, so "Perfect Decoupling" comes at the price of not getting ANY benefits of Information Hiding. Here again, it's about what trade off makes the most sense to you.
Say you are creating an AuthenticationManager class to log users in. Do you have it get all the information from the UI? Probably not. Let the UI class handle those details and call you to manage the authentication parts. The AuthenticationManager can then assume it's first job is when someone calls its public Login() function and the username and password will be supplied as parameters. No need for AuthenticationManager to get involved in any of that.
Now we need to find out what the user's hashed password is from the database? Is that really authentication related? Data Access is more DAL level work, so call the DAL class which handles your users and pass in the user name; the DAL will hand you back the hashed password from the database. NOW we have coupled Authentication Manager to a function in the DAL, is this bad? Well, it means that someone (quite probably you) will have to update this code in AuthenticationManager if someone ever changes the interface of that DAL function. That's not a great thing and we would prefer not to if we don't have to. But in this case there's no other way to get that hashed password so effectively we HAVE to. Therefore I AM going to couple my AuthenticationManager to this DAL function because I want to be able to hide all the implementation of how the DAL function actually does everything it does. In this case, my desire for encapsulation and information hiding is trumping my desire for decoupling.
Now I have the password the user provided and the hashed password from the database. Obviously this does me NO good, I can't compare those two things. I need to hash the password the user provided. Am I going to write this code into the AuthenticationManage? If I do, I have excellent decoupling. However, here again, I would submit that there's nothing about an AuthenticationManager that specifically screams "I hash stuff." If this is a really small application and I want to combine the hashString() function into this class, then we should probably rename our class from AuthenticationManager to SecurityManager because it's no longer handling just authentication but cryptographic management as well. Our other option, is to encapsulate hashString() out to a new CryptographyManager class and couple ourselves to the hashString() function. Note that if we want to get any work done we are coupling to the hashString() function. Unless we're not going to accomplish anything (and our program will do nothing), we are only talking about WHERE we are coupling to. We can either depend on a function in our class or we can depend on a function in another class. Ideally, we would like the function to be in our class to limit the complexity of our application (fewer classes running around is better than many classes running around). However, if our classes our difficult to understand (like a hashString() function in an AuthenticationManager) or if our classes get too long or confusing then it's time to simplify things with another class.
How do you determine what to break out?? ABSOLUTELY look for the code you can pull out which will require the minimum amount of contact between the old and the new class, ie look for how you can decouple the new class most completely. Chances are this is the best way to create a usable abstraction as well.
Exceptions are expensive, so they're generally my 3rd choice. 1st, try to take care of it yourself. Work around the error; this may mean throwing out the results of an iteration and continuing, assuming a default value, etc. 2nd, terminate and see if the user can take care of it; this is standard practice for user input errors. There's no need to throw an exception, just return an error string telling the user what they need to do e.g. "I'm sorry, I can't create a new account for you without a valid email address, please try again." In best case scenarios, exceptions are only to handle issues that you believe will never come up in your application when it goes live. But then we live in the real world so you will find them sometimes.
Getters and Setters are ways to expose information or modify information. This means they couple one class to another and violate the principle of information hiding. Are they bad? No. Just make sure you use them only when you really need them. For example, I find very little objectionable about Getters. If you've created your classes to good and consistent abstractions, you have a very robust application architecture, and you should feel free to create getters for any "necessary" information that other parts of the application may need. ie don't create a field and a Getter function for it. Wait until another class needs it and then create a Getter method; you should probably consider whether that class really NEEDS the information in the first place too.
Setters, I'm absolutely more skeptical about. I'm perfectly willing to let other parts of the app LOOK at the first and last name fields of my User class. Should any of them be CHANGING it?? If I think twice before I create a Getter, I think AT LEAST three times before creating a Setter. When given the opportunity, I've actually created the admin for a website as a separate application on a separate subdomain to increase security for reasons that included this. If you don't need to admin the application, you can leave out a lot of Setters. You'll need those in the admin application, but not here.