【Java基础】——java面向对象(中)—继承、抽象类、接口

来源:互联网 发布:移动广告网络平台 编辑:程序博客网 时间:2024/05/18 11:28

一、面向对象——继承

1、继承概述

①什么是继承?

继承是面向对象的一个重要方面。当多个类存在相同属性和行为时,将这些类抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只需要继承那个类即可。关键字extends表明正在构造的新生类派生于一个已存在的类。已存在的类被称为超类(superclss)、基类(base class)或父类(parent class);新类被称为子类(subclass)。“is-a”关系是继承的一个明显特征。

②通过extends关键字来实现类与类的继承,如:
class 子类名 extends 父类名{}
有了继承以后,我们在定义一个类的时候,可以在一个已经存在的类的基础上,还可以定义自己的新成员。
但是,我们需要注意的是,在java中支持单继承,不直接支持多继承。但对c++中的多继承机制进行了改良。
a、单继承:一个子类只能有一个直接父类
b、多继承:一个子类可以有多个直接父类。(java中不允许)因为多个父类中有相同成员时,会产生调用的不确定性。
c、java支持多层继承(多重继承):如C继承B,B继承A,这样就出现了继承体系。
当我们需要使用一个继承体系时,我们首先要查看该体系中的顶层类,了解该体系的基本功能,然后再创建体系中的最子类对象,完成功能的使用。
③继承的设计原则:"高内聚低耦合"
所谓高内聚低耦合,简单的理解,内聚就是指自己完成某件事情的能力,耦合就是类与类之间的关系。我们在设计继承时候的原则就是:自己能完成的就不要麻烦别
人,这样将来别人产生了修改,就对我的影响较小。由此可见:在开发中使用继承其实是在使用一把双刃剑。今天我们还是以继承的好处来使用,因为继承还有很多其他的特性。
④继承的好处
  • 将多个类相同的成员可以放到一个类中,提高了代码的复用性。
  • 如果功能的代码需要修改,修改一处即可,提高了代码的维护性。
  • 让类与类之间产生了关系,是多态的前提。但这也造成了高耦合,也是其弊端之一
继承的一个简单示例如下:
/*继承概述*///使用继承前/*class Student {public void eat() {System.out.println("吃饭");}public void sleep() {System.out.println("睡觉");}}class Teacher {public void eat() {System.out.println("吃饭");}public void sleep() {System.out.println("睡觉");}}*///使用继承后,老师和学生都是人的范畴,抽取了老师和学生的共性功能,作为人的功能,让老师和学生去继承。class Person {public void eat() {System.out.println("吃饭");}public void sleep() {System.out.println("睡觉");}}class Student extends Person {}class Teacher extends Person {}class ExtendsDemo {public static void main(String[] args) {Student s = new Student();s.eat();s.sleep();System.out.println("-------------");Teacher t = new Teacher();t.eat();t.sleep();}}

2、继承注意事项

①子类只能继承父类所有非私有的成员(成员方法和成员变量)

②子类不能继承父类的构造方法,但是可以通过super关键字去访问父类构造方法。

③不要为了部分功能去继承。

④当类之间出现了”is-a“的关系时,我们才可以去继承。不能因为两个类中有部分代码相同就使用继承,这是不对的。

代码演示如下:

/*继承的注意事项:class A {public void show1(){}public void show2(){}}class B {public void show2(){}public void show3(){}}//我们发现B类中出现了和A类一样的show2()方法,所以,我们就用继承来体现class B extends A {public void show3(){}}这样其实不好,因为这样你不但有了show2(),还多了show1()。有可能show1()不是你想要的。继承其实体现的是一种关系:"is a"。采用假设法。如果有两个类A,B。只有他们符合A是B的一种,或者B是A的一种,就可以考虑使用继承。*/class Father {private int num = 10;public int num2 = 20;//私有方法,子类不能继承private void method() {System.out.println(num);System.out.println(num2);}public void show() {System.out.println(num);System.out.println(num2);}}class Son extends Father {public void function() {//num可以在Father中访问private//System.out.println(num); //子类不能继承父类的私有成员变量System.out.println(num2);}}class ExtendsDemo3 {public static void main(String[] args) {// 创建对象Son s = new Son();//s.method(); //子类不能继承父类的私有成员方法s.show();s.function();}}
3、super关键字

①super关键字和this的用法很像,this代表的是对应的引用,而super代表父类存储空间的标识(父类引用)。

②super关键字的用法:

  • 访问成员变量:super.成员变量
  • 访问构造方法:super(……)
  • 访问成员方法:super.成员方法()。
③继承中构造方法的关系
为什么子类所有的构造方法默认都会访问父类空参数的构造方法?
因为子类会继承父类的数据,可能还会使用父类的数据。所以,子类初始化之前,一定要先完成父类数据的初始化。为了实现这个效果,在子类构造的第一条语句上默认有一个super()。

/*继承中构造方法的关系A:子类中所有的构造方法默认都会访问父类中空参数的构造方法B:为什么呢?因为子类会继承父类中的数据,可能还会使用父类的数据。所以,子类初始化之前,一定要先完成父类数据的初始化。注意:子类每一个构造方法的第一条语句默认都是:super();*/class Father {int age;public Father() {System.out.println("Father的无参构造方法");}public Father(String name) {System.out.println("Father的带参构造方法");}}class Son extends Father {public Son() {//super();System.out.println("Son的无参构造方法");}public Son(String name) {//super();System.out.println("Son的带参构造方法");}}class ExtendsDemo6 {public static void main(String[] args) {//创建对象Son s = new Son();System.out.println("------------");Son s2 = new Son("林青霞");}}

④继承中成员方法的关系
a、当子父类中方法声明不一样的时候,通过子类对象去访问方法,这个很容易访问。
b、当子父类方法声明一样的时候,首先是通过子类对象去访问方法,先查找子类中有没有该方法,如果有该方法,就使用。如果子类中没有该方法,则去父类中查找有没有该方法,如果父类中有该方法,则使用。如果字父类中都没有该方法,则报错。
如下代码所示:

/*继承中成员方法的关系:*///父类class Father {public void show() {System.out.println("show Father");}}//子类class Son extends Father {public void method() {System.out.println("method Son");}public void show() {System.out.println("show Son");}}class ExtendsDemo8 {public static void main(String[] args) {//创建对象Son s = new Son();s.show();//找子类。s.method();//method son。先找子类中,子类有则调用。//s.fucntion(); //找不到符号,子父类中都没有该方法。}}
⑤方法重载(overroad)和方法覆盖(override)
a、什么是方法重载?(同一个类中)方法重载是指在同一个类中,出现方法名相同,参数列表不同的情况。
b、什么是方法覆盖?(子父类中)方法覆盖是指在子类中,出现和父类一模一样的方法声明的时候,会运行子类的函数,这种现象称为覆盖操作。
方法覆盖会发生在有继承关系的父类和子类之间,而且是在子类类型中,子类继承到父类的方法之后,觉得方法实现已经不足以满足新一代的要求了,于是就给出了新的方法实现。
覆盖注意事项:
  • 子类方法覆盖父类方法时,子类权限必须大于等于父类中的权限。
  • 静态只能覆盖静态或者被静态覆盖。
c、如何判断方法是不是重载呢?
  • 方法名必须相同
  • 返回值类型可能不同
  • 参数列表必须不同:参数类型不同,参数个数不同,参数顺序不同。

3、阻止继承:final类和方法

有时候,可能希望阻止人们利用某个类定义子类。不允许扩展的类被称为final类。如果在定义类的时候使用了final修饰符就表明这个类是final类。

①final关键字是最终的意思,可以修饰类,成员变量,成员方法。

  • final关键字修饰的类不可以被继承。
  • final修饰的方法不可以被覆盖。
  • final修饰的变量是一个常量,只能被覆盖。

②类中的方法也可以被声明为final。如果这样做,子类就不能覆盖这个方法(final类中的所有方法自动称为final方法。)将方法或类声明为final主要是鉴于以下原因:

  • 确保它们不会在子类中改变语义。例如:Calendar类中的getTime个setTime方法都声明为final。这表明Calendar类的设计者负责实现Date类与日历状态之间的转换,而不允许子类处理这些问题。同样的,String类也是final类,这意味着不允许任何人定义String的子类。换而言之,如果有一个String的引用,它引用的一定是一个String对象,而不可能是其它对象。
③为什么要用final修饰变量?
其实在程序中如果一个数据是固定的,那么直接使用这个数据就可以了。但是这样阅读性很差,所以就给该数据起个名称,而且这个变量名称的值不能再变化,所以加上final固定。
写法规范:常量所有字符都大写,如果多个单词,中间用"_"连接。
代码示范如下:

/*final可以修饰类,方法,变量*///final class Fu //无法从最终Fu进行继承class Fu {public int num = 10;public final int NUM_2 = 20;// Zi中的show()无法覆盖Fu中的show()/* public final void show() {System.out.println(num);} */}class Zi extends Fu {// Zi中的show()无法覆盖Fu中的show()public void show() {num = 100;System.out.println(num);//无法为最终变量num2分配值//NUM_2 = 200;System.out.println(NUM_2);}}class FinalDemo {public static void main(String[] args) {Zi z = new Zi();//100z.show();//20}}

④final关键字面试题
final修饰局部变量
  • 在方法内部,该变量不可改变。
  • 在方法声明上,如果是基本类型,则值不能改变;如果是引用类型,则是地址值不能改变。
代码示例如下:
/*面试题:final修饰局部变量的问题基本类型:基本类型的值不能发生改变。引用类型:引用类型的地址值不能发生改变,但是,该对象的堆内存的值是可以改变的。*/class Student {int age = 10;}class FinalTest {public static void main(String[] args) {//局部变量是基本数据类型int x = 10;x = 100;System.out.println(x);//100final int y = 10;//无法为最终变量y分配值(基本类型)//y = 100;System.out.println(y);//10System.out.println("--------------");//局部变量是引用数据类型Student s = new Student();System.out.println(s.age);//10s.age = 100;System.out.println(s.age);//100System.out.println("--------------");//引用数据类型的地址值不可改变,但堆内存的值是可变的!final Student ss = new Student();System.out.println(ss.age);//10ss.age = 100;System.out.println(ss.age);//100//重新分配内存空间//错误:无法为最终变量ss分配值(引用类型)//ss = new Student();}}
  final修饰变量的初始化时机:在构造对象前完毕即可。

二、面向对象——抽象类

1、什么是抽象?

定义:抽象类就从多个事物中,将共性的、本质的东西提取出来。

例如:老师和学生,都是人,都具有如姓名、性别、年龄等属性。将其抽取出来,放置于继承关系较高层次的通用超类人中。

2、抽象类

①什么是抽象类?

Java中可以定义没有方法体的方法,该方法的具体实现由子类完成,该方法称为抽象方法,包含抽象方法的类就是抽象类。

抽象类的由来:多个对象都具备相同的功能,但是功能具体内容有所不同,那么在抽取过程中,只抽取了功能定义,并未抽取功能主体,那么只有功能声明,没有功能主题的方法称为抽象方法。

②抽象类的特点

  • 方法只有声明没有实现时,该方法就是抽象方法,需要abstract修饰,抽象方法必须定义在抽象类中,该类也必须被abstract修饰。
  • 抽象类不能被实例化,因为调用抽象方法没有意义。抽象类按照多台的方式,由具体的子类实例化。
  • 抽象类必须有其子类覆盖了所有的抽象方法后,该子类才可以初始化,否则这个子类还是抽象类。
  • 抽象类不一定有抽象方法,有抽象方法的类一定是抽象类。
如下代码所示,演示一下抽象类的使用:
/*抽象类的概述:动物不应该定义为具体的东西,而且动物中的吃,睡等也不应该是具体的。我们把一个不是具体的功能称为抽象的功能,而一个类中如果有抽象的功能,该类必须是抽象类。抽象类的实例化其实是靠具体的子类实现的。是多态的方式。People p = new Student();*///abstract class People //抽象类的声明格式abstract class People {//抽象方法//public abstract void eat(){} //空方法体,这个会报错。抽象方法不能有主体public abstract void eat();public People(){}}//子类是抽象类abstract class Teacher extends People {}//没有实现父类的抽象方法。还是抽象类。//子类是具体类,重写抽象方法class Student extends People {public void eat() {System.out.println("学生吃饭");}}class AbstractDemo {public static void main(String[] args) {//创建对象//people是抽象的; 无法实例化//People p = new People();//通过多态的方式People p = new Student();p.eat();}}

3、抽象类的几个问题

①抽象类中有构造方法吗?
有,用于给子类对象进行初始化。
②抽象类可以定义非抽象方法吗?
可以。但是很少见,目的是不让该类创建对象,AWT的适配器对象就是这种类,通常这个类中的方法有方法体,但是却没有内容。
③抽象的关键字不可以和哪些关键字共存?
private不行,static不行,final不行。
原因在于
  • final:被final修饰的类不能有子类。而abstract修饰的类一定是父类。
  • private:抽象类中的私有的抽象方法,不能被子类所知,也就无法复写,而抽象方法的出现就是要被复写。
  • static:如果static修饰抽象方法,那么对象都不用new了。直接类名.方法调用就行了。
如下列代码所示:
/*一个类如果没有抽象方法,可不可以定义为抽象类?如果可以,有什么意义?A:可以。B:不让创建对象。abstract不能和哪些关键字共存?private冲突final冲突static无意义*/abstract class Fu {//public abstract void show();//非法的修饰符组合: abstract和private//private abstract void show();//非法的修饰符组合//final abstract void show();//非法的修饰符组合static abstract void show();public static void method() {System.out.println("method");}}class Zi extends Fu {public void show() {}}class AbstractDemo {public static void main(String[] args) {Fu.method();}}

4、抽象类和一般类的异同点

①相同点:抽象类和一般类都是用来描述事物的,都是内部定义了成员。
②不同点:
  • 一般类有足够的信息描述事物;抽象类中描述的信息可能不足。
  • 一般类不能定义抽象方法,只能定义非抽象方法;抽象类可以定义抽象方法,也可以定义非抽象方法。
  • 一般类可以实例化;抽象类不可以实例化。

三、接口

1、接口的概念

接口可以被看成是一个特殊的抽象类。当一个抽象类中的方法都是抽象的时候,这是可以将抽象类用另一种形式来定义和表示,就是接口(interface)。定义接口使用的关键字不是class,而是interface,接口当中的常见成员,而且这些成员都有固定的修饰符:全局变量,抽象方法。

2、接口的特点

接口用关键字interface表示格式:interface接口名{}。

类实现接口用implements表示。格式:class类名implements接口名{}。

③接口不是类,不能实例化。尤其不能用new运算符实例化一个接口。按照多态的方式,由具体的子类实例化。其实这也是多态的一种,接口多态。

④接口的子类要么是抽象类,要么重写接口中的所有方法。

⑤不能构造接口的对象,但却能声明接口的变量。如:Comparable x;

⑥接口变量必须引用实现了接口的类对象。如:x = new XXXX();

⑦接口中不能包含实例域或静态方法,但却可以包含常量。

3、接口成员的特点

①成员变量:只能是常量。默认修饰符是public static final。

②构造方法:没有,因为接口主要是扩展功能的,而没有具体存在

③成员方法:只能是抽象方法,默认修饰符public abstract。

4、接口必须掌握的知识

  • 接口当中的成员都是公共的,是自动属于public的。因此,在接口方法声明中,可以不写public。
  • 类与类之间的是继承关系(extends);类与接口之间是实现关系(implments)。
  • 接口不可以实例化,只能由实现类接口的子类并覆盖了接口中所有的抽象方法后,该子类才可以实例化,否则,这个子类就是一个抽象类。
  • 接口实现的步骤:a、将类声明为实现给定的接口。b、对接口中的所有方法进行定义。
  • 在java中不直接支持多继承,因为会出现调用的不确定性,所以java将多继承机制进行了改良,在java中可以多实现,即一个类可以实现多个接口。
  • 一个类在继承另一个类的同时,还能实现多个接口。接口的出现避免了单继承的局限性。
  • 接口与接口之间可以继承,而且接口可以多继承。

5、抽象类和接口的异同点

①相同点:都是不断向上抽取而来的。

②不同点:

  • 抽象类需要被继承,而且只能单继承;接口需要被实现,而且能多实现。
  • 抽象类中可以定义抽象方法和非抽象方法,子类继承后,可以直接使用非抽象方法;接口中只能定义抽象方法,必须由接口去实现。
  • 抽象类的继承是is-a关系,在定义该体系内的基本共性内容;接口的实现是like-a关系,在定义体系的额外功能。
接口应用案例代码演示:
/*老师和学生案例,加入抽烟的额外功能分析:从具体到抽象老师:姓名,年龄,吃饭,睡觉学生:姓名,年龄,吃饭,睡觉由于有共性功能,我们提取出一个父类,人类。人类:姓名,年龄吃饭();睡觉(){}抽烟的额外功能不是人或者老师,或者学生一开始就应该具备的,所以,我们把它定义为接口抽烟接口。部分老师抽烟:实现抽烟接口部分学生抽烟:实现抽烟接口实现:从抽象到具体使用:具体*///定义抽烟接口interface Smoking {//抽烟的抽象方法public abstract void smoke();}//定义抽象人类abstract class Person {//姓名private String name;//年龄private int age;public Person() {}//构造器初始化public Person(String name,int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}//吃饭方法;public abstract void eat();//睡觉睡觉方法{}public void sleep() {System.out.println("睡觉觉了");}}//具体老师类class Teacher extends Person {public Teacher() {}public Teacher(String name,int age) {super(name,age);}public void eat() {System.out.println("吃大白菜");}}//具体学生类class Student extends Person {public Student() {}public Student(String name,int age) {super(name,age);}public void eat() {System.out.println("吃红烧肉");}}//抽烟的老师class SmokingTeacher extends Teacher implements Smoking {public SmokingTeacher() {}public SmokingTeacher(String name,int age) {super(name,age);}//抽烟方法public void smoke() {System.out.println("抽烟的老师");}}//抽烟的学生class SmokingStudent extends Student implements Smoking {public SmokingStudent() {}public SmokingStudent(String name,int age) {super(name,age);}//实现抽烟方法public void smoke() {System.out.println("抽烟的学生");}}class InterfaceTest2 {public static void main(String[] args) {//测试学生SmokingStudent ss = new SmokingStudent();ss.setName("黄祥");ss.setAge(23);System.out.println(ss.getName()+"---"+ss.getAge());ss.eat();ss.sleep();ss.smoke();System.out.println("-------------------");//测试老师SmokingTeacher st = new SmokingTeacher();st.setName("刘老师");st.setAge(45);System.out.println(st.getName()+"---"+st.getAge());st.eat();st.sleep();st.smoke();}}

2 0
原创粉丝点击