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 权限修饰符 (理解)

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

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区域,用来存放父类的数据

总结:

  子类当中所有的构造方法,默认都会通过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字节码文件。对应的字节码会在运行的时候动态生成

  

  

  

    

  

  

  

  

  

  

 

  

  

  

  

 

  

  

 

  

 

  

  

  

  

  

  

 

  

  

17.继承 and18.接口和多态 内部类 匿名内部类,Lambda表达式的更多相关文章

  1. Java基础进阶:内部类lambda重点摘要,详细讲解成员内部类,局部内部类,匿名内部类,Lambda表达式,Lambda表达式和匿名内部类的区别,附重难点,代码实现源码,课堂笔记,课后扩展及答案

    内部类lambda重点摘要 内部类特点: 内部类可以直接访问外部类,包括私有 外部类访问内部类必须创建对象 创建内部对象格式: 外部类.内部类 对象名=new外部类().new内部类(); 静态内部类 ...

  2. Java学习笔记——继承、接口、多态

    浮点数的运算需要注意的问题: BigDecimal operand1 = new BigDecimal("1.0"); BigDecimal operand2 = new BigD ...

  3. 5.Go-封装、继承、接口、多态和断言

    面向对象 Go语言开发者认为:面向对象就是特定类型(结构体)有着自己的方法,利用这个方法完成面向对象编程, 并没有提封装.继承.多态.所以Go语言进行面向对象编程时,重点在于灵活使用方法. Go语言有 ...

  4. Java 的各种内部类、Lambda表达式

    内部类 内部类是指在一个外部类的内部再定义一个类.内部类的出现,再次打破了Java单继承的局限性. 内部类可以是静态 static 的,也可用 public,default,protected 和 p ...

  5. JavaSE 学习笔记03丨继承、接口、多态、内部类

    Chapter. 5 继承 继承作为面向对象的三大特征之一,它是多态的前提.它主要解决的问题是共性抽取. Java中的继承,是单继承.多级继承的. 已存在的类,被称为超类.基类.父类(parent c ...

  6. IT第十九天 - 继承、接口、多态、面向对象的编程思想

    IT第十九天 上午 继承 1.一般情况下,子类在继承父类时,会调用父类中的无参构造方法,即默认的构造方法:如果在父类中只写了有参的构造方法,这时如果在子类中继承时,就会出现报错,原因是子类继承父类时无 ...

  7. [二] java8 函数式接口详解 函数接口详解 lambda表达式 匿名函数 方法引用使用含义 函数式接口实例 如何定义函数式接口

    函数式接口详细定义 package java.lang; import java.lang.annotation.*; /** * An informative annotation type use ...

  8. jdk 8 lambda表达式以及Predicate接口

    了解lambda之前先了解下什么是函数式接口,函数式接口即接口里必须有一个抽象方法(抽象的方法只能有一个,可以有其他的用default修饰的方法) jdk8里新增了一个@FunctionalInter ...

  9. Lambda表达式与函数式接口

    Lambda表达式的类型,也被称为目标类型(targer type),Lambda表达式的目标类型必须是"函数式接口(functional interface)".函数式接口代表只 ...

随机推荐

  1. DAOS 分布式异步对象存储|故障模型

    DAOS 依靠大规模分布式单端口存储.因此,每个 Target 实际上都是一个单独的失败点. DAOS 通过在不同的容错域中提供 Target 间的冗余来实现数据和元数据的可用性和持久性.DAOS 内 ...

  2. 【linux】驱动-8-一文解决设备树

    目录 前言 8. Linux设备树 8.1 设备树简介 8.2 设备树框架 8.2.1 设备树格式 8.2.1.1 DTS 文件布局 8.2.1.2 node 格式 8.2.1.3 propertie ...

  3. 学会使用 Mysql show processlist 排查问题

    mysql show full processlist 查看当前线程处理情况 事发现场 每次执行看到的结果应该都有变化,因为是实时的,所以我定义为:"事发现场",每次执行就相当于现 ...

  4. Java利用线程工厂监控线程池

    目录 ThreadFactory 监控线程池 扩展线程池 扩展线程池示例 优化线程池大小 线程池死锁 线程池异常信息捕获 ThreadFactory 线程池中的线程从哪里来呢?就是ThreadFoct ...

  5. JS基础学习第二天

    类型转换 类型转换就是指将其他的数据类型,转换为String Number 或 Boolean 转换为String 方式一(强制类型转换): 调用被转换数据的toString()方法例子:var a ...

  6. MyBatis-Plus日常工作学习

    一:Mybatis-Plus概述 MyBatis-Plus (opens new window)(简称 MP)是一个 MyBatis (opens new window)的增强工具,在 MyBatis ...

  7. Unity2D项目-平台、解谜、战斗! 1.5 Player框架、技能管理组件

    各位看官老爷们,这里是RuaiRuai工作室,一个做单机游戏的兴趣作坊. 前文提到,凡是有"攻击"语义的对象,在游戏中,我们给予其一个"CanFight"组件予 ...

  8. C#开发医学影像胶片打印系统(一):万能花式布局的实现思路

    本篇文章将介绍开发医学影像胶片打印系统(printscu模式)遇到不规则排版时的一种思路, 一般来讲,医院打印胶片时都是整张胶片打印,但有时需要将多个病人或一个病人的多个检查打印在同一张胶片上, 这时 ...

  9. UT之最后一测

    经过前面几次文章的分享的UT的相关知识,今天接着分享UT相关最后一测文章,希望对大家在UT的学习中有一点点的帮助. Spring集成测试 有时候我们需要在跑起来的Spring环境中验证,Spring ...

  10. 【VritualEnv】虚拟环境的介绍和基本使用

    一.virtualenv的介绍: 在python开发中,我们可能会遇到一种情况,就是当前的项目依赖的是某一个版本,但是另一个项目依赖的是另一个版本,这样就会造成依赖冲突,而virtualenv就是解决 ...