面向对象高级
学生管理系统
package com.itheima.edu.info.manager.entry;
import com.itheima.edu.info.manager.controller.StudaentController;
import java.util.Scanner;
public class InfoManageEntry {
public static void main(String[] args) {
// 主菜单的搭建
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.println("--------欢迎来到黑马信息管理系统--------");
System.out.println("请输入您的选择: 1.学生管理 2.老师管理 3.退出");
String choice = scanner.next();
switch (choice) {
case "1":
//System.out.println("学生管理");
//开启学生管理系统的代码逻辑
StudaentController studaentController = new StudaentController();
studaentController.start();
break;
case "2":
System.out.println("老师管理");
break;
case "3":
System.out.println("感谢你的使用");
System.exit(0);
//退出正在运行的jvm虚拟机
break;
default:
System.out.println("你的输入有误");
break;
}
}
}
}
学生管理系统的接待员
package com.itheima.edu.info.manager.controller;
import com.itheima.edu.info.manager.domain.Student;
import com.itheima.edu.info.manager.service.StudentService;
import java.util.Scanner;
public class StudaentController {
//开启学生管理系统并且展示学生管理系统的菜单的
private StudentService studentservice = new StudentService();
private Scanner scanner = new Scanner(System.in);
public void start() {
studentLoop:while (true) {
System.out.println("--------欢迎来到 <学生> 管理系统--------");
System.out.println("请输入您的选择: 1.添加学生 2.删除学生 3.修改学生 4.查看学生 5.退出");
String choice = scanner.next();
switch (choice) {
case "1":
//System.out.println("添加");
//
addStudent();
break;
case "2":
//System.out.println("删除");
deleteStudentById();
break;
case "3":
//System.out.println("修改");
updateStudent();
break;
case "4":
findAllstudent();
break;
case "5":
System.out.println("感谢你使用学生管理系统,再见!!");
break studentLoop;
default:
System.out.println("你的输入有误");
break;
}
}
}
public void updateStudent() {
String updateId;
while (true) {
//1.键盘录入要删除的学生id
System.out.println("请你输入你要删除的学生id");
updateId = scanner.next();
boolean exist = studentservice.isExist(updateId);
if (!exist) {
System.out.println("你输入的id是不存在的");
} else {
break;
}
}
System.out.println("请输入学生姓名");
String name = scanner.next();
System.out.println("请输入学生年龄");
String age = scanner.next();
System.out.println("请输入学生生日");
String birthday= scanner.next();
Student newstu = new Student();
newstu.setId(updateId);
newstu.setName(name);
newstu.setAge(age);
newstu.setBirthday(birthday);
studentservice.updateStudnet(updateId, newstu);
System.out.println("修改成功");
}
public void deleteStudentById() {
String delId;
while (true) {
//1.键盘录入要删除的学生id
System.out.println("请你输入你要删除的学生id");
delId = scanner.next();
boolean exist = studentservice.isExist(delId);
if (!exist) {
System.out.println("你输入的id是不存在的");
} else {
break;
}
}
//2.判断id是否是存在的如果不存在的话就需要一直录入
//3.调用的是业务员当中的deleteStudentById()方法根据id删除学生
studentservice.deleteStudentById(delId);
//4.提示删除成功
System.out.println("删除成功");
}
public void findAllstudent() {
/*1.调用业务员中的方法
* 2.判断数组的内存地址是否为null
* 3.遍历数组获取数组中的每个学生对象*/
Student[] stus = studentservice.findAllstudent();
if (stus == null) {
System.out.println("查无信息,请添加后重试");
return;
}
System.out.println("学号\t\t姓名\t年龄\t生日");
for (int i = 0; i < stus.length; i++) {
Student stu = stus[i];
if (stu != null) {
System.out.println(stu.getId() + "\t"+stu.getName()+"\t"+stu.getAge()+"\t"+stu.getBirthday());
}
}
}
public void addStudent() {
//1.键盘接收学生信息
//StudentService studentservice = new StudentService();
String id;
while (true) {
System.out.println("请输入学生id");
id = scanner.next();
boolean flag = studentservice.isExist(id);
if (flag) {
System.out.println("学号已被占用,请重新输入");
} else {
break;
}
}
System.out.println("请输入学生姓名");
String name = scanner.next();
System.out.println("请输入学生年龄");
String age = scanner.next();
System.out.println("请输入学生生日");
String birthday= scanner.next();
//2.将学生信息封装为学生对象
Student stu = new Student();
stu.setId(id);
stu.setName(name);
stu.setAge(age);
stu.setBirthday(birthday);
//3.将学习对象传递给StudentService中的的addStudent方法
boolean result = studentservice.addstudnet(stu);
//4.根据返回的bool类型在控制台打印输出是否成功
if (result) {
System.out.println("添加成功");
} else {
System.out.println("添加失败");
}
}
}
学生管理系统的业务员
package com.itheima.edu.info.manager.service;
import com.itheima.edu.info.manager.dao.StudentDao;
import com.itheima.edu.info.manager.domain.Student;
public class StudentService
{
private StudentDao studentDao = new StudentDao();
public boolean addstudnet(Student stu)
{
//2.将学生对象传递给Studentdao中的addSudent()方法
//3.将返回的boolean结果返回给studentservice对象
return studentDao.addStudent(stu);
}
public boolean isExist(String id) {
Student[] stus = studentDao.findAllstudent();
//假设id在数组当中是不存在的
boolean flag = false;
//遍历整个数组,看学生id是否是存在的
for (int i = 0; i < stus.length; i++) {
Student students = stus[i];
if (students !=null && students.getId().equals(id))
{
flag = true;
break;
}
}
return flag;
}
public Student[] findAllstudent() {
//1.调用的是库管的findallstudent方法
//2.如果存在对象的话直接返回的是数组不存在就返回的null
Student[] allstudent = studentDao.findAllstudent();
boolean flag = false;
for (int i = 0; i < allstudent.length; i++) {
Student student = allstudent[i];
if (student != null) {
flag = true;
break;
}
}
if (flag) {
return allstudent;
}
else
{
return null;
}
}
public void deleteStudentById(String delId) {
studentDao.deleteStudentById(delId);
}
public void updateStudnet(String updateId, Student newstu) {
studentDao.updateStudnet(updateId, newstu);
}
}
学生管理系统的库管
package com.itheima.edu.info.manager.service;
import com.itheima.edu.info.manager.dao.StudentDao;
import com.itheima.edu.info.manager.domain.Student;
public class StudentService
{
private StudentDao studentDao = new StudentDao();
public boolean addstudnet(Student stu)
{
//2.将学生对象传递给Studentdao中的addSudent()方法
//3.将返回的boolean结果返回给studentservice对象
return studentDao.addStudent(stu);
}
public boolean isExist(String id) {
Student[] stus = studentDao.findAllstudent();
//假设id在数组当中是不存在的
boolean flag = false;
//遍历整个数组,看学生id是否是存在的
for (int i = 0; i < stus.length; i++) {
Student students = stus[i];
if (students !=null && students.getId().equals(id))
{
flag = true;
break;
}
}
return flag;
}
public Student[] findAllstudent() {
//1.调用的是库管的findallstudent方法
//2.如果存在对象的话直接返回的是数组不存在就返回的null
Student[] allstudent = studentDao.findAllstudent();
boolean flag = false;
for (int i = 0; i < allstudent.length; i++) {
Student student = allstudent[i];
if (student != null) {
flag = true;
break;
}
}
if (flag) {
return allstudent;
}
else
{
return null;
}
}
public void deleteStudentById(String delId) {
studentDao.deleteStudentById(delId);
}
public void updateStudnet(String updateId, Student newstu) {
studentDao.updateStudnet(updateId, newstu);
}
}
继承
-
继承的概念
- 继承是面向对象三大特征之一,可以使得子类具有父类的属性和方法,还可以在子类中重新定义,以及追加属性和方法
-
实现继承的格式
- 继承通过extends实现
- 格式:class 子类 extends 父类
- 举例:class Dog extends Animal
-
继承带来的好处
- 继承可以让类与类之间产生关系,子父类关系,产生子父类后,子类则可以使用父类中非私有的成员。
3.1抽象类的概述(理解)
当我们在做子类共性功能抽取时,有些方法在父类中并没有具体的体现,这个时候就需要抽象类了!
在Java中,一个没有方法体的方法应该定义为抽象方法,而类中如果有抽象方法,该类必须定义为抽象类!
3.2抽象类的特点(记忆)
-
抽象类和抽象方法必须使用 abstract 关键字修饰
//抽象类的定义 public abstract class 类名 {} //抽象方法的定义 public abstract void eat();
-
抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
-
抽象类不能实例化
-
抽象类可以有构造方法
-
抽象类的子类
要么重写抽象类中的所有抽象方法
要么是抽象类
final(应用)
-
fianl关键字的作用
- final代表最终的意思,可以修饰成员方法,成员变量,类
-
final修饰类、方法、变量的效果
-
fianl修饰类:该类不能被继承(不能有子类,但是可以有父类)
-
final修饰方法:该方法不能被重写
-
final修饰变量:表明该变量是一个常量,不能再次赋值
-
变量是基本类型,不能改变的是值
-
变量是引用类型,不能改变的是地址值,但地址里面的内容是可以改变的
-
举例
public static void main(String[] args){ final Student s = new Student(23); s = new Student(24); // 错误 s.setAge(24); // 正确 }
-
-
4.1代码块概述 (理解)
在Java中,使用 { } 括起来的代码被称为代码块
4.2代码块分类 (理解)
-
局部代码块
-
位置: 方法中定义
-
作用: 限定变量的生命周期,及早释放,提高内存利用率
-
示例代码
-
-
public class Test { /* 局部代码块 位置:方法中定义 作用:限定变量的生命周期,及早释放,提高内存利用率 */ public static void main(String[] args) { { int a = 10; System.out.println(a); } // System.out.println(a); } }
-
构造代码块
-
位置: 类中方法外定义
-
特点: 每次构造方法执行的时,都会执行该代码块中的代码,并且在构造方法执行前执行
-
作用: 将多个构造方法中相同的代码,抽取到构造代码块中,提高代码的复用性
-
示例代码
-
-
public class Test { /* 构造代码块: 位置:类中方法外定义 特点:每次构造方法执行的时,都会执行该代码块中的代码,并且在构造方法执行前执行 作用:将多个构造方法中相同的代码,抽取到构造代码块中,提高代码的复用性 */ public static void main(String[] args) { Student stu1 = new Student(); Student stu2 = new Student(10); } } class Student { { System.out.println("好好学习"); } public Student(){ System.out.println("空参数构造方法"); } public Student(int a){ System.out.println("带参数构造方法..........."); } }
-
静态代码块
-
位置: 类中方法外定义
-
特点: 需要通过static关键字修饰,随着类的加载而加载,并且只执行一次
-
作用: 在类加载的时候做一些数据初始化的操作
-
示例代码
public class Test { /* 静态代码块: 位置:类中方法外定义 特点:需要通过static关键字修饰,随着类的加载而加载,并且只执行一次 作用:在类加载的时候做一些数据初始化的操作 */ public static void main(String[] args) { Person p1 = new Person(); Person p2 = new Person(10); } } class Person { static { System.out.println("我是静态代码块, 我执行了"); } public Person(){ System.out.println("我是Person类的空参数构造方法"); } public Person(int a){ System.out.println("我是Person类的带...........参数构造方法"); } }
-
1.3接口的概述(理解)
- 接口就是一种公共的规范标准,只要符合规范标准,大家都可以通用。
- Java中接口存在的两个意义
- 用来定义规范
- 用来做功能的拓展
1.4接口的特点(记忆)
-
接口用关键字interface修饰
public interface 接口名 {}
-
类实现接口用implements表示
public class 类名 implements 接口名 {}
-
接口不能实例化
我们可以创建接口的实现类对象使用
-
接口的子类
要么重写接口中的所有抽象方法
要么子类也是抽象类
1.5接口的成员特点(记忆)
-
成员特点
-
成员变量
只能是常量
默认修饰符:public static final -
构造方法
没有,因为接口主要是扩展功能的,而没有具体存在
-
成员方法
只能是抽象方法
默认修饰符:public abstract
关于接口中的方法,JDK8和JDK9中有一些新特性,后面再讲解
-
-
代码演示
- 接口
public interface Inter { public static final int NUM = 10; public abstract void show(); }
- 实现类
class InterImpl implements Inter{ public void method(){ // NUM = 20; System.out.println(NUM); } public void show(){ } }
- 测试类
public class TestInterface { /* 成员变量: 只能是常量 系统会默认加入三个关键字 public static final 构造方法: 没有 成员方法: 只能是抽象方法, 系统会默认加入两个关键字 public abstract */ public static void main(String[] args) { System.out.println(Inter.NUM); } }
1.6类和接口的关系(记忆)
-
类与类的关系
继承关系,只能单继承,但是可以多层继承
-
类与接口的关系
实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口
-
接口与接口的关系
继承关系,可以单继承,也可以多继承
2.接口组成更新
2.1接口组成更新概述【理解】
-
常量
public static final
-
抽象方法
public abstract
-
默认方法(Java 8)
-
静态方法(Java 8)
-
私有方法(Java 9)
2.2接口中默认方法【应用】
-
格式
public default 返回值类型 方法名(参数列表)
-
作用
解决接口升级的问题
-
范例
public default void show3() { }
-
注意事项
- 默认方法不是抽象方法,所以不强制被重写。但是可以被重写,重写的时候去掉default关键字
- public可以省略,default不能省略
- 如果实现了多个接口,多个接口中存在相同的方法声明,子类就必须对该方法进行重写
2.3接口中静态方法【应用】
-
格式
public static 返回值类型 方法名(参数列表)
-
范例
public static void show() { }
-
注意事项
- 静态方法只能通过接口名调用,不能通过实现类名或者对象名调用
- public可以省略,static不能省略
2.4接口中私有方法【应用】
-
私有方法产生原因
Java 9中新增了带方法体的私有方法,这其实在Java 8中就埋下了伏笔:Java 8允许在接口中定义带方法体的默认方法和静态方法。这样可能就会引发一个问题:当两个默认方法或者静态方法中包含一段相同的代码实现时,程序必然考虑将这段实现代码抽取成一个共性方法,而这个共性方法是不需要让别人使用的,因此用私有给隐藏起来,这就是Java 9增加私有方法的必然性
-
定义格式
-
格式1
private 返回值类型 方法名(参数列表)
-
范例1
private void show() { }
-
格式2
private static 返回值类型 方法名(参数列表)
-
范例2
private static void method() { }
-
-
注意事项
- 默认方法可以调用私有的静态方法和非静态方法
- 静态方法只能调用私有的静态方法
3.多态
3.1多态的概述(记忆)
-
什么是多态
同一个对象,在不同时刻表现出来的不同形态
-
多态的前提
- 要有继承或实现关系
- 要有方法的重写
- 要有父类引用指向子类对象
-
代码演示
class Animal { public void eat(){ System.out.println("动物吃饭"); } } class Cat extends Animal { @Override public void eat() { System.out.println("猫吃鱼"); } } public class Test1Polymorphic { /* 多态的前提: 1. 要有(继承 \ 实现)关系 2. 要有方法重写 3. 要有父类引用, 指向子类对象 */ public static void main(String[] args) { // 当前事物, 是一只猫 Cat c = new Cat(); // 当前事物, 是一只动物 Animal a = new Cat(); a.eat(); } }
3.2多态中的成员访问特点(记忆)
-
成员访问特点
-
成员变量
编译看父类,运行看父类
-
成员方法
编译看父类,运行看子类
-
-
代码演示
class Fu { int num = 10; public void method(){ System.out.println("Fu.. method"); } } class Zi extends Fu { int num = 20; public void method(){ System.out.println("Zi.. method"); } } public class Test2Polymorpic { /* 多态的成员访问特点: 成员变量: 编译看左边 (父类), 运行看左边 (父类) 成员方法: 编译看左边 (父类), 运行看右边 (子类) */ public static void main(String[] args) { Fu f = new Zi(); System.out.println(f.num); f.method(); } }
3.3多态的好处和弊端(记忆)
-
好处
提高程序的扩展性。定义方法时候,使用父类型作为参数,在使用的时候,使用具体的子类型参与操作
-
弊端
不能使用子类的特有成员
3.4多态中的转型(应用)
-
向上转型
父类引用指向子类对象就是向上转型
-
向下转型
格式:子类型 对象名 = (子类型)父类引用;
-
代码演示
class Fu { public void show(){ System.out.println("Fu..show..."); } } class Zi extends Fu { @Override public void show() { System.out.println("Zi..show..."); } public void method(){ System.out.println("我是子类特有的方法, method"); } } public class Test3Polymorpic { public static void main(String[] args) { // 1. 向上转型 : 父类引用指向子类对象 Fu f = new Zi(); f.show(); // 多态的弊端: 不能调用子类特有的成员 // f.method(); // A: 直接创建子类对象 // B: 向下转型 // 2. 向下转型 : 从父类类型, 转换回子类类型 Zi z = (Zi) f; z.method(); } }
3.5多态中转型存在的风险和解决方案 (应用)
-
风险
如果被转的引用类型变量,对应的实际类型和目标类型不是同一种类型,那么在转换的时候就会出现ClassCastException
-
解决方案
-
关键字
instanceof
-
使用格式
变量名 instanceof 类型
通俗的理解:判断关键字左边的变量,是否是右边的类型,返回boolean类型结果
-
-
代码演示
-
代码演示
abstract class Animal { public abstract void eat(); } class Dog extends Animal { public void eat() { System.out.println("狗吃肉"); } public void watchHome(){ System.out.println("看家"); } } class Cat extends Animal { public void eat() { System.out.println("猫吃鱼"); } } public class Test4Polymorpic { public static void main(String[] args) { useAnimal(new Dog()); useAnimal(new Cat()); } public static void useAnimal(Animal a){ // Animal a = new Dog(); // Animal a = new Cat(); a.eat(); //a.watchHome(); // Dog dog = (Dog) a; // dog.watchHome(); // ClassCastException 类型转换异常 // 判断a变量记录的类型, 是否是Dog if(a instanceof Dog){ Dog dog = (Dog) a; dog.watchHome(); } } }
4.内部类
4.1 内部类的基本使用(理解)
-
内部类概念
- 在一个类中定义一个类。举例:在一个类A的内部定义一个类B,类B就被称为内部类
-
内部类定义格式
-
格式&举例:
/* 格式: class 外部类名{ 修饰符 class 内部类名{ } } */ class Outer { public class Inner { } }
-
-
内部类的访问特点
- 内部类可以直接访问外部类的成员,包括私有
- 外部类要访问内部类的成员,必须创建对象
-
示例代码:
/* 内部类访问特点: 内部类可以直接访问外部类的成员,包括私有 外部类要访问内部类的成员,必须创建对象 */ public class Outer { private int num = 10; public class Inner { public void show() { System.out.println(num); } } public void method() { Inner i = new Inner(); i.show(); } }
2.2 成员内部类(理解)
-
成员内部类的定义位置
- 在类中方法,跟成员变量是一个位置
-
外界创建成员内部类格式
- 格式:外部类名.内部类名 对象名 = 外部类对象.内部类对象;
- 举例:Outer.Inner oi = new Outer().new Inner();
-
私有成员内部类
-
将一个类,设计为内部类的目的,大多数都是不想让外界去访问,所以内部类的定义应该私有化,私有化之后,再提供一个可以让外界调用的方法,方法内部创建内部类对象并调用。
-
示例代码:
class Outer { private int num = 10; private class Inner { public void show() { System.out.println(num); } } public void method() { Inner i = new Inner(); i.show(); } } public class InnerDemo { public static void main(String[] args) { //Outer.Inner oi = new Outer().new Inner(); //oi.show(); Outer o = new Outer(); o.method(); } }
-
-
静态成员内部类
-
静态成员内部类访问格式:外部类名.内部类名 对象名 = new 外部类名.内部类名();
-
静态成员内部类中的静态方法:外部类名.内部类名.方法名();
-
示例代码
class Outer { static class Inner { public void show(){ System.out.println("inner..show"); } public static void method(){ System.out.println("inner..method"); } } } public class Test3Innerclass { /* 静态成员内部类演示 */ public static void main(String[] args) { // 外部类名.内部类名 对象名 = new 外部类名.内部类名(); Outer.Inner oi = new Outer.Inner(); oi.show(); Outer.Inner.method(); } }
-
2.3 局部内部类(理解)
-
局部内部类定义位置
- 局部内部类是在方法中定义的类
-
局部内部类方式方式
- 局部内部类,外界是无法直接使用,需要在方法内部创建对象并使用
- 该类可以直接访问外部类的成员,也可以访问方法内的局部变量
-
示例代码
class Outer { private int num = 10; public void method() { int num2 = 20; class Inner { public void show() { System.out.println(num); System.out.println(num2); } } Inner i = new Inner(); i.show(); } } public class OuterDemo { public static void main(String[] args) { Outer o = new Outer(); o.method(); } }
2.4 匿名内部类(应用)
-
匿名内部类的前提
- 存在一个类或者接口,这里的类可以是具体类也可以是抽象类
-
匿名内部类的格式
-
格式:new 类名 ( ) { 重写方法 } new 接口名 ( )
-
举例:
new Inter(){ @Override public void method(){} }
-
-
匿名内部类的本质
- 本质:是一个继承了该类或者实现了该接口的子类匿名对象
-
匿名内部类的细节
-
匿名内部类可以通过多态的形式接受
Inter i = new Inter(){ @Override public void method(){ } }
-
-
匿名内部类直接调用方法
interface Inter{ void method(); } class Test{ public static void main(String[] args){ new Inter(){ @Override public void method(){ System.out.println("我是匿名内部类"); } }.method(); // 直接调用方法 } }
2.4 匿名内部类在开发中的使用(应用)
-
匿名内部类在开发中的使用
- 当发现某个方法需要,接口或抽象类的子类对象,我们就可以传递一个匿名内部类过去,来简化传统的代码
-
示例代码:
/* 游泳接口 */ interface Swimming { void swim(); } public class TestSwimming { public static void main(String[] args) { goSwimming(new Swimming() { @Override public void swim() { System.out.println("铁汁, 我们去游泳吧"); } }); } /** * 使用接口的方法 */ public static void goSwimming(Swimming swimming){ /* Swimming swim = new Swimming() { @Override public void swim() { System.out.println("铁汁, 我们去游泳吧"); } } */ swimming.swim(); } }
5.Lambda表达式
5.1体验Lambda表达式【理解】
-
代码演示
/* 游泳接口 */ interface Swimming { void swim(); } public class TestSwimming { public static void main(String[] args) { // 通过匿名内部类实现 goSwimming(new Swimming() { @Override public void swim() { System.out.println("铁汁, 我们去游泳吧"); } }); /* 通过Lambda表达式实现 理解: 对于Lambda表达式, 对匿名内部类进行了优化 */ goSwimming(() -> System.out.println("铁汁, 我们去游泳吧")); } /** * 使用接口的方法 */ public static void goSwimming(Swimming swimming) { swimming.swim(); } }
-
函数式编程思想概述
在数学中,函数就是有输入量、输出量的一套计算方案,也就是“拿数据做操作”
面向对象思想强调“必须通过对象的形式来做事情”
函数式思想则尽量忽略面向对象的复杂语法:“强调做什么,而不是以什么形式去做”
而我们要学习的Lambda表达式就是函数式思想的体现
5.2Lambda表达式的标准格式【理解】
-
格式:
(形式参数) -> {代码块}
- 形式参数:如果有多个参数,参数之间用逗号隔开;如果没有参数,留空即可
- ->:由英文中画线和大于符号组成,固定写法。代表指向动作
- 代码块:是我们具体要做的事情,也就是以前我们写的方法体内容
-
组成Lambda表达式的三要素:
- 形式参数,箭头,代码块