本文目录
一、本文目标
二、封装
2.1 封装的概念
2.2 封装的原则
2.3 封装的步骤
2.4 封装的操作
三、继承
3.1 继承的概述
3.2 继承的格式
3.3 继承的特点
3.3.1 成员变量
3.3.2 成员方法
四、多态
本文内容包含面向对象的语言中的三大基本特征,即封装、继承和多态(面试的时候经常被问到哦,以后在代码开发过程中也会经常用到)。学完之后会理解封装思想和理解构造方法的含义,用封装思想定义一个标准类,了解继承的特点,什么是多态,多态的向上转型和向下转型等内容。
抛开抽象概念来讲,封装就是将一些复杂的或是不想让你看到的事物包装起来,叫做封装。从程序的角度来说就是,把一些内部复杂的逻辑或是不想让其他人员修改程序内部进而把部分程序包装起来,叫做封装。
面向对象编程语言是对客观世界的模拟,客观世界里成员变量都是隐藏在对象内部的,外界无法直接操作和修改。
封装可以被认为是一个保护屏障,防止该类的代码和数据被其他类随意访问。要访问该类的数据,必须通过指定的方式。适当的封装可以让代码更容易理解与维护,也加强了代码的安全性。
将属性隐藏起来,若需要访问某个属性,提供公共方法对其访问。
使用 private 关键字封装
private 关键字的含义
1、private 的使用格式
private 数据类型 变量名 ;
2、使用 private 修饰成员变量,示例代码如下:
/**
* 定义一个学生类
*/
public class Student {
// private 修饰成员变量
// 姓名
private String name;
// 年龄
private int age;
}
3、提供 getXxx 方法 / setXxx 方法,可以访问成员变量,代码如下:
其实,以下一个学生类的格式就是一个标准的 JavaBean 。
/**
* 定义一个学生类
*/
public class Student {
// private 修饰成员变量
// 姓名
private String name;
// 年龄
private int age;
// 对成员变量的 name 的 getter 方法
public String getName() {
return name;
}
// 对成员变量的 name 的 setter 方法
public void setName(String name) {
this.name = name;
}
// 对成员变量的 age 的 getter 方法
public int getAge() {
return age;
}
// 对成员变量的 age 的 setter 方法
public void setAge(int age) {
this.age = age;
}
}
Tips:上述代码中的 this 代表什么含义呢?
this 代表所在类的当前对象的引用(地址值),即对象自己的引用。
重点:方法被哪个对象调用,方法中的 this 就代表哪个对象。即谁在调用,this 就代表谁。
this 的使用格式
this.成员变量名;
继承:就是子类继承父类的属性和行为,使得子类对象具有与父类相同的属性、相同的行为。子类可以直接访问父类中的非私有的属性和行为。
多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那一个类即可。
生活中继承示例如图所示:
其中,多个类可以称为子类,单独那一个类称为父类、超类(superclass)或者基类。
继承描述的是事物之间的所属关系,这种关系是:is-a 的关系。例如,图中兔子属于食草动物,食草动物属于动物。可见,父类更通用,子类更具体。我们通过继承,可以使多种事物之间形成一种关系体系。
继承是面向对象语言的重要机制。借助继承,可以扩展原有的代码,应用到其他程序中,而不必重新编写这些代码。
所以它的优点就是 提高了代码的复用性。
通过 extends 关键字,可以声明一个子类继承另外一个父类,定义格式如下:
class 父类 {...}class 子类 extends 父类 {...}
继承的示例代码如下:
/**
* 定义员工类 Employee,做为父类
*/
public class Employee {
// 定义员工姓名的属性
String name;
// 定义员工职位的属性
String position;
// 定义员工的工作方法
public void goodWork() {
System.out.println("好好的工作!");
}
}
/**
* 定义一个经理类 Manager 继承 员工类 Employee
*/
class Manager extends Employee {
// 定义一个打印 name 和 position 的方法
public void print() {
System.out.println("name = " + name);
System.out.println("position = " + position);
}
}
以下是测试类示例代码:
public class ExtendDemo {
public static void main(String[] args) {
// 创建一个经理类对象
Manager manager = new Manager();
// 为该员工类的 name 属性进行赋值
manager.name = "小明";
// 为该员工类的 position 属性进行赋值
manager.position = "经理";
// 调用该员工的 print() 方法
manager.print(); // name = 小明,position = 经理
// 调用 Manager 类继承来的work()方法
manager.goodWork(); // 好好的工作!
}
}
Java 继承的特点有以下几个,希望读者能熟练掌握,面试的时候可能会经常被问到哦。生活中的例子,儿子、父亲、爷爷,可以用这三个身份来理解继承的特点,就会很容易理解啦。
代码举例说明:
// 一个类只能有一个父类,不可以有多个父类。class C extends A {} // 这样是正确的class C extends A , B ... // 这样就是错误的,编译器就不会通过
代码举例说明:C 继承 B,而 B 又继承 A
// A 是爷爷,B 是父亲,C 是儿子class A {}class B extends A {}class C extends B {}
Tips:在Java语言体系中,Object 类是顶层父类 。所有的类都默认继承 Object 类。
如果子类父类中出现不重名的成员变量,这时的访问是没有影响的。可以放心使用和访问。示例代码如下:
public class FuClassDemo1 {
public static void main(String[] args) {
// 创建子类对象
ZiClass1 ziClass1 = new ZiClass1();
// 调用子类中的 print() 打印方法
ziClass1.print();
}
}
class FuClass1 {
// Fu类中的成员变量
int num1 = 10;
}
class ZiClass1 extends FuClass1 {
// Zi类中的成员变量
int num2 = 20;
// Zi类中的成员方法
public void print() {
// 访问父类中的 num1
System.out.println("Fu类中的 num1 = " + num1); // 这是从父类中继承而来的,所以能直接访问。
// 访问子类中的 num2
System.out.println("Zi类中的 num2 = " + num2);
}
}
上述代码运行后的结果如下:
但是如果子类中使用与父类中名字相同的成员变量时,这就会产生影响,下面举例说明:
public class FuClassDemo2 {
public static void main(String[] args) {
// 创建子类对象
ZiClass2 ziClass2 = new ZiClass2();
// 调用子类中的 print() 打印方法
ziClass2.print();
}
}
class FuClass2 {
// Fu类中的成员变量
int num = 30;
}
class ZiClass2 extends FuClass2 {
// Zi类中的成员变量
int num = 40;
// Zi类中的成员方法
public void print() {
// 访问父类中的 num
System.out.println("Fu类中的 num = " + num);
// 访问子类中的 num
System.out.println("Zi类中的 num = " + num);
}
}
运行结果如下:
子类中有与父类中相同名字的成员变量时,结果运行和预期结果(Fu类中的 num = 30,Zi类中的 num = 40)大相径庭。
程序访问变量时遵循了就近原则,所以在子类中访问同一个 非私有的成员变量时,就近的访问了子类中的 成员变量,所以就出现了上述运行结果。那么该怎么访问父类中的成员变量呢?这就会用到另外一个关键字,super 关键字。
子父类中出现了同名的成员变量时,在子类中需要访问父类中非私有成员变量时,需要使用 super 关键字,修饰父类成员变量,super 关键字的用法类似于之前学过的 this 。
Tips:在以后的实际代码开发过程中,我们应该尽量避免使用同名的成员变量。
super 的使用格式:
super . 父类成员变量名
子类方法需要修改,代码如下:
public class FuClassDemo3 {
public static void main(String[] args) {
// 创建子类对象
ZiClass3 ziClass3 = new ZiClass3();
// 调用子类中的 print() 打印方法
ziClass3.print();
}
}
class FuClass3 {
// Fu类中的成员变量
int num = 30;
}
class ZiClass3 extends FuClass3 {
// Zi类中的成员变量
int num = 40;
// Zi类中的成员方法
public void print() {
// 访问父类中的 num
System.out.println("Fu类中的 num = " + super.num);
// 访问子类中的 num
System.out.println("Zi类中的 num = " + this.num);
}
}
修改后的程序运行结果便和我们预期的一致了:
如果子类父类中出现不重名的成员方法,这时的调用是没有影响的。对象调用方法时,会先在子类中查找有没有对应的方法,若子类中存在就会执行子类中的方法,若子类中不存在就会执行父类中相应的方法。
代码如下:
public class FuClassDemo4 {
public static void main(String[] args) {
// 创建子类对象
ZiClass4 ziClass4 = new ZiClass4();
// 调用子类中的方法
ziClass4.showZi();
// 子类中没有 showFu() 方法,所以它就会去它的父类中找 showFu() 方法去执行
ziClass4.showFu();
}
}
class FuClass4 {
// 父类中的 showFu() 方法
public void showFu() {
System.out.println("Fu类中的 showFu()方法被执行!");
}
}
class ZiClass4 extends FuClass4 {
// 父类中的 showZi() 方法
public void showZi() {
System.out.println("Fu类中的 showZi()方法被执行!");
}
}
如果子类父类中出现重名的成员方法,这个时候,成员方法重名,就会出现一种特殊情况,叫做方法重写 (Override)。
方法重写:子类中出现与父类一模一样的方法时(返回值类型,方法名和入参列表都相同),会出现覆盖效果,也称为重写或者复写。声明不变,重新实现。
示例代码如下:
public class FuClassDemo5 {
public static void main(String[] args) {
// 创建子类对象
ZiClass5 ziClass5 = new ZiClass5();
// 子类中有 show() 方法,只执行重写后的 show() 方法
ziClass5.show();
}
}
class FuClass5 {
// 父类中的 show() 方法,注意这里的返回是 void,方法名是 show,入参为空!
public void show() {
System.out.println("Fu类中的 show()方法被执行!");
}
}
class ZiClass5 extends FuClass5 {
// 子类中的 show() 方法,注意这里的返回是 void,方法名是 show,入参为空!这三项都与父类相同
public void show() {
System.out.println("Zi类中的 show()方法被执行!");
}
}
运行结果:
Tips:这里的重写,如果想用父类的方法,就需要用到 super 关键字,即 super.父类方法。表示调用父类的成员方法。
注意事项:
首先我们要知道两个事情,即构造方法的定义格式和作用。
- 构造方法的名字是与类名一致的。所以子类是无法继承父类构造方法的。
- 构造方法的作用是初始化成员变量的。
所以子类的初始化过程中,必须先执行父类的初始化动作。子类的构造方法中默认有一个 super(),表示调用父类的构造方法,父类成员变量初始化后,才可以给子类使用。
示例代码如下:
public class FuClassDemo6 {
public static void main(String[] args) {
// 创建子类对象
ZiClass6 ziClass6 = new ZiClass6();
}
}
class FuClass6 {
private int fu;
// 父类的构造方法
FuClass6() {
System.out.println("父类中的构造方法被执行了!");
}
}
class ZiClass6 extends FuClass6 {
// 子类中的构造方法
ZiClass6() {
// super() 方法,调用父类的构造方法
super();
System.out.println("子类中的构造方法被执行了!");
}
}
程序运行结果:
完结!