内部类这个地方比较容易混淆,还是要多总结、多是实战、多敲例子来熟悉、加深印象,从而熟练地掌握内部类的各种使用方法。

1、接口的定义:类似于class,其实就是一种特殊的类,而关键字是interface。

2、接口的默认定义:

  •   接口中的属性默认是静态常量、而且访问权限是public。
  •   接口中的方法默认都是抽象方法、而且访问权限是public。

    注意点:接口中一般不写成员变量,只写方法,相当于制定规则,故而将接口成为方法列表。

3、接口的作用:让java从单继承间接的实现了多继承,扩充了功能,可以将之认为是类的补充。

4、接口起作用的方式:让类去实现接口

  • 类与类之间的关系--继承  extends
  • 类与接口之间的关系--实现 implements

5、一个类可以实现多个接口

     注意点:如果两个借口定义了两个同名、同参数的方法、但是返回值不同,类则是没办法同时实心这两个接口。因为在同时实现的时候,无法最终确定这个方法的返回值类型。就好比一个人签订了两份合同,A合同要求性别男,B合同要求性别女。这是有冲突 的条件的,因此类无法同时实现。

    代码例子:

interface CookService{
   void cook();
}

interface NursemaidService{
    void cook();
    void wash();
}
class Nurse implements NursemaidService,CookService{
// 这个cook方法,在这两个接口中都定义了,那么只需要实现一次即可
    @Override
    public void cook() {
        System.out.println("做饭");
    }

    @Override
    public void wash() {
        System.out.println("洗衣服");
    }
}

6、接口的问题

  • 父类与接口的功能如何分配?

    答:一般父类存放的是主要功能,接口中放的是额外的功能,接口作为父类的补充。

  • 当一个类实现的接口中出现了相同的方法,子类中实现方法的时候会不会混淆?

    答:不会,接口中的方法都是抽象的,要通过子类来实现,接口中的方法只是声明,而我们在调用方法时候最终看的是功能,而功能在子类中。

  • 接口与父类可以同时存在吗? 答:可以
  • 一个子类只能有一个父类,也只能有一个接口吗?答:可以同时有多个接口。
  • 接口是否可以直接创建对象?答:不可以,只能通过子类实现。
  • 接口与接口之间是否可以有关系?如果有,会有什么关系? 答:可以有继承的关系,并且是多继承。

7、接口的向上向下转型类似于多态,就不用多加介绍了。

8、接口的子类重写方法注意事项:

  • 如果一个类实现两个接口,这两个接口中同时有相同的抽象方法,则在类中只需要重写一次这个方法。
  • 如果这两个接口中方法名、参数都相同的方法,其中一个接口是抽象方法、另一个是default修饰的有方法体。则该类也必须重写该方法。

9、接口的两个新特性,接上面的注意事项

  • 接口中添加了static方法:可以在接口中定义静态方法,静态方法不是抽象方法,是有实现部分的。同时,这个静态方法,只能由当前的接口名字调用,接口的引用和实现类都是不能使用的。
package practice;

public class Demo4 {
    public static void main(String[] args) {
        MyInterface.method1(); //正确
        MyInterface m = new Person();//接口中的静态方法,只能有当前接口的名字调用,接口的引用和实现类都不能使用的
//        m.method1();  错误
//        Person.method1();错误
    }
}

interface MyInterface {
    public static void method1() {
        System.out.println("接口中的静态方法实现");
    }
}

class Person implements MyInterface{
//这里不可以重写method1方法
//    @Override
//    public static void method1(){
//
//    }

}
  •   接口中添加了default方法:修饰接口中的方法,default修饰的方法可以添加默认的实现部分。此时, 实现类在实现接口的时候,对于这些方法可以重写,也可以不重写。
package practice;

public class Demo5 {
    public static void main(String[] args) {

        MyInterface2 myInterface23 = new Person1();
        myInterface23.method();   
//        Person.method2();//错误

    }
}
interface MyInterface2{
    public default void method(){
        System.out.println("接口的方法默认实现");
    }
}

class Person1 implements MyInterface2{
//    @Override
//    public void method() {
//        System.out.println("重写的method");
//    }
}

———————————————————————————————

1、内部类的分类

  •   成员内部类
  1. 概念:其实就是定义在一个类中内部的类(大白话就是在类中在定义一个类)

                注意:此内部类的地位与外部类的成员变量、成员方法,也可以把内部类看做成是外部类的成员,而成员           之间可以相互调用。而且成员内部类中,不能写静态属性、静态方法

     2. 成员内部类创建内部类对象

  • 因为成员内部类是外部类的一个成员部分,所以创建内部类对象时候,必须依赖外部类对象
  • Outer outer = new Outer(); Inner inner = outer.new Inner();
  • Inner inner = new Outer().new Inner();          //使用匿名外部类对象进行引用

    3. 示例代码如下:

class OuterClass {
    public String name;

    public OuterClass(String name) {
        this.name = name;
    }

    public class InnerClass {
        public InnerClass(String name) {
            this.name = name;
        }

        public String name;
        public void show(String name) {
            System.out.println(name); // 访问参数 name
            System.out.println(this.name); // 访问内部类属性 name
            System.out.println(OuterClass.this.name); // 访问外部类属性 name
        }
    }
    public void test(){
        System.out.println("Outer-show");
        InnerClass inner = new InnerClass("构造函数");
        inner.show("当前方法的值");
    }
}

public class Demo6 {
    public static void main(String[] args) {
        // 实例化外部类对象
        OuterClass outer = new OuterClass("外部类的值");
        //调用内部类的方法的方式
        //第一种:借助于外部类的方法实现
        outer.test();
        //方式二:借助外部类对象,实例化内部类对象
        //引用:外部类.内部类
        //构成:外部类对象的引用.new 内部类的构造方法
        OuterClass.InnerClass innerClass = outer.new InnerClass("afaf");  
        OuterClass.InnerClass innerClass = new OuterClass.new InnerClass("afaf");  //用匿名外部类对象引用
    }
}

 

  •   静态内部类

1、概念:使用static修饰的类,在类的内部与实例变量、实例方法地位相同的。

    注意点:静态内部类不一定有静态方法,但是有静态方法的一定是静态内部类。

2、成员内部类创建内部类对象

  • 不依赖外部类对象,可以直接创建或通过类名访问。
  • OuterClass.InnerClass inner = new OuterClass.InnerClass();

3、示例代码如下:

package practice;

public class OuterClass {
    //静态内部类不一定有静态方法,有静态方法的一定是静态内部类  类似于抽象类的介绍
    static class InnerClass {
        String name;
        public void show(String name) {
            System.out.println(name);
            System.out.println(this.name);
        }
    }
}
class Test {
    public static void main(String[] args) {
// 1. 实例化静态内部类的对象
//构成: new + 外部类名字.内部类的构造方法
//       OuterClass.InnerClass innerClass = new OuterClass.InnerClass();
// 2. 可以通过导包的形式,
// 先导包 import 包.OuterClass.InnerClass
 Demo7.InnerClass innerClass = new Demo7.InnerClass();
        innerClass.show("aaa");
    }
}

总结1:成员内部类与静态内部的区别:第一点:成员内部类中不能写静态方法和静态属性;而静态内部类中可以写静态方法和静态属性。第二点:成员内部类对象的实例化需要借助外部类对象,而静态内部类的实例化不需要借助外部类对象。

  •   局部内部类

1、概念: 局部内部类其实就是定义在外部类的方法中,作用范围和创建对象范围仅限于当前方法。

     注意:局部内部类的变量必须是final修饰的常量不能使用访问权限修饰符修饰(public、protected等修饰符不能修饰局部内部类);局部内部类对象只能在方法中进行实例化,从而利用外部类对象访问成员方法进行创建局部内部类对象。

2、示例代码如下

package practice;

public class Demo8 {
    public static void main(String[] args) {
        Outer1 outer1 = new Outer1();
        outer1.show();  //输出 Inner-ruan=123
    }
}
class Outer1{
    public void show(){

        //定义一个局部变量,如果这个局部变量被包裹(或者说在局部代码段中用到了此局部变量)在了一个局部代码段中(比如局部内部类、匿名内部类),此时这个局部变量被隐式的定义为final
        int height=123;
        class Inner{
            public void run(){
                System.out.println("Inner-run="+height);
            }
        }
        //创建局部内部类对象
        Inner inner = new Inner();
        inner.run();
    }
}

  问题:为什么局部内部类访问外部类当前方法中的局部变量时,变量必须修饰为final?

             答:因为无法保障变量的生命周期用自身相同,所以局部变量必须修饰为final。

3、局部内部类的作用:局部内部类实现了对方法的功能私有化,并且对方法内的代码进行了整理,增强了代码可读性和可操作性。

     体现局部内部类对方法的功能私有化的 示例如下:

package practice;

public class Demo9 {
    public void play(){

        //如果我们想将这两个方法编程play方法的私有功能时,发现不能再play方法中直接写方法的定义,所以
        //必须将之写入局部内部类中
        /*public void printCar(){
            System.out.println("打印汽车");
        }*/

        class Inner{
            public void printCar(){
                System.out.println("打印汽车");
            }

            public void printAnimal(){
                System.out.println("打印动物");
            }

        }

        Inner inner = new Inner();
        inner.printAnimal();
        inner.printCar();
    }

    public void show(){
        //由于这两个方法是play的局部内部类中的方法,对play方法之外的方法都不可见
        //因为局部内部类对其进行了功能的私有化
        pirntCar();  //错误
        printAnimal();//错误
    }

}

       注意点: 若是在外部类的方法中写入了局部内部类了,其实就是对外部类当前类的功能进行了私有化,仅仅只能当前类可以访问,其他的方法根本不能访问,请谨记这一点。

  • 匿名内部类

1、概念:匿名内部类(对象)其实就是定义在一个类的方法中匿名子类对象,也是局部内部类

      提示:匿名内部类可以看成两部分:

                     匿名子类对象: 没有名字的子类的对象。

                     匿名内部类对象:定义在一个类中的方法中的匿名子类对象

 2、注意点:

  •   一切特点与局部内部类相同
  • 必须继承一个父类或者实现一个接口。
  • 进行了定义类、实现类、创建对象的语法合并,仅仅只能创建一个对象。

3、匿名内部类的作用:

  •   无需实例化,只用到当前子类的一个实例对象的时候,定义好马上就用,只使用一次(被当做垃圾了、会被垃圾回收机制处理),使用完立马释放
  •  当不太好起名字的时候使用匿名内部类
  •  可以更好的定义运行时的回调(知道即可)

4、创建匿名子类对象重点

  •    方式一: 使用已有的子类创建匿名子类对象

              使用场景: 已经创建好的子类可以多次使用,使用于相同功能

  •    方式二:  直接只用Animal创建匿名子类对象(重点)

            构成: new + 父类的名字/接口的名字 + () + {写当前子类的成员} + ;

            使用场景:只能使用一次,使用完会被当做垃圾回收,适用于每次都使用不同的功能

          匿名子类对象的示例代码如下:

package task;

public class BigData08 {
    public static void main(String[] args) {
        Animal animal = new Animal();
        new Animal().eat();  //匿名对象

        //匿名子类对象
        //第一种方式: 利用已有的子类创建匿名子类对象;
        new Dog().eat();

        //第二种方式: 直接使用Animal创建匿名子类对象,直接创建没有名字的Animal的匿名子类对象
        new Animal(){
            @Override
            public void eat() {
                System.out.println("Animal的匿名子类对象-eat");
            }
        }.eat();

    }

}
class Animal{
    public void eat(){
        System.out.println("Animal-eat");
    }
}
class Dog extends Animal{
    public void eat(){
        System.out.println("Dog-eat");
    }
}

5、匿名内部类的注意点:

  •  我们可以用匿名内部类作为方法的参数或返回值
  •  匿名内部类的父类可以是父类也可以是父接口
  • 除了new Object类是匿名对象,其他所有类的匿名对象本质上都是匿名子类对象。

 示例代码如下:

package task;

public class BigData08 {
    public static void main(String[] args) {
        //测试匿名内部类
        Test test1 = new Test();
        test1.canShuTest();//task.Animal@1b6d3586
        test1.canShuTest1();//task.Test$2@4554617c 

    }

}
class Animal{
    public void eat(){
        System.out.println("Animal-eat");
    }
}
//研究匿名内部类
class Test{
    public void show(){
        //匿名内部类
        new Animal(){
            @Override
            public void eat() {
                System.out.println("匿名子类对象-eat");
            }
        }.eat();
    }
    //普通的匿名对象作为参数
    public void canShuTest(){
        System.out.println(new Animal());
    }
    //匿名内部类作为参数
    public void canShuTest1(){
        System.out.println(new Animal(){
            @Override
            public void eat() {
                System.out.println("eat");
            }
        });
    }
    //普通的匿名对象作为返回值
    public Animal fanHuiZhiTest() {
        return new Animal();
    }
    //匿名内部类作为返回值
    public Animal fanHuiZhiTest1(){
        return new Animal(){
        };
    }
}