Let's be precise here, so you understand exactly what's going on.
First: z is a
variable, not an object. A variable is a
reference to an object (like a pointer in C or C++).
Variables have a type. The type of the variable z is X. This means that z looks like an X to the compiler: you can call whatever methods are accessible from type X or its supertypes.
If you do X z = new Y();, then z
actually refers to an object of type Y. But because the type of the variable is X, you can't access the extra methods that are only in class Y.
Why is this useful? To explain that, it's easier to look at types with less abstract names than 'X' and 'Y'. So, let's look at: List<
String> list = new ArrayList<>();
'list' looks like a List to the compiler. You can do everything with it that you can do with a List. That you used a specific implementation of List (namely, ArrayList) isn't important for the rest of the application. In fact, maybe you'd want to change it to a different implementation later. If you want to do that, you only have to change the line to: List<String> list = new LinkedList(); (for example). To the rest of the program, 'list' still looks like a List. That there's now a different implementation behind it (LinkedList instead of ArrayList) isn't something the rest of the program needs to know.
Note that inheritance implies an "is a" relationship. So, if Y extends X, that means that an Y
is an X (or, more specifically: an Y
is a special kind of X). For example, an ArrayList
is a List, and a LinkedList is also a List.
More about inheritance: You can see an object as an onion, with layers, where each layer is a level of inheritance, which adds extra stuff to the object. So, an Y object consists of a core (java.lang.Object), and then the layer added by class X, and finally the layer added by class Y. When you create a new Y object, then the object is created "from the inside out". So, first the constructor of class java.lang.Object is called, then the constructor of X, then the constructor of Y - and then you have a complete Y object.
When you have a variable z of type X, then through that variable you an only see the X part and the java.lang.Object part of the object. The Y layer is "hidden", except for the fact that class Y can override methods in the lower layers. When you call a method on the object that is overridden by class Y, then the Y version of the method is called, even though the variable is of type X. That feature is called "late binding" (the actual method to call is looked up at runtime, from the actual type of the object, rather than at compile time, via the type of the variable).