zoukankan      html  css  js  c++  java
  • 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字节码文件。对应的字节码会在运行的时候动态生成

      

      

      

        

      

      

      

      

      

      

     

      

      

      

      

     

      

      

     

      

     

      

      

      

      

      

      

     

     

     

      

      

  • 相关阅读:
    三个Bootstrap免费字体和图标库
    前端实时消息提示的效果-websocket长轮询
    带分页的标签
    VMware-workstation安装
    摄影/肥猫的大头贴
    Smith Numbers(分解质因数)
    C
    B
    Ball
    Property Distribution(DFS)
  • 原文地址:https://www.cnblogs.com/faded8679/p/13897296.html
Copyright © 2011-2022 走看看