内部类+图书管理系统
内部类主要有以下几种:
- 实例内部类
- 静态内部类
- 局部内部类
- 匿名内部类
为什么会有内部类呢?当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服务,那么这个内部的完整结构最好使用内部类。在 Java 中,可以将一个类定义在另一个类或者一个方法的内部,前者称为内部类,后者称为外部类。内部类也是封装的一种体现。(就是 整体与部分 的关系)
1. 实例内部类
1.1 实例内部类的结构
实例内部类就是定义在另一个类里面,并且没有什么其他的修饰符修饰
class OutClass { class InnerClass1{ } }
就把内部类当成一个普通的类,也可以定义成员变量和成员方法。
class Out{ public int date1=1; public int date2=2; //内部类 class Inner{ public int date3=3; public static final int date4=4; public int date2=22; public void fun(){ System.out.println("Inner::fun"); System.out.println(date1); System.out.println(date3); System.out.println(date2); System.out.println(Out.this.date2); } } Inner inner = new Inner(); public void fun(){ System.out.println("Out::fun"); System.out.println(inner.date3); } }
1.2 实例内部类的一些问题
1.2.1 如何在main中创建实例内部类对象?
在main中我们并不能直接创建一个内部类对象,必须先创建一个外部类对象,再利用外部类创建内部类对象。
public class Test { public static void main(String[] args) { Out out = new Out(); Out.Inner inner = out.new Inner(); } }
1.2.2 内部类成员变量被static修饰问题?
如果内部类的成员变量被static修饰,那么同时也要被final修饰。
class Out{ class Inner{ //同时被static和final修饰 public static final int date4=4; } }
1.2.3 内部类和外部类变量重名的调用问题?
在内部类中,肯定是“就近原则”,首先看自己有没有这个名字的变量,再看外部类有没有,若都没有就报错。(简而言之——“就近原则”)
若是非要调用外部类的重名变量,就用 外部类名称.this.同名成员 来访问
实例内部类的非静态方法中包含了一个指向外部类对象的引用
class Out{ public int date1=1; public int date2=2; class Inner{ public int date3=3; public static final int date4=4; public int date2=22; public void fun(){ System.out.println("Inner::fun"); System.out.println(date1); System.out.println(date3); //这个date2是调用内部类的 System.out.println(date2); //这个date2是调用外部类的 System.out.println(Out.this.date2); } } Inner inner = new Inner(); public void fun(){ System.out.println("Out::fun"); System.out.println(inner.date3); } }
1.2.4 外部类访问内部类变量的问题
外部类不能直接访问内部类的普通成员变量以及成员方法,必须现在外部类中实例化一个内部类对象,才能进行访问。
class Out{ class Inner{ public int date3=3; } //先要实例化一个内部类对象 Inner inner = new Inner(); public void fun(){ System.out.println("Out::fun"); //通过内部类对象进行访问 System.out.println(inner.date3); } }
2. 静态内部类
2.1 静态内部类的结构
静态内部类和实例内部类的结构没什么不一样,就是多了一个 “stastc” 访问修饰限定符。
class OutClass { class static InnerClass1{ } }
2.2 静态内部类的一些问题
2.2.1 如何在mian中创建对象?
这个时候已经不需要先创建一个外部类的对象,再创建内部类对象,我们可以直接创建内部类的对象(不依赖与外部类)
public class Test { public static void main(String[] args) { //是需要用到外部类名,但是不需要外部类对象了 Out.Inner inner = new Out.Inner(); } }
2.2.2 在静态内部类中访问外部非静态内部类问题?
静态内部类的方法中只能够访问外部类中静态的成员变量,不能够访问普通的成员变量。
class Out{ public int date1=1; public static int date5=2; static class Inner{ public int date3=3; //对于静态内部类,static不再需要配合final使用 public static int date4=4; public void fun(){ System.out.println("Inner::fun"); //这个时候会报错,因为date1是普通的成员变量 System.out.println(date1); System.out.println(date3); //date5不会报错,因为是静态变量 System.out.println(date5); } } }
3. 局部内部类
局部内部类就是在方法中定义一个类,并且只能够在这个方法中使用,语法什么的没有什么特别之处,并且很少使用,因此就不多做讲解。
4. 匿名内部类
我们都知道,接口是不可以实例化的,但是我们可以通过内部类让它间接实例化。
interface Run{ void test(); } public class Test { public static void main(String[] args) { //间接实例化 Run run=new Run() { @Override public void test() { } }; } }
其实按道理接口是不可以实例化的,上面这一串代码之所以可以是因为其等价于:有一个类实现了这个接口,并且在main方法里面创建了这个类的对象(发生了向上转型),因为这个类并没有具体实现的代码,因此说是匿名内部类。
5. 图书管理系统的基本功能
首先,这个图书管理系统有 管理员 和 普通用户 ,不同的身份所能使用的功能不同。
管理员:
普通用户:
然后可以使用自己对应的功能,以“管理员”-“显示图书”为例:
6. 图书管理系统的设计逻辑
用java设计一个基本的东西,我们要遵循 “找对象” 、“创建对象”、“使用对象”
对于图书管理系统,我们有 ①与书相关的:书、书架;②操作类:所有的功能(查找图书、添加图书等);③管理者和普通用户这个User类;
6.1 Book类
成员变量有:名字、作者、价格、类型、是否借出;(为了体现封装的特性,我们所有的变量都用 private 修饰)
然后基本的成员方法,必须得到这些变量和设置这些变量(get and set)
我们还需要打印一本书的相关信息(toString)
注:我们可以对打印的形式进行改写,比如我就是通过使用三目运算符,改变了“是否借出”的打印
然后我们还要定义一个构造方法
注:isBorrowed 默认就是false,因此不需要在构造方法里面初始化
6.2 BookList类
成员变量就只有两个: ①Book数组;②当前书架书的数量
注:这里用 DEFAULT_CAPACITY 记录数组的长度,是为了方便修改;在书架上先放了三本书;
成员方法也有最基本的 get、set
注:这里的setBook 和 getBook 需要我们自己去写
6.3 Operation
我们把每一个操作都写为一个类,由于每个操作都是对书架进行操作,因此我们可以定义一个接口,然后每个操作都继承这个接口
注:继承接口这一步非常重要,因为这样可以统一所有的操作,然后还可以把所有的或者几个操作放到一个数组里面,实现多态
接口IOperation
6.4 User
我们有“管理员”和“普通用户”两种身份,然后他们又都是用户,因此我们可以用一个父类User来抽取他们共有的属性
注:使用父类也可以实现“管理员”和“普通用户”的统一,也能够实现多态;并且从这里也可以看出“父类”与“接口”的区别
父类User
管理员AdminUser
注:不同的身份,所能够执行的功能不同,也就是“菜单”不同,因此我们要把菜单写上,并且这个菜单还要让我们进行选择
普通用户NormalUser
注:无论是管理员还是普通用户,都要有构造方法,并且先对父类进行构造,然后根据自己的功能声明IOperation数组的内容
6.5 Main进行统一,用来实现整个程序
在这一步中,巧妙地使用了 login() 这个方法,完成了身份的选择以及第一步向上转型
然后通过while循环,使得我们能够多次选择
7. 图书管理系统的细节以及操作方法的实现
7.1 FindOperation
注:1. 一般来说都需要用 currentSize 用来接收当前书架上的书的数量,基本上每个操作都需要用到
注:2. 然后注意获得书架上某一本书的信息的方法: Book book = bookList.getBook(i) ;
注:3. 最后是如何判断是否找到了这本书:book.getName().equals(name);
7.2 AddOperation
注:1. 我们有两个检验,检验是否书架满了,检验要添加的书书架上是否存在
注:2. 学习这里的方法,如何添加一本书,要先给出添加书籍的基本的信息,然后利用 bookList.setBook这个方法进行添加,并且还要修改书架上书的数量
7.3 ExitOperation
注:1. 这里只需要学习退出系统的方法即可
7.4 DelOperation
注:1. 这里我们同样需要检验,判断书架上时候有书籍,然后还需要检验你要删除的书籍书架上是否存在
注:2. 这里删除书籍有一个小的算法,就是用后一项代替前一项,然后最后一项设置为null,也就是图中圈起来的部分,但是我们这里要改变bookList里面books数组里面的内容,必须使用bookList.setBook(j,book1)这个方法,不能像下面的图一样,这样不能够完成修改
8. 图书管理系统的总结和改进
总的来说,这个图书管理系统让我初步将多态、父类、接口、数组、类和对象等所有基本的知识混合在一起,并且使用。我在这个过程中也是初步体会到了java程序封装的特性以及面向对象开发的特点。
但是我们的图书管理系统还有许多可以改进的地方,比如持久化存储、网页交互+各种框架等等
图书管理系统的一些重点:
- get set
- toString改写
- login登录进行串联
- menu返回int
- 判断找到图书的方法
- 退出系统的方法
- 要改变目标数组内容的方法