面向对象之多态
- 对象的多种形态和状态
- 针对方法
- 有了封装、继承才有了多态
- 必要条件
- 继承、重写
- 当编译时类型和运行时类型不一致,就会出现多态(Polymorphism)
- 编译看左边(左边=),运行看右边(=右边)
多态实现基础
- 父类声明的变量可以引用所有子类的对象,这是多态实现的基础
- 只有在运行时,才会知道引用变量所指向的具体实例对象
多态的作用
把不同的子类对象都当做父类来看,可以屏蔽不同子类对象之间的差异,写出通用的代码,做出通用的编程,以适应需求的不断变化。
- 就是父类或接口的引用限量指向子类的对象
多态入门–继承
//父类
public class Fruit {
//水果类
}
//子类
public class Apple extends Fruit {
//苹果类也是水果的一种,所以继承水果类
}
//子类
public class Banana extends Fruit {
//香蕉类
}
//子类
public class Grape extends Fruit {
//葡萄类
}
//测试
public class Test {
public static void main(String[] args) {
Fruit fruit1 = new Fruit();
Apple apple = new Apple();
Banana banana = new Banana();
Grape grape = new Grape();
//等号左侧是一个父类的引用,右侧是一个子类的对象
//父类引用指向了子类的对象
Fruit fruit2 = apple;
Fruit fruit3 = new Apple();
System.out.println(fruit2);
System.out.println(fruit3);
System.out.println("--------------");
Fruit fruit4 = banana;
Fruit fruit5 = new Banana();
System.out.println(fruit4);
System.out.println(fruit5);
System.out.println("--------------");
Fruit fruit6 = grape;
Fruit fruit7 = new Grape();
System.out.println(fruit6);
System.out.println(fruit7);
System.out.println("--------------");
}
}
多态进阶–继承
//父类
public class Animal {
public void shout(){
System.out.println("动物会叫");
}
}
//子类
public class Cat extends Animal {
@Override
public void shout(){
System.out.println("喵喵喵");
}
public void eatFish(){
System.out.println("小猫吃鱼");
}
}
//子类
public class Dog extends Animal {
@Override
public void shout() {
System.out.println("汪汪汪");
}
public void eatBone(){
System.out.println("小狗啃骨头");
}
}
//测试
public class Test {
public static void main(String[] args) {
//a1 又叫引用,它指向了堆里面的真正的对象。也就是常说的栈指向堆
//a1 叫做引用 创建出来的对象叫做实例(instans)---多态
Animal a1 = new Animal();
a1.shout();
Dog d1 = new Dog();
d1.shout();
d1.eatBone();
Cat c1 = new Cat();
c1.shout();
c1.eatFish();
System.out.println("------------");
Animal a2 = new Dog();
a2.shout();
//a2.eatBone();//Animal类的引用 只能看到Animal的方法 它引用的是子类重写父类的方法
Animal a3 = new Cat();
a3.shout();
//a3.eatFish();
System.out.println("--------------");
// 类型转换---多态
Animal a4 = new Dog();
Dog d2 = (Dog) a4;
d2.shout();
d2.eatBone();
Animal a5 = new Cat();
Cat c2 = (Cat) a5;
c2.shout();
c2.eatFish();
System.out.println("--------------");
}
}
结果:
动物会叫
汪汪汪
小狗啃骨头
喵喵喵
小猫吃鱼
------------
汪汪汪
喵喵喵
--------------
汪汪汪
小狗啃骨头
喵喵喵
小猫吃鱼
--------------
向上转型 upcasting
- 系统自动转型
- 执行父类的方法
- 一个引用变量如果声明为父类的类型,但实际引用的是子类对象,那么该变量就不能访问子类中特有的属性和方法
Animal a2 = new Dog();
a2.shout();
//a2.eatBone();//Animal类的引用 只能看到Animal的方法 它引用的是子类重写父类的方法
Animal a3 = new Cat();
a3.shout();
//a3.eatFish();
向下转型 downcasting
- 强制转型(强制类型转换符—())
- 父类转换子类
- 执行子类的方法
// 类型转换---多态
Animal a4 = new Dog();
Dog d2 = (Dog) a4;
d2.shout();
d2.eatBone();
Animal a5 = new Cat();
Cat c2 = (Cat) a5;
c2.shout();
c2.eatFish();
多态的总结
- 静态方法不能被重写,故静态方法没有多态性
- 成员变量不具备多态性。成员变量的取值取决于所属的类
- 成员方法
- 编译时:检查引用变量所属类中是否有所调用的方法
- 运行时:调用实际对象所属类中的重写方法
类型转换的重点
- 弄清楚继承关系,谁是父类谁是子类
- 关键点在于创建时调用的是谁的构造器
instanceof
- 关键字
- 是否 是实例
- 返回布尔值
- 语法
- boolean result = 检查的对象 instanceof 对象是不是这类的实例(类名);
- System.out.println(a5 instanceof Animal);
public static void main(String[] args){
Animal a5 = new Cat();
Cat c2 = (Cat) a5;
c2.shout();
c2.eatFish();
System.out.println(a5 instanceof Animal);
System.out.println(c2 instanceof Animal);
System.out.println(c2 instanceof Cat);
}
结果:
true
true
true
多态的应用场景
继承的多态
- 多个子类对同一个父类方法的重写,可以在运行时表现出不同行为
接口的多态
- 一个接口可以有多个实现类。所以,多个实现对接口中同一个方法的重写,可以在运行时表现出不同的行为
- 实例
- 接口名 变量名 = new 实现类名();
public interface MyInterface {
void test();
}
public class MyInterfaceImpl1 implements MyInterface{
@Override
public void test() {
System.out.println("Impl111");
}
}
public class MyInterfaceImpl2 implements MyInterface{
@Override
public void test() {
System.out.println("test222");
}
}
public class Test {
public static void main(String[] args) {
MyInterfaceImpl1 myInterfaceImpl1 = new MyInterfaceImpl1();
myInterfaceImpl1.test();
MyInterfaceImpl2 myInterfaceImpl2 = new MyInterfaceImpl2();
myInterfaceImpl2.test();
System.out.println("-----------");
MyInterface myInterface1 = new MyInterfaceImpl1();
myInterface1.test();
MyInterface myInterface2 = new MyInterfaceImpl2();
myInterface2.test();
System.out.println("------------");
//=右边 是没有名字的类的对象的方法--给予接口实现匿名内部类
MyInterface myInterface3 =new MyInterface() {
@Override
public void test() {
System.out.println("怎么回事!");
}
};
myInterface3.test();
}
}
形参的多态
- 多态可以作为参数,接受范围更广的对象,接收参数更加灵活
public class Test2 {
public static void main(String[] args) {
Test2 t1 = new Test2();
Cat cat = new Cat();
t1.getCat(cat);
}
public void getCat(Animal animal){
System.out.println("cat");
}
}
返回值的多态
- 多态可以作为返回值,接受范围更广的对象
public class Test3 {
public static void main(String[] args) {
Animal animal = Test3.printClass();
}
public static Animal printClass(){
Cat cat = new Cat();
return cat;
}
}
内部类
概述
在一个类的内部定义的类称为 内部类,其实就是类定义的位置发生了变化。再类中定义的内部类叫 成员内部类,在函数中定义的内部类 叫局部内部类; 使用static修饰的成员内部类叫 静态内部类。讲台内部类使用很少,用的最多的是成员内部类。
- 内部类生产的class文件为“外部类$内部类”,它表明该内部类是属于那个外部类
成员内部类
- 定义成员内部类
- 成员内部类也是一个类,可以由自己的成员属性,成员方法
- 成员内部类可以访问外部类的成员方法和成员属性
- 内部类中this.name 中的this 表示内部类
public class Test {
private int treeType = 100;
private String name = "le";
public int getTreeType(){
return treeType;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
class Test1{
private String name = "fa";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//访问外部类属性
public int getTreeNumber(){
int numTpye = Test.this.treeType;
return numTpye;
}
//访问外部类方法
public String getTestName(){
return Test.this.getName();
}
}
}
小结1
- 内部类访问外部类的字段
- 外部类名.this.字段
- 内部类访问外部类的方法
- 外部类名.this.方法
- 内部类访问内部类字段
- this.字段
- 内部类访问内部类方法
- this.方法
2.成员内部类的使用
- 成员内部类必须依赖于外部类的对象
- 所以,首先创建外部类,然后使用外部类的实例创建普通内部类
//第一种
public class Test1 {
public static void main(String[] args) {
//创建外部类对象
Test test = new Test();
System.out.println(test.getName());
System.out.println(test.getTreeType());
//创建内部类对象
Test.Test1 test1 = test.new Test1();
System.out.println("----------------");
//内部类对象调用方法
int number = test1.getTreeNumber();
System.out.println(number);
System.out.println(test1.getTestName());
System.out.println(test1.getName());
}
}
//第二种
public class Test1 {
public static void main(String[] args) {
//创建外部类对象
Test test = new Test();
System.out.println(test.getName());
System.out.println(test.getTreeType());
//创建内部类对象
Test.Test1 test1 = new Test().new Test1();
System.out.println("----------------");
//内部类对象调用方法
int number = test1.getTreeNumber();
System.out.println(number);
System.out.println(test1.getTestName());
System.out.println(test1.getName());
}
}
小结2
- 内部类可以直接访问玩不累的成员,包括外部类的私有对象
- 外部类要访问内部类,必须创建对象
3.静态内部类
- 静态内部类的定义
- 静态内部类就是一个Java类,它可以访问外部类的静态成员和静态方法,不能访问外部类的实例成员和实例方法
- 静态类内部类的使用
- 创建静态内部类时,不需要先创建外部类的实例,直接使用 new Test.Test1(); 语法就可以创建一个实例
Test.Test1 test2 = new Test().Test1();
System.out.print(a.getName());
System.out.print(Test.Test1.getTreeType());
4.局部内部类
包含在外部类的函数中的内部类称之为局部内部类。
- 局部内部类只能访问所在函数的final属性
public class Demo {
String name = "外部类的name";
public void test(){
final int a=100;//只有声明为final 才能被局部内部类访问
int c = 10;
//局部内部类
class DemoSon{
int b = 10;
public void print(){
System.out.println("这是局部内部类的方法"+a);
System.out.println("内部类:b="+b);
System.out.println("内部类访问test:c="+c);
System.out.println("内部类访问test:name="+name);
}
}
DemoSon demoSon = new DemoSon();
demoSon.print();
System.out.println("test b="+demoSon.b);
}
}
//测试
public class Test3 {
public static void main(String[] args) {
Demo demo = new Demo();
demo.test();
System.out.println(demo.name);
}
}
结果:
这是局部内部类的方法100
内部类:b=10
内部类访问test:c=10
内部类访问test:name=外部类的name
test b=10
外部类的name
Process finished with exit code 0
匿名内部类
基于接口实现匿名内部类
public static void main(String[] args){
//=右边 是没有名字的类的对象的方法--给予接口实现匿名内部类
MyInterface myInterface3 =new MyInterface() {
@Override
public void test() {
System.out.println("怎么回事!");
}
};
myInterface3.test();
}
}
基于类实现匿名内部类
//抽象类
public abstract class Animal {
public abstract void run();
public abstract void sleep();
}
//测试
public class Demo {
public static void main(String[] args) {
Demo demo = new Demo();
Animal animal = new Animal() {
@Override
public void run() {
System.out.println("你在狗叫什么");
}
@Override
public void sleep() {
System.out.println("狗在睡觉");
}
};
animal.run();
animal.sleep();
}
}
结果
你在狗叫什么
狗在睡觉