多态:是指同⼀⾏为,具有多个不同表现形式 。它是继封装、继承之后,⾯向对象的第三⼤特性。
⽣活中,⽐如跑的动作,⼩猫、⼩狗和⼤象,跑起来是不⼀样的。再⽐如⻜的动作,昆⾍、⻦类和⻜机,⻜起来也是不⼀样的。可⻅,同⼀⾏为,通过不同的事物,可以体现出来的不同的形态。多态,描述的就是这样的状态。
父类型引用 指向 子类型对象 -> 向上造型
父类型 a = new 子类型对象();
接口类型 a = new 实现类对象();
public class Fu {
int age = 10;
int num = 100;
public void m1() {
System.out.println("Fu m1方法");
}
public void m2() {
System.out.println("Fu m2方法");
}
public int getAge() {
return this.age;
}
public void setAge(int age) {
this.age = age;
}
}
public class Zi extends Fu {
// 和父类中同名的成员变量
int age = 20;
// 和父类中不同的成员变量 - 子类自己单独声明的
int score = 150;
// 额外继承了父类中的num变量
// 重写父类中的m1方法
@Override
public void m1() {
System.out.println("Zi m1方法");
}
// 继承了父类的m2方法
// 子类自己的方法 m3
public void m3() {
System.out.println("Zi m3方法");
}
@Override
public int getAge() {
return age;
}
}
public class Main {
public static void main(String[] args) {
// 父类型引用 指向 子类型对象
Fu f = new Zi();
public class Main1 {
public static void main(String[] args) {
Fu f = new Zi();
System.out.println(f.age); // 10
// 子类中重写了getAge()方法, 得到20,若没有重写getAge()方法, 得到10
System.out.println(f.getAge()); // 20
// Fu类中没有声明score变量, 不能直接调用
// System.out.println(f.score);
// int a = (int)3.14; double d = 3;
// 向下转换类型
Zi zi = (Zi) f;
System.out.println(zi.score);
public class Main2 {
public static void main(String[] args) {
Fu f = new Zi();
f.m1();//输出:“Zi m1方法”
// m3没有在Fu中声明, 所以不能直接调用
// f.m3();
// 如果想用子类自己的方法, 需要强制转换
Zi zi = (Zi) f;
zi.m3();
}
}
左边类型决定了变量 能调用哪些方法,
右边类型决定了最终使用的方法是哪个 - 优先调用自己重写过的
编译器 看 = 左边类型, 运行时JVM 看 = 右边类型
口诀: 编译看左边, 运行看右边
ClassCastException: 类型转换异常
解决方案: 转换之前先判断引用 实际上是不是要转换的类型对象
引用a instanceof 子类型 => boolean
问 a 实际上是不是子类型对象,返回true/false
public abstract class Animal {
public abstract void eat();
}
public class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗吃骨头!");
}
public void watchHouse() {
System.out.println("狗看家!");
}
}
public class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃鱼!");
}
public void catchMouse() {
System.out.println("猫和老鼠的故事!");
}
}
public class Main {
public static void main(String[] args) {
Animal d = new Dog();
d.eat();
Animal c = new Cat();
c.eat();
// 想要调用cat中的catchMouse方法,向下转型
Cat cat = (Cat) c;
cat.catchMouse();
// 编译器看c是Animal类型, 和Dog是父子关系, 所以语法通过
// 运行时, 发现c实际上是Cat, 不能转换成Dog, 提示ClassCastException
if(c instanceof Dog) {
Dog dog = (Dog) c;
dog.watchHouse();
}
giveMePet(new Dog());
giveMePet(new Cat());
}
// Animal作为返回值类型, 这个方法就可以返回多种不同种类的对象
public Animal salePet() {
return null;
}
// Animal作为参数, 可以传递多种类型的对象
public static void giveMePet(Animal a){
if (a instanceof Dog) {
((Dog) a).watchHouse();
} else if (a instanceof Cat) {
((Cat) a).catchMouse();
}
a.eat();
}
/*public static void giveMePet(Dog d) {
d.watchHouse();
d.eat();
}*/
/*public static void giveMePet(Cat d) {
d.catchMouse();
d.eat();
}*/
}
类中写个类
人体类 中有 心脏类
车类 中有 发动机类
种类: 成员内部类,静态内部类,局部内部类[匿名内部类]
成员内部类: 权限 public,protected,(default),private
语法:
public class Outer{
// …
public class Inner{
//…
}
}
内部类中, 可以随意使用外部类成员
外部类中, 使用内部类成员时需要创建内部类对象
无关类中, 使用内部类成员
1.间接调用, 在外部类方法中调用内部类方法
main方法中, 调用外部类方法
2.直接调用, 需要创建内部类对象
外部类.内部类 a = new 外部类().new 内部类();
内部类和外部类有同名成员变量
外部类的成员变量: 外部类.this.变量
内部类的成员变量: this.变量
外部类名.内部类名 对象名 = new 外部类型().new 内部类型();
局部内部类: 在方法内部定义类, 只在方法中有效
不能使用权限修饰符
在局部内部类中, 使用局部变量, 必须是final的
JDK8 后, 局部变量事实上没有发生改变, 那么final可以省略
一次性的类, 只能用一次
局部内部类, 需要类只使用一次, 优化 -> 匿名内部类
继承抽象类或实现接口
语法: new 接口/抽象类() {
int a;
// 实现的所有抽象方法
}
匿名内部类, 只能用一次, new一个对象
匿名对象, 只能使用一次, 只能调用一次方法
注意: 匿名内部类 和 匿名对象 不是一回事 ! ! !
静态内部类 对比 成员内部类 => 静态方法 和 成员方法