封装(encapsulation,有时称为信息隐藏)是处理对象的一个重要概念。
从形式上看,封装就是将数据和行为组合在一个包中,并对对象的使用者
隐藏具体的实现细节。对象中的数据称为实例字段(instance field),
操作数据的过程称为方法(method)。作为一个类的实例,一个特定对象
就有一组特定的实例字段值。这些值的集合就是这个对象当前的状态(state)
只要在对象上调用一个方法,它的状态就有可能发生改变。
封装的关键:
绝对不能让其他类中的方法直接访问这个类的实例字段。程序只能通过对象的方法与对象的数据进行交互。
下面是小编整理的访问限定符,这是一定要掌握的哦
No | 范围 | private | default | protected | public |
---|---|---|---|---|---|
1 | 同一包中的同一类 | ✅ | ✅ | ✅ | ✅ |
2 | 同一包中的不一类 | ✅ | ✅ | ✅ | |
3 | 不同包中的子类 | ✅ | ✅ | ||
4 | 不同包中的非子类 | ✅ |
我们在设计程序的时候追求高内聚、低耦合
高内聚——类的内部数据的操作和细节自己完成,不允许外部干涉;
低耦合——对外提供少量的公共方法使用
我们在实现的一个类的时候隐藏类内部的复杂性、之对外提供简单的公开的接口。便于外界调用从而提高系统的可拓展性、可维护性,这里就要讲到Java中权限访问修饰符(private、默认(什么都不加)、protected、public)进行封装。
下面我们用封装的思想来编写一个员工类
属性——姓名、年龄、工资
行为——上班摸鱼
public class Employee {
/**
* 将特有的属性进行封装,并且不希望外界随意
* 的对他进行修稿,这里我们就要用到私有的访
* 问修饰符——private
*/
private String name;
private int age;
private double salary;
//构造方法,供外界实例一个类对象来操作数据
public Employee(String name,int age,double salary) {
this.name = name;
this.age = age;
this.salary = salary;
}
/**
* 对外提供少量的公共方法供实例对象来操作数据
* 主要是get、set方法,就比如说有一天我的员工修
* 改了姓名或者调薪
*/
//set设置器
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
//get访问器
public String getName() {
return name;
}
public int getAge() {
return age;
}
public double getSalary() {
return salary;
}
//特有的行为
public void work() {
System.out.println("名字为" + name + "的员工正在上班摸鱼");
}
public void raiseSalary(double byPercent) {
double raise = salary * byPercent / 100;
salary += raise;
}
}
最后我们再仔细看一下非常简单getName、getSalary方法
public String getName() {
return name;
}
public double getSalary() {
return salary;
}
这些都是典型的访问器。由于他们只返回实例字段的值,因此又称为字段访问器(field accessor)。
如果将name、salary字段标记为公共,而不是编写单独的访问器方法,不是更容易一些吗?
不过name只是一个只读字段,一旦再构造器(构造方法)中设置,就没有办法能够修改这个字段。这样我们就可以确保name字段不会受到外界的破坏。
虽然salary不是只读字段,但是它只能用raiseSalary方法修改。具体的,如果这个值发生错误,那么只需要调试这个raiseSalary方法就可以了。如果salary字段是公共的,破坏这个字段的罪魁祸首就可以出现再任何地方(那就很难调试了)。
让我们回忆一下上面讨论的employee类。假设你在某个公司工作,这个公司里经理的待遇与普通员工的待遇存在着一些差异。不过,当然他们当中也存在很多相同的地方,例如,他们都领取薪水。只是普通员工在完成本职任务后仅领取薪水,而经理在完成了预期的业绩之后还能得到奖金。这种情形就需要使用继承。
为什么呢?因为需要为经理定义一个新类Manager,并增加一些新功能,但可以重用Employee类中已经编写好的部分代码,并保留Employee类中的所有字段。
**继承机制:**是面向对象程序设计使代码可以复用的重要手段,它允许程序员在保持原有类特性的基础上进行拓展,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。继承主要解决的问题是:共性的抽取,实现代码复用。
继承并不仅限于一个层次。例如,可以由Animal类派生出狗类、猫类、鸟类,再由猫类派生出中华田园猫、英国短毛猫、布偶猫等。由一个公共超类派生出来的所有类的集合称为继承层次结构(inheritance hierarchy),如下图所示。再继承层次结构中,从某个特定的类到其祖先的路径称为该类的继承链(inheritance chain)。
补充:
在C++中,一个类可以有多个超类。Java不支持多重继承,但支持多层继承。
由于设计不好,或者因场景需要,子类和父类中可能会存在相同名称的成员,如果要在子类方法中访问父类同名成员时,该如何操作?直接访问是无法做到的,Java提供了super关键字,该关键字主要作用:在子类方法中访问父类的成员。
this关键字主要在本类访问本类成员成员变量,**该关键字的主要作用:**在本类方法中访问本类的成员
super和this都可以在成员方法中用来访问:成员变量和调用其他的成员函数,都可以作为构造方法的第一条语句,他们之间有什么区别呢/?
【相同点】
1.都是Java中的关键字
2。只能在类的非静态方法中使用,用来访问非静态的成员方法和字段
3.在构造方法中调用时,必须时构造方法中的第一条语句,并且不能不能同时存在
【不同点】
1.this是当前对象的引用,当前对象即为调用实例方法的对象,super相当于是子类对象中从父类继承下来部分成员的引用
2. 在非静态成员方法中,this用来访问本类的方法和属性,super用来访问父类继承下来的方法和属性
3. 在构造方法中:this(…)用于调用本类构造方法,super(…)用于调用父类构造方法,两种调用不能同时在构造
方法中出现
4. 构造方法中一定会存在super(…)的调用,用户没有写编译器也会增加,但是this(…)用户不写则没有
4.1 继承语法:
Java中如果要表示类之间的继承关系,需借助extends关键字,具体如下:
修饰符 class 子类名 extends 父类名 {
//...
}
4.2 设计一个继承体系
父类Employee类:
属性——姓名、年龄、工资
行为——上班摸鱼、调薪
子类Manager类:
属性——姓名、年龄、工资、奖金(特有的)
行为——看看谁摸鱼、调薪
class Employee {
//属性
private String name;
private int age;
private double salary;
//构造方法
public Employee(String name,int age,double salary) {
/**this关键字的用法解释
* 由于方法形参与成员变量同名,不适用this关键字就造成变量
* 同名,不使用this关键字就遵循就近原则,影响不到成员变量
* 故this.name指定了成员变量的name,而非形参的name
*/
this.name = name;
this.age = age;
this.salary = salary;
}
//set更改器
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
//get访问器
public String getName() {
return name;
}
public int getAge() {
return age;
}
public double getSalary() {
return salary;
}
//行为
public void work() {
System.out.println("名字为" + name + "的员工正在上班摸鱼!");
}
public void raiseSalary(double byPercent) {
double raise = salary * byPercent / 100;
salary += raise;
}
}
class Manager extends Employee {
//属性,与父类相同的属性就不必写了,这就是继承的好处
private double bonus;
public Manager(String name, int age, double salary) {
/**super关键字用法的解释
* 因为这个类继承了Employee类,必须帮助父类构造
* 这里的super是调用了父类的构造方法
*/
super(name, age, salary);
//这里的奖金并不是构造对象时马上给出的,根据业绩的给定的
this.bonus = 0;
}
/*由于父类的访问器已经不能满足子类的需求了,故需要重写
父类的getSalary()方法,这里不加拓展重写,有关内容期待博主更新
或者网上查阅资料哦!!!*/
public double getSalary() {
//这里super关键字的用法时调用父类的成员方法
double baseSalary = super.getSalary();
return baseSalary + bonus;
}
public void setBonus(double bonus) {
this.bonus = bonus;
}
public void superviseWork() {
System.out.println("名为" + super.getName() + "正在看看谁上班摸鱼!");
}
}
在Java程序设计语言中,对象变量时多态的(polymorphic)。一个Employee类型的变量既可以引用一个Employee类型的对象,也可以引用Employee类的任何一个子类的对象(例如:Manager)。
那就意味着我们可以这样用,接上面的继承篇
Manager boss = new Manager("张三",24,12000);
Employee[] staff = new Employee[3];
staff[0] = boss;
boss.setBonus(5000)//OK
staff[0].setBonus(5000)//ERROR
为什么会发生错误呢?这是因为staff声明是Employee类型,Employee类中并没有setBonus()方法,boss则是声明为Manager类型,类中有setBonus()方法,所以调用时正确的。
**多态的概念:**通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生不同的状态。
在Java中要想完成多态,必须要满足如下几个条件,缺一不可:
**多态体现:**在代码运行时,当传递不同类对象时,会调用对应类中的方法。
Employee boss = new Manager("张三",24,12000);
/**
*此时父类和子类中都有setSalary()方法,编译器在编译代码时并不知道
*要调用Employee中的方法还是Manager中的方法,等程序运行起来后,变量
*boss引用的具体对象确定后,才知道要调用那个方法。明显这里boss引用
*Manager,所以回调用Manager类中的getSalary方法。
*/
boss.getSalary();
1.多态的优点
2.多态的缺陷
当父类和子类都有同名属性的时候,通过父类引用,只能引用父类自己
的成员属性
结语:每个人的成长和进步都离不开点点滴滴的积累,如果小编的内容对您有所帮助,希望友友们三连加关注哦,你的支持是小编创作的最大动力。