文章目录
- 面向对象编程(2)
- 一、封装
- (1)private
- 1. 通过private把属性隐藏起来
- 2. 通过setter、getter方法访问属性
- 3. setter/getter和直接访问的异同
- (2)this
- 3)构造方法
- 1. 构造方法的特点
- 2. 构造方法的书写格式
- 3. 构造方法示例
- 4. 构造方法的使用
- (4)标准的Java类
- 1. 标准Java类的定义原则
- 2. 标准Java类示例
- (6)封装
- 总结
面向对象编程(2)
面向对象语言有三大特性:封装、继承和多态。本文我们先来讲一讲封装性。
一、封装
在讲解**封装(encapsulation)**的概念之前,我们先学习一下private关键字。
(1)private
先看下面的代码:
有一个Student类
public class Student {
//实例变量
String name; //姓名
int age; //年龄
//实例方法
public void study() {
System.out.println(name + "正在努力学习Java面向对象的知识。");
}
public void doHomework() {
System.out.println(name + "在努力的写作业。十万行代码成就你十万年薪");
}
public void showInfo() {
System.out.println("姓名:" + name + ",年龄:" + age);
}
}
Student类创建了2个对象。
public static void main(String[] args) {
Student stu1 = new Student();
stu1.name = "马化腾";
stu1.age = 22;
stu1.showInfo();
Student stu2 = new Student();
stu2.name = "李彦宏";
stu2.age = -20;
stu2.showInfo();
}
输出结果:
姓名:马化腾,年龄:22
姓名:李彦宏,年龄:-20
上述的输出结果是没有错的,程序也正常执行的。但是人的年龄是-20是有点违背逻辑的。
之所以出现这种问题,是因为外界可以直接操作属性。想要避免这种问题,最好的办法是不让外界直接操作属性,把属性隐藏起来,给外界提供访问属性的方法,这时就需要用到我们上文提到的private关键字了。
1. 通过private把属性隐藏起来
private单词的含义是私有的。private可以修饰类中的属性和方法,修饰属性的时候,表示属性私有,外界无法直接访问(所谓的直接访问是通过对象名.属性名访问),但是在本类中可以访问。
public class Student {
//实例变量
private String name; //姓名
private int age; //年龄
//实例方法
public void study() {
System.out.println(name + "正在努力学习Java面向对象的知识。");
}
public void doHomework() {
System.out.println(name + "在努力的写作业。十万行代码成就你十万年薪");
}
public void showInfo() {
System.out.println("姓名:" + name + ",年龄:" + age);
}
}
上述代码实现了属性的私有,一旦是有,外界将不能使用对象名.属性名去访问对象的属性。
如果强行使用,就会像下图一样报错:
2. 通过setter、getter方法访问属性
那么如果我们想要获得private属性进行获取和赋值应该怎么办呢?我们有以下两种方法对private属性进行操作:
setter方法:为属性(实例变量)赋值的方法。
getter方法:获取属性(实例变量)值的方法。
由于getter、setter方法是我们特意为外界定义的方法,方便外界能访问属性,因此要有public修饰。
举个栗子:
public String getName() {
return name;
}
public void setName(String n) {
name = n;
}
public int getAge() {
return age;
}
public void setAge(int a) {
age = a;
}
需要注意的是:
setter方法既然是为属性赋值,就必须要有参数,而且参数的类型和属性的类型相同。setter方法的方法名是set+首字母大写的属性名。例如:属性名是name,setter方法的方法名是setName,属性名是age,setter方法的方法名是setAge。
getter方法既然是要获取属性的值,就必须有返回值,而且返回值的类型和属性的类型相同。getter方法的方法名是get+首字母大写的属性名。例如:属性名是name,getter方法的方法名是getName,属性名是age,getter方法的方法名是getAge。
提供了方法以后,外界就可以通过方法去访问属性。
那么,我们上文中关于马某人和李某人的代码就可以变成如下的形式:
public static void main(String[] args) {
Student stu1 = new Student();
stu1.setName("马化腾");
stu1.setAge(22);
stu1.showInfo();
Student stu2 = new Student();
stu2.setName("李彦宏");
stu2.setAge(-20);
stu2.showInfo();
}
这时候有人会说了,你这输出没有任何的改变啊,我还多写这么多代码,你这不是坑人呢?
那我们来对这个setAge稍微改进,改进代码如下:
public void setAge(int a) {
if(a < 0) {
System.out.println("您输入的年龄有误。");
}else {
age = a;
}
}
聪明的同学应该已经懂了封装性的主要作用了,没懂得也不要着急,我们接着往下看。
3. setter/getter和直接访问的异同
相同点:都是在访问属性,包括赋值和取值。
不同点:1.直接访问属性优势是简单快捷,一步到位;劣势是有可能会出现数据错误。2.通过setter/getter访问属性优势是数据没有直接赋值给属性,在赋值之前可以做一些操作和处理;劣势是代码量略大。
在开发中属性一般情况下都定义private,对外提供pulic的getter和setter方法。
(2)this
讲解this之前,先看一下刚才写的setter、getter方法
public String getName() {
return name;
}
public void setName(String n) {
name = n;
}
public int getAge() {
return age;
}
public void setAge(int a) {
if(a < 0) {
System.out.println("您输入的年龄有误。");
}else {
age = a;
}
}
上面方法中的参数并没有做到见名知意。如果做到见名知意,应改为:
public String getName() {
return name;
}
public void setName(String name) {
name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if(age < 0) {
System.out.println("您输入的年龄有误。");
}else {
age = age;
}
}
改成这样以后,确实见名知意了,但是程序的结果却不对了。
姓名:null,年龄:0
您输入的年龄有误。
姓名:null,年龄0
这是因为:在一个类中,如果局部变量和实例变量名称相同,在方法中使用变量的时候,使用的是局部变量,而不是实例变量。想要在方法中使用实例变量的话,需要使用this.实例变量名。
所以我们将代码改成下面这样:
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if(age < 0) {
System.out.println("您输入的年龄有误。");
}else {
this.age = age;
}
}
this的英文含义是这个,在代码中this是一个特殊的对象,它始终指的是调用方法的对象,即谁调用方法,this就是谁。看下图:
可以通过打印内存地址的方式,来检验this究竟是谁。
this关键字
- this修饰的变量用于指代实例变量。方法的形参如果与实例变量同名,不带this的变量是形参,而不是实例变量。方法的形参如果和实例变量不同名,不需要用this修饰。
- this代表的就是调用方法的那个对象。
3)构造方法
构造方法是一种特殊的方法,它是创建对象时调用的方法,用于在创建对象的时候对属性进行初始化。
Student stu1 = new Student();//Student()就是一个构造方法。
1. 构造方法的特点
- 构造方法只能用在对象创建的时候。
- 构造方法的作用是对属性进行初始化。
- 构造方法的方法名必须和类名相同。
- 构造方法没有返回值,连void都不能写。
- 如果自己没有提供构造方法,系统会默认生成一个无参数的构造方法。
- 如果自己提供了任何一个构造方法,系统将不再生成无参数的构造方法。
- 构造方法可以重载。
2. 构造方法的书写格式
public 类名(参数列表){
}
3. 构造方法示例
public Student() {
}
public Student(String name) {
this.name = name;
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
上面的三个方法都是构造方法。
4. 构造方法的使用
public static void main(String[] args) {
Student stu1 = new Student("马化腾",22);
stu1.showInfo();
Student stu2 = new Student("李彦宏");
stu2.setAge(20);
stu2.showInfo();
Student stu3 = new Student();
stu3.setName("马云");
stu3.setAge(25);
stu3.showInfo();
}
在开发过程中,我们通过会至少给2个构造方法。其中一个是无参构造方法,另外一个是全参的构造方法。除此以外根据需求添加别的构造方法。
(4)标准的Java类
根据上文内容,我们可以得到一个标准的Java类,这种类包括了一个类实现特殊功能的最基本的需求。
1. 标准Java类的定义原则
- 属性用private修饰
- 提供属性对应的setter、getter方法
- 提供1个或多个构造方法
- 提供正常的功能性方法
2. 标准Java类示例
public class Circle {
//属性
private double r; //半径
public double getR() {
return r;
}
public void setR(double r) {
this.r = r;
}
public Circle() {
}
public Circle(double r) {
this.r = r;
}
//实例方法
public double perimeter() {
return 2 * Math.PI * r;
}
public double area() {
return Math.PI * r * r;
}
}
使用圆的时候:
public static void main(String[] args) {
// 使用无参构造创建对象
Circle c1 = new Circle();
c1.setR(10);
c1.perimeter();
c1.area();
// 使用有参构造创建对象
Circle c2 = new Circle(4);
c2.perimeter();
c2.area();
}
(6)封装
封装:隐藏对象的内部细节,对外提供接口(访问方式)。
**封装的原则:**将类的某些信息隐藏在类内部,不允许外界直接访问,而是给外界提供接口(方法)。外界通过接口访问内部的数据以及类的功能。
- getter、setter封装了实例变量。
- 方法封装了功能的实现细节。
- 类封装了属性和方法。
封装的好处:
- 通过方法来控制实例变量的操作,提高了代码的安全性。
- 把代码用方法进行封装,提高了代码的复用性。
总结
本文我们讲解了面向对象三大特性之一的封装性,封装性在我们以后的学习中极大地精简了我们的代码,减少了重复,并且对于学习来说也方便他人阅读代码。并且通过方法控制实例变量的操作,极大地提高了代码的安全性(但肯定不是100%安全)。接下来我们会对继承性进行讲解,感谢观看。