继承
面向对象的三大特性之一,是实现软件可重用的重要手段,如果A继承了B,那么A就拥有B的全部特性。java中只支持单继承。
java中继承的特性
java语言中子类继承父类,会将父类中的所有数据均继承过来(包含私有属性、方法和静态的属性和方法),对于父类的私有属性,子类无法直接访问,但是可以间接访问(get方法)
父类:
public class Father {
public String fatherName = "father";
private int fatherAge = "40";
public static String address = "河南省";
public Father() {
System.out.println("调用了父类的构造方法");
}
public int getFatherAge() {
return fatherAge;
}
public void setFatherAge(int fatherAge) {
this.fatherAge = fatherAge;
}
public Father(String name, int age) {
this.fatherName = name;
this.fatherAge = age;
}
}
子类:
public class Son extends Father{
public static void main(String[] args) {
Son s = new Son();
System.out.println(s.fatherName);
System.out.println(s.getFatherAge());
System.out.println(s.address);
//修改父类中的属性值
s.fatherName = "son";
s.setFatherAge(10);
s.address = "海南省";
System.out.println(s.fatherName);
System.out.println(s.getFatherAge());
System.out.println(s.address);
}
}
测试结果
在这个测试中,可以看出子类可以访问父类的非私有成员变量,私有成员变量和静态成员变量,而私有成员变量的访问和修改必须有get和set方法,非私有成员变量和静态方法可以直接修改。
但是子类不继承父类的构造方法,子类拥有自己的构造方法,但是子类的构造方法默认会在第一行调用父类的无参构造方法以初始化父类,如果想在子类构造方法的第一行调用父类的有参构造方法可以通过super()方法
子类和父类同名属性和方法的问题
父类:
public class Father {
public String fatherName = "father";
private int fatherAge= 40;
static String address = "河南省";
public Father() {
System.out.println("调用了父类的构造方法");
}
public int getFatherAge() {
return fatherAge;
}
public void setFatherAge(int fatherAge) {
this.fatherAge = fatherAge;
}
public Father(String name, int age) {
this.fatherName = name;
this.fatherAge = age;
}
public void test() {
System.out.println("这是父类方法");
}
}
子类:
public class Son extends Father{
public String fatherName = "son";
public int fatherAge = 10;
public static String address = "河北省";
public void test() {
System.out.println("这是子类");
}
public static void main(String[] args) {
Son s = new Son();
System.out.println(s.fatherName);
System.out.println(s.address);
s.test();
System.out.println("--------");
Father f = new Son();
System.out.println(f.fatherName);
System.out.println(f.getFatherAge());
System.out.println(f.address);
f.test();
}
}
测试结果:
- 在继承当中,子类继承父类的属性和继承方法的方式上有所差别,子类和父类中同名的成员属性是相互独立的,当子类中存在和父类同名属性,父类属性会隐藏起来,在多态的情况下属性被调用时会激活父类属性子类属性隐藏起来,而方法不会隐藏,一旦被重写,只能使用super来在子类调用。
- 对于同名属性,如果定义的是父类对象,那么调用的就是父类中的属性,如果定义的是子类对象,那么调用的就是子类的属性,如上图中的第一个对象s定义的是Son类的对象,它调用的就是Son中的属性,而第二个对象f尽管实例化的是Son的对象,但是其定义为父类Father的对象,所以其调用的是父类中的同名属性;
- 对于父类和子类中的同名方法的调用,上述规则是不适用的,实例化对象是谁就调用谁的方法,第一个对象Son s = new Son(),定义的对象是Son,实例化对象也是Son,所以调用的是Son中的test方法,而第二个对象Father f = new Son(),其定义的是父类对象,但是实例化是子类的对象,所以其调用的是子类的test方法。
关于继承问题子类实例化
-
原理:
如果一个类继承了其他类,那么在初始化的时候,super默认先调用父类的无参构造,(这个是java自己调用,不需要显式的写出来)或者super调用有参构造,(这种必须自己显式的调用,因为java不知道你要调用父类的那个有参构造方法),之后调用子类的构造器实例化子类的属性。
注意:如果 要显式的调用父类的构造方法,super必须位于子类构造器的第一行。 -
关于this调用构造器的问题
1.this()只能调用本类的的其他的构造方法,且必须放在构造器的第一行。
2.在一个构造器中不能同时显式的出现super和this,只能出现一个,this调用的是其他构造函数,而其他构造函数中也有super存在,这样就存在两个super,意味着父类的两次初始化,这不安全。如果一个构造器使用this调用其他的构造函数,那么这个构造函数将不再隐式调用父类的无参构造函数,而是在this调用的构造函数中显式或隐式调用父类的构造函数。
子类覆盖父类方法的问题
- 子类重新实现父类的方法称重写;重写时可以修改访问权限修饰符和返回值,方法名和参数类型及个数都不可以修改;子类重写的方法的访问权限不能低于父类,仅当返回值为类类型时,重写的方法才可以修改返回值类型,且必须是父类方法返回值的子类;要么就不修改,与父类返回值类型相同。那么,该如何理解呢?为什么要是父类返回值类型的子类?根据里氏替换原则,基类能够出现的地方,子类也可以出现,即能用子类替换掉父类,如果子类重写的方法返回类型与父类不一致,或者不是父类方法返回类型的子类,那么就无法完成这种替换,里氏替换原则也就被破坏了。
- 子类不能重写父类的私有方法,当子类有一个与父类私有方法同名的方法时,此时并不是对父类该方法的重写,而是子类自己重新定义了一个同名方法,举例说明一下
public class Person {
public void print(){
System.out.println("这是父类");
}
public void fun() {
print();
}
}
public class Student extends Person{
public void print(){
System.out.println("这是子类");
}
public static void main(String[] args) {
Student s = new Student();
s.fun();
}
}
输出结果:
这是子类
如果把父类的print方法的访问权限修改成private后
public class Person {
public void print(){
System.out.println("这是父类");
}
public void fun() {
print();
}
}
public class Student extends Person{
public void print(){
System.out.println("这是子类");
}
public static void main(String[] args) {
Student s = new Student();
s.fun();
}
}
输出结果
这是父类
可以看出子类并不会重写父类的私有方法, 也不能通过super关键字调用父类的私有方法