斜体样式1.1 面向对象三大特征 ?
- 封装 , 继承 , 多态
1.2 什么是多态 ?斜体样式
- 一个对象在不同时刻体现出来的不同形态
- 举例 : 一只猫对象
我们可以说猫就是猫 : Cat cat = new Cat();
我们也可以说猫是动物 : Animal cat = new Cat();
这里对象在不同时刻,体现出来的不同形态 , 我们就可以理解为多态
1.3 多态的前提
- 有继承/实现关系
- 有方法重写
- 父类的引用指向子类的对象
/* 多态的三个前提条件 1 需要有继承/实现关系 2 需要有方法重写 3 父类的引用指向子类的对象 */ public class AnimalTest { public static void main(String[] args) { // 3 父类的引用指向子类的对象 // 多态形式对象 Animal a = new Cat(); } } class Animal{ public void eat(){ System.out.println("吃东西"); } } class Cat extends Animal{ @Override public void eat() { System.out.println("猫吃鱼...."); } }
1.4 多态的成员访问特点
- 构造方法 : 和继承一样 , 子类通过super()访问父类的构造方法
- 成员变量 : 编译看左边(父类) , 执行看左边(父类)
- 成员方法 : 编译看左边(父类) , 执行看右边(子类)
/* 多态的成员访问特点 : 1 构造方法 : 和继承一样 , 都是通过super()访问父类的构造方法 2 成员变量 : 编译看左边(父类) , 执行看左边(父类) 3 成员方法 : 编译看左边(父类) , 执行看右边(子类) , 注意 , 如果执行时 1) 子类没有回动态去找父类中的方法 2) 子类的特有方法无法进行调用(多态的缺点) */ public class MemberTest { public static void main(String[] args) { // 父类的引用指向子类的对象 Fu f = new Zi(); // 多态对象调用成员变量 System.out.println(f.num); // 多态对新乡调用调用成员方法 f.show(); // 多态对象不能调用子类特有的方法 // f.show2(); } } class Fu { int num = 100; public void show() { System.out.println("父类的show方法"); } } class Zi extends Fu { int num = 10; public void show() { System.out.println("子类的show方法"); } public void show2(){ System.out.println("子类特有的方法"); } }
1.5 多态的优缺点
- 优点 : 提高代码的扩展性
- 缺点 : 不能调用子类特有的功能
public abstract class Animal { private String breed; private String color; public Animal() { } public Animal(String breed, String color) { this.breed = breed; this.color = color; } public String getBreed() { return breed; } public void setBreed(String breed) { this.breed = breed; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } public abstract void eat(); }
public class Cat extends Animal { public Cat() { } public Cat(String breed, String color) { super(breed, color); } @Override public void eat() { System.out.println("猫吃鱼..."); } public void catchMouse() { System.out.println("抓老鼠..."); } }
public class Dog extends Animal { public Dog() { } public Dog(String breed, String color) { super(breed, color); } @Override public void eat() { System.out.println("狗吃骨头!"); } public void lookDoor(){ System.out.println("狗看门..."); } }
public class Pig extends Animal { public Pig() { } public Pig(String breed, String color) { super(breed, color); } @Override public void eat() { System.out.println("猪拱白菜..."); } public void sleep() { System.out.println("一直再睡..."); } }
/* 如果方法的参数是一个类的话 , 那么调用此方法需要传入此类的对象 , 或者子类对象 多态的好处 : 提高代码的扩展性 , 灵活性 多态的缺点: 不能调用子类的特有功能 */ public class AnimalTest { public static void main(String[] args) { useAnimal(new Cat()); System.out.println("---------"); useAnimal(new Dog()); System.out.println("---------"); useAnimal(new Pig()); } public static void useAnimal(Animal a){// Animal a = new Dog() a.eat(); // 多态不能访问子类特有的功能 // 如果解决 ? // 向下转型 if(a instanceof Cat) { Cat cat = (Cat) a; cat.catchMouse(); } if(a instanceof Dog) { Dog dog = (Dog) a; dog.lookDoor(); } if(a instanceof Pig) { ((Pig) a).sleep(); } } // // 定义一个使用猫类的方法 // public static void useAnimal(Cat c) {// Cat c = new Cat(); // c.eat(); // c.catchMouse(); // } // // // 定义一个使用狗类的方法 // public static void useAnimal(Dog d) {// Dog d = new Dog(); // d.eat(); // d.lookDoor(); // } // // // 定义一个使用猪类的方法 // public static void useAnimal(Pig pig) { // pig.eat(); // pig.sleep(); // } }
1.6 多态的转型
- 向上转型 : 把子类类型数据转成父类类型数据 Animal a = new Cat();
- 向下转型 : 把父类类型数据转成子类类型数据 Cat cat = (Cat)a;
1.7 多态的转型注意
- 如果被转的对象 , 对应的实际类型和目标类型不是同一种数据类型 , 那么转换时会出现ClassCastException异常
异常代码如下 public static void main(String[] args) { Animal a = new Cat(); useAnimal(a); } public static void useAnimal(Animal a) { Dog d = (Dog) a; d.eat(); }
1.8 解决转型安全隐患
使用关键字 instanceof
作用 : 判断一个对象是否属于一种引用数据类型
格式 : 对象名 instanceof 引用数据类型
通俗的理解:判断关键字左边的变量,是否是右边的类型,返回boolean类型结果
2 内部类
2.1 内部类的分类
什么是内部类 ?
- 一个A类 中 定义一个B类 , 那么B类就属于A类的内部类 , A类就属于B类的外部类
什么时候使用内部类 ?
- 多个事物之间有包含关系, 可以使用内部类
内部类分类 ?
- 成员内部类
- 局部内部类
- 匿名内部类
2.2 成员内部类
- 定义的位置 : 类中方法外
- 创建成员内部类对象格式 : 外部类名.内部类名 对象名 = new 外部类名().new 内部类名(参数);
// 外部类 public class Person { // 成员内部类 public class Heart { // 频率变量 private int rate; // 跳动方法 public void beats() { System.out.println("咚咚咚!"); } } } class Test { public static void main(String[] args) { // 创建内部类对象 Person.Heart heart = new Person().new Heart(); // 调用内部类中的方法 heart.beats(); } }
成员内部类访问外部类的成员
- 在内部类中有代表外部类对象的格式 : 外部类名的.this , 私有的也可以访问
- 外部类要想访问内部类成员 , 需要创建内部类对象
public class Person { private String name = "张三"; private int num = 10; // 成员内部类 public class Heart { int num = 100; // 频率 private int rate; // 跳动 public void beats() { System.out.println("咚咚咚!"); } // 调用外部类的成员 public void show(){ int num = 1000; System.out.println(Person.this.name); System.out.println(num);// 1000 就近原则 System.out.println(this.num);// 100 System.out.println(Person.this.num);// 10 } } } class Test { public static void main(String[] args) { Person.Heart heart = new Person().new Heart(); heart.beats(); heart.show(); } }
2.3 匿名内部类
- 匿名内部类 : 没有名字的类 , 一次性产品
- 使用场景 : 直接调用方法 , 作为方法的传参 , 返回值类型
- 好处 : 简化代码 , 快速实现接口或者抽象的抽象方法
- 格式 :
new 类名/接口名(){ 重写抽象方法 } 注意 : 此处创建的是子类对象!!!
- 使用方式 :
直接调用方法
作为方法的参数传递
作为方法的返回值类型
//接口 interface Flyable { void fly(); }
// 直接调用方法 Flyable f1 = new Flyable() { @Override public void fly() { System.out.println("不知道什么在飞....."); } }; f1.fly();
// 作为方法的参数传递 showFlyable( new Flyable() { @Override public void fly() { System.out.println("不知道什么在飞3333"); } } ); public static void showFlyable(Flyable flyable) { flyable.fly(); }
// 作为方法的返回值类型 public static Flyable getFlyable() { return new Flyable() { @Override public void fly() { System.out.println("3333333333333"); } }; }
/* 1 如果方法的参数是一个类的话 , 调用此方法需要传入此类的对象或者此类的子类对象 2 如果方法的返回值类型是一个类的话 , 需要返回此类的对象 , 或者此类的子类对象 3 如果方法的参数是一个接口的话 , 调用此方法需要传入此接口的实现类对象 4 如果方法的返回值类型是一个接口的话 , 需要返回此接口的实现类对象 匿名内部类 : 代表的就是子类对象!!! new 类名/接口名(){ 重写抽象类或者接口中的抽象方法 }; 使用方向 : 1 调用方法 2 作为方法参数传递 3 作为方法的返回值 */ public interface Swim { public abstract void swimming(); } class Test { public static void main(String[] args) { // // 子类对象!!! // 1 调用方法 // new Swim() { // @Override // public void swimming() { // System.out.println("匿名内部类 , 重写了接口中的抽象方法..."); // } // }.swimming(); // // 2 作为方法参数传递 // useSwim(new Swim() { // @Override // public void swimming() { // System.out.println("匿名内部类 , 重写了接口中的抽象方法..."); // } // }); // // 3 作为方法的返回值 // Swim s = getSwim(); // s.swimming(); } public static Swim getSwim() { return new Swim() { @Override public void swimming() { System.out.println("匿名内部类 , 重写了接口中的抽象方法..."); } }; } /* Swim swim = new Swim() { @Override public void swimming() { System.out.println("匿名内部类 , 重写了接口中的抽象方法..."); } }; */ public static void useSwim(Swim swim) { swim.swimming(); } }