5. 内部类

接口变化

JDK 1.8 之后,接口的语法有了新的变化。增加了两类成员。

  1. 静态方法 public static。static 不可以省略,通过 “接口名.方法名” 来进行调用。
  2. 默认方法 public default。default 不可以省略,通过 “实现类对象.方法名” 来进行调用。
package com.atguigu.code;

public class NewInterface {
    public static void main(String[] args) {
        MyInter.print();    // 直接调用接口的静态方法

        MyImpl myImpl = new MyImpl();
        myImpl.test();      // 调用接口实现的抽象方法
        myImpl.method();    // 调用接口的默认方法
    }
}

interface MyInter{
    String INFO = "info";  // 1.8 之前就有的属性,全局的静态的常量
    void test();           // 1.8 之前就有的属性,公共的抽象的方法

    public static void print() {
        System.out.println("1.8 接口新增静态方法");
    }

    public default void method() {
        System.out.println("1.8 接口新增默认方法");
    }
}

class MyImpl implements MyInter{

    @Override
    public void test() {
        System.out.println("实现接口的抽象方法");
    }

}

接口中默认方法冲突

  1. 在实现多个接口时候,接口中都有签名相同的方法。
package com.atguigu.code;


public class InterfaceSameMethod {
    public static void main(String[] args) {
        C c = new C();
        c.method();
    }
}

interface A{
    public default void method() {
        System.out.println("A 默认方法");
    }
}

interface B{
    public default void method() {
        System.out.println("B 默认方法");
    }
}

class C implements A, B{
    @Override
    public void method() {
        A.super.method();  // 可以使用 A.super.method() 调用 A 中的 method
        B.super.method();  // 可以使用 B.super.method() 调用 B 中的 method
    }
}
  1. 在继承和接口的实现中,父类和接口中都有签名相同的方法。
package com.atguigu.code;

import org.omg.CORBA.PUBLIC_MEMBER;

public class InterfaceSameMethod {
    public static void main(String[] args) {
        D d = new D();
        d.method();   // 默认调用的是父类中的,也就是说当父类和接口的有签名相同的方法时,先使用父类中的

        E e = new E();
        e.method();   // 指定调用

        F f = new F();
        f.method();      // 自己重新实现
    }
}

interface A{
    public default void method() {
        System.out.println("A 默认方法");
    }
}

class C{
    public void method() {
        System.out.println("C 中的 method");
    }
}

class D extends C implements A{

}

class E extends C implements A{
    public void method () {
        A.super.method();      // 指定调用接口中的
    }
}

class F extends C implements A{
    public void method () {
        System.out.println("自己实现");      // 自己重新实现
    }
}

内部类

含义:当一个类的内部,仍然有一个完整的结构。这个完整的结构仍然需要一个类进行描述,因为有自己的特征(属性,方法),并且这个内部类是为外部类服务的。

分类

  1. 成员内部类:和成员变量一样,在类中,方法外。成员内部类又可以分为两种,分别是静态成员内部类(简称静态内部类)和非静态成员内部类(简称成员内部类
  2. 局部内部类:在类中,方法内。局部内部类又可以分为两种,分别是由名字的局部内部类(简称为局部内部类)和没有名字的局部内部类(简称为匿名内部类

常见程度:静态内部类 = 成员内部类 > 匿名内部类 > 局部内部类

静态内部类

静态内部类中,类的5大成员它都可以拥有。

  1. 属性:静态和非静态属性
  2. 方法:静态方法和非静态方法。在抽象的静态内部类中,还可以有抽象方法
  3. 代码块:静态的和非静态的
  4. 构造器:有参的和无参的
  5. 内部类:语法上可以,但是太复杂了,一般不这么写

静态内部类的修饰符

  1. 权限修饰符:4个都可以有
  2. static必须有
  3. abstract 可以有
  4. final 可以有

使用

  1. 在静态内部类中不允许使用外部类的非静态的成员(static 中不能使用非 static)

  2. 在外部类中,使用静态内部类和使用其他类一样

  3. 在外部类的外面使用静态内部类,不需要外部内的对象。例如下面的 Outer.Inner

  4. 在外部类的外面要调用静态内部类的非静态方法时,则需要静态内部类的对象。例如:

    Outer.Inner obj = new Outer.Inner();
    obj.test();

  5. 在外部类的外面要调用静态内部类的静态方法时,则不需要静态内部类的对象。例如:Outer.Inner.test2();

package com.atguigu.code;

import com.atguigu.code.Outer.Inner;

public class TestStaticInner {
    public static void main(String[] args) {
        // 调用 Inner 的 test 方法
        Outer.Inner obj = new Outer.Inner();
        obj.test();
        // 调用 Inner 的 test2 方法
        Outer.Inner.test2();
    }
}


class Outer{
    private static int i;
    private int j;

    /**
     * 静态内部类
     * @author rex
     *
     */
    static class Inner{
        /**
         *静态内部类中的非静态方法
         */
        public void test() {
            System.out.println(i);     // 正确,静态内部类中可以直接使用外部类的静态私有成员。
            // System.out.println(j);  错误的,static 中不能使用非 static
        }

        /**
         * 静态内部类中的静态方法
         */
        public static void test2() {
            System.out.println(i);
        }
    }

    /**
     * 在外部类中使用静态内部类和使用其他类一样
     * @return
     */
    public Inner GetInner() {
        Inner inner = new Inner();
        return inner;
    }
}

静态内部类也有自己的字节码文件,文件格式为 外部类$静态内部类.class

非静态内部类

非静态内部类中,类的5大成员它都可以拥有。但是都是非静态的

  1. 属性:非静态属性
  2. 方法:非静态方法。在抽象的静态内部类中,还可以有抽象方法
  3. 代码块:非静态的
  4. 构造器:有参的和无参的
  5. 内部类:语法上可以,但是太复杂了,一般不这么写

非静态内部类的修饰符

  1. 权限修饰符:4个都可以有
  2. static 没有
  3. abstract 可以有
  4. final 可以有

使用

  1. 在非静态内部类中可以使用外部类的所有成员
  2. 在外部类中,使用非静态内部类有限制。在外部类的静态方法中不允许使用非静态内部类。(static 中不能使用非 static)
  3. 在外部类外面要调用非静态内部类的非静态方法,需要外部类的对象和内部类的对象。例如:
package com.atguigu.code;


public class TestStaticInner2 {
    public static void main(String[] args) {
        // 调用 Inner 中的 test 方法
        Outer2 outer2 = new Outer2();              // 需要外部类 Outer2 对象
        Outer2.Inner inner = outer2.new Inner();   // 需要 Inner 对象
        inner.test();                              // 调用 test 方法

        // 若上面的outer2.new Inner() 写法太奇怪,还可以借助 "外部类中调用非静态内部类,并返回对象" 方法
        Outer2.Inner inner2 = outer2.getInner();
        inner2.test();
    }
}


class Outer2{
    private static int i;
    private int j;

    /**
     * 非静态内部类
     * @author rex
     *
     */
    class Inner{
        /**
         * 非静态内部类中的非静态方法
         */
        public void test() {
            System.out.println(i);
            System.out.println(j);
        }
    }

    /**
     * 外部类中调用非静态内部类,并返回对象
     * @return
     */
    public Inner getInner() {
        return new Inner();
    }
}

非静态内部类也有自己的字节码文件,文件格式为 外部类$非静态内部类.class

局部内部类

局部内部类的修饰符

  1. 权限修饰符:没有
  2. static 没有
  3. abstract 可以有
  4. final 可以有

使用

  1. 局部内部类的作用域只在类所在的方法中,非常狭窄,这也是局部内部类用得比较少的原因。
  2. 局部内部类中是否能使用外部类的非静态成员变量,要看所在方法是否是静态的。(要遵循 static 中不能使用非 static 的原则)
  3. 局部内部类中可以使用所在方法的局部变量,但是该局部变量必须加 final 声明 (JDK 1.8 之前必须手动加上 final,1.8 开始就自动为我们加上了)
  4. 局部内部类中不能有静态成员(能有静态成员的内部类就只有静态内部类)
package com.atguigu.code2;

public class TestLocalInner {

}

class Outer{
    public static int i = 10;
    public int j = 10;

    public void outTest() {
        int a = 10;  // 局部变量,1.8 开始前面默认就加上类 final

        class Inner{
            public void test() {
                System.out.println(i);
                System.out.println(j);
                System.out.println(a);
            }
        }

        Inner inner = new Inner();
        inner.test();

    } // Inner 的作用域只到这儿

    public static void outMethod() {

        class Inner{
            public void test() {
                System.out.println(i);
                // System.out.println(j); 这里就不能使用j,要遵循 static 中不能使用非 static 的原则
            }
        }

    }
}

局部内部类也有自己的字节码文件,文件格式为 外部类$数字+局部内部类.class。有数字的原因是很可能有多个名称相同的局部内部内,例如上面例子中就有两个局部内部类 Inner

匿名内部类

匿名内部类的声明位置在创建对象的位置,特点是一边声明一边创建对象,且匿名内部类只有唯一的一个对象。匿名内部类没有任何修饰符,也没有名字。匿名内部类有构造器(默认的构造器),但是不能复写(因为没有名字)

使用匿名内部类是一种特殊的局部内部类,所以凡事局部内部类的限制,匿名内部类都有。如下所示

  1. 局部内部类的作用域只在类所在的方法中,非常狭窄,这也是局部内部类用得比较少的原因。
  2. 局部内部类中是否能使用外部类的非静态成员变量,要看所在方法是否是静态的。(要遵循 static 中不能使用非 static 的原则)
  3. 局部内部类中可以使用所在方法的局部变量,但是该局部变量必须加 final 声明 (JDK 1.8 之前必须手动加上 final,1.8 开始就自动为我们加上了)
  4. 局部内部类中不能有静态成员(能有静态成员的内部类就只有静态内部类)
package com.atguigu.code3;

public class TestNoNameInner {
    public static void main(String[] args) {
        // 调用父类无参构造,myClass 是 并不是 MyClass 的对象,而是 MyClass 的子类对象
        MyClass myClass = new MyClass() {
            @Override
            public void test() {
                System.out.println("匿名内部类重写 MyClass test 方法");
            }
        };
        myClass.test();

        // 调用父类有参构造, myClass2 是 并不是 MyClass 的对象,而是 MyClass 的子类对象
        MyClass myClass2 = new MyClass("父类有参构造") {
            @Override
            public void test() {
                System.out.println("匿名内部类重写 MyClass test 方法");
            }
        };
        myClass2.test();

        // myInner 并不是 MyInner 的对象,而是 MyInner 的子类对象
        MyInner myInner = new MyInner() {
            @Override
            public void test() {
                System.out.println("重写接口的抽象方法 test");
            }
        };
        myInner.test();
    }
}

/**
 * 抽象类
 * @author rex
 *
 */
abstract class MyClass{
    private String info;

    // 抽象方法
    public abstract void test();

    // 有参,无参构造
    public MyClass(String info) {
        super();
        this.info = info;
    }

    public MyClass() {
        super();
    }

    // get set 方法
    public String getInfo() {
        return info;
    }

    public void setInfo(String info) {
        this.info = info;
    }
}

/**
 * 接口
 * @author rex
 *
 */
interface MyInner {
    void test();
}

匿名内部类也有自己的字节码文件,文件格式为 外部类$数字.class。因为没有名字,所以就是数字表示

Comparator

之前学习了 java.lang 包下的 Comparator。现在 java.utils 下还有一个 Comparator 接口。里面有一个 compare 方法,接收两个对象,如果第一个对象大于第二个,返回一个正整数,相等返回0,否则返回负整数。那么为什么会多有一个 Comparator 接口呢,因为如果要对一个对象的多个特性进行比较排序的化,java.utils.Comparator 结合 Arrays.sort 使用更加方便。

package com.atguigu.code3;

import java.util.Arrays;
import java.util.Comparator;

public class TestComprator {
    public static void main(String[] args) {
        Student[] arr = new Student[3];
        arr[0] = new Student(1, "张三", 88);
        arr[1] = new Student(3, "李四", 99);
        arr[2] = new Student(2, "王五", 77);

        // 方法1: 之前学习的 Arrays.sort 中的排序方式,传入数组,数组元素实现 Comparable 接口中的 compareTo 方法
        Arrays.sort(arr);  

        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }

        System.out.println("-------------------------");

        // 方法2: Arrays.sort 支持传入一个待排序的数组,第二个参数为 Comparator 对象。这里的第二个参数是一个匿名内部类
        Arrays.sort(arr, new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                Student student1 = (Student) o1;
                Student student2 = (Student) o2;
                return student1.getScore() - student2.getScore();
            }
        });  

        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }

//  上面的匿名内部类的写法是对下面这种写法的简写。        
//        class StudentScoreCompare implements Comparator{
//            @Override
//            public int compare(Object o1, Object o2) {
//                Student student1 = (Student) o1;
//                Student student2 = (Student) o2;
//                return student1.getScore() - student2.getScore();
//            }}
//        
//        StudentScoreCompare studentScoreCompare = new StudentScoreCompare();
//        Arrays.sort(arr, studentScoreCompare); 

    }
}


class Student implements Comparable{
    private int id;
    private String name;
    private int score;

    @Override
    public String toString() {
        return "Student [id=" + id + ", name=" + name + ", score=" + score + "]";
    }

    public Student(int id, String name, int score) {
        super();
        this.id = id;
        this.name = name;
        this.score = score;
    }

    public Student() {
        super();
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getScore() {
        return score;
    }

    public void setScore(int score) {
        this.score = score;
    }

    /**
     * 借助 java.lang.Comparable 按照 id 进行自然排序
     */
    @Override
    public int compareTo(Object o) {
        Student student = (Student) o;
        return this.id - student.id;
    }

}

 上一篇
6. 枚举 6. 枚举
枚举列举,罗列。代表一系列的类型,这些类型有一个非常的明显的特征,就是他们的对象是有限的几个。枚举是 JDK 1.5 之后才有的。枚举类型也是类,原来用 class 声明类,现在用 enum 来声明枚举。 说明: 枚举类型的构造器一定是私
2019-10-20
下一篇 
4. Final, Native, Abstract, 接口, Comparable 4. Final, Native, Abstract, 接口, Comparable
Finalfinal: 最终的意思,是一个修饰符。 修饰类:可以修饰类,包括内部类和外部类。 修饰类的时候表示这个类不能被继承,是个太监类,即没有子类。 修饰方法 修饰方法的时候表示该方法可以被子类继承,但是不能被子类重写。 修饰变量
2019-10-13
  目录