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

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

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("学号		姓名	年龄		生日");
        for (int i = 0; i < stus.length; i++) {
            Student stu=stus[i];
            if(stu!=null){
                System.out.println(stu.getId()+"	"+stu.getName()+"	"+stu.getAge()+"		"+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("学号		姓名	年龄		生日");
        for (int i = 0; i < teachers.length; i++) {
            Teacher t=teachers[i];
            if (t != null) {
                System.out.println(t.getId()+"	"+t.getName()+"	"+t.getAge()+"		"+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字节码文件。对应的字节码会在运行的时候动态生成

  

  

  

    

  

  

  

  

  

  

 

  

  

  

  

 

  

  

 

  

 

  

  

  

  

  

  

 

 

 

  

  

原文地址:https://www.cnblogs.com/faded8679/p/13897296.html