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

2 篇文章 0 订阅
1 篇文章 0 订阅
为了提高代码的复用,将共性代码的代码进行抽取。
抽取到单独的一个类中进行封装。这时学生和工人类中就需要定义姓名和年龄了。可是学生和工人怎么获取到Person类中的姓名和年龄呢?可以让学生和Person产生一个关系,java中提供了一个关键字可以完成这个动作。extends(继承)。
例子1:父子类继承实例演示。

 
 
[java] view plain copy
  1. package cn.itheima.day17;  
  2. class Person{  
  3.     String name;  
  4.     int age;  
  5. }  
  6. class Student extends Person{  
  7. //Student就称之为子类,Person就成为父类,或者超类,或者基类。  
  8.                    //子类可以获取到父类中的成员(当然,不是全部。一会要逐一说明)  
  9.     public void study(){  
  10.         System.out.println("study");  
  11.     }  
  12. }  
  13. class Work extends Person{  
  14.     public void work(){  
  15.         System.out.println("work");  
  16.     }  
  17. }  
  18. public class ExtendsDemo {  
  19.     public static void main(String[] args) {  
  20.         Student s = new Student();  
  21.         s.name="heima";  
  22.         s.age = 30;  
  23.         s.study();  
  24.     }  
  25. }  
继承:
    好处:
       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:继承的错误表达实例演示。
[java]  view plain  copy
  1. class A{  
  2.     void method1(){}  
  3.     void method2(){}  
  4. }  
  5. class B {//extends A//如果为了提高复用,让B继承A这样,B就不用在定义method1()方法了,  
  6.     //但是B也获取到它不应该具备的method2().那么这样的继承不可以存在。  
  7.     //但是我们发现,虽然A和B之间没有继承关系,但是他们有共性的内容,那么就可以向上抽取。  
  8.     void method1(){}  
  9.     void method3(){}  
  10. }  
  11. class C{  
  12.     void method1(){}  
  13. }  
  14. class A extends C{  
  15.     void method2(){}  
  16. }  
  17. class B extends C{  
  18.     void method3(){}  
  19. }  
注意的是:不要为了获取另一个类中的部分功能,而继承。这样是仅仅为了提高复用性而继承,并没有判断事物之间的关系。这样不可取。
继承出现后,在子父类中的成员特点。
成员:
    成员变量。
    成员函数。
    构造函数。
在子父类中成员变量的特点当子父类中出现同名的成员变量时,这时为了区分两个变量,在子类,用this调用的是子类的变量。用     super调用的是父类中的变量。
     super其实和this的用法很相似。
     this代表是本类对象的引用
     super代表的是父类的存储空间。
     this可以区分局部变量和成员变量重名的情况。
     super可以用于区分子父类中成员变量重名的情况。
属性在类中通常都是私有的,而且父类定义完了以后,子类一般是不会定义同名变量的。而是直接使用父类的变量即可所以开发并不常见。而面试却很常见。
注意:在访问上,子类不可以直接访问父类中的私有成员。可以通过间接的形式访问。
例子3:成员变量的实例演示。
[java]  view plain  copy
  1. package cn.itheima.day17;  
  2. class Fu{  
  3.     protected int num = 3;  
  4.     public int getNum() {  
  5.         return num;  
  6.     }  
  7.     public void setNum(int num) {  
  8.         this.num = num;  
  9.     }  
  10. }  
  11. class Zi extends Fu{  
  12.     private int num = 5;  
  13.     public void show(){  
  14.         int num = 6;  
  15.         System.out.println("num="+num);  
  16.         System.out.println("num="+this.num);  
  17.         System.out.println("num="+super.num);  
  18.     }  
  19. }  
  20. public class ExtendsDemo2 {  
  21.     public static void main(String[] args) {  
  22.         Zi z = new Zi();  
  23.         z.show();  
  24.     }  
  25. }  
子父类中成员函数的特点特殊情况:
    当子父类中出现一模一样的函数时,子类对象在调用该函数时,运行的是子类中的函数。父类中的函数好像被覆盖一样。这就是函数的另一个特性:覆盖(复写,重写)override 函数的另一个特性: 重载 overload
覆盖:在子父类中,如果出现一模一样的函数时,就会发生覆盖操作。
例子4:成员函数的实例演示。
[java]  view plain  copy
  1. package cn.itheima.day17;  
  2. class Fu1{  
  3.     void show(){  
  4.         System.out.println("fu show run");  
  5.     }  
  6. }  
  7. class Zi1 extends Fu1{  
  8.     void show(){  
  9.         System.out.println("zi show run");  
  10.     }  
  11. }  
  12. class Telephone{  
  13.     void show(){  
  14.         System.out.println("number");  
  15.     }  
  16.     void call(){  
  17.         System.out.println("call");  
  18.     }  
  19. }  
  20. /*在日后的几年中,手机也在不断的升级。来电显示功能,既可以显示号码, 
  21.  * 又可以显示姓名,还可以显示大头贴。 
  22.       解决办法:可以在原来的TelPhone类中对show功能的源代码进行修改。 
  23.       这种解决动作,一旦修改,改的就不仅仅是一个代码而是一堆,那就是灾难。 
  24.       解决方法二:重新对新手机进行描述,并定义新功能。但是有些功能没有变化, 
  25.       只要继承自原来的老版手机可以获取该功能。新手机定义一个新的来电显示功能, 
  26.    newShow.但是继承了TelPhone以后,新手机就有了两个来电显示功能。Show和newShow. 
  27.       这样的设计是不合理,因为新手机没有必要具备show功能。 
  28. 那么重新设计,应该是这样: 
  29.       原手机中已有来电显示功能,而新手机,具备该功能,这时该功能的内容有所不同。 
  30.      所以没有必要定义新的功能,只要沿袭原来的功能声明,并定义自己新的功能实现即可。 
  31.      发现继承的好处,可以有利于程序的扩展。不需要改动原来代码的情况下,就可以实现 
  32.      程序的功能扩展定义。*/  
  33. class NewTelephone extends Telephone{  
  34.     void show(){  
  35.                 //System.out.println("number");//发现来电号码显示父类已经定义完了,  
  36.                                      //子类没有必须重复定义,只要使用父类的已有功能即可。  
  37.                                     //这时只要调用父类已有功能。因为父类和子类出现了一模一样  
  38.                             //的方法,为了区分,可以使用super关键字来完成。  
  39.         super.show();  
  40.         System.out.println("name");  
  41.         System.out.println("picture");  
  42.     }  
  43. }  
  44. public class ExtendsDemo3 {  
  45.     public static void main(String[] args) {  
  46.         Zi1 z = new Zi1();  
  47.         z.show();  
  48.         NewTelephone tp = new NewTelephone();  
  49.         tp.show();  
  50.         tp.call();  
  51.     }  
  52. }  
子父类中的构造函数的特点通过结果发现,子类的构造函数运行前,父类的构造函数先运行了,为什么呢?
    原因在于在子类的构造函数的第一行,其实就一条默认的隐式语句 super();
    super():和this():用法是一样的。
       this();调用了本类中的构造函数。
       super():调用了父类中的构造函数。
子类的构造函数中为什么都有一个默认的super()语句呢?
子类在实例化的时候,因为子类继承了父类的成员数据,所以必须要先看父类是如何对自己的成员进行初始化的。子类的所有构造函数默认都会访问父类中空参数的构造函数。当父类中没有定义空参数的构造时,子类必须通过super语句或者this语句,明确指定要访问的子类中或者父类中的构造函数。                 
简单说:子类的构造函数无论如何,都必须要访问父类中的构造函数。要明确父类的初始化过程.                                  
super语句用于访问父类的初始化,而初始化动作要先完成,所以super语句必须定义在构造函数的第一行。那么就和曾经的this语句冲突了。因为this语句也要定义在构造函数的第一行。所以一个构造函数中,只能有一个要么this语句,要么super语句。而且不冲突,因为子类中至少会有一个构造函数会去访问父类中的构造函数。一样可以完成父类的初始化。这个就是子类的实例化过程。
例子5:子父类构造函数的初始化的实例演示。
[java]  view plain  copy
  1. package cn.itheima.day17;  
  2. class Fu2{  
  3.     int num;  
  4.     public Fu2(){  
  5.         num = 4;  
  6.         System.out.println("fu run");  
  7.     }  
  8.     public Fu2(int x) {  
  9.         System.out.println("fu..."+x);  
  10.     }  
  11. }  
  12. class Zi2 extends Fu2{  
  13.     public Zi2() {  
  14.         this(20);  
  15.         System.out.println("zi run..."+num);  
  16.     }  
  17.     public Zi2(int x) {  
  18.         //super(90);  
  19.         System.out.println("zi2...."+x);  
  20.     }  
  21. }  
  22. public class ExtendsDemo4 {  
  23.     public static void main(String[] args) {  
  24.         //Zi2 zi1 = new Zi2();  
  25.         Zi2 zi2 = new Zi2(2);  
  26.     }  
  27. }  
继承的弊端:打破的封装性。如果恶意继承并进行不正确的覆盖,会导致原功能的错误。
不让其他类继承可以解决这个问题。这就需要一个关键字来完成 :final(最终)
final:作为一个修饰符     1)它可以修饰类,可以修饰方法,可以修饰变量。
     2)final修饰的类是一个最终类,不可以被继承。
     3)final修饰的方法不可以被覆盖。
     4)final修饰的变量的是一个常量,只能被赋值一次。这个赋值指的是显示初始化赋值。
什么时候将变量修饰成final的呢?
通常在程序中会使用一些不会变化的数据。也就是常见的一些常量值。比如 3.14。那么这个数据直接使用是可以的,但是不利于阅读,所以一般情况下,都会被该数据起个容易阅读的名称。final double PI = 3.14; final修饰的常量定义一般都有规范书写,被final修饰的常量名称,所有字母都大写。如果由多个单词所组成,每一个单词间用“_”连接。
例子6:final关键字的应用实例。
[java]  view plain  copy
  1. package cn.itheima.day17;  
  2. /*final*/ class Fu3{  //final修饰的类不能被继承  
  3.     /*final*/ void show(){  //final修饰的方法不能被覆盖  
  4.         System.out.println("调用系统资源");  
  5.     }  
  6.     void method(){  
  7.         System.out.println("method run");  
  8.     }  
  9. }  
  10. class Zi3 extends Fu3{  
  11.     final int num; //final修饰成员变量,最终化的值应该是显示初始化值。  
  12.     public static final int aa = 45//全局常量  
  13.     public Zi3() {  
  14.         num = 8;  
  15.     }  
  16.     public Zi3(int x) {  
  17.         num = x;  
  18.     }  
  19.     void show(){  
  20.         final int MY_NUMBER = 4;  
  21.         System.out.println("haha...系统被干掉啦"+num);  
  22.     }  
  23. }  
  24. public class FinalDemo {  
  25.     public static void main(String[] args) {  
  26.         Zi3 z=new Zi3();  
  27.         z.show();  
  28.         Zi3 z1=new Zi3(55);  
  29.         z1.show();  
  30.     }  
  31. }  
  32. //饿汉式  
  33. class Single{  
  34.     private static final Single s = new Single();  
  35.     private Single(){}  
  36.     public static Single getInstance(){  
  37.         return s;  
  38.     }  
  39. }  
  40. */  
抽象类两个类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:
     预热班学生:学习。
      就业班学生:学习。
可以对这两个的学生进行共性抽取。形成一个父类:学员。 
[java]  view plain  copy
  1. package cn.itheima.day17;  
  2. /** 
  3.  * 需求:预热班学生:学习。就业班学生:学习。 
  4.  * 可以对这两个的学生进行共性抽取。形成一个父类:学员。  
  5.  * @author wl-pc 
  6.  */  
  7. abstract class XueYuan{  
  8.     abstract void study();  
  9. }  
  10. class YuRenBanXueYuan extends XueYuan{  
  11.     @Override  
  12.     void study() {  
  13.         System.out.println("JAVA SE");  
  14.     }  
  15. }  
  16. class JiuYeBanXueYuan extends XueYuan{  
  17.     @Override  
  18.     void study() {  
  19.         System.out.println("JAVA EE");  
  20.     }  
  21. }  
  22. public class AbstractDemo {  
  23.     public static void main(String[] args) {  
  24.         YuRenBanXueYuan xueyuan1 = new YuRenBanXueYuan();  
  25.         xueyuan1.study();  
  26.         JiuYeBanXueYuan xueyuan2 = new JiuYeBanXueYuan();  
  27.         xueyuan2.study();  
  28.     }  
  29. }  
例子8:公司中程序员有姓名,工号,薪水,工作内容。项目经理除了有姓名,工号,薪水,还有奖金,工作内容。对给出需求进行数据建模。

 
 
[java] view plain copy
  1. package cn.itheima.day17;  
  2. /* 
  3. 需求:公司中程序员有姓名,工号,薪水,工作内容。 
  4. 项目经理除了有姓名,工号,薪水,还有奖金,工作内容。 
  5. 对给出需求进行数据建模。 
  6. 分析: 
  7. 这里有两个具体事物。 
  8. 1,程序员 
  9.     属性: name id pay  
  10.     行为: work() 
  11. 2,项目经理。 
  12.     属性: name id pay  bonus 
  13.     行为:work() 
  14. 发现这两个事物具备共性内容。为了提高代码的复用性。 
  15. 两个事物间是不具备继承关系的。因为两个事物不存在谁是谁中一种。 
  16. 但是,可以确定是无论程序员,还是经理,他们都是公司员工。 
  17. 他们都是员工的一种。而且员工都具备一些基本属性和行为。 
  18. 这时就可以将两个事物向上抽取,出一个员工类。该类中定义就是两个事物共性的内容。 
  19. */  
  20. abstract class Employee{  
  21.     private String name;  
  22.     private String id;  
  23.     private double pay;  
  24.     public Employee(String name, String id, double pay) {  
  25.         super();  
  26.         this.name = name;  
  27.         this.id = id;  
  28.         this.pay = pay;  
  29.     }  
  30.     public abstract void work();  
  31. }  
  32. class Manager extends Employee{  
  33.     private double bonus;  
  34.     public Manager(String name, String id, double pay, double bonus) {  
  35.         super(name, id, pay);  
  36.         this.bonus = bonus;  
  37.     }  
  38.     @Override  
  39.     public void work() {  
  40.         System.out.println("manager");  
  41.     }  
  42. }  
  43. class Programmer extends Employee{  
  44.     public Programmer(String name, String id, double pay) {  
  45.         super(name,id,pay);  
  46.     }  
  47.     @Override  
  48.     public void work() {  
  49.         System.out.println("code");  
  50.     }  
  51. }  
  52. public class AbstractDemo2 {  
  53.     public static void main(String[] args) {  
  54.         Manager manager =new Manager("zhangsan""12"5000.003000.00);  
  55.         manager.work();  
  56.         Programmer programmer =new Programmer("lisi""03"7000.00);  
  57.         programmer.work();  
  58.     }  
  59. }  
抽象类的一些细节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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值