房屋出租系统:
我们通常设置一个模式策略图:
在这个Houserent大包下面建立这四个子小包
源代码如下:
1.测试类HouseRentApp
2.建立一个House类
package JavaSE练习代码.Houserent.domin;
/**
* 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;
}
@Override
public String toString() {
return id +
"\t" + name +
"\t"+ phone +
"\t" + address+
"\t" + rent +
"\t" + state ;
}
}
3.界面展示类:HouseView
package JavaSE练习代码.Houserent.view;
import JavaSE练习代码.Houserent.domin.House;
import JavaSE练习代码.Houserent.service.HouseSevice;
import java.util.Scanner;
/**
* 1.显示界面
* 2.接收用户的输入
* 3.调用HouseSerivce完成对房屋信息的各种操作
*/
public class HouseView {
private boolean loop=true;
private int key;
//new一个HouseSevice对象,并且初始化它的size大小
private HouseSevice houseSevice=new HouseSevice(10);
public void exit(){
Scanner scanner=new Scanner(System.in);
do {
System.out.println("请输入(1)表示你确认真的要退出系统");
int leo=scanner.nextInt();
if(leo==1){
loop=false;
}
}while (loop);
}
/**
* 显示界面
* 1.add添加
* 2.del删除
* 3.find查找
* 4.update修改
* 5.显示房屋列表
*/
//1.
public void add(){
Scanner scanner=new Scanner(System.in);
System.out.println("===========添加房屋=================");
System.out.print("姓名:");
String name=scanner.next();
System.out.print("电话:");
String phone=scanner.next();
System.out.print("地址:");
String address=scanner.next();
System.out.print("月租:");
int rent=scanner.nextInt();
System.out.print("状态:");
String state=scanner.next();
//创建一个新的House对象,但是注意id这个号是系统自动递增分配的
House house=new House(0,name,phone,address,rent,state);
if(houseSevice.add(house)){
System.out.println("添加房屋成功");
} else {
System.out.println("添加房屋失败");
}
}
//2.
public void del(){
Scanner scanner=new Scanner(System.in);
System.out.println("===========删除房屋=================");
System.out.println("请输入删除房屋的编号(-1退出)");
int delId=scanner.nextInt();
if(delId==-1){
System.out.println("放弃删除房屋信息");
return;//退出方法
}
System.out.println("请输入(1)表示确定 输入其他的字符表示放弃删除");
int choice=scanner.nextInt();
if(choice==1){
if(houseSevice.del(delId)){
System.out.println("删除成功");
} else {
System.out.println("对不起,删除失败,该号码不存在");
}
} else {
System.out.println("放弃删除房屋信息");
}
}
//3.
public void finById(){
Scanner scanner=new Scanner(System.in);
System.out.println("===========查找房屋=================");
System.out.println("请您输入你要查找的房屋编号");
int id=scanner.nextInt();
House house=houseSevice.findById(id);
if(house!=null){
System.out.println("恭喜您查找成功哦,宝贝");
System.out.println(house);
} else {
System.out.println("查找失败,无此房屋");
}
}
//4.修改
public void update(){
Scanner scanner=new Scanner(System.in);
System.out.println("===========修改房屋=================");
System.out.println("请您输入你要修改的房屋编号");
int updateid=scanner.nextInt();
House house= houseSevice.findById(updateid);
if(house==null){
System.out.println("不好意思,没有此房屋,输入错误");
return;
}
System.out.print("姓名("+house.getName()+"):");
String name=scanner.next();
if(!"".equals(name)){
house.setName(name);
}
System.out.print("电话("+house.getPhone()+"):");
String phone=scanner.next();
if(!"".equals(phone)){
house.setPhone(phone);
}
System.out.print("地址("+house.getAddress()+"):");
String address=scanner.next();
if(!"".equals(address)){
house.setAddress(address);
}
System.out.print("租金("+house.getRent()+"):");
int rent=scanner.nextInt();
if(rent>0){
house.setRent(rent);
}
System.out.print("状态("+house.getAddress()+"):");
String state=scanner.next();
if(!"".equals(state)){
house.setAddress(state);
}
}
//5.
public void listHouse(){
System.out.println("===========房屋列表=================");
System.out.println("编号\t\t房主\t\t电话\t\t地址\t\t月租\t\t状态(未出租/出租)");
House[] houses=houseSevice.list();//返回的是这个房屋数组对象
for (int i = 0; i < houses.length; i++) {
//如果房屋为空了,那么就应该直接退出for循环
if(houses[i]==null){
break;
}
System.out.println(houses[i]);
}
System.out.println("===========房屋列表显示完毕============");
}
/**
* 显示主菜单
*/
public void mainMenu(){
do {
System.out.println("=========房屋出租系统菜单========");
/**
* ctrl+向下箭头:快速复制当前行
*/
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.println("请输入当前选择数字(1-6)");
Scanner scanner=new Scanner(System.in);
key=scanner.nextInt();
switch (key){
case 1:
add();
break;
case 2:
finById();
break;
case 3:
del();
break;
case 4:
update();
break;
case 5:
listHouse();
break;
case 6:
exit();
System.out.println("退出");
loop=false;
break;
}
}while (loop);
}
}
4.业务执行逻辑层:HouseService
对于界面的方法逻辑方法进行逻辑的构建:
package JavaSE练习代码.Houserent.service;
import JavaSE练习代码.Houserent.domin.House;
/**
* 1.响应HouseView的调用
* 2.完成对房屋信息的各种操作(增删改查:c[creat]r[read]u[update]d[delete])
*/
public class HouseSevice {
private int houseNums=1;//记录当前有多少个房屋信息
private int idcounter=1;//记录当前id号是多少,由于我们初始化了一个所以默认从1开始
private House[] houses;//保存House对象
//构造器:我们把这个数组大小放到数组中进行初始化
public HouseSevice(int size){
houses=new House[size];//当创建HouseService对象时,指定数组大小
/**
* 构造器中创建的对象,是初始化的信息。。。是为了测验功能的
*/
houses[0]=new House(1,"\tjack","110","北京",2000,"未出租");
}
//list方法 ,我们返回的是houses对象
public House[] list(){
return houses;
}
//添加新对象,返回boolean值
public boolean add(House newhouse){
//判断是否还能继续添加
if(houseNums== houses.length){
System.out.println("数组已满了哦,不可以继续添加");
return false;
}
//把newhouse对象加到数组中
houses[houseNums++]=newhouse;
//搞出来一个递加的学号
newhouse.setId(++idcounter);
return true;
}
//删除对象
public boolean del(int delId){
int index=-1;
for (int i = 0; i < houses.length; i++) {
if(houses[i].getId()==delId){
index=i;//记录要删除对象的下标
}
}
if(index==-1){
return false;
}
//进行删除,为防止空指针异常,那么要判断好i的右边范围
for (int i =index; i <houses.length-1 ; i++) {
houses[i]=houses[i+1];
}
//并且把最后一个搞成null,因为它已经赋给前面了
houses[houseNums-1]=null;
//置空之后就可以进行直接
houseNums--;//减少一个
/**
* 可以把上面两句和为一句:house[--houseNums]=null;
*/
return true;
}
//查找房屋的方法
public House findById(int id){
for (int i = 0; i < houses.length; i++) {
if(houses[i].getId()==id){
return houses[i];
}
}
return null;
}
}
静态变量Static(类变量):
类变量引入:
注意:类的加载一个类只进行一次,因此静态变量只进行一次的初始化
只要是一个静态的变量,我们通过类实例化出的对象引用,进行访问指向的静态变量都是同一个的。
类变量的内存布局:
总结:
1.无论静态变量位于方法区还是位于堆空间,记住一点就是静态变量是被所有实例出的对象引用所共享的。(在JDK8以前,静态变量内存空间位于方法区,JDK8之后,是位于堆空间的Class实例对象的尾部的位置。)
解释:
因为Class对象是由类加载信息出的,将加载出的结构信息存储在方法区。这个静态变量是保存在Class实例对象的尾部,Class对象确实在堆中。
类加载信息可以在堆空间加载出一个Class对象,
2.所以无论它是位于方法区还是堆空间,都是被类Child所有的对象实例所共享的 (即是被类new出来的所有对象的所有对象引用共同指向)
注意:类的加载一个类只进行一次,因此静态变量只进行一次的初始化
类变量的总结细节:
非静态变量有多种叫法,我们要懂得名字的不同:
6.
——————————————————————————————————————————
类方法:
下图是测试类的访问:
先创建两个实例化的对象进行访问改变静态变量的值
之后再通过类名进行访问。
静态方法(类方法)的使用场景:
我们不想要创建实例 ,而是直接通过类名去调用某一个方法的时候,我们把该方法作为静态方法。
举个例子:
类方法(静态方法)的使用细节:
this是代表该类的这个实例化出来的对象的引用,然而类方法和类变量(即是静态方法和静态属性变量)的构建是不依赖于对象的,而是通过类的加载信息而加载出来的【但是注意一点,一个类只进行一次的加载,因此静态成员只进行一次的初始化】,将结构的信息存储在方法区,加载出来的Class对象是位于堆空间上的,静态变量和静态方法的地址都是位于Class实例对象的尾部的。因此我们不可以通过this关键字去访问类方法和类属性。
静态方法只能访问静态成员(即是静态变量或静态方法):
n2为静态变量
say为普通成员方法,hi为静态成员方法
成员包括方法和属性。
————————————————————————————————————————
类(静态)方法和类(静态)变量的练习
依次输出 9 10 11;静态变量count在内存中仅有一份
对于一个类Test,我们只进行一次的类加载。因此静态成员只进行一次的初始化。
错误点:
id是非静态成员变量,因此我们不可以在静态成员方法中进行访问。
构造器相当于是一个非静态的成员方法,我们可以进行访问静态的成员和非静态的成员
答案是
total==4
错误之处:
我解释一下:
首先我们知道,能在静态方法中进行访问的一定是静态成员,当然这里就是为了把传进来的total值赋给我们类中的静态成员变量total,但是不可以用this去访问我们的静态成员。因为this代表的是当前对象,this代表的对象的地址是存放在栈空间中的。【静态变量和静态方法的地址都是位于Class实例对象的尾部的】但是静态成员的创建是不依赖于对象的,而是通过类的加载信息而加载出来的,将结构的信息存储在方法区,加载出来的Class对象是位于堆空间上的,静态变量和静态方法的地址都是位于Class实例对象的尾部的。因此我们不可以通过this关键字去访问类方法和类属性。
我们只可以用类名进行访问来解决这个问题。。。。。PS:如图
深入理解main方法语法
解释上面几点结论:
1.main方法是Java虚拟机进行调用的。
2.因为Java虚拟机调用main方法的时候,它俩不在同一个类中,所以main方法需要public修饰
3.因为Java虚拟机调用main方法的时候不创建对象直接调用,因此必须是static类型
4.执行程序的时候输入的参数就是args数组中的内容。
5.由于该main方法是Java虚拟机进行调用的,所以不需要返回什么,所以无返回值,是void
注意:
如图:
——————————————————————————————————————
如何在idea上传递参数给我们的main方法?
代码块
基本语法:
使用场景:
重点:
在测试类main方法中创建对象
无论调用哪一个构造器,都会优先调用代码块的内容
其实代码块就是对构造器进行协助操作,
当构造器发生重载时,如图:多个构造器的代码重复的过多。我们可以把这些重复的代码放到代码块中,在new一个对象的时候,调用代码块会优先于执行构造器
代码块使用细节(重点):
1.
一个类只进行一次类的加载,因此,一个静态的成员只进行一次的初始化。因此,静态代码块只执行一次。
2.
3.
举例子说明上面三条细节:
创建一个对象实例之后,我们可知,类AA的信息会随着类加载到方法区。。。。
类加载的三种情况:并且记住一个类只可以进行一次的类加载,因此静态成员只可以被初始化一次
——————————————————————————————————————
1.通过该类名去new,创建出来一个实例化对象引用
2.创建子类对象实例
继承的本质是先加载父类再加载子类(先有爸爸再有儿子),先父类的信息加载到方法区在加载子类的信息
3.如图使用类的静态成员 用类名直接调用静态成员
new出来两个对象但是类只加载一次(因为一个类只被加载一次),因此静态代码块只执行一次
普通代码块在new,就是创建对象实例的时候,会被隐式的调用,被加载一次就会被调用一次,但是如果只是用类名调用静态成员的时候,不会调用普通的代码块。。。。。
普通代码块的调用与否不取决于类加载,而是取决于是否创建实例对象。
可以理解为普通代码块就是一个构造器的补充。
并且注意一点,静态代码块只能被类加载调用一次。无论它是通过这个相同的类new出了多少个对象,记住一点就是这个类只进行一次的加载。因此只要是有关静态的成员都只会进行一次的初始化,即是只进行一次的执行调用。
————————————————————————————————————
类加载的方式有两种:
1.通过这个类名去new,创建了一个实例化对象
2.使用类名直接调用静态成员
PS:类加载子类的时候,先类加载父类的信息再类加载子类的信息
3.创建子类实例对象引用也是会让父类进行类加载的
——————————————————————————————————————
代码块重点:调用顺序
第一个:
优先级一样,则按先后顺序进行调用
第二个点:
调用顺序:
构造器的调用优先级是最低的
调用顺序从前到后1 2 3进行顺序执行
倘若优先级相等,那么谁在前面那么就先执行谁。。。。
结论:
注意一个点就是:当一个类继承了一个父类之后:
我们对于一个构造器第一行都会默认是调用父类的无参构造器的,倘若说父类的有参构造器覆盖了父类的无参构造器,那么我们就要用super(父类构造器的参数)进行调用。
解释一下为什么:继承这个东西,子类继承了父类除构造器之外的所有东西,但是构造器也会进行调用父类的构造器,所以出现了super调用构造器这一现象。
调用顺序分析:
第一次new对象的之前,第一步要进行的是类加载,但是代码中并没有静态的成员,因此这一步直接省略过去。
我们new了一个实例对象,调用无参构造器,由于BBB继承了AAA所以这个无参构造器是默认带有一个super(),因为父类是有一个隐藏的无参构造器的。。。。。并且第二步是调用普通代码块(只要对象实例化一次,就调用一次普通代码块),调用AAA类的无参构造器,AAA也有一个super()默认调用父类Object的无参构造器,以及第二步调用AAA本类的普通代码块(但是没有)所以执行顺序一目了然了就。。。。。。。
最难调用顺序:
当我们在main方法中进行测试,即是new B02();创建一个对象
当我们通过一个类名第一次去new一个对象实例的时候我们要进行的步骤为:
1.进行类的加载 ,由于静态代码块的调用是通过类加载进行调用的,因此可知,静态代码块是优先被调用的 。如果一个类继承的有父类,那么我们可知,先进行父类的加载,再进行子类的加载。直到第一步类的加载执行完毕之后,我们才能执行第二步new一个对象
2.创建对象
代码如下:
public B02(){
System.out.println("B02的构造器");
}上图少截取的一段代码
__________________________________________________________________
顺序如图:
挺多的,但是还是要总结一下上面这道题,,:
1.当我们new出来一个子类B02的实例对象的时候,我们要先明白new一个对象时的步骤顺序
值得我们记住的是这一次new对象实例,是我们通过这个类名进行的第一次new对象实例,因此必须进行类的加载,类的加载完成之后,我们才可以进行第二步new对象实例
步骤是这样的:(一定要记住一点,当执行完第一步之后,我们才可以执行第二个步骤的调用)
第一步:先进行类的加载。类的加载让我们明白,我们必须优先把静态的先进行调用完毕,无论是父类还是子类的静态的东西ok,
那么对于静态代码块和静态的成员来说,优先级是一致的,所以我们要按先后顺序进行调用执行。
记住一个顺序,先执行父类的再执行子类的。所以我们可以知道,当父类的所有静态的东西执行完了之后,我们才可以执行子类静态的东西。当父类与子类静态的东西执行完了之后,我们才可以进行下一步步骤。
第二步:创建对象。我们由图可知:new B02();调用的是子类的无参构造器,我们要记住一点,当父类的无参构造器没被覆盖的时候,
我们要分析出 子类构造器是隐藏两步和显示的一步:
1.super()调用父类的无参构造器 2.调用子类普通代码块。
以及显示的第三步 执行子类构造器显示内容
注意一点:只有当第一步调用父类无参构造器执行完之后,我们才会执行第二步 执行完第二步才会执行第三步:执行子类的构造器
ok执行第一步:如下:
当调用父类的无参构造器也是隐藏两步和显示的一步:
1.调用父类Object的无参构造器(即是super();调用)
2.调用父类的普通代码块
以及显示的第三步 执行父类构造器的显示内容
当父类这两步执行完了之后,然后就要执行显示的第三步 执行父类构造器的显示内容
当以上三步执行完之后,我们才会开始执行子类的第二步,因为这三步都是子类第一步super();扩展出来的
——————————————————————————————————————
(PS:当父类的无参构造器被覆盖之后,我们必须只能把子类的无参构造器改为有参构造器,并且用super(父类构造器的参数);调用父类构造器)
再看一次吧:
代码块练习题:
答案:
使用类名进行调用,也是产生类加载,优先级相同,按顺序执行,当静态的东西执行完之后才会在main方法中打印输出结果。并且我们要知道,static静态代码块只可以执行一次。
输出如图所示:
记住一点:在一个类中只要是不加static的,其他的都是普通代码块
例如:
A b=new A(); 这就是一个普通代码块。。。。。所以题中的 Sample类new对象也是一个普通代码块
答案:
单列设计模式:
饿汉式
如何保障只创建一个对象?
这就要用到单例设计模式了:
static静态的东西是类加载中就完成了,我们可以知道:new一个对象实例的时候分为两个步骤:
第一步:类加载
第二步:创建对象
静态的对象只可以类加载一次,因此每一次都是同一个女朋友。
测试类:
私有化构造器是为了防止外部类直接进行创建对象
为什么叫做饿汉式?
因为还没有拿去用这个对象的时候就已经static类加载完成了。。有可能创建了一个对象,但是后来我们并没有去使用它,而是把它浪费了,这就是饿汉行为。
这就是饿汉式的意义所在。。
懒汉式:
但是当我们再一次进行调用的时候,但是还是返回的是上一个对象
这就是懒汉。只会创建一次对象,不会造成资源的浪费。。。。。
线程安全问题依旧存在:
当三个线程同时进来的时候,我们在那一个时间点三个线程就会创建三个对象,但是到最后只会保留一个对象。
经典的饿汉单例模式:
记住一点就行,
当我们new一个对象的时候,要进行两步。第一步是先进行类加载,之后第二步再进行创建对象
但是当类名访问静态成员的时候,只有一步类加载。。。。无创建对象的过程