I must confess to having some trepidation about this section.
Visibility is one of those subjects that is simple in principle but has complex subtleties. The simple idea is that any class has public and private elements. Public elements can be used by any other class; private elements can be used only by the owning class. However, each language makes its own rules. Although many languages use terms such as "public," "private," and "protected," they mean different things in different languages. These differences are small, but they lead to confusion, especially for those of us who use more than one language.
The UML tries to address this without getting into a horrible tangle. Essentially, within the UML, you can tag any attribute or operation with a visibility indicator. You can use any marker you like, and its meaning is language-dependent. However, the UML provides three (rather hard to remember) abbreviations for visibility: + (public), - (private), and # (protected).
I'm tempted to leave it at that, but unfortunately, people draw diagrams that use visibility in specific ways. Therefore, to really understand some of the common differences that exist among models, you need to understand the approaches that different languages take to visibility. So, let's take a deep breath and dive into the murk.
We start with C++, because it's the basis for standard UML usage.
A public member is visible anywhere in the program and may be called by any object within the system.
A private member may be used only by the class that defines it.
A protected member may be used only by (a) the class that defines it or (b) a subclass of that class.
Consider a Customer class that has a Personal Customer subclass. Consider also the object Martin, which is an instance of Personal Customer. Martin can use any public member of any object in the system. Martin may also use any private member of the class Personal Customer. Martin may not use any private members defined within Customer; Martin may, however, use protected members of Customer and protected members of Personal Customer.
Now look at Smalltalk. Within that language, all instance variables are private, and all operations are public. However, private doesn't mean the same thing in Smalltalk that it does in C++. In a Smalltalk system, Martin can access any instance variable within his own object whether that instance variable was defined within Customer or Personal Customer. So, in a sense, private in Smalltalk is similar to protected in C++.
Ah, but that would be too simple.
Let's go back to C++. Say that I have another instance of Personal Customer, called Kendall. Kendall can access any member of Martin that was defined as part of the Personal Customer class, whether public, private, or protected. Kendall may also access any protected or public member of Martin that was defined within Customer. However, in Smalltalk, Kendall cannot access Martin's private instance variables only Martin's public operations.
In C++, you may access members of other objects of your own class in the same way that you access your own members. In Smalltalk, it makes no difference whether another object is of the same class or not; you can access only public parts of another object.
Java is similar to C++ in that it offers free access to members of other objects of one's own class. Java also adds a new visibility level, called package. A member with package visibility may be accessed only by instances of other classes within the same package.
In keeping with our theme, to ensure that things are not too simple, Java slightly redefines protected visibility. In Java, a protected member may be accessed by subclasses but also by any other class in the same package as the owning class. This means that in Java, protected is more public than package.
Java also allows classes to be marked public or package. A public class's public members may be used by any class that imports the package to which the class belongs. A package class may be used only by other classes in the same package.
C++ adds a final twist. One C++ method or class can be made a friend of a class. A friend has complete access to all members of a class hence, the phrase "in C++, friends touch each other's private parts."
When you are using visibility, use the rules of the language in which you are working. When you are looking at a UML model from elsewhere, be wary of the meanings of the visibility markers, and be aware of how those meanings can change from language to language.
I usually find that visibilities change as you work with the code. So, don't get too hung up on them early on.