多态简介
概念:多态意味着允许不同类的对象对同一消息做出不同的响应
分类:多态分为编译时多态和运行时多态,一般我们说java的多态,就是指运行时多态
— 编译时多态,通过方法重载在设计时实现多态
— 运行时多态,程序运行时动态决定调用哪个方法
条件:满足继承关系,父类引用指向子类对象
实现:通过子类重写父类方法,实现多态
package com.test;import com.animal.Animal;import com.animal.Cat;import com.animal.Dog;public class Test {public static void main(String[] args) {Animal one = new Animal();Animal two = new Cat();Animal three = new Dog();one.eat(); // 动物,会吃东西two.eat(); // 猫,会吃东西three.eat(); // 狗,会吃东西}}
通过这个例子可以看到,对象one,two,three虽然都是Animal类型,但是由于指向的对象的类型是不同的,导致eat方法执行不同的处理,这就是多态
向上转型
- 又称向上转型,隐式转型,自动转型
- 意思是父类引用指向子类实例
- 可以调用父类派生的方法
- 可以调用子类重写父类的方法,且执行的是子类重写后的处理
- 无法调用子类独有的方法
- 父类中的静态方法无法被子类重写,所有向上转型之后,只能调用到父类原有的静态方法
package com.test;import com.animal.Animal;import com.animal.Cat;public class Test {public static void main(String[] args) {Animal one = new Animal();Animal two = new Cat();two.setAge(2); // setAge是父类派生的方法,可以调用two.eat(); // eat是子类重写的方法,可以调用,且执行的是子类重写后的处理// two.setWeight(17); // 报错,无法调用子类独有的方法}}
向下转型
- 意思是子类引用指向父类实例
- 需要进行强制类型转换
- 可以调用子类特有的方法
- 跟用子类直接实例化子类对象没有什么区别
package com.test;import com.animal.Animal;import com.animal.Cat;public class Test {public static void main(String[] args) {Animal one = new Cat();Cat two = (Cat) one;two.eat(); // eat是子类重写的方法,可以调用,且执行的是子类重写后的处理two.setWeight(12.0); // setWeight是子类特有的方法,可以调用System.out.println(two.getWeight()); // getWeight是子类特有的方法,可以调用// 下面这样是不行的,会编译报错// Cat three = (Cat) new Animal();}}
instanceof
- 判断对象是否是某个类的实例,如果是返回true,反之返回false
- 如果 A instanceof B == ture,那么A instanceof B的父类,结果也是 true
- 常用于强制类型转换之前的类型判断

package com.imooc.test;import com.imooc.animal.Animal;import com.imooc.animal.Cat;public class Test {public static void main(String[] args) {Animal one = new Cat();if (one instanceof Cat) {Cat two = (Cat) one;two.eat();two.setWeight(12.0);System.out.println(two.getWeight());}// 条件成立if (one instanceof Animal) {System.out.println("Animal");}// 条件成立if (one instanceof Object) {System.out.println("Object");}}}
抽象类和抽象方法
我们先来看一种场景,以下代码编译是不会出错的,因为父类的确有eat这个方法,但是这样做没有实际意义。
一般来说,子类(比如Cat)会重写eat方法,所以调用子类的eat方法才有实际意义。
// 定义父类 Animalpublic class Animal {private String name;public Animal() {super();}public String getName() {return name;}public void setName(String name) {this.name = name;}public void eat() {System.out.println("动物,会吃东西");}}// 使用Animal类进行实例化Animal pet = new Animal("花花",2);pet.eat();
怎么才能在编码阶段就提示这种没有意义的做法呢,这就要用到抽象类里了,请继续往下看。
作用:Java中使用抽象类,可以限制实例化
应用场景:
某个父类只是知道其子类应该包含怎样的方法,但无法准确知道这些子类如何实现这些方法
使用规则:
- 使用abstract关键字定义抽象类
- 抽象类不能直接实例化,只能被继承,可以通过向上转型完成对象实例
- 使用sbstract关键字定义抽象方法,不需要具体实现
- 包含抽象方法的类是抽象类
- 抽象类中可以没有抽象方法
- static final private不能与abstract并存
// 定义抽象父类 Animalpublic abstract class Animal {private String name;public Animal() {super();}public String getName() {return name;}public void setName(String name) {this.name = name;}public abstract void eat(); // 抽象方法}// 使用Animal类进行实例化// Animal pet = new Animal("花花",2); => 这样是会报错的Animal pet = new Cat("花花",2); => 这样是这正确的用法
接口
有些类不能抽取出一个公共的父类,但是它们之间又具有一些相同的行为,怎么才能使它们关联起来?
在java中,根据行为建立它们之间的联系,就需要用到接口。
eclipse创建接口的方法:New => Interface
#一般接口的名字以I开头
比如:我们来看看怎样根据行为建立手机,智能手表,照相机之间的联系
智能手机拥有的行为:发短信,打电话,拍照
照相机拥有的行为 :拍照
拍照接口:IPhoto.java
package com.dev;/*** 具有照相能力的接口* @author 57681**/public interface IPhoto {public void photo();}
照相机类:Camera.java
package com.dev;public class Camera implements IPhoto{@Overridepublic void photo() {System.out.println("照相机可以拍照");}// public void photo() {// System.out.println("照相机可以拍照");// }}
智能手机类:SmartPhone.java
package com.dev;public class SmartPhone implements IPhoto {public void message() {System.out.println("智能手机可以发短信");}public void call() {System.out.println("智能手机可以打电话");}// public void photo() {// System.out.println("智能手机可以拍照");// }@Overridepublic void photo() {System.out.println("智能手机可以拍照");}}
测试接口
package com.imooc;import com.dev.Camera;import com.dev.IPhoto;import com.dev.SmartPhone;public class PhotoTest {public static void main(String[] args) {IPhoto ip = new Camera();ip.photo(); // 照相机可以拍照ip = new SmartPhone();ip.photo(); // 智能手机可以拍照}}
接口成员(抽象方法&常量)
- 接口定义了某一批类所需要遵守的规范
- 接口不关心这些类的内部数据,也不关心这些类里方法的实现细节,它只规定这些类里必须提供某些方法
接口:INet
package com.dev;// 接口访问修饰符:public 默认public interface INet {// 接口类中的抽象方法可以不写abstract关键字// 方法的访问修饰符默认publicvoid network();void connection();// 接口中可以包含常量,默认public static finalint TEMP = 20;}
电脑类:Compute
#必须实现接口中所有的抽象方法
package com.dev;public class Compute implements INet {@Overridepublic void network() {System.out.println("network");}@Overridepublic void connection() {System.out.println("connection");}}
测试接口
package com.imooc;import com.dev.Compute;import com.dev.INet;public class NetTest {public static void main(String[] args) {INet net = new Compute();net.network(); // networknet.connection(); // connectionSystem.out.println(net.TEMP); // 20}}
接口成员(默认方法&静态方法)
接口:INet
package com.dev;// 接口访问修饰符:public 默认public interface INet {// default:默认方法 可以带方法体 jdk1.8后新增// 可以在实现类中重写,并可以通过接口的引用调用default void start() {System.out.println("我是接口中的默认方法");}// static:静态方法 可以带方法体 jdk1.8后新增// 不可以在实现类中重写,可以通过接口名调用static void stop() {System.out.println("我是接口中的静态方法");}}
电脑类:Compute
package com.dev;public class Compute implements INet {@Overridepublic void start() {INet.super.start(); // 调用接口中默认的方法System.out.println("custom");}}
测试接口
package com.imooc;import com.dev.Compute;import com.dev.INet;public class NetTest {public static void main(String[] args) {INet net = new Compute();net.start(); // 我是接口中的默认方法 customeINet.stop(); // 我是接口中的静态方法System.out.println(net.TEMP); // 20}}
多接口中重名默认方法的解决方案
接口INet和接口IPhoto同时拥有默认方法start
接口INet
package com.dev;// 接口访问修饰符:public 默认public interface INet {default void start() {System.out.println("我是接口中的默认方法");}}
接口IPhoto
package com.dev;/*** 具有照相能力的接口* @author 57681**/public interface IPhoto {default void start() {System.out.println("我是IPhoto接口中的默认方法");}}
电脑类:Compute
#实施接口INet和接口IPhoto
package com.dev;public class Compute implements INet, IPhoto {// 这里如果不定义自己的start方法,是会报错的public void start() {System.out.println("Compute中的默认方法");}}
注意:比如Compute类继承了一个父类,然后父类中有start方法,那么即使不定义自己的start方法,也是不会报错的。实例对象调用start方法的话,执行的就是父类的start方法。
测试接口
package com.imooc;import com.dev.Compute;import com.dev.INet;import com.dev.IPhoto;public class NetTest {public static void main(String[] args) {INet net = new Compute();net.start(); // Compute中的默认方法IPhoto photo = new Compute();photo.start(); // Compute中的默认方法}}
多接口中重名常量的解决方案
测试结果:出错
原因分析:接口One和接口Two中有相同的常量x,FinalTest类中不知道该使用哪个,所以会出错
package com.imooc;interface One{static int x = 11;}interface Two{static int x = 22;}class Three{public static final int x = 33;}public class FinalTest implements One, Two {public void test() {System.out.println(x);}public static void main(String[] args) {new FinalTest().test();}}
测试结果:出错
原因分析:跟重名方法不同,即使父类中存在重名常量,也不会使用父类的常量。在这里父类不好使。
package com.imooc;interface One{static int x = 11;}interface Two{static int x = 22;}class Three{public static final int x = 33;}public class FinalTest extends Three implements One, Two {public void test() {System.out.println(x);}public static void main(String[] args) {new FinalTest().test();}}
测试结果:正确
package com.imooc;interface One{static int x = 11;}interface Two{static int x = 22;}class Three{public static final int x = 33;}public class FinalTest extends Three implements One, Two {public static final int x = 44;public void test() {System.out.println(x);}public static void main(String[] args) {new FinalTest().test();}}
接口的继承
IFather1.java
package com.dev;public interface IFather1 {void run();}
IFather2.java
package com.dev;public interface IFather2 {void eat();}
ISon.java:继承IFather1,IFather2
#子类只能继承一个父类,但是接口可以继承多个接口
package com.dev;public interface ISon extends IFather1, IFather2 {void fly();}
Demo类:实现接口ISon,需要重写所有的方法
package com.dev;public class Demo implements ISon {@Overridepublic void run() {// TODO Auto-generated method stub}@Overridepublic void eat() {// TODO Auto-generated method stub}@Overridepublic void fly() {// TODO Auto-generated method stub}}
内部类
内部类的定义:
- 在java中,可以将一个类定义在另一个类的里面或者一个方法里面,这样的类成为内部类
- 与之对应,包含内部类的类被成为外部类
内部类的好处:
因为内部类隐藏在外部类之内,更好的实现了数据隐藏。
内部类的分类:
成员内部类,静态内部类,方法内部类,匿名内部类
成员内部类(普通内部类)
Person.java
package com.people;// 外部类public class Person {public int age;public Heart getHeart() {new Heart().temp = 12;// temp; 无法识别return new Heart();}public void eat() {System.out.println("人会吃东西");}// 成员内部类// 1. 内部类在外部使用时,无法直接实例化,需要借由外部类信息才能完成实例化// 2. 内部类的访问修饰符,可以任意,但是访问范围会受到影响// 3. 内部类可以直接访问外部类的成员,如果出现同名属性,优先访问内部类中定义的// 4. 可以使用外部类.this.成员的方式,访问外部类中同名的信息// 5. 外部类访问内部类信息,需要通过内部类实例,无法直接访问// 6. 内部类编译后.class文件命名:外部类$内部类.classpublic class Heart {int age = 13;int temp = 22;public void eat() {System.out.println("测试eat()");}public String beat() {Person.this.eat();// return age + "心脏在跳动";return Person.this.age + "岁的心脏在跳动";}}}
PeopleTest.java
package com.people;public class PeopleTest {public static void main(String[] args) {Person lili = new Person();lili.age = 12;// 获取内部类对象实例,方式1:new 外部类.new 内部类Person.Heart myHeart = new Person().new Heart();System.out.println(myHeart.beat());// 获取内部类对象实例,方式2:外部类对象.new 内部类myHeart = lili.new Heart();System.out.println(myHeart.beat());// 获取内部类对象实例,方式3:外部类对象.获取方法myHeart = lili.getHeart();System.out.println(myHeart.beat());}}
静态内部类
Person.java
package com.people;// 外部类public class Person {int age;public Heart getHeart() {new Heart().temp = 12;return new Heart();}public void eat() {System.out.println("人会吃东西");}// 成员内部类// 1. 静态内部类中,只能直接访问外部类的静态方法,如果需要调用非静态方法,可以通过对象实例// 2. 静态内部类对象实例时,可以不依赖于外部类对象// 3. 可以通过外部类.内部类.静态成员的方式,访问内部类中的静态成员// 4. 当内部类属性与外部类属性同名时,默认直接调用内部类中的成员// 5. 如果需要访问外部类的静态属性,则可以通过 外部类.属性的方式// 5. 如果需要访问外部类的非静态属性,则可以通过 new 外部类().属性的方式static class Heart {public static int age = 13;int temp = 22;public static void say() {System.out.println("hello");}public String beat() {new Person().eat();return new Person().age + "岁的心脏在跳动";}}}
PeopleTest.java
package com.people;public class PeopleTest {public static void main(String[] args) {Person lili = new Person();lili.age = 12;// 获取静态内部类对象实例Person.Heart myHeart = new Person.Heart();System.out.println(myHeart.beat());Person.Heart.say();}}
方法内部类(局部内部类)
Person.java
package com.people;// 外部类public class Person {int age;// 方法内部类// 1. 定义在方法内部,作用范围也在方法内// 2. 和方法内部成员使用规则一样,class前面不可以添加public,private,protected,static// 3. 类中不能包含静态成员// 4. 类中可以包含final,abstract修饰的成员public Object getHeart() {class Heart {public int age = 13;public void say() {System.out.println("hello");}public String beat() {new Person().eat();return new Person().age + "岁的心脏在跳动";}}return new Heart().beat();}public void eat() {System.out.println("人会吃东西");}}
PeopleTest.java
package com.people;public class PeopleTest {public static void main(String[] args) {Person lili = new Person();lili.age = 12;System.out.println(lili.getHeart());}}
匿名内部类
将类的定义与类的创建,放到一起完成
用匿名内部类可以简化抽象类和接口实现的操作
适用场景:
- 只用到类的一个实例
- 类在定义后马上用到
- 给类命名并不会导致代码更容易被理解
例子:根据传入的不同的人的类型,调用对应的read方法
Person.java
package com.anonymous;public abstract class Person {private String name;public Person() {}public String getName() {return name;}public void setName(String name) {this.name = name;}public abstract void read();}
Man.java
package com.anonymous;public class Man extends Person {@Overridepublic void read() {// TODO Auto-generated method stubSystem.out.println("男生喜欢看科幻类书籍");}}
Woman.java
package com.anonymous;public class Woman extends Person {@Overridepublic void read() {// TODO Auto-generated method stubSystem.out.println("女生喜欢言情小说");}}
PersonTest.java
#方案1:
package com.anonymous;public class PersonTest {public void getRead(Man man) {man.read();}public void getRead(Woman woman) {woman.read();}public static void main(String[] args) {PersonTest test = new PersonTest();Man one = new Man();Woman two = new Woman();test.getRead(one);test.getRead(two);}}
方案2
package com.anonymous;public class PersonTest {public void getRead(Person person) {person.read();}public static void main(String[] args) {PersonTest test = new PersonTest();Man one = new Man();Woman two = new Woman();test.getRead(one);test.getRead(two);}}
方案3(匿名内部类)
- 匿名内部类没有类型名称,实例对象名称
- 编译后的文件命名:外部类$数字.class
- 无法使用private,public,protected,abstract,static修饰
- 无法编写构造方法,可以添加构造代码块
- 不能出现静态成员
- 匿名内部类可以实现接口也可以继承父类,但是不可兼得 ```java package com.anonymous;
public class PersonTest {
public void getRead(Person person) {person.read();}public static void main(String[] args) {PersonTest test = new PersonTest();test.getRead(new Person() {@Overridepublic void read() {// TODO Auto-generated method stubSystem.out.println("男生喜欢看科幻类书籍");}});test.getRead(new Person() {@Overridepublic void read() {// TODO Auto-generated method stubSystem.out.println("女生喜欢看科幻类书籍");}});}
}
<a name="os3TJ"></a>## 知识巩固问题:请写出程序输出结果<br />答案:1,4,2,3,5,6<br />分析:<br />//1. 静态优先<br />//2. 父类优先<br />//3. 非静态块优先于构造函数```javapackage com.imooc.interview;//请写出程序输出结果//1. 静态优先//2. 父类优先//3. 非静态块优先于构造函数public class ExecutionSequence {public static void main(String[] args) {new GeneralClass();}}class ParentClass{static {System.out.println("①我是父类静态块");}{System.out.println("②我是父类非静态块");}public ParentClass(){System.out.println("③我是父类构造函数");}}class GeneralClass extends ParentClass{static{System.out.println("④我是子类静态块");}{System.out.println("⑤我是子类非静态块");}public GeneralClass(){System.out.println("⑥我是子类构造函数");}}
