大家好,我是白夜,今天给大家聊聊面向对象的三大特征——封装
一、包(package)
1.1、包的引入
先来看看我们之前写的代码结构
以上代码存在的问题
- 所有类写在一个目录下面,非常难管理,因为以后项目不可能只有这么几个类,当类数量很大的时候,就不容易管理了。
- 不能写同名但是不同需求的类。
为了解决这些问题,我们需要把不同的东西放到专门的里面,进行分类管理。也就是通过文件夹进行管理,Java 中的文件夹就是包
1.2、定义包
包命名规范:一般是公司域名反写.项目名.模块名字.子模块名;
要求:包名是全英文小写。
例如 : itsource.cn 域名
package cn.itsource.erp.oa.domain; 自动化办公
package cn.itsource.erp.sa.entity; 系统管理
package cn.itsource.packagedemo;// 声明包
/**
* 包package
*/
public class PackageDemo {
public static void main(String[] args) {
new java.util.Date();
new java.sql.Date(1L);
System.out.println();
}
}
1.3、导入包
当我们需要使用别人或者 JDK 中的类的时候,就需要告知 JVM 从什么地方去加载这个类,这个过程就是导包
其实本质是导入包中的类。
导入包的语法 :
import 包名.子包名.类名
注意:
-
导入包的代码应该在声明包(就是该类所在的包)的后面,声明类的前面。
-
import java.util; 表示导入本包中所有会使用到的 util 包中的类,只会导入 util 包下面直接的类型,不包括util 包的子包中的类型。
-
java.lang是核心包,下面的直接类型是自动导入的;
例如:String、System类,lang 包的子包中的类型不会自动导入,需要手动导入。
-
在一个类中会用到同名不同包的时候必须使用全限定路径
例如:同时使用 java.util.Date 和 java.sql.Date
package cn.itsource.packagedemo;//声明包
import java.util.*;// 只会导入util包下使用到的类
/**
* 包package
*/
public class PackageDemo {
public static void main(String[] args) {// String是java.lang核心包下的,程序会自动导入;
// 使用Arrays类中的toString方法
// String str = java.util.Arrays.toString(new int[]{1, 2, 3});// 每次都写全限定类名比较麻烦
// 使用Arrays类中的sort方法排序
// java.util.Arrays.sort(new int[]{3, 2, 1});
// 用自动导包可以简化上面代码
String str = Arrays.toString(new int[]{1, 2, 3});// 每次都写全限定类名比较麻烦
System.out.println(str);
Arrays.sort(new int[]{3, 2, 1});
// 当使用同名不同包的多个类怎么用? 必须用全限定类名
new java.util.Date();// java.util包下
new java.sql.Date(1L);// java.sql包下
}
}
1.4、Java中常用的包(了解)
java/ javax(java 增强包)
java.lang (java 的核心包--基本包) 帮我们自动导包的
java.util(java 的工具包 --集合框架 ArrayList LinkedList)
java.io(java IO包input-output 读写文件)
java.net (网络编程)
java.awt/javax.swing(java的图形化界面)
java.math 数学相关的包
java.sql 数据库相关的包
java.text 是文本格式化相关的包
java.time 时间相关的包
二、封装
2.1、为什么要封装
来看下面一个案例
package cn.itsource.potting1;
/**
* 账户Account类
* 封装引入
*/
public class Account {
/** String类型成员变量姓名name */
protected String name;
/** String类型成员变量密码pwd */
String pwd;
/** double类型成员变量余额money */
double money;
/** boolean类型成员变量是否是vip用户 */
boolean vip;
/**
* Account类无参构造
*/
public Account() {}
/**
* 有4个参数构造方法
* @param n
* @param p
* @param m
* @param v
*/
public Account(String n, String p, double m, boolean v) {
name = n;// 将局部变量n赋值给成员变量name
pwd = p;// 将局部变量p赋值给成员变量pwd
money = m;// 将局部变量m赋值给成员变量money
vip = v;// 将局部变量v赋值给成员变量vip
}
/**
* 获取当前成员变量money的值
* @return
*/
public double getMoney() {
return money;
}
}
测试类:
package cn.itsource.potting1;
/**
* Account测试类
* 封装的引入
*/
public class AccountTest {
public static void main(String[] args) {
// 创建Account对象,调用无参构造
Account acc1 = new Account();
// 给acc1成员变量赋值
acc1.name = "某菲";
acc1.pwd = "6969";
acc1.money = 1.0;
acc1.vip = false;
// 打印acc1成员变量的值
System.out.println(acc1.name);
System.out.println(acc1.pwd);
System.out.println(acc1.money);
System.out.println(acc1.vip);
// acc1调用getMoney方法
double money = acc1.getMoney();// 因为getMoney方法是非static修饰,并且有返回值,所以用acc1调用,用double变量接收
System.out.println(money);
// 需求:当money达到50000.00的时候,会将vip升级为true。现在,能不能没有达到5万就不能升级。
acc1.vip = true;// 这里没有经过任何权限判断,直接修改了值,不安全。所以,用封装解决这个问题。
System.out.println(acc1.vip);// true
}
}
而在 Java 中是通过封装来完成保护内部成员的目的
2.2、封装的作用
封装是为了保护内部数据的安全:
- 不希望在外部类中随意访问类中的成员变量
- 达到权限要求的才能访问。
- 只是获取数据的时候。例如:单例模式。
- 我们在程序设计的时候就追求“高内聚、低耦合”:
- 高内聚:类的内部数据操作细节自己完成,不允许外部干涉;
- 低耦合:仅对外部暴露少量的方法用于使用。
2.3、封装的使用
-
如何控制程序中的访问 ?
通过给类中的成员(字段,方法,构造方法)添加访问权限修饰符来实现封装(访问控制)。
-
什么是访问权限:简单的认为访问权限就是不同级别的人能够干不同级别的事,不同级别的人能看到的页面是不同的。
例子:比如做一个系统,不同人登录进去的访问权限不一样;
-
访问权限修饰符:
public 最大权限,被其修饰的成员,在任意目录下,都可以访问到 (所有类)
protected 在同包类和子类中都可以访问
默认不写 只能在同包类中访问
private 只能在当前类中访问
-
封装的步骤:
- 将类的属性设置为 private,这样外部就不能直接访问。
- 提供公共的 getter 方法用于获取属性值,通常是 public 的。
- 提供公共的 setter 方法用于设置属性值,也是 public 的,并可以在其中添加逻辑检查。
- 提供一个无参构造,有参构造根据需求确定是否要写。
- 该类用 public 修饰。
public class Account {// 5. 该类用 public 修饰 //1. 私有化成员变量(用 private 修饰成员变量) private String name; private String pwd; // 4. 提供一个无参构造 public Account() {} // 有参构造根据实际需求,决定是否要写 public Account(String n, String p) { name = n;// 将局部变量赋值给成员变量 pwd = p; } /** * 2. 为每一个成员变量提供合理的 public void setName(String n) 方法 * 给成员变量name赋值方法 * @param n */ public void setName(String n) { // 可以写判断条件 name = n;// 将局部变量n赋值给成员变量 name } public void setPwd(String p) { pwd = p; } /** * 2. 为每一个成员变量提供合理的 public String getName() 方法 * 获取成员变量 name 的值 * @return */ public String getName() { // 可以写判断条件 return name;// 直接返回成员变量 name } public String getPwd() { return pwd; } }
/** * Account 测试类 * 封装的引入 */ public class AccountTest { public static void main(String[] args) { // 创建Account对象,调用无参构造 // Account acc1 = new Account(); // 给 acc1 成员变量赋值 /* * 因为 private 修饰成员变量后,不能在其他类中使用了。 acc1.name = "小七"; acc1.pwd = "6969"; */ // 使用有参构造赋值 Account acc1 = new Account("小七", "6969"); // 打印acc1成员变量的值 // System.out.println(acc1.name);// 因为 private 修饰成员变量后,不能在其他类中使用了。 // System.out.println(acc1.pwd); // 因为封装了,所以只能调用:public 返回值 getXxx() 获取成员变量的值 String name = acc1.getName();// acc1 调用 getName 方法获取成员变量 name 的值 String pwd = acc1.getPwd();// acc1 调用 getPwd 方法获取成员变量 pwd 的值 // 打印上面获取的值 System.out.println(name); System.out.println(pwd); // 当需要修改创建好的对象的成员变量值,用 set 方法赋值 acc1.name("小八"); String name2 = acc1.getName(); System.out.println(money2); } }
2.4、 封装的注意事项
- 不是只有 private 才叫封装,private 只是最大限度的封装而已。
- get 和 set 方法都是只能获取或者赋值一个成员变量,**不能 **set(String n, double m, boolean v) 赋值3个成员变量。
- 单一职能原则:功能最小化,不要想着一个方法写完所有的功能,因为代码复用率高。
2.5、封装的好处
- 增强安全性:隐藏数据和方法的实现细节,防止外部直接访问,降低因错误使用而导致的风险。
- 提高复用性:封装后的代码更容易被其他程序或模块复用,因为它们的实现细节对于使用者来说是透明的。
- 便于维护:封装使得代码修改时影响范围局限,提高了代码的可维护性。
三、this关键字
this 的概念:
this 指代当前对象,即,哪个对象调用就指代哪个对象
3.1、this 的使用
1. 解决局部变量和成员变量的二义性【set 方法和有参构造中形参跟成员变量同名不同义】文章来源:https://www.toymoban.com/news/detail-849198.html
public Account(String name,String pwd) {
// 要解决二义性问题,就需要用到this,加上this,就会直接从成员变量位置找name
this.name = name;
this.pwd = pwd;
}
1. 本类中构造方法之间的相互调用,但是必须是构造方法内的第一句。文章来源地址https://www.toymoban.com/news/detail-849198.html
package cn.itsource.task.topic02;
public class People {
private String name;
private boolean sex;
private int age;
private int weight;
public People(){
}
public People(String name, boolean sex, int age, int weight){
this(name, sex, age);
this.weight = weight;
}
public People(String name, boolean sex, int age){
this(name, sex);
this.age = age;
}
public People(String name, boolean sex){
this(name);
this.sex = sex;
}
public People(String name){
this.name = name;
}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public boolean isSex() { return sex; }
public void setSex(boolean sex) { this.sex = sex; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
public int getWeight() { return weight; }
public void setWeight(int weight) { this.weight = weight; }
}
四、构造方法和set方法的区别
- 构造方法可以为多个成员变量赋值,set只能对一个进行赋值;
- 构造方法由JVM自动调用,set需要手动调用;
- 构造方法针对同一个对象只能调用一次,set方法针对同一个对象可以调用多次;
- 构造方法用于给变量赋值,set可以给变量赋值或者修改值;
- 构造可以使代码更加简洁,set方法灵活性更高。
到了这里,关于Java基础知识篇02——封装的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!