包、访问修饰符、封装、继承、super关键字、方法重写和覆盖、多态
一、包
注意:有了import新建类时不用加包名,但不能同时import相同名字的类
可以在下面加包名来区分
包的命名
常用包
注意事项和细节
二、访问修饰符
访问范围和注意事项
- 同一个包不能访问私有的private
- 不同包只能访问公开的public
三、封装
好处
实现步骤
构造器和set方法结合
四、继承
带来的便利
继承的细节
注意:super()只能在构造器中的第一行
子类创建的内存布局
注意: 如果只有父类和爷爷类都有相同属性如age,但是父类有private修饰,子类输出age时会被父类的private限制而报错,不会去访问爷爷的age
习题:不要忘了隐藏的super()
super关键字
语法
好处和细节
// 父类Computer
public Computer(String cpu, int memory, int disk) {
this.cpu = cpu;
this.memory = memory;
this.disk = disk;
}
// 子类
public PC(String cpu, int memory, int disk, String brand) {
//下面两句实现了父类的属性由父类初始化,子类的属性由子类初始化
super(cpu, memory, disk);
this.brand = brand;
}
补充:
- super可以理解为指向父类的指针,this可以理解为指向当前对象的指针
- super和this都不能在static方法中使用
- 子类中super和this找不到属性和方法时都会往上面一层找
super和this的比较
注意:super和this调用的方法如果没有会往上面找
方法的重写和覆盖(override)
基本介绍
注意事项和使用细节
注意3
//细节2
class AAA {
public AAA a() {
return null;
}
}
class BBB extends AAA {
/*
//ok
public CCC a() {
return null;
}*/
/*
//no,String不是AAA的子类
public String a() {
return null;
}*/
}
class CCC extends AAA {
}
补充: 子类方法可以放大父类的访问权限
方法重写和方法重载的比较
多态
多态的介绍
多态的具体体现
方法的多态
- 通过重载,传入不同参数调用sum方法就会调用不同的方法
- 通过重写,不同对象(如父类中有say,子类中也有say)调用say方法就会调用不同的方法
对象的多态(核心,困难,重点)
左边是编译类型,右边是运行类型(左边有云)
案例:通过父类引用实现子类的共同功能,从而实现代码的复用
编译过程中能否编译由编译类型决定,运行过程中运行结果由运行类型决定
多态的注意事项和细节使用
多态的前提
两个对象(类)存在继承关系
向上转型
语法:
规则:能调用的方法看编译类型,运行结果看运行类型
案例中父类Animal,子类Cat
注意:
- 最终运行结果看子类的的具体实现,即调用方法时从子类向上查找
- 父类例如被private修饰,子类重写父类方法eat(),在Test类中,Animal animal = new Cat(); animal.eat()编译不通过,因为animal中的eat()具有private权限
向下转型
语法:把指向子类对象的父类引用重写变回子类引用指向子类对象,即变回编译类型和运行类型都是子类对象的类型
前提:虽然编译类型是Animal,但是animal的运行类型是Cat,所以animal可以强转回Cat,不能强转成Dog
多态的注意事项和细节
属性看编译类型,方法看运行类型
java的动态绑定机制(非常非常重要)
机制:调用方法时看运行类型,调用属性时哪里声明哪里使用,如果没有的话该向上找就向上找
如果B中的sum()注销,则去A中调用sum(),然后调用B中的getI(),使用B中的i=20,结果是30
如果B中的sum1()注销,则去A中调用sum1(),使用A中的i=10,结果是20
多态的应用
多态数组
package com.poly_.poly_arr;
public class Test {
public static void main(String[] args) {
Person smith = new Person("Smith", 28);
Person s1 = new Student("s1", 20, 202201, 88);
Person s2 = new Student("s2", 21, 202203, 99);
Person t1 = new Teacher("t1", 32, '男');
Person t2 = new Teacher("t2", 43, '女');
Person[] persons = new Person[5];
persons[0] = smith;
persons[1] = s1;
persons[2] = s2;
persons[3] = t1;
persons[4] = t2;
for (Person person : persons) {
//编译类型都是Person,运行类型由jvm根据实际情况判断
System.out.println(person.say());
if (person instanceof Student) {
//向下转型
Student student = (Student) person;
student.study();
} else if (person instanceof Teacher) {
Teacher teacher = (Teacher) person;
teacher.teach();
}
}
}
}
/*结果
我叫Smith我的年龄是28
我叫s1我的年龄是20我的ID是202201我的分数是88.0
我叫s2我的年龄是21我的ID是202203我的分数是99.0
我叫t1我的年龄是32我的性别是男
我叫t2我的年龄是43我的性别是女
*/
多态参数
主人喂动物
//Master类
public void feed(Animal animal, Food food) {
System.out.println("主人" + this.name + "正在给" + animal.getName() + "吃" + food.getName());
//Test类
Master smith = new Master("Smith");
Cat tom = new Cat("Tom");
Dog dog = new Dog("大黄");
Bone bone = new Bone("大棒骨");
Fish fish = new Fish("黄花鱼");
smith.feed(dog, bone);
smith.feed(tom, fish);
//说明:形参要的是父类,实参传的是子类
}
员工求工资
public static void main(String[] args) {
Employee e1 = new Worker("张三", 3200);
Employee e2 = new Manger("李四", 4500, 5000);
Test test = new Test();
test.showEmpAnnual(e1);
test.showEmpAnnual(e2);
test.testWorker(e2);
test.testWorker(e1);
}
public void showEmpAnnual(Employee e) {
System.out.println(e.getName() + "的年工资是:" + e.getAnnual());
}
public void testWorker(Employee e) {
if (e instanceof Worker) {
//向下转型
((Worker) e).work();
} else if (e instanceof Manger) {
((Manger) e).manage();
} else {
System.out.println("输入错误");
}
}