面向对象之多态
多态:可以理解为事物存在的多种体现形态。
例如:人:男人,女人;
动物:猫,狗
猫 x = new 猫();
动物 x = new 猫();
从以下几个方面介绍多态:
- 多态的体现——父类的引用指向了自己的子类对象。父类的引用也可以接收自己的子类对象
- 多态的前提——必须是类与类之间有关系,要么继承,要么实现。通常还有一个前提:存在覆盖
- 多态的好处——多态的出现大大的提高了程序的扩展性
- 多态的弊端——提高了扩展性,但是只能使用父类的引用访问父类中的成员,不能预先使用子类,因为那时子类还没存在
- 多态的应用
- 多态的出现在代码中的特点(多态使用的注意事项)
以动物:猫,狗,猪为例说之
abstract class Animal { public abstract void eat(); } class Cat extends Animal { public void eat() { System.out.println("吃鱼"); } public void catchMouse() { System.out.println("抓老鼠"); } } class Dog extends Animal { public void eat() { System.out.println("吃骨头"); } public void KanJia() { System.out.println("看家"); } } class Pig extends Animal { public void eat() { System.out.println("饲料"); } public void gongDi() { System.out.println("拱地"); } } //----------------------------------------------------------- public class PolDemo { public static void main(String[] args) { // Cat c = new Cat(); // c.eat(); // // Dog d = new Dog(); // d.eat(); // Cat c = new Cat(); // function(c); // // function(new Dog()); // // function(new Pig()); // Animal c = new Cat(); // c.eat(); Animal a = new Cat();//类型提升。向上转型 a.eat(); //如果想要调用猫的特有方法时,如何操作? //强制将父类的引用转成子类类型,向下转型 Cat c = (Cat)a; c.catchMouse(); /* * 千万不要出现这样的操作,就是将父类对象转成子类类型 * 我们能转换的是父类引用指向了自己的子类对象时,该引用可以被提升,也可以被强制转换 * 多态自始至终都是子类对象在做着变化 * Animal a = new Animal(); * Cat c = (Cat)a; * */ // function(new Cat()); // function(new Dog()); // function(new Pig()); } public static void function(Animal a) { //Animal a = new Cat(); a.eat(); // a.catchMouse(); /*if(a instanceof Animal) { System.out.println("hello"); } else*/ if(a instanceof Cat) { Cat c = (Cat)a; c.catchMouse(); } else if(a instanceof Dog) { Dog d = (Dog)a; d.KanJia(); } } /* public static void function(Cat c) { //Cat c = new Cat(); c.eat(); } public static void function(Dog d) { d.eat(); } public static void function(Pig p) { p.eat(); } */ }
以下例说明多态的应用:
基础班学生:学习,睡觉
高级班学生:学习,睡觉
可以将这两类事物进行抽取。
代码如下:
abstract class Student { public abstract void study(); public void sleep() { System.out.println("躺着睡"); } } //工具类 class DoStudent { public void doSomething(Student s) { s.study(); s.sleep(); } } //-------------------------------------------------------------- class BaseStudent extends Student { public void study() { System.out.println("base study"); } public void sleep() { System.out.println("坐着睡"); } } class AdvStudent extends Student { public void study() { System.out.println("adv study"); } } public class DuoTaiDemo { public static void main(String[] args) { DoStudent ds = new DoStudent(); ds.doSomething(new BaseStudent()); ds.doSomething(new AdvStudent()); // BaseStudent bs = new BaseStudent(); // bs.study(); // bs.sleep(); // AdvStudent as = new AdvStudent(); // as.study(); // as.sleep(); } }
以下例说明多态的出现在代码中的特点(多态使用的注意事项)
在多态中成员函数(非静态)的特点:
- 在编译时期:参阅引用型变量所属的类中是否有调用的方法,如果有,编译通过,如果没有编译失败。
- 在运行时期:参阅对象所属的类中是否有调用的方法。
简单总结就是:
- 成员函数(非静态)在多态调用时,编译看左边,运行看右边。
- 在多态中,成员变量的特点:无论编译和运行,都参考左边(引用型变量所属的类)
- 在多态中,静态成员函数的特点:无论编译和运行,都参考左边。
示例代码如下:
class Fu { int num = 5; void method1() { System.out.println("fu method_1"); } void method2() { System.out.println("fu method_2"); } static void method4() { System.out.println("fu method_4"); } } class Zi extends Fu { int num = 8; void method1() { System.out.println("zi method_1"); } void method3() { System.out.println("zi method_3"); } static void method4() { System.out.println("zi method_4"); } } public class DuoTaiDemo1 { public static void main(String[] args) { /* * 在多态中,成员变量的特点:无论编译和运行,都参考左边(引用型变量所属 的类) */ // Fu f = new Zi(); // System.out.println(f.num); // // Zi z = new Zi(); // System.out.println(z.num); /* * 在多态中,静态成员函数的特点:无论编译和运行,都参考左边。 */ Fu f = new Zi(); f.method4(); Zi z = new Zi(); z.method4(); /* * 在多态中成员函数(非静态)的 特点:在编译时期:参阅引用型变量所属的类中是否有调用的方法,如果有,编译通过,如果没有编译失败 * 在运行时期:参阅对象所属的类中是否有调用的方法 * 简单总结就是:成员函数在多态调用时,编译看左边,运行看右边。 */ // Fu f = new Zi(); // f.method1(); // f.method2(); // f.method3(); } }
多态的应用:
1、电脑运行示例,电脑运行基于主板
代码示例如下:
//接口定义规则 interface PCI { public void open(); public void close(); } class MainBoard { public void run() { System.out.println("mainboard run"); } public void usePCI(PCI p) {// PCI p = new NetCard(); 接口型引用指向自己的子类对象 if(p != null) { p.open(); p.close(); } } } class NetCard implements PCI { public void open() { System.out.println("netcard open"); } public void close() { System.out.println("netcard close"); } } class SoundCard implements PCI { public void open() { System.out.println("SoundCard open"); } public void close() { System.out.println("SoundCard close"); } } public class DuoTaiDemo2 { public static void main(String[] args) { MainBoard mb = new MainBoard(); mb.run(); mb.usePCI(null); mb.usePCI(new NetCard()); mb.usePCI(new SoundCard()); } }
2、数据库的操作。数据是:用户信息。
- 连接数据库(JDBC Hibernate)
- 操作数据库(CRUD)——C creat R read U update D delete
- 关闭数据库连接
代码示例如下:
interface UserInfoDao { //dao: d(data) a(access) o(object)——数据访问对象 public void add(User user); public void delete(User user); } class UserInfoByJDBC implements UserInfoDao { public void add(User user) { 1、JDBC连接数据库 2、使用sql添加语句添加数据 3、关闭连接 } public void delete(User user) { 1、JDBC连接数据库 2、使用sql删除语句删除数据 3、关闭连接 } } class UserInfoByHibernate implements UserInfoDao { public void add(User user) { 1、Hibernate连接数据库 2、使用sql添加语句添加数据 3、关闭连接 } public void delete(User user) { 1、Hibernate连接数据库 2、使用sql删除语句删除数据 3、关闭连接 } } public class DBOperate { public static void main(String[] args) { // UserInfoByJDBC ui = new UserInfoByJDBC(); // UserInfoByHibernate ui = new UserInfoByHibernate(); UserInfoDao ui = new UserInfoByJDBC(); //多态 ui.add(user); ui.delete(user); } }
Object类
Object:是所有对象的直接或者间接父类,传说中的上帝。该类中定义的肯定是所有对象都具备的功能。
Object类中已经提供了对对象是否相同的比较方法,如果自定义类中也有比较相同的功能,没有必要重新定义。只要沿袭父类中的功能,建立自己特有比较内容即可,这就是覆盖。
内部类
内部类的访问规则:
- 内部类可以直接访问外部类中的成员,包括私有。之所以可以直接访问外部类中的成员,是因为内部类中持有了一个外部类中的引用,格式--外部类名.this
- 外部类要访问内部类,必须建立内部类对象
访问格式:
1、当内部类定义在外部类的成员位置上,而且非私有,可以在外部其他类中,可以直接建立内部类对象。
格式:外部类名.内部类名 变量名 = 外部类对象.内部类对象
Outer.Inner in = new Outer().new Inner();
2、当内部类在成员位置上,就可以被成员修饰符修饰,比如,private将内部类在外部类中进行封装,static内部类就具备static的特性。当内部类被static修饰后,只能直接访问外部类中的static成员,出现了访问局限。
在外部其他类中,如何直接访问静态内部类非静态成员呢?
new Outer.Inner().function();
在外部其他类中,如何直接访问静态内部类静态成员呢?
Outer.Inner.function();
注意:当内部类中定义了静态成员,该内部类必须是static的。当外部类中的静态方法访问内部类时,内部类也必须是static的。
示例代码如下:
class Outer { private static int x = 3; static class Inner {//内部类可以被私有修饰(静态内部类) // int x = 4; static void function() { // int x = 6; System.out.println("inner:"+x); } } static class Inner2 { void show() { System.out.println("inner2 show"); } } public static void method() { // Inner in = new Inner(); // in.function(); // Inner.function(); new Inner2().show(); } } public class InnerClassDemo { public static void main(String[] args) { Outer.method(); // Outer.Inner.function(); // Outer out = new Outer(); // out.method(); //直接访问内部类中的成员 // Outer.Inner in = new Outer().new Inner(); // in.function(); } }
内部类什么时候使用呢?
当描述事物时,事物的内部还有事物,该事物用内部类来描述,因为内部事物在使用外部事物的内容。以人体和心脏为例进行说明之(示例代码如下)
//人体(外部类) class Body { //心脏(内部类) private class XinZang { //心脏得封装起来 } public void show() { new XinZang().tiaoDong(); } }
内部类定义在局部时
- 不可以被成员修饰符修饰,因为private、static不能修饰局部成员
- 可以直接访问外部类中的成员,因为还持有外部类中的引用。但是不可以访问它所在的局部中的变量,只能访问被final修饰的局部变量,java8好像没这个区别了
示例代码如下:
class Outer_P { int x = 3; void method(final int a) { final int y = 4; class Inner { //private、static不能修饰局部成员 void function() { // System.out.println(Outer_P.this.x); // System.out.println(y); System.out.println(a); } } new Inner().function(); } } public class InnerClassDemo1 { public static void main(String[] args) { Outer_P out = new Outer_P(); out.method(7); out.method(8); } }
匿名内部类
- 匿名内部类其实就是内部类的简写格式
- 定义匿名内部类的前提:内部类必须是继承一个类或者实现接口
- 匿名内部类的格式:new 父类或者接口() {定义子类的内容}
- 匿名内部类其实就是一个匿名子类对象,而且这个对象有点胖。可以理解为带内容的对象
- 匿名内部类中定义的方法最后不要超过3个。
示例代码如下:
abstract class AbsDemo { abstract void show(); } class Outer_I { int x =3; /* class Inner extends AbsDemo { int num = 90; void show() { System.out.println("show:"+num); } void abc() { System.out.println("haha"); } } */ public void function() { // new Inner().show(); // Inner in = new Inner(); // in.show(); // in.abc(); //AbsDemo a = new Inner(); //new AbsDemo(){}是一个AbsDemo的匿名子类对象 AbsDemo d = new AbsDemo() { int num = 9; void show() { System.out.println("num==="+num); } //子类特有方法 void abc() { System.out.println("haha"); } }; d.show(); // d.abc();//编译失败 new AbsDemo() { void show() { System.out.println("x="+x); } //子类特有方法 void abc() { System.out.println("haha"); } }.abc(); } } public class InnerClassDemo2 { public static void main(String[] args) { new Outer_I().function(); } }
练习:补全代码。通过匿名内部类
示例代码:
interface Inter { void method(); } class Test { /* static class Inner implements Inter { public void method() { System.out.println("method run"); } } static Inter function() { return new Inner(); } */ //补足代码。通过匿名内部类 static Inter function() { return new Inter() { public void method() { System.out.println("haha"); } }; } } public class InnerClassTest { public static void main(String[] args) { //Test.function():Test类中有一个静态的方法function //.method():function这个方法运算后的结果是一个对象,而且是一个Inter类型的对象,因为只有是Inter类型的对象,才可以调用method(). Test.function().method(); // Inter in = Test.function(); // in.method(); show(new Inter() { public void method() { System.out.println("main run"); } }); } public static void show(Inter in) { in.method(); } }
面试时可能遇到的一个小问题(有关匿名内部类),如果没有一个类继承或一个抽象类实现,还能使用匿名内部类吗?答案是可以的。示例代码如下:
class InnerTest { public static void main(String[] args) { new Object() {// new Object() {}是Object类的子类对象 public void function() { System.out.println("hello"); } }.function(); } }