JAVA笔记(十四):接口、内部类

接口

1、案例

在这里插入图片描述
在这里插入图片描述

2、基本介绍

接口:给出一些没有实现的方法,封装到一起,到方法要使用的时候,再具体实现
(需实现所有方法)
在这里插入图片描述
PS:
jdk7以前,接口里的所有方法都没有方法体
jdk8以后,接口里可以有静态方法(需要用static修饰)默认方法(需要使用default修饰),接口中可以有方法的具体实现
在这里插入图片描述

3、应用场景

  1. 制定规定、规范、规格
  2. 开发软件,定义一个接口,然后由程序员实现接口

4、注意事项

  1. 接口不能被实例化(原因和抽象类一样,因为接口“不完整”)

  2. 接口中所有的方法是public方法(需要别的类实现它)

  3. 接口中抽象方法,可以不用abstract修饰

interface IB {
    void hi(); //相当于public abstract void hi();
}
  1. 一个普通类实现接口,就必须将该接口的所有方法都实现

  2. 抽象类实现接口,可以不用实现接口的所有方法

  3. 一个类可以同时实现多个接口

interface IB {
    void hi(); 
}

interface IC {
    void say();
}

class Pig implements IB,IC {
    @Override
    public void hi() {

    }

    @Override
    public void say() {

    }
}
  1. 接口中的属性,只能是final,而且是public static final修饰
    • static:接口属性是属于接口本身而不是实现接口的类的实例,即接口属性和接口绑定,直接通过接口就可以调用
    • final:接口属性是常量,一旦定义后就不能修改其值(但是可以被继承,也就是可以被使用)
interface IB {
    int n1 = 10; //等价 public  static final int n1 = 10;
}
  1. 一个接口不能继承其它的类,但是可以继承多个别的接口
interface ID extends IB,IC {}
  1. 接口的修饰符只能是public和默认,这点和类的修饰符是一样的

5、练习

  1. 判断语法是否正确,如果正确,输出什么?
public class Test {
    public static void main(String[] args) {
        B b = new B();
        System.out.println(b.a);  //子类可以访问父类的非私有属性、方法
        System.out.println(B.a);  //a是static属性,可以通过类名调用,子类类名可以调用父类的static属性
        System.out.println(A.a);
    }
}

interface A {
    int a = 23;
}
class B implements A {
}

输出:
在这里插入图片描述

6、接口实现vs继承

继承:父子类是一种is关系(包含)
实现:’父子类‘是一种学习关系(子类学习父类的方法)

public class Test {
    public static void main(String[] args) {
        LittleMonkey wukong = new LittleMonkey("wukong");
        wukong.climb();
        wukong.swim();
    }
}

class Monkey {
    String name;

    public Monkey(String name) {
        this.name = name;
    }
    public void climb() {
        System.out.println("猴子会爬树");
    }
}
interface Fish {
    public void swim();
}
class LittleMonkey extends Monkey implements Fish {
    public LittleMonkey(String name) {
        super(name);
    }

    @Override
    public void swim(){
        System.out.println("猴子学会了游泳");
    }
}

输出:
在这里插入图片描述
小结:

当子类继承了父类,就自动拥有弗雷德功能

当子类需要扩展功能,可以通过 实现接口 的方式扩展

可以理解 实现接口 是 对java 单继承机制 的一种补充

总结:
在这里插入图片描述

7、接口的多态

1) 多态参数

public class Computer {
    //UsbInterface形参是接口类型,这里体现了多态参数
    //既可以接收手机对象,也可以接收相机对象
    public void work(UsbInterface usbInterface) {
        //通过接口调用方法
        usbInterface.start();
        usbInterface.stop();
    }
}
public class Test {
    public static void main(String[] args) {
        //接口多态
        //接口类型变量ia 可以指向实现了IA的接口类 的对象实例
        IA ia = new Bike();
        ia = new Car();

        //继承多态
        //类变量aa      可以指向继承了AAA的子类 的对象实例
        AA aa = new BB();
        aa = new CC();
    }
}

interface IA {}
class Bike implements IA {};
class Car implements IA {};

class AA{}
class BB extends AA {};
class CC extends AA {};

2) 多态数组

public class Test {
    public static void main(String[] args) {
        Usb[] usbs = new Usb[2];

        usbs[0] = new Phone();
        usbs[1] = new Camera();

        for(int i = 0 ; i < usbs.length ; i++){
            if(usbs[i] instanceof Phone){ //判断运行类型是否为Phone
                ((Phone)usbs[i]).call();
            }
            usbs[i].work(); //动态绑定
        }
    }
}

interface Usb{
    void work();
}
class Phone implements Usb {
    public void call() {
        System.out.println("这是手机");
    }
    public void work(){
        System.out.println("手机工作中");
    }
}
class Camera implements Usb {
    public void work(){
        System.out.println("相机工作中");
    }
}

3) 接口存在多态传递

public class Test {
    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、接口多态练习

interface A {
    int x = 0;
}
class B {
    int x = 1;
}
class C extends B implements A {
    //访问接口的 x 就使用A.x
    //访问父类的 x 就使用super.x
    public void pX() {
        System.out.println(A.x + " " + super.x);
    }

    public static void main(String[] args) {
        new C().pX();
    }
}

内部类⭐⭐⭐⭐⭐

1、基本介绍

一个类的内部又完整地嵌套了另一个类结构,被嵌套的类成为内部类

内部类是类的第五大成员
(类的五大成员:属性、方法、构造器、代码块、内部类)

内部类最大的特点就是:可以直接访问私有属性。并且可以体现类与类之间的包含关系

在这里插入图片描述
案例

class Outer { //外部类
    private int n1 = 100; //属性

    public Outer(int n1) { //构造器
        this.n1 = n1;
    }

    public void m1() { //方法
        System.out.println("m1()");
    }
    { //代码块
        System.out.println("代码块");
    }

    class Inner { //内部类

    }
}

2、内部类的分类

在这里插入图片描述

3、局部内部类

  1. 定义在外部类的局部位置,通常在方法中,并且有类名

  2. 可以直接访问外部类的所有成员,包含私有的

  3. 不能添加访问修饰符,因为它的得就是一个局部变量。局部变量不能使用修饰符。但是可以用final修饰,因为局部变量也可以用final

  4. 作用域:仅仅在定义它的方法或代码块中

  5. 局部内部类—访问—>外部类的成员 【访问方式:直接访问】

  6. 外部类—访问—>局部内部类的成员 【访问方式:创建对象,再访问(注意:必须在作用域内)

  7. 外部其他类—不能访问---->局部内部类(局部内部类地位是一个局部变量)

  8. 如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问

public class LocalInnerClass {
    public static void main(String[] args) {
        Outer02 outer02 = new Outer02();
        outer02.m1();
    }
}

class Outer02 { //外部类
    private int n1 = 100;
    private void m2(){
        System.out.println("Outer02 m2()");
    } //私有方法,仅本类可访问

    public void m1() {//方法
        //1. 定义在外部类的局部位置,通常在方法中
        //3. 不能添加访问修饰符
        //4. 作用域:仅仅在定义它的方法或代码块中(Inner02只能在m1中使用)

        final class Inner02 { //局部内部类(本质还是一个类)(但其地位为局部变量)
            private int n1 = 800;
            //2. 可以直接访问外部类的所有成员,包含私有的
            public void f1() {
                //5. 局部内部类可以直接访问外部类的成员
                //8. 如果外部类和局部内部类的成员重名时,默认遵循就近原则
                //   如果想访问外部类的成员,则可以使用(外部类名.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();
    }
}

总结:
1)定义在局部位置
2)作用域在使用它的方法或代码块中
3)本质还是一个类,地位是局部变量

4、匿名内部类⭐⭐⭐⭐⭐

1)本质

1) 本质是类

2) 内部类

3) 该类没有名字

4) 同时还是一个对象

2)注意事项

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

  2. 为什么要使用匿名内部类?【需求是Tiger类只是使用一次,以后再也不使用了】

  3. 内部类的基本语法
    在这里插入图片描述

class Outer04 { //外部类
    private int n1 = 10;
    public void method() {
        //基于接口的匿名内部类
        //1. 需求:想使用接口IA,并创建对象
        //2. 传统方法:写一个类实现该接口,并创建这个类的对象
        //3. 需求是Tiger类只是使用一次,以后再也不使用了
        //4. 可以使用匿名内部类来简化

        //5. tiger的编译类型? IA
        //6. tiger的运行类型? 匿名内部类
        /*
            底层:系统会分配类名 :外部类类名$1
            class XXXX implements IA {
                @Override
                public void cry() {
                    System.out.println("老虎叫唤");
                }
            }
         */
        //7. jdk底层在创建匿名内部类Outer04$1,立即创建了Outer04$1的实例,并且把地址返回给tiger
        //8. 匿名内部类(Outer04$1)使用一次就不能再使用
        IA tiger = new IA() { //接口不能直接new,是因为它不完整。但我new的同时使它变完整(实现其方法),那就可以了
            @Override
            public void cry() {
                System.out.println("老虎叫唤");
            }
        };
        System.out.println("tiger的运行类型=" + tiger.getClass());
        tiger.cry();


        //分析
        //1. father的编译类型?Father
        //2. father的运行类型?Outer04$2
        //3. 参数列表jack会传递给构造器
        Father father = new Father("jack") {
            @Override
            public void test() {
                System.out.println("父亲来了");
            }
        };
        System.out.println("father的运行类型=" + father.getClass());
        father.test();
    }
}

interface IA {
    public void cry();
}

class Father {
    public Father(String name) {
        System.out.println("接收到name=" + name);
    }
    public void test(){

    }
}

输出:
在这里插入图片描述

3)使用

  1. 匿名内部类既是一个类的定义,本身也是一个对象。

  2. 因此从语法上看,它既有定义类的他特征,也有创建对象的特征。

public class AnonymousInnerClassDetail {
    public static void main(String[] args) {
        Outer05 outer05 = new Outer05();
        outer05.f1();
    }
}

class Outer05 {
    private int n1 = 99;
    public void f1() {
        //创建一个基于类的匿名内部类
        Person p = new Person() {
            @Override
            public void hi() {
                System.out.println("匿名内部类重写了hi方法");
            }
        };
        p.hi(); //动态绑定,运行类型是Outer05$1

        //也可以直接调用
        new Person() {
            @Override
            public void hi() {
                System.out.println("匿名内部类重写了hi方法");
            }
        }.hi();
    }
}

class Person {
    public void hi() {
        System.out.println("Person hi()");
    }
}

在这里插入图片描述

  1. 可以直接访问外部类的所有成员,包含私有的(如果外部类和匿名内部类的成员有重名时,遵循就近原则。如果想访问外部类的成员,可以使用外部类名.this.成员去访问)

  2. 不能添加访问修饰符,因为它的地位就是一个局部变量

  3. 作用域:仅仅再定义它的方法或代码块中

  4. 匿名内部类—访问—>外部类成员【访问方式:直接访问】

  5. 外部其他类—不能访问—>匿名内部类

4)匿名内部类的最佳实践

  1. 当作实参直接传递
public class Test {
    public static void main(String[] args) {
        //匿名内部类当作实参直接传递,简洁高效
        f1(new IM() { //new出来的对象,默认返回值为对象类型(IM)
            @Override
            public void show() {
                System.out.println("这是一幅名画");
            }
        });

        //传统方法
        f1(new Picture());
    }

    //静态方法,形参是接口类型
    public static void f1(IM im) {
        im.show();//动态绑定,和运行类型绑定
    }
}

interface IM {
    void show();
}

class Picture implements IM {
    @Override
    public void show() {
        System.out.println("这是一幅名画");
    }
}

课堂练习2
在这里插入图片描述

public class Test {
    public static void main(String[] args) {
        CellPhone cellPhone = new CellPhone();
        cellPhone.alarmclock(new Bell() {
            @Override
            public void ring() {
                System.out.println("懒猪起床了");
            }
        });
        cellPhone.alarmclock(new Bell() {
            @Override
            public void ring() {
                System.out.println("小伙伴上课了");
            }
        });
    }
}

interface Bell {
    void ring();
}

class CellPhone {
    public void alarmclock(Bell bell){
        bell.ring();
    }
}

5、成员内部类

  1. 成员内部类是定义在外部类的成员位置,并且没有static修饰

  2. 可以直接访问外部类的所有成员,包含私有的

  3. 可以添加任意访问修饰符(public、protected、默认、private),因为它的地位就是一个成员

  4. 作用域:和外部类的其他成员一样,作用域为整个类体

  5. 成员内部类—访问—>外部类成员【访问方式:直接访问】

  6. 外部类—访问—>成员内部类【访问方式:创建对象再访问】

  7. 外部其他类—访问—>成员内部类【两种方式】

    • 方式一:把成员内部类当成外部类的一个成员,用 外部类对象实例.new 内部类 来调用
    • 方式二:在外部类中,编写一个方法,可以返回Inner08对象
Outer08 outer08 = new Outer08();

//外部其他类---访问--->成员内部类【两种方式】
//方式一:把成员内部类当成外部类的一个成员,用  外部类对象实例.new 内部类  来调用
Outer08.Inner08 inner08 = outer08.new Inner08();
inner08.say();

//方式二:在外部类中,编写一个方法,可以返回Inner08对象
Outer08.Inner08 inner08Instance = outer08.getInner08Instance();
inner08Instance.say();
  1. 如果外部类和匿名内部类的成员有重名时,遵循就近原则。如果想访问外部类的成员,可以使用外部类名.this.成员去访问
public class Test {
    public static void main(String[] args) {
        Outer08 outer08 = new Outer08();
        outer08.t1();

        //外部其他类---访问--->成员内部类【三种方式】
        //方式一:把成员内部类当成外部类的一个成员,用  外部类对象实例.new 内部类  来调用
        Outer08.Inner08 inner08 = outer08.new Inner08();
        inner08.say();

        //方式二:在外部类中,编写一个方法,可以返回Inner08对象
        Outer08.Inner08 inner08Instance = outer08.getInner08Instance();
        inner08Instance.say();
    }
}

class Outer08 {
    private int n1 = 10;
    public String name = "张三";
    public class Inner08 { //成员内部类:定义在外部类的成员位置上
        double d1 = 0.99;
        public void say() {
            //可以直接访问外部类的所有成员,包含私有的
            System.out.println("Outer08 的 n1 = " + n1);
            System.out.println("Outer08 的 name = " + name);
        }
    }

    public void t1() {
        //外部类---访问--->成员内部类【访问方式:创建对象再访问】
        Inner08 inner08 = new Inner08();
        inner08.say();
        System.out.println("Inner08 的 d1 = " + inner08.d1);
    }

    //方式二:在外部类中,编写一个方法,可以返回Inner08对象
    public Inner08 getInner08Instance() {
        return new Inner08();
    }
}

6、静态内部类

  1. 静态内部类是定义在外部类的成员位置,并且有static修饰

  2. 可以直接访问外部类的所有成员,包含私有的,但不能直接访问非静态成员

  3. 可以添加任意访问修饰符

  4. 作用域:同其他的成员,为整个类体

  5. 静态内部类—访问—>外部类(比如静态属性)【访问方式:直接访问所有静态成员】

  6. 外部类—访问—>静态内部类【访问方式:创建对象再访问】

  7. 外部其他类—访问—静态内部类【两种方式】

  8. 如果外部类和匿名内部类的成员有重名时,遵循就近原则。如果想访问外部类的成员,可以使用外部类名.成员去访问(这里不加this,因为访问的成员是静态的,静态的不能和this一起出现)

public class Test {
    public static void main(String[] args) {
        Outer10 outer10 = new Outer10();
        outer10.m1();

        //外部其他类---访问---静态内部类【两种方式】
        //方式一:
        new Outer10.Inner10().say();
        //等价于:
        Outer10.Inner10 inner10 = new Outer10.Inner10();
        inner10.say();
        //这里不会创建一个Outer10实例
        //静态成员,不用进行类加载(不用加载Outer10)
        //但是say()不是静态成员,所以必须创建Inner10类实例对象,进行访问

        //方式二:
        //↓适用于public static void say() {}
        //Outer10.Inner10.say();

        //方式三:编写一个方法,可以返回静态内部类的对象实例
        Outer10.Inner10 inner101 = outer10.getInner10Instance();

        //↓适用于public static Inner10 getInner10Instance(){}
        //Outer10.Inner10 inner102 = Outer10.getInner10Instance();
    }
}

class Outer10 {
    private int n1 = 10;
    private static String name = "张三";

    static class Inner10 { //静态内部类
        private static String name = "张四";
        public void say() {
            //System.out.println(n1); //错误,不能访问非静态成员
            System.out.println(name); //张四
            System.out.println(Outer10.name); //访问外部类的同名成员:张三
        }
    }
    public void m1() { //外部类---访问--->静态内部类【访问方式:创建对象再访问】
        Inner10 inner10 = new Inner10();
        inner10.say();
    }

    public Inner10 getInner10Instance(){
        return new Inner10();
    }
}

小结:
(1)内部类有四种:局部内部类、匿名内部类、成员内部类、静态内部类
(2)重点:匿名内部类

new/接口(参数列表){
...
};

(3)成员内部类,静态内部类是放在外部类的成员位置,本质就是一个成员

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值