JAVA学习笔记–类的继承
类的继承:
概念:
继承面向对象的第二大主要特征,继承是从已有的类(父类)中派生出新的类(子类),子类能继承父类的数据属性和行为,并能扩展新的能力,具有属于自己独特的方法和属性。
特点:
Java的继承通过extends关键字实现.
实现继承的类被称为子类.
被继承的类被称为父类.
父类和子类的关系, 是一种一般和特殊的关系.
例如水果和苹果的关系, 苹果继承了水果, 苹果是水果的子类, 水果是苹果的父类.
语法:
calss 子类 extends 父类{
//关键字 extends
}
实现举例:
public class Student {//父类:学生类
String name; //父类属性
String gender;
int age;
int grade;
//父类方法
public void print(){
System.out.println("父类的方法");
}
public static void main(String [] args){//主函数
Student stu1=new Student();//创建父类和子类对象
UNStudent unstu1=new UNStudent();
stu1.print();//调用父类和子类方法
unstu1.print();
}
}
class UNStudent extends net.dzw0827.Student {//创建子类:大学生类
int gaoshuGrade;?//独属于子类自己的特征属性
int dawuGrade;
@Override//重写注释:帮助检查方法重写的正确与否
public void print(){ //子类方法重写
System.out.println("子类重写的方法");
}
}
继承的结果:
- 继承时,父类的所有属性和方法都能继承过来,但是在调用时具有局限性,需要根据访问修饰符的不同判断所继承的方法和属性的调用范围。
- 子类从外表上看是继承并扩充了父类的功能,但是实际上子类是将父类定义的更加具体化的一种手段,子类的表示范围小,但是更加丰富精细。比如学生类和大学生类,大学生继承了学生的特征(可以全部继承,但是不能全部利用,比如还有中学生的存在),但是又拥有属于自己的独特的特征。
- 继承范围:
- 访问修饰符:控制代码可以访问的权限范围
- 权限范围:项目(project)<包(package)<.java文件< 类 < 属性和方法 > > > >
- 调用范围:方法和属性
- public:整个项目所有类
- default(缺省):整个包所有类
- private:类自身(能继承,不能调用,但可以通过自定义方法,在子类中继承方法得到)
- protected:同一个包所有类、不同包的子类中
继承的限制:
-
单继承局限:虽然继承可以进行类的扩充,使子类功能更丰富,但在继承时具有限制。一般情况下,一个子类继承一个父类,如果想要一个子类继承多个父类,需要注意语法。
class A{} class B{} //单继承 class C extends A{} ( class C extends B{} ) //多重继承 class C extends A,B{} // 错,想要继承两个父类,只能多层继承 //正确写法 class A {} class B extends A {} class C extends B {}//对,C是实际上属于A的(孙)子类。 //相当于B继承了A类的全部属性和方法,C又继承了B的全部属性和方法,这种操作称为多层继承。
-
私有限制:当父类中的一些方法或属性定义为私有时,是不可直接创建子类对象来使用的,但可以通过父类的方法,让子类得到这个私有属性(隐式继承,间接完成)。
public class Student {//父类:学生类 String name; //父类属性 int age; private String adr; //父类方法 public void setAdr(String adr){ this.adr=adr; } public String getAdr(){ return adr; } public static void main(String [] args){//主函数 Student stu1=new Student();//创建父类和子类对象 UNStudent unstu1=new UNStudent(); //直接调用报错 unstu1.adr="abc";//报错 //通过方法间接完成 unstu1.setAdr("abc"); System.out.println(unstu1.getAdr()); } } class UNStudent extends net.dzw0827.Student {//创建子类:大学生类 int gaoshuGrade;//独属于子类自己的特征属性 int dawuGrade; }
-
调用构造函数限制(super限定):在继承关系之中,如果要实例化子类对象,会默认先调用父类构造,为父类之中的属性初始化,之后再调用子类构造,为子类之中的属性初始化,即:默认情况下,子类会找到父类之中的无参构造方法。
public class Student {//父类:学生类 String name; //父类属性 int age; private String adr; public Student(){ //默认构造函数 System.out.println("父类的默认构造函数"); } public static void main(String [] args){//主函数 //创建子类对象 UNStudent unstu1=new UNStudent(); } } class UNStudent extends net.dzw0827.Student {//创建子类:大学生类 public UNStudent(){ System.out.println("子类的默认构造函数"); } } 输出: 父类的默认构造函数 子类的默认构造函数
- 批注:
这个时候虽然实例化的是子类对象,但是发现它会默认先执行父类构造,调用父类构造的方法体执行,而后再实例化子类对象,调用子类的构造方法。而这个时候,对于子类的构造而言,就相当于隐含了一个super()的形式。
******* class B extends A { public B() { // 子类构造 super(); // 调用父类构造 super调用父类构造时,一定要放在构造方法的首行上。 System.out.println("子类的构造函数"); } }
- 批注:
super限定:
意义:
如果需要在子类方法中调用父类被覆盖的实例方法,则可以使用 super 限定来调用父类被覆盖的实例方法。
super是java提供的一个关键字,super用于限定该对象调用它从父类继承得到的属性和方法,需要注意的是和this一样super也不能出现在static修饰的方法中,因为static修饰的是类,调用者只能是类,不能是对象。
如果发生了子类方法重写,在正常情况下子类只能调用重写后的方法,但是在子类方法中可以使用super关键字从而访问到父类中的方法或属性
语法实例:
public class Student {//父类:学生类
public void academic(){//父类方法
System.out.println("我是学生");
}
public static void main(String [] args){
UNStudent unstu1=new UNStudent();//创建对象
unstu1.academic();//调用子类重写方法
}
}
class UNStudent extends net.dzw0827.Student {//创建子类:大学生类
@Override
public void academic(){ //方法重写
super.academic(); //*****使用super调用父类中已经被重写的方法
System.out.println("我是大学生");
}
}
父类方法重写:
意义:
子类继承父类的方法时,会根据自身的特性做出适当的改变。比如说学生类具有输出学历的方法,那么大学生将继承到输出学历,但是这个方法对于大学生就要更加具体的输出自己的本科学历,中学生就要输出自己的高中学历,很明显从学生类继承到的方法不能直接被大学生和中学生调用,而是需要进行方法的重写。
语法:
-
这种子类包含与父类同名方法的现象称为方法重写(Override),也被称为方法覆盖,可以说子类重写了父类的方法, 也可以说子类覆盖了父类的方法。
-
方法的重写要遵循两同两小一大规则:
- 两同: 方法名相同 / 形参列表相同
- 两小: 子类方法返回值类型应比父类方法返回值类型小或相等. / 子类方法声明抛出的异常类应比父类方法声明抛出的异常类更小或相等.
- 一大: 子类方法的访问权限应比父类方法的访问权限大或相等.
通常情况下一言概之,除了方法体之外,访问修饰符、返回类型、方法名、形参列表,都要一致。
public class Student {//父类:学生类
String name; //父类属性
int age;
public String academic(){//父类方法
System.out.println("我是学生");
}
}
class UNStudent extends net.dzw0827.Student {//创建子类:大学生类
public String academic(){ //方法重写
System.out.println("我是大学生");
}
}
class MidStudent extends net.dzw0827.Student{//创建子类:中学生类
public String academic(){//方法重写
System.out.println("我是中学生");
}
}
重写注释:
重写注释:对于重写方法的注释,可以自行判断方法重写的正确与否
语法:@Override
class UNStudent extends net.dzw0827.Student {//创建子类:大学生类 @Override public void academic(){ //方法重写 System.out.println("我是大学生"); super.academic(); }}
注意事项:
-
重写后调用的是子类重写后的方法,不重写则调用父类的方法。
-
当子类覆盖了父类方法后, 子类的对象将无法访问父类中被覆盖的方法.
-
但可以在子类方法中调用父类中被覆盖的方法.
-
如果需要在子类方法中调用父类中被覆盖的方法, 则可以使用super(被覆盖的是实例方法) 或者 父类类名(被覆盖的是类方法) 来作为调用者, 调用父类中被覆盖的方法.
-
如果父类方法具有 private 访问权限, 则该方法对其子类是隐藏的,因此子类无法访问该方法, 也就无法重写该方法.
-
如果子类中定义了一个与父类 private 方法具有相同的方法名 / 相同的形参列表 / 相同的返回值类型的方法, 依然不是重写. 这只是在子类中重新定义了一个新的方法.
重载与重写:
-
方法重载与方法重写在英文中分别是overload和override。
-
相同点:
- 无论是重载还是重写方法名都必须相同
-
不同点:
- 重载发生在同一个类中,重写是发生在子类和父类之间(不同类中)
- 重写时,访问修饰符、返回类型、参数列表也必须一致;但重载时不必要,且重载时参数列表必不相同。
-
如果一个子类定义了一个与父类方法有相同方法名,但参数列表不同的方法,就会形成与父类方法和子类方法的重载,子类调用方法时需要根据参数列表的不同自行选择匹配的方法。
对象的类型转换:
意义:
-
一个对象可能继承了多个类的属性和方法,如果把类比作是身份的话,对象就是具有这个身份的个体,那么有时一个对象可能具有多种身份,我们需要进行切换身份时就需要进行对象的类型转换。
-
基本数据类型之间可以进行转换,对象同样可以,也分为自动转换和强制转换,主要体现在父类和子类之间。
-
无继承关系的引用类型间的转换是非法的
原则:
-
自动转换:子类可以自动转换为父类,称为向上转换,比如一个大学生可以转化为学生类对象,在可以转换为人类对象。
public class A{ //父类}class B extends A{ // 子类}A a=new B(); 或者 B b=new B(); A a=b; //A是B的父类,左大右小
-
强制转换(造型):向下转换,父类可以转化为子类,但是需要前提条件:这个父类对象是由子类对象转化而来的,就比如要把一个学生转化为一个大学生,那么这个学生本质上必须是一个大学生,不然你不确定他是大学生,或许是中学生也未可知。
public class A{ //父类}class B extends A{ //子类}B b=new B(); //创建子类对象A a=b; //子类对象自动转换为父类对象B b1=(B) a; //父类对象强制转换为子类对象
注意事项:
-
子类对象转换为父类类型后,依旧调用子类重写后的方法,而不是父类原本的方法。如,让这个学生去写作,虽然他利用的是学生的身份去写,但是写出来的依旧是大学生的水平,所以要用重写后的方法。
-
子类对象转换为父类类型后,不能调用子类独有的方法和属性。
应用实例:
public class Student {//父类:学生类 int age; //父类属性 public void academic(){//父类方法 System.out.println("我是学生"); } //父类方法 public static void main(String [] args){ UNStudent unstu1=new UNStudent(); //创建子类对象 Student stu=unstu1; //自动转换为父类对象 //利用转换后的父类对象调用父类方法、属性、子类独有属性、子类独有方法 stu.academic(); //实际调用的是子类重写后的方法 stu.age=18; //父类有的属性可以调用 stu.gaoshuGrade=18; //报错,子类独有的属性,现在是父类对象不可调用 stu.print();//报错,子类独有的方法,现在是父类对象不可调用 //强制转换 UNStudent unstu3=(UNStudent) stu; unstu3.academic(); //利用转换后的子类对象调用父类方法,实际调用的是子类重写的方法 unstu3.gaoshuGrade=100; //转换后称为子类对象,可以利用子类都有属性和父类公有属性 unstu3.age=100; unstu3.print(); //可以调用子类独有属性 }}