黑马程序员Java基础第五章-----类的继承 多态 接口

时间:2023-02-12 14:20:56
------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------

一.类的特性之二  继承

1.类的继承的概念:

          一个类通过使用关键字extends和另外一个类取得关系,并且可以获得另外一个类的成员(成员变量和成员函数),叫做继承。

2.类的继承出现的原因:

          很多的时候,我们定义一个类,但是已经有一个更加抽象一些的类已经存在,它已经有了我们这个类的大部分的属性和方法,只是有少部分的本类的特有的方法和属性没有,为了避免大量的重复的工作,就出现了继承的概念,通过继承子类可以获得父类的方法和属性,子类中只需要定义特有的方法和属性,或者同一功能父类和子类的实现有差别,子类中只需要重写父类的方法,给出自己的实现,就会覆盖父类的方法。这样做极大的简化了编程的工作量。

3.继承的体现形式:使用关键字extends来声明,格式:   class    父类名   extends   自类名{ 括号中定义子类特有的方法和属性,或者覆盖父类的方法 }

父类:class Person                                        子类继承父类 :    class Student extends Person

          {                                                                                          {

                private  String  name ;                                                      private int grade ;

                private   int    age ;                                                                    public void sudy()

                  String city ;                                                                                 {

                  public  void walk()                                                                            执行代码

                  {                                                                                                  }

                        执行代码                                                                           }                                                     

                   }

         }       

示例中:1)父类中的属性name和age可以被子类Student继承到(私有的方法也能被继承),只是不能在子类中直接访问

              2)父类中的city属性可以被子类Student继承到,可以在子类中直接访问    

              3)父类中的walk方法可以被子类继承到

              4)子类中不能访问父类的name和age,但是若是从父类继承的方法中可以访问父类的私有属性name或者age,是可以的。                        

4.继承中的成员:

         1)可以继承父类的所有的成员,就像保存了一份到子类中一样(私有只是不能直接访问而已,已经继承到了)

         2)父类中的构造函数不能被子类继承,即使父类中的构造函数时public的(因为构造函数要和类名一样,如果继承到了子类中,就成了一般方法,但是这个方法没有返回值类型,违反了函数的定义,所以不能被继承)

         3)当子类中的成员变量和父类中的成员变量的名称一样,非私有时,子类中访问子类的那个变量默认即可省略了this,调用父类的那个要使用super.属性

         4)子类可以重写父类的方法,会在子类发生覆盖,但是子类方法的权限要大于或者等于父类的

         5)子类只可以访问父类中除了private权限的成员(同一个包中,不同包中的子类不能访问父类的默认的权限)

         6)父类私有的属性,用super也不能调用,子类无法直接访问父类的私有属性,子类需要通过调用父类的公有的方法访问。

         7)构造子类时先调用父类的构造函数构造父类(其实是查看一下父类构造函数对属性的初始化情况),然后在构造子类,子类构造函数第一行默认有super()。此时,父类对象在堆内存的子类对象的空间里面,对外内存中只有一个对象。

                                                                         黑马程序员Java基础第五章-----类的继承  多态  接口

5.覆盖和重载的区别:

         覆盖(复写)是指子列和父类之间,在继承过程中发生的两个函数一模一样的,只能发生在父子类之间,父类中的静态方法只能用静态方法来覆盖。

         重载是值在同一个类之间,有一个以上的名称相同但是参数列表不同的函数,返回值类型不同不能叫重载

比方说,在Student类中,我么也有一个方法

pulic void walk()

      执行代码

}

这就是覆盖,覆盖要两个方法一摸一样,并且子类的方法的权限大于父类。(在后面多态中会用到,编译只看父类的方法,若子类的方法权限小于父类甚至私有,在运行时会报错,所以必须子类的权限大于父类)

6.继承的特点:java中的继承都是单继承,一个类只能继承一个类

二.类的特性之三 多态

1.多态的概念:多态是将子类的对象赋给父类的引用,然后在调用成员时所发生的多种不同的调用方式

2.多态的表现形式:

                              父类   引用类型1 = new  子类();     引用类型1.方法();、

引用类型1就是父类型 的引用,指向了子类的对象,其实是将子类型向上转型了,向上转型是自动发生的,这时引用类型1调用方法时,就形成了多态。

3.多态的调用规则:

    1)调用非静态成员函数,编译时看左边,运行时可能右边

(意思是说:在编译时期,java编译器会去查看父类的引用所调用的方法在父类中有没有,如果有,则编译成功。如果没有,就编译失败。

                     在运行时期,JVM虚拟机会看右边是哪个子类对象,就调用那个子类对象中的方法,如果子类自己覆盖了该方法,就调用覆盖后的方法。如果子类没有覆盖方法,则调用的就是从父类继承过来的方法)

   2)调用成员变量,无论编译还是运行都看左边

(意思是说,在编译时期,java编译器会看左边的父类中有没有这个调用的成员变量,如果有,编译成功。如果没有,则编译失败。

                      在运行时期,JVM虚拟机调用也是看左边的类,如果是父类的引用,则调用父类的成员变量,如果是子类的引用则调用子类的成员变量[这种方式已经不是多态了]。)

   3)调用静态成员函数,无论编译还是运行都看左边

(意思是说,在编译时期,会查看左边的类中有没有这个方法,有就编译通过,没有就编译失败。运行时期,调用的是左边的类中的方法)

4.多态的前提:1.发生多态的两个类之间要有关系,要么继承要么实现

                         2.子类对象要指向父类的引用

                         3.父子类之间要发生覆盖(然后调用时,就会看调用那个方法),不覆盖则调用的还是从父类继承过来的方法

5.多态的好处:大大的提高了程序的可扩展性。

6.局限性:用父类的引用只能访问父类中有的成员,不能访问父类中没有但是子类中有的特有成员,那样会编译失败。

三.接口

1接口的概念:接口是一个特殊的类

2.接口的表现形式:interface A

                                {

                                       抽象方法(public abstract)

                                       常量(public  static  final )

                                }

3.接口的说明:定义接口使用关键字interface来完成。

 接口中可以定义抽象方法和常量两种组成部分:

         1)方法:接口中的方法都是抽象,都不能有方法体。都有固定的修饰符:public  abstract,但这个修饰符可以省略,java会默认加上这个修饰符
         2)常量:接口中定义的都是常量,不能改变值的量。public static final(final不能被赋值,只能初始化值一次)

4.接口的实现:一个类实现接口使用关键字implements来声明。

         1) 接口中的方法都是抽象的,子类实现接口中的所有的抽象方法,有一个抽象方法没有实现,那么这个子类就是抽象类要用abstract来修饰
         2) 接口不能实例化对象,子类实现了接口中的所有抽象方法,子类就可以实例化new对象
         3) 如果只实现了部分抽象方法,则子类是一个抽象类,也不能实例化对象
         4) 类只能单继承,可以多实现,接口可以多继承 

5.类实现接口的形式:

interface A

{

          void  sum(int a,int b);//接口中所有的方法都是抽象的

}

B类继承接口A:

class B implements A

{

        public void sum(int a,int b)

        {

               System.out.println("a+b="+a+b);

        }

}

 说明:实现一个接口,最简单的方式是空实现,方法体中什么都不写。

实现类中的覆盖方法都要是public的,因为子类实现接口,要覆盖其中的方法,因此实现的方法的权限要大于或者等于接口中方法的权限,而接口中的抽象的方法的固定修饰符是public  abstract,所以实现类中的方法也要是public的。

四.类,抽象类和接口总结

1)一般来说,当一个类中有些方法没有办法实现,而要靠它的子类来实现时,这个类就要实现定义成抽象类,大多数的顶层的类都是抽象的,下面有很多的直接或者间接的子类,每个子类对方法都有不同的实现,满足不同的功能。

2)一般来说,类中定义的方法都是同一类事物的共有方法,而对于类的特有的功能,属于扩展功能,一般都定义在接口中,只要让需要这个功能的类来实现这个接口就可以了。

五.程序示例

//定义一个学生类,继承一个抽象类,实现一个接口
abstract class Person
{
private String name ;
private int age ;
String city = "北京";
//所有的类都有构造函数,抽象类也有,但是不能实例化对象
Person(String name,int age)
{
this.name = name ;
this.age = age ;
}
//抽象方法,由子类来实现
public abstract void eat();

//向外提供获取姓名的公有的方法,让子类可以访问父类的私有的成员
public String getName()
{
return this.name ;
}

public int getAge()
{
return this.age ;
}
}

//玩游戏接口
interface Playing
{
//接口中的方法都是抽象方法,省略了public abstract。
void playGame();
}

//一个类继承另一个类,就继承了类的所有的成员
class Student extends Person implements Playing
{
//学生类特有的属性
private int grade ;

String city = "上海" ;//成员变量不会发生覆盖

Student(String name,int age)
{
super(name,age);
}

Student(String name,int age,int grade)
{
super(name,age);
this.grade = grade ;
}

public void eat()
{
//子类中访问本类的city用this,不写默认省略this
System.out.println(getName()+"在吃饭....."+this.city);
}
//子类中不能直接访问父类中的私有的成员,要通过父类的公有方法访问(私有成员只能在本类中访问)
public void show()
{
//子类中访问本类的city用this,this可以省略
System.out.println(getName()+"是学生,今年"+getAge()+"岁,班级是:"+grade+"....."+city);
}

public void playGame()
{
//子类中访问本类的city用super
System.out.println(getName()+"我在玩游戏......"+super.city);
}
}

class ClassDemo
{
public static void main(String[] args)
{
//普通的调用方式
Student s = new Student("张三",10,3);
s.eat();//上海
s.show();//上海
s.playGame();//北京
/*
父子类中有相同的名称成员变量,编译运行都看左边
就是说,那个引用类型调用的就调用那个类的成员变量

所以 s.city是上海 p.city是北京
*/
System.out.println(s.city);//上海

//多态的调用方式
Person p = new Student("李四",20);
p.eat();//上海
/*
不能调用,编译失败,多态中调用成员函数,编译看左边,运行看右边
这也是多态的限制,只能调用父类中有的成员
*/
//p.playGame();
//s.show();

System.out.println(p.city);//北京
}
}

运行结果:

黑马程序员Java基础第五章-----类的继承  多态  接口