一、封装
1. 封装概述
面向对象编程语言是对客观世界的模拟,客观世界里成员变量都是隐藏在对象内部的,外界无法直接操作和修改。封装可以被认为是一个保护屏障,防止该类的代码和数据被其他类随意访问。要访问该类的数据,必须通过指定的方式。适当的封装可以让代码更容易理解与维护,也加强了代码的安全性。
原则:将属性隐藏起来,若需要访问某个属性,提供公共方法对其访问。
2. 封装步骤
(1)使用 private 关键字来修饰成员变量。
private关键字:
1. private是一个权限修饰符,代表最小权限。
2. 可以修饰成员变量和成员方法。
3. 被private修饰后的成员变量和成员方法,只在本类中才能访问。
(2)对需要访问的成员变量,提供对应的一对 getXxx
方法 和 setXxx
方法。
3. 定义一个标准类的规范(JavaBean)
一个标准的类通常要拥有以下四个部分:
(1)所有的成员变量都要使用private关键字修饰
(2)为每一对成员变量编写一对Getter/Setter方法
(3)编写一个无参数的构造方法
(4)编写一个满参数的构造方法
【注】其实写这样的类只需要自己写好成员变量即可,其余的部分都可以通过IDEA Code->generate...来自动生成。
举例:定义一个标准的学生类
public class Student {
// 所有的成员变量都要使用private关键字修饰
private String name;
private int age;
// 无参数的构造方法
public Student() {
}
// 满参数的构造方法
public Student(String name, int age) {
this.name = name;
this.age = age;
}
// Getter/Setter方法
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;
}
}
二、继承
1. 继承概述
继承:就是子类继承父类的属性和行为,使得子类对象具有与父类相同的属性、相同的行为。子类可以直接访问父类中的非私有的属性和行为。通过 extends
关键字,可以声明一个子类继承另外一个父类。
好处:
-
提高了代码的复用性。
-
类与类之间产生了关系,是多态的前提。
2. 在继承关系中,父子类成员变量的访问特点
- 若成员变量不重名,则可以任意直接访问父类和子类中的成员变量。
- 若是重名的成员变量,则默认会访问子类中的该成员变量,若想要访问父类中的该成员变量,需要使用super关键字。
【注】子类只可以访问父类中非私有的成员变量,若父类中的成员变量私有化了,那只能通过父类提供的getXxx方法和setXxx方法来进行访问。
3. 在继承关系中,父子类成员方法的访问特点
-
若成员方法不重名,则可以任意直接访问父类和子类中的成员方法。
-
若成员方法重名(返回值类型,方法名和参数列表都相同),则默认会访问子类中的该成员方法,若想要访问父类中的该成员方法,需要使用super关键字。
这被称为方法的覆盖重写,增强了代码的可扩展性。举例:
//重写父类的来电显示号码功能,并增加自己的显示姓名和图片功能
public void showNum(){
//在子类的成员方法中,访问父类的同名成员方法要使用super关键字
super.showNum();
//增加自己特有显示姓名和图片功能
System.out.println("显示来电姓名");
System.out.println("显示头像");
}
【注】子类方法覆盖重写父类方法,返回值类型、方法名和参数列表都要一模一样。
特别提醒:子类只能直接访问父类非私有的成员变量和成员方法。
4. 在继承关系中,父子类构造方法的访问特点
- 在子类构造方法中有一个默认隐含的"super()"调用,所以一定是先调用的父类构造,后执行的子类构造。
- 在子类构造方法中可以通过super(参数1,,参数2,...)来调用父类重载的构造方法。
- 利用super关键字来调用父类的构造方法,super语句必须是子类构造方法中的第一条语句(因为要先对父类成员变量进行初始化后再对子类成员变量进行初始化),并且在子类的一个构造方法中只能出现一次super语句。
【总结】子类必须调用父类的构造方法,不写则编译器赠送一个super();写了则用写的那个super语句调用。super只能有一个,还必须是第一个。
三、多态
1. 多态概述
引入
生活中,比如跑的动作,小猫、小狗和大象,跑起来是不一样的。再比如飞的动作,昆虫、鸟类和飞机,飞起来也是不一样的。可见,同一行为,通过不同的事物,可以体现出来的不同的形态。多态,描述的就是这样的状态。
多态:是指同一行为,具有多种不同的表现形式。
多态的前提:
(1)继承或者实现接口【二选一】
(2)方法的重写【意义体现:不重写,无意义】
(3)父类引用指向子类对象【格式体现】
【注】当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,执行的是子类重写后的方法。
2. 多态的好处
在实际的开发过程中,将父类类型作为方法的形参,把子类对象作为实参传递给方法,进行方法的调用,这里便能体现出多态的扩展性与便利性。
举例:
定义父类
public abstract class Animal {
public abstract void eat();
}
定义子类
class Cat extends Animal {
public void eat() {
System.out.println("吃鱼");
}
}
class Dog extends Animal {
public void eat() {
System.out.println("吃骨头");
}
}
定义测试类
public class Test {
public static void main(String[] args) {
// 多态形式,创建对象
Cat c = new Cat();
Dog d = new Dog();
// 调用showCatEat
showCatEat(c);
// 调用showDogEat
showDogEat(d);
/*
以上两个方法, 均可以被showAnimalEat(Animal a)方法所替代 而执行效果一致
*/
showAnimalEat(c);
showAnimalEat(d);
}
public static void showCatEat (Cat c){
c.eat();
}
public static void showDogEat (Dog d){
d.eat();
}
public static void showAnimalEat (Animal a){
a.eat();
}
}
由于多态特性的支持,showAnimalEat方法的参数Animal类型,是Cat和Dog的父类类型,父类类型接收子类对象,当然可以把Cat对象和Dog对象传递给方法。
当eat方法执行时,多态规定,执行的是子类重写的方法,那么效果自然与showCatEat、showDogEat方法一致,所以showAnimalEat完全可以替代以上两方法。
不仅仅是替代,在扩展性方面,无论之后再多的子类出现,我们都不需要编写showXxxEat方法了,直接使用 showAnimalEat就都可以完成。
所以,多态的好处,体现在,可以使程序编写的更简单,并具有良好的扩展性。
3. 引用类型转换
多态的转型分为向上转型与向下转型两种:
-
向上转型:父类引用指向子类对象(左父右子),也就是多态的写法。类比基本数据类型中的自动类型转换。
-
向下转型:子类类型 变量名 = (子类类型) 父类变量名; 。类比基本数据类型中的强制类型转换。
【注1】为什么要转型?
答:当使用多态方式调用方法时,不能调用子类拥有的,而父类没有的方法。因此,如果想要调用子类特有的方法,必须做向下转型。
【注2】在向下转型过程中,要避免产生类型转换异常。因此,我们可以使用instanceof关键字来判断。
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方法 }