2. 封装
1) 我们日常使用的电脑主机,把cpu、内存、主板等等都封装到机箱里面去。假如没有机箱的话的出现什么问题?
主机、主板全部都散落在一处,然后开机没有开机按钮,那么需要我们直接操作接跳线才能把电脑开启。
这样子的话假如操作不慎的话,会让机器损坏危险,那么假如用机箱封装起来的话,那么就不需要这样子做了。这体现了封装的---安全特性。
2) 你拿电脑去加内存,可以直接给电脑给维修的人。等他加好内存了之后,你拿到的还是那个机箱,里面发生了啥变化你并不知道。
封装的第二个好处---将变化隔离。
3) 在机箱里面提供一个开机按钮,而不需要你直接使用跳线开机的话,体现了封装的---便于使用的特性。
4) 只要机箱提供了一个开机的功能,然后无论这个机箱拿到哪里去,都可以使用这个开机的功能。体现了封装的---可重复性的特性。
举个栗子:模拟员工类,看看封装与不封装有什么区别
不使用变量封装:
class Employee {
String name;
int age;
}
public class Test {
public static void main(String[] args) {
Employee employee = new Employee();
employee.name = "辣条";
employee.age = 24;
System.out.println(employee.name);
}
}
使用变量封装:
class Employee {
private String name;
private int age = 24;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
}
public class Test {
public static void main(String[] args) {
Employee employee = new Employee();
//employee.name = "辣条"; -- 编译报错,变量被定义为private,不允许访问
employee.setName("辣条");
//employee.setAge(24); -- 报错,没有开放setter方法,不能修改age的值
System.out.println(employee.getName());
}
}
很容易发现被我注释掉的那两行都在编译期报错了,这就体现了封装的几个特性。
再举个栗子,说明封装的易操作:模拟一个手机,提供一个可以接收信息的方法
class Phone {
private String name;
private int price = 5000;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPrice() {
return price;
}
/**
* 接收信息
* @author LinkedBear
* @param message 发送的信息
*/
public void getMessage(String message) {
System.out.println("价值" + price + "元的" + name + " 收到信息:" + message);
}
}
public class Test {
public static void main(String[] args) {
Phone phone = new Phone();
phone.setName("高端大气上档次的诺基亚黄屏手机");
phone.getMessage("尊敬的用户您好,您的智商不足,请及时充值。。。");
}
}
//运行结果:
//价值5000元的高端大气上档次的诺基亚黄屏手机 收到信息:尊敬的用户您好,您的智商不足,请及时充值。。。
我们再重复一遍 ̄へ ̄:
封装的好处:安全性、变化隔离、便于使用、可重复性
PS:封装之后的属性可以提供getter与setter方法来获取和修改。(setter还有一些东西,咱到后面说this的时候再唠)
咱来思考一个问题:
如果我们做这么一个功能:获取指定毫秒值对应的时间、日期等多种格式(可能部分代码不容易理解,我尽量把注释写清楚):
import java.text.SimpleDateFormat;
import java.util.Date;
public class Demo3 {
public static void main(String[] args) {
long time = System.currentTimeMillis(); // 用该方法可以获取当前时刻,精确到毫秒
SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日"); // 用日期格式化类转换,格式为1970年01月01日
SimpleDateFormat format2 = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒"); // 用日期格式化类转换,格式为1970年01月01日 12时00分00秒
Date date = new Date(time); // 把毫秒值变成一个日期的对象
System.out.println(format.format(date)); //转换,打印输出
System.out.println(format2.format(date)); //转换,打印输出
Date date2 = new Date();
System.out.println(format.format(date2));
System.out.println(format2.format(date2));
}
}
//运行结果:
//2018年01月29日
//2018年01月29日 11时30分17秒
//2018年01月29日
//2018年01月29日 11时30分17秒
发现代码很多都重复了!
封装的第二种用途:提取相同/相似的代码,封装成一个方法
import java.text.SimpleDateFormat;
import java.util.Date;
public class Demo {
public static void main(String[] args) {
long time = System.currentTimeMillis(); // 用该方法可以获取当前时刻,精确到毫秒
parseTimeToDate(time);
}
/**
* 将一个毫秒值转换成年月日的字符串
* @author LinkedBear
* @param time 代表指定时刻的毫秒值
*/
public static void parseTimeToDate(Long time) {
SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒"); // 用日期格式化类转换,格式为1970年01月01日 12时00分00秒
Date date = new Date(time); // 把毫秒值变成一个日期的对象
System.out.println(format.format(date)); //转换,打印输出
}
}
//运行结果:
//2018年01月29日 11时22分31秒
这样封装固然好,但是容易出现了一个新的问题:
如果我有一组实现效果相似的功能,总不能让我叫方法1,方法2,方法3吧,这也太low了!
所以,我们引入了下面的概念——
方法的重载Overload:
方法名相同,但传入参数的个数、类型、顺序不同,都可以用同名方法。
那我们再举一个栗子:上面的黄屏手机中新添加一个播放歌曲的无参方法,再添加一个指定播放曲目的有参方法
class Phone {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
/**
* 无参方法,默认播放社会摇 ̄へ ̄
* @author LinkedBear
*/
public void music() {
System.out.println(name + "正在播放:社会摇。。。");
}
/**
* 带参方法,播放指定歌曲
* @author LinkedBear
* @Time 2018年1月29日 上午11:36:39
* @param musicName
*/
public void music(String musicName) {
System.out.println(name + "正在播放:" + musicName);
}
}
public class Test {
public static void main(String[] args) {
Phone phone = new Phone();
phone.setName("高端大气上档次的诺基亚黄屏手机");
phone.music();
phone.music("一人饮酒醉");
}
}
//运行结果:
//高端大气上档次的诺基亚黄屏手机正在播放:社会摇。。。
//高端大气上档次的诺基亚黄屏手机正在播放:一人饮酒醉
--------------------------------优雅的分割线------------------------------------
放在最后的,是我在实际开发中一直都在用的一个转换工具类,封装和重载的功能还是蛮够用的,贴出来分享一下吧 ̄へ ̄
import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* 转换工具类,支持时间格式转换、数字格式转换等
* @author LinkedBear
*/
public class FormatUtil {
public static final String DATE_PATTERN = "yyyy-MM-dd";
public static final String TIME_PATTERN = "yyyy-MM-dd HH:mm";
private static SimpleDateFormat formatDate = new SimpleDateFormat(DATE_PATTERN);
private static SimpleDateFormat formatTime = new SimpleDateFormat(TIME_PATTERN);
private static DecimalFormat df = new DecimalFormat("0.00");
/**
* 标准日期转换yyyy-MM-dd(接收Date)
* @author LinkedBear
* @param date
* @return
*/
public static synchronized String formatDate(Date date) {
return formatDate.format(date);
}
/**
* 标准日期转换yyyy-MM-dd(接收Long)
* @author LinkedBear
* @param date
* @return
*/
public static synchronized String formatDate(Long date) {
return formatDate.format(new Date(date));
}
/**
* 标准时间转换yyyy-MM-dd HH:mm(接收Date)
* @author LinkedBear
* @param date
* @return
*/
public static synchronized String formatMinute(Date date) {
return formatTime.format(date);
}
/**
* 标准时间转换yyyy-MM-dd HH:mm(接收Long)
* @author LinkedBear
* @param date
* @return
*/
public static synchronized String formatMinute(Long date) {
return formatTime.format(new Date(date));
}
/**
* 指定转换格式的日期转换(接收Date)
* @author LinkedBear
* @param date
* @param pattern
* @return
*/
public static String formatDate(Date date, String pattern) {
SimpleDateFormat format = new SimpleDateFormat(pattern);
return format.format(date);
}
/**
* 指定转换格式的日期转换(接收Long)
* @author LinkedBear
* @param date
* @param pattern
* @return
*/
public static String formatDate(Long date, String pattern) {
SimpleDateFormat format = new SimpleDateFormat(pattern);
return format.format(new Date(date));
}
/**
* 标准字符串日期yyyy-MM-dd转成Long
* @author LinkedBear
* @param date
* @return
*/
public static Long parseStringToLong(String date) {
try {
return formatDate.parse(date).getTime();
} catch (ParseException e) {
e.printStackTrace();
return -1L;
}
}
/**
* 字符串时间转成指定格式的Long
* @author LinkedBear
* @param date
* @param pattern
* @return
*/
public static Long parseStringToLong(String date, String pattern) {
SimpleDateFormat format = new SimpleDateFormat(pattern);
try {
return format.parse(date).getTime();
} catch (ParseException e) {
e.printStackTrace();
}
return -1L;
}
/**
* 将金额数值格式化为两位小数
* @author LinkedBear
* @param num
* @return
*/
public static synchronized String formatMoney(long num) {
return df.format(num);
}
/**
* 将金额数值格式化为两位小数
* @author LinkedBear
* @param num
* @return
*/
public static synchronized String formatMoney(double num) {
return df.format(num);
}
private FormatUtil() {
}
}