当前位置:   article > 正文

Java中的抽象类和接口

java中的抽象类和接口

1.抽象类

   1.1 抽象类得概念 

   1.2 抽象类的定义和相关语法

   1.3  抽象类和抽象方法的特性

       1.3.1 抽象类第一个特征就是抽象类不能被实例化

       1.3.2 抽象方法的修饰词不能为private

       1.3.3 抽象方法不能被final和static修饰

        1.3.4 如果抽象方法继承了抽象类就不需要实现父类的抽象方法

   1.4抽象类和抽象方法的注意事项

2.接口

  2.1接口的定义和使用

  2.2接口里面的特征

  2.3接口的骚玩法

  2.4接口和继承

  2.5使用接口时要注意的事项和接口的总结

3.浅拷贝和深拷贝

1.抽象类

   1.1抽象类的概念

   在我们面向对象得时候,有事会发现当我们需要去完成一个多态语法得时候,我们的子类都需要对父类的对应的方法进行重写,这么一趟流程走下来就会感觉我们在刚开始定义父类里面的方法好像并没有没实现,只是为多态充当了一个跳板的作用,但不去实现父类的方法里面的代码的话编译器就会报错,那我们有没有一种写法可以去完成前面的操作呢?

  这个时候我们就要去认识一个新的语法----抽象类了,抽象类里面的语法是不需要在自己里面实现的,而当子类继承抽象类的话一定要去重写抽象类里面的方法。在此之前先让我们看看抽象类长啥样的

  1. abstract class Shape {
  2. public abstract void draw ();
  3. }

   1.2 抽象类的定义和相关语法

    观察上图可以知道定义抽象类和定义抽象类里面的抽象方法都需要使用到我们的关键字abstract,这里有个需要注意的点就是抽象类里面不一定有抽象方法,但抽象方法一定在抽象类里面(瓜田里面不一定有瓜,但瓜一定长在瓜田里)

  抽象类其实与普通的类没啥子区别,就是抽象类里面的抽象方法不需要被实现,这里就别误会了一个点就是定义在抽象类里面的方法就一定是抽象方法的,抽象方法需要我们自己去加abstract关键字的。

   抽象类里面还是可以去定义普通方法的

  那我们就那抽象的父类去实现一个多态打印图形的代码

  1. abstract class Shape {
  2. public abstract void draw ();
  3. }
  4. class Cycle extends Shape {
  5. @Override
  6. public void draw() {
  7. System.out.println("⚪");
  8. }
  9. }
  10. class Rect extends Shape {
  11. @Override
  12. public void draw() {
  13. System.out.println("矩形");
  14. }
  15. }
  16. public class Test {
  17. public static void Print(Shape shape){
  18. shape.draw();
  19. }
  20. public static void main(String[] args) {
  21. Print(new Cycle());
  22. Print(new Rect());
  23. }
  24. }

这个就是使用抽象类实现的多态 

1.3  抽象类和抽象方法的特性

    1.3.1 抽象类第一个特征就是抽象类不能被实例化

    拿鼠标移到报错的地方就可以看到报错的原因:Shape被abstract修饰---就是抽象类无法被实例化的意思。

   1.3.2 抽象方法的修饰词不能为private

      当我们试着去想一下下就可以明白这个道理,我把一个方法定义成抽象类的目的就是这个方法都本身要在我的子类里面需要重写,父类里面不想这么麻烦去实现才把这个方法去给定义成抽象方法的,而你却把这个方法的访问权限给限制成private----只能在本身的类里面去使用,并提供给外部使用,这不就离谱起来了,所以抽象方法的修饰词(abstract)与访问权限修饰词private是不能放在一起使用的

   1.3.3 抽象方法不能被final和static修饰

   由于我们的抽象方法是要随时被重写的,并且是属于类和非静态的,所以当我们的抽象方法被final和static修饰的时候会嘎嘎报错

 

   1.3.4 如果抽象方法继承了抽象类就不需要实现父类的抽象方法

   这个就有点点好玩的了,我们前面说过如果一个子类继承了我们的抽象方法就必须在我们的子类里面去重写父类的抽象方法,当有意思的就来了 当我们的子类B也是抽象方法的时候就不用与重写父类中的抽象方法了, 但是但是有句话说的好 “出来混迟早是要还的”-----当我们在拿一个子类去继承C前面那个子类B的话,那么这个子类C既要重写父类的抽象方法也要与重写子类B的抽象方法

  1. abstract class Shape {
  2. public abstract void draw ();
  3. }
  4. abstract class A extends Shape{
  5. //如果子类也是抽象类 就不要重写父类里面的方法
  6. public abstract void print();
  7. }
  8. class C extends A {
  9. @Override
  10. public void draw() {
  11. //那个子类的子类就要帮它们两个抽象里面的抽象方法都重写
  12. }
  13. @Override
  14. public void print() {
  15. //重写子类A的抽象方法
  16. }
  17. }

1.4 抽象类和抽象方法的注意事项

 1.抽象类和抽象方法都是使用abstract进行修饰的
 2.抽象类不能进行实例化,但是普通类是可以的!!
 3.抽象类当中 不一定包含抽象方法,但是包含抽象方法的类,一定是抽象类.
 4.抽象类当中 可以定义成员变量和成员方法! !
 5.当一个普通类 继承我们的 抽象类了,此时在普通类当中一定要重写抽象类中的抽象方法!!!
 6.抽象类存在的最大的意义 就是 为了被继承!!
 7.当一个抽象类A继承一个抽象类B,此时抽象类A不需要重写抽象类B中的成员,但是当一个普通类C,
   继承了抽象类A,此时就需要重写所有没有被重写的抽象方法! !!!
 8.一定要满足重写的要求! ! !
 9.final关键字 不可能同时作用在一个方法或者类上! !
 10.抽象类当中可以存在构造方法,在子类实例化的时候,会帮助父类的成员进行初始化!!

2.接口

  2.1接口的定义和使用

  其实接口的定义跟类的定义差不多接口本质上来说也是一个类,接口就相当于一个公共规范,定义接口的关键字为interface,类和接口之间 使用关键字 implements来进行关联,先让我们用一段代码看一下接口的定义和类和接口的关联

 那我们是浅浅的使用一下下接口写一段代码

    

  1. public interface USB {
  2. void openDevice();
  3. void closeDevice();
  4. }
  5. public class Keyboard implements USB{
  6. @Override
  7. public void openDevice() {
  8. System.out.println("打开键盘");
  9. }
  10. @Override
  11. public void closeDevice() {
  12. System.out.println("关闭键盘");
  13. }
  14. public void TapKeyboard() {
  15. System.out.println("点击键盘");
  16. }
  17. }
  18. public class Mouse implements USB{
  19. @Override
  20. public void openDevice() {
  21. System.out.println("打开鼠标");
  22. }
  23. @Override
  24. public void closeDevice() {
  25. System.out.println("关闭鼠标");
  26. }
  27. public void TepMouse() {
  28. System.out.println("点击鼠标");
  29. }
  30. }
  31. public class Main {
  32. private static void print(USB usb) {
  33. usb.openDevice();
  34. usb.closeDevice();
  35. if (usb instanceof Mouse) {
  36. Mouse mouse=(Mouse) usb;
  37. mouse.TepMouse();
  38. } else if(usb instanceof Keyboard) {
  39. Keyboard keyboard=(Keyboard) usb;
  40. keyboard.TapKeyboard();
  41. }
  42. }
  43. public static void main(String[] args) {
  44. print(new Mouse());
  45. System.out.println("===========");
  46. print(new Keyboard());
  47. }
  48. }

在接口里面也是可以完多态的哦

 2.2接口里面的特征

   2.2.1  在接口里面不能去实现非静态的方法

   在图上我们看的出来,报错的原因为abstract方法不能有主体,这就不得不说一下下接口的第二个特性了

   2.2.2在接口当中定义的方法都是默认给public abstract修饰的,也就说明了当有子类关联了一个接口,就要去重写接口里面的方法(有例外的)

  2.2.3  由特征1我们就可以知道在接口中静态的方法是可以给实现的

   2.2.4剩下的特征由于篇幅原因 我在这就这就把整体的图直接放出来了

2.3接口的骚玩法

    接口可以去实现 接口去关联接口的操作的,其目的就是为了达到扩展功能性的效果

   这里的关键在不再是implements,而是变成了关键字extends,在这里有人可能会觉得我们我不一开始直接创建好一个接口去实现里面这些功能而是要把它们分散开来在去接在一起。

    这个原理就跟拼积木差不多,我没有一个积木是可以实现任意一种形态的把,但是我用那些分散的小小的积木零件可以去实现任何形态,还有就是跟生产汽车的零件一样,生产汽车都是把各个地方的零件运往一个地方进行组合的----------主打的就是一手 高内聚低耦合

2.4接口和继承

   当我们看完上面的知识点以后我们可能会感觉接口好像和继承没什么关系,但还记得继承在Java中是不支持多继承的知识点不,多继承只是你继承的语法不行,在Java中是支持一个类可以去关联多接口的,如果是这样子的话我们就可以对某一对象进行更为完善的描述了

   我们可以把动物的共性定义为抽象类,然后把动物的特性定义成接口,最后通过定义的抽象类和接口去描述一个对象    --------  上代码

  1. //一个类可以实现接收多接口可以解决多继承的问题 Java里面不能实现多继承
  2. interface IFlying {
  3. //飞的动作
  4. void fly();
  5. }
  6. interface ISwimming {
  7. void swim();//游泳
  8. }
  9. interface IRunning {
  10. void run();//跑
  11. }
  12. abstract class Animal {
  13. private String name;
  14. private int age;
  15. Animal(String name ,int age) {
  16. this.age=age;
  17. this.name=name;
  18. }
  19. public abstract void eat();
  20. public String getName() {
  21. return name;
  22. }
  23. public int getAge() {
  24. return age;
  25. }
  26. }
  27. //先继承类在实现接口
  28. //不是所有的动物都能满足上面3个接口的特征 所以不能都放在Ainmal 再加上 Java只能单继承
  29. //这里接口解决了多继承的问题
  30. class Dog extends Animal implements IRunning,ISwimming{
  31. Dog(String name,int age) {
  32. super(name, age);
  33. }
  34. @Override
  35. public void swim() {
  36. System.out.println(this.getName()+"在狗刨");
  37. }
  38. @Override
  39. public void run() {
  40. System.out.println(this.getName()+"在快快跑");
  41. }
  42. @Override
  43. public void eat() {
  44. System.out.println(this.getName()+"在吃狗粮");
  45. }
  46. }
  47. class Bird extends Animal implements IFlying {
  48. Bird(String name,int age) {
  49. super(name,age);
  50. }
  51. @Override
  52. public void eat() {
  53. System.out.println(this.getName()+"在吃虫子");
  54. }
  55. @Override
  56. public void fly() {
  57. System.out.println(this.getName()+"在打飞机");
  58. }
  59. }
  60. public class Test {
  61. //分类多态 在智能化一点
  62. private static void testeat(Animal animal) {
  63. animal.eat();
  64. }
  65. public static void testFly(IFlying iFlying) {
  66. iFlying.fly();
  67. }
  68. public static void testswim(ISwimming iSwimming) {
  69. iSwimming.swim();
  70. }
  71. public static void testrun(IRunning iRunning) {
  72. iRunning.run();
  73. }
  74. public static void main(String[] args) {
  75. testFly(new Bird("小鸟",1));
  76. testrun(new Dog("旺财",6));
  77. }
  78. }

 //在这里就有一点点要注意的就是子类继承和关联接口的顺序了,一定是先继承在关联(这两个的关系不能乱)

  由上面的代码就可以很好的体现继承和接口结合在一起以后代码的简洁性和便利性,我后面如果还要构建一个新的类,由上面的接口和抽象类直接在类里面重写就行了

2.5使用接口时要注意的事项和接口的总结

  1.接口是使用interface来进行定义的

  2.不管是接口还是抽象类,他们仍然是可以发生向上转型

  3.接口当中 不能有实现的方法,但是有2种类型的方法需要注意:

        3.1 静态方法可以有具体的实现

        3.2 这个方法被default关键字修饰,也是可以的[1.8开始才引入的这个特性]

  4.接口当中的方法默认是public abstract 修饰的 

  5.接口当中的成员变量默认是: public static final 修饰的

  6.接口也不能通过关键字new 来进行实例化

  7.类和接口之间 使用关键字 implements来进行 关联

  8.当一个类实现一个接口之后,这个类必须重写这个接口当中的抽象方法!!

  9. 当接口当中,存在default方法,可以选择重写,也可以不重写,具体看业务需求! !!!

  10.不管是接口还是抽象类,他们仍然是可以发生向上转型

  11.子类在实现接口里面的抽象方法的时候 要用pubilc

  12.接口里面不能有构造方法和代码块(纯粹)

  13.一个类不想实现接口里面的重现方法 ,那么这个类可以定义为抽象类

3.浅拷贝和深拷贝

     在开始这个知识点前我们先介绍一下下Object类,我们在继承和多态的知识点里面说过一个类只能继承一个父类,但是每一个类里面都会继承Object类,Object类是所有类的父类,明白这个就欧克克了。

   浅拷贝

  在拷贝这里我们需要使用到的方法为clone方法,先上代码

   在这里会出现第一个错误就是类型的问题,由于clone的返回值是Object,所有在赋值的时候要进行一次向下转型

    然后它由报错了

这次的原因要去看一下下clone在库里面是怎么给定义的了 

    会发现clone在定义的时候后面多了一窜这样的英文  throws CloneNotSupportedException

 百度翻译的意思是抛出克隆不支持异常

这个问题还挺好解决一下的就直接在main方法后面加上这句话就欧克克了

     在main加入throws CloneNotSupportedException后它又报错了,这次的原因就是修饰clone方法的访问权限修饰词protected的功劳了,clone方法对外访问的权限只能在不同包的子类中进行访问,所以要在子类中重写一下clone这个方法

 

     在这里会报错最后一个错误就是该类不支持克隆的错误,这个时候千万不要急,这个时候只要让我们的子类去接上一个接口就行了

     这个接口是一个标记接口,只要我们的子类接上了这个接口就会被标记可以被克隆,就有了使用clone的权限

 它这里是可以正常跑的只是我没什么东西给它罢了(人话摆了)

那我们就先对拷贝进行一个小总结

1.由于clone方法是给访问权限修饰符protected修饰的,它是要在不同包中的子类才能访问-----》要在我们要克隆的子类里面重写一下clone方法
2.而且要在main方法这里加一行 throws CloneNotSupportedException 这个就欧克克了 ---  解决异常点
当进行方法重写后又发现它还在报错  ---- 牵扯到了向下转型问题
3.是因为返回值为Object类型,所有要发生一下向下转型
4.当发生向下转型的时候运行的时候又会发现编译器报错说不能被克隆  ----  不支持克隆
5.这里就要接上一个接口Cloneable   这个为标记接口(如果实现了这个接口的话,就说明这个类是可以被克隆的)

深拷贝

  在讲深拷贝前我先在这抛出一个问题,当我们要拷贝的子类继承了一个父类后,然后实现拷贝的代码不变,那我在变动被拷贝元素父类的成员变量新拷贝出来的对象的父类的成员变量也会变吗?

  那就让我们康康代码

 

 显然它们的关系是这种

   它并没有把父类的元素额外拷贝一份而是直接把它们指向了同一块地址

//这里会发现student2的money一起变值了  所以这个拷贝并没有把Money这个成员变量拷贝一份
//把student1所指向的对象克隆了一份然后给student2 然而money从本质来说是 在student1外面在实例化了一个类
//本质来说不属于student1的东西,所以在进行拷贝的时候
//该拷贝只会把student2的monky指向student1的money所指向的对象,而不是拷贝一份出来
//这里就是浅拷贝了  -----  没有拷贝完全
//那我们就去实现一下深拷贝  每一个对象都要进行拷贝

//我们就把这种不完全的拷贝称为浅拷贝 ,与之相对立的称为深拷贝

    在这种情况实现深拷贝就需要在我的父类中也重写clone方法,并且在子类实现的时候还实现完全

 

这个时候就完成了对数据的深拷贝!!!

     

       那么那么,这次的知识分享就到这里就结束了,我在下次尽量把equals和hashCode一起写了,还有就是欢迎大佬指出错误!!!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/180509?site
推荐阅读
相关标签
  

闽ICP备14008679号