------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------
一.继承的概念:
1.首先为什么需要继承?
当多个种类相同的一些类中具有一些相同的属性和行为时,可以将这些相同的属性和行为进一步抽取,形成一个"父类"。子类就不需要再定义这些属性和行为了,只需要"继承"就可以了。使用继承之后,子类就拥有了可以被继承的父类中的成员属性和成员方法。
2.继承的好处?
借助继承,可以扩展原有的代码,应用到其他程序中,而不必重新编写这些代码。在java语言中,继承是通过扩展原有的类,声明新类来实现的。扩展声明的新类称为子 类,原有的类称为父类。继承规定,子类可以拥有父类的所有属性和方法,也可以扩展定义自己特有的属性,增加新方法和重新定义父类的方法。
3."继承"使用关键字:extends
本文会多以代码的形式展现出所有的叙述,这样会让大家更好的理解;下面的这段程序test就展现了继承的概念以及好处,Student类和Teacher类继承了Person类,因为学生和老师都具有姓名年龄性别这些属性,而在定义学生类和老师类的时候就不用重新定义这些成员变量了,突出了代码的重用性.
class Person
<span style="white-space:pre"></span>{
String name;//姓名
int age;//年龄
char sex;//性别
void show(){
System.out.println("黑马程序员");
<span style="white-space:pre"></span>}
<span style="white-space:pre"></span>}
<span style="white-space:pre"></span>class Student extends Person
<span style="white-space:pre"></span>{
<span style="white-space:pre"></span>}<span style="white-space:pre"></span>class Teacher extends Person<span style="white-space:pre"></span>{<span style="white-space:pre"></span>}<span style="white-space:pre"></span><span style="white-space:pre"></span>class test <span style="white-space:pre"></span>{public static void main(String[] args) {Student stu = new Student();System.out.println(stu.name);stu.show();<span style="white-space:pre"></span>}<span style="white-space:pre"></span>}
1.继承后,子类可以拥有自己的成员属性和成员方法;
如:
<span style="white-space:pre"></span>class Person
{
String name;//姓名
int age;//年龄
char sex;//性别
void show(){
System.out.println("黑马程序员");
}
}
class Student extends Person
{
<span style="white-space:pre"></span>int score;
<span style="white-space:pre"></span>void Study{
<span style="white-space:pre"></span><span style="font-family: Arial, Helvetica, sans-serif;"></span><span style="font-family: Arial, Helvetica, sans-serif;">System.out.println("学习");</span>
<span style="white-space:pre"></span>}}class Teacher extends Person{
<span style="white-space:pre"></span>int salary;
<pre name="code" class="java"><span style="white-space:pre"></span>void Study{
<span></span><span style="font-family: Arial, Helvetica, sans-serif;"></span><span style="font-family: Arial, Helvetica, sans-serif;">System.out.println("教学");</span>
<span></span>}}
2.只支持单继承,不支持多继承,因为类与类多继承的话,容易带来安全隐患。
如:
<span style="white-space:pre"></span>class Animal
<span style="white-space:pre"></span>{<span style="white-space:pre"></span>void eat(){<span style="white-space:pre"></span>System.out.println("吃鱼");<span style="white-space:pre"></span>}<span style="white-space:pre"></span>}<span style="white-space:pre"></span>class birds
<span style="white-space:pre"></span>{<span style="white-space:pre"></span>void eat(){<span style="white-space:pre"></span>System.out.println("吃虫");<span style="white-space:pre"></span>}<span style="white-space:pre"></span>}<span style="white-space:pre"></span>class cat
<span style="white-space:pre"></span>{<span style="white-space:pre"></span>}很显然,从现实生活中要是cat继承Animal和birds的话就乱套了,通俗讲就是一个儿子不能有多个父亲;而且在父类中定义了相同的方法,子类继承后不知道去调用哪一个,所以类不能被多继承.
三.继承的注意事项:
1.子类只能继承父类所有非私有的成员(成员方法和成员变量)
2.子类不能继承父类的构造方法,但是可以通过super关键字去访问父类构造方法,super也可以访问父类被覆盖的成员变量;
3.不要为了部分功能而去继承;
<span style="white-space:pre"></span>class A
<span style="white-space:pre"></span>{
<span style="white-space:pre"></span>int a=1;<span style="white-space:pre"></span>A(){<span style="white-space:pre"></span>System.out.println("构造方法A");<span style="white-space:pre"></span>}<span style="white-space:pre"></span>private int num = 1;<span style="white-space:pre"></span> int getNum(){ return num;<span style="white-space:pre"></span>}<span style="white-space:pre"></span>}<span style="white-space:pre"></span>class B extends A<span style="white-space:pre"></span>{<span style="white-space:pre"></span>
<span style="white-space:pre"></span>int a=2;<span style="white-space:pre"></span>B(){<span style="white-space:pre"></span>System.out.println("构造方法B");<span style="font-family: Arial, Helvetica, sans-serif;"></span>
<span style="white-space:pre"></span><pre name="code" class="java"><span style="white-space:pre"></span>System.out.println(a+super.a);//第一个a是B类中的,而super.a中的a是属于父类,通过super访问;} } class Demo { public static void main(String[] args) {B b = new B(); //System.out.println(b.num);//错误,不能访问 System.out.println(b.getNum());//OK的。可以间接访问父类私有成员;} }
四.继承中成员变量和成员方法的关系
1.成员变量:子类中可以定义跟父类相同的成员变量,此时将覆盖父类的成员变量;发生覆盖时,如果在子类中访问此成员变量,访问的将是子类的。
2.成员方法:子类中可以定义跟父类"一模一样"的成员方法,此时将覆盖父类中的同名的成员方法。这叫:方法重写;
方法重写的规则:什么是一模一样?
1)要求返回值类型、方法名、形参列表都必须一致;
返回值类型(一致) 方法名(一致) 形参列表(不一致):编译通过。效果"相当于"重载
返回值类型(不一致) 方法名(一致) 形参列表(一致):编译错误。
2)关于访问修饰符:子类必须拥有比父类更宽的访问修饰符;
方法重写的注意事项:
1)父类中私有的方法不能被重写;
2)子类重写父类方法时,访问权限不能更低:private,(默认),protected,public
3)父类静态方法,子类也必须通过静态方法进行重写。
<span style="white-space:pre"></span>class A
<span style="white-space:pre"></span>{
int num = 10;
<span style="white-space:pre"></span>private void pri(){//不能被子类重写<span style="white-space:pre"></span>System.out.println("A --> pri()");<span style="white-space:pre"></span>}
<span style="white-space:pre"></span>static void speak(){<span style="font-family: Arial, Helvetica, sans-serif;"></span>
<span style="white-space:pre"></span><pre name="code" class="java"><span style="white-space:pre"></span>System.out.println("你好");
<span style="white-space:pre"></span>}
void show()
<span style="white-space:pre"></span>{System.out.println("大家好");}void f(int a){System.out.println("A --> f()");}void m(int a){}<span style="white-space:pre"></span>}<span style="white-space:pre"></span>class B extends A<span style="white-space:pre"></span>{int num = 20;void show(){super.show();//super是在子类中用的。访问父类System.out.println("嗨!");}
<span style="white-space: pre;"></span><pre name="code" class="java"><span style="white-space:pre"></span>static void speak(){//重写父类时必须都是静态的<span style="font-family: Arial, Helvetica, sans-serif;"></span>
<span></span><pre name="code" class="java"><span style="white-space:pre"></span>System.out.println("你好啊");
<span></span>}void f(String a){System.out.println("B --> f()");}int m(int b){} } class Demo {public static void main(String[] args) {B b = new B();System.out.println(b.num);//访问的是子类的numb.show();//子类showb.f(10);//由于子类中没有f(int a)方法,所以调用父类的b.f("aa");//访问子类的 } } 五.概念与关键字的区别
1.重写和重载的区别:
1.重写:Override;存在子父类继承关系的情况下,子类重写父类的方法。
子类定义跟父类相同返回值类型、方法名、形参列表的方法;
重载:Overload;在一个类中。
定义多个同名的方法,但形参列表不完全相同。
跟返回值类型无关;
跟形参名无关;
2.this和super的区别?
this: 1)始终指向当前对象的引用,
2)访问当前对象的成员属性、成员方法、构造方法;
3)查找是从当前类中查找,如果没有,向父类查找;
4)必须使用this的场景:
(1).局部变量覆盖成员变量:使用this去访问成员变量
(2).一个构造方法调用另一个构造方法,必须使用this()去调用;必须放在此构造方法的第一句话;
super:1)始终指向父类对象的引用:
2)访问父类对象的成员属性、成员方法、构造方法;
3)必须使用super的场景:
(1).子类的成员变量覆盖父类的成员变量,使用super去访问父类的成员变量;
(2).父类没有无参构造方法,子类的构造方法必须显示的使用super()去调用父类带参的构造方法;必须放在此构造方法的第一句话;
六.总结
这里以一个实例代码的分析形式来总结,为了综合进行分析,代码中还加入了代码块,便于对之前的知识进行复习.
<span style="white-space:pre"></span>class Fu
<span style="white-space:pre"></span>{
String name;
int age;
void show(){
System.out.println("aaaaaaa");
}
Fu(int a){
age=a;
System.out.println("父中age="+a);
}
Fu(String a){
name=a;
System.out.println("name="+a);
}
{
System.out.println("代码块 Fu");
}
<span style="white-space:pre"></span>}
<span style="white-space:pre"></span>class child extends Fu
<span style="white-space:pre"></span>{
<span style="white-space:pre"></span>int age=23;
child(String a){
super(a);
System.out.println("name=="+age);
}
child(){
super(2);
System.out.println("age=="+age+”-”+this.age+”-”+super.age);
}
{
System.out.println("代码块 Zi");
}
<span style="white-space:pre"></span>}
<span style="white-space:pre"></span>class Demo
<span style="white-space:pre"></span>{
public static void main(String[] args)
{
child zi=new child();
}
<span style="white-space:pre"></span>}
程序中有子父类,且子父中各有自己的代码块和构造函数(方法),构造函数为带参数和不带参数;分析其运行情况;
1).当在main方法中new子类对象时,父类的代码块会被先执行,然后是构造函数的执行,其构造函数的执行过程是:如果子类中的构造函数有不带参数的构造函数,则new子类对象时虚拟机会调用的是不带参数的该构造函数,但是子类是继承了父类,所以此时父类的构造函数会先被加载,而加载的时候如果父类有带参的构造函数,则子类构造函数的第一句必须是”super(与父类一模一样的参数列表);”,否则会报错,因为类中要是有一个带参的构造函数了系统不会默认添加空参数的构造函数.
2).当new一个子类对象时,如果子类的构造函数中没有空参数的构造函数,则新建子类对象时()中必须有对应的实参,而子类的构造函数中的第一句也要与父类的对应的super(父类一致),
综上所述,下边的代码运行结果为:第一句:child zi=new child(),此时会调用child(){},然后是super(2),因为父类中只有Fu(int a){}和Fu(String a){}两个构造函数,则子类中super的()中的实参是什么类型就会调用父类中对应的构造函数,2是int型所以会调用Fu(int a){},然后Fu类中执行age=2,所以打印”父中age=2”结果,此时子类中的super(2)运行完毕,由于在程序中的赋值按就近原则进行赋值,所以System.out.println("age=="+age+”this.age=”+this.age+”super.age=”+super.age)中第一个age会是本类中的赋值,既前面的int age=23,要是没有前面的赋值则age会是最近的super里面的赋值结果,而this.age是本类的age,此时已经在子类中有自己的赋值了,所以this.age是23,要是没有前面的age=23,则this.age跟super一致;因为在父类中的age成员变量已经被局部变量所覆盖,所以super.age会是2,再加上代码块是一个类中最新被加载的部分所以运行结果是