首页 > 其他 > 详细

10-面向对象进阶

时间:2021-03-01 11:44:53      阅读:21      评论:0      收藏:0      [点我收藏+]

第十章 面向对象进阶

1.分类思想

1.1分类思想概述 (理解)

分工协作,专人干专事

2.分包思想

2.1分包思想概述 (理解)

如果将所有的类文件都放在同一个包下,不利于管理和后期维护,所以,对于不同功能的类文件,可以放在不同的包下进行管理

2.2包的概述 (记忆)

  • 本质上就是文件夹

  • 创建包

    多级包之间使用 " . " 进行分割
    多级包的定义规范:公司的网站地址翻转(去掉www)
    比如:黑马程序员的网站址为www.itheima.com
    后期我们所定义的包的结构就是:com.itheima.其他的包名

  • 包的命名规则

    字母都是小写

2.3包的注意事项 (理解)

  • package语句必须是程序的第一条可执行的代码
  • package语句在一个java文件中只能有一个
  • 如果没有package,默认表示无包名

2.4类与类之间的访问 (理解)

  • 同一个包下的访问

    不需要导包,直接使用即可

  • 不同包下的访问

    1.import 导包后访问

    2.通过全类名(包名 + 类名)访问

  • 注意:import 、package 、class 三个关键字的摆放位置存在顺序关系

    package 必须是程序的第一条可执行的代码

    import 需要写在 package 下面

    class 需要在 import 下面

3.static关键字

3.1static关键字概述 (理解)

static 关键字是静态的意思,是Java中的一个修饰符,可以修饰成员方法,成员变量

3.2static修饰的特点 (记忆)

  • 被类的所有对象共享

    是我们判断是否使用静态关键字的条件

  • 随着类的加载而加载,优先于对象存在

    对象需要类被加载后,才能创建

  • 可以通过类名调用

    也可以通过对象名调用

3.3static关键字注意事项 (理解)

  • 静态方法只能访问静态的成员
  • 非静态方法可以访问静态的成员,也可以访问非静态的成员
  • 静态方法中是没有this关键字

4. 继承

4.1 继承的实现(掌握)

  • 继承的概念

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

    • 继承通过extends实现
    • 格式:class 子类 extends 父类 { }
      • 举例:class Dog extends Animal { }
  • 继承带来的好处

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

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

4.2 继承的好处和弊端(理解)

  • 继承好处
    • 提高了代码的复用性(多个类相同的成员可以放到同一个类中)
    • 提高了代码的维护性(如果方法的代码需要修改,修改一处即可)
  • 继承弊端
    • 继承让类与类之间产生了关系,类的耦合性增强了,当父类发生变化时子类实现也不得不跟着变化,削弱了子类的独立性
  • 继承的应用场景:
    • 使用继承,需要考虑类与类之间是否存在is..a的关系,不能盲目使用继承
      • is..a的关系:谁是谁的一种,例如:老师和学生是人的一种,那人就是父类,学生和老师就是子类

4.3. Java中继承的特点(掌握)

  • Java中继承的特点

    1. Java中类只支持单继承,不支持多继承
      • 错误范例:class A extends B, C { }
    2. Java中类支持多层继承
  • 多层继承示例代码:

    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方法
    }
    

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

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

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

  1. 子类局部范围找
  2. 子类成员范围找
  3. 父类成员范围找
  4. 如果都没有就报错(不考虑父亲的父亲…)
  • 示例代码

    class Fu {
        int num = 10;
    }
    class Zi {
        int num = 20;
        public void show(){
            int num = 30;
            System.out.println(num);
        }
    }
    public class Demo1 {
        public static void main(String[] args) {
            Zi z = new Zi();
            z.show();	// 输出show方法中的局部变量30
        }
    }
    

5.2 super(掌握)

  • this&super关键字:
    • this:代表本类对象的引用
    • super:代表父类存储空间的标识(可以理解为父类对象引用)
  • this和super的使用分别
    • 成员变量:
      • this.成员变量 - 访问本类成员变量
      • super.成员变量 - 访问父类成员变量
    • 成员方法:
      • this.成员方法 - 访问本类成员方法
      • super.成员方法 - 访问父类成员方法
  • 构造方法:
    • this(…) - 访问本类构造方法
    • super(…) - 访问父类构造方法

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

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

? 子类会继承父类中的数据,可能还会使用父类的数据。所以,子类初始化之前,一定要先完成父类数据的初始化,原因在于,每一个子类构造方法的第一条语句默认都是:super()

问题:如果父类中没有无参构造方法,只有带参构造方法,该怎么办呢?

1. 通过使用super关键字去显示的调用父类的带参构造方法
2. 子类通过this去调用本类的其他构造方法,本类其他构造方法再通过super去手动调用父类的带参的构造方法

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

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

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

  1. 子类成员范围找
  2. 父类成员范围找
  3. 如果都没有就报错(不考虑父亲的父亲…)

5.5 super内存图(理解)

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

    技术分享图片

5.6 方法重写(掌握)

  • 1、方法重写概念
    • 子类出现了和父类中一模一样的方法声明(方法名一样,参数列表也必须一样)
  • 2、方法重写的应用场景
    • 当子类需要父类的功能,而功能主体子类有自己特有内容时,可以重写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容
  • 3、Override注解
    • 用来检测当前的方法,是否是重写的方法,起到【校验】的作用

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

  • 方法重写的注意事项
  1. 私有方法不能被重写(父类私有成员子类是不能继承的)
  2. 子类方法访问权限不能更低(public > 默认 > 私有)
  3. 静态方法不能被重写,如果子类也有相同的方法,并不是重写的父类的方法
  • 示例代码
public class Fu {
    private void show() {
        System.out.println("Fu中show()方法被调用");
    }

    void method() {
        System.out.println("Fu中method()方法被调用");
    }
}

public class Zi extends Fu {

    /* 编译【出错】,子类不能重写父类私有的方法*/
    @Override
    private void show() {
        System.out.println("Zi中show()方法被调用");
    }
   
    /* 编译【出错】,子类重写父类方法的时候,访问权限需要大于等于父类 */
    @Override
    private void method() {
        System.out.println("Zi中method()方法被调用");
    }

    /* 编译【通过】,子类重写父类方法的时候,访问权限需要大于等于父类 */
    @Override
    public void method() {
        System.out.println("Zi中method()方法被调用");
    }
}

5.8 权限修饰符 (理解)

技术分享图片

6.抽象类

6.1抽象类的概述(理解)

? 当我们在做子类共性功能抽取时,有些方法在父类中并没有具体的体现,这个时候就需要抽象类了!

? 在Java中,一个没有方法体的方法应该定义为抽象方法,而类中如果有抽象方法,该类必须定义为抽象类!

6.2抽象类的特点(记忆)

  • 抽象类和抽象方法必须使用 abstract 关键字修饰

    //抽象类的定义
    public abstract class 类名 {}
    
    //抽象方法的定义
    public abstract void eat();
    
  • 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类

  • 抽象类不能实例化

  • 抽象类可以有构造方法

  • 抽象类的子类

    ? 要么重写抽象类中的所有抽象方法

    ? 要么是抽象类

6.3抽象类的案例(应用)

  • 案例需求

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

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

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

  • 实现步骤

    1. 猫类和狗类中存在共性内容,应向上抽取出一个动物类(Animal)
    2. 父类Animal中,无法将 eat 方法具体实现描述清楚,所以定义为抽象方法
    3. 抽象方法需要存活在抽象类中,将Animal定义为抽象类
    4. 让 Cat 和 Dog 分别继承 Animal,重写eat方法
    5. 测试类中创建 Cat 和 Dog 对象,调用方法测试
  • 代码实现

    • 动物类
    public abstract class Animal {
        public void drink(){
            System.out.println("喝水");
        }
    
        public Animal(){
    
        }
    
        public abstract void eat();
    }
    
    • 猫类
    public class Cat extends Animal {
        @Override
        public void eat() {
            System.out.println("猫吃鱼");
        }
    }
    
    • 狗类
    public class Dog extends Animal {
        @Override
        public void eat() {
            System.out.println("狗吃肉");
        }
    }
    
    • 测试类
    public static void main(String[] args) {
            Dog d = new Dog();
            d.eat();
            d.drink();
    
            Cat c = new Cat();
            c.drink();
            c.eat();
    
            //Animal a = new Animal();
            //a.eat();
        }
    

6.4模板设计模式

  • 设计模式

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

  • 模板设计模式

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

  • 模板设计模式的优势

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

  • 示例代码

    模板类

    /*
        作文模板类
     */
    public abstract class CompositionTemplate {
    
        public final void write(){
            System.out.println("<<我的爸爸>>");
    
            body();
    
            System.out.println("啊~ 这就是我的爸爸");
    
        }
    
        public abstract void body();
    }
    

    实现类A

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

    实现类B

    public class Tony extends CompositionTemplate {
        @Override
        public void body() {
    
        }
    
        /*public void write(){
    
        }*/
    }
    

    测试类

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

6.5final(应用)

  • fianl关键字的作用

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

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

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

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

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

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

      • 举例

        public static void main(String[] args){
            final Student s = new Student(23);
          	s = new Student(24);  // 错误
         	s.setAge(24);  // 正确
        }
        

7.代码块

7.1代码块概述 (理解)

在Java中,使用 { } 括起来的代码被称为代码块

7.2代码块分类 (理解)

  • 局部代码块

    • 位置: 方法中定义

    • 作用: 限定变量的生命周期,及早释放,提高内存利用率

    • 示例代码

      public class Test {
          /*
              局部代码块
                  位置:方法中定义
                  作用:限定变量的生命周期,及早释放,提高内存利用率
           */
          public static void main(String[] args) {
              {
                  int a = 10;
                  System.out.println(a);
              }
      
             // System.out.println(a);
          }
      }
      
  • 构造代码块

    • 位置: 类中方法外定义

    • 特点: 每次构造方法执行的时,都会执行该代码块中的代码,并且在构造方法执行前执行

    • 作用: 将多个构造方法中相同的代码,抽取到构造代码块中,提高代码的复用性

    • 示例代码

      public class Test {
          /*
              构造代码块:
                  位置:类中方法外定义
                  特点:每次构造方法执行的时,都会执行该代码块中的代码,并且在构造方法执行前执行
                  作用:将多个构造方法中相同的代码,抽取到构造代码块中,提高代码的复用性
           */
          public static void main(String[] args) {
              Student stu1 = new Student();
              Student stu2 = new Student(10);
          }
      }
      
      class Student {
      
          {
              System.out.println("好好学习");
          }
      
          public Student(){
              System.out.println("空参数构造方法");
          }
      
          public Student(int a){
              System.out.println("带参数构造方法...........");
          }
      }
      
  • 静态代码块

    • 位置: 类中方法外定义

    • 特点: 需要通过static关键字修饰,随着类的加载而加载,并且只执行一次

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

    • 示例代码

      public class Test {
          /*
              静态代码块:
                  位置:类中方法外定义
                  特点:需要通过static关键字修饰,随着类的加载而加载,并且只执行一次
                  作用:在类加载的时候做一些数据初始化的操作
           */
          public static void main(String[] args) {
              Person p1 = new Person();
              Person p2 = new Person(10);
          }
      }
      
      class Person {
          static {
              System.out.println("我是静态代码块, 我执行了");
          }
      
          public Person(){
              System.out.println("我是Person类的空参数构造方法");
          }
      
          public Person(int a){
              System.out.println("我是Person类的带...........参数构造方法");
          }
      }
      

8.接口

8.1接口的概述(理解)

  • 接口就是一种公共的规范标准,只要符合规范标准,大家都可以通用。
  • Java中接口存在的两个意义
    1. 用来定义规范
    2. 用来做功能的拓展

8.2接口的特点(记忆)

  • 接口用关键字interface修饰

    public interface 接口名 {} 
    
  • 类实现接口用implements表示

    public class 类名 implements 接口名 {}
    
  • 接口不能实例化

    ? 我们可以创建接口的实现类对象使用

  • 接口的子类

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

    ? 要么子类也是抽象类

8.3接口的成员特点(记忆)

  • 成员特点

    • 成员变量

      ? 只能是常量
      ? 默认修饰符: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);
        }
      
    }
    

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

  • 类与类的关系

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

  • 类与接口的关系

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

  • 接口与接口的关系

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

2.接口组成更新

2.1接口组成更新概述【理解】

  • 常量

    public static final

  • 抽象方法

    public abstract

  • 默认方法(Java 8)

  • 静态方法(Java 8)

  • 私有方法(Java 9)

2.2接口中默认方法【应用】

  • 格式

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

  • 作用

    解决接口升级的问题

  • 范例

    public default void show3() { 
    }
    
  • 注意事项

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

2.3接口中静态方法【应用】

  • 格式

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

  • 范例

    public static void show() {
    }
    
  • 注意事项

    • 静态方法只能通过接口名调用,不能通过实现类名或者对象名调用
    • public可以省略,static不能省略

2.4接口中私有方法【应用】

  • 私有方法产生原因

    Java 9中新增了带方法体的私有方法,这其实在Java 8中就埋下了伏笔:Java 8允许在接口中定义带方法体的默认方法和静态方法。这样可能就会引发一个问题:当两个默认方法或者静态方法中包含一段相同的代码实现时,程序必然考虑将这段实现代码抽取成一个共性方法,而这个共性方法是不需要让别人使用的,因此用私有给隐藏起来,这就是Java 9增加私有方法的必然性

  • 定义格式

    • 格式1

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

    • 范例1

      private void show() {  
      }
      
    • 格式2

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

    • 范例2

      private static void method() {  
      }
      
  • 注意事项

    • 默认方法可以调用私有的静态方法和非静态方法
    • 静态方法只能调用私有的静态方法

9.多态

9.1多态的概述(记忆)

  • 什么是多态

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

  • 多态的前提

    • 要有继承或实现关系
    • 要有方法的重写
    • 要有父类引用指向子类对象
  • 代码演示

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

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

  • 成员访问特点

    • 成员变量

      ? 编译看父类,运行看父类

    • 成员方法

      ? 编译看父类,运行看子类

  • 代码演示

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

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

  • 好处

    ? 提高程序的扩展性。定义方法时候,使用父类型作为参数,在使用的时候,使用具体的子类型参与操作

  • 弊端

    ? 不能使用子类的特有成员

9.4多态中的转型(应用)

  • 向上转型

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

  • 向下转型

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

  • 代码演示

    class Fu {
        public void show(){
            System.out.println("Fu..show...");
        }
    }
    
    class Zi extends Fu {
        @Override
        public void show() {
            System.out.println("Zi..show...");
        }
    
        public void method(){
            System.out.println("我是子类特有的方法, method");
        }
    }
    
    public class Test3Polymorpic {
        public static void main(String[] args) {
            // 1. 向上转型 : 父类引用指向子类对象
            Fu f = new Zi();
            f.show();
            // 多态的弊端: 不能调用子类特有的成员
            // f.method();
    
            // A: 直接创建子类对象
            // B: 向下转型
    
            // 2. 向下转型 : 从父类类型, 转换回子类类型
            Zi z = (Zi) f;
            z.method();
        }
    }
    

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

  • 风险

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

  • 解决方案

    • 关键字

      instanceof

    • 使用格式

      变量名 instanceof 类型

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

  • 代码演示

    abstract class Animal {
        public abstract void eat();
    }
    
    class Dog extends Animal {
        public void eat() {
            System.out.println("狗吃肉");
        }
    
        public void watchHome(){
            System.out.println("看家");
        }
    }
    
    class Cat extends Animal {
        public void eat() {
            System.out.println("猫吃鱼");
        }
    }
    
    public class Test4Polymorpic {
        public static void main(String[] args) {
            useAnimal(new Dog());
            useAnimal(new Cat());
        }
    
        public static void useAnimal(Animal a){  // Animal a = new Dog();
                                                 // Animal a = new Cat();
            a.eat();
            //a.watchHome();
    
    //        Dog dog = (Dog) a;
    //        dog.watchHome();  // ClassCastException  类型转换异常
          
            // 判断a变量记录的类型, 是否是Dog
            if(a instanceof Dog){
                Dog dog = (Dog) a;
                dog.watchHome();
            }
        }
    
    }
    

10.内部类

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

  • 内部类概念

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

    • 格式&举例:

      /*
      	格式:
          class 外部类名{
          	修饰符 class 内部类名{
          	
          	}
          }
      */
      
      class Outer {
          public class Inner {
              
          }
      }
      
  • 内部类的访问特点

    • 内部类可以直接访问外部类的成员,包括私有
    • 外部类要访问内部类的成员,必须创建对象
  • 示例代码:

    /*
        内部类访问特点:
            内部类可以直接访问外部类的成员,包括私有
            外部类要访问内部类的成员,必须创建对象
     */
    public class Outer {
        private int num = 10;
        public class Inner {
            public void show() {
                System.out.println(num);
            }
        }
        public void method() {
            Inner i = new Inner();
            i.show();
        }
    }
    

10.2 成员内部类(理解)

  • 成员内部类的定义位置

    • 在类中方法,跟成员变量是一个位置
  • 外界创建成员内部类格式

    • 格式:外部类名.内部类名 对象名 = 外部类对象.内部类对象;
    • 举例:Outer.Inner oi = new Outer().new Inner();
  • 私有成员内部类

    • 将一个类,设计为内部类的目的,大多数都是不想让外界去访问,所以内部类的定义应该私有化,私有化之后,再提供一个可以让外界调用的方法,方法内部创建内部类对象并调用。

    • 示例代码:

      class Outer {
          private int num = 10;
          private class Inner {
              public void show() {
                  System.out.println(num);
              }
          }
          public void method() {
              Inner i = new Inner();
              i.show();
          }
      }
      public class InnerDemo {
          public static void main(String[] args) {
      		//Outer.Inner oi = new Outer().new Inner();
      		//oi.show();
              Outer o = new Outer();
              o.method();
          }
      }
      
  • 静态成员内部类

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

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

    • 示例代码

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

10.3 局部内部类(理解)

  • 局部内部类定义位置

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

    • 局部内部类,外界是无法直接使用,需要在方法内部创建对象并使用
    • 该类可以直接访问外部类的成员,也可以访问方法内的局部变量
  • 示例代码

    class Outer {
        private int num = 10;
        public void method() {
            int num2 = 20;
            class Inner {
                public void show() {
                    System.out.println(num);
                    System.out.println(num2);
                }
            }
            Inner i = new Inner();
            i.show();
        }
    }
    public class OuterDemo {
        public static void main(String[] args) {
            Outer o = new Outer();
            o.method();
        }
    }
    
    

10.4 匿名内部类(应用)

  • 匿名内部类的前提

    • 存在一个类或者接口,这里的类可以是具体类也可以是抽象类
  • 匿名内部类的格式

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

    • 举例:

      new Inter(){
          @Override
          public void method(){}
      } 
      
  • 匿名内部类的本质

    • 本质:是一个继承了该类或者实现了该接口的子类匿名对象
  • 匿名内部类的细节

    • 匿名内部类可以通过多态的形式接受

      Inter i = new Inter(){
        @Override
          public void method(){
              
          }
      }
      
  • 匿名内部类直接调用方法

    interface Inter{
        void method();
    }
    
    class Test{
        public static void main(String[] args){
            new Inter(){
                @Override
                public void method(){
                    System.out.println("我是匿名内部类");
                }
            }.method();	// 直接调用方法
        }
    }
    

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

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

    • 当发现某个方法需要,接口或抽象类的子类对象,我们就可以传递一个匿名内部类过去,来简化传统的代码
  • 示例代码:

    /*
        游泳接口
     */
    interface Swimming {
        void swim();
    }
    
    public class TestSwimming {
        public static void main(String[] args) {
            goSwimming(new Swimming() {
                @Override
                public void swim() {
                    System.out.println("铁汁, 我们去游泳吧");
                }
            });
        }
    
        /**
         * 使用接口的方法
         */
        public static void goSwimming(Swimming swimming){
            /*
                Swimming swim = new Swimming() {
                    @Override
                    public void swim() {
                        System.out.println("铁汁, 我们去游泳吧");
                    }
                }
             */
            swimming.swim();
        }
    }
    

11.Lambda表达式

11.1体验Lambda表达式【理解】

  • 代码演示

    /*
        游泳接口
     */
    interface Swimming {
        void swim();
    }
    
    public class TestSwimming {
        public static void main(String[] args) {
            // 通过匿名内部类实现
            goSwimming(new Swimming() {
                @Override
                public void swim() {
                    System.out.println("铁汁, 我们去游泳吧");
                }
            });
    
            /*  通过Lambda表达式实现
                理解: 对于Lambda表达式, 对匿名内部类进行了优化
             */
            goSwimming(() -> System.out.println("铁汁, 我们去游泳吧"));
        }
    
        /**
         * 使用接口的方法
         */
        public static void goSwimming(Swimming swimming) {
            swimming.swim();
        }
    }
    
  • 函数式编程思想概述

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

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

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

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

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

  • 格式:

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

    • 形式参数:如果有多个参数,参数之间用逗号隔开;如果没有参数,留空即可
    • ->:由英文中画线和大于符号组成,固定写法。代表指向动作
    • 代码块:是我们具体要做的事情,也就是以前我们写的方法体内容
  • 组成Lambda表达式的三要素:

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

11.3Lambda表达式练习1【应用】

  • Lambda表达式的使用前提

    • 有一个接口
    • 接口中有且仅有一个抽象方法
  • 练习描述

    ? 无参无返回值抽象方法的练习

  • 操作步骤

    • 定义一个接口(Eatable),里面定义一个抽象方法:void eat();
    • 定义一个测试类(EatableDemo),在测试类中提供两个方法
      • 一个方法是:useEatable(Eatable e)
      • 一个方法是主方法,在主方法中调用useEatable方法
  • 示例代码

    //接口
    public interface Eatable {
        void eat();
    }
    //实现类
    public class EatableImpl implements Eatable {
        @Override
        public void eat() {
            System.out.println("一天一苹果,医生远离我");
        }
    }
    //测试类
    public class EatableDemo {
        public static void main(String[] args) {
            //在主方法中调用useEatable方法
            Eatable e = new EatableImpl();
            useEatable(e);
    
            //匿名内部类
            useEatable(new Eatable() {
                @Override
                public void eat() {
                    System.out.println("一天一苹果,医生远离我");
                }
            });
    
            //Lambda表达式
            useEatable(() -> {
                System.out.println("一天一苹果,医生远离我");
            });
        }
    
        private static void useEatable(Eatable e) {
            e.eat();
        }
    }
    

11.4Lambda表达式练习2【应用】

  • 练习描述

    有参无返回值抽象方法的练习

  • 操作步骤

    • 定义一个接口(Flyable),里面定义一个抽象方法:void fly(String s);
    • 定义一个测试类(FlyableDemo),在测试类中提供两个方法
      • 一个方法是:useFlyable(Flyable f)
      • 一个方法是主方法,在主方法中调用useFlyable方法
  • 示例代码

    public interface Flyable {
        void fly(String s);
    }
    
    public class FlyableDemo {
        public static void main(String[] args) {
            //在主方法中调用useFlyable方法
            //匿名内部类
            useFlyable(new Flyable() {
                @Override
                public void fly(String s) {
                    System.out.println(s);
                    System.out.println("飞机自驾游");
                }
            });
            System.out.println("--------");
    
            //Lambda
            useFlyable((String s) -> {
                System.out.println(s);
                System.out.println("飞机自驾游");
            });
    
        }
    
        private static void useFlyable(Flyable f) {
            f.fly("风和日丽,晴空万里");
        }
    }
    

11.5Lambda表达式练习3【应用】

  • 练习描述

    有参有返回值抽象方法的练习

  • 操作步骤

    • 定义一个接口(Addable),里面定义一个抽象方法:int add(int x,int y);
    • 定义一个测试类(AddableDemo),在测试类中提供两个方法
      • 一个方法是:useAddable(Addable a)
      • 一个方法是主方法,在主方法中调用useAddable方法
  • 示例代码

    public interface Addable {
        int add(int x,int y);
    }
    
    public class AddableDemo {
        public static void main(String[] args) {
            //在主方法中调用useAddable方法
            useAddable((int x,int y) -> {
                return x + y;
            });
    
        }
    
        private static void useAddable(Addable a) {
            int sum = a.add(10, 20);
            System.out.println(sum);
        }
    }
    

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

  • 省略的规则

    • 参数类型可以省略。但是有多个参数的情况下,不能只省略一个
    • 如果参数有且仅有一个,那么小括号可以省略
    • 如果代码块的语句只有一条,可以省略大括号和分号,和return关键字
  • 代码演示

    public interface Addable {
        int add(int x, int y);
    }
    
    public interface Flyable {
        void fly(String s);
    }
    
    public class LambdaDemo {
        public static void main(String[] args) {
    //        useAddable((int x,int y) -> {
    //            return x + y;
    //        });
            //参数的类型可以省略
            useAddable((x, y) -> {
                return x + y;
            });
    
    //        useFlyable((String s) -> {
    //            System.out.println(s);
    //        });
            //如果参数有且仅有一个,那么小括号可以省略
    //        useFlyable(s -> {
    //            System.out.println(s);
    //        });
    
            //如果代码块的语句只有一条,可以省略大括号和分号
            useFlyable(s -> System.out.println(s));
    
            //如果代码块的语句只有一条,可以省略大括号和分号,如果有return,return也要省略掉
            useAddable((x, y) -> x + y);
        }
    
        private static void useFlyable(Flyable f) {
            f.fly("风和日丽,晴空万里");
        }
    
        private static void useAddable(Addable a) {
            int sum = a.add(10, 20);
            System.out.println(sum);
        }
    }
    

11.7Lambda表达式的使用前提【理解】

  • 使用Lambda必须要有接口
  • 并且要求接口中有且仅有一个抽象方法

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

  • 所需类型不同
    • 匿名内部类:可以是接口,也可以是抽象类,还可以是具体类
    • Lambda表达式:只能是接口
  • 使用限制不同
    • 如果接口中有且仅有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类
    • 如果接口中多于一个抽象方法,只能使用匿名内部类,而不能使用Lambda表达式
  • 实现原理不同
    • 匿名内部类:编译之后,产生一个单独的.class字节码文件
    • Lambda表达式:编译之后,没有一个单独的.class字节码文件。对应的字节码会在运行的时候动态生成

10-面向对象进阶

原文:https://www.cnblogs.com/he-peng/p/hepeng_15.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!