[关闭]
@liayun 2016-05-25T13:34:54.000000Z 字数 7325 阅读 1306

面向对象——多态

java基础


多态概述

多态定义

多态:可以理解为事物存在的多种体现形态。
例如:

人:男人,女人
动物:猫,狗

猫这个对象对应的类型是猫类型:猫 x = new 猫();,同时猫也是动物中的一种,也可以把猫称为动物:动物 x = new 猫();。动物是猫和狗等具体事物中抽取出来的父类型。
从以下几个方面介绍多态:

  1. 多态的体现——父类的引用指向了自己的子类对象。即父类的引用也可以接收自己的子类对象
  2. 多态的前提——必须是类与类之间有关系,要么继承,要么实现。通常还有一个前提:存在覆盖
  3. 多态的好处——多态的出现大大的提高了程序的扩展性
  4. 多态的弊端——提高了扩展性,但是只能使用父类的引用访问父类中的成员,不能预先使用子类,因为那时子类还没存在
  5. 多态的应用
  6. 多态的出现在代码中的特点(多态使用的注意事项)

以动物:猫,狗,猪为例说之

  1. abstract class Animal {
  2. public abstract void eat();
  3. }
  4. class Cat extends Animal {
  5. public void eat() {
  6. System.out.println("吃鱼");
  7. }
  8. public void catchMouse() {
  9. System.out.println("抓老鼠");
  10. }
  11. }
  12. class Dog extends Animal {
  13. public void eat() {
  14. System.out.println("吃骨头");
  15. }
  16. public void kanJia() {
  17. System.out.println("看家");
  18. }
  19. }
  20. class Pig extends Animal {
  21. public void eat() {
  22. System.out.println("饲料");
  23. }
  24. public void gongDi() {
  25. System.out.println("拱地");
  26. }
  27. }

那么以下代码:

  1. Animal a = new Cat(); // 类型提升,向上转型
  2. a.eat();

如果想要调用猫的特有方法时,如何操作?——强制将父类的引用转成子类类型,向下转型,即:

  1. Cat c = (Cat)a;
  2. c.catchMouse();

千万不要出现这样的操作,就是将父类对象转成子类类型,即:

  1. Animal a = new Animal();
  2. Cat c = (Cat)a;

我们能转换的是父类引用指向了自己的子类对象时,该引用可以被提升,也可以被强制转换。多态自始至终都是子类对象在做着变化。

instanceof关键字的使用

instanceof关键字的使用,代码如下:

  1. class DuoTaiDemo2 {
  2. public static void main(String[] args) {
  3. function(new Dog());
  4. function(new Cat());
  5. }
  6. public static void function(Animal a) {
  7. a.eat();
  8. if(a instanceof Cat) {
  9. Cat c = (Cat)a;
  10. c.catchMouse();
  11. } else if(a instanceof Dog) {
  12. Dog d = (Dog)a;
  13. d.kanJia();
  14. }
  15. }
  16. }

多态的特点

在多态中成员函数(非静态)的特点:

简单总结就是:成员函数(非静态)在多态调用时,编译看左边,运行看右边。
在多态中,静态成员函数(或者静态成员变量)的特点:无论编译和运行,都参考左边(引用型变量所属的类)。

在多态中,成员变量的特点:

例,

  1. class Fu {
  2. static int num = 5;
  3. void method1() {
  4. System.out.println("fu method_1");
  5. }
  6. void method2() {
  7. System.out.println("fu method_2");
  8. }
  9. static void method4() {
  10. System.out.println("fu method_4");
  11. }
  12. }
  13. class Zi extends Fu {
  14. static int num = 8;
  15. void method1() {
  16. System.out.println("zi method_1");
  17. }
  18. void method3() {
  19. System.out.println("zi method_3");
  20. }
  21. static void method4() {
  22. System.out.println("zi method_4");
  23. }
  24. }
  25. class DuoTaiDemo4 {
  26. public static void main(String[] args) {
  27. Fu f = new Zi();
  28. System.out.println(f.num);
  29. f.method4();
  30. Zi z = new Zi();
  31. z.method4();
  32. }
  33. }

输出结果为:

5
fu method_4
zi method_4

多态的应用

例1,基础班学生:学习,睡觉;高级班学生:学习,睡觉。可以将这两类事物进行抽取。

  1. abstract class Student {
  2. public abstract void study();
  3. public void sleep() {
  4. System.out.println("躺着睡");
  5. }
  6. }
  7. // 工具类
  8. class DoStudent {
  9. public void doSomething(Student stu) {
  10. stu.study();
  11. stu.sleep();
  12. }
  13. }
  14. class BaseStudent extends Student {
  15. public void study() {
  16. System.out.println("base study");
  17. }
  18. public void sleep() {
  19. System.out.println("坐着睡");
  20. }
  21. }
  22. class AdvStudent extends Student {
  23. public void study() {
  24. System.out.println("adv study");
  25. }
  26. }
  27. class DuoTaiDemo3 {
  28. public static void main(String[] args) {
  29. DoStudent ds = new DoStudent();
  30. ds.doSomething(new BaseStudent());
  31. ds.doSomething(new AdvStudent());
  32. }
  33. }

例2,需求:电脑运行示例,电脑运行基于主板。

  1. // 接口定义规则
  2. interface PCI {
  3. public void open();
  4. public void close();
  5. }
  6. class MainBoard {
  7. public void run() {
  8. System.out.println("mainboard run");
  9. }
  10. public void usePCI(PCI p) { // PCI p = new NetCard(); // 接口型引用指向自己的子类对象
  11. if(p != null) {
  12. p.open();
  13. p.close();
  14. }
  15. }
  16. }
  17. class NetCard implements PCI {
  18. public void open() {
  19. System.out.println("netcard open");
  20. }
  21. public void close() {
  22. System.out.println("netcard close");
  23. }
  24. }
  25. class SoundCard implements PCI {
  26. public void open() {
  27. System.out.println("soundcard open");
  28. }
  29. public void close() {
  30. System.out.println("soundcard close");
  31. }
  32. }
  33. class DuoTaiDemo5 {
  34. public static void main(String[] args) {
  35. MainBoard mb = new MainBoard();
  36. mb.run();
  37. mb.usePCI(null);
  38. mb.usePCI(new NetCard());
  39. mb.usePCI(new SoundCard());
  40. }
  41. }

示意图:
多态的应用
例3,数据库的操作。数据是:用户信息。

  1. 连接数据库(JDBC Hibernate)
  2. 操作数据库(CRUD)——C creat R read U update D delete
  3. 关闭数据库连接
  1. interface UserInfoDao {
  2. public void add(User user);
  3. public void delete(User user);
  4. }
  5. class UserInfoByJDBC implements UserInfoDao {
  6. public void add(User user) {
  7. 1JDBC连接数据库
  8. 2、使用sql添加语句添加数据
  9. 3、关闭连接
  10. }
  11. public void delete(User user) {
  12. 1JDBC连接数据库
  13. 2、使用sql删除语句删除数据
  14. 3、关闭连接
  15. }
  16. }
  17. class UserInfoByHibernate implements UserInfoDao {
  18. public void add(User user) {
  19. 1Hibernate连接数据库
  20. 2、使用sql添加语句添加数据
  21. 3、关闭连接
  22. }
  23. public void delete(User user) {
  24. 1Hibernate连接数据库
  25. 2、使用sql删除语句删除数据
  26. 3、关闭连接
  27. }
  28. }
  29. class DBOperate {
  30. public static void main(String[] args) {
  31. UserInfoDao ui = new UserInfoByHibernate();
  32. ui.add(user);
  33. ui.delete(user);
  34. }
  35. }

示意图:
多态的应用

Object类

Object:是所有对象的直接或者间接父类,传说中的上帝。该类中定义的肯定是所有对象都具备的功能。
Object类中已经提供了对对象是否相同的比较方法,如果自定义类中也有比较相同的功能,没有必要重新定义。只要沿袭父类中的功能,建立自己特有比较内容即可,这就是覆盖。
例,复写Object类中的equals()方法。

  1. class Demo { // extends Object
  2. private int num;
  3. Demo(int num) {
  4. this.num = num;
  5. }
  6. /*
  7. public boolean compare(Demo d) {
  8. return this.num == d.num;
  9. }
  10. */
  11. public boolean equals(Object obj) { // Object obj = new Demo();
  12. if(!(obj instanceof Demo))
  13. return false;
  14. Demo d = (Demo)obj;
  15. return this.num == d.num;
  16. }
  17. }

这里,我们会接触一点点反射的知识,以后会补上。
A.classB.class这些class文件都有名称,这些文件内都有构造函数,一般方法,java中用Class来描述这些class文件,通过getName()获取名称。
所以以下代码

  1. Demo d1 = new Demo(4);
  2. Class c = d1.getClass();
  3. System.out.println(c.getName()); // Demo

会输出Demo
查询API帮助文档,我们可以发现:Object类的toString方法返回一个字符串,该字符串由类名(对象是该类的一个实例)、at标记符“@”和此对象哈希码的无符号十六进制表示组成。换句话说,该方法返回一个字符串,它的值等于:getClass().getName() + '@' + Integer.toHexString(hashCode())
当然我们也可以自己弄:

  1. Demo d1 = new Demo(4);
  2. Class c = d1.getClass();
  3. System.out.println(c.getName()+"@@"+Integer.toHexString(d1.hashCode()));

会输出Demo@@139a55

内部类

将一个类定义在另一个类的里面,对里面那个类就称为内部类(内置类,嵌套类)。
内部类的访问规则:

访问格式:

  1. 当内部类定义在外部类的成员位置上,而且非私有,可以在外部其他类中,可以直接建立内部类对象。
    格式:外部类名.内部类名 变量名 = 外部类对象.内部类对象;,例,Outer.Inner in = new Outer().new Inner();
  2. 当内部类在成员位置上,就可以被成员修饰符修饰,比如,private将内部类在外部类中进行封装,static内部类就具备static的特性。当内部类被static修饰后,只能直接访问外部类中的static成员,出现了访问局限。
    在外部其他类中,如何直接访问静态内部类非静态成员呢?
    答:格式为new Outer.Inner().function();
    在外部其他类中,如何直接访问静态内部类静态成员呢?
    答:格式为:Outer.Inner.function();

注意:当内部类中定义了静态成员,该内部类必须是static的。当外部类中的静态方法访问内部类时,内部类也必须是static的。
例,

  1. class Outer {
  2. private static int x = 3;
  3. static class Inner { // 静态内部类
  4. static void function() { // 当内部类中定义了静态成员,该内部类必须是static的
  5. System.out.println("inner::::"+x); // 当内部类被static修饰后,只能直接访问外部类中的static成员
  6. }
  7. }
  8. static class Inner2 {
  9. void show() {
  10. System.out.println("inner2 show");
  11. }
  12. }
  13. public static void method() {
  14. new Inner2().show(); // 当外部类中的静态方法访问内部类时,内部类也必须是static的
  15. }
  16. }

内部类什么时候使用呢?
答:当描述事物时,事物的内部还有事物,该事物用内部类来描述,因为内部事物在使用外部事物的内容。
例,以人体和心脏为例进行说明之(示例代码如下)

  1. //人体(外部类)
  2. class Body {
  3. //心脏(内部类)
  4. private class XinZang { //心脏得封装起来
  5. }
  6. public void show() {
  7. new XinZang().tiaoDong();
  8. }
  9. }

内部类定义在局部时

  1. 不可以被成员修饰符修饰,因为privatestatic不能修饰局部成员。
  2. 可以直接访问外部类中的成员,因为还持有外部类中的引用。但是不可以访问它所在的局部中的变量,只能访问被final修饰的局部变量。注意:java8没这个区别了,但是被final修饰的变量是一个常量,只能被赋值一次,所以一经存在就不得更改。

例,以下是java8的运行环境。

  1. class Outer {
  2. int x = 3;
  3. void method(int a) {
  4. // a++; // 从内部类引用的本地变量必须是最终变量或实际上的最终变量
  5. int y = 4;
  6. class Inner {
  7. void function() {
  8. System.out.println(a);
  9. }
  10. }
  11. new Inner().function();
  12. }
  13. }
  14. class InnerClassDemo {
  15. public static void main(String[] args) {
  16. Outer out = new Outer();
  17. out.method(7);
  18. out.method(8);
  19. }
  20. }

匿名内部类

  1. 匿名内部类其实就是内部类的简写格式。
  2. 定义匿名内部类的前提:内部类必须是继承一个类或者实现接口。
  3. 匿名内部类的格式:new 父类或者接口() {定义子类的内容}
  4. 匿名内部类其实就是一个匿名子类对象,而且这个对象有点胖。可以理解为带内容的对象。
  5. 匿名内部类中定义的方法最后不要超过3个。

例,

  1. abstract class AbsDemo {
  2. abstract void show();
  3. }
  4. class Outer {
  5. int x = 3;
  6. public void function() {
  7. AbsDemo d = new AbsDemo() {
  8. int num = 9;
  9. void show() {
  10. System.out.println("num==="+num);
  11. }
  12. void abc() {
  13. System.out.println("haha");
  14. }
  15. };
  16. d.show();
  17. // d.abc(); // 编译失败,因为父类中没有这个方法
  18. }
  19. }

练习:补全代码。通过匿名内部类。

  1. interface Inter {
  2. void method();
  3. }
  4. class Test {
  5. // 补足代码。通过匿名内部类
  6. }
  7. class InnerClassTest {
  8. public static void main(String[] args) {
  9. Test.function().method();
  10. }
  11. }

解:

  1. interface Inter {
  2. void method();
  3. }
  4. class Test {
  5. // 补足代码。通过匿名内部类
  6. static Inter function() {
  7. return new Inter() {
  8. public void method() {
  9. System.out.println("Inter method");
  10. }
  11. };
  12. }
  13. }
  14. class InnerClassTest {
  15. public static void main(String[] args) {
  16. // Test.function():Test类中有一个静态的方法function
  17. // .method():function这个方法运算后的结果是一个对象,而且是一个Inter类型的对象,
  18. // 因为只有是Inter类型的对象,才可以调用method().
  19. Test.function().method();
  20. }
  21. }

面试时可能遇到的一个小问题(有关匿名内部类),如果没有一个类继承或一个接口实现,还能使用匿名内部类吗?答案是可以的。

  1. class InnerTest {
  2. public static void main(String[] args) {
  3. new Object() {// new Object() {}是Object类的子类对象
  4. public void function() {
  5. System.out.println("hello");
  6. }
  7. }.function();
  8. }
  9. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注