一、封装encapsulation
封装:把抽象出的数据(属性)和对数据的操作(方法)封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作,才能对数据进行操作。
- 隐藏实现细节
- 对数据进行验证,保证安全合理
1.封装的实现
- 将属性进行私有化,使其不能被直接修改
- 提供一个公共的set方法,用于对该属性赋值,且判断赋值是否合理
- 提供一个公共的get方法,用于获取属性的值
2.代码示例
新建com.aroma包,在包下新建 Person类
java
package com.aroma;
public class Person {
public String name;
private int age;
private double salary;
private String job;
public Person() {
}
public Person(String name, int age, double salary, String job) {
this.setName(name);
this.setAge(age);
this.setSalary(salary);
this.setJob(job);
}
public String getName() {
return name;
}
public void setName(String name) {
if (name.length() >=2 && name.length() <= 6) this.name = name;
else{
System.out.println("名字过长或过短,使用默认名字:无名客");
this.name = "无名客";
}
}
public void setAge(int age) {
if (age <= 0 || age > 120){
System.out.println("年龄不合理,使用默认年龄:18");
this.age = 18;
}
else this.age = age;
}
public void setSalary(double salary) {
this.salary = salary;
}
public void setJob(String job) {
this.job = job;
}
public void showInfo() {
System.out.println("姓名:" + this.name + ",年龄:" + this.age + ",薪水:" + this.salary + ",工作:" + this.job);
}
}
在Main.java里测试:
java
import com.aroma.Person;
public class Main {
public static void main(String[] args) {
Person person = new Person("aroma", 121, 10000, "IT");
person.showInfo();
}
}
结果如下:

二、继承extends
当多个类存在相同的属性和方法时,抽象一个父类,在该父类中定义这些相同的属性和方法,其所有子类不再需要重新定义这些属性和方法。
- ctrl+H可以查看类的层级关系
java
class subClass extends parentClass {}
- 子类subClass会自动拥有父类定义的属性和方法
- 父类,又名 超类、基类
- 子类,又名:派生类
- 继承可以解决代码复用
- 子类继承父类所有的属性和方法,但子类无法访问父类的私有private属性,可以通过公共的方法getXXX()来访问
- 子类必须调用父类的构造器,完成父类的初始化
1.注意事项
- 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会调用父类的无参构造器。
- 若父类没有提供无参构造器(意思就是父类写了有参构造器,但是没有写无参构造器,而此时无参构造器已经被覆盖了,相当于没有无参构造器了),则必须在子类的构造器中用【super】去 指定使用父类的所拥有的构造器 来 完成对父类的初始化工作,否则编译不会通过。
super(指定父类构造器的参数列表);
必须放在子类构造器的第一行this();
也只能放在第一行【this();用于调用本类的其他构造器】- 所以,super和this不能放在同一个构造器中
- Object类是java所有类的基类,反过来就是java所有类都是Object类的子类
- 子类最多只能继承一个父类(指直接继承),即 Java中是单继承机制
2.super关键字
super代表父类的引用,用于访问父类的属性、方法和构造器。
super.属性名
:访问父类的属性,但不能访问父类的private属性super.方法名(参数列表)
:访问父类的方法,但不能访问父类的private方法super(参数列表);
:访问父类的构造器。该句只能放在构造器里的第一行,且只能出现一句。
- super的访问不限于 直接父类,若爷爷类和本类中有同名的成员(属性和方法),也可以通过super去访问爷爷类的成员;如果多个基类中都有同名的成员,使用supper遵循就近原则。
区别点 | this | super |
---|---|---|
访问属性 | 访问本类中的属性,如果本类没有此属性则从父类中继续查找 | 访问父类中的属性 |
调用方法 | 访问本类中的方法,如果本类没有此方法则从父类中继续查找 | 访问父类中的方法 |
调用构造器 | 调用本类构造器,必须放在构造器的第一行 | 调用父类构造器,必须放在子类构造器的第一行 |
特殊 | 表示当前对象 | 子类中访问父类对象 |
3.方法重写/覆盖Override
- 子类的方法的 【形参列表,方法名称】 和父类方法的【形参列表,方法名称】完全一样
- 子类方法的返回类型和父类返回类型一样,或父类返回类型的子类。
例:父类返回类型是Object,子类返回类型是String
- 子类方法不能缩小父类方法的访问权限(public > protected > 默认 > private)
名称 | 发生范围 | 方法名 | 参数列表 | 返回类型 | 修饰符 |
---|---|---|---|---|---|
重载overload | 同类 | 必须相同 | 返回类型、个数或顺序至少一个不同 | 无要求 | 无要求 |
重写override | 父子类 | 必须相同 | 必须相同 | 相同或子类返回类型是父类的子类 | 子类修饰符不能比父类小 |
三、多态polymorphic
- 为解决"代码复用性不高,不利于代码维护",提出【多态】
- 多态:方法或对象具有多种形态,多态是建立在封装和继承基础上的。
- 方法的多态:重写、重载
1.对象的多态
- 一个对象的编译类型和运行类型可以不一致
- 编译类型在定义对象时确定,不能更改
- 运行类型是可以变化的
- 编译类型:定义对象时"="左边;运行类型:定义对象时"="右边
java
Animal animal = new Dog(); // 编译类型是Animal,运行类型是Dog
animal = new Cat(); // 运行类型变成Cat,但编译类型仍是Animal
2.注意事项
- 多态的前提:两个对象(类)存在继承关系
- 属性没有重写之说,属性的值看编译类型
- instanceOf(比较操作符):用于判断对象的运行类型是否为XX类型或XX类型的子类
3.多态的向上转型
- 本质:父类的引用指向子类的对象
- 语法:
父类类型 引用名 = new 子类类型();
- 编译类型:父类类型;运行类型:子类类型
- 特点:可以调用父类中的所有成员(须遵循访问权限)、不能调用子类中的特有成员、最终运行效果看子类的具体实现。
- 调用规则:
1)调用方法先在子类类型中查找,若没有需要的方法,则在父类类型里查找;
2)调用属性是看编译类型(父类)
4.多态的向下转型
- 语法:
子类类型 引用名 = (子类类型) 父类引用;
- 要求父类引用必须指向的是当前目标类型的对象
- 编译类型:子类类型;运行类型:子类类型
- 只能强转父类的引用,不能强转父类的对象
- 当向下转型后,可以调用子类类型中所有的成员
⭐5.动态绑定机制
动态绑定机制(Dynamic Binding)
- 当调用对象【方法】时,该方法会和 该对象的内存地址/运行类型 绑定
- 当调用对象【属性】时,没有动态绑定机制,哪里声明,哪里使用
A a = new B();
定义对象a,其编译类型为A,运行类型为B
- 【1】
System.out.println(a.sum());
调用对象a的sum()方法,该方法与对象a的运行类型B绑定。
先找B类里的sum()方法:
1)有则执行B类的sum()
- B类的sum()方法体为
return i + 20;
调用属性 i,而属性没有动态绑定机制。方法是在B类里的,则此 i 在B类声明,故 i = 20。
2)无则继续向上找B的父类A类的sum()
- A类的sum()方法体为
return getI() + 10;
调用方法getI(),该方法也与对象a的运行类型B绑定。
先找B类里的getI()方法,有则执行B类的getI()
- B类的getI()方法体为
return i;
调用属性 i,而属性没有动态绑定机制。方法是在B类里的,则此 i 在B类声明,故 i = 20。
- 【2】
System.out.println(a.sum1());
调用对象a的sum1()方法,该方法与对象a的运行类型B绑定。
先找B类里的sum1()方法:
1)有则执行B类的sum1()
- B类的sum1()方法体为
return i + 10;
调用属性 i,而属性没有动态绑定机制。方法是在B类里的,则此 i 在B类声明,故 i = 20。
2)无则继续向上找B的父类A类的sum1()
- A类的sum1()方法体为
return i + 10;
调用属性 i,而属性没有动态绑定机制。方法是在A类里的,则此 i 在A类声明,故 i = 10。
java
import com.aroma.Account;
import com.aroma.Person;
public class Hello {
public static void main(String[] args) {
// 对象a - 编译类型:A; 运行类型:B
A a = new B();
// 对象调用方法先找该对象的运行类(该方法与该对象的运行类型绑定)
System.out.println(a.sum()); // 运行类B有sum()方法,在方法体里执行 i + 20
System.out.println(a.sum1()); // 运行类B有sum1()方法,在方法体里执行 i + 10
// 方法体里调用属性(此时A类和B类均有所需要的属性),没有动态绑定机制,则i是在运行类型B类里声明的,则使用i = 20
}
}
class A {
public int i = 10;
public int sum() {
return getI() + 10;
}
public int sum1() {
return i + 10;
}
public int getI() {
return i;
}
}
class B extends A {
public int i = 20;
public int sum() {
return i + 20;
}
public int sum1() {
return i + 10;
}
public int getI() {
return i;
}
}

java
import com.aroma.Account;
import com.aroma.Person;
public class Hello {
public static void main(String[] args) {
// 对象a - 编译类型:A; 运行类型:B
A a = new B();
// 1.对象调用方法先找该对象的运行类,而此种情况下运行类型B里没有sum()和sum1()方法
// 2.找B类的父类A类,此时A类里有sum()和sum1()方法
System.out.println(a.sum()); // 执行A类里的sum(),方法体为getI() + 10
// 3.方法体里又调用getI()方法【方法和该对象的运行类型绑定,所以先找运行类B类里的getI()
// 4.运行类B类里有getI(),return i;
// 5.这里的属性i是在B类声明的,则i = 20,则getI() = 20,a.sum() = 20 + 10 = 30
System.out.println(a.sum1()); // 执行A类里的sum1(),方法体为i + 10
// 6.这里的属性i是在A类声明的,则i = 10,则a.sum1() = 10 + 10 = 20
}
}
class A {
public int i = 10;
public int sum() {
return getI() + 10;
}
public int sum1() {
return i + 10;
}
public int getI() {
return i;
}
}
class B extends A {
public int i = 20;
public int getI() {
return i;
}
}

6.应用:多态数组
多态数组:数组的定义类型为父类类型,里面保存的实际元素类型为子类类型
1)应用实例:要求创建1个Person对象,2个Student对象和2个Teacher对象,统一放在数组中,并调用每个对象的say()

java
package test;
public class Main {
public static void main(String[] args) {
Person[] persons = new Person[5];
persons[0] = new Person("jack", 20);
persons[1] = new Student("aroma", 22, 100);
persons[2] = new Student("kira", 23, 60);
persons[3] = new Teacher("nancy", 32, 3000);
persons[4] = new Teacher("vicky", 35, 4000);
System.out.println("姓名 年龄 成绩/薪水");
for (int i = 0; i < persons.length; i++) {
System.out.println(persons[i].say()); // 动态绑定机制:persons[i]的运行类型会根据实际情况进行改变
}
}
}
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String say() {
return name + "\t" + age;
}
}
class Student extends Person {
private double score;
public Student(String name, int age, double score) {
super(name, age);
this.score = score;
}
public double getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
public String say() {
return super.say() + " " + score;
}
}
class Teacher extends Person {
private double salary;
public Teacher(String name, int age, double salary) {
super(name, age);
this.salary = salary;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public String say() {
return super.say() + " " + salary;
}
}

2)应用示例:Teacher类有teach(),Student类有study(),如何调用子类特有的方法
java
package test;
public class Main {
public static void main(String[] args) {
Person[] persons = new Person[5];
persons[0] = new Person("jack", 20);
// 多态的向上转型:此时对象是无法调用运行类型特有的方法
persons[1] = new Student("aroma", 22, 100);
persons[2] = new Student("kira", 23, 60);
persons[3] = new Teacher("nancy", 32, 3000);
persons[4] = new Teacher("vicky", 35, 4000);
System.out.println("姓名 年龄 成绩/薪水");
for (int i = 0; i < persons.length; i++) {
System.out.println(persons[i].say()); // 动态绑定机制:persons[i]的运行类型会根据实际情况进行改变
// 判断对象的运行类型,利用多态的向下转型,再调用运行类型特有的方法
if (persons[i] instanceof Teacher) {
((Teacher) persons[i]).teach();
}else if (persons[i] instanceof Student) {
((Student) persons[i]).study();
} else if (persons[i] instanceof Person) {
System.out.println("该对象类型为Person,不做任何处理");
} else {
System.out.println("该对象的类型有误,请检查代码");
}
}
}
}
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String say() {
return name + "\t" + age;
}
}
class Student extends Person {
private double score;
public Student(String name, int age, double score) {
super(name, age);
this.score = score;
}
public double getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
public String say() {
return super.say() + " " + score;
}
public void study() {
System.out.println("学生" + getName() + "正在学习...");
}
}
class Teacher extends Person {
private double salary;
public Teacher(String name, int age, double salary) {
super(name, age);
this.salary = salary;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public String say() {
return super.say() + " " + salary;
}
public void teach() {
System.out.println("老师" + getName() + "正在授课...");
}
}

7. 应用:多态参数
多态参数:方法定义的形参类型为父类类型,实参类型允许为子类类型
1)应用示例:主人给动物(猫、狗)喂食(鱼、骨头)
java
public class Main {
public static void main(String[] args) {
Master master = new Master("aroma");
Cat cat = new Cat("牛奶");
Dog dog = new Dog("大黄");
Fish fish = new Fish("鱼罐头");
Bone bone = new Bone("骨头罐头");
master.feed(cat, fish);
master.feed(dog, bone);
}
}
class Master {
private String name;
public Master() {
}
public Master(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void feed(Animal animal, Food food) {
System.out.println("主人" + name + "给" + animal.getName() + "吃" + food.getFoodName());
}
}
class Animal {
private String name;
public Animal(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class Cat extends Animal {
public Cat(String name) {
super(name);
}
}
class Dog extends Animal{
public Dog(String name) {
super(name);
}
}
class Food {
private String foodName;
public Food() {
}
public Food(String foodName) {
this.foodName = foodName;
}
public String getFoodName() {
return foodName;
}
public void setFoodName(String foodName) {
this.foodName = foodName;
}
}
class Fish extends Food{
public Fish(String foodName) {
super(foodName);
}
}
class Bone extends Food{
public Bone(String foodName) {
super(foodName);
}
}

2)应用示例:
- 定义员工类Employee,包含姓名和月工资(private),计算年工资getAnnual();
- 普通员工和经理继承员工类,普通员工类有work(),经理类有奖金bonus属性和管理manage();
- 普通员工和经理类要求分别重写getAnnual();
- 测试类中添加方法①showEmployeeAnnual(Employee e)来获取任何员工对象的年工资,②testWork():如果是普通员工,则调用work(),如果是经理,则调用manage()
java
package test;
public class Test {
public static void main(String[] args) {
Staff staff = new Staff("kira", 10000);
Manager manager = new Manager("aroma", 20000, 2000);
Test t = new Test();
t.showEmployeeAnnual(staff);
t.showEmployeeAnnual(manager);
t.testWork(staff);
t.testWork(manager);
}
public void showEmployeeAnnual(Employee e) {
System.out.println("该员工年工资:" + e.getAnnual());
}
public void testWork(Employee e) {
if (e instanceof Manager) {
((Manager) e).manage();
}else if (e instanceof Staff) {
((Staff) e).work();
}else {
System.out.println("类型有误,请检查代码");
}
}
}
class Employee {
private String name;
private double monthSalary;
public Employee(String name, double monthSalary) {
this.name = name;
this.monthSalary = monthSalary;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getMonthSalary() {
return monthSalary;
}
public void setMonthSalary(double monthSalary) {
this.monthSalary = monthSalary;
}
public double getAnnual() {
return monthSalary * 12;
}
}
class Staff extends Employee {
public Staff(String name, double monthSalary) {
super(name, monthSalary);
}
public void work() {
System.out.println("普通员工" + getName() + "正在工作...");
}
@Override
public double getAnnual() {
return super.getAnnual();
}
}
class Manager extends Employee {
private double bonus;
public Manager(String name, double monthSalary,double bonus) {
super(name, monthSalary);
this.bonus = bonus;
}
public void manage() {
System.out.println("经理" + getName() + "正在管理...");
}
public double getAnnual() {
return super.getAnnual() + bonus;
}
}
