• Java开发知识之Java的继承多态跟接口*


              Java开发知识之Java的继承多态跟接口

    一丶继承

      1.继承的写法

      在Java中继承的 关键字是 extends 代表一个类继承另一个类.

    继承的含义以及作用: 继承就是基于某个父类的扩展.制定出来的一个新的子类.子类可以继承父类原有的属性跟方法. 也可以自己增加特有的属性跟方法.

    代码例如下:

    public class Anmail {
        public void eat() {
            System.out.println("父类吃");
        }
    
    }
    父类代码

    子类代码:

    public class Dog extends Anmail {
        public void eat() {
            System.out.println("子类吃");
        }
    }

    通过上面代码.我们可以看到.子类 Dog类. 继承了父类. 使用了关键字 extends

    并且子类重写了父类的方法.

      2.子类访问父类的方法

      上面说了子类继承父类.那么子类也可以调用父类的方法. 我们学过this关键字. this可以区分局部变量跟成员变量.也可以在构造中调用其它构造函数.

    那么我们还提供了一个关键字. super() super关键字可以访问父类.

    代码如下:

    父类代码一样.之帖子类代码.

    public class Dog extends Anmail {
        public  Dog() {
            super(); //调用父类构造 调用构造的时候必须放在最上面.
            super.eat();//调用父类方法.
        }
        public void eat() {
            System.out.println("子类吃");
        }
    }

    创建子类对象.并且输出.

    可以看到.在调用构造的时候.他会先访问父类的构造.但因为父类构造我们并没有输出内容.所以没有输出内容,子类继续调用父类的eat方法. eat方法我们输出了.

    就是父类吃. 所以在子类构造的时候.会调用父类构造以及父类的方法.

      super()的关键字用法限制. super关键字只能调用父类中 公共权限(public) 以及保护全选(protected)的方法

       3.重写的概念

      子类可以重写父类的方法. 什么是重写.就是子类跟父类的方法是一模一样的. 也就是说,重写是在子类跟父类中才会出现的. 返回值一样. 方法名一样. 参数一样.

    在J2SE 5.0 以上.支持了新的功能.也就是说返回值可以不一样.但是 方法名 跟 参数必须一样.

      JAVA 类编译的流程. java中.创建子类的时候.会自动调用父类的构造方法进行初始化. 我们可以做个例子. 并且重写一个方法.

    public class Anmail {
        public Anmail() {
            System.out.println("父类构造方法");
        }
        public void eat() {
            System.out.println("父类吃");
        }
    
    }

    子类.

    public class Dog extends Anmail {
        public  Dog() {
            
        }
        public void eat() {                //重写父类方法
            System.out.println("子类吃");
        }
    }

    创建对象.

    通过实例可以终结出. 1. 子类重写了父类方法.输出的内容是自己的"子类吃" 2.在给子类实例化的时候.会自动调用父类进行实例化操作.也就是说父类也会被初始化.

    PS: 子类实例化的时候.会调用父类的无参构造进行实例化父类.但是并不会自动调用父类的有参构造.这个我们需要使用super关键字才可以.

    二丶Object 类

      object类是一个比较特殊类的. 位于java.lang.包中. 它是所有类的父类.比如我们以前学习过字符串类. String类. String类中 我们比较两个对象是否相等就是用.

    equleas()方法. 这个就是object类中的.只不过字符串进行了重写. 我们自定义的类也是继承自object类.只不过是默认继承. 所以任何类都可以重写父类object中的方法.

    在object类中 加了final类的方法是不能被重写的. 例如 getClass() notify() notifyAll() wait()等等.

    object类的方法介绍.

      1.getClass()方法

        getClass()方法会返回指定是的Class实例. 然后可以使用此时调用getName()获得这个类的名称. 

        getClass().getName(); 也可以配合toString()方法使用.

      2.toString()方法

        toString()方法就是返回一串字符串.在object类中,就是讲一个对象返回为字符串形式.实际应用中就是重写这个字符串.返回什么是你自定的.

      3.equals()方法;

        equals()方法就是比较.当时说过区别. 就是 == 与 equals()的区别. == 是比较地址. equals()是比较你自定的内容.也就是对象的实际内容.通常也是重写.

    比较什么你自己定. 如果你写了一个类有一个成员变量是 a; 我们重写equals() 就判断 a 跟 比较对象的a即可. 就是 a 跟 a比较.

    三丶对象类型转换.

        对象类型转换.包括向上转型.以及向下转型. 通俗理解就是 强转对应的数据类型.  但是你在强转的时候要判断一下是否是这个数据类型.这个就是转型.

    向上转型以及向下转型就是说 类我们强转为父类.  也可以父类强转为子类.

    1.向上转型

       子类对象赋值给父类对象称为向上转型.  Anmail a = new Dog(): 这个就是向上转型.

    比如我们有动物对象.  跟 狗对象.  狗对象可以看做是动物对象的一个子类. 

    还比如 四边形类 跟 平行四边形类.  平行四边形 对象可以看做是 四边形类的一个对象.

    如下图:

      

    常规的继承图都是父类在上. 子类在下.例如上图.  所以我们将子类看做是父类对象的时候成为向上转型.  也就是平行四边形对象看做是四边形类的对象的时候.

    向上转型是具体的类像抽象的类进行的转换.所以它总是安全的. 我们可以说平行四边形是特殊的四边形. 但是不能是四边形是平行四变形.

    因为代码写法:  四边形 a = new 平行四变形(); 所以很多人就会说 a就是平行四边形. 其实是错的. a是四边形. 我们只能说a平行四边形是一个特殊的四边形.

      如果在C++ 中.内存分配就是 父类占一小块内存. 子类上半部分是父类内存.下半部分是子类特有的成员变量开辟的内存. 子类转为父类. 就是不要子类下边的内存了.

    所以总是安全的. 我只要上面的哪块内存.也就是父类的内存.

    所以在上边. 子类转为父类. 父类调用方法的时候.并不会调用到子类特有成员变量.

    2.向下转型

      向下转型就是 抽象的类转为具体的类. 比如 动物是鸟.  动物是抽象的.不能说他是鸟.所以不和逻辑.而且会出现问题.

    比如父类 Anmail a = new Dog();

        Dog b = a;  这样是错误的. 我们不能这样赋值.原因就是不能说 动物是狗.

        Dog c = (Dog)a; 这样可以.强转为子类型.写法是正确的.

    站在C++的角度:

      为什么上面向下转型是错误的. 原因是 a 会有一块内存. 我们可以假定为0x20个字节大小. b是Dog也就是子类对象.他继承了父类.有自己特有的成员方法

    以及成员变量. 所以它的头0x20个字节是父类的内存.下面多出的内存是自己了.假设是0x30个字节. 所以我们子类转为父类(向上转型)

    其实就是把30个字节的内存转为20个字节的内存.所以不会出问题. 但是 0x20个字节.也就是父类转为子类. 就会出为题. 意思就是说 0x20个字节转为0x30个字节.

    首先我们并不知道是转为0x30个字节.这样内存访问就会出错了.但是如果我们强转了.相当于就是父类在强转为子类的时候.按照子类的内存强转.这样就不会有问题了.

    也就是上面的我们的 Dog c  c的头0x20个字节是父类. 下面的0x10个字节就是自己特有的所以不会出错.

    3.使用关键字判断对象类型

        上面我们的父类转为子类的时候.必须加上子类的数据类型才可以强转. 原因就是转为子类的时候.内存会按照子类的大小进行扩大.这样就不会出现问题了.

    但是我们怎么知道 我们的子类.是否是这个父类的子类. 所以有了运算符  instanceof()来判断.

    就是判断 子类是否是父类的. 父类中有没有这个子类.如果有就进行转换.

    语法:

      Myobject instance ExampleClass

    1.Myobject 就是某个类的对象引用. 可以理解为是父类填写的是父类引用.

    2.ExampleClass 某各类.                 可以理解为子类. 填写类名.

    例如:

      Anmail a = new Anmail();
    
    if (a instanceof Dog)
    
    {
    
        判断父类中是否有子类Dog,如果有我就进行转换.
    
        Dog d = (Dog) a;
        d.xxxx;
    }

    四丶方法重载.

      1.重载介绍.

      重载的含义. 重载就是可以有多个相同函数.重载的参数确定是否重载.

    我们写过有参构造跟无参构造. 方法名是一样.不一样的就是参数列表不同.

      重载的构成:

        1.重载的构成是方法名字一样,

        2. 参数列表个数不同 

        3.  参数类型不同 

        4.参数列表顺序不同

    例如:

      public void eat(); 

      public void eat(String,int); 重载eat,参数是String,int

      public void eat(int,String); 参数顺序不同.构成重载.

      public int eat(int,int);   返回值是int. 参数列表是两个int值. 返回值也可以为void不影响重载.

    PS: 方法的返回值并不会影响重载.真正影响的是参数列表. 你有两个成员方法.方法命一样.参数列表一样.类型一样.返回值不同.不能构成重载.

    特殊的重载:

      Java中可以定义不定长的参数类表.

    语法如下:

        返回值 方法名(参数数据类型 . . . 参数名称) 主要是三个...

    其实不定长参数.就是一个一维数组.我们可以当做数组去操作参数. 

    代码如下:

      

        public void eat(int ... a) {
            for (int i = 0; i < a.length;i++)
            {
                .......
            }
        }

    5.多态的概念

      在上面我们学过向上转型.就是子类对象可以当做父类对象去使用. 其意思就是我可以当做父类对象去使用. 那么就可以使用子类跟父类的共有的方法跟属性了.

    因为我们站在内存的角度上也说了.我们用的都是父类的内存.所以我们可以调用父类的方法.跟成员去使用. 但是多态是什么意思.多态就是调用父类的方法的时候.

    因为子类重写了父类的方法.所以调用时会调用子类的特有方法.

    例如:

      Anmail a = new Dog();

      a.eat()

    输出结果:  子类吃.  按理说应该输出父类吃.不是说向上转型了.我们用的都是父类内存了.子类就不该会被调用. 愿意你是这样了.如果站在C++角度来说.

    我们首先会new一个子类的对象实例. 而new子类对象的时候.会先初始化父类. 在初始化自己. 为什么说一下流程.原因是父类有虚表.也就是有一个表.保存着

    自己的方法.而子类在实例化的时候.父类的虚表先初始化. 初始化完了之后.子类的再初始化.它会先把父类的虚表拷贝过来.然后覆盖他. 这样我们a调用方法的时候

    其实调用的是子类方法.原因就是子类的虚表已经覆盖了父类虚表.

    Java中的原理. java中其实也是一样的.只不过给你隐藏了这个步骤了.不用理解的这么复杂.我们只要知道.向上转型之后.调用子类跟父类共有的方法.就能实现多态.

    注意: 子类重写了父类方法.那么调用的时候才是子类的方法.原因是子类重写了.才会覆盖父类.

    代码例子:

     多态的用法: 多态的好处就是程序员不同定义相同的方法了.避免了相同的大量重复代码的开发.只要实例化一个子类对象.维护这个方法即可.

    再举个例子;

    我们手机. 有一代手机 二代手机 三代手机 四代手机 ...n代手机.

    1代手机 只支持打电话

    2代手机 可以发信息了.

    3.手机   可以上网了

    4.手机  可以拍照了.

    此时我们只需要二代手机继承1代手机对1代手机扩展功能. 比如增加发信息的方法. 打电话的方法进行重写.可以打电话.也可以录音了. 而一代手机根本不用动方法.

    此时我们的二代手机就可以出手了.第三代手机同样继承第二代手机.增加扩展功能.重写维护的方法. 而不用修改二代的代码.

    这样不管我们有第几代手机.只需要继承上一代的类.进行扩展.以及维护即可.

    6.抽象类

      抽象类就是说一个不可以被存在的类. 比如我们有动物 跟 狗. 而动物是不能被实例化对象的. 狗是一个具体的生物.我们可以实例化.

    所以抽象就是指 动物. 也就是说我不能让你被实例化.原因就是 动物泛指万千.不能具体为一个动物.

    定义抽象类的关键字

    abstract

       使用abstract 修饰的类称为抽象类.是不能实例化的.

      使用 abstract 修饰的方法.称为抽象方法.是不能实现的.比如子类重写. 修饰的方法没有方法体.

     反过来说.如果一个类中修饰了成员方法.那么就必须定义这个类为抽象类.

    抽象类跟普通类一样.只不过就是不能实例化. 必须要有子类继承.如果有抽象方法.子类必须重写. 

    抽象类的继承图:

    代码写法,需要将我们的Anmail类写成抽象类. 并且方法改为抽象方法

    //public abstract class Anmail
    abstract public   class  Anmail {
    
        abstract public void eat() ;
        public abstract void  play();
    }

    abstract 卸载权限修饰符的前边或者后边都可以.不影响.例如类上面加了一行注释.我们也可以写成上面的写法.以及抽象方法.也是两个不同顺序来举例子

    public class Dog extends Anmail {
    
        @Override
        public void eat() {
            // TODO 自动生成的方法存根
            System.out.println("狗在吃东西");
        }
    
        @Override
        public void play() {
            // TODO 自动生成的方法存根
            System.out.println("狗在玩耍");
        }
    
    }

    使用的时候.可以使用向上转型.使用多态的方式.  子类可以自己new自己

    在C++中.也有抽象类的概念.只不过称之为纯虚类. 他的方式就是这个类中的方法定义为纯虚方法(抽象方法)

    void eat() = 0; 后面加上 = 0; 跟接口很类似. 一般也是接口

    七丶接口的含义

       接口就是抽象类的延伸.上面我们定义了一个抽象类.如果这个抽象类是一个父类.有很多子类.但是我们可以这样想一下. 如果子类很多.都要实现这个抽象类中的方法.

    这样就造成了代码冗余. 我们有的类完全可以不实现抽象类中的抽象方法啊.

    比如  play方法. 我们每个子类都要实现.但是有的动物就不会玩我完全可以不实现了.但是按照抽象类.我们必须实现.所以就代码冗余了.

    此时接口就出现了. 接口就是说 . 我接口中的方法都是抽象方法. 你要实现play. 就可以实现我这个接口. 如果不需要玩的话.就不用实现我这个接口

    例如下图:

     可以看到我们的N边型类.并没有实现接口.我完全可以不用实现Draw方法.虽然我继承了父类.

    接口的定义与使用:

      1.接口使用intface关键字 修饰. 可以使用权限修饰符修饰

      2.接口的实现使用关键字 implements关键字

      3.接口中的方法都是抽象方法.而且都没有方法体. 且默认加的关键字就是abstract 而且权限必须是public因为要被实现.其它权限修饰则不被编译器认可.

      4.在继承一个类同时.可以实现接口.

      5.在接口中定义的 字段(成员变量)都是默认修饰符 static + final修饰的. 也就是说一个静态常量.

    代码如下:

      定义一个接口

    public interface IAnmail {
        
        public abstract void Play(); //定义一个抽象方法
    }

    接口的实现.

      

    public class Dog extends Anmail implements IAnmail{
    
        @Override
        public void eat() {
            // TODO 自动生成的方法存根
            System.out.println("狗在吃东西");
        }
    
        @Override
        public void Play() {            //实现了接口必须实现他的方法
            // TODO 自动生成的方法存根
            System.out.println("狗在玩耍");
        }
    
    
    }

    八丶总结

      1.类的继承

        类的继承使用 extends关键字

        注意的问题:

          1.不能多继承.

          2.子类继承父类自动用于父类的方法以及成员变量

          3.子类在构造中可以调用父类构造.(无参或者有参) 使用的是 super();关键字. 也可以调用父类方法 super.xxxx

      2.公共父类Object

        在Java中每个类都继承了父类Object. Object中提供了常用的方法

      比较:  equeals();

      转为字符串表现形式 toString();

      这些方法都是可以被重写的.除了加了 final修饰的.

      3.对象的类型转换.

       在Java中有向上转型跟向下转型

        1.向上转型:  子类转为父类.  Anmail d = new Dog(): 安全的.因为在内存角度来说.使用的部分只有父类部分内存.

        2.向下转型: 父类转为子类  Dog c = (Dog) d; 必须加上类型.父类内存转成子类内存,比如给指定子类内存大小.

        3.对象类型判断 使用 instanceof 

        语法  父类引用 instance 子类类名  判断父类是否有这个子类

        如果是我们就可以进行转换了.

      4.类中的方法重写与重载

        重写: 

          1.返回值相同

          2.方法名相同  

          3.参数列表相同

        满足以上三点才能是重写.也就是子类跟父类一模一样才是重写.

        重载:

           1.方法名一样

            2.参数列表个数不同

           3.参数列表类型不同

            4.参数列表顺序不同.

        满足以上四点才能构成重载.

        特殊重载可以使用 三个点定义为可变参数  public void eat(int ...a); 此时a就是一个数组.我们可以遍历他来获取参数.

      5.多态

       多态就是父类可以调用子类跟父类共有的方法.比如是子类重写父类才会有不同的结果.

      使用向上转型

        Anmai a = new Dog(); a.eat() ;  狗在吃东西.

      内存角度来说. 就是虚表覆盖.

      6.抽象类

        抽象类使用 abstract关键字进行修饰

        public abstract class 类名{}'

        1.修饰类就是抽象类,不可以被实例化.就是不能创建对象

        2.修饰方法就是抽象方法,没有方法体.子类继承比如重写.

        3.一个类中有抽象方法.则这个类必须定义为抽象类.

        

      7.接口

        接口定义的关键字  Intface    public intfact 接口名{}

        实现接口的关键字 implements   public class Anmail Implements 接口名

      1.接口中的方法都是抽象方法. 且默认修饰符为 public abstract 权限必须是public 否则编译器不认可.

      2.接口中的成员变量都是默认的 static final 修饰的.

      

      

  • 相关阅读:
    [数据结构与算法]Note
    [C++]构造函数那些事
    [C++]constexpr函数
    [计算机系统]字/字节/位
    [C++]返回数组指针
    [C++]Top-level const/Low-level const
    [信号]预加重与去加重--转
    Smart solution of decode String
    ACE学习:
    模糊匹配算法
  • 原文地址:https://www.cnblogs.com/iBinary/p/9792055.html
Copyright © 2020-2023  润新知