一、封装
1.1、封装(encapsulation)
就是把抽象出的数据[属性]和对数据的操作[方法]封装在一起,数据被保护在内部,程序的其他部分只有通过对授权的操作[方法],才能对数据进行操作。
1.2、封装的理解和好处
1、隐藏实现细节:方法(连接数据库)<--调用(传入参数..)
2、可以对数据进行验证,保证安全合理
1.3、封装的实现步骤 (三步)
1、将属性进行私有化private 【不能直接修改属性】
2、提供一个公共的(public)set方法,用于对属性判断并赋值
public void setXxx(类型参数名){//Xxx表示某个属性
//加入数据验证的业务逻辑
属性=参数名
}
3、提供一个公共的(public)get方法,用于获取属性的值
public数据类型getXxx(){//权限判断,Xxx某个属性
return xx;
}
注:
1)自己写 setXxx 和 getXxx 太慢,我们使用构造器快捷键alt+insert-->Getter and Setter
2)可以将 set 方法写在构造器(一个无参(继承调用) 一个有参+set)中,这样仍然可以限制
二、继承extends
2.1、基本介绍和示意图
继承可以解决代码复用,让我们的编程更加靠近人类思维。当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过 extends 来声明继承父类即可。
2.2、继承的基本语法
class 子类 extends 父类{
}
1、子类就会自动拥有父类定义的属性和方法
2、父类又叫超类,基类
3、子类又叫派生类
2.3、继承给编程带来的便利
1、代码的复用性提高了
2、代码的扩展性和维护性提高了
2.4、继承的注意事项和使用细节
1、子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问, 但是私有属性和方法不能在子类直接访问,要通过父类提供公共的方法去访问
2、子类必须调用父类的构造器,完成父类的初始化
3、当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用 super 去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过
4、如果希望指定去调用父类的某个构造器,则显式的调用一下 : super(参数列表)
5、super 在使用时,必须放在构造器第一行(super 只能在构造器中使用)
6、super() 和 this() 都只能放在构造器第一行,因此这两个方法不能共存在一个构造器
7、java 所有类都是 Object 类的子类, Object 是所有类的基类,顶级类
8、父类构造器的调用不限于直接父类!将一直往上追溯直到 Object 类(顶级父类)
9、子类最多只能继承一个父类(指直接继承),即 java 中是单继承机制。
思考:如何让 A 类继承 B 类和 C 类? 【A 继承 B, B 继承 C】
10、不能滥用继承,子类和父类之间必须满足 is-a 的逻辑关系
2.5、继承的本质分析(重要)
查找关系来返回信息
A) 首先看子类是否有该属性
B) 如果子类有这个属性,并且可以访问,则返回信息
C) 如果子类没有这个属性,就看父类有没有这个属性(如果父类有该属性,并且可以访问,就返回信息..)
D) 如果父类没有就按照(3)的规则,继续找上级父类,直到 Object..
提示:如果查找方法的过程中,找到了,但是不能访问, 则报错, cannot access
如果查找方法的过程中,没有找到,则提示方法不存在
三、多态
3.1、基本介绍:
方法或对象具有多种形态。是面向对象的第三大特征,多态是建立在封装和继承基础之上的。
3.2、多态的具体体现
1、方法的多态:重写和重载就体现多态
2、对象的多态 (核心,困难,重点)
1)一个对象的编译类型和运行类型可以不一致
2)编译类型在定义对象时,就确定了,不能改变
3)运行类型是可以变化的
4)编译类型看定义时“=”号的左边,运行类型看“=”号的右边
Animal animal = new Dog();//【animal 编译类型是Animal,运行类型Dog】
animal=new Cat()://【animal的运行类型变成了Cat,编译类型仍然是Animal】
3.3、多态的注意事项
多态的前提是:两个对象(类)存在继承关系
3.3.1、多态的向上转型
1、本质:父类的引用指向了子类的对象
2、语法:父类类型 引用名=new 子类类型();
3、特点:编译类型看左边,运行类型看右边。
A)可以调用父类中的所有成员(需遵守访问权限),不能调用子类中特有成员;(因为在编译阶段,能调用哪些成员,是由编译类型来决定的)
B)最终运行效果看子类(运行类型)的具体实现!(调用方法时,按照从子类(运行类型)开始查找方法,然后调用,规则我前面我们讲的方法调用规则“查找关系来返回信息”一致)
3.3.2、多态向下转型
1、语法:子类类型 引用名=(子类类型)父类引用;
2、只能强转父类的引用,不能强转父类的对象
3、要求父类的引用必须指向的是当前目标类型的对象
Animal animal = new Cat();
Cat cat = (Cat) animal;
Dog dog = (Dog) animal; //错误×
4、当向下转型后,可以调用子类类型中所有的成员
3、属性没有重写之说!属性的值看编译类型
//假设Base 为父类,Sub 为子类
Base base = new Sub();//向上转型
System.out.println(base.count);// ? 看编译类型Base里的count值
Sub sub = new Sub();
System.out.println(sub.count);// ? Sub里的count值
4、instanceOf 比较操作符(对象 instanceOf XX),用于判断对象的运行类型是否为 XX 类型或 XX 类型的子类型
5、对象.getClass():获得该对象的运行类型
3.4、java的动态绑定机制
1、当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定
2、当调用对象属性时,没有动态绑定机制,哪里声明,那里使用
3.5、多态的应用
3.5.1、多态数组
数组的定义类型为父类类型,里面保存的实际元素类型为子类类型
应用实例:现有一个继承结构如下:要求创建 1 个 Person 对象、2 个 Student 对象和 2 个 Teacher 对象, 统一放在数组中,并调用每个对象say 方法
public 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;
}
}
public 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(double score) {
this.score = score;
}
//重写父类say
@Override
public String say() {
return "学生 " + super.say() + " score=" + score;
}
//特有的方法
public void study() {
System.out.println("学生 " + getName() + " 正在学java...");
}
}
public 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;
}
//写重写父类的say方法
@Override
public String say() {
return "老师 " + super.say() + " salary=" + salary;
}
//特有方法
public void teach() {
System.out.println("老师 " + getName() + " 正在讲java课程...");
}
}
public class PloyArray {
public static void main(String[] args) {
Person[] persons = new Person[5];
persons[0] = new Person("jack", 20);
persons[1] = new Student("mary", 18, 100);
persons[2] = new Student("smith", 19, 30.1);
persons[3] = new Teacher("scott", 30, 20000);
persons[4] = new Teacher("king", 50, 25000);
//循环遍历多态数组,调用say
for (int i = 0; i < persons.length; i++) {
//老师提示: person[i] 编译类型是 Person ,运行类型是是根据实际情况有JVM来判断
System.out.println(persons[i].say());//动态绑定机制
//使用 类型判断 + 向下转型.
if(persons[i] instanceof Student) {//判断person[i] 的运行类型是不是Student
Student student = (Student)persons[i];//向下转型
student.study();
//也可以使用一条语句 ((Student)persons[i]).study();
} else if(persons[i] instanceof Teacher) {
Teacher teacher = (Teacher)persons[i];
teacher.teach();
} else if(persons[i] instanceof Person){
//System.out.println("你的类型有误, 请自己检查...");
} else {
System.out.println("你的类型有误, 请自己检查...");
}
}
}
}
3.5.2、多态参数
方法定义的形参类型为父类类型,实参类型允许为子类类型
四、super 关键字
4.1、基本介绍
super 代表父类的引用,用于访问父类的属性、方法、构造器
4.2、基本语法
1、访问父类的属性,但不能访问父类的private属性àsuper.属性名;
2、访问父类的方法,不能访问父类的private方法àsuper.方法名(参数列表);
3)访问父类的构造器:super(参数列表);只能放在构造器的第一句,只能出现一句
4.3、super 的注意事项和使用细节
1、调用父类的构造器的好处(分工明确,父类属性由父类初始化,子类的属性由子类初始化)
2、当子类中有和父类中的成员(属性和方法)重名时,为了访问父类的成员,必须通过super。如果没有重名,使用super、this、直接访问是一样的效果!
3、super的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用super去访问爷爷类的成员;如果多个基类(上级类)中都有同名的成员,使用super访问遵循就近原则。A->B->C,当然也需要遵守访问权限的相关规则
五、super 和 this 的比较
5.1、super关键字
见上
5.2、this关键字
5.3、super 和 this 的比较
No. | 区别点 | this | Super |
1 | 访问属性 | 访问本类中的属性,如果本类没有此属性则从父类中继续查找 | 从父类开始查找属性 |
2 | 调用方法 | 访问本类中的方法,如果本类没有此方法则从父类继续查找 | 直接从父类开始查找方法 |
3 | 调用构造器 | 调用本类构造器,必须放在构造器的首行 | 调用父类构造器,必须放在子类构造器的首行 |
4 | 特殊 | 表示当前对象 | 子类中访问父类对象 |