17.继承 and18.接口和多态 内部类 匿名内部类,Lambda表达式

1. 继承

1.1 继承的实现(掌握)

  • 继承的概念

    • 继承是面向对象三大特征之一,可以使得子类具有父类的属性和方法,还可以在子类中重新定义,以及追加属性和方法

  • 实现继承的格式

    • 继承通过extends实现  public class 子类名 extends 父类名{ }

    • 格式:class 子类 extends 父类 { }

      • 举例:class Dog extends Animal { }

      • Fu:是父类,也被称为基类,超类
      • Zi:是子类,也被称为派生类
  • 继承带来的好处

    • 继承可以让类与类之间产生关系,子父类关系,产生子父类后,子类则可以使用父类中非私有的成员。

    • 提高了代码的复用性
    • 提高了代码的维护性
    • 让类与类之间产生了关系,是多态的前提
  • 继承的弊端:

    继承是侵入性的

    降低了代码的灵活性

      继承关系,导致子类必须拥有父类非私有属性和方法,让子类*的世界中多了些约束

    增强了代码的耦合性

      代码与代码之间存在关联都可以将其称之为“耦合”

  • 耦合性:代码与代码之间存在关联都可以将其称之为“耦合”

 什么时候使用继承?

  当类与类之间,存在相同(共性)的内容,并且产生了ia a的关系,就可以考虑使用继承,来优化代码

继承的特点:   

  Java只支持单继承,不支持多继承,但支持多层继承

单继承:子类只能继承一个父类

多层继承:子类A继承父类B,父类B可以继承父类C

问题:为什么不支持多继承?

为了避免逻辑冲突问题,所以不支持多继承

public class Fu {
public void show() {
System.out.println("show方法被调用");
}
}
public class Zi extends Fu {
public void method() {
System.out.println("method方法被调用");
}
}
public class Demo {
public static void main(String[] args) {
//创建对象,调用方法
Fu f = new Fu();
f.show(); Zi z = new Zi();
z.method();
z.show();
}
}

  继承的特点代码

public class Granddad {

    public void drink() {
System.out.println("爷爷爱喝酒");
} } public class Father extends Granddad { public void smoke() {
System.out.println("爸爸爱抽烟");
} } public class Mother { public void dance() {
System.out.println("妈妈爱跳舞");
} }
public class Son extends Father {
// 此时,Son类中就同时拥有drink方法以及smoke方法
}

2. 继承中的成员访问特点

2.1 继承中变量的访问特点(掌握)

在子类方法中访问一个变量,采用的是就近原则。

  1. 子类局部范围找

  2. 子类成员范围找

  3. 父类成员范围找

  4. 如果都没有就报错(不考虑父亲的父亲…

  注意:如果子父类中,出现了重名的成员变量,通过就近原则,会优先使用子类的,如果一定要使用父类的,可以通过super关键字,进行区分。

  • 示例代码

public class Fu {
int a=10;
} public class Zi extends Fu{
//子父类当中,出现了重名的成员变量
int a=20;
public void method(){
int a=30;
System.out.println(a);//30 //需求1:在控制台打印本类成员变量20
System.out.println(this.a);//20
//需求2:在控制台打印父类成员变量10
System.out.println(super.a);//10
}
} public class Test {
public static void main(String[] args) {
Zi z=new Zi();
z.method();
}
}

  结果打印 30   20   10

2.2 super(掌握)

  • super关键字的用法和this关键字的用法相似
  • this&super关键字:

    • this:代表本类对象的引用

    • super:代表父类存储空间的标识(可以理解为父类对象引用)

  • this和super的使用分别

    • 成员变量:

      • this.成员变量 - 访问本类成员变量

      • super.成员变量 - 访问父类成员变量

    • 成员方法:

      • this.成员方法 - 访问本类成员方法

      • super.成员方法 - 访问父类成员方法

    • 构造方法:

      • this(…) - 访问本类构造方法

      • super(…) - 访问父类构造方法

2.4 继承中成员方法的访问特点(掌握)

通过子类对象访问一个方法

  1. 子类成员范围找

  2. 父类成员范围找

  3. 如果都没有就报错(不考虑父亲的父亲…)

2.6 方法重写(掌握)

  • 1、方法重写概念

    • 在继承体系中,子类出现了和父类中一模一样的方法声明(方法名一样,参数列表也必须一样,返回值类型也是)

  • 2、方法重写的应用场景

    • 当子类需要父类的功能,而功能主体子类有自己特有内容时,可以重写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容

    • 练习:手机类和新手机类
  • 注意:

    方法重写: 在继承体系中, 子类出现了和父类一模一样的方法声明 (方法名, 参数列表, 返回值类型)

    方法重载: 在同一个类中, 方法名相同, 参数列表不同, 与返回值无关.
  • 3、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...");
}
} public class iPearV2 extends iPearV1{
/* 2. 定义新手机类 iPearV2
call(String name) : 打电话方法
smallBlack() : 语音助手 (speak english... 说中文)
方法重写的应用场景:
当子类需要父类的功能,而功能主体子类有自己特有内容
可以重写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容
*/
@Override
public void smallBlack(){
super.smallBlack();//父类功能保留
System.out.println("说中文");
} } public class TestOverride {
/*
需求: 1. 定义手机类 iPearV1
call(String name) : 打电话方法
smallBlack() : 语音助手 (speak english...) 2. 定义新手机类 iPearV2
call(String name) : 打电话方法
smallBlack() : 语音助手 (speak english... 说中文) 方法重写的应用场景:
当子类需要父类的功能,而功能主体子类有自己特有内容
可以重写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容 注意: 方法重写: 在继承体系中, 子类出现了和父类一模一样的方法声明 (方法名, 参数列表, 返回值类型) 方法重载: 在同一个类中, 方法名相同, 参数列表不同, 与返回值无关. */
public static void main(String[] args) {
iPearV2 i=new iPearV2();
i.smallBlack();
}
}

2.7 方法重写的注意事项(掌握)

  • 方法重写的注意事项

  1. 私有方法不能被重写(父类私有成员子类是不能继承的)

  2. 子类重写父类方法时,访问权限必须大于等于父类    (public > protected> 默认 > 私有)

  3. 父类静态方法,子类必须通过静态方法进行重写,父类非静态方法,子类也必须通过非静态方法进行重写

   注意:静态方法不能被重写!!!如果子类中,也存在一个方法声明一模一样的方法

   可以理解为,子类将父类中同名的方法,隐藏了起来,并非是方法重写!现象是重写,本质上并不是

  • 示例代码

public class Fu {
public static void show(){
System.out.println("Fu...");
}
void method(){
System.out.println("Fu...method");
}
} public class Zi extends Fu{
//@Override //注解:检查当前的方法是否是一个正确的重写方法
public static void show() {
System.out.println("Zi...");
} @Override
public void method() { }
} public class Test {
public static void main(String[] args) {
Zi z=new Zi();
z.show();
}
}

2.8 权限修饰符 (理解)

17.继承  and18.接口和多态 内部类 匿名内部类,Lambda表达式

权限修饰符可以修饰的内容是成员 (成员变量,成员方法,构造方法)

2.3 继承中构造方法的访问特点(理解)

注意:子类中所有的构造方法默认都会访问父类中无参的构造方法

为什么?

子类在初始化的时候,有可能会使用到父类中的数据,如果父类没有完成初始化,子类将无法使用父类的数据

所以,子类初始化之前,一定要先完成父类数据的初始化。

怎样初始化?

每一个子类构造方法的第一条语句默认都是:super()

注意:如果我们编写的类,没有手动指定父类,系统也会自动继承Object(Java继承体系中的最顶层父类)

问题:如果父类中没有空参构造方法,只有带参构造方法,会出现什么现象呢?

  • 子类通过super,手动调用父类的带参构造方法
  • 子类通过this去调用本类的其他构造方法,本类其他构造方法再通过super去手动调用父类的带参构造方法

注意:this(...)super(...)必须放在构造方法的第一行有效语句,并且二者不能共存

//如果一个类被public 修饰了类名必须和文件名一致
public class Test2 {
public static void main(String[] args) {
Zi z=new Zi(); }
}
class Fu{
int age;
//空参数构造方法
public Fu(){
System.out.println("父类空参构造方法");
}
//带参数构造方法
public Fu(int age){
this.age=age;
}
}
class Zi extends Fu{
public Zi(){
this(10); // this(),super()都必须放在构造方法第一条语句,二者不能共存
//super();
} public Zi(int age){
super(age); }
}

2.5 super内存图(理解)

  • 对象在堆内存中,会单独存在一块super区域,用来存放父类的数据

17.继承  and18.接口和多态 内部类 匿名内部类,Lambda表达式

总结:

  子类当中所有的构造方法,默认都会通过super()访问父类中无参的构造方法

  每一个子类构造方法的第一条语句默认都是:super()

  this(...)super(...)必须放在构造方法的第一行有效语句,并且二者不能共存

黑马信息管理系统改进 

  思路:把学生类和老师类共性的内容向上抽取,抽取到一个Person父类,让学生类老师类继承Person类 

步骤1:抽取Person类。

步骤2:优化StudentController类中,inputStudentInfo方法。将setXxx赋值方式,改进为构造方法初始化

    注意:直接修改这种操作方式,不符合我们开发当中的一个原则

       开闭原则(队扩展开放对修改关闭)

    解决:重新创建一个OtherStudentController类

    便写新的inputStudentInfo方法

步骤3:根据StudentController类,OtherStudentController类,向上抽取出BaseStudentController类

    再让StudentController类,OtherStudentController类,继承BaseStudentController类

该三个部分

controller类

BaseStudentController:

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 BaseStudentController {
// 3. 将学生对象,传递给StudentService(业务员)中的addStudent方法
private StudentService studentService=new StudentService(); private Scanner sc=new Scanner(System.in);
//开启学生管理系统,并展示学生管理系统菜单
public void start() {
studentloop:while(true){
System.out.println("--------欢迎来到 <学生> 管理系统--------");
System.out.println("请输入您的选择: 1.添加学生 2.删除学生 3.修改学生 4.查看学生 5.退出");
String choice=sc.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":
//System.out.println("查询");
findAllStudent();
break;
case "5":
System.out.println("感谢您使用学生管理系统,再见!");
break studentloop;
default:
System.out.println("您的输入有误,请重新输入");
break;
}
}
}
//修改学生方法
public void updateStudent() {
String updateId=inputStudentId();
Student newStu=inputStudentInfo(updateId);
studentService.updateStudent(updateId,newStu);
System.out.println("修改成功");
}
//删除学生方法
public void deleteStudentById() {
String delId =inputStudentId();
//调用业务员中的deleteStudentById根据id,删除学生
studentService.deleteStudentById(delId);
//提示:删除成功
System.out.println("删除成功");
}
//查看学生方法
public void findAllStudent() {
//1.调用业务员中的获取方法,得到学生的对象数组
Student[] stus=studentService.findAllStudent();
//2.判断数组的内存地址,是否为null
if(stus==null){
System.out.println("查无信息,请添加后重试");
return;
}
//3.遍历数组,获取学生的信息,并打印在控制台
System.out.println("学号\t\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\t"+stu.getBirthday());
}
}
}
//添加学生方法
public void addStudent() { String id;
while(true){
System.out.println("请输入学生id");
id=sc.next();
boolean flag=studentService.isExists(id);
if (flag) {
System.out.println("学号已经被占用,请重新输入");
}else{
break;
}
} Student stu=inputStudentInfo(id); boolean result=studentService.addStudent(stu);
// 4. 根据返回的boolean类型结果, 在控制台打印成功\失败
if (result) {
System.out.println("添加成功");
}else {
System.out.println("添加失败");
}
}
//键盘录入学生id
public String inputStudentId(){
String Id;
while(true){
System.out.println("请输入学生id");
Id=sc.next();
boolean exists=studentService.isExists(Id);
if (!exists) {
System.out.println("您输入的id不存在,请重新输入");
}else{
break;
}
}
return Id;
} //键盘录入学生信息
//开闭原则:对扩展内容开放,对修改内容关闭
public Student inputStudentInfo(String id){
return null;
}
}

BaseTeacherController:

package com.itheima.edu.info.manager.controller;

import com.itheima.edu.info.manager.domain.Teacher;
import com.itheima.edu.info.manager.service.TeacherService; import java.util.Scanner; public class BaseTeacherController {
private Scanner sc = new Scanner(System.in);
private TeacherService teacherService=new TeacherService();
public void start() {
teacherloop: while(true){ System.out.println("--------欢迎来到 <老师> 管理系统--------");
System.out.println("请输入您的选择: 1.添加老师 2.删除老师 3.修改老师 4.查看老师 5.退出");
String choice = sc.next(); switch (choice) {
case "1":
//System.out.println("添加");
addTeacher();
break;
case "2":
//System.out.println("删除");
deleteTeacherById();
break;
case "3":
//System.out.println("修改");
updateTeacher();
break;
case "4":
//System.out.println("查询");
findAllTeacher();
break;
case "5":
System.out.println("感谢您使用老师管理系统,再见!");
break teacherloop;
default:
System.out.println("您的输入有误,请重新输入");
break;
}
}
}
//修改老师
public void updateTeacher() {
String updateId = inputTeacherId();
Teacher newTeacher = inputTeacherInfo(updateId);
//调用业务员的修改方法
teacherService.updateTeacher(updateId,newTeacher);
System.out.println("修改成功");
} //删除老师
public void deleteTeacherById() {
String delId = inputTeacherId();
//调用业务员中的deleteStudentById根据i
//,删除老师
teacherService.deleteTeacherById(delId);
//提示:删除成功
System.out.println("删除成功");
} //查询老师
public void findAllTeacher() {
//1.从业务员中获取老师对象数组
Teacher[] teachers=teacherService.findAllTeacher();
//判断数组中是否有元素
if (teachers == null) {
System.out.println("查无信息,请添加后重试");
return;
} //3.遍历数组,获取学生信息,并打印在控制台
System.out.println("学号\t\t姓名\t年龄\t\t生日");
for (int i = 0; i < teachers.length; i++) {
Teacher t=teachers[i];
if (t != null) {
System.out.println(t.getId()+"\t"+t.getName()+"\t"+t.getAge()+"\t\t"+t.getBirthday());
}
}
} //添加老师
public void addTeacher() {
String id;
while (true){
//1.接受一个不存在的老师id
System.out.println("请输入老师id");
id=sc.next();
//2.判断id是否存在
boolean exists =teacherService.isExists(id);
if (exists) {
System.out.println("id已被占用,请重新输入");
}else{
break;
}
}
Teacher t = inputTeacherInfo(id); //5.将封装好的老师对象,传递给TeacherService继续完成添加操作
boolean result=teacherService.addTeacher(t);
if (result ) {
System.out.println("添加成功");
}else{
System.out.println("添加失败");
} }
//录入老师id
public String inputTeacherId(){
String id;
while(true){
//键盘接收要删除的老师id
System.out.println("请输入id");
id=sc.next();
boolean exists = teacherService.isExists(id);
if (!exists ) {
System.out.println("您输入的id不存在,请重新输入:");
}else{
break;
}
}
return id;
}
//录入老师信息,封装为老师对象类型
public Teacher inputTeacherInfo(String id){
return null;
}
}

OtherStudentController:

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 OtherStudentController extends BaseStudentController{ private Scanner sc=new Scanner(System.in); //键盘录入学生信息
//开闭原则:对扩展内容开放,对修改内容关闭
@Override
public Student inputStudentInfo(String id){
System.out.println("请输入学生姓名");
String name=sc.next();
System.out.println("请输入学生年龄");
String age=sc.next();
System.out.println("请输入学生生日");
String birthday=sc.next();
//2.将学生信息封装为学生对象
Student stu=new Student(id,name,age,birthday); return stu;
}
}

OtherTeacherController:

package com.itheima.edu.info.manager.controller;

import com.itheima.edu.info.manager.domain.Teacher;
import com.itheima.edu.info.manager.service.TeacherService; import java.util.Scanner; public class OtherTeacherController extends BaseTeacherController{
private Scanner sc = new Scanner(System.in);
//录入老师信息,封装为老师对象类型
public Teacher inputTeacherInfo(String id){
//3.接受老师的其他信息
System.out.println("请输入老师的姓名");
String name=sc.next();
System.out.println("请输入老师的年龄");
String age=sc.next();
System.out.println("请输入老师的生日");
String birthday=sc.next();
//封装为老师对象
Teacher t=new Teacher(id,name,age,birthday); return t;
}
}

StudentController:

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 StudentController extends BaseStudentController{
private Scanner sc=new Scanner(System.in);
//键盘录入学生信息
//开闭原则:对扩展内容开放,对修改内容关闭
@Override
public Student inputStudentInfo(String id){
System.out.println("请输入学生姓名");
String name=sc.next();
System.out.println("请输入学生年龄");
String age=sc.next();
System.out.println("请输入学生生日");
String birthday=sc.next();
//2.将学生信息封装为学生对象
Student stu=new Student();
stu.setId(id);
stu.setName(name);
stu.setAge(age);
stu.setBirthday(birthday);
return stu;
}
}

TeacherController:

package com.itheima.edu.info.manager.controller;

import com.itheima.edu.info.manager.domain.Student;
import com.itheima.edu.info.manager.domain.Teacher;
import com.itheima.edu.info.manager.service.TeacherService; import java.util.Scanner; public class TeacherController extends BaseTeacherController{
private Scanner sc = new Scanner(System.in);
//录入老师信息,封装为老师对象类型
public Teacher inputTeacherInfo(String id){
//3.接受老师的其他信息
System.out.println("请输入老师的姓名");
String name=sc.next();
System.out.println("请输入老师的年龄");
String age=sc.next();
System.out.println("请输入老师的生日");
String birthday=sc.next();
//封装为老师对象
Teacher t=new Teacher();
t.setId(id);
t.setName(name);
t.setAge(age);
t.setBirthday(birthday);
return t;
}
}

  domain:

Person:

package com.itheima.edu.info.manager.domain;

public class Person {
private String id;
private String name;
private String age;
private String birthday; public Person() {
} public Person(String id, String name, String age, String birthday) {
this.id = id;
this.name = name;
this.age = age;
this.birthday = birthday;
} 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 String getAge() {
return age;
} public void setAge(String age) {
this.age = age;
} public String getBirthday() {
return birthday;
} public void setBirthday(String birthday) {
this.birthday = birthday;
}
}

Student:

package com.itheima.edu.info.manager.domain;

public class Student extends Person{
public Student() {
} public Student(String id, String name, String age, String birthday) {
super(id, name, age, birthday);
}
}

Teacher:

package com.itheima.edu.info.manager.domain;

public class Teacher extends Person{
public Teacher() {
} public Teacher(String id, String name, String age, String birthday) {
super(id, name, age, birthday);
}
}

InfoManagerEntry

package com.itheima.edu.info.manager.entry;

import com.itheima.edu.info.manager.controller.OtherStudentController;
import com.itheima.edu.info.manager.controller.OtherTeacherController;
import com.itheima.edu.info.manager.controller.StudentController;
import com.itheima.edu.info.manager.controller.TeacherController; import java.util.Scanner; public class InfoManagerEntry {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
//主菜单搭建
while (true){
System.out.println("--------欢迎来到黑马信息管理系统--------");
System.out.println("请输入您的选择: 1.学生管理 2.老师管理 3.退出");
String choice=sc.next();
switch (choice){
case "1":
//System.out.println("学生管理");
//开启学生管理系统
OtherStudentController studentController=new OtherStudentController();
studentController.start();
break;
case "2":
//System.out.println("老师管理");
OtherTeacherController teacherController=new OtherTeacherController();
teacherController.start();
break;
case "3":
System.out.println("感谢您的使用");
//退出当前正在运行的JVM虚拟机
System.exit(0);
break;
default:
System.out.println("您的输入有误,请重新输入");
break;
}
} } }

3.抽象类

3.1抽象类的概述(理解)

​   抽象方法:将共性的行为(方法)抽取到父类之后,发现该方法的实现逻辑无法在父类中给出具体明确,该方法就可以定义为抽象方法

   抽象类:如果一个类中存在抽象方法,那么该类就必须声明为抽象类

  抽象方法的定义格式:

    public abstract 返回值类型 方法名(参数列表)

  抽象类的定义格式:

    public abstract class 类名{

    }

3.3抽象类的案例(应用)

  • 案例需求

    定义猫类(Cat)和狗类(Dog)

    猫类成员方法:eat(猫吃鱼)drink(喝水…)

    狗类成员方法:eat(狗吃肉)drink(喝水…)

  • 实现步骤

    1. 猫类和狗类中存在共性内容,应向上抽取出一个动物类(Animal)

    2. 父类Animal中,无法将 eat 方法具体实现描述清楚,所以定义为抽象方法

    3. 抽象方法需要存活在抽象类中,将Animal定义为抽象类

    4. 让 Cat 和 Dog 分别继承 Animal,重写eat方法

    5. 测试类中创建 Cat 和 Dog 对象,调用方法测试

  • 代码实现

TestAnimal:

package com.itheima.test1;

public class TestAnimal {
/*
需求:定义猫类(Cat)和狗类(Dog)
猫类成员方法:eat(猫吃鱼)drink(喝水…)
狗类成员方法:eat(狗吃肉)drink(喝水…) 步骤:
1. 猫类和狗类中存在共性内容,应向上抽取出一个动物类(Animal)
2. 父类Animal中,无法将 eat 方法具体实现描述清楚,所以定义为抽象方法
3. 抽象方法需要存活在抽象类中,将Animal定义为抽象类
4. 让 Cat 和 Dog 分别继承 Animal,重写eat方法
5. 测试类中创建 Cat 和 Dog 对象,调用方法测试
*/ public static void main(String[] args) {
Dog d=new Dog();
d.eat();
d.drink(); Cat c=new Cat();
c.eat();
c.drink();
}
}

Animal:

package com.itheima.test1;

public abstract class Animal {
public void drink(){
System.out.println("喝水");
}
public abstract void eat();
}

Dog:

package com.itheima.test1;

public class Dog extends Animal{
@Override
public void eat() {
System.out.println("狗吃肉");
}
}

Cat:

package com.itheima.test1;

public class Cat extends Animal{

    @Override
public void eat() {
System.out.println("猫吃鱼");
}
}

抽象类的注意事项 

1.抽象类不能创建对象

2.抽象类中有构造方法

3.抽象类的子类

  A:必须要重写父类中所有的抽象方法

  B:可以将自己也变成一个抽象类

4.抽象类中的方法

  抽象类中可以没有抽象方法,但是由抽象方法的类一定是抽象类

3.4模板设计模式

  • 设计模式

    设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。 使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。是一套良好的编码风格,并非是一个技术点。

  • 模板设计模式

    把抽象类整体就可以看做成一个模板,模板中不能决定的东西定义成抽象方法 让使用模板的类(继承抽象类的类)去重写抽象方法实现需求

  • 模板设计模式的优势

    模板已经定义了通用结构,使用者只需要关心自己需要实现的功能即可

  • 示例代码

    模板类

  CompositionTemplate类

package com.itheima.test2;
/*
* 作文模板类
* */
public abstract class CompositionTemplate {
public void write(){
System.out.println("<<我的爸爸>>");
body();

System.out.println("啊,这就是我的爸爸");
}
public abstract void body();
}

  Test类

package com.itheima.test2;

public class Test {
public static void main(String[] args) {
Tom t=new Tom();
t.write();
} }

  Tom类

package com.itheima.test2;

public class Tom extends CompositionTemplate{
@Override
public void body() {
System.out.println("那是一个秋天, 风儿那么缠绵,记忆中, " +
"那天爸爸骑车接我放学回家,我的脚卡在了自行车链当中, 爸爸蹬不动,他就站起来蹬..."); }
}

  模板方法一般都是去定义骨架(结构),是不允许更改的

3.5final(应用)

  • fianl关键字的作用

    • final代表最终的意思,可以修饰成员方法,成员变量,类

  • final修饰类、方法、变量的效果

    • fianl修饰类:该类不能被继承(不能有子类,但是可以有父类)

    • final修饰方法:该方法不能被重写

    • final修饰变量:表明该变量是一个常量,不能再次赋值

      • 变量是基本类型,不能改变的是值

      • 变量是引用类型,不能改变的是地址值,但地址里面的内容是可以改变的

package com.itheima.mfinal;

public class TestFinal {
/*
final修饰变量: 基本数据类型变量: 其值不能被更改 引用数据类型变量: 地址值不能被更改, 但是可以修改对象的属性值
*/
public static void main(String[] args) {
//常量的命名规范.如果是一个单词,所有字母大写,如果是多个单词,所有字母大写,但是中间是用下划线进行分隔
final int A=10;
//a=10;
final int MAX=10;
final int MAX_VALUE=20; final Student stu=new Student();
stu.setName("张三");
stu.setName("李四"); // stu=new Student();
}
}
/*
class Fu{ }
class Zi extends Fu{ }*/
class Student{
private String name;
//final修饰成员变量,初始化时机
//1.在创建的时候,直接给值
//2.在构造方法结束之前,完成赋值 final int a=10; public String getName() {
return name;
}
/* public Student(){
a=10;
}*/ public void setName(String name) {
this.name = name;
}
}

4.代码块

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关键字修饰,随着类的加载而加载,并且只执行一次

作用: 在类加载的时候做一些数据初始化的操作

示例代码

package com.itheima.block.mstatic;

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类的带参数构造方法>>>>");
}
}

  我是静态代码块,我执行了

  我是Person类的空参数构造方法
  我是Person类的带参数构造方法>>>>

4.3黑马信息管理系统使用代码块改进 (应用)

  • 需求:使用静态代码块,初始化一些学生数据

  • 实现步骤

    1. 在StudentDao类中定义一个静态代码块,用来初始化一些学生数据

    2. 将初始化好的学生数据存储到学生数组中

  • 示例代码

  • TeacherDao类

static{
Teacher teacher1=new Teacher("heima001","张三","23","1999-11-11");
Teacher teacher2=new Teacher("heima002","李四","24","2000-11-11");
teachers[0]=teacher1;
teachers[1]=teacher2;
}

  StudentDao类  

 static{
Student stu1=new Student("heima001","张三","23","1999-11-11");
Student stu2=new Student("heima002","李四","24","2000-11-11");
stus[0]=stu1;
stus[1]=stu2;
}

1.接口

1.1黑马信息管理系统集合改进 (应用)

  • 使用数组容器的弊端

    1. 容器长度是固定的,不能根据添加功能自动增长

    2. 没有提供用于赠删改查的方法

  • 优化步骤

    1. 创建新的StudentDao类,OtherStudentDao

    2. 创建ArrayList集合容器对象

    3. OtherStudentDao中的方法声明,需要跟StudentDao保持一致

      注意:如果不一致,StudentService中的代码就需要进行修改

    4. 完善方法(添加、删除、修改、查看)

    5. 替换StudentService中的Dao对象

  • 代码实现

package com.itheima.edu.info.manager.dao;

import com.itheima.edu.info.manager.domain.Student;

import java.util.ArrayList;

public class OtherStudentDao extends BaseStudentDao{
// 集合容器
private static ArrayList<Student> stus=new ArrayList<>(); static{
Student stu1=new Student("heima001","张三","23","1999-11-11");
Student stu2=new Student("heima002","李四","24","2000-11-11");
stus.add(stu1);
stus.add(stu2);
} public boolean addStudent(Student stu) { // 添加学生方法 思路:将对象存入到数组中null元素对应的索引位置 stus.add(stu);
return true;
} public Student[] findAllStudent() {
Student[] students=new Student[stus.size()];
for (int i = 0; i < students.length; i++) {
students[i]=stus.get(i);
}
return students;
} public void deleteStudentById(String delId) {
//1.查找id在容器中的索引位置
int index=getIndex(delId);
//2.将该索引位置,使用null元素进行覆盖
stus.remove(index);
} public int getIndex(String id){
int index=-1;
for (int i = 0; i < stus.size(); i++) {
Student stu=stus.get(i);
if (stu != null&&stu.getId().equals(id)) {
index=i;
break;
}
}
return index;
} public void updateStudent(String updateId, Student newStu) {
//1.查找updateId,在容器中的索引位置
int index =getIndex(updateId);
stus.set(index,newStu);
}
}

StudentService类

public class StudentService {
// 创建StudentDao (库管)
private OtherStudentDao studentDao = new OtherStudentDao();
// 其他方法没有变化,此处省略...
}

1.2黑马信息管理系统抽取Dao (应用)

  • 优化步骤

    1. 将方法向上抽取,抽取出一个父类 ( BaseStudentDao )

    2. 方法的功能实现在父类中无法给出具体明确,定义为抽象方法

    3. 让两个类分别继承 BaseStudentDao ,重写内部抽象方法

  • 代码实现

    BaseStudentDao类

public abstract class BaseStudentDao {
// 添加学生方法
public abstract boolean addStudent(Student stu);
// 查看学生方法
public abstract Student[] findAllStudent();
// 删除学生方法
public abstract void deleteStudentById(String delId);
// 根据id找索引方法
public abstract int getIndex(String id);
// 修改学生方法
public abstract void updateStudent(String updateId, Student newStu);
}

StudentDao类

public class StudentDao extends BaseStudentDao {
// 其他内容不变,此处省略
}

OtherStudentDao类

public class OtherStudentDao extends BaseStudentDao {
// 其他内容不变,此处省略
}

1.3接口的概述(理解)

  场景:一个类中,所有的方法都是抽象方法。          制定规则

  • 当一个类中的所有方法都是抽象方法的时候,我们就可以将其定义为接口

  • 接口也是也是一种引用数据类型,它比抽象类还要抽象
  • Java中接口存在的两个重要意义

    1. 规则的定义

    2. 程序的扩展性

1.4接口的定义和特点(记忆)

  • 接口用关键字interface修饰

  •   public interface 接口名{}
  • 接口不能实例化
  • 接口和类之间是实现关系,通过implements关键字表示
  •   public class 类名 implements 接口名{}
  • 接口的子类(实现类)

      要么重写接口中的所有抽象方法

      要么是抽象类    

注意:接口和类的实现关系,可以单实现,也可以多实现

  public class 类名 implements 接口名1,接口名2{}  

Inter接口类

package com.itheima.test1;

public interface
Inter {
public abstract void study();
}

InterA接口类

package com.itheima.test1;

public interface InterA {
public abstract void print1();
public abstract void print2();
public abstract void study();
}

InterImpl实现类

package com.itheima.test1;

public class InterImpl implements Inter,InterA{
@Override
public void study() {
System.out.println("我是实现类中的study方法");
} @Override
public void print1() { } @Override
public void print2() { }
}

Test1Interface测试类

package com.itheima.test1;

public class Test1Interface {
/*
接口的定义格式:
public interface 接口名 {} 类实现接口的格式:
public class 类名 implements 接口名 {} */
public static void main(String[] args) {
//Inter i=new Inter()
InterImpl ii=new InterImpl();
ii.study();
}
}

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);
} }

JDK8版本后,Java只对接口的成员方法就行了改进

  允许接口定义带有方法体的方法(非抽象方法),但是需要使用关键字default修饰,这些方法就是默认方法。作用:解决接口升级问题

  接口中允许static静态方法

  接口中默认方法的定义格式:

  格式:public default 返回值类型 方法名(参数列表){   }

  举例:public default void show(){   }

接口中默认方法的注意事项:

  • 默认方法不是抽象方法,所以不强制被重写,但是可以被重写,重写的时候去掉default关键字
  • public可以省略,default不能省略
  • 如果实现了多个接口,多个接口中存在相同的方法声明,子类就必须队该方法进行重写  

接口中静态方法的定义格式

  格式:public static 返回值类型 方法名(参数列表){   }

  范例:public static void show(){   }  

接口中静态方法的注意事项:

  静态方法只能通过接口名调用,不能通过实现类名或者对象名调用

  public可以省略,static不能省略

JDK9版本接口成员的特点

接口中私有方法的定义格式:

  格式1:private 返回值类型 方法名(参数列表){   }  

  范例1:private void show(){   }  

  格式2:private static 返回值类型 方法名(参数列表){   }

  范例2:private static void method(){   }

接口的使用思路

  如果发现一个类中所有的方法都是抽象方法,那么就可以将该类,改进为一个接口

  涉及到了接口大面积更新方法,而不想去修改每一个实现类,就可以将更新的方法,定义为带有方法体的默认方法 

  希望默认方法的调用更加简洁,可以考虑设计为static静态方法(需要去掉default关键字)静态方法只能通过接口名进行调用

  默认方法中出现了重复的代码,可以考虑抽取出一个私有方法(需要去掉default关键字)

1.6类和接口的关系(记忆)

  • 类与类的关系

    继承关系,只能单继承,但是可以多层继承

  • 类与接口的关系

    实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口

  • 接口与接口的关系

    继承关系,可以单继承,也可以多继承

InterA

public interface InterA {
public abstract void showA(); public default void method(){
System.out.println("InterA...method方法");
}
}

InterB

public interface InterB {
public abstract void showB(); public default void method(){
System.out.println("InterB...method方法");
}
}

InterC

public interface InterC extends InterA,InterB{
@Override
public default void method() {
System.out.println("InterC接口,解决代码逻辑冲突问题, 重写method方法");
}
}

InterImpl

public class InterImpl implements InterC{

    @Override
public void showA() { } @Override
public void showB() { }
}
TestInterface
public class TestInterface {
public static void main(String[] args) {
InterImpl ii=new InterImpl();
ii.method();
}
}

1.7黑马信息管理系统使用接口改进 (应用)

  • 实现步骤

    1. 将 BaseStudentDao 改进为一个接口

    2. 让 StudentDao 和 OtherStudentDao 去实现这个接口

  • 代码实现

    BaseStudentDao接口

public interface BaseStudentDao {
// 添加学生方法
public abstract boolean addStudent(Student stu);
// 查看学生方法
public abstract Student[] findAllStudent();
// 删除学生方法
public abstract void deleteStudentById(String delId);
// 根据id找索引方法
public abstract int getIndex(String id);
// 修改学生方法
public abstract void updateStudent(String updateId, Student newStu);
}

StudentDao类

public class StudentDao implements BaseStudentDao {
// 其他内容不变,此处省略
}

OtherStudentDao类

public class OtherStudentDao implements BaseStudentDao {
// 其他内容不变,此处省略
}

1.8黑马信息管理系统解耦合改进 (应用)

  • 实现步骤

    1. 创建factory包,创建 StudentDaoFactory(工厂类)

    2. 提供 static 修改的 getStudentDao 方法,该方法用于创建StudentDao对象并返回

  • 代码实现

代码实现

StudentDaoFactory类

public class StudentDaoFactory {
public static OtherStudentDao getStudentDao(){
return new OtherStudentDao();
}
}

StudentService类

public class StudentService {
// 创建StudentDao (库管)
// private OtherStudentDao studentDao = new OtherStudentDao(); // 通过学生库管工厂类, 获取库管对象
private OtherStudentDao studentDao = StudentDaoFactory.getStudentDao();
}

3.多态

3.1多态的概述(记忆)

  • 什么是多态

    同一个对象,在不同时刻表现出来的不同形态

  • 举例:猫
  • 我们可以说是猫:猫 cat=new 猫();   我们也可以说猫是动物:动物animal=new 猫();这里猫在不同的时刻表现出来了不同的形态,这就是多态
  • 多态的前提

    • 要有继承或实现关系

    • 要有方法的重写

    • 要有父类引用指向子类对象

  • 代码演示

package com.itheima.test1;

public class Test1Polymorphic {
/*
多态的前提
1.要有继承或实现关系
2.要有方法的重写
3.要有父类引用指向子类对象
*/
public static void main(String[] args) {
//当前事物,是一只动物
Animal a=new Cat();
a.eat();
//当前事物,是一只猫
Cat c=new Cat();
}
}
class Animal{
public void eat(){
System.out.println("动物吃饭");
}
}
class Cat extends Animal{
@Override
public void eat() {
System.out.println("猫吃鱼");
}
}

3.2多态中的成员访问特点(记忆)

  • 成员访问特点

    • 构造方法:同继承一样,子类会通过super访问父类构造方法
    • 成员变量:编译看左边(父类),执行看左边(父类)
    • 成员方法:编译看左边(父类),执行看右边(子类)  
  • 代码演示

    package com.itheima.test2;
    
    public class Test2Polymorpic {
    /*
    多态的成员访问特点: 成员变量: 编译看左边 (父类), 运行看左边 (父类) 成员方法: 编译看左边 (父类), 运行看右边 (子类)
    */
    public static void main(String[] args) {
    //多态的形式创建对象
    Fu fu=new Zi();
    System.out.println(fu.num);
    fu.method();
    }
    } class Fu{
    int num=10;
    public void method(){
    System.out.println("Fu...method");
    }
    }
    class Zi extends Fu{
    @Override
    public void method() {
    int num=20;
    System.out.println("Zi...method");
    }
    }

      

为什么成员变量和成员方法的访问不一样呢?

  因为成员方法有重写,而成员变量没有

3.3多态的好处和弊端(记忆)

  • 好处

    提高程序的扩展性。定义方法时候,使用父类型作为参数,在使用的时候,该方法就可以接收这父类的任意子类对象

  • 弊端

    不能使用子类的特有功能

package com.itheima.test3;

public class Test3Polymorpic {
public static void main(String[] args) {
useAnimal(new Dog());
useAnimal(new Cat());
}
public static void useAnimal(Animal a){ //Animal a=new Dog();
a.eat(); //Animal a=new Cat();
//a.watchHome();
} }
abstract class Animal{
public abstract void eat();
} class Dog extends Animal{
@Override
public void eat() {
System.out.println("狗吃肉");
}
public void watchHome(){
System.out.println("看家");
}
} class Cat extends Animal{
@Override
public void eat() {
System.out.println("猫吃鱼");
}
}

3.4多态中的转型(应用)

  • 向上转型

    父类引用指向子类对象就是向上转型     从子到父

  • 向下转型      从父到子   父亲引用转为子类对象

    格式:子类型 对象名 = (子类型)父类引用;

  • 代码演示

package com.itheima.test4;

public class Test4Polymorpic {
public static void main(String[] args) {
//1.向上转型:父类引用指向子类对象
Fu f=new Zi();
f.show();
//多态的弊端,不能调用子类特有的成员
//f.method; //A:直接创建子类对象
//B:向下转型:从父类类型转换为子类类型
Zi z=(Zi)f;//强制转换
z.method();
}
} 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");
}
}

3.5多态中转型存在的风险和解决方案 (应用)

  • 风险

    如果被转的引用类型变量,对应的实际类型和目标类型不是同一种类型,那么在转换的时候就会出现ClassCastException

  • 解决方案

    • 关键字

      instanceof

    • 使用格式

      变量名 instanceof 类型

      通俗的理解:判断关键字左边的变量,是否是右边的类型,返回boolean类型结果

  • 代码演示

package com.itheima.test3;

public class Test3Polymorpic {
public static void main(String[] args) {
useAnimal(new Dog());
useAnimal(new Cat());
}
public static void useAnimal(Animal a){ //Animal a=new Dog();
a.eat(); //Animal a=new Cat();
//a.watchHome();
if(a instanceof Dog){
Dog dog=(Dog) a;
dog.watchHome();//ClassCastException 类型转换异常
} // Dog dog=(Dog) a;
// dog.watchHome();//ClassCastException 类型转换异常
} }
abstract class Animal{
public abstract void eat();
} class Dog extends Animal{
@Override
public void eat() {
System.out.println("狗吃肉");
}
public void watchHome(){
System.out.println("看家");
}
} class Cat extends Animal{
@Override
public void eat() {
System.out.println("猫吃鱼");
}
}

3.6黑马信息管理系统多态改进 (应用)

  • 实现步骤

    1. StudentDaoFactory类中方法的返回值定义成父类类型BaseStudentDao

    2. StudentService中接收方法返回值的类型定义成父类类型BaseStudentDao

  • 代码实现

StudentDaoFactory类

public class StudentDaoFactory {
public static BaseStudentDao getStudentDao(){
return new OtherStudentDao();
}
}

StudentService类

public class StudentService {
// 创建StudentDao (库管)
// private OtherStudentDao studentDao = new OtherStudentDao(); // 通过学生库管工厂类, 获取库管对象
private BaseStudentDao studentDao = StudentDaoFactory.getStudentDao();
}

4.内部类

4.1 内部类的基本使用(理解)

  • 内部类概念

    • 在一个类中定义一个类。举例:在一个类A的内部定义一个类B,类B就被称为内部类

  • 内部类定义格式

    • 格式&举例:

      /*
      格式:
      class 外部类名{
      修饰符 class 内部类名{ }
      }
      */ class Outer {
      public class Inner { }
      }

      内部类的访问特点

      • 内部类可以直接访问外部类的成员,包括私有

      • 外部类要访问内部类的成员,必须创建对象  

按照内部类在类中定义的位置不同,可以分为如下两种形式

  在类的成员位置:成员内部类

  在类的局部位置:局部内部类  

成员内部类,外界如何创建对象使用呢?

  格式:

外部类名.内部类名 对象名=new 外部类对象().new 内部类对象();
package com.itheima.test1;

public class TestInner {
public static void main(String[] args) { /*
* 创建内部类对象的格式
* 外部类名.内部类名 对象名=new 外部类对象().new 内部类对象();
* */
Outher.Inner i=new Outher().new Inner();
System.out.println(i.num);
i.show();
}
}
class Outher{
private int a=10;
class Inner{
int num=10; public void show(){
System.out.println("Inner..show");
//内部类访问外部类成员,可以直接访问,包括私有
System.out.println(a);
}
}
}

2.2 成员内部类(理解)

成员内部类,也属于(成员),既然是成员就可以被一些修饰符所修饰

private

  私有成员内部类访问:在自己所在的外部类中创建对象访问。

static

  静态成员内部类访问格式:外部类名.内部类名 对象名=new 外部类名.内部类名();

  静态成员内部类中的静态方法:外部类名.内部类名.方法名();

私有的成员内部类:

package com.itheima.test2;
public class Test2Innerclass {
//私有的成员内部类
public static void main(String[] args) {
//Outer.Inner oi=new Outer().new Inner();
Outer outer=new Outer();
outer.method();
}
}
class Outer{
private class Inner{
public void show(){
System.out.println("inner...show");
}
}
public void method(){
Inner i=new Inner();
i.show();
}
} 
静态的成员内部类

package com.itheima.test3;

public class Test3Innerclass {
//静态的成员内部类
public static void main(String[] args) {
//外部类名.内部类名 对象名=new 外部类名.内部类名();
Outer.Inner oi=new Outer.Inner();
oi.show();
Outer.Inner.method();
}
}
class Outer{
static class Inner{
public void show(){
System.out.println("inner...show");
}
public static void method(){
System.out.println("inner...method");
}
} }

2.3 局部内部类(理解) 平时很少编写

  • 局部内部类定义位置

    • 局部内部类是在方法中定义的类

  • 局部内部类方式方式

    • 局部内部类,外界是无法直接使用,需要在方法内部创建对象并使用

    • 该类可以直接访问外部类的成员,也可以访问方法内的局部变量

  • 示例代码

package com.itheima.test4;

public class Test4Innerclass {
/*
* 局部内部类
* 访问方式:之恩那个在方法中,创建对象并访问
* */
public static void main(String[] args) {
Outer o= new Outer();
o.method();
}
}
class Outer{
int a=10;
public void method(){
int b=20;
class Inner{
public void show(){
System.out.println("show...");
System.out.println(a);
System.out.println(b);
}
} Inner i=new Inner();
i.show();
}
}

2.4 匿名内部类(应用)

概述:匿名内部类本质上是一个特殊的局部内部类(定义在方法内部)

  • 匿名内部类的前提

    • 存在一个类或者接口,这里的类可以是具体类也可以是抽象类

  • 匿名内部类的格式

    • 格式:new 类名 ( ) { 重写方法 }       new 接口名 ( ) { 重写方法 }

    • 理解:匿名内部类是将(继承/实现)(方法重写)(创建对象)三个步骤,放在了一步进行
    • 匿名内部类的本质
    • 本质:是一个继承了该类或者实现了该接口的子类匿名对象
    • 匿名内部类的细节
    • 匿名内部类可以通过多态的形式接受
    • 举例:

package com.itheima.test5;

public class Test5Innerclass {
/*
* 1.创建实现类,通过implements关键字去实现接口
* 2.重写方法
* 3.创建类对象
* 4.调用重写后的方法
*
* 匿名内部类:需要存在类/接口
* 格式:
* new 类名/接口名(){
* 重写方法
* }
* */
public static void main(String[] args) {
InterImpl ii=new InterImpl();
ii.show(); //匿名内部类的理解:将继承/实现,方法重写,创建对象,放在了一部进行.
//实现了Inter 接口的,一个实现类对象
new Inter(){ @Override
public void show() {
System.out.println("我是匿名内部类中的show方法");
}
}.show(); //接口中存在多个方法
Inter2 i=new Inter2(){ @Override
public void show1() {
System.out.println("show1");
} @Override
public void show2() {
System.out.println("show2");
}
};
i.show1();
i.show2(); }
}
interface Inter{
void show();
}
interface Inter2{
void show1();
void show2();
}
class InterImpl implements Inter{ @Override
public void show() {
System.out.println("InterImpl 重写的show方法");
}
}

2.4 匿名内部类在开发中的使用(应用)

  • 匿名内部类在开发中的使用

    • 当方法的形式参数是接口或者抽象类时,可以将匿名内部类作为实际参数进行传递

  • 示例代码:

    package com.itheima.test6;
    
    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();
    }
    }
    /*
    * 使用接口方法
    *
    * */ /*
    * 游泳接口
    * */
    interface Swimming{
    void swim();
    }  

  

5.Lambda表达式

5.1体验Lambda表达式【理解】

代码更少,关注点更明确了  

1.方法要一个接口,我得给个实现类对象

2.创建匿名内部类对象,重写方法

3.方法要干嘛呢,打印一句话吧

面向对象思想:更多关注怎么做,谁来(哪个对象去做)

更多关注做什么,函数式编程思想  

在数学中,函数就是有输入量,输出量的一套计算方案,也就是“拿数据做计算”  

面向对象思想强调“必须通过对象的形式来做事情”  

函数式思想则尽量忽略面向对象的复杂算法:“强调做什么,而不是以什么形式去做”  

而我们要学习的Lambda表达式就是函数式编程思想的体现

5.2Lambda表达式的标准格式【理解】

  • 格式:

    (形式参数) -> {代码块}

    • 形式参数:如果有多个参数,参数之间用逗号隔开;如果没有参数,留空即可

    • ->:由英文中画线和大于符号组成,固定写法。代表指向动作

    • 代码块:是我们具体要做的事情,也就是以前我们写的方法体内容

  • 组成Lambda表达式的三要素:

    • 形式参数,箭头,代码块

Lambda表达式的使用前提

  有一个接口

  接口中有且仅有一个抽象方法

  练习1:

    编写一个接口(ShowHandler)

    在该接口中存在一个抽象方法(show),该方法是无参数无返回值

    在测试类(ShowHandlerDemo)中存在一个方法(useShowHandler),方法的参数是ShowHandler类型的,在方法的内部调用了ShowHandler的show方法

package com.itheima.test2;

public class TestLambda {
/*
Lambda表达式的使用前提
1. 一个接口
2. 接口中有且仅有一个抽象方法 练习1:
1. 编写一个接口(ShowHandler)
2. 在该接口中存在一个抽象方法(show),该方法是无参数无返回值
3. 在测试类(ShowHandlerDemo)中存在一个方法(useShowHandler)
方法的的参数是ShowHandler类型的
在方法内部调用了ShowHandler的show方法
*/
public static void main(String[] args) {
useShowHandler(new ShowHandler() {
@Override
public void show() {
System.out.println("我是匿名内部类当中的show方法");
}
}); //Lambda实现
useShowHandler(()->{
System.out.println("我是Lambda当中的show方法");
});
} public static void useShowHandler(ShowHandler showHandler){
showHandler.show();
}
}
interface ShowHandler{
void show();
}

Lambda表达式的练习2

1.首先存在一个接口(StringHandler)

2.在该接口中存在一个抽象方法(PrintMessage),该方法是有参数无返回值

3.在测试类(StringHandlerDemo)中存在一个方法(useStringHandler),方法的参数是StringHandler类型的,在方法的内部调用了StringHandler的printMessage方法

package com.itheima.test3;

public class StringHandlerDemo {
/*
1.首先存在一个接口(StringHandler)
2.在该接口中存在一个抽象方法(printMessage),该方法是有参数无返回值
3.在测试类(StringHandlerDemo)中存在一个方法(useStringHandler)
方法的的参数是StringHandler类型的
在方法内部调用了StringHandler的printMessage方法
*/
public static void main(String[] args) {
useStringHandler(new StringHandler() {
@Override
public void printMessage(String msg) {
System.out.println("我是匿名内部类"+msg);
}
}); useStringHandler((String msg)->{
System.out.println("我是Lambda表达式 "+msg);
});
}
public static void useStringHandler(StringHandler stringHandler){
stringHandler.printMessage("itheima");
}
}
interface StringHandler{
void printMessage(String msg);
}

Lambda表达式练习3

1. 首先存在一个接口(RandomNumHandler)
2. 在该接口中存在一个抽象方法(getNumber),该方法是无参数但是有返回值
3. 在测试类(RandomNumHandlerDemo)中存在一个方法(useRandomNumHandler)
方法的的参数是RandomNumHandler类型的
在方法内部调用了RandomNumHandler的getNumber方法

package com.itheima.test4;

import java.util.Random;

public class RandomNumHandlerDemo {
/* 1. 首先存在一个接口(RandomNumHandler)
2. 在该接口中存在一个抽象方法(getNumber),该方法是无参数但是有返回值
3. 在测试类(RandomNumHandlerDemo)中存在一个方法(useRandomNumHandler)
方法的的参数是RandomNumHandler类型的
在方法内部调用了RandomNumHandler的getNumber方法*/
public static void main(String[] args) {
useRandomNumHandler(new RandomNumHandler() {
@Override
public int getNumber() {
Random r=new Random();
int num=r.nextInt(10)+1;
return num;
}
}); useRandomNumHandler(()->{
Random r=new Random();
int num=r.nextInt(10)+1;
return num;
//注意:如果Lambda所操作的接口中的方法,有返回值,一定要通过return语句,将结果返回,否则会出现编译错误
}); }
public static void useRandomNumHandler(RandomNumHandler randomNumHandler){
int result=randomNumHandler.getNumber();
System.out.println(result);
} }
interface RandomNumHandler{
int getNumber();
}

Lambda表达式的练习4

1. 首先存在一个接口(Calculator)
2. 在该接口中存在一个抽象方法(calc),该方法是有参数也有返回值
3. 在测试类(CalculatorDemo)中存在一个方法(useCalculator)
方法的的参数是Calculator类型的
在方法内部调用了Calculator的calc方法

package com.itheima.test5;

import com.sun.source.tree.BreakTree;

public class CalculatorDemo {
/* 1. 首先存在一个接口(Calculator)
2. 在该接口中存在一个抽象方法(calc),该方法是有参数也有返回值
3. 在测试类(CalculatorDemo)中存在一个方法(useCalculator)
方法的的参数是Calculator类型的
在方法内部调用了Calculator的calc方法
*/
public static void main(String[] args) {
useCalculator(new Calculator() {
@Override
public int calc(int a, int b) {
return a+b;
}
}); useCalculator((int a,int b)->{
return a+b;
});
}
public static void useCalculator(Calculator calculator){
int result=calculator.calc(10,20);
System.out.println(result);
}
}
interface Calculator{
int calc(int a,int b);
}

5.6Lambda表达式的省略模式【应用】

省略的规则

  • 参数类型可以省略。但是有多个参数的情况下,不能只省略一个

  • 如果参数有且仅有一个,那么小括号可以省略

  • 如果代码块的语句只有一条,可以省略大括号和分号,和return关键字

package com.itheima.Test6;

public class Test6 {
public static void main(String[] args) {
useInter((a,b) ->
a + b
);
} public static void useInter(Inter i) {
double a = i.method(12.3, 22.3);
System.out.println(a);
}
} interface Inter {
//用于计算a+b的结果并返回
double method(double a, double b);
}

5.8Lambda表达式和匿名内部类的区别【理解】

  • 所需类型不同

    • 匿名内部类:可以是接口,也可以是抽象类,还可以是具体类

    • Lambda表达式:只能是接口

  • 使用限制不同

    • 如果接口中有且仅有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类

    • 如果接口中多于一个抽象方法,只能使用匿名内部类,而不能使用Lambda表达式

  • 实现原理不同

    • 匿名内部类:编译之后,产生一个单独的.class字节码文件

    • Lambda表达式:编译之后,没有一个单独的.class字节码文件。对应的字节码会在运行的时候动态生成

  

  

  

    

  

  

  

  

  

  

 

  

  

  

  

 

  

  

 

  

 

  

  

  

  

  

  

 

  

  

上一篇:springboot2 webflux 响应式编程学习路径


下一篇:【JMeter】(1)---入门