1、包(package)
1.1 包的引入
先来看看我们之前写的代码结构(采用Eclips)
代码结构存在以下问题:
- 所有类写在一个目录下面,非常难管理,因为以后项目不可能只有这么几个类,当类数量很大的时候,就不容易管理了。
- 不能写同名但是不同需求的类。
那么我们要怎么解决这些问题呢?
我们可以通过生活中例子来获取经验,例如:假设我们有很多电影资源,那么我们是怎么管理这些片的?
通过文件夹
国产
欧美
爱情
动作
恐怖
日韩
爱情
动作
恐怖
…
所以,为了解决这些问题,我们就必须也要通过文件夹来完成管理。Java中的文件夹就是包,所以接下来我们要学习一下包。
1.2 包package概念
概念:简单的理解包就是一个文件夹。
1.3 包作用
① 方便管理项目中的类等文件。
② 可以避免类名冲突的问题。
1.4 如何使用包
1.4.1 定义包
包命名规范:一般是公司域名反写.项目名.模块名字.子模块名;
要求:包名是全英文小写。
例如 : xianshu.cn 域名
package cn.xianshu.erp.oa.domain;自动化办公
package cn.xianshu.erp.sa.entity; 系统管理
注意:不要写成nc.
// 例如:
package cn.xianshu.packagedemo; // 声明包
/**
* 包package
*/
public class PackageDemo {
public static void main(String[] args) {
new java.util.Date();
new java.sql.Date(1L);
System.out.println();
}
}
1.4.2 导入包
为什么要导包?
当我们需要使用别人或者JDK中的类的时候,就需要告知JVM从什么地方去加载这个类,这个过程就是导包。
其实本质是导入包中的类。
导入包的语法 : import 包名.子包名.类名;
注意:
- 导入包的代码应该在声明包(就是该类所在的包)的后面,声明类的前面。
- import java.util.*; 表示导入本包中所有会使用到的util包中的类,只会导入util包下面直接的类型,不包括util包的子包中的类型。
- java.lang是核心包,下面的直接类型是自动导入的;
例如:String、System类,lang包的子包中的类型不会自动导入,需要手动导入。
- 在一个类中会用到同名不同包的时候必须使用全限定路径
例如:同时使用java.util.Date和java.sql.Date
// 例如:
package cn.xianshu.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.5 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. 封装
2.1 为什么要封装?
先来下面一个案例:
package cn.xianshu.potting1;
/**
* 账户Account类
* 封装引入
*/
public class Account {
/** String类型成员变量姓名name */
protected String name;
/** String类型成员变量密码password */
String password;
/** 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
password = p;// 将局部变量p赋值给成员变量password
money = m;// 将局部变量m赋值给成员变量money
vip = v;// 将局部变量v赋值给成员变量vip
}
/**
* 获取当前成员变量money的值
* @return
*/
public double getMoney() {
return money;
}
}
测试类:
package cn.xianshu.potting1;
/**
* Account测试类
* 封装的引入
*/
public class AccountTest {
public static void main(String[] args) {
// 创建Account对象,调用无参构造
Account acc1 = new Account();
// 给acc1成员变量赋值
acc1.name = "某菲";
acc1.password = "6969";
acc1.money = 1.0;
acc1.vip = false;
// 打印acc1成员变量的值
System.out.println(acc1.name);
System.out.println(acc1.password);
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 封装的作用
封装是为了保护内部数据的安全:
1. 不希望在外部类中随意访问类中的成员变量
2. 达到权限要求的才能访问。
3. 只是获取数据的时候。例如:单例模式。
2.3 怎么封装
- 如何控制程序中的访问 ?
通过给类中的成员(字段,方法,构造方法)添加访问权限修饰符来实现封装(访问控制)。
- 什么是访问权限:简单的认为访问权限就是不同级别的人能够干不同级别的事,不同级别的人能看到的页面是不同的。
例子:比如做一个系统,不同人登录进去的访问权限不一样;
3. 访问权限修饰符:
public 最大权限,被其修饰的成员,在任意目录下,都可以访问到 (所有类)
protected 在同包类和子类中都可以访问
默认不写 只能在同包类中访问
private 只能在当前类中访问
2.4 封装的步骤
步骤: 1. 私有化成员变量(用private修饰成员变量)。 2. 为每一个成员变量提供合理的public修饰的: getXxx()方法 获取成员变量的值,如果当前成员变量类型是boolean类型,将getXxx()改为isXxx()。 setXxx(...)方法 设置成员变量的值。 3. 提供一个无参构造,有参构造根据需求确定是否要写。 4. 该类用public修饰。 |
封装案例:
public class Account { // 4. 该类用public修饰
/** String类型成员变量姓名name 1. 私有化成员变量(用private修饰成员变量)*/
private String name;
/** String类型成员变量密码password 1. 私有化成员变量(用private修饰成员变量)*/
private String password;
/** double类型成员变量余额money 1. 私有化成员变量(用private修饰成员变量)*/
private double money;
/** boolean类型成员变量是否是vip用户 1. 私有化成员变量(用private修饰成员变量)*/
private boolean vip;
/**
* 3. 提供一个无参构造
* 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
password = p; // 将局部变量p赋值给成员变量pwd
money = m; // 将局部变量m赋值给成员变量money
vip = v; // 将局部变量v赋值给成员变量vip
}
/**
* 2. 为每一个成员变量提供合理的public void setName(String n)方法
* 给成员变量name赋值方法
* @param n
*/
public void setName(String n) {
// 可以写判断条件
name = n; // 将局部变量n赋值给成员变量name
}
/**
* 2. 为每一个成员变量提供合理的 public void setpassword(String p)方法
* 给成员变量password赋值方法
* @param p
*/
public void setpassword(String p) {
// 可以写判断条件
password = p; // 将局部变量p赋值给成员变量pwd
}
/**
* 2. 为每一个成员变量提供合理的 public void setMoney(double m) 方法
* 给成员变量money赋值方法
* @param m
*/
public void setMoney(double m) {
// 可以写判断条件
money = m; // 将局部变量m赋值给成员变量money
}
/**
* 2. 为每一个成员变量提供合理的 public void setVip(boolean v) 方法
* 给成员变量vip赋值方法
* @param v
*/
public void setVip(boolean v) {
// 可以写判断条件
vip = v; // 将局部变量v赋值给成员变量vip
}
/**
* 2. 为每一个成员变量提供合理的 public String getName() 方法
* 获取成员变量name的值
* @return
*/
public String getName() {
// 可以写判断条件
return name; // 直接返回成员变量name
}
/**
* 2. 为每一个成员变量提供合理的 public String getPwd() 方法
* 获取成员变量pwd的值
* @return
*/
public String getpassword() {
// 可以写判断条件
return password; // 直接返回成员变量pwd
}
/**
* 2. 为每一个成员变量提供合理的 public double getMoney() 方法
* 获取成员变量money的值
* @return
*/
public double getMoney() {
// 可以写判断条件
return money; // 直接返回成员变量money
}
/**
* 2. 为每一个成员变量提供合理的 public boolean isVip() 方法
* 获取当前成员变量vip的值。如果当前成员变量类型是boolean类型,将getXxx()改为 isXxx()
* @return
*/
public boolean isVip() {
// 可以写判断条件
return vip; // 直接返回成员变量vip
}
}
测试案例:
/**
* Account测试类
* 封装的引入
*/
public class AccountTest {
public static void main(String[] args) {
// 创建Account对象,调用无参构造
// Account acc1 = new Account();
// 给acc1成员变量赋值
/*
* 因为private修饰成员变量后,不能在其他类中使用了。
acc1.name = "某菲";
acc1.password = "6969";
acc1.money = 1.0;
acc1.vip = false;
*/
// 使用有参构造赋值
Account acc1 = new Account("某菲", "6969", 1.0, false);
// 打印acc1成员变量的值
// System.out.println(acc1.name); // 因为private修饰成员变量后,不能在其他类中使用了。
// System.out.println(acc1.pwd);
// System.out.println(acc1.money);
// System.out.println(acc1.vip);
// 因为封装了,所以只能调用:public 返回值 getXxx() 获取成员变量的值
String name = acc1.getName(); // acc1调用getName方法获取成员变量name的值
String password = acc1.getpassword(); // acc1调用getPwd方法获取成员变量pwd的值
double money = acc1.getMoney(); // acc1调用getMoney方法获取成员变量money的值
boolean vip = acc1.isVip(); // acc1调用isVip方法获取成员变量vip的值
// 打印上面获取的值
System.out.println(name);
System.out.println(password);
System.out.println(money);
System.out.println(vip);
// 当需要修改创建好的对象的成员变量值,怎么办?用setXxx方法赋值
acc1.setMoney(50000);// acc1调用setMoney方法给成员变量money赋值,值随便写一个即可
acc1.setVip(true);// acc1调用setVip方法给成员变量vip赋值,值随便写一个即可
// 重新调用getXxx()或者 isXxx() 方法获取上面修改的成员变量值
// acc1调用getMoney方法
double money2 = acc1.getMoney();// 因为getMoney方法是非static修饰,并且有返回值,所以用acc1调用,用double变量接收
System.out.println(money2);
// acc1调用isVip方法
boolean vip2 = acc1.isVip();// 因为isVip方法是非static修饰,并且有返回值,所以用acc1调用,用boolean变量接收
System.out.println(vip2);
}
}
2.5 封装的注意事项
1. 不是只有private才叫封装,private只是最大限度的封装而已。
2. get和set方法都是只能获取或者赋值一个成员变量,不能set(String n, double m, boolean v)赋值3个成员变量。
3. 单一职能原则:功能最小化,不要想着一个方法写完所有的功能,因为代码复用率高。
2.6 封装小结
对象赋值的方式:
1. 有参构造 :new 类名(...);
2. 对象名.setXxx(...);
3. 对象名.成员变量 = 值; // 封装后不能用
一般配合使用,如果成员变量比较少,可以直接用有参构造。
如果成员变量比较多,一般用无参构造 + setXxx(...)。
如果对象已经创建,需要修改值,用setXxx(...)。
- 对象取值的方式:
1. 对象名.getXxx(); // 对象名.isXxx()
2. 对象名.成员变量 ; // 封装后不能用
3. this
3.1 this引入
先看我们上面写的代码,在有参构造和setXxx方法中形参应该按照见名知意原则来写,代码如下:
public class Student {
private String name;
public Student() {}
public String getName() {
return name;
}
public void setName(String name) { // name按照简明知意原则,应该将n改为name
name = name;
}
}
测试类:
public class StudentTest {
public static void main(String[] args) {
// 调用有参构造,创建对象并且直接赋值
Student stu = new Student();
stu.setName("隔壁老王");
System.out.println(stu.getName()); // null(发现赋值是失败的,取的是默认值null)
}
}
这是为什么呢?请看内存分析图
根据内存图,我们知道这是就近原则造成的,但是我们必须要解决这个问题。
那么我们怎么解决这个问题呢? 使用this
3.2 this介绍
- this的概念:this指代当前对象,即,哪个对象调用就指代哪个对象。
- this理解示意图:
3. 测试this
public class Student {
public void printThis() {
System.out.println("this = " + this);
}
}
测试类:
public class StudentTest {
public static void main(String[] args) {
/*
* this表示的是当前对象,可看成this就是指代当前对象的地址;
*/
Student stu1 = new Student();
System.out.println("stu1= " + stu1);
stu1.printThis(); // this所在方法正在被stu1调用 则 this指代stu1的地址
// this指代当前对象,换了其他对象,this代表的就是其对象了
Student stu2 = new Student();
System.out.println("stu2= " + stu2);
stu2.printThis(); // this所在方法正在被stu2调用 则 this指代stu2的地址
}
}
/**
打印结果:
stu1 = Student@15db9742
this = Student@15db9742
stu2 = Student@6d06d69c
this = Student@6d06d69c
*/
3.3 this使用
- 解决局部变量和成员变量的二义性【set方法和有参构造中形参跟成员变量同名不同义】
// 例如:
public Account(String name) {
// 在方法中使用变量的时候,优先从局部范围找,就近原则
// 这里因为成员变量和局部变量名字相同,存在二义性问题
// 要解决二义性问题,就需要用到this,加上this,就会直接从成员变量位置找name
this.name = name;
}
2.本类中构造方法之间的相互调用,但是必须是构造方法内的第一句。
/**
语法:this(...); 调用本类中的另一个构造方法,并不会创建额外的对象,会根据参数自动匹配调用对应的构造方法。
作用:
1.可以创建对象的时候,给定某些成员变量默认值
2.可以复用其它构造方法,简化给成员变量赋值的代码
例如:
*/
// 1.可以创建对象的时候,给定某些成员变量默认值
public Account(String name) {
// name和111111都是String类型,只不过name是一个变量,而111111是一个写死的值
// 并通过该构造方法创建的对象都有相同的 【默认密码】
this(name,"111111"); // 必须是构造方法内的第一句,这里调用的是有两个String参数的构造方法
}
public Account(String name,String password) {
// 要解决二义性问题,就需要用到this,加上this,就会直接从成员变量位置找name
this.name = name;
this.pwd = password;
}
// 2.可以复用其它构造方法,简化给成员变量赋值的代码
public Account(String name, String password, double money) {
/*
this.name = name;
this.password = password;
可以使用下面的方式,this(...)简化代码
*/
this(name, password);// 复用上面的有两个String参数的构造方法,简化代码作用
this.money = money;
}
3.4 this注意和小结
- this用途:
- 解决局部变量和成员变量的二义性
- 在本类之间,构造方法之间的相互调用this()调用无参数的构造方法,this(...)可以添加参数,表示调用有参数的构造方法
- this作为参数传递,this作为返回值
- this注意事项:
- this不能在静态的方法中使用: 无法从静态上下文中引用非静态成员this。
- static是类级别,this是对象级别
static修饰的是属于类。
this是属于某一个具体的对象。
3.5 this综合代码案例
package cn.xianshu.this3;
/**
* 账户Account类
* this
*/
public class Account {// 4. 该类用public修饰
/** String类型成员变量姓名name 1. 私有化成员变量(用private修饰成员变量)*/
private String name;
/** String类型成员变量密码pwd 1. 私有化成员变量(用private修饰成员变量)*/
private String pwd;
/** double类型成员变量余额money 1. 私有化成员变量(用private修饰成员变量)*/
private double money;
/** boolean类型成员变量是否是vip用户 1. 私有化成员变量(用private修饰成员变量)*/
private boolean vip;
/**
* 3. 提供一个无参构造
* Account类无参构造
*/
public Account() {}
/**
* 有2个String参数的构造方法,一个参数是name,一个参数是pwd
* @param name
* @param pwd
*/
public Account(String name, String pwd) {
/*
* 因为就近原则,导致使用变量的时候,优先从局部范围查找,如果找到了,就直接使用,没找到才从成员变量查找
* 这里将将局部变量赋值给成员变量就会存在二义性问题,解决方案:在成员变量前加上: this.
*/
this.name = name;// 将局部变量name赋值给成员变量name,使用this解决了成员变量和局部变量二义性
this.pwd = pwd;// 将局部变量pwd赋值给成员变量pwd,使用this解决了成员变量和局部变量二义性
}
/**
* 有参构造根据实际需求,决定是否要写
* 有4个参数构造方法
* @param name
* @param pwd
* @param money
* @param vip
*/
public Account(String name, String pwd, double money, boolean vip) {
/*
* 因为就近原则,导致使用变量的时候,优先从局部范围查找,如果找到了,就直接使用,没找到才从成员变量查找
* 这里将将局部变量赋值给成员变量就会存在二义性问题,解决方案:在成员变量前加上: this.
*/
/*this.name = name;// 将局部变量name赋值给成员变量name
this.pwd = pwd;// 将局部变量pwd赋值给成员变量pwd
用this()第二种用法简化上面成员变量赋值代码: this(...); 表示在调用当前类中另外一个定义好的构造方法,调用的时候,会根据传入的实参自动匹配调用
注意:this(...)只能在构造方法第一行。this()调用构造方法不会创建额外的对象,只是简化代码
*/
this(name, pwd);// this(name, pwd);表示在调用上面2个String参数的构造方法
this.money = money;// 将局部变量m赋值给成员变量money
this.vip = vip;// 将局部变量v赋值给成员变量vip
}
/**
* 2. 为每一个成员变量提供合理的public void setName(String name)方法
* 给成员变量name赋值方法
* @param name
*/
public void setName(String name) {
// 可以写判断条件
this.name = name;// 将局部变量name赋值给成员变量name
}
/**
* 2. 为每一个成员变量提供合理的 public void setPwd(String pwd)方法
* 给成员变量pwd赋值方法
* @param pwd
*/
public void setPwd(String pwd) {
// 可以写判断条件
this.pwd = pwd;// 将局部变量pwd赋值给成员变量pwd
}
/**
* 2. 为每一个成员变量提供合理的 public void setMoney(double money) 方法
* 给成员变量money赋值方法
* @param money
*/
public void setMoney(double money) {
// 可以写判断条件
this.money = money;// 将局部变量money赋值给成员变量money
}
/**
* 2. 为每一个成员变量提供合理的 public void setVip(boolean vip) 方法
* 给成员变量vip赋值方法
* @param vip
*/
public void setVip(boolean vip) {
// 可以写判断条件
this.vip = vip;// 将局部变量vip赋值给成员变量vip
}
/**
* 2. 为每一个成员变量提供合理的 public String getName() 方法
* 获取成员变量name的值
* @return
*/
public String getName() {
// 可以写判断条件
return name;// 直接返回成员变量name
}
/**
* 2. 为每一个成员变量提供合理的 public String getPwd() 方法
* 获取成员变量pwd的值
* @return
*/
public String getPwd() {
// 可以写判断条件
return pwd;// 直接返回成员变量pwd
}
/**
* 2. 为每一个成员变量提供合理的 public double getMoney() 方法
* 获取成员变量money的值
* @return
*/
public double getMoney() {
// 可以写判断条件
return money;// 直接返回成员变量money
}
/**
* 2. 为每一个成员变量提供合理的 public boolean isVip() 方法
* 获取当前成员变量vip的值。如果当前成员变量类型是boolean类型,将getXxx()改为 isXxx()
* @return
*/
public boolean isVip() {
// 可以写判断条件
return vip;// 直接返回成员变量vip
}
}
测试代码:
package cn.xianshu.this3;
/**
* Account 中this测试类
*/
public class AccountTest {
public static void main(String[] args) {
// 使用有参构造赋值
Account acc1 = new Account("某菲", "6969", 1.0, false);
String name = acc1.getName();// acc1调用getName方法获取成员变量name的值
String pwd = acc1.getPwd();// acc1调用getPwd方法获取成员变量pwd的值
double money = acc1.getMoney();// acc1调用getMoney方法获取成员变量money的值
boolean vip = acc1.isVip();// acc1调用isVip方法获取成员变量vip的值
// 打印上面获取的值
System.out.println(name);
System.out.println(pwd);
System.out.println(money);
System.out.println(vip);
// 当需要修改创建好的对象的成员变量值,怎么办?用setXxx方法赋值
acc1.setMoney(50000);// acc1调用setMoney方法给成员变量money赋值,值随便写一个即可
// 重新调用getXxx()或者 isXxx() 方法获取上面修改的成员变量值
// acc1调用getMoney方法
double money2 = acc1.getMoney();// 因为getMoney方法是非static修饰,并且有返回值,所以用acc1调用,用double变量接收
System.out.println(money2);
}
}
下一章 十一、《面向对象-继承、抽象、方法重写》"三"