java冷知识:在Java中,子类数组的引用可以转换成父类数组的引用,而不需要采用强制类型转换。
1.多态
什么是多态?
同类型的对象,执行同一个行为,会表现出不同的行为特征
多态成员的访问特点:
- 方法调用:编译时看左边,运行时看右边
- 变量调用:编译、运行时都看左边
public abstract class Animal {
public String name="动物";
public abstract void run();
}
public class Dog extends Animal{
private String name="狗";
@Override
public void run() {
System.out.println("奔跑");
}
public static void main(String[] args) {
Animal animal = new Dog();
System.out.println(animal.name);
animal.run();
}
}
当我们使用多态时,父类在左边,右边是实现类。
调用成员变量时,输出的是父类变量。当调用方法时,需要父类中有这个方法,是由子类实现的。
多态好处:
- 右边对象可以实现解耦合,便于扩展和维护
- 定义方法时,使用父类类型作为参数,该方法就可以接收这父类的一起子类对象,体现多态的扩展性与便利
public static void main(String[] args) {
Animal animal = new Dog();
// System.out.println(animal.name);
// animal.run();
go(animal);
}
public static void go(Animal animal){
System.out.println("开始~");
animal.run();
System.out.println("结束~");
}
虽然传参是父类类型Animal,但是调用的方法确实子类Dog的,这又被成为方法回调。
但是不能调用子类中独有的方法。
在上面我们说过,调用方法时,编译看左边,运行看右边。左边父类中如果没有定义该方法,那么在编译期间便会报错。
类型转换问题:
- 自动类型转换(从子到父):子类对象赋值给父类类型的变量指向
- 强制类型转换(从父到子):此时必须进行强制类型转换,子类 对象 = (子类)父类类型。
原因:父类的范围大,子类范围小,小范围转大范围不需要强转,大范围转小范围需要强转。
public static void main(String[] args) {
//子类转父类
Animal animal = new Dog();
//父类转子类
Dog dog = (Dog) animal;
}
注意:有继承或者实现关系,编译阶段可以强转,运行时可能报错。
public static void main(String[] args) {
//子类转父类
Animal animal = new Dog();
//父类转子类
Cat cat = (Cat) animal;
}
虽然编译期间不会报错,但是运行时会报错ClassCastException。
当我们在转换之前,使用instanceof判断当前对象的真实类型,在进行强转。
public static void main(String[] args) {
//子类转父类
Animal animal = new Dog();
//父类转子类
if(animal instanceof Dog){
Dog dog = (Dog) animal;
}else if(animal instanceof Cat){
Cat cat = (Cat) animal;
}
}
2.内部类
使用场景及作用
- 一个事物的内部有一部分需要一个完整的结构来进行描述,而这个内部完整的结构又只为了外部事物提供服务,那么整个内部的完整结构可以选择使用内部类来设计
- 内部类可以直接访问外部类的静态成员,不能直接访问外部类的实例成员
- 内部类提供了更好的封装性,内部类本身就可以用private protected等修饰,封装性可以做更多控制
1.静态内部类(了解)
由static修饰,与普通类用法相同。
public class People {
public static class Heart{
private double width;
private double height;
public double getWidth() {
return width;
}
public void setWidth(double width) {
this.width = width;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
}
}
创建方法
外部类名.内部类名 变量名 = new 外部类名.内部类名
public static void main(String[] args) {
People.Heart heart = new People.Heart();
}
静态内部类不可以直接访问外部类的实例对象。
2.成员内部类(了解)
创建方法
外部类名.内部类名 变量名 = new 外部类名().new 内部类名
public static void main(String[] args) {
People.Heart heart = new People().new Heart();
}
注意:
- 成员内部类可以直接访问外部类静态成员
- 成员内部类的实例方法可以直接访问外部类的实例成员(必须先有外部类,才能有成员内部类)
成员内部类可以通过外部类名去调用外部类的成员变量
public class People {
private int h = 666;
public class Heart{
private int h = 888;
private double width;
private double height;
public void show(){
int h = 999;
System.out.println(h);
System.out.println(this.h);
System.out.println(People.this.h);
}
public double getWidth() {
return width;
}
public void setWidth(double width) {
this.width = width;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
}
}
public static void main(String[] args) {
People.Heart heart = new People().new Heart();
heart.show();
}
3.局部内部类(了解,几乎不会用到)
- 局部内部类放在方法、代码块、构造器等执行体中
- 局部内部类的类文件名为:外部类$N内部类.class
随用随消失,使用范围在main函数内
public static void main(String[] args) {
class Eye{
int dre;
}
Eye eye = new Eye();
}
4.匿名内部类(重要)
作用:方便创建子类对象,最终目的为了简化代码编写
特点:
- 匿名内部类是一个没有名字的内部类
- 匿名内部类写出来就会产生一个匿名内部类对象
- 匿名内部类的对象类型相当于是当前new的内个类型的子类
举例子:
栗子1
public abstract class Animal {
public abstract void run();
}
class t1{
public static void main(String[] args) {
Animal animal = new Animal() {
@Override
public void run() {
System.out.println("我是匿名内部类~~");
}
};
animal.run();
}
}
栗子2
public abstract class Animal {
public abstract void run();
}
class t1{
public static void main(String[] args) {
new Animal() {
@Override
public void run() {
System.out.println("我是匿名内部类~~");
}
}.run();
}
}