这周学习了黑马116-143集的内容,内容包括:
学生管理系统,面向对象进阶
一.学生管理系统
1.业务分析
Java学习了不少东西了,是时候做个项目了
(1).创建需求文档
(2).思路分析
可以用画图工具理清一下需求,如下
2.代码实现
(1).所需的JavaBean类
首先,完成学生JavaBean类(快速生成空参构造,有参构造及get,set方法在idea中有快捷键alt+insert+…)
public class Student {
private String id;
private String name;
private int age;
private String address;
public Student() {//alt+insert快捷键
}
public Student(String id, String name, int age, String address) {
this.id = id;
this.name = name;
this.age = age;
this.address = address;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
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;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
(2).所需测试类
思路:主菜单->查询,添加->删除,修改
import java.util.ArrayList;
import java.util.Scanner;
public class Test1 {
public static void main(String[] args) {
ArrayList<Student> list=new ArrayList<>();
loop:while (true) {//ctrl+alt+t //....:是给循环起名字
System.out.println("------------欢迎来到朱朱学生管理系统------------");
System.out.println("1:添加学生");
System.out.println("2:删除学生");
System.out.println("3:修改学生");
System.out.println("4:查询学生");
System.out.println("5:退出");
System.out.println("请输入您的选择:");
Scanner sc=new Scanner(System.in);
String choose= sc.next();//用next容错率更高
switch (choose){
case "1"->addStu(list);
case "2"-> deleteStu(list);
case "3"-> changeStu(list);
case "4"-> queryStu(list);
case "5"-> {System.out.println("退出");
break loop;}
//System.exit(0);}//停止虚拟机运行
default -> System.out.println("没有这个选项");
}
}
}
public static void addStu(ArrayList<Student> list){
Scanner sc=new Scanner(System.in);
Student stu=new Student();//建造空的对象
String id=null;
while(true) {//ctrl+alt+t语句环绕!!!!!!!
System.out.println("请输入学生id");
id= sc.next();
boolean flag = contain(list, id);
if (flag) {
System.out.println("id已存在,请重新输入");
} else {
stu.setId(id);
break;
}
}
System.out.println("请输入学生姓名");
String name=sc.next();
stu.setName(name);
System.out.println("请输入学生年龄");
int age=sc.nextInt();
stu.setAge(age);
System.out.println("请输入学生家庭住址");
String address = sc.next();
stu.setAddress(address);
list.add(stu);//将对象存入集合中
System.out.println("学生信息添加成功");
}
public static void deleteStu(ArrayList<Student> list){
Scanner sc=new Scanner(System.in);
System.out.println("请输入要删除学生的id");
String id=sc.next();
boolean flag=contain(list,id);
if (flag) {
list.remove(getIndex(list,id));
System.out.println("删除成功");
}else{
System.out.println("删除失败");
}
}
public static void changeStu(ArrayList<Student> list){
Scanner sc=new Scanner(System.in);
System.out.println("请输入要修改的学生id");
String id= sc.next();
int index=getIndex(list,id);
if (index==-1) {
System.out.println("您输入的id"+id+"不存在");
}else{
//运行到这里说明id存在
Student stu=list.get(index);
System.out.println("输入修改后的名字");
String newName=sc.next();
stu.setName(newName);
System.out.println("输入修改后的年龄");
int newAge=sc.nextInt();
stu.setAge(newAge);
System.out.println("输入修改后的地址");
String newAddress=sc.next();
stu.setAddress(newAddress);
}
}
public static void queryStu(ArrayList<Student> list){
System.out.println("查询学生");
if (list.size()==0){
System.out.println("当前无学生信息,请添加后再查询");
}else{
System.out.println("id\t姓名\t年龄\t家庭住址");
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i).getId()+"\t"+list.get(i).getName()+"\t"+list.get(i).getAge()+
"\t"+list.get(i).getAddress());
}
}
}
//定义方法看id是否存在
public static boolean contain(ArrayList<Student> list,String id){
//for循环遍历集合得到各个学生对象,获取对象id,再做比较
for (int i = 0; i < list.size(); i++) {
Student s= list.get(i);
String sid=s.getId();
if(sid.equals(id)){
return true;
}
}return false;
}
//定义方法获取索引
public static int getIndex(ArrayList<Student> list,String id){
for (int i = 0; i < list.size(); i++) {
Student stu=list.get(i);
String sid= stu.getId();
if (sid.equals(id)) {
return i;
}
}return -1;
}
}
这里还有黑马升级版学生管理系统,有空单独实现○| ̄|_
二.面向对象进阶
1.static
static表示静态,是Java中的一个修饰符,可以修饰成员方法,成员变量
(1).静态变量
被static修饰的成员变量,叫做静态变量
[1].特点
- 被该类所有对象共享
- 不属于对象,属于类(即不需要先创建对象就可以用)
- 随着类的加载而加载,优先于对象存在
[2].调用方式
- 类名调用(推荐)-------类名.静态变量名;
- 对象名调用--------对象名.静态变量名;
(2).静态方法
被static修饰的成员方法,叫做静态方法
[1].特点
- 多用在测试类和工具类中
- JavaBean类中很少用
补充:[工具类]
不同于测试类和JavaBean类,工具类是帮助我们做一些事情的,但是不描述任何事物的类
特点:1.类名见名知意2.私有化构造方法3.方法定义为静态
[2].调用方式
- 类名调用(推荐)
- 对象名调用
[3].注意事项
- 静态方法中,只能访问静态
- 非静态可以访问所有
- 静态中没有this关键字1
2.继承
(1).概述
- Java中提供一个extends关键字,可以让一个类和另一个类建立起继承关系
格式:
public class 子类 extends 父类{}
[1].好处
- 可以把多个子类中重复的代码抽取到父类中,提高代码的复用性
- 子类可以在父类的基础上增加其功能,使子类更强大
[2].什么时候用继承
当类与类之间,存在相同(共性)的内容,并 满足子类是父类中的一种,就可以考虑使用继承来优化代码
(2).特点
Java只支持单继承,不支持多继承,但支持多层继承2
Java中所有类都直接或间接继承于Object类
(3).继承体系的设计
例.
设计继承体系时不能光想,可以用画图法完成设计
如:
结果:
(4).子类能继承父类中的哪些内容?
非私有 | 私有 | |
---|---|---|
构造方法 | 不能 | 不能 |
成员变量 | 能 | 能 |
成员方法 | 方法能添加到虚方法表3就能 | 否则不能 |
内存解释请看黑马P126
[1].继承中成员变量访问特点
就近原则:先在局部位置找,本类成员位置找,父类成员位置找,逐级往上
- 如果出现了重名的成员变量怎么办?
System.out.println(name);//从局部位置开始往上找
System.out.println(this.name);//从本类成员位置开始往上找
System.out.println(super.name);//从父类成员位置开始往上找
[2].继承中成员方法访问特点
this调用:就近原则
super调用:直接找父类
在写继承中方法时,可能会用上方法重写
补充:方法重写
在继承中,子类出现了和父类中一模一样的方法声明,就称子类的这个方法是重写的方法.
应用场景:当父类中的方法,不能满足子类现在的需求时,就需要重写
注意:子类中重写的方法上面需要加上 @override
方法重写注意事项和要求:
1.子类重写的方法尽量跟父类中的方法保持一致
2.只有虚方法表里面的方法可以被重写
本质:覆盖虚方法表中的方法
[3].继承中构造方法的访问特点
- 父类中的构造方法不会被子类继承
- 子类中的构造方法默认先访问父类中的无参构造,再执行自己
原因
- 子类在初始化的时候,可能会使用父类中的数据,如果父类没有完成初始化,子类将无法使用父类的数据
- 子类初始化之前,一定要调用父类构造方法先完成父类数据空间的初始化
调用方法
- 子类构造方法的第一行语句默认都是:super(),不写也存在,且必须在第一行
- 如果想调用父类有参构造,必须手动写super进行调用
例.调用无参构造时
调用有参构造时
(5).this,super使用总结
- this:理解为一个变量,表示当前方法调用者的地址值
- super:代表父类存储空间
关键字 | 访问成员变量 | 访问成员方法 | 访问构造方法 |
---|---|---|---|
this | this.成员变量(访问本类成员变量) | this.成员方法(…)(访问本类成员方法) | this.(…)(访问本类其他构造方法) |
super | super.成员变量(访问父类成员变量) | super.成员方法(…)(访问父类成员方法) | super.(…)(访问父类构造方法) |
(6).练习
题目:带有继承结构的标准JavaBean类
父类
public class Employee {
private String id;
private String name;
private double salary;
public Employee() {
}
public Employee(String id, String name, double salary) {
this.id = id;
this.name = name;
this.salary = salary;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public void work(){
System.out.println("员工在工作");
}
public void eat(){
System.out.println("员工在吃饭");
}
}
经理和厨师类
public class Manager extends Employee{
private double bonus;
public Manager() {
}
public Manager(String id, String name, double salary, double bonus) {
super(id, name, salary);
this.bonus = bonus;
}
public double getBonus() {
return bonus;
}
public void setBonus(double bonus) {
this.bonus = bonus;
}
@Override
public void work() {
System.out.println("管理其他人");
}
}
public class Cook extends Employee{
public Cook() {
}
public Cook(String id, String name, double salary) {
super(id, name, salary);
}
@Override
public void work() {
System.out.println("炒菜");
}
}
测试类
public class Test {
public static void main(String[] args) {
Manager m=new Manager("zhuzhu01","张三",18000,8000);
System.out.println(m.getId()+","+m.getName()+","+m.getSalary()+","+m.getBonus());
m.work();
m.eat();
Cook c=new Cook("zhuzhu002","李四",3000);
System.out.println(c.getId()+","+c.getName()+","+c.getSalary());
c.work();
c.eat();
}
}
结果:
zhuzhu01,张三,18000.0,8000.0
管理其他人
员工在吃饭
zhuzhu002,李四,3000.0
炒菜
员工在吃饭
3.多态
(1).概述
对象的多种形态
[1].前提
- 有继承/实现关系
- 有父类引用指向子类对象
- 有方法的重写
[2].表现形式
父类类型 对象名称=子类对象;
如:Fu f=new Zi ();
(2).调用成员的特点
[1].变量调用:
编译看左边,运行也看左边
编译看左边:Javac编译代码的时候,会看左边的父类中有没有这个变量,如果有,编译成功,如果没有就编译失败
运行看左边:Java运行代码的时候,实际获取的就是左边父类中成员变量的值
[2].方法调用:
编译看左边,运行看右边
编译看左边:Javac编译代码的时候,会看左边的父类中有没有这个方法,如果有,编译成功,如果没有就编译失败
运行看右边:Java运行代码的时候,实际上运行的是子类
(3).多态的优势和弊端
好处:使用父类型作为参数,可以接收所有子类对象
弊端:不能使用子类的特有功能
解决办法:强制类型转换,转换时要用instanceof关键字判断
例.
4.包,final,权限修饰符,代码块
(1).包
包就是文件夹,用来管理各种不同功能的Java类
包名书写的规则:
公司域名反写+包的作用,需要全部英文小写,见名知意
全类名:包名+类名
什么时候需要导包?什么时候不需要导包?
- 使用同一个包中的类时,不需要导包
- 使用Java.lang包中的类时,不需要导包
- 其他情况都需要导包
- 如果同时使用两个包中的同名类,需要用全类名
(2).final
final修饰方法:最终方法,不能被重写
final修饰类:最终类,不能被继承
final修饰变量:是常量,不能被修改
- 基本数据类型:变量的值不能修改
- 引用数据类型:地址值不能修改,内部的属性值可以修改
(3).权限修饰符
用来控制一个成员能够被访问的范围的
可以修饰成员变量,方法,构造方法,内部类
分类
有四种作用范围由小到大(private<空着不写<protected<public)
使用规则
实际开发中,一般只用private和public
- 成员变量私有
- 方法公开
(4).代码块
有局部代码块,构造代码块,静态代码块.
静态代码块
格式: static{}
特点:需要static关键字修饰,随着类的加载而加载,并且自动触发,只执行一次
作用:在类加载的时候,做一些数据的初始化时使用
5.抽象类
- 抽象方法:将共性的行为(方法)抽取到父类之后.由于每一个子类执行的内容是不一样的,所以在父类中不能确定具体的方法体,该方法就可以定义为抽象方法
定义格式:
public abstract 返回值类型 方法名(参数列表);
- 抽象类:如果一个类中存在抽象方法,那么该类就必须声明为抽象类
定义格式:
public abstract class 类名{}
[注意事项]
- 抽象类不能实例化(即不能被创建对象)
- 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
- 可以有构造方法
- 抽象类的子类: 要么重写抽象类中的所有抽象方法(推荐),要么是抽象类
[练习]
JavaBean类
public abstract class Animal {//父类
private String name;
private int age;
public Animal() {
}
public Animal(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;
}
public void drink(){
System.out.println("动物在喝水");
}
public abstract void eat();//按alt+enter
}
public class Frog extends Animal {//青蛙
public Frog() {
}
public Frog(String name, int age) {
super(name, age);
}
@Override
public void eat() {
System.out.println("青蛙在吃虫子");
}
}
public class Dog extends Animal{//狗
public Dog() {
}
public Dog(String name, int age) {
super(name, age);
}
@Override
public void eat() {
System.out.println("狗在吃骨头");
}
}
测试类
public class Main {
public static void main(String[] args) {
Frog f=new Frog("咕呱",12);
System.out.println(f.getName()+","+f.getAge());
f.eat();
f.drink();
Dog d=new Dog("哈皮",3);
System.out.println(d.getName()+","+d.getAge());
d.eat();
d.drink();
}
}
结果:
咕呱,12
青蛙在吃虫子
动物在喝水
哈皮,3
狗在吃骨头
动物在喝水
6.接口
(1).接口的定义和使用
-
接口用关键字interface来定义
public interface 接口名{} -
接口不能实例化(即不能被创建对象)
-
接口和类之间是实现关系,通过implements关键字表示
public class 类名 implements 接口名{} -
接口的子类(实现类)
要么重写接口中的所有抽象方法(推荐),要么是抽象类
注意1:接口和类的实现关系,可以单实现,也可以多实现.
public class 类名 implements 接口名1,接口名2{}
注意2:实现类还可以在继承一个类的同时实现多个接口
public class 类名 extends 父类 implements 接口名1,接口名2{}
以上题为例改写:
接口
public interface Swim {
public abstract void Swim();
}
其他的一些改变
public class Dog extends Animal implements Swim{
//................
@Override
public void Swim() {
System.out.println("狗刨");
}
public class Frog extends Animal implements Swim {
//................
@Override
public void Swim() {
System.out.println("蛙泳");
}
因为兔子不能游泳,而青蛙和狗能游泳,所以不能直接用抽象方法,要用接口
(2).接口中成员的特点
- 成员变量
只能是常量,默认修饰符: public static final - 构造方法
没有 - 成员方法
只能是抽象方法,默认修饰符: public abstract
(3).接口和类之间的关系(总结)
-
类和类的关系
继承关系,只能单继承,不能多继承,但是可以多层继承 -
类和接口的关系
实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口(但要重写所有方法) -
接口和接口的关系
继承关系,可以单继承,也可以多继承(若实现类实现的是最下边的子接口时要重写所有方法)
(4).多学三招
[1].JDK8开始接口中新增的方法
- JDK8:接口中可以定义有方法体的方法(默认,静态)
----默认
用关键字default修饰定义默认方法
作用:解决接口升级的问题
定义格式:public default 返回值类型 方法名(参数列表){ }
注意事项:
- 默认方法不是抽象方法,所以不强制被重写.如果被重写,需要去掉default关键字
- public可以省略,default不能省略
- 如果实现了多个接口,多个接口中存在相同名字的默认方法,子类就必须对该方法重写
----静态
用关键字static定义静态方法
定义格式:public static 返回值类型 方法名(参数列表){ }
注意事项:
- 静态方法只能通过接口名调用,不能通过实现类名或者对象名调用
- public可以省略,static不能省略
- JDK9:接口中可以定义私有方法
分为普通的私有方法,静态的私有方法
定义格式1: private 返回值类型 方法名(参数列表){ }
定义格式2:private static 返回值类型 方法名(参数列表){ }
[2].接口的应用
1.接口代表规则,是行为的抽象,想要让哪个类拥有一个行为,就让这个类实现对应的接口就可以了
2.当一个方法的参数是接口时,可以传递接口所有实现类的对象,这种方法称之为接口多态
[3].适配器设计模式
设计模式就是各种套路
适配器设计模式:解决接口与接口实现类之间的矛盾问题
使用场景:当一个接口中抽象方法过多,但是我只要其中一部分的时候,就可以适配器设计模式
书写步骤:
- 编写中间类XXXAdapter,实现对应的接口
- 对接口中的抽象方法进行空实现
- 让真正的实现类继承中间类,并重写需要用的方法
- 为了避免其它类创建适配器类的对象,中间的适配器类用abstract进行修饰
7.内部类
类的五大成员:
属性,方法,构造方法,代码块,内部类
内部类:写在一个类里面的类就叫做内部类
使用内部类的条件:B类表示的事务是A类的一部分,且B单独存在没有意义
分类:
- 成员内部类
- 静态内部类
- 局部内部类
- 匿名内部类(掌握)
[1].匿名内部类
本质上就是隐藏了名字的内部类,可以写在成员位置,也可以写在局部位置
格式:
new 类名或者接口名(){
重写方法;
};
细节:
包含了继承或实现,方法重写,创建对象
整体就是一个类的子类对象或者接口的实现类对象
使用场景:
当方法的参数是接口或者类时,
以接口为例,可以传递这个接口的实现类对象,
如果实现类只要使用一次,就可以用匿名内部类简化代码.
[2].成员内部类
- 写在成员位置的,属于外部类的成员
- 成员内部类可以被一些修饰符所修饰,如private,默认,protected,public等
- 在成员内部类里面,JDK 16开始才可以定义静态变量
获取成员内部类对象
方式一:
在外部类中编写方法,对外提供内部类的对象
方式二:
直接创建格式:外部类名.内部类名 对象名=外部类对象.内部类对象;
外部类成员变量和内部类成员变量重名时,在内部类如何访问?
System.out.println(外部类名.this.变量名);
[3].静态内部类
成员内部类中特殊的一类,静态内部类只能访问外部类中的静态变量和方法,如果想要访问非静态的需要创建对象
创建静态内部类对象的格式:
外部类名.内部类名 对象名=new 外部类名.内部类名();
调用非静态方法的格式:
先创建对象,用对象调用
调用静态方法的格式:
外部类名.内部类名.方法名();
[4].局部内部类
- 将内部类定义在方法里面就叫做局部内部类,类似于方法里面的局部变量
- 外界是无法直接使用,需要在方法内部创建对象并使用
- 该类可以直接访问外部类的成员,也可以访问方法内的局部变量
三.本周小结
这面向对象进阶的内容是真的多(´Д`)
这次博客学会了使用标记文本和注释
有点发烧,下周再见ヾ( ̄▽ ̄)ByeBye
觉得可以别忘点赞( ͡° ͜ʖ ͡°)