一、多态
概念:父类引用指向子类对象,从而产生多种形态。 继承+重写
二者具有直接或间接的继承关系时,父类引用可指向子类对象,即形成多态。
父类引用仅可调用父类所声明的属性和方法,不可调用子类独有的属性和方法。
class Animal{
String breed;
int age;
String sex;
public void eat() {}
public void sleep() {}
}
class Dog extends Animal{
String furColor;
public void run() {}
}
public static void main(String[] args) {
//父类引用 子类对象
//引用类型 对象类型
Animal a = new Dog();//将狗对象,当成动物来看待
}
编译看 = 左边, 运行看 = 右边
对象能点出什么方法看父类类型,执行什么方法看子类类型
多态的应用:
方法重载可以解决接收不同对象参数的问题,但其缺点也比较明显。
- 首先,随着子类的增加,父类需要继续提供大量的方法重载,多次修改并重新编译源文件。
- 其次,每一个父类的方法与某一种具体类型形成了密不可分的关系,耦合太高。
场景一:使用父类作为方法形参实现多态,使方法参数的类型更为宽泛。
场景二:使用父类作为方法返回值实现多态,使方法可以返回不同子类对象。
装箱和拆箱:
向上转型(装箱):
父类引用中保存真实子类对象,称为向上转型(即多态核心概念)。
注意: 仅可调用Animal中所声明的属性和方法。
class Animal{
public void eat(){
system.out.println("动物在吃..")
}
}
class Dog extends Animal{
public void eat(){
system.out.println("狗狗在吃..")
}
}
public static void main(String[] args) {
//父类引用 子类对象
//引用类型 对象类型
Animal a = new Dog();// 父类引用中保存真实子类对象,称为向上转型(即多态核心概念)
}
向下转型(拆箱):
将父类引用中的真实子类对象,强转回子类本身类型,称为向下转型。
注意:只有转换回子类真实类型,才可调用子类独有的属性和方法。
class Animal{
public void eat(){
system.out.println("动物在吃..")
}
}
class Dog extends Animal{
public void eat(){
system.out.println("狗狗在吃..")
}
}
public static void main(String[] args) {
//父类引用 子类对象
//引用类型 对象类型
Animal a = new Dog();
Dog dog = (Dog)a; // 将父类引用中的真实子类对象,强转回子类本身类型,称为向下转型。
}
类型转换异常:
向下转型时,如果父类引用中的子类对象类型和目标类型不匹配,则会发生类型转换异常。
instance of关键字:
向下转型前,应判断引用中的对象真实类型,保证类型转换的正确性。
语法:父类引用 instance of 类型 //返回boolean类型结果
二、方法的重写
当父类提供的方法无法满足子类需求时,可在子类中定义和父类相同的方法进行重写(Override)。
方法重写原则:
- 方法名称、参数列表、返回值类型必须与父类相同。
- 访问修饰符可与父类相同或是比父类更宽泛。
访问权限可以放大
私有的方法不可以重写
返回值类型:
可以一致或者是其子类 – 子类的功能更加强大
方法名相同
参数相同
方法重写的执行:
- 子类重写父类方法后,调用时优先执行子类重写后的方法。
多态中的方法重写
思考:如果子类中重写了父类中的方法,以父类类型引用调用此方法时,优先执行父类中的方法还是子类中的方法?
实际运行过程中,依旧遵循重写原则,如果子类重写了父类中的方法,执行子类中重写后的方法,否则执行父类中的方法。
三、静态 - static
概念:
- 静态(static)可以修饰属性和方法。
- 称为静态属性(类属性)、静态方法(类方法)。
- 静态成员是全类所有对象共享的成员。
- 在全类中只有一份,不因创建多个对象而产生多份。
- 不必创建对象,可直接通过类名访问。
3.1 静态属性
实例属性与静态属性:
实例属性:每个对象各自持有的独立空间(多份),对象单方面修改,不会影响其他对象。
public class Test{
public static void main(String[] args){
Class c1 = new Class();
Class c2 = new Class();
c1.a = 10;
c2.a = 20;
System.out.println(c1.a + " " + c2.a);
// 结果为 10 20
}
}
class Class{
int a;// 实例属性
}
静态属性:整个类共同持有的共享空间(一份),任何对象修改,都会影响其他对象。
public class Test{
public static void main(String[] args){
Class c1 = new Class();
Class c2 = new Class();
c1.a = 10;
c2.a = 20;
System.out.println(c1.a + " " + c2.a);
// 结果为 20 20
}
}
class Class{
static int a;// 静态属性
}
3.2 静态方法
static修饰的方法叫静态方法
特点:
- 静态方法允许直接访问静态成员。
- 静态方法不能直接访问非静态成员。
- 静态方法中不允许使用this或是super关键字。
- 静态方法可以继承,不能重写、没有多态。
3.2.1 动态代码块
创建对象时,触发动态代码块的执行,
执行地位:初始化属性之后,构造方法代码之前。
作用:可为实例属性赋值,或必要的初始行为
3.2.2 静态代码块
3.2.3 类加载
JVM首次使用某个类时,需通过CLASSPATH查找该类的.class文件。
- 将.class文件中对类的描述信息加载到内存中,进行保存。
- 如:包名、类名、父类、属性、方法、构造方法…
- 加载时机:
- 创建对象。
- 创建子类对象。
- 访问静态属性。
- 调用静态方法。
- 主动加载:Class.forName(“全限定名”);
四、final
4.1 final类
- final修饰类:此类不能被继承。
- String、Math、System均为final修饰的类,不能被继承。
- final修饰方法:此方法不能被覆盖。
- 意为最终方法,不支持子类以覆盖的形式修改。
4.2 final变量
final修饰变量:此变量值不能被改变(常量)。
所有final修饰的变量只能赋值一次,值不允许改变。
4.3 实例常量
- 实例常量不再提供默认值,必须手动赋予初始值。
- 赋值时机:显示初始化、动态代码块、构造方法。
- 注意:如果在构造方法中为实例常量赋值,必须保证所有的构造方法都能对其正确赋值。
4.4 静态常量
- 静态常量不再提供默认值,必须手动赋予初始值。
- 赋值时机:显示初始化、静态代码块。
4.5 对象常量
public class Test{
public static void main(String[] args){
final int num = 100;
num += 10; // final修饰基本数据类型: 值不可变
final int[] nums = new int[]{1,2,3,4};
nums = new int[5];
final Student s = new Student();
s = new Student(); // final修饰引用数据类型(数组,类等等):地址不可变
}
}
class Stundet{
String name;
}