面向对象编程OOP(Object Oriented Programming)的三大特征之一:封装
封装就是把类的一部分属性或方法等"隐藏"起来,若外部想要访问必须要通过封装类提供的方法才能对数据进行操作。通过封装我们可以将一些重要的数据放在内部保护起来。
封装的优点
-
隐藏方法实现的细节。
-
对数据进行验证,保证数据的安全合理。(例:Person类的age属性只能在1-120之间)
封装的实现
使用private访问修饰符 ,修饰属性或方法。对外界提供public修饰的方法对内部"隐藏"数据进行操作。
简单案例
Person类:
public class Person {
public String name;
private int age;
private double salary;
private String job;
//构造器 + Set : 需要满足约束条件
public Person(String name, int age, double salary, String job) {
setName(name);
setAge(age);
setSalary(salary);
setJob(job);
}
//重写toString方法
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + ", salary=" + salary + ", job=" + job + "]";
}
public void setName(String name) {
if (name.length() >= 2 && name.length() <= 6) {
this.name = name;
} else {
System.out.println("姓名长度2-6,默认值小范");
this.name = "小范";
}
}
public void setAge(int age) {
if (age >= 1 && age <= 120) {
this.age = age;
} else {
System.out.println("年龄范围1-120,默认值18");
this.age = age;
}
}
public void setSalary(double salary) {
if (salary <= 1000000 && salary >= 1) {
this.salary = salary;
}else {
System.out.println("工资范围1-1000000,默认值10000");
this.salary = 10000;
}
}
public void setJob(String job) {
this.job = job;
}
public double getSalary() {
return salary;
}
public double getAge() {
return age;
}
public String getJob() {
return job;
}
}
Test类:
public class Test {
public static void main(String[] args) {
//使用set get 方式
Person person = new Person();
person.setAge(120);
person.setJob("teacher");
person.setName("aaaaaaa");
person.setSalary(12000);
System.out.println("name: " + person.name);
System.out.println("age:" + person.getAge());
System.out.println("job:" + person.getJob());
System.out.println("salary:" + person.getSalary());
Person person = new Person("aaaaa", 8888,130000,"teacher");
//重写了toString方法之后
System.out.println(person.toString());
//没有重写toString方法 则会返回 全类名 + 十六进制HashCode地址
A a = new A();
System.out.println(a); //如果不写toString方法, 默认也是返回toString
System.out.println(a.toString()); //返回十六进制
System.out.println(a.hashCode()); //返回十进制
}
}
class A{
}
说明:
-
Person类:
-
使用private修饰符封装了 name,age,salary,job属性。
-
通过setXxx() 与 getXxx() 的方法实现给属性赋值以及访问属性值的功能。
-
这里注意不一定方法名一定是set和get,但一定都是使用public修饰的。
-
可以看到在setAge和setSalary时,都做了if判断。通过这种方面实现了外部输入数据的过滤,对内部的数据进行了保护。避免出现不符合语义的数据进入,例如Age = 2000,salary = -10000。
-
在Person类中我们还使用了有参构造器与set方法结合使用。这样既可以便捷的使用构造器给对象赋值,又可以使用set方法的数据保护机制。
-
重写了toString方法,"@Override"这个符号现在不用考虑,之后会讲到。具体同String方法说明在Test类中体现。
-
-
Test类:
-
使用封装类的第一种方式,使用无参构造器,然后通过Person类提供的set方法为对象赋值。
-
数据输出时,也通过get方法去访问内部数据。可以看到这种方式实现时,代码量会比较大。
-
使用封装类的第二种方式,使用有参构造器,直接给对象赋值。
-
再使用toString方法输出对象的各属性信息。关于toString()方法的说明:
-
我们使用了一个测试类A来演示,在输出对象信息时会默认的调用toString方法。
-
若不对toString方法进行重写,输出的内容为:全类名 + 十六进制HashCode地址
-
由于我们在Person类中对toString方法进行了重写,所以会Person类的对象在输出时会按照我们定义的方式输出。
-
重写的概念会在之后的博客中给出。
-
-
案例总结
-
在对封装起来的属性写保护机制的时候,尽量在输入数据出错时附带一个默认值,这样可以保证整个程序不会出现"0"或"null"等无意义数据存在。
-
对象赋值可以灵活的选择set方法和有参构造器两种方式。
-
输出对象信息时,尽量使用toString方法,可以使用 "System.out.println(对象名);"的方式,简洁明了。这里实际上是 "System.out.println(对象名.toString());" 只不过toString方法默认隐藏。
接下来我们看两个对于封装类的实际应用,为了代码方便阅读这里将各个类写在了一起,但在实际开发中应该保证一个类一个文件,所有类在同一个包下。
应用案例1
定义一个Person类 {name, age, job}, 初始化Person 对象数组,有3个person对象, 并按照 age 从 大到小进行排序,提示,使用冒泡排序.
public class Test {
public static void main(String[] args) {
Person[] person = new Person[3];
// 初始化对象数组
person[0] = new Person("小范", 20, "老师");
person[1] = new Person("小黄", 18, "店长");
person[2] = new Person("小雨", 19, "舞蹈老师");
// 遍历显示Person数组
for (int i = 0; i < person.length; i++) {
System.out.println(person[i]); // 默认就带有toString方法
}
sort(person); // 排序
System.out.println("===========排序后=============");
for (int i = 0; i < person.length; i++) {
System.out.println(person[i].toString());
}
}
// 冒泡排序
private static void sort(Person[] person) {
// 临时对象
// Person temp = new Person(); 自己的思路
// 冒泡排序 自己的思路
// for (int i = 0; i < person.length - 1; i++) {
// for (int j = 0; j < person.length - 1 - i; j++) {
// if (person[j].getAge() > person[j + 1].getAge()) {
// // 使用 temp 对象 ,临时保存 , 将堆中的内容进行改变
// temp.setName(person[j].getName());
// temp.setJob(person[j].getJob());
// temp.setAge(person[j].getAge());
//
// person[j].setName(person[j + 1].getName());
// person[j].setJob(person[j + 1].getJob());
// person[j].setAge(person[j + 1].getAge());
//
// person[j + 1].setName(temp.getName());
// person[j + 1].setJob(temp.getJob());
// person[j + 1].setAge(temp.getAge());
// }
// }
// }
// 冒泡 韩老师的思路
Person temp = null;
// 冒泡排序 韩老师的思路
for (int i = 0; i < person.length - 1; i++) {
for (int j = 0; j < person.length - 1 - i; j++) {
if (person[j].getAge() > person[j + 1].getAge()) {
// 使用 temp 对象 ,临时保存 ,使用引用地址来进行交换
temp = person[j];
person[j] = person[j + 1];
person[j + 1] = temp;
}
}
}
}
}
class Person {
private String name;
private int age;
private String job;
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 getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
// 有参构造器
public Person(String name, int age, String job) {
this.name = name;
this.age = age;
this.job = job;
}
// 无参构造器
public Person() {
}
// 输出打印Person信息
public String toString() {
return "Person [name=" + name + ", age=" + age + ", job=" + job + "]";
}
}

应用案例2
计算圆的面积,圆柱的体积。
public class Test {
public static void main(String[] args) {
Circle circle = new Circle();
Cylinder cylinder = new Cylinder();
circle.setRadius(2.5);
System.out.printf("圆半径:%.2f 圆面积:%.2f\n",circle.getRadius(),circle.findArea());
cylinder.setLength(3.1);
System.out.printf("圆柱高:%.2f 圆柱体积:%.2f\n",cylinder.getLength(),cylinder.findVolume(circle));
}
}
/*
* class Circle(圆)
* -radius :double
Circle(): 构造方法,将radius 私有属性初始化为1
+setRadius(double radius) : void
+getRadius(): double
+findArea():double 计算圆的面积
*/
class Circle{
private double radius;
public Circle() { //构造器
this.radius = 1;
}
public double getRadius() {
return radius;
}
public void setRadius(double radius) {
this.radius = radius;
}
public double findArea() {
return radius * radius * Math.PI; //Math.PI 返回 3.14.....
}
}
/*
* class Cylinder(圆柱)
* -length:double //高度
* Cylinder(): 构造方法,将length属性初始化为1
+setLength(double length):void
+getLength():double
+findVolume() :double 计算圆柱体积
*
*/
class Cylinder{
private double length;
public Cylinder(double length) {
this.length = length;
}
public Cylinder() {
this(1); //调用上面的有参构造,将length初始化为1
}
public double getLength() {
return length;
}
public void setLength(double length) {
this.length = length;
}
public double findVolume(Circle circle) { //使用Circle对象作为参数传递
return circle.findArea() * length; //直接使用findArea()方法
}
}