Java基础之抽象类和继承知识点总结

来源:互联网 发布:淘宝卖家说给补偿5元 编辑:程序博客网 时间:2024/06/05 19:15


为了提高代码的复用,将共性代码的代码进行抽取。抽取到单独的一个类中进行封装。这时学生和工人类中就需要定义姓名和年龄了。可是学生和工人怎么获取到Person类中的姓名和年龄呢?可以让学生和Person产生一个关系,java中提供了一个关键字可以完成这个动作。extends(继承)。
例子1:父子类继承实例演示。
package cn.itheima.day17;class Person{String name;int age;}class Student extends Person{//Student就称之为子类,Person就成为父类,或者超类,或者基类。   //子类可以获取到父类中的成员(当然,不是全部。一会要逐一说明)public void study(){System.out.println("study");}}class Work extends Person{public void work(){System.out.println("work");}}public class ExtendsDemo {public static void main(String[] args) {Student s = new Student();s.name="heima";s.age = 30;s.study();}}
继承:    好处:       1)继承的出现,提高了代码的复用性。       2)继承的出现,让类与类之间产生了关系。而这个关系出现,就导致面向对象的第三个特征,多态的产生。简单说:继承就是多态的前提。Java中支持单继承,不支持多继承(其实更确切的说,是java将多继承进行改良,避免了安全隐患的产生)。    classA{void show(){System.out.println("a");}    classB{}{void show(){System.out.println("b");}    classC extends A,B{}//这就是多继承,一个子类同时有多个父类。在java中是允许的。    C c= new C();    c.show();//到底是运行a,还是运行b呢?所以这就是安全隐藏。为了避免,所以java不允许这样继承。简单说:一个儿子只能有一个爹,不能同时有多个爹。Java中可以存在多层(多重)继承。    classA{}    classB extends A{}    classC extends B{}这时就出现了继承体系。记住一个原则:    1)A类中定义是这个体系中的最共性的功能。    2)要使用这个体系的功能,建议建立最子类的对象来完成调用。父类的由来都是子类不断向上抽取而来的。就代表着,A中定义的功能是该体系中最共性的功能。所以要了解这个体系的功能,只要参考A类中的功能即可。了解后,需要建立对象来调用这些功能,那么建立哪个类的对象好呢?建议建立C的对象,因为C中可以使用A类中的共性功能,也可以使用C类中的特有功能。简单说:要使用一个继承体系,原则:   1)参阅父类功能,   2)建立最子类对象。什么时候定义继承呢?    继承是用于程序设计的。只要一个事物是另一个事物的一种。就可以用继承体现。例如:猫或者虎都是猫科这类事物中的一种。就是可以进行继承。狗或者狼都是犬科,也可以继承。猫科和犬科都是哺乳动物中的一种,也可以进行继承。简单的分析:怎么判断是另一个事物的中的一种呢?    那么可以先视为可以继承,用更简单的方式判断,一个类如果继承了另一个类,那么这个类是否应该具备另一个类的所有的成员。如果可以。继承有效,如果不可以,那么无法继承。
例子2:继承的错误表达实例演示。
class A{void method1(){}void method2(){}}class B {//extends A//如果为了提高复用,让B继承A这样,B就不用在定义method1()方法了,//但是B也获取到它不应该具备的method2().那么这样的继承不可以存在。//但是我们发现,虽然A和B之间没有继承关系,但是他们有共性的内容,那么就可以向上抽取。void method1(){}void method3(){}}class C{void method1(){}}class A extends C{void method2(){}}class B extends C{void method3(){}}
注意的是:不要为了获取另一个类中的部分功能,而继承。这样是仅仅为了提高复用性而继承,并没有判断事物之间的关系。这样不可取。继承出现后,在子父类中的成员特点。成员:    成员变量。    成员函数。    构造函数。在子父类中成员变量的特点当子父类中出现同名的成员变量时,这时为了区分两个变量,在子类,用this调用的是子类的变量。用     super调用的是父类中的变量。     super其实和this的用法很相似。     this代表是本类对象的引用     super代表的是父类的存储空间。     this可以区分局部变量和成员变量重名的情况。     super可以用于区分子父类中成员变量重名的情况。属性在类中通常都是私有的,而且父类定义完了以后,子类一般是不会定义同名变量的。而是直接使用父类的变量即可所以开发并不常见。而面试却很常见。注意:在访问上,子类不可以直接访问父类中的私有成员。可以通过间接的形式访问。
例子3:成员变量的实例演示。
package cn.itheima.day17;class Fu{protected int num = 3;public int getNum() {return num;}public void setNum(int num) {this.num = num;}}class Zi extends Fu{private int num = 5;public void show(){int num = 6;System.out.println("num="+num);System.out.println("num="+this.num);System.out.println("num="+super.num);}}public class ExtendsDemo2 {public static void main(String[] args) {Zi z = new Zi();z.show();}}
子父类中成员函数的特点特殊情况:    当子父类中出现一模一样的函数时,子类对象在调用该函数时,运行的是子类中的函数。父类中的函数好像被覆盖一样。这就是函数的另一个特性:覆盖(复写,重写)override 函数的另一个特性: 重载 overload覆盖:在子父类中,如果出现一模一样的函数时,就会发生覆盖操作。
例子4:成员函数的实例演示。
package cn.itheima.day17;class Fu1{void show(){System.out.println("fu show run");}}class Zi1 extends Fu1{void show(){System.out.println("zi show run");}}class Telephone{void show(){System.out.println("number");}void call(){System.out.println("call");}}/*在日后的几年中,手机也在不断的升级。来电显示功能,既可以显示号码, * 又可以显示姓名,还可以显示大头贴。      解决办法:可以在原来的TelPhone类中对show功能的源代码进行修改。      这种解决动作,一旦修改,改的就不仅仅是一个代码而是一堆,那就是灾难。      解决方法二:重新对新手机进行描述,并定义新功能。但是有些功能没有变化,      只要继承自原来的老版手机可以获取该功能。新手机定义一个新的来电显示功能,   newShow.但是继承了TelPhone以后,新手机就有了两个来电显示功能。Show和newShow.      这样的设计是不合理,因为新手机没有必要具备show功能。那么重新设计,应该是这样:      原手机中已有来电显示功能,而新手机,具备该功能,这时该功能的内容有所不同。     所以没有必要定义新的功能,只要沿袭原来的功能声明,并定义自己新的功能实现即可。     发现继承的好处,可以有利于程序的扩展。不需要改动原来代码的情况下,就可以实现     程序的功能扩展定义。*/class NewTelephone extends Telephone{void show(){                //System.out.println("number");//发现来电号码显示父类已经定义完了,                                     //子类没有必须重复定义,只要使用父类的已有功能即可。                                    //这时只要调用父类已有功能。因为父类和子类出现了一模一样                    //的方法,为了区分,可以使用super关键字来完成。super.show();System.out.println("name");System.out.println("picture");}}public class ExtendsDemo3 {public static void main(String[] args) {Zi1 z = new Zi1();z.show();NewTelephone tp = new NewTelephone();tp.show();tp.call();}}
子父类中的构造函数的特点通过结果发现,子类的构造函数运行前,父类的构造函数先运行了,为什么呢?    原因在于在子类的构造函数的第一行,其实就一条默认的隐式语句 super();    super():和this():用法是一样的。       this();调用了本类中的构造函数。       super():调用了父类中的构造函数。子类的构造函数中为什么都有一个默认的super()语句呢?子类在实例化的时候,因为子类继承了父类的成员数据,所以必须要先看父类是如何对自己的成员进行初始化的。子类的所有构造函数默认都会访问父类中空参数的构造函数。当父类中没有定义空参数的构造时,子类必须通过super语句或者this语句,明确指定要访问的子类中或者父类中的构造函数。                 简单说:子类的构造函数无论如何,都必须要访问父类中的构造函数。要明确父类的初始化过程.                                  super语句用于访问父类的初始化,而初始化动作要先完成,所以super语句必须定义在构造函数的第一行。那么就和曾经的this语句冲突了。因为this语句也要定义在构造函数的第一行。所以一个构造函数中,只能有一个要么this语句,要么super语句。而且不冲突,因为子类中至少会有一个构造函数会去访问父类中的构造函数。一样可以完成父类的初始化。这个就是子类的实例化过程。
例子5:子父类构造函数的初始化的实例演示。
package cn.itheima.day17;class Fu2{int num;public Fu2(){num = 4;System.out.println("fu run");}public Fu2(int x) {System.out.println("fu..."+x);}}class Zi2 extends Fu2{public Zi2() {this(20);System.out.println("zi run..."+num);}public Zi2(int x) {//super(90);System.out.println("zi2...."+x);}}public class ExtendsDemo4 {public static void main(String[] args) {//Zi2 zi1 = new Zi2();Zi2 zi2 = new Zi2(2);}}
继承的弊端:打破的封装性。如果恶意继承并进行不正确的覆盖,会导致原功能的错误。不让其他类继承可以解决这个问题。这就需要一个关键字来完成 :final(最终)final:作为一个修饰符     1)它可以修饰类,可以修饰方法,可以修饰变量。     2)final修饰的类是一个最终类,不可以被继承。     3)final修饰的方法不可以被覆盖。     4)final修饰的变量的是一个常量,只能被赋值一次。这个赋值指的是显示初始化赋值。什么时候将变量修饰成final的呢?通常在程序中会使用一些不会变化的数据。也就是常见的一些常量值。比如 3.14。那么这个数据直接使用是可以的,但是不利于阅读,所以一般情况下,都会被该数据起个容易阅读的名称。final double PI = 3.14; final修饰的常量定义一般都有规范书写,被final修饰的常量名称,所有字母都大写。如果由多个单词所组成,每一个单词间用“_”连接。
例子6:final关键字的应用实例。
package cn.itheima.day17;/*final*/ class Fu3{  //final修饰的类不能被继承/*final*/ void show(){  //final修饰的方法不能被覆盖System.out.println("调用系统资源");}void method(){System.out.println("method run");}}class Zi3 extends Fu3{final int num; //final修饰成员变量,最终化的值应该是显示初始化值。public static final int aa = 45; //全局常量public Zi3() {num = 8;}public Zi3(int x) {num = x;}void show(){final int MY_NUMBER = 4;System.out.println("haha...系统被干掉啦"+num);}}public class FinalDemo {public static void main(String[] args) {Zi3 z=new Zi3();z.show();Zi3 z1=new Zi3(55);z1.show();}}//饿汉式class Single{private static final Single s = new Single();private Single(){}public static Single getInstance(){return s;}}*/
抽象类两个类DemoA DemoB里面都有共性的功能,可以进行抽取。可是功能的声明相同,但是功能的具体内容不同。这时,我们只对相同的功能声明进行抽取。而不抽取功能的内容。抽象类就是子类不断向上抽取而来的,只抽取了子类的功能声明,而没有抽取子类的具体的功能内容。所以功能是抽象的,需要定义在抽象类中。  抽象类的特点      1)抽象类和抽象方法必须用abstract关键字修饰。      2)抽象方法一定要存放在抽象类中。      3)抽象类不可以被实例化。也就是不可以通过该类建立对象。因为抽象类建立对象后,调用抽象方法是没有意义。       4)只有抽象类的子类将抽象类中的抽象方法全都覆盖掉,该子类就可以了建立对象了。如果只是部分覆盖,那么该子类还是一个抽象类。   实例演示:     abstract class Demo{          abstract void show();//这时这个函数就看不懂了,因为没有方法体。          //这个就需要被标识一下成看不懂的函数。需要一个关键字来修饰一下。abstract(抽象)当类中出现了抽象方法后,该类也必须标识成抽象的。      }      class DemoA extends Demo {            void show(){            System.out.println("showa");            }      }      class DemoB extends Demo {             void show(){             System.out.println("showb");            }      }抽象类什么时候定义。当我们分析事物时,对对象进行描述时,其实就不断把对象中的共性内容向上抽取。在抽取过程中,发现对象具备相同的功能,但是功能的细节不同。这时在定义类时,该功能是没有具体的实现的,是由具体的对象来完成的。那么该功能就是抽象的。抽象类可以定义事物的共性内容,而且定义抽象功能,可以强迫子类去实现。
例子7:     预热班学生:学习。      就业班学生:学习。可以对这两个的学生进行共性抽取。形成一个父类:学员。 
package cn.itheima.day17;/** * 需求:预热班学生:学习。就业班学生:学习。 * 可以对这两个的学生进行共性抽取。形成一个父类:学员。  * @author wl-pc */abstract class XueYuan{abstract void study();}class YuRenBanXueYuan extends XueYuan{@Overridevoid study() {System.out.println("JAVA SE");}}class JiuYeBanXueYuan extends XueYuan{@Overridevoid study() {System.out.println("JAVA EE");}}public class AbstractDemo {public static void main(String[] args) {YuRenBanXueYuan xueyuan1 = new YuRenBanXueYuan();xueyuan1.study();JiuYeBanXueYuan xueyuan2 = new JiuYeBanXueYuan();xueyuan2.study();}}
例子8:公司中程序员有姓名,工号,薪水,工作内容。项目经理除了有姓名,工号,薪水,还有奖金,工作内容。对给出需求进行数据建模。
package cn.itheima.day17;/*需求:公司中程序员有姓名,工号,薪水,工作内容。项目经理除了有姓名,工号,薪水,还有奖金,工作内容。对给出需求进行数据建模。分析:这里有两个具体事物。1,程序员属性: name id pay 行为: work()2,项目经理。属性: name id pay  bonus行为:work()发现这两个事物具备共性内容。为了提高代码的复用性。两个事物间是不具备继承关系的。因为两个事物不存在谁是谁中一种。但是,可以确定是无论程序员,还是经理,他们都是公司员工。他们都是员工的一种。而且员工都具备一些基本属性和行为。这时就可以将两个事物向上抽取,出一个员工类。该类中定义就是两个事物共性的内容。*/abstract class Employee{private String name;private String id;private double pay;public Employee(String name, String id, double pay) {super();this.name = name;this.id = id;this.pay = pay;}public abstract void work();}class Manager extends Employee{    private double bonus;public Manager(String name, String id, double pay, double bonus) {super(name, id, pay);this.bonus = bonus;}@Overridepublic void work() {System.out.println("manager");}}class Programmer extends Employee{public Programmer(String name, String id, double pay) {super(name,id,pay);}@Overridepublic void work() {System.out.println("code");}}public class AbstractDemo2 {public static void main(String[] args) {Manager manager =new Manager("zhangsan", "12", 5000.00, 3000.00);manager.work();Programmer programmer =new Programmer("lisi", "03", 7000.00);programmer.work();}}
抽象类的一些细节1,抽象类中是否有构造函数      有。只要是class定义的类,里面肯定有构造函数。抽象类中的构造函数,用于给子类提供实例化。其实抽象类和一般类没什么区别。该怎么描述事物,就怎么描述。只不过有些功能,是该类中无法确定的内容,所以比普通类多了抽象方法。2,抽象类中是否可以不定义抽象方法     可以不定义抽象方法,没有抽象方法的抽象类存在意义仅仅是不让该类创建对象。因为创建的没意义。这种情况在java awt中有具体体现。3,抽象关键字和哪些关键字不可以共存    final:如果方法被抽象,就需要被覆盖,而final是不可以被覆盖,所以冲突。              编译提示:非法的修饰符组合:abstract和 final    private:如果函数被私有了,子类无法直接访问,怎么覆盖呢?              编译提示:非法的修饰符组合:abstract 和 private    static : 不需要对象,类名既可以调用抽象方法。而调用抽象方法没有意义。               编译提示:非法的修饰符组合:abstract和 static子父类中覆盖的一些细节。      1,子类覆盖父类必须要保证权限大于等于父类. 重点。一定要注意权限。      2,静态覆盖静态。开发的时候,一般没有静态覆盖静态的情况。实例演示:final class Fu{    //abstract void show();    //staticvoid method(){}}class Zi extends Fu{    //publicvoid show(){}    //staticvoid method(){}}





0 0
原创粉丝点击