一、多态
多态是继封装、继承之后,面向对象的第三大特性。
1.概述
封装—>面向对象—>继承、多态
多态是出现在继承或者实现关系中的。
同种类型的对象,表现出的不同形态
eg:
Student s = new Student();
//学生形态 对象
Person p = new Student();
//人的形态 对象
父类类型 变量名 = new 子类/实现类构造器;
变量名.方法名();
前提:
(1)有继承关系
(2)有父类引用指向子类对象(Person p = new Student();)
(3)有方法重写
2.应用场景
Teacher,Student,Administrator
关于register(),里面的参数到底写谁?
可以写他们共同的父类Person
public void register(Person p){
}//注册学生时,相当于Person p = new Student
3.多态的好处和弊端
好处
(1)使用父类型作为参数,可以接收所有子类对象
(2)体现多态的扩展性与便利
弊端
不能调用子类特有的功能(父类里没有的方法),在编译时会先检查父类里有没有该方法,如果没有会报错
解决:调用者a变回子类类型即可
Dog d = (Dog) a;
为了避免ClassCastException的发生,Java提供了 instanceof
关键字,给引用变量做类型的校验,格式如下:
变量名 instanceof 数据类型
如果变量属于该数据类型或者其子类类型,返回true。
如果变量不属于该数据类型或者其子类类型,返回false。
if (a instanceof Cat){
Cat c = (Cat)a;
c.catchMouse(); // 调用的是 Cat 的 catchMouse
} else if (a instanceof Dog){
Dog d = (Dog)a;
d.watchHouse(); // 调用的是 Dog 的 watchHouse
}
JDK14的时候提出了新特性,把判断和强转合并成了一行
//新特性
//先判断a是否为Dog类型,如果是,则强转成Dog类型,转换之后变量名为d
//如果不是,则不强转,结果直接是false
if(a instanceof Dog d){
d.lookHome();
}else if(a instanceof Cat c){
c.catchMouse();
}else{
System.out.println("没有这个类型,无法转换");
}
4.多态调用成员的特点
Animal a = new Dog();
sout(a);
a.show();
(1)变量调用
编译看左边,运行看左边
<1>javac编译代码时,会看左边父类中有没有该变量,如果有,编译成功,否则不成功;
在子类对象中,会把父类成员变量也继承。父:name 子:name
<2>java运行代码时,实际获取的是左边父类成员变量中的值
(2)方法调用
编译看左边,运行看右边
<1>javac编译代码时,会看左边父类中有没有该方法,如果有,编译成功,否则不成功;
<2>java运行代码时,实际获取的是右边子类成员方法中的内容
如果子类对方法进行了重写,虚方法表中会把父类的方法进行覆盖
(3)内存解释
加载字节码顺序:main()—>Object—>Animal—>Dog
Animal a = new Dog();在堆内存开辟空间,一分为二,左边存储父类name = “动物”,右边存储子类name = “狗”,地址值赋给a;sout(a),看父类里有没有name,有的话输出。如果是Dog d = new Dog(),先看右边有没有name,有就输出,没有去父类里找。
二、包
1.什么是包
包就是文件夹,用来管理不同功能的Java类,方便后期代码维护。
包的命名规则:公司域名反写+包的作用,需要全部小写
2.使用其他类的规则
(1)使用同一个包中的类时,不需要导包
(2)使用java.lang包中的类时,不需要导包
(3)其他情况都需要导包
(4)如果同时使用两个包中的同名类,需要用全类名
//使用全类名的形式即可。
//全类名:包名 + 类名
//拷贝全类名的快捷键:选中类名crtl + shift + alt + c 或者用鼠标点copy,再点击copy Reference
com.itheima.homework.demo1.Student s1 = new com.itheima.homework.demo1.Student();
com.itheima.homework.demo2.Student s2 = new com.itheima.homework.demo2.Student();
三、final
1.可以修饰方法、类、变量
(1)修饰方法:表明该方法是最终方法,不能被重写(例如方法是一种规则,不希望被重写)
(2)修饰类:最终类,不能被继承
(3)修饰变量:叫做常量,只能被赋值一次
2.常量
命名规则
单个单词:全部大写
多个单词:全部大写,单词间用下划线隔开
3.final的细节
final修饰的变量是基本类型:变量存储的数据值不能发生变化
final修饰的变量是引用类型:变量存储的地址值不能发生变化,对象内部可以改变
new一个对象s,s的地址值不会变化,但s.setName(“王一博”)是不会报错,内部的属性值是可以修改的。
final int[] arr = {1,2,3,4};
arr[0] = 10;
arr[1] = 20;//不会报错
数组的地址值不能改变,但每个数组元素的值是可以修改的
—>字符串是不可变的, 在存储字符串时,实际是
private final byte[] value这样一个数组存储的,因此字符串无法改变
四、权限修饰符
1.概述
用来控制一个成员能够被访问的范围
可以修饰成员变量、方法、构造方法、内部类
2.分类
作用范围由小到大
private—>空着不写—>protected—>public
public | protected | 默认 | private | |
---|---|---|---|---|
同一类中 | √ | √ | √ | √ |
同一包中的类 | √ | √ | √ | |
不同包的子类 | √ | √ | ||
不同包中的无关类 | √ | |||
实际开发中,一般只用private,public |
五、代码块
1.局部代码块
写在方法一对 { } 里的代码,提前结束变量作用范围,节约内存
2.构造代码块
写在成员变量位置的代码块
public class Student{
private String name;
{
sout("开始创建对象了!");
}
public Student(){}
public Student(String name){
this,name = name;
}
}
把多个构造方法里重复的代码写到构造代码块里,先执行构造代码块,再执行构造方法
如果多个构造方法同时执行一句话,可以通过this()实现,即上述代码修改如下:
public class Student{
private String name;
public Student(){
this(null);//调用本类其他构造
}
public Student(String name){
this,name = name;
}
}
3.静态代码块
(1)格式
static{}
(2)特点
需要通过static关键字修饰,随着类的加载而加载,并且自动触发,只会执行一次
(3)使用场景
程序刚开始时做一些数据初始化