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

时间:2021-07-16 22:26:00

          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("子类吃");
}
}

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

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

可以看到.在调用构造的时候.他会先访问父类的构造.但因为父类构造我们并没有输出内容.所以没有输出内容,子类继续调用父类的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("子类吃");
}
}

创建对象.

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

通过实例可以终结出. 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(): 这个就是向上转型.

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

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

如下图:

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

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

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

因为代码写法:  四边形 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中其实也是一样的.只不过给你隐藏了这个步骤了.不用理解的这么复杂.我们只要知道.向上转型之后.调用子类跟父类共有的方法.就能实现多态.

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

代码例子:

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

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

再举个例子;

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

1代手机 只支持打电话

2代手机 可以发信息了.

3.手机   可以上网了

4.手机  可以拍照了.

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

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

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

6.抽象类

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

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

定义抽象类的关键字

abstract

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

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

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

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

抽象类的继承图:

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

代码写法,需要将我们的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自己

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

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

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

七丶接口的含义

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

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

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

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

例如下图:

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

可以看到我们的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("狗在玩耍");
} }

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

八丶总结

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