java中的内部类

Posted 伊万夫斯基

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java中的内部类相关的知识,希望对你有一定的参考价值。

概述

将一个类定义在另一个类的里面,里面的那个类就称为内部类(内置类,嵌套类)

分析事物A时,发现该事物A描述中还有事物B,而且这个事物B还在访问被描述事物A的内容,将事物B定义成内部类来描述。

特点

  1. 内部类可以直接访问外部类中的成员;
  2. 外部类要访问内部类,必须建立内部类的对象;

举例:

package innerdemo;

public class InnerDemo1 {
    public static void main(String[] args) {
        Outer.Inner in = new Outer().new Inner(); // 直接访问内部类的方法
        in.show();

        Outer o = new Outer();//
        o.method();
    }
}
class Outer{

    private int num = 3;
    // 内部类
    class Inner{
        void show(){
            num = 100;
            System.out.println("内部类run..."+num);
        }
    }

    public void method(){
        Inner in = new Inner(); //外部类要访问内部类,必须建立内部类的对象;
        in.show();
    }
}

修饰符

  1. 直接访问外部类中的内部类中的成员:(不多见)

    外部类名.内部类名 变量名 = new 外部类名(). new 内部类名();

    Outer.Inner in = new Outer().new Inner();

    in.show();

  2. 内部类是静态的类,相当于一个外部类。外部类只要一加载,内部类就存在了,所以直接new一个内部类对象出来;

    Outer.Inner in1 = new Outer.Inner();

    public class InnerClassDemo {
        public static void main(String[] args) {
            // 内部类是静态的。相当于一个外部类
            Outer.Inner in1 = new Outer.Inner();
            in1.show();
        }
    }
    class Outer{
        private static int num = 3;
        // 内部类
        static class Inner{
            void show(){
                System.out.println("内部类run..."+num);
            }
        }
    }
  3. 如果内部类是静态的,成员是静态的,就不需要对象了。如果内部类中定义了静态成员,该内部类也必须是静态的。

    package java_demo_2;
    
    public class InnerClassDemo {
        public static void main(String[] args) {
            Outer.Inner.function();
        }
    }
    class Outer{
        private static int num = 3;
        // 内部类
        static class Inner{
            void show(){
                System.out.println("内部类run..."+num);
            }
            public static void function(){
                System.out.println("内部类是静态的,成员也是静态的,不需要创建对象"+num);
            }
        }
    }

细节

package java_demo_2;

public class InnerClassDemo {
    public static void main(String[] args) {
        new Outer().method();
    }

class Outer{
    int num = 3;
    // 内部类
    class Inner{
        int num = 4;
        void show(){
            int num = 5;
            System.out.println(num); // num 5
            System.out.println(this.num); // 4 局部和成员变量重名
            System.out.println(Outer.this.num); // 3
        }
    }
    void method(){
        new Inner().show();
    }
}

为什么内部类能直接访问外部类中成员呢?

那是因为内部类持有了外部类的引用。外部类名.this

局部内部类

内部类可以存放在局部位置上,可以放在方法中,放在代码块中。

class Outer1{

    int num = 3;

    void method(){
        // 内部类在方法中

        class Inner{
            void show(){
                System.out.println("方法中的内部类:"+num);
            }
        }
        // 创建对象
        Inner in = new Inner();
        in.show();
    }

    {
        // 内部类在代码块中
        class Inner2{
            void show(){
                System.out.println("代码块中的内部类:" + num);
            }
        }
        Inner2 in2 = new Inner2();
        in2.show();
    }
}

内部类在局部位置上只能访问局部中被final修饰的局部变量;

class Outer{
    int num = 3;
    void method(){

        final int x  = 9; // 这个好像都行 int x = 9 不报错;
        class Inner{
            void show(){
                System.out.println("show..."+x); // 内部类在局部位置上只能访问局部中被final修饰的局部变量;
            }
        }
        // 创建对象
        Inner in = new Inner();
        in.show();
    }
}

匿名内部类

概述

内部类的简写格式。要简写,必须和外部类有关系。必须有前提:内部类必须继承或者实现一个外部类或者接口。

格式: new 父类or接口(){子类内容};

匿名内部类,其实就是一个匿名子类对象;这个对象比较胖。

按道理要先继承父类,重构方法,再创建对象调用方法如:

class Inner extends InnerDemo{
    void show() {
        System.out.println("内部类run..." + num);
    }
}
public void method() {
   new Inner().show();
}

现在直接在方法里,进行new 父类(){重构方法;}

new InnerDemo(){ // 匿名内部类,new 他爹(){把方法重写;}
    void show(){
        System.out.println("show..."+num);
    }
}

具体代码如下:

public class InnerClassDemo {
    public static void main(String[] args) {
        new Outer().method();
    }
}

// 抽象类
abstract class InnerDemo{
    abstract void show();
}
class Outer {

    private int num = 3;

    // 内部类
//    class Inner {
//        void show() {
//            System.out.println("内部类run..." + num);
//        }
//
//    }

    public void method() {
//        new Inner().show();
        new InnerDemo(){ // 匿名内部类,new 他爹(){把方法重写;}
            void show(){
                System.out.println("show..."+num);
            }
        }.show();
    }
}

应用

注释部分:普通内部类继承外部接口时代码:

interface Inter{
    void show1();
    void show2();
}
class Outer {
    private int num = 3;

    // 内部类
    /*
    class Inner implements Inter {
        public void show1(){

        }
        public void show2(){

        }
    }
    */
    public void method(){

//        Inner in = new Inner();
//        in.show1();
//        in.show2();
        Inter in = new Inter(){
            public void show1(){

            }
            public void show2(){

            }

        };
        in.show1();
        in.show2();
    }
}

通常的使用场景之一:当函数参数是接口类型时,而且接口中的方法不超过三个,可以用匿名内部类作为实际参数进行传递。

// 正常写
public class InnerClassDemo {
    public static void main(String[] args) {
    
    show(new InterImpl());
}
    public static void show(Inter in){
        in.show1();
        in.show2();
    }
}
interface Inter{
    void show1();
    void show2();
}

class InterImpl implements Inter{
    public void show1(){}
    public void show2(){}
}

// 简写,将匿名内作为实际参数传递
public class InnerClassDemo {
    public static void main(String[] args) {
    
    show(new Inter(){
        public void show1(){}
        public void show2(){}
    });
}   
    public static void show(Inter in){
        in.show1();
        in.show2();
    }
}
//接口
interface Inter{
    void show1();
    void show2();
}

分类

  • 成员内部类(静态内部类和非静态内部类)
  • 局部内部类
  • 匿名内部类

成员内部类

  • 静态内部类:直接通过外部类调用静态内部类的构造器
  • 非静态内部类:先创建外部类的对象,再通过外部类的对象调用内部类的构造器或者
    外部类名.内部类名 对象名 = new 外部类型().new 内部类型();
package innerdemo;

/**
 * @ClassName: InnerClassDemo
 * @author: benjamin
 * @version: 1.0
 * @description: TODO
 * @createTime: 2019/06/11/10:11
 */

public class InnerClassDemo {
    public static void main(String[] args) {
        // 创建静态内部类的对象
        Person.Dog d = new Person.Dog();// 直接通过外部类调用静态内部类的构造器
        //创建非静态内部类的对象
//        Person.Bird b = new Person().new Bird();
        Person p = new Person();
        Person.Bird b = p.new Bird();// 通过外部类的对象调用内部类的构造器
        b.info();

    }
}

class Person{

    String name;
    int age;

    // 非静态成员内部类
    class Bird{
        String name;
        int id;
        public Bird(){

        }

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

    // 静态成员内部类
    static class Dog{
        String name;
        int id;
        public Dog(){

        }

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

        }
    }


    // 外部类的成员方法
    public void show(){
        System.out.println("我是外部类的show()方法");
    }
}

局部内部类:常常使用一个方法,使其返回值为某个类或接口的对象,而这个类或接口在方法内部创建。举例:

匿名内部类:

举例:

class OuterJu{

    // 局部内部类的使用
    public Comparable getComparable(){
        //1.创建一个实现Comparable接口的类:局部内部类
        class MyComparable implements Comparable{
            @Override
            public int compareTo(Object o) {
                return 0;
            }
        }
        // 2. 返回一个实现类的对象
        return new MyComparable();
    }

    // 匿名内部类的使用
    public Comparable getComparable1(){
        //返回一个实现Comparable接口的匿名内部类的对象
        return new Comparable() {
            @Override
            public int compareTo(Object o) {
                return 0;
            }
        };
    }
}

细节

细节1

主函数会报错,因为无法访问非静态类,而且由于是静态不能采用this.new Inner()

方法:需要将Inner定义成静态类;

package java_demo_2;

/**
 * @ClassName: InnerClassDemo5
 * @author: benjamin
 * @version: 1.0
 * @description: TODO
 * @createTime: 2019/04/13/22:28
 */

public class InnerClassDemo5 {
    class Inner{// 如果不定义成静态,主函数会报错;

    }

    public static void main(String[] args) {

        new Inner(); //无法从静态上下文中引用非静态 变量 this
        // 因为主函数是静态的;
    }

    public void method(){
        
        new Inner(); // 正确
    }

    public static void show(Inter in){
        in.show1();
        in.show2();
    }
}

interface Inter{
    void show1();
    void show2();
}

细节2:

// 正确的
package java_demo_2;

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

        new Outer6().method();
    }

}

class Outer6{

    // 创建的匿名内部类相当于
//    class Inner6 extends Object{
//        public void show(){
//            System.out.println("show run");
//        }
//    }
//    new Inner6.show();
    void method(){

        new Object(){
            public void show(){
                System.out.println("show run");
            }
        }.show();
    }
}

编译看左边

package java_demo_2;

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

        new Outer6().method();
    }
}

class Outer6{

    void method(){

         Object obj = new Object(){
            public void show(){
                System.out.println("show run");
            }
         };
         obj.show();
//         Error:(35, 13) java: 找不到符号
        // 因为匿名内部类这个子类对象被向上转型为Object类型,
        // 这样就不再使用子类特有的方法了;
    }
}

以上是关于java中的内部类的主要内容,如果未能解决你的问题,请参考以下文章

java中的内部类

java中的内部类

Java中的四种内部类总结

java内部类的匿名内部类

浅谈Java中的内部类

并发包java.util.concurrent.locks.Lock