面向对象编程(高级)

面向对象编程(高级)

1、类变量和类方法

(1) 概念

类变量,也称为静态变量,是指在类级别声明的变量。它们与特定类相关联,而不是与类的实例(对象)相关联。每个类变量只有一份副本,它被类的所有实例共享。

static 变量保存在Class实例的尾部。而class对象确实存在堆中,所以,可以认为,static放在堆中

理解:

  • static变量是同一个类所有对象共享
  • static类变量,在类加载的时候就生成了

类方法(静态方法)是与类本身相关联的方法,而不是与类的实例(对象)相关联。它们可以直接通过类名调用不需要实例化对象。当方法中不涉及到任何对象相关的成员,就可以将方法设计成静态方法,提高开发效率。

举例:比如工具类里面的方法utils,Math类、Arrays类、Collections集合

(2)定义
类变量语法:
   1、 访问修饰符 static 数据类型 变量名;(推荐)
   2static 访问修饰符 数据类型 变量名;

类方法语法:
   1、 访问修饰符 static 数据返回类型 方法名(){};(推荐)
   2static 访问修饰符 数据返回类型 方法名(){};
(3)访问
类变量访问:
   1、 类名.类变量名  (推荐)  静态变量的访问修饰符的访问权限和范围和普通变量是一样的。
   2、 对象名.类变量名
    
类方法访问:
   1、 类名.类方法名  (推荐)  静态变量的访问修饰符的访问权限和范围和普通变量是一样的。
   2、 对象名.类方法名 

举例:

类变量:

public class StaticTest {
    public static void main(String[] args) {
        //类名.类变量名
        //类变量是随着类的加载而创建,不用创建对象也可以访问
        System.out.println(A.name);
        A a = new A();
        //通过对象名.类变量名也可以访问
        System.out.println(a.name);
    }
}

class A{
    //类变量也需要遵循相关的访问权限
    public static String name = "你好";
    //普通属性/普通成员变量/非静态属性/非静态成员变量
    private int num = 10;
}

类方法:

public class Student {
    public static void main(String[] args) {
        //类.类方法进行调用
        Stu.Pay(100);
        Stu.Pay(200);
        Stu.Show();
        System.out.println(MyTools.calSum(10,30));
    }
}

//开发自己的工具类,可以将方法做成静态的,方便调用
class MyTools{
    public static double calSum(double n1, double n2){
        return n1 + n2;
    }
}

class Stu{
    public static double totalFee = 0;
    private String name;

    public Stu(String name) {
        this.name = name;
    }
    //1、方法使用static修饰时,该方法就为静态方法
    //2、静态方法只能访问静态变量
    public static void Pay(double fee){
        Stu.totalFee += fee;
    }
    public static void Show(){
        System.out.println("总学费为"+totalFee);
    }
}
(4)注意事项和细节
  1. 什么时候需要用类变量

    需要某个类的所有对象都共享一个变量时,就可以考虑使用类变量(静态变量)

  2. 类变量和实例变量(普通变量)的区别

    类变量时类的所有对象共享,实例变量是每个对象共享

  3. 实例变量不能通过 类名.类变量名,进行访问

  4. 静态变量是类加载的时候,就创建了,所以不需要实例化对象,也可以访问

  5. 类变量的声明周期是随着类的加载开始,随着类消亡而销毁

  6. 静态方法(类方法)只能访问静态成员

  7. 类方法和普通方法都是随着类的加载而加载,将结构信息存储在方法区,类方法中无this参数,普通方法中隐藏着this参数。普通方法主要不能直接通过类名.类方法进行调用

  8. 类方法中不允许使用和对象有关的关键字,比如thissuper,普通方法可以

(5)类方法与普通方法的对比
特点 普通方法(实例方法) 类方法(静态方法)
关联实例 隶属于类的实例,通过实例化对象调用 不隶属于特定对象实例,直接通过类名调用
访问实例变量 可以访问和操作实例变量和实例方法,也可以访问静态方法、静态变量 不能直接访问实例变量或实例方法,只能访问静态方法和静态变量
thissuper关键字 可以在方法中使用 this 关键字、super关键字 不能在方法中使用 thissuper 关键字
动态绑定 支持动态绑定,根据对象类型执行方法 不支持动态绑定,根据类类型执行方法
非静态上下文 在对象实例上下文中执行 在静态上下文中执行

2、理解main方法

public static void main(String[] args){}
  1. main方法是虚拟机调用的

  2. java虚拟机需要调用类的main方法(不同类、不同包),所以该方法的访问权限必须是public

  3. java虚拟机在执行main()方法的时候不用创建对象,所以该方法是static

  4. 该方法接受String类型的数组参数,该数组中保存执行java命令时传递给所运行的类的参数

    java 执行的程序 参数1 参数2 参数3
    
  5. main方法中,可以直接调用main所在类的静态方法和静态属性,但是不能使用非静态方法和属性,除非创建该类的实例对象之后,才能通过这个对象去访问类的

IDEA如果给main函数传参:

面向对象编程(高级)_第1张图片

3、代码块

(1)概念

代码块是初始化块,属于类中的成员,类似于方法,将逻辑语句封装在方法体重,通过{}包围起来。但与方法不同,没有方法名,参数和返回值,只有方法体, 不需要对象或者类显示的调用,而是加载类,或创建对象时隐式的调用。

(2)语法
[修饰符]{
    代码
};
  1. 修饰符可选,要么不写,要么就是static写了static的代码块叫静态代码块,没有static修饰的,叫普通代码块/非静态代码块
  2. 最后面的分号可写可不写。里面的代码可以是任何的逻辑语句(比如:输入、输出、方法调用,循环,判断等等)
  3. 相当于另外一种形式的构造器(对于构造器的补充机制),可以做初始化的操作,如果多个构造器里面都有重复的雨具,可以抽取到初始化块当中,提高代码的重用性。
(3)注意事项
  1. 静态代码块(static),作用是对类进行初始化,随着类的加载而执行,并且 只会执行一次,如果是普通的代码块,每创建一个对象,就会执行。

  2. 类什么时候被加载?【重要】

    1. 创建对象实例时(new
    2. 创建子类对象实例,父类会被加载
    3. 使用类的静态成员时(静态属性、静态方法)
  3. 普通的代码块,在创建对象实例时,会被隐式的调用。被创建一次,就会被调用一次。如果只是使用类的静态成员时,普通代码块并不会执行。

  4. 创建对象时,一个类的调用顺序【重点和难点】:

    1. 调用静态代码块和静态属性的位置(多个则按顺序来)
    2. 调用普通代码块和普通属性(多个按顺序来)
    3. 调用构造方法

    举例:

    package JavaEE.chapter08.CodeBlock;
    
    public class CodeBlockDetail02 {
        public static void main(String args[]){
            A a = new A();
        }
    }
    
    class A{
        private int n2 = getN2();
        private static int n1 = getN1();
    
        {
            System.out.println("A的普通代码块");
        }
    
        static {
            System.out.println("A的静态代码块");
        }
    
        public static int getN1(){
            System.out.println("getn1被调用");
            return 100;
        }
    
        public int getN2(){
            System.out.println("A普通方法被调用");
            return 200;
        }
    
        public A(){
            System.out.println("无参构造器");
        }
    
    }
    
    /*输出
    getn1被调用
    A的静态代码块
    A普通方法被调用
    A的普通代码块
    无参构造器
    */
    
  5. 构造器的最前面隐含了super()和调用普通代码块,静态相关的代码块,属性初始化在类加载的时,就执行完毕了,因此是优先于构造器和普通代码块执行的。

    举例:

    class A{
        public A(){	//构造器
            //隐藏的执行要求
            //(1)super();
            //(2)调用普通代码块
            System.out.println("无参构造器");
        }
    }
    
  6. 创建子类对象时,静态代码块,静态属性初始化,普通代码块,普通属性初始化,构造方法调用顺序:

    1. 父类的静态代码块和静态属性
    2. 子类的静态代码和静态属性
    3. 父类的普通代码块和普通属性
    4. 父类的构造方法
    5. 子类的普通代码块和普通属性
    6. 子类的构造方法

    个人总结:

    1. 类加载(父类->子类)相关static代码执行
    2. 构造器
      1. super(父类)
      2. 普通代码块
      3. 构造方法
  7. 静态代码块只能调用静态成员,普通方法可以调用任意成员。

4、单例设计模式

(1)设计模式

设计模式是在大量的实践中总结和理论化之后优选的代码结构,编程风格,以及解决问题的思考方式。

(2)单例设计模式介绍
  1. 单例设计模式就是在整个的软件系统中,采取一定的方法保证某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法

  2. 实现方式

    1. 饿汉式(还没有用到对象,可能对象已经创建好)

      • 构造器私有化
      • 类的内部构造对象
      • 向外暴露一个静态的公共方法返回对象

      举例:

      class GirlFriend {
          private String name;
      
          //第一步:将构造器私有化
          private GirlFriend(String name){
              this.name = name;
          }
      
          //第二步,在类的内部直接创建静态对象
          private static GirlFriend gf = new GirlFriend("小红");
      
          //第三步:提供一个公共的static方法,返回gf对象
          public static GirlFriend getInstance(){
              return gf;
          }
      
          @Override
          public String toString() {
              return "GirlFriend{" +
                      "name='" + name + '\'' +
                      '}';
          }
      }
      
    2. 懒汉式(只有当用户使用getInstance的时候,才返回对象,再次调用时,返回是上次创建的cat对象)

      • 第一步:构造器私有化
      • 第二步:定义一个新的static静态对象
      • 第三步:提供一个publicstatic方法,可以返回一个对象

      举例:

      //单例设计模式的懒汉式
      class Cat{
          private String name;
          public static int n1 = 999;
      
          //第一步:构造器私有化
          private Cat(String name) {
              System.out.println("类加载。。。。");
              this.name = name;
          }
      
          //第二步:定义一个新的static静态对象
          private static Cat cat;
      
          //第三步:提供一个public的static方法,可以返回一个Ca对象
          public static Cat getInstance(){
              //还没有对象,就创建对象
              if(cat == null){
                  cat = new Cat("小花喵");
              }
              return cat;
          }
      
          @Override
          public String toString() {
              return "Cat{" +
                      "name='" + name + '\'' +
                      '}';
          }
      }
      
(3)饿汉式和懒汉式对比
  1. 饿汉式是在类加载的时候就创建了对象实例,而懒汉式是在使用时才创建
  2. 饿汉式不存在线程安全问题,懒汉式存在线程安全问题
  3. 饿汉式存在浪费资源的可能,懒汉式不存在这个问题
  4. javaSE中,java.lang.Runtime是一个经典的单例模式

5、FINAL 关键字

final 是 Java 中的一个关键字,它可以确保常量的值不会被修改。对于方法和类,它可以确保方法实现或类结构不会被修改或继承改变,这在某些情况下有助于代码的安全性和性能优化。

(1)final应用场景
  1. 对变量使用 final
    • 当应用于变量时,final 关键字表示该变量只能被赋值一次。一旦赋值后,就不能再更改。这适用于基本数据类型和对象引用。
    • 例如:final int x = 10; 声明了一个只能被赋值一次的整数变量 x
  2. 对方法使用 final
    • 当应用于方法时,final 表示该方法不能被子类重写(覆盖)。
    • 例如:final void someMethod() { // do something } 定义了一个不能被子类重写的方法。
  3. 对类使用 final
    • 当应用于类时,final 表示该类不能被继承,即它是最终的,不能有子类。
    • 例如:final class MyClass { // class definition } 声明了一个不能被继承的类。
(2)注意事项
  1. final修饰的属性又叫做常量,一般用用XX_XX来命名(大写加下划线)

  2. final修饰的属性必须赋初值,并且以后不能被修改,赋值可以有以下三个地方可以进行:

    1. 定义,例如: public final double TAX_RATE = 0.5;
    2. 构造器中
    3. 代码块中
  3. 如果final修饰的属性是静态的,初始化的位置只能是:

    1. 定义中
    2. 静态代码块中
  4. 类不是final类,但是含有final方法,方法不能被重写,但是可以被继承

    举例:

    public class FinalDetail01 {
        public static void main(String[] args) {
            BB bb = new BB();
            bb.cal();
        }
    }
    class AA{
        final public void cal(){
            System.out.println("CAL方法");
        }
    }
    class BB extends AA{
    
    }
    
  5. 一般来说,一个类已经是final类了,没有必要再将方法修饰成final方法。(因为已经无法被继承了)

  6. final不能修饰构造方法(即构造方法)

  7. finalstaic往往搭配使用,效率更高,不会导致类加载,底层编译器做了优化处理

    举例:

    class Demo{
        public final static int n1 = 16;
        static {
            System.out.println("这里是类加载,不会被执行");
        }
    }
    
  8. 包装类(Integer,Double,Float,Boolean等都是final)String也是final类

6、抽象类

(1)介绍

当父类的一些方法不能确定时,可以用abstract关键字来修饰该方法,这个方法就是抽象方法,用abstract来修饰该类就是抽象类。抽象类通常用于当我们希望有一个通用的类定义,但其中的某些方法在基类中并不具备实际的实现,而是应该由具体的子类来实现。在框架和设计模式比较多

举例:

//抽象类不能被实例化,有抽象方法,这个类必须声明为abstract
abstract class AA{
    //抽象方法没有方法体
    public abstract void eat();
}
(2)注意事项
  1. 抽象类不能被实例化

  2. 抽象类可以没有抽象方法,但是有抽象方法的类一定是抽象类。

    举例:

    abstract class BB{
        public void cal(){
            System.out.println("抽象类可以没有抽象方法");
        }
    }
    
  3. 抽象类只能修饰类和方法,不能修饰属性和其他的类型。

  4. 抽象类可以有任意成员 (抽象类本质还是类),比如:非抽象方法、构造器、静态属性等。

    举例:

    abstract class BB{
        //属性
        private String name;
        //构造器
        public BB() {
        }
        //普通方法
        public void cal(){
            System.out.println("抽象类可以没有抽象方法");
        }
        //抽象方法,没有方法体
        abstract void hi();
    }
    
  5. 如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,除非它自己也声明为abstract

    举例:

    abstract  class E{
        public abstract void hi();
        public abstract void sorry();
    }
    //子类必须实现父类的所有抽象方法
    class F extends E{
        public void hi(){
            System.out.println("hi");
        }
        public void sorry() {
            System.out.println("sorry");
        }
    }
    
  6. 抽象方法不能用private、final和static来修饰,因为这些关键字是与重写相违背的。

(3)抽象类实践-模版设计模式

抽象类体现的是一种模版模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展,改造,但子类总体会保留抽象类的行为模式。

可以解决的问题有:

  1. 当功能内部一部分是确定的,一部分是不确定的,这时可以把不确定的部分暴露出去,让子类去实现
  2. 编写一个抽象父类,父类提供多个子类的通用方法,并把一个活多个方法留给子类实现,这个就是模版设计模式。

举例:

abstract class Template{
    public abstract void job();

    //一部分抽象,一部分不抽象,可以转变为一个模板,提供给子类实现
    public void calculate(){
        long time = System.currentTimeMillis();
        job();
        long nowtime = System.currentTimeMillis();
        System.out.println("该任务执行时间为:"+(nowtime-time));
    }
}

class Cat extends Template{
    //子类只需要负责实现抽象部分即可
    public void job(){
        long sum = 0;
        for (int i = 0; i < 8000000; i++) {
            sum += i*2;
        }
    }
}

class Tiger extends Template{
    //子类只需要负责实现抽象部分即可
    public void job(){
        long sum = 0;
        for (int i = 0; i < 8000000; i++) {
            sum += i;
        }
    }
}

7、接口

(1) 基本介绍

接口就是给出一些没有实现的方法,封装到一起,到某个类需要使用的时候,在根据具体情况把这些方法写出来

语法:

interface 接口名{
    //属性
    //方法
}

class 类名 implements 接口{
    //自己属性
    //自己方法
    //必须实现的接口的抽象方法
}
//Jdk7.0之前,接口里面的所有方法都没有方法体
//Jdk8.0之后,接口中可以有方法的具体实现(默认方法、或者静态方法)

接口是更加抽象的抽象的类,Jdk8.0之后,接口类里面可以用静态方法,默认方法,可以有具体方法的实现。

举例:

interface AInterface{
    public int n1 = 10;
    //抽象方法
    public void hi();
    //使用关键字default修饰之后,可以实现具体的方法
    default void ok(){
        System.out.println("这里可以声明方法");
    }
    //使用static修饰之后,也可以实现具体的方法
    static void you(){
        System.out.println("这里可以声明方法");
    }
}

//需要实现接口类的所有抽象方法
class A implements AInterface{
    @Override
    public void hi() {
        System.out.println("实现接口类的抽象方法");
    }
}
(2)注意事项
  1. 接口不能被实例化

  2. 接口中所有的方法都是public方法,接口中的抽象方法,可以不用abstract修饰

    void aaa();
    实际上等同于 public abstract void aaa();
    
  3. 一个普通类实现接口,就必须将该接口的所有方法都实现。而抽象类实现接口,就可以不用实现接口的方法。

    interface AInterface{
        //抽象方法
        public abstract void hi();
    }
    
    //抽象类可以不用实现抽象方法
    abstract class B implements AInterface{
    }
    
  4. 一个类可以实现多个接口

    interface  AI{}
    interface BI{}
    class BC implements AI, BI{}
    
  5. 接口中的属性,只能是final,而且是public static final修饰符,比如:int a = 1;实际上是 public static final int a = 1(必须初始化),属性访问形式:接口名.属性名

  6. 接口不能继承其他的类,但是可以继承多个别的接口

    interface  AI{}
    interface BI{}
    interface BD extends AI,BI{}
    
  7. 接口的修饰符,只能是public或者是默认的, 这里和普通的类一样

(3)接口对比继承

接口是对行为的抽象,而继承是对类的抽象。 当子类继承了父类,就自动拥有父类的功能。如果子类需要扩展功能,可以通过实现接口的方式扩展。可以理解为接口是对单继承机制的一种补充。

  1. 继承的价值:解决代码的复用性和可维护性(is - a关系)
  2. 接口的价值:设计好各种规范(方法),让这些类去实现这些诶方法,更加的灵活。(like -a 关系)
  3. 接口在一定程度上可以实现代码的解耦(接口规范性+动态绑定)
(4) 接口多态
  1. 接口的多态

  2. 多态数组

    以上两种举例:

    public class InterfacePolyParameter {
        public static void main(String[] args) {
            //接口的多态,
            //接口类型的变量 if01可以指向实现了IF接口的类的对象实例
            IF if01 = new Monster();
            if01 = new Car();
    
            //多态数组
            IF[] if02 = new IF[2];
            if02[0] = new Monster();
            if02[1] = new Car();
        }
    }
    
    interface IF{}
    class Monster implements IF {}
    class Car implements  IF{}
    
  3. 接口多态传递

public class InterfacePoly {
    public static void main(String[] args) {
        //接口类型变量可以指向,实现该接口的类的对象实例
        IG ig = new Teacher();
        //IG继承了IH接口,Teacher实现了IG接口,相当于Teacher实现了IH接口,这就是接口的多态传递现象
        IH ih = new Teacher();
    }
}
interface IH{}
interface IG extends IH{}
class Teacher implements IG{}

8、内部类【难点和重点】

(1)基本介绍

一个类的内部有完成的嵌套了另外一个类的结构。被嵌套的类称为内部类(inner class),嵌套其他类的类称为外部类(outer class)。是类的第五大成员(属性、方法、构造器、代码块、内部类),内部类的最大特点就是可以直接访问私有属性,并且可以体现类与类之间的包含关系。在很多源码当中都有大量内部类。

举例:

//类的五大成员
class Outer{//外部类
    //属性
    private int n1 = 100;
    
    //构造器
    public Outer(int n1) {
        this.n1 = n1;
    }
    //方法
    public void m1(){
        System.out.println("方法");
    }
    //代码块
    {
        System.out.println("代码块...");
    }
    //内部类
    class Inner{//内部类,在Outer类的内部
        
    }
}

内部类的种类:

  1. 在局部位置(方法/代码块):
    1. 局部内部类(有类名)
    2. 匿名内部类(没有类名,重点)
  2. 成员位置:
    1. 成员内部类(没有static修饰)
    2. 静态内部类(有static修饰)

基本语法:

class Outer{//外部类
    class Inner{//内部类
    }
}
(2)局部内部类

局部内部类是定义在外部类的局部位置,比如方法或者代码块中,并且有类名

  1. 可以直接访问外部类的所有成员,包含私有成员
  2. 不能添加访问修饰符,因为这是一个局部变量,可以使用final,局部变量可以使用final
  3. 作用域:仅仅在定义它的方法或者代码块中
  4. 局部内部类—访问---->外部类的成员[访问方式:直接访问]
  5. 外部类—访问—>局部内部类的成员[访问方式:创建对象后,再访问(必须在作用域内)]
  6. 外部其他类—不能访问---->局部内部类
  7. 如果外部类与局部内部类的成员重名时,遵循就近访问原则,如果想访问外部类的成员可以使用[外部类名.this.成员]去访问

举例:

class Outer02{//外部类
    private int n1 = 10;
    private void m2(){}//私有方法
    public void m1(){//方法
        //1、局部内部类是定义在外部类的局部位置,通常实在方法里面
        //3、不能添加访问修饰符,但是可以使用final修饰(不能被继承)
        //4、作用域:仅仅在方法或代码块中
        class Inner02{  //局部内部类(本质仍为一个类)
            private int n1 = 800;
            public void f1(){
                //2、可以直接访问外部类的所有成员,包含私有的
                //5、局部内部类可以直接访问外部类的成员,比如n1,m2()
                //7、如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,可以使用 外部类名.this.成员去访问
                //Outer02.this 的本质就是外部类的对象,哪个对象调用了m1,Outer02.this就是哪个对象
                System.out.println("内部类n1= "+n1);
                System.out.println("外部类的n1 ="+Outer02.this.n1);
                m2();
            }
        }
        //6、外部类在方法中,可以创建Inner02对象,然后调用方法即可
        Inner02 inner02 = new Inner02();
        inner02.f1();
    }
}

注意事项:

  • 局部内部类定义在方法中/代码块中
  • 作用域在方法体或者代码块中
  • 本质仍然是一个类
(3)匿名内部类(重要)

匿名内部类是一个没有类名的内部类。如果一个内部类的对象在一个地方只需要用一次,那么我们可以使用匿名内部类,它可以简化代码,使得代码更易于阅读。匿名内部类在编译的时候由系统自动用其父类名加$1、$2这样的方式来命名。

匿名内部类定义在外部类的局部位置,比如方法中,并且没有类名

基本语法:

new 类或接口(参数列表){
    类体
};
//参数列表会传给类的构造器

举例:

class Outer04{//外部类
    private int n1 = 10;//属性
    public void method(){//方法
        //基于接口的匿名内部类
        //1、需要A接口,并创建对象
        //2、传统方法,是写一个类,实现接口,并创建对象
        //3、只需要使用一次,后面不需要再使用
        //4、使用匿名内部类简化开发
        //5、匿名内部类tiger的运行类型为 Outer04$1,编译类型是A
        //6、匿名内部类的底层实现:
        /*
            class Outer04$ implement A{
                public void cry() {
                System.out.println("老虎在叫");
                }
            }
         */
        //7、jdk在底层创建匿名内部类Outer04$1,立马就创建了Outer04$01实例,并且把地址返回
        //8、匿名内部类使用一次就不再使用
        A tiger = new Tiger(){
            public void cry() {
                System.out.println("老虎在叫");
            }
        };
        tiger.cry();
//        A tiger = new Tiger();
//        tiger.cry();
    }
}

interface A{//接口
    public void cry();
}

class Tiger implements A{
    @Override
    public void cry() {
        System.out.println("老虎在叫");
    }
}

注意事项:(与局部内部类类似)

  1. 语法比较不一样,既有定义类的特征,也有创建对象的特征。
  2. 可以直接访问外部类的所有成员,包含私有成员
  3. 不能添加访问修饰符,因为这是一个局部变量,可以使用final,局部变量可以使用final
  4. 作用域:仅仅在定义它的方法或者代码块中
  5. 外部其他类—不能访问---->局部内部类
  6. 如果外部类与局部内部类的成员重名时,遵循就近访问原则,如果想访问外部类的成员可以使用[外部类名.this.成员]去访问

实践:

public class AnonymousClassPractice {
    public static void main(String[] args) {
        //1、将匿名内部类直接当做实参传入函数当中
        f1(new Fot() {//软编码模式
            @Override
            public void show() {
                System.out.println("匿名内部类重写show方法");
            }
        });
        //正常实现,比较麻烦
        Example example = new Example();
        f1(example);
    }

    public static void f1(Fot fot){
        fot.show();
    }
}

interface Fot{
    void show();
}

//硬编码模式
class Example implements Fot{
    @Override
    public void show() {
        System.out.println("普通类重写show方法");
    }
}
(4)成员内部类

成员内部类是创建在外部类的成员位置,没有static修饰

注意事项

  1. 可以直接访问外部类的所有成员,包含私有成员
  2. 可以添加任意的修饰符(public、protected、private、默认),可以认为这个类就是一个成员
  3. 作用域是和其他类的成员一样,是整个类体
  4. 成员内部类-----访问----->外部类成员(比如:属性)【访问方式:直接访问】
  5. 外部类------访问------>成员内部类【访问方式:创建对象,再访问】
  6. 外部其他类-----访问----->成员内部类【两种方式:外部类名.new 内部类名,或者外部类名创建一个方法返回内部类】
  7. 如果外部类和内部类的成员重名时,内部类访问遵循就近原则,如果想访问外部类的成员,可以使用(外部类名.this.成员)去访问

举例:

public class MemberInnerClass {
    public static void main(String[] args) {
        Outer08 outer08 = new Outer08();
        outer08.t1();

        //外部其他类,访问成员内部类的两种种方式;
        //第一种方式:把内部类当做外部类的一个成员来使用
        Outer08.Inner08 inner08 = outer08.new Inner08();
        inner08.say();

        //第二种方式,在Outer里面创建一个方法,这个方法返回一个内部类地址
        Outer08.Inner08 inner081 = outer08.getInner08Instance();
        inner081.say();

    }
}

class Outer08{
    private int n1 = 10;
    public String name = "张三";
    //1、内部类是定义在外部类的成员位置
    //2、可以用public、private、protected修饰
    //3、作用域在整个类体
    class Inner08{//成员内部类
        public void say(){
            //4、可以访问外部类的所有成员,包含私有的
            System.out.println("n1 = "+n1 + " name = "+name);
        }
    }

    public void t1(){
        //5、外部类使用内部类,创建对象,使用他的成员即可
        Inner08 inner08 = new Inner08();
        inner08.say();
    }

    public Inner08 getInner08Instance(){
        return new Inner08();
    }
}
(5)静态内部类

静态内部类是定义在外部类的成员位置,并且有static修饰,与成员内部类基本没有区别

注意事项:

  1. 可以直接访问外部类的所有成员,包含私有成员
  2. 可以添加任意的修饰符(public、protected、private、默认),可以认为这个类就是一个成员
  3. 作用域是和其他类的成员一样,是整个类体
  4. 成员内部类-----访问----->外部类成员(比如:属性)【访问方式:直接访问】
  5. 外部类------访问------>成员内部类【访问方式:创建对象,再访问】
  6. 外部其他类-----访问----->成员内部类【两种方式:外部类名.new 内部类名,或者外部类名创建一个方法返回内部类】
  7. 如果外部类和内部类的成员重名时,内部类访问遵循就近原则,如果想访问外部类的成员,可以使用(外部类名.成员)去访问 【不需要加this】

你可能感兴趣的:(Java知识体系,java,python,前端)