1.0.0
                |
              1.0.1
                |
(public 1.0)  1.0.2-----
                |       \
              2.0.0    1.1.0
                |        |
              2.0.1    1.1.1 (public 1.1)
                |
(public 2.0)  2.0.2-----
                |       \
              3.0.0    2.1.0
                         |
                       2.1.1 (public 2.1)
                         |
                       2.2.0
                         |
                       2.2.1
X.Y.Z is our internal version number. X.Y is the public version number, the one that has a meaning to our clients. When a X.Y.Z version becomes public, there will never be a X.Y.(Z+1) version : the public version is always the last of the serie.
X is incremented when a major version is released.
Y is used for the maintenance branches of those major releases, only for bug fixes.
Z is used internally, and has no fixed meaning. Until now, I create a new Z version when I think that the application has a set of features that are interesting to show to non developers, and is relatively stable. This way, I can show a demo of the "last known good version" of the application when someone ask one. In a near future, I plan to use the Z number versions for naming a "target" of features, in our bugtracker.
As a side note, we use maven (with the release command) to increment the version number. So, there are X.Y.Z-SNAPSHOT versions, too (which indicates any version between X.Y.(Z-1) and X.Y.Z).