JavaSE进阶(Day4~Day6)
Day4-面向对象高级
一、分类和static
1、案例驱动授课方式
通过我们已掌握的知识点,先实现一个案例;然后找出这个案例中,存在的一些问题;
以往代码的问题:
重复代码过多的臃肿;
业务逻辑聚集紧密导致的可读性差;
代码可维护性差
案例驱动要达到的三个目标: 复用性
、可读性
、维护性
;
通过我们已掌握的知识点,先实现一个案例;
找出这个案例中,存在的一些问题,再通过新知识点解决问题;
2、分类思想
分类思想:分工协作,专人干专事
。
例如:学生信息管理系统分类
Student 类
:标准学生类,封装键盘录入的学生信息(id , name , age , birthday)
StudentDao 类
:Dao : (Data Access Object 缩写) 用于访问存储数据的数组或集合
StudentService 类
:用来进行业务逻辑的处理(例如:判断录入的id是否存在)
StudentController 类
:和用户打交道(接收用户需求,采集用户信息,打印数据到控制台)
3、分包思想
分包思想介绍:
如果将所有的类文件都放在同一个包下,不利于管理和后期维护
所以,对于不同功能的类文件
,可以放在不同的包下进行管理
包的概述:
包:本质上就是文件夹
创建包:(单级包、多级包)多级包之间使用 " . " 进行分割
多级包的定义规范:公司的网站地址翻转(去掉www)
比如:黑马程序员的网站址为www.itheima.com
后期我们所定义的包的结构就是:com.itheima.其他的包名。
包的命名规则:字母都是小写
包的定义
-
使用
package
关键字定义包 -
格式:package 包名; 如果是多级包,中间用"."进行分割;
-
注意:一般情况下,我们
不会手动
的去给某一个类定义包
,使用idea开发工具创建即可
4、包的注意事项和类的访问
包的注意事项:
- package语句必须是程序的第一条可执行的代码(注释是不可执行代码)
- package语句在一个java文件中只能有一个
- 如果没有package,默认表示无包名
类与类之间的访问:
- 同一个包下的访问
不需要导包,直接使用即可 - 不同包下的访问(两种方式)
①先import 导包后访问 (建议)
②通过全类名(包名 + 类名)访问 【应用场景:多个包下出现了相同的类名称,就可以使用这种方式,以此来进行区分】 - 注意:import 、package 、class 三个关键字的摆放位置存在顺序关系
①package 必须是程序的第一条可执行的代码
②import 需要写在 package 下面
③class 需要在 import 下面
二、黑马学生信息管理系统
1、系统介绍
需求说明
- 添加学生:键盘录入学生信息(id,name,age,birthday)
使用数组存储学生信息,要求学生的id不能重复。 - 删除学生:键盘录入要删除学生的id值,将该学生从数组中移除,如果录入的id在数组中不存在,需要重新录入。
- 修改学生:键盘录入要修改学生的id值和修改后的学生信息
将数组中该学生的信息修改,如果录入的id在数组中不存在,需要重新录入。 - 查询学生:将数组中存储的所有学生的信息输出到控制台。
- 使用分类思想、分包思想完成
2、环境搭建
- 创建模块:
itheima-edu-info-manager
- 创建包、创建类
例如业务员:判断录入的id是否已经存在
要进行业务逻辑的处理
往往需要访问存储数据的数组或者集合
而要访问存储数据的数组或者集合
需要通过StudentDao这个类完成
因此在该类中可能需要调用StudentDao的相关方法
例如客服接待:接收用户键盘录入的学生数据,将学生数据打印到控制台
录入完学生的一些数据以后,可能需要进行一些业务逻辑的操作
(比如需要判断id是否已经存在了)
而业务逻辑的处理是在StudentService类中定义的
因此在该类中可能需要调用StudentService的相关方法
domain
包下的类:封装数据的类;
controller
包下的类:客服接待(和客户打交道的)
dao
包下的类:库管,对数据的增删改查;
entry
包下的类:程序的入口点;
service
包下的类:业务员(负责业务的逻辑处理)
3、菜单搭建
实现步骤:
- 黑马信息管理系统菜单搭建
- 学生管理系统菜单搭建
黑马信息管理系统菜单搭建:
思路
- 用
输出语句
完成主界面的编写 - 用
Scanner
实现键盘录入数据 - 用
switch
语句完成操作的选择
学生管理系统菜单搭建:
注意:此处的退出,是回退到上级菜单,并不是将整个程序结束掉;
4、基本添加功能(思路分析和功能实现)
判断数组是否已满:
思路:将对象存入到数组中null元素对应的索引位置.
步骤:
- 1定义变量index为-1,假设数组已经全部存满,没有null的元素
- 遍历数组取出每一个元素,判断是否是null
- 如果为null,让index变量记录当前索引位置,并使用break结束循环遍历
最后的结果:如果index最终不是-1则数组不满;反之数组已满;
存在的问题: 一个类中的多个方法,创建了相同的对象
优化方式:将该对象提取到成员变量位置;
到此为止,添加的功能基本实现,但是在添加时,第二次添加已经存在的学生ID,不会告警,原因是什么呢?看下图:
原因:
在两次添加的过程中会调用两次方法,两次方法分别创建了一个数组,也就是形成了连个数组,最终导致第二次添加的时候所添加的数组还是一个空数组;
就像下图所示:
解决方法:使用 static 关键字解决【将数据变成一份,达到共享数据的目的!】
5、查询学生功能
6、删除学生功能
7、修改学生功能
8、系统优化
三、static关键字
static 关键字是 静态 的意思,是Java中的一个修饰符
,可以修饰成员方法
,成员变量
- 被static修饰的成员变量,一般叫做静态变量
- 被static修饰的成员方法,一般叫做静态方法
static 修饰的特点
- 被类的所有对象共享
是我们判断是否使用静态关键字的条件 - 随着类的加载而加载,优先于对象存在 (就算没有创建对象,我也可以加载静态创建的量)
对象需要类被加载后,才能创建 - 可以通过类名调用
也可以通过对象名调用
推荐使用类名调用
static 关键字的注意事项
- 静态方法只能访问静态的成员
- 非静态方法可以访问静态的成员,也可以访问非静态的成员
- 静态方法中是没有this关键字 (this代表当前对象的引用)
总结:静态方法中,只能访问静态成员,静态中没有this关键字;
非静态方法可以访问静态的成员,也可以访问非静态的成员
示例代码:
package com.itheima.test;
public class Student {
String name;
int age;
static String school;
/*
静态随着类的加载而加载, 优先于对象存在
非静态需要在创建对象之后,才可以进行使用
1. 静态方法中, 只能访问静态成员(成员变量, 成员方法)
2. 非静态方法中, 可以使用静态成员, 也可以使用非静态成员
3. 静态方法中, 没有this关键字
*/
public void show() {
System.out.println(name + "..." + age + "..." + school);
}
public static void method(){
// this: 当前对象的引用
// this需要在创建对象之后, 才会存在, 静态存在的时候, 对象可能还没有被创建
// this.name = "张三";
System.out.println(school);
}
}
问题解决:添加功能-学号重复问题解决
思路:将使用的Student[] 学生数组变为共享数据,加入static静态修饰;
四、黑马教师信息管理系统
老师管理系统需求说明
环境搭建
1、添加功能实现
2、查询老师功能实现
3、删除老师功能实现
4、修改老师功能实现
5、系统优化
Day5-面向对象高级
一、继承(上)
1、继承概述
变成下面的内容:
继承: 让类与类之间产生关系
(子父类关系),子类
可以直接使用父类
中非私有的成员;
继承的格式:
格式:public class 子类名 extends 父类名 { }
范例:public class Zi extends Fu { }
Fu:是父类,也被称为基类、超类
Zi:是子类,也被称为派生类
2、继承的好处和弊端
继承的好处
- 提高了代码的
复用性
; - 提高了代码的
维护性
; - 让
类与类之间产生了关系
,是多态的前提;
继承的弊端
- 继承是
侵入性
的 降低了代码的灵活性
继承关系,导致子类必须拥有父类非私有属性和方法,让子类自由的世界中多了些约束增强了代码的耦合性
代码与代码之间存在关联都可以将其称之为"耦合"。
什么时候使用继承?
当类与类之间,存在相同 (共性) 的内容,并且产生了 is a 的关系,就可以考虑使用继承,来优化代码。 如下图:
3、继承的特点
Java只支持单继承
,不支持多继承
,但支持多层继承
。
多层继承:子类 A 继承父类 B ,父类B 可以 继承父类 C
如下代码:C继承自B,B继承自A;则C可以调用A
package com.itheima.test3;
public class TestExtends {
public static void main(String[] args) {
C c = new C();
c.methodA(); //C继承自B,B继承自A;则C可以调用A;
c.methodB();
}
}
4、继承的成员变量访问特点
在子类方法中访问一个变量(按照下面的顺序,依次往下查找)
- 子类局部范围找
- 子类成员范围找
- 父类成员范围找
注意: 如果子父类中
,出现了重名的成员变量,通过就近原则
,会优先使用子类
的;如果一定要使用父类的,可以通过super
关键字,进行区分。
5、super
和this
访问成员的格式
super
关键字的用法和 this
关键字的用法相似
this
:代表本类对象的引用
super
:代表父类存储空间的标识(可以理解为父类对象引用)
关键字 | 访问成员变量 | 访问成员方法 | 访问构造方法 |
---|---|---|---|
this | this.成员变量 ;访问本类成员变量 |
this.成员方法(…) ;访问本类成员方法 |
this(…) ;访问本类构造方法 |
super | super.成员变量 ;访问父类成员变量 |
super.成员方法(…) ;访问父类成员方法 |
super(…) ;访问父类构造方法 |
6、继承中成员方法的访问特点
通过子类对象访问一个方法
- 子类成员范围找
- 父类成员范围找
如下代码:
package com.itheima.test5;
public class Zi extends Fu {
public void show(){
System.out.println("子类show方法");
}
public void method(){
show(); //调用本类;默认执行this.show();
this.show(); //调用本类
super.show(); //调用父类;
}
}
7、方法重写
方法重写概述:super.方法名();
- 在继承体系中,子类出现了和父类中
一模一样
的方法声明;(一模一样指的是:方法名、参数列表,返回值类型完全相同;)
方法重写的应用场景:
- 当子类
需要父类
的功能,而功能主体子类有自己特有内容
,可以重写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容 - 练习:手机类和新手机类
方法重写和方法重载的区别:
方法重写
: 在继承体系中, 子类出现了和父类一模一样的方法声明 (方法名, 参数列表, 返回值类型一模一样);方法重载
: 在同一个类中, 方法名相同, 参数列表不同, 与返回值无关;
示例代码:
手机的更新迭代,新版本要用到旧版本中的内容,但是还有一些新版本自己的新加的内容:
旧版本:
package com.itheima.override;
public class iPearV1 {
/*
1. 定义手机类 iPearV1
call(String name) : 打电话方法
smallBlack() : 语音助手 (speak english...)
*/
public void call(String name){
System.out.println("给" + name + "打电话");
}
public void smallBlack(){
System.out.println("speak english...");
}
}
新版本(在smallBlack()
方法中新加了说中文的内容)
package com.itheima.override;
public class iPearV2 extends iPearV1 {
/*
2. 定义新手机类 iPearV2
call(String name) : 打电话方法
smallBlack() : 语音助手 (speak english... 说中文)
方法重写的应用场景:
当子类需要父类的功能,而功能主体子类有自己特有内容
可以重写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容
*/
public void smallBlack(){
super.smallBlack();
System.out.println("说中文");
}
}
8、方法重写注意事项
- 父类中私有方法不能被重写(子类访问不到);
- 父类
静态方法
,子类必须通过静态方法进行重写
,父类非静态方法
,子类也必须通过非静态方法进行重写
;
注意:静态方法不能被重写!如果子类中,也存在一个方法声明一模一样的方法,可以理解为,子类将父类中同名的方法,隐藏了起来,并非是方法重写!(但是现象是一样的);
- 子类重写父类方法时,访问权限必须大于等于父类
权限修饰符(常用的是private和public)
修饰符 | 同一个类中 | 同一个包中,子类无关类 | 不同包的子类 | 不同包的无关类 |
---|---|---|---|---|
private |
√ | |||
默认 | √ | √ | ||
protected | √ | √ | √ | |
public |
√ | √ | √ | √ |
9、继承中构造方法的访问特点
子类中所有的构造方法默认都会访问父类中无参的构造方法
为什么?
子类在初始化的时候,有可能会使用到父类中的数据,如果父类没有完成初始化,子类将无法使用父类的数据。
子类初始化之前,一定要先完成父类初始化。
怎么初始化?
构造方法的第一条语句默认都是:super()
;这是默认的,即使没有写,也会默认执行;
注意: 如果我们编写的类,没有手动指定父类,系统也会自动继承Object
(Java继承体系中的最顶层父类)
父类中没有空参构造方法
如果父类中没有空参构造方法,只有带参构造方法,会出现什么现象呢?(子类代码会出错
)
- 子类通过 super,手动调用父类的带参的构造方法(推荐)
- 子类通过 this 去调用本类的其他构造方法,本类其他构造方法再通过 super 去手动调用父类的带参的构造方法(不推荐)
注意: this(…) super(…)
必须放在构造方法的第一行有效语句,并且二者不能共存。
如下代码:
package com.itheima.constructor;
public class Test2 {
public static void main(String[] args) {
Zi z = new Zi();
}
}
class Fu {
int age;
// 带参数构造方法
public Fu(int age){
this.age = age;
}
}
class Zi extends Fu {
public Zi(int age){
super(age);
}
}
10、代码优化
父类:
package com.itheima.constructor;
public class Person extends Object {
private String name;
private int age;
public Person() {
//无参构造方法
}
public Person(String name, int age) {
//有参构造方法
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
子类:
package com.itheima.constructor;
public class Student