您的位置:首页 > 编程语言 > Java开发

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

2018-10-15 17:05 375 查看

          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 修饰的.


  


  


 


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐