java内部类与匿名内部类作用分别是啥?

Posted

tags:

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

参考技术A Java 内部类 \\x0d\\x0a分四种:成员内部类、局部内部类、静态内部类和匿名内部类。 \\x0d\\x0a1、成员内部类: 即作为外部类的一个成员存在,与外部类的属性、方法并列。\\x0d\\x0a注意:成员内部类中不能定义静态变量,但可以访问外部类的所有成员。\\x0d\\x0apublic class Outer\\x0d\\x0aprivate static int i = 1;\\x0d\\x0aprivate int j=10;\\x0d\\x0aprivate int k=20;\\x0d\\x0apublic static void outer_f1()\\x0d\\x0a //do more something\\x0d\\x0a\\x0d\\x0apublic void out_f2()\\x0d\\x0a //do more something\\x0d\\x0a\\x0d\\x0a\\x0d\\x0a//成员内部类\\x0d\\x0aclass Inner\\x0d\\x0a//static int inner_i =100; //内部类中不允许定义静态变量\\x0d\\x0aint j=100;//内部类中外部类的实例变量可以共存\\x0d\\x0aint inner_i=1;\\x0d\\x0avoid inner_f1()\\x0d\\x0a System.out.println(i);//外部类的变量如果和内部类的变量没有同名的,则可以直接用变量名访问外部类的变量\\x0d\\x0a System.out.println(j);//在内部类中访问内部类自己的变量直接用变量名\\x0d\\x0a System.out.println(this.j);//也可以在内部类中用"this.变量名"来访问内部类变量\\x0d\\x0a //访问外部类中与内部类同名的实例变量可用"外部类名.this.变量名"。\\x0d\\x0a System.out.println(k);//外部类的变量如果和内部类的变量没有同名的,则可以直接用变量名访问外部类的变量\\x0d\\x0a outer_f1();\\x0d\\x0a outer_f2();\\x0d\\x0a\\x0d\\x0a\\x0d\\x0a//外部类的非静态方法访问成员内部类\\x0d\\x0apublic void outer_f3()\\x0d\\x0a Inner inner = new Inner();\\x0d\\x0a inner.inner_f1();\\x0d\\x0a\\x0d\\x0a\\x0d\\x0a//外部类的静态方法访问成员内部类,与在外部类外部访问成员内部类一样\\x0d\\x0apublic static void outer_f4()\\x0d\\x0a //step1 建立外部类对象\\x0d\\x0a Outer out = new Outer();\\x0d\\x0a //***step2 根据外部类对象建立内部类对象***\\x0d\\x0a Inner inner=out.new Inner();\\x0d\\x0a //step3 访问内部类的方法\\x0d\\x0a inner.inner_f1();\\x0d\\x0a\\x0d\\x0a\\x0d\\x0apublic static void main(String[] args)\\x0d\\x0a outer_f4();\\x0d\\x0a\\x0d\\x0a\\x0d\\x0a成员内部类的优点:\\x0d\\x0a⑴ 内部类作为外部类的成员,可以访问外部类的私有成员或属性。(即使将外部类声明为PRIVATE,但是对于处于其内部的内部类还是可见的。)\\x0d\\x0a⑵ 用内部类定义在外部类中不可访问的属性。这样就在外部类中实现了比外部类的private还要小的访问权限。\\x0d\\x0a注意:内部类是一个编译时的概念,一旦编译成功,就会成为完全不同的两类。对于一个名为outer的外部类和其内部定义的名为inner的内部类。编译完成后出现outer.class和outer$inner.class两类。\\x0d\\x0a2、局部内部类: 即在方法中定义的内部类,与局部变量类似,在局部内部类前不加修饰符public或private,其范围为定义它的代码块。\\x0d\\x0a注意:局部内部类中不可定义静态变量,可以访问外部类的局部变量(即方法内的变量),但是变量必须是final的。\\x0d\\x0apublic class Outer \\x0d\\x0a private int s = 100;\\x0d\\x0a private int out_i = 1;\\x0d\\x0a public void f(final int k)\\x0d\\x0a final int s = 200;\\x0d\\x0a int i = 1;\\x0d\\x0a final int j = 10;\\x0d\\x0a class Inner //定义在方法内部\\x0d\\x0a int s = 300;//可以定义与外部类同名的变量\\x0d\\x0a //static int m = 20;//不可以定义静态变量\\x0d\\x0a Inner(int k)\\x0d\\x0a inner_f(k);\\x0d\\x0a \\x0d\\x0a int inner_i = 100;\\x0d\\x0a void inner_f(int k)\\x0d\\x0a System.out.println(out_i);//如果内部类没有与外部类同名的变量,在内部类中可以直接访问外部类的实例变量\\x0d\\x0a System.out.println(k);//*****可以访问外部类的局部变量(即方法内的变量),但是变量必须是final的*****\\x0d\\x0a// System.out.println(i);\\x0d\\x0a System.out.println(s);//如果内部类中有与外部类同名的变量,直接用变量名访问的是内部类的变量\\x0d\\x0a System.out.println(this.s);//用"this.变量名" 访问的也是内部类变量\\x0d\\x0a System.out.println(Outer.this.s);//用外部"外部类类名.this.变量名" 访问的是外部类变量\\x0d\\x0a \\x0d\\x0a \\x0d\\x0a new Inner(k);\\x0d\\x0a \\x0d\\x0a\\x0d\\x0a public static void main(String[] args) \\x0d\\x0a //访问局部内部类必须先有外部类对象\\x0d\\x0a Outer out = new Outer();\\x0d\\x0a out.f(3);\\x0d\\x0a \\x0d\\x0a\\x0d\\x0a\\x0d\\x0a\\x0d\\x0a注意:\\x0d\\x0a在类外不可直接生成局部内部类(保证局部内部类对外是不可见的)。要想使用局部内部类时需要生成对象,对象调用方法,在方法中才能调用其局部内部类。通过内部类和接口达到一个强制的弱耦合,用局部内部类来实现接口,并在方法中返回接口类型,使局部内部类不可见,屏蔽实现类的可见性。\\x0d\\x0a\\x0d\\x0a3、静态内部类: 静态内部类定义在类中,任何方法外,用static定义。\\x0d\\x0a注意:静态内部类中可以定义静态或者非静态的成员\\x0d\\x0apublic class Outer \\x0d\\x0a private static int i = 1;\\x0d\\x0a private int j = 10;\\x0d\\x0a public static void outer_f1()\\x0d\\x0a \\x0d\\x0a \\x0d\\x0a public void outer_f2()\\x0d\\x0a \\x0d\\x0a \\x0d\\x0a// 静态内部类可以用public,protected,private修饰\\x0d\\x0a// 静态内部类中可以定义静态或者非静态的成员\\x0d\\x0a static class Inner\\x0d\\x0a static int inner_i = 100;\\x0d\\x0a int inner_j = 200;\\x0d\\x0a static void inner_f1()\\x0d\\x0a System.out.println("Outer.i"+i);//静态内部类只能访问外部类的静态成员\\x0d\\x0a outer_f1();//包括静态变量和静态方法\\x0d\\x0a \\x0d\\x0a void inner_f2()\\x0d\\x0a// System.out.println("Outer.i"+j);//静态内部类不能访问外部类的非静态成员\\x0d\\x0a// outer_f2();//包括非静态变量和非静态方法\\x0d\\x0a \\x0d\\x0a \\x0d\\x0a \\x0d\\x0a \\x0d\\x0a public void outer_f3()\\x0d\\x0a// 外部类访问内部类的静态成员:内部类.静态成员\\x0d\\x0a System.out.println(Inner.inner_i);\\x0d\\x0a Inner.inner_f1();\\x0d\\x0a// 外部类访问内部类的非静态成员:实例化内部类即可\\x0d\\x0a Inner inner = new Inner();\\x0d\\x0a inner.inner_f2();\\x0d\\x0a \\x0d\\x0a \\x0d\\x0a public static void main(String[] args) \\x0d\\x0a new Outer().outer_f3();\\x0d\\x0a \\x0d\\x0a\\x0d\\x0a\\x0d\\x0a\\x0d\\x0a注意:*******生成(new)一个静态内部类不需要外部类成员:这是静态内部类和成员内部类的区别。静态内部类的对象可以直接生成:\\x0d\\x0aOuter.Inner in=new Outer.Inner();\\x0d\\x0a而不需要通过生成外部类对象来生成。这样实际上使静态内部类成为了一个顶级类。静态内部类不可用private来进行定义。*******\\x0d\\x0a\\x0d\\x0a例子:\\x0d\\x0a对于两个类,拥有相同的方法:\\x0d\\x0aclass People\\x0d\\x0a\\x0d\\x0a run();\\x0d\\x0a\\x0d\\x0aclass Machine\\x0d\\x0a run();\\x0d\\x0a\\x0d\\x0a此时有一个robot类:\\x0d\\x0aclass Robot extends People implement Machine.\\x0d\\x0a此时run()不可直接实现。\\x0d\\x0a注意:当类与接口(或者是接口与接口)发生方法命名冲突的时候,此时必须使用内部类来实现。用接口不能完全地实现多继承,用接口配合内部类才能实现真正的多继承。\\x0d\\x0a\\x0d\\x0a4、匿名内部类 \\x0d\\x0a匿名内部类是一种特殊的局部内部类,它是通过匿名类实现接口。\\x0d\\x0aIA被定义为接口。\\x0d\\x0aIA I=new IA();\\x0d\\x0a\\x0d\\x0a匿名内部类的特点:\\x0d\\x0a\\x0d\\x0a1,一个类用于继承其他类或是实现接口,并不需要增加额外的方法,只是对继承方法的事先或是覆盖。\\x0d\\x0a2,只是为了获得一个对象实例,不需要知道其实际类型。\\x0d\\x0a3,类名没有意义,也就是不需要使用到。\\x0d\\x0a\\x0d\\x0apublic class Outer \\x0d\\x0a private static int i = 1;\\x0d\\x0a private int j = 10;\\x0d\\x0a public static void outer_f1()\\x0d\\x0a \\x0d\\x0a \\x0d\\x0a public void outer_f2()\\x0d\\x0a \\x0d\\x0a \\x0d\\x0a// 静态内部类可以用public,protected,private修饰\\x0d\\x0a// 静态内部类中可以定义静态或者非静态的成员\\x0d\\x0a static class Inner\\x0d\\x0a static int inner_i = 100;\\x0d\\x0a int inner_j = 200;\\x0d\\x0a static void inner_f1()\\x0d\\x0a System.out.println("Outer.i"+i);//静态内部类只能访问外部类的静态成员\\x0d\\x0a outer_f1();//包括静态变量和静态方法\\x0d\\x0a \\x0d\\x0a void inner_f2()\\x0d\\x0a// System.out.println("Outer.i"+j);//静态内部类不能访问外部类的非静态成员\\x0d\\x0a// outer_f2();//包括非静态变量和非静态方法\\x0d\\x0a \\x0d\\x0a \\x0d\\x0a \\x0d\\x0a public void outer_f3()\\x0d\\x0a// 外部类访问内部类的静态成员:内部类.静态成员\\x0d\\x0a System.out.println(Inner.inner_i);\\x0d\\x0a Inner.inner_f1();\\x0d\\x0a// 外部类访问内部类的非静态成员:实例化内部类即可\\x0d\\x0a Inner inner = new Inner();\\x0d\\x0a inner.inner_f2();\\x0d\\x0a \\x0d\\x0a \\x0d\\x0a public static void main(String[] args) \\x0d\\x0a new Outer().outer_f3();\\x0d\\x0a \\x0d\\x0a\\x0d\\x0a\\x0d\\x0a\\x0d\\x0a注:一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类,没有类名,根据多态,我们使用其父类名。因他是局部内部类,那么局部内部类的所有限制都对其生效。匿名内部类是唯一一种无构造方法类。大部分匿名内部类是用于接口回调用的。匿名内部类在编译的时候由系统自动起名Out$1.class。如果一个对象编译时的类型是接口,那么其运行的类型为实现这个接口的类。因匿名内部类无构造方法,所以其使用范围非常的有限。当需要多个对象时使用局部内部类,因此局部内部类的应用相对比较多。匿名内部类中不能定义构造方法。如果一个对象编译时的类型是接口,那么其运行的类型为实现这个接口的类。\\x0d\\x0a\\x0d\\x0a________________________________________________________________________________\\x0d\\x0a\\x0d\\x0a内部类总结:\\x0d\\x0a1.首先,把内部类作为外部类的一个特殊的成员来看待,因此它有类成员的封闭等级:private ,protected,默认(friendly),public\\x0d\\x0a 它有类成员的修饰符: static,final,abstract\\x0d\\x0a2.非静态内部类nested inner class,内部类隐含有一个外部类的指针this,因此,它可以访问外部类的一切资源(当然包括private)\\x0d\\x0a 外部类访问内部类的成员,先要取得内部类的对象,并且取决于内部类成员的封装等级。\\x0d\\x0a 非静态内部类不能包含任何static成员.\\x0d\\x0a3.静态内部类:static inner class,不再包含外部类的this指针,并且在外部类装载时初始化.\\x0d\\x0a 静态内部类能包含static或非static成员.\\x0d\\x0a 静态内部类只能访问外部类static成员.\\x0d\\x0a 外部类访问静态内部类的成员,循一般类法规。对于static成员,用类名.成员即可访问,对于非static成员,只能\\x0d\\x0a 用对象.成员进行访问\\x0d\\x0a\\x0d\\x0a4.对于方法中的内部类或块中内部类只能访问块中或方法中的final变量。\\x0d\\x0a\\x0d\\x0a类成员有两种static , non-static,同样内部类也有这两种\\x0d\\x0anon-static 内部类的实例,必须在外部类的方法中创建或通过外部类的实例来创建(OuterClassInstanceName.new innerClassName(ConstructorParameter)),并且可直接访问外部类的信息,外部类对象可通过OuterClassName.this来引用\\x0d\\x0astatic 内部类的实例, 直接创建即可,没有对外部类实例的引用。\\x0d\\x0a内部类不管static还是non-static都有对外部类的引用\\x0d\\x0anon-static 内部类不允许有static成员\\x0d\\x0a\\x0d\\x0a方法中的内部类只允许访问方法中的final局部变量和方法的final参数列表,所以说方法中的内部类和内部类没什麽区别。但方法中的内部类不能在方法以外访问,方法中不可以有static内部类\\x0d\\x0a匿名内部类如果继承自接口,必须实现指定接口的方法,且无参数 \\x0d\\x0a匿名内部类如果继承自类,参数必须按父类的构造函数的参数传递

内部类与匿名内部类

内部类不是很好理解,但说白了其实也就是一个类中还包含着另外一个类

如同一个人是由大脑、肢体、器官等身体结果组成,而内部类相当于其中的某个器官之一,例如心脏:它也有自己的属性和行为(血液、跳动)

显然,此处不能单方面用属性或者方法表示一个心脏,而需要一个类

而心脏又在人体当中,正如同是内部类在外部内当中

 

实例1:内部类的基本结构

//外部类
class Out {
    private int age = 12;
     
    //内部类
    class In {
        public void print() {
            System.out.println(age);
        }
    }
}
 
public class Demo {
    public static void main(String[] args) {
        Out.In in = new Out().new In();
        in.print();
        //或者采用下种方式访问
        /*
        Out out = new Out();
        Out.In in = out.new In();
        in.print();
        */
    }
}
运行结果:12

从上面的例子不难看出,内部类其实严重破坏了良好的代码结构,但为什么还要使用内部类呢?

因为内部类可以随意使用外部类的成员变量(包括私有)而不用生成外部类的对象,这也是内部类的唯一优点

如同心脏可以直接访问身体的血液,而不是通过医生来抽血

 

程序编译过后会产生两个.class文件,分别是Out.class和Out$In.class

其中$代表了上面程序中Out.In中的那个 .

Out.In in = new Out().new In()可以用来生成内部类的对象,这种方法存在两个小知识点需要注意

  1.开头的Out是为了标明需要生成的内部类对象在哪个外部类当中

  2.必须先有外部类的对象才能生成内部类的对象,因为内部类的作用就是为了访问外部类中的成员变量

 

实例2:内部类中的变量访问形式

class Out {
    private int age = 12;
     
    class In {
        private int age = 13;
        public void print() {
            int age = 14;
            System.out.println("局部变量:" + age);
            System.out.println("内部类变量:" + this.age);
            System.out.println("外部类变量:" + Out.this.age);
        }
    }
}
 
public class Demo {
    public static void main(String[] args) {
        Out.In in = new Out().new In();
        in.print();
    }
}
运行结果:

局部变量:14
内部类变量:13
外部类变量:12

从实例1中可以发现,内部类在没有同名成员变量和局部变量的情况下,内部类会直接访问外部类的成员变量,而无需指定Out.this.属性名

否则,内部类中的局部变量会覆盖外部类的成员变量

而访问内部类本身的成员变量可用this.属性名,访问外部类的成员变量需要使用Out.this.属性名

 

实例3:静态内部类

class Out {
    private static int age = 12;
     
    static class In {
        public void print() {
            System.out.println(age);
        }
    }
}
 
public class Demo {
    public static void main(String[] args) {
        Out.In in = new Out.In();
        in.print();
    }
}
运行结果:12

可以看到,如果用static 将内部内静态化,那么内部类就只能访问外部类的静态成员变量,具有局限性

其次,因为内部类被静态化,因此Out.In可以当做一个整体看,可以直接new 出内部类的对象(通过类名访问static,生不生成外部类对象都没关系)

 

实例4:私有内部类

class Out {
    private int age = 12;
     
    private class In {
        public void print() {
            System.out.println(age);
        }
    }
    public void outPrint() {
        new In().print();
    }
}
 
public class Demo {
    public static void main(String[] args) {
        //此方法无效
        /*
        Out.In in = new Out().new In();
        in.print();
        */
        Out out = new Out();
        out.outPrint();
    }
}
运行结果:12

如果一个内部类只希望被外部类中的方法操作,那么可以使用private声明内部类

上面的代码中,我们必须在Out类里面生成In类的对象进行操作,而无法再使用Out.In in = new Out().new In() 生成内部类的对象

也就是说,此时的内部类只有外部类可控制

如同是,我的心脏只能由我的身体控制,其他人无法直接访问它

 

实例5:方法内部类

class Out {
    private int age = 12;
 
    public void Print(final int x) {
        class In {
            public void inPrint() {
                System.out.println(x);
                System.out.println(age);
            }
        }
        new In().inPrint();
    }
}
 
public class Demo {
    public static void main(String[] args) {
        Out out = new Out();
        out.Print(3);
    }
}
运行结果:

3
12

在上面的代码中,我们将内部类移到了外部类的方法中,然后在外部类的方法中再生成一个内部类对象去调用内部类方法

如果此时我们需要往外部类的方法中传入参数,那么外部类的方法形参必须使用final定义

至于final在这里并没有特殊含义,只是一种表示形式而已
匿名内部类也就是没有名字的内部类

正因为没有名字,所以匿名内部类只能使用一次,它通常用来简化代码编写

但使用匿名内部类还有个前提条件:必须继承一个父类或实现一个接口

 

实例1:不使用匿名内部类来实现抽象方法

abstract class Person {
    public abstract void eat();
}
 
class Child extends Person {
    public void eat() {
        System.out.println("eat something");
    }
}
 
public class Demo {
    public static void main(String[] args) {
        Person p = new Child();
        p.eat();
    }
}
运行结果:eat something

可以看到,我们用Child继承了Person类,然后实现了Child的一个实例,将其向上转型为Person类的引用

但是,如果此处的Child类只使用一次,那么将其编写为独立的一个类岂不是很麻烦?

这个时候就引入了匿名内部类

 

实例2:匿名内部类的基本实现

abstract class Person {
    public abstract void eat();
}
 
public class Demo {
    public static void main(String[] args) {
        Person p = new Person() {
            public void eat() {
                System.out.println("eat something");
            }
        };
        p.eat();
    }
}
运行结果:eat something

可以看到,我们直接将抽象类Person中的方法在大括号中实现了

这样便可以省略一个类的书写

并且,匿名内部类还能用于接口上

 

实例3:在接口上使用匿名内部类

interface Person {
    public void eat();
}
 
public class Demo {
    public static void main(String[] args) {
        Person p = new Person() {
            public void eat() {
                System.out.println("eat something");
            }
        };
        p.eat();
    }
}
运行结果:eat something

 

由上面的例子可以看出,只要一个类是抽象的或是一个接口,那么其子类中的方法都可以使用匿名内部类来实现

最常用的情况就是在多线程的实现上,因为要实现多线程必须继承Thread类或是继承Runnable接口

 

实例4:Thread类的匿名内部类实现

public class Demo {
    public static void main(String[] args) {
        Thread t = new Thread() {
            public void run() {
                for (int i = 1; i <= 5; i++) {
                    System.out.print(i + " ");
                }
            }
        };
        t.start();
    }
}
运行结果:1 2 3 4 5

 

实例5:Runnable接口的匿名内部类实现

public class Demo {
    public static void main(String[] args) {
        Runnable r = new Runnable() {
            public void run() {
                for (int i = 1; i <= 5; i++) {
                    System.out.print(i + " ");
                }
            }
        };
        Thread t = new Thread(r);
        t.start();
    }
}

 

以上是关于java内部类与匿名内部类作用分别是啥?的主要内容,如果未能解决你的问题,请参考以下文章

“全栈2019”Java第一百零四章:匿名内部类与外部成员互访详解

Java中的内部类与匿名内部类详解

“全栈2019”Java第一百零五章:匿名内部类覆盖作用域成员详解

“全栈2019”Java第一百零六章:匿名内部类与抽象类接口注意事项

“全栈2019”Java第一百零七章:匿名内部类与构造方法注意事项

内部类与匿名内部类