【Java复健指南09】项目练习全解--房屋出租系统
一个基于文本界面的综合练习,主要用于串联和回忆知识点,比较简单
各个界面的设计样式
主菜单
=============房屋出租系统菜单============
1 新 增 房 源
2 查 找 房 屋
3 删除房屋信息
4 修改房屋信息
5 房 屋 列 表
6 退 出
请输入你的选择(1-6):
新增房源
=============添加房屋============
姓名: jk
电话: 174
地址: 丰台区
月租: 5600
状态:
未出租
查找房源
=============查询房屋信息============
请输入想查询的房屋ID:
1
1 jk1 13544856575 西青区 1800 未出租
删除房源
=============删除房屋信息============
请输入待删除的房屋编号或输入-1退出: 1
请输入你的选择(Y/N): 请小心选择
y
=============删除房屋信息成功============
修改房源
=============修改房屋信息============
请选择待修改房屋编号(-1退出):
2
姓名(jk):jack
电话(174):124
地址(丰台区): 海淀区
租金(5600):5300
状态(未出租):已出租
=============修改房屋信息成功============
房屋列表
=============房屋列表============
编号 房主 电话 地址 月租 状态(未出租/已出租)
2 jack 124 海淀区 5300 已出租
3 gg 485 昌平区 1800 已出租
=============房屋列表显示完毕============
退出系统
=============房屋出租系统菜单============
1 新 增 房 源
2 查 找 房 屋
3 删除房屋信息
4 修改房屋信息
5 房 屋 列 表
6 退 出
请输入你的选择(1-6):6
请输入你的选择(Y/N): 请小心选择
y
========已退出========
项目设计
相较之前的练习来说,这个程序体量已经达到小项目级别。因此,需要使用合理的方式进行规划
使用分层模式对软件各部分功能进行设计规划
步骤:
1、明确系统有哪些类
2、确定类之间的关系
系统希望通过文字界面进行展示
那么显示界面需要一个类实现【HouseView.java】
显示界面中的各项功能背后需要的业务逻辑代码应该单独放在一个类中,即该类用于相应主界面类的各项服务【HouseService.java】
在系统中,对于各种数据的操作是通过房屋类间接进行的【House.java】,其中定义了一间房屋应该包含的属性与方法
三大主类基本完成了,现在需要一个调用各对象的类作为程序的入口,即【HouseRentApp.java】
上述类的关系如图所示
ps:Utility类作为工具类是提前预制的,无需纠结,主要负责判断输入是否合法等
项目结构如下
功能实现
实现主菜单
理清关系后,最开始肯定先写界面嘛,即HouseView中定义一个显示主菜单的方法【mainMenu】
//显示主菜单
//仍然是用之前提到的do-while循环实现显示,loop标志位控制是否结束(退出系统)显示
public void mainMenu(){
do{
System.out.println("=============房屋出租系统菜单============");
System.out.println("\t\t\t1 新 增 房 源");
System.out.println("\t\t\t2 查 找 房 屋");
System.out.println("\t\t\t3 删除房屋信息");
System.out.println("\t\t\t4 修改房屋信息");
System.out.println("\t\t\t5 房 屋 列 表");
System.out.println("\t\t\t6 退 出");
System.out.print("请输入你的选择(1-6):");
key = Utility.readChar();
switch (key) {//以下是菜单中各功能的实现方法,刚开始没写完可以先用sout占位
case '1':
addHouse();
break;
case '2':
findHouse();
break;
case '3':
delHouse();
break;
case '4':
update();
break;
case '5':
listHouses();
break;
case '6':
exit();
loop = false;
break;
}
}while (loop);
注意,界面类的方法只负责界面部分的功能,对于数据的相关操作需要在业务类定义
界面类只负责引用相关业务方法
定义业务类
/*
* //定义House[],保存House对象
1.响应HouseView的调用
2.完成对房屋信息的各种操作
* (增删改查c[create]r[read]u[update]d[delete])*/
public class HouseService {
}
实现房屋列表
接下来实现对现有房屋的展示,即房屋列表的展示【listHouses()】
为什么先实现这个功能呢?因为能够展示房屋意味着我们需要完成对房屋类的定义,这是后面的功能也需要的
因此作为基础部分,需要先做这个房屋列表功能
//编写listHouses()显示房屋列表
//其中使用业务类的方法,实例化并返回房屋对象House数组
//因此需要先对House进行定义
public void listHouses(){
System.out.println("=============房屋列表============");
System.out.println("编号\t\t房主\t\t电话\t\t地址\t\t月租\t\t状态(未出租/已出租)");
House[] houses = houseService.list();//返回所有房屋信息,保存在houses数组中
for (int i = 0; i < houses.length; i++) {
if (houses[i] == null) {
break;
}
System.out.println(houses[i]);
}
System.out.println("=============房屋列表显示完毕============");
}
业务方法list()
list()需要返回系统用于存放房屋信息的一个数组,数组元素为房屋对象
通过遍历此数组可以获取所有房屋的信息
/*
* //定义House[],保存House对象
1.响应HouseView的调用
2.完成对房屋信息的各种操作
* (增删改查c[create]r[read]u[update]d[delete])*/
public class HouseService {
private House[] house;
//构造器
public HouseService(int size){
house = new House[size];//创建HouseService对象时,需要指定House数组的大小
//测试,初始化一个默认HouseService对象
house[0] = new House(1, "jk", "13544856575","西青区", 1800,"未出租");
}
//list方法返回house(数组)
public House[] list(){
return house;
}
}
这里已经需要使用房屋类了,自然的我们需要编写房屋类
定义房屋类
房屋类House.java如下
/*
* House的对象表示一个房屋信息*/
public class House {
//需要以下信息
//编号 房主 电话 地址 月租 状态(未出租/已出租)
private int id;
private String name;
private String phone;
private String address;
private int rent;
private String state;
public House(int id, String name, String phone, String address, int rent, String state) {
this.id = id;
this.name = name;
this.phone = phone;
this.address = address;
this.rent = rent;
this.state = state;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public int getRent() {
return rent;
}
public void setRent(int rent) {
this.rent = rent;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
//为了方便的输出对象信息,需要实现toString方法
//比如有House h1,这样直接sout(h1)就可以打印房屋信息了
@Override
public String toString() {
return id +
"\t" + name +
"\t" + phone +
"\t" + address +
"\t" + rent +
"\t" + state ;
}
}
实现房屋添加
实现房屋列表之后,实际上房屋类也完成了定义
那么现在可以考虑实现房屋信息的添加
每个房屋添加进来应该给一个编号,这是需要注意的,后续需要通过编号查询
//编写addHouse()接受输入,创建House对象,调用add方法
//addHouse()仅负责在界面层面的添加,实际去操控House对象数组完成添加的方法属于具体的业务功能
//即add()方法应该在HouseService.java中定义,在HouseView.java的addHouse()调用,遵循之前的类关系图设计
public void addHouse(){
System.out.println("=============添加房屋============");
System.out.print("姓名: ");
String name = Utility.readString(8);
System.out.print("电话: ");
String phone = Utility.readString(12);
System.out.print("地址: ");
String address = Utility.readString(16);
System.out.print("月租: ");
int rent = Utility.readInt();
System.out.println("状态: ");
String state = Utility.readString(3);
//按照输入信息,创建一个新的House对象
//id是系统分配
House newHouse = new House(0, name, phone, address, rent, state);
if(houseService.add(newHouse)){
System.out.println("=============添加房屋成功============");
}else {
System.out.println("=============添加房屋失败============");
}
}
业务方法add()
需要在该业务方法中实现对房屋对象数组的新增数据操作
并且需要实现对房屋信息的自动编号
public class HouseService {
private House[] house;
private int houseNums = 1;//记录当前有多少个房屋信息
private int idCounter = 1;//记录当前的id增长到的值
//构造器
public HouseService(int size){
house = new House[size];//创建HouseService对象时,需要指定House数组的大小
//测试,初始化一个HouseService对象
house[0] = new House(1, "jk", "13544856575","西青区", 1800,"未出租");
}
//list方法返回house(数组)
public House[] list(){
return house;
}
//add方法,添加新对象,返回boolean
//输入参数是House对象
public boolean add(House newHouse){
//判断是否还可以继续添加(暂时不考虑数组扩容问题)
if(houseNums == house.length){//若房屋信息数量大于数组长度则不能再加
System.out.println("数组已满,无法添加");
return false;
}
//数组还没满的话把newHouse对象加入到数组,同时房屋数量计数+1
// house[houseNums] = newHouse;
// houseNums ++;//新增一个房屋
//上述代码可以利用"后++"的特性整合
//houseNums++的值是运行一次之后才会自增的
//例如,当前houseNums为1,数组设定为10,新增房屋信息肯定是可以的
//因此,在数组索引为1的地方加入newHouse,之后houseNums自增为2
house[houseNums++] = newHouse;//后++
//需要设计一个id自增长机制,然后更新newHouse的id
// idCounter ++;
// newHouse.setId(idCounter);
newHouse.setId(++idCounter);//前++,同理
return true;
}
}
实现房屋信息删除
增删改查,那么现在应该实现房屋信息的删除
这里可以根据房屋信息的编号进行删除,也可以根据房屋的具体信息,例如:地址、姓名、电话等
以房屋编号为例
//编写delHouse() 接收输入的id号
//与addHouse()类似,最终的删除逻辑在业务类中实现,这边只是调用相关方法
public void delHouse(){
System.out.println("=============删除房屋信息============");
System.out.print("请输入待删除的房屋编号或输入-1退出: ");
int delId = Utility.readInt();//获取一个输入
if(delId == -1){
System.out.println("=============放弃删除房屋信息============");
return;
}
//该方法本身就有循环判断逻辑,必须输入Y/N才能退出
char choice = Utility.readConfirmSelection();
if(choice=='Y'){//真的删除就调用业务方法
if(houseService.del(delId)){
System.out.println("=============删除房屋信息成功============");
}else {
System.out.println("=============房屋编号不存在,删除失败============");
}
}else {
System.out.println("=============放弃删除房屋信息============");
}
}
业务方法del()
需要在该业务方法中,按房屋编号实现对房屋对象数组元素的删除操作
//del方法,删除一个房屋信息
public boolean del(int delId){
//应先找到删除房屋的对应下标,房屋编号与下标不对应
int index = -1;
for (int i = 0; i < houseNums; i++) {
if(delId == house[i].getId()){
//要删除的房屋(id),是数组下标为i的元素
index = i;//记录i
}
}
if(index == -1){//说明delId在数组中不存在
return false;
}
//如果找到应该怎么删除?
//待删除元素(使用下标确定)的后一个元素前移,覆盖待删除元素
//然后最后一位,置为null,重复上述操作
for (int i = index; i < houseNums - 1; i++) {
house[i] = house[i+1];
}
//置空最后一个元素
//"前--"表示直接取用houseNums减一后的值,详见业务方法add()中对于"后++"的解释
// house[houseNums - 1] = null;
// houseNums--;//少一个元素
house[--houseNums] = null;//把当前存在的房屋信息的最后一个置空
return true;
}
实现房屋查找
"增删改查"中的查询,依旧是先在界面类中给出功能findHouse(),调用业务方法实现房屋查询
//根据id查找房屋(改进,通过地址查询)
public void findHouse(){
System.out.println("=============查询房屋信息============");
System.out.println("请输入想查询的房屋ID: ");
int findId = Utility.readInt();
House houses = houseService.findById(findId);//返回所有房屋信息,保存在houses数组中
if(houses ! = null){
System.out.println(houses);
}else {
System.out.println("无查询结果,请输入正确的房屋ID");
}
}
业务方法findById()
查询的具体方式也是根据编号来,这样比较简单。后续更新可以加入按其他属性查询的功能(摸了)
//findById()方法,根据id查找房屋,返回House对象或者null
public House findById(int findId){
//遍历房屋数组
for (int i = 0; i < houseNums; i++) {
if(findId == house[i].getId()){
return house[i];
}
}
return null;
}
实现房屋信息更新
更新房屋信息其实也包含着查询动作,先查出来才能更新,因此业务上不用写新的方法,复用findById()即可
//根据id修改房屋信息
//调findById就行,不用在HouseService中添加新业务功能
public void update(){
System.out.println("=============修改房屋信息============");
System.out.println("请选择待修改房屋编号(-1退出):");
int updateId = Utility.readInt();
if(updateId == -1){
System.out.println("=============已放弃修改============");
return;
}
//根据输入的ID查找对象
//因为这里是引用对象,所以原数组是会被同步改动的
House house = houseService.findById(updateId);
if(house==null){
System.out.println("=============房屋信息不存在。。。=============");
return;
}
//若房屋存在
System.out.print("姓名("+house.getName()+"):");
String name = Utility.readString(10,"");//若用户直接回车则默认""
if (!"".equals(name)) {//如果不是空的就用get方法修改
house.setName(name);
}
System.out.print("电话(" + house.getPhone() + "):");
String phone = Utility.readString(12, "");
if (!"".equals(phone)) {
house.setPhone(phone);
}
System.out.print("地址(" + house.getAddress() + "): ");
String address = Utility.readString(18, "");
if (!"".equals(address)) {
house.setAddress(address);
}
System.out.print("租金(" + house.getRent() + "):");
int rent = Utility.readInt(-1);
if (rent != -1) {
house.setRent(rent);
}
System.out.print("状态(" + house.getState() + "):");
String state = Utility.readString(3, "");
if (!"".equals(state)) {
house.setState(state);
}
System.out.println("=============修改房屋信息成功============");
}
实现方式有点质朴,其实还可以优化的,下次再说
总结
汇总一些用到的编程技巧
trick1
在编写稍大一点的程序时,需要将页面和业务分离
trick2
对象数组也可以初始化
trick3
删除数组中元素的技巧:覆盖删除【来着业务方法del()】
//待删除元素(使用下标确定)的后一个元素前移,覆盖待删除元素
//然后最后一位,置为null,重复上述操作
for (int i = index; i < houseNums - 1; i++) {
house[i] = house[i+1];
}
trick4
"后++"对于精简代码的实际应用【业务方法add()】
// house[houseNums] = newHouse;
// houseNums ++;//新增一个房屋
//上述代码可以利用"后++"的特性整合
//houseNums++的值是运行一次之后才会自增的
//例如,当前houseNums为1,数组设定为10,新增房屋信息肯定是可以的
//因此,在数组索引为1的地方加入newHouse,之后houseNums自增为2
house[houseNums++] = newHouse;//后++
完整代码
HouseView.java
package com./.../.houserent.view;
import com./.../.houserent.domain.House;
import com./.../.houserent.service.HouseService;
import com./.../.houserent.utils.Utility;
/**
* 1.显示界面
* 2.接收用户的输入
* 3.调用HouseService完成对房屋信息的各种操作
*/
public class HouseView {
private boolean loop = true;//控制显示菜单
private char key = ' ';//接受用户选择
private HouseService houseService = new HouseService(10);//数组大小10
//根据id修改房屋信息
//调findById就行,不用在HouseService中添加新业务功能
public void update(){
System.out.println("=============修改房屋信息============");
System.out.println("请选择待修改房屋编号(-1退出):");
int updateId = Utility.readInt();
if(updateId == -1){
System.out.println("=============已放弃修改============");
return;
}
//根据输入的ID查找对象
//因为这里是引用对象,所以原数组是会被同步改动的
House house = houseService.findById(updateId);
if(house==null){
System.out.println("=============房屋信息不存在。。。=============");
return;
}
//若房屋存在
System.out.print("姓名("+house.getName()+"):");
String name = Utility.readString(10,"");//若用户直接回车则默认""
if (!"".equals(name)) {//如果不是空的就用get方法修改
house.setName(name);
}
System.out.print("电话(" + house.getPhone() + "):");
String phone = Utility.readString(12, "");
if (!"".equals(phone)) {
house.setPhone(phone);
}
System.out.print("地址(" + house.getAddress() + "): ");
String address = Utility.readString(18, "");
if (!"".equals(address)) {
house.setAddress(address);
}
System.out.print("租金(" + house.getRent() + "):");
int rent = Utility.readInt(-1);
if (rent != -1) {
house.setRent(rent);
}
System.out.print("状态(" + house.getState() + "):");
String state = Utility.readString(3, "");
if (!"".equals(state)) {
house.setState(state);
}
System.out.println("=============修改房屋信息成功============");
}
//根据id查找房屋(改进,通过地址查询)
public void findHouse(){
System.out.println("=============查询房屋信息============");
System.out.println("请输入想查询的房屋ID: ");
int findId = Utility.readInt();
House houses = houseService.findById(findId);//返回所有房屋信息,保存在houses数组中
if(houses != null){
System.out.println(houses);
}else {
System.out.println("无查询结果,请输入正确的房屋ID");
}
}
//完成退出确认
public void exit() {
//这里我们使用Utility提供方法,完成确认
char c = Utility.readConfirmSelection();
if (c == 'Y') {
loop = false;
}
}
//编写delHouse() 接收输入的id号
//与addHouse()类似,最终的删除逻辑在业务类中实现,这边只是调用相关方法
public void delHouse(){
System.out.println("=============删除房屋信息============");
System.out.print("请输入待删除的房屋编号或输入-1退出: ");
int delId = Utility.readInt();
if(delId == -1){
System.out.println("=============放弃删除房屋信息============");
return;
}
//该方法本身就有循环判断逻辑,必须输入Y/N才能退出
char choice = Utility.readConfirmSelection();
if(choice=='Y'){//真的删除
if(houseService.del(delId)){
System.out.println("=============删除房屋信息成功============");
}else {
System.out.println("=============房屋编号不存在,删除失败============");
}
}else {
System.out.println("=============放弃删除房屋信息============");
}
}
//编写addHouse()接受输入,创建House对象,调用add方法
//addHouse()仅负责在界面层面的添加,实际去操控House对象数组完成添加的方法属于具体的业务功能
//即add()方法应该在HouseService.java中定义,在HouseView.java的addHouse()调用,遵循之前的类关系图设计
public void addHouse(){
System.out.println("=============添加房屋============");
System.out.print("姓名: ");
String name = Utility.readString(8);
System.out.print("电话: ");
String phone = Utility.readString(12);
System.out.print("地址: ");
String address = Utility.readString(16);
System.out.print("月租: ");
int rent = Utility.readInt();
System.out.println("状态: ");
String state = Utility.readString(3);
//创建一个新的House对象
//id是系统分配
House newHouse = new House(0, name, phone, address, rent, state);
if(houseService.add(newHouse)){
System.out.println("=============添加房屋成功============");
}else {
System.out.println("=============添加房屋失败============");
}
}
//编写listHouses()显示房屋列表
//其中实例化调用了房屋对象House,并使用了对象数组
//因此需要先对House进行定义
public void listHouses(){
System.out.println("=============房屋列表============");
System.out.println("编号\t\t房主\t\t电话\t\t地址\t\t月租\t\t状态(未出租/已出租)");
House[] houses = houseService.list();//返回所有房屋信息,保存在houses数组中
for (int i = 0; i < houses.length; i++) {
if (houses[i] == null) {
break;
}
System.out.println(houses[i]);
}
System.out.println("=============房屋列表显示完毕============");
}
//显示主菜单
//仍然是用之前提到的do-while循环实现显示,loop标志位控制是否结束(退出系统)显示
public void mainMenu(){
do{
System.out.println("=============房屋出租系统菜单============");
System.out.println("\t\t\t1 新 增 房 源");
System.out.println("\t\t\t2 查 找 房 屋");
System.out.println("\t\t\t3 删除房屋信息");
System.out.println("\t\t\t4 修改房屋信息");
System.out.println("\t\t\t5 房 屋 列 表");
System.out.println("\t\t\t6 退 出");
System.out.print("请输入你的选择(1-6):");
key = Utility.readChar();
switch (key) {
case '1':
addHouse();
break;
case '2':
findHouse();
break;
case '3':
delHouse();
break;
case '4':
update();
break;
case '5':
listHouses();
break;
case '6':
exit();
loop = false;
break;
}
}while (loop);
}
}
HouseService.java
package com./.../.houserent.service;
import com./.../.houserent.domain.House;
/*
* //定义House[],保存House对象
1.响应HouseView的调用
2.完成对房屋信息的各种操作
* (增删改查c[create]r[read]u[update]d[delete])*/
public class HouseService {
private House[] house;
private int houseNums = 1;//记录当前有多少个房屋信息
private int idCounter = 1;//记录当前的id增长到的值
//构造器
public HouseService(int size){
house = new House[size];//创建HouseService对象时,需要指定House数组的大小
//测试,初始化一个HouseService对象
house[0] = new House(1, "jk", "13544856575","西青区", 1800,"未出租");
}
//list方法返回house(数组)
public House[] list(){
return house;
}
//findById()方法,根据id查找房屋,返回House对象或者null
public House findById(int findId){
//遍历房屋数组
for (int i = 0; i < houseNums; i++) {
if(findId == house[i].getId()){
return house[i];
}
}
return null;
}
//del方法,删除一个房屋信息
public boolean del(int delId){
//应先找到删除房屋的对应下标,房屋编号与下标不对应
int index = -1;
for (int i = 0; i < houseNums; i++) {
if(delId == house[i].getId()){
//要删除的房屋(id),是数组下标为i的元素
index = i;//记录i
}
}
if(index == -1){//说明delId在数组中不存在
return false;
}
//如果找到应该怎么删除?
//待删除的下标位置的元素的后一个元素前移,覆盖待删除元素
//然后最后一位,置为null,重复上述操作
for (int i = index; i < houseNums - 1; i++) {
house[i] = house[i+1];
}
//置空最后一个元素
// house[houseNums - 1] = null;
// houseNums--;//少一个元素
house[--houseNums] = null;//把当前存在的房屋信息的最后一个置空
return true;
}
//add方法,添加新对象,返回boolean
public boolean add(House newHouse){
//判断是否还可以继续添加(暂时不考虑数组扩容问题)
if(houseNums == house.length){//不能再加
System.out.println("数组已满,无法添加");
return false;
}
//把newHouse对象加入到数组
// house[houseNums] = newHouse;
// houseNums ++;//新增一个房屋
//上述代码可以利用"后++"的特性整合
//houseNums++的值是运行一次之后才会自增的
//例如,当前houseNums为1,数组设定为10,新增房屋信息肯定是可以的
//因此,在数组索引为1的地方加入newHouse,之后houseNums自增为2
house[houseNums++] = newHouse;//后++
//需要设计一个id自增长机制,然后更新newHouse的id
// idCounter ++;
// newHouse.setId(idCounter);
newHouse.setId(++idCounter);//前++
return true;
}
}
House.java
package com./.../.houserent.domain;
/*
* House的对象表示一个房屋信息*/
public class House {
//编号 房主 电话 地址 月租 状态(未出租/已出租)
private int id;
private String name;
private String phone;
private String address;
private int rent;
private String state;
public House(int id, String name, String phone, String address, int rent, String state) {
this.id = id;
this.name = name;
this.phone = phone;
this.address = address;
this.rent = rent;
this.state = state;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public int getRent() {
return rent;
}
public void setRent(int rent) {
this.rent = rent;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
//为了方便的输出对象信息,需要实现toString方法
//比如有House h1,这样直接sout(h1)就可以打印房屋信息了
@Override
public String toString() {
return id +
"\t" + name +
"\t" +phone +
"\t" + address +
"\t" + rent +
"\t" + state ;
}
}
HouseRentApp.java
package com./.../.houserent;
import com./.../.houserent.view.HouseView;
public class HouseRentApp {
public static void main(String[] args) {
//创建HouseView对象,显示界面,是程序的主入口
new HouseView().mainMenu();
System.out.println("========已退出========");
}
}