Why is it allowed for a parent class object to be instantiated using a child class's constructor?
That's not what's happening there. In this line:
parent p = new child();
...you're creating a child object, not a parent object. The variable you're using to refer to it, p, has the type parent, which means that you can only use parent stuff via that reference, but the object is a child object. The object does have a y, you just can't access it via p. You can see that by doing this:
parent p = new child();
System.out.println( ((child)p).y );
By telling the compiler we know that p refers to child even though it's of type parent (via a "cast"), we can access the child-specific parts of it. (This is not good practice when you have an alternative.)
This ability for there to be a difference (within constraints) between the reference you have to something and what it actually is is called "polymorphism" (taking multiple forms) and is central to object-oriented programming.
Consider:
class Animal {
}
class Dog extends Animal {
}
class Cat extends Animal {
}
All Dogs are Animals, as are all Cats, but Dogs are not Cats. Suppose you run a pet shop and want to store a list of the animals you have on hand:
List<Animal> animals;
The entries can be objects of type Animal or any of its subtypes, such as Dog and Cat. This lets you have common aspects of animals on Animal, and then only Dog- or Cat- specific aspects (properties or behaviors) on those types. You can run through your list of Animals acting on their animal-ness without worrying about whether they're Dogs or Cats.