内部类

本文详细介绍了Java中的内部类,包括成员内部类、局部内部类、静态内部类和匿名内部类。内容涵盖它们的声明、特点、使用场景以及与外部类的交互方式。特别强调了匿名内部类在简洁性和灵活性方面的优势,以及如何声明和使用匿名内部类的对象。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、类中的结构:成员变量、成员方法、构造器、代码块、内部类
2、内部类的概念?
从名字上就能看出它的意思:声明在另一个类里面的类,称为内部类。

class Outer{//外部类
    class Inner{//内部类
    }

    public void method(){
        class InClass{ //内部类
        }
    }
}

3、根据声明的位置不同:
(1)成员内部类:在外部类中,但是在方法和代码块的外面,和外部类的成员变量等是并列关系。
A:静态成员内部类,有static修饰,简称静态内部类
B:非静态成员内部类,没有static修饰,简称成员内部类,也就是说很多时候说的成员内部类都是指非静态成员内部类。
(2)局部内部类:在外部类的方法体或代码块中,和局部变量是并列关系。
A:有给局部内部类取名字:有名字局部内部类
B:没有给局部内部类取名字:匿名局部内部类,简称匿名内部类

匿名对象:是一个对象没有赋值给一个变量。
匿名内部类,是类没有名字。

public class TestInner {
    static class One{

    }
    class Two{

    }

    public static void main(String[] args) {
        System.out.println("-------------匿名对象和有名对象-----------------");
        new TestInner();//匿名对象

        TestInner t1 = new TestInner();//有名字的对象

        System.out.println("----------------------");
        new TestInner().method();//匿名对象直接调用method方法
        TestInner t2 = new TestInner();
        t2.method();//有名字的对象,通过变量/对象名调用method方法

        System.out.println("----------------------");
        test(new TestInner());//匿名对象作为调用test方法的实参
        TestInner t3 = new TestInner();
        test(t3);//使用有名字的对象作为调用test方法的实参

        System.out.println("------------匿名内部类和有名的局部内部类------------------");
        class Three{//有名字局部内部类,它在main中声明的局部内部类,名字叫做Three
            public void fun(){
                System.out.println("内部类Inner的fun方法");
            }
        }

        new Object(){
          public void fun(){
              System.out.println("匿名内部类的fun方法");
          }
        }; //这是一个匿名内部类,正因为它没有名字,直接在声明类的同时,把对象也创建了
            //这里有个;,因为这个在声明类的同时,new对象了,是一个表达式了,单独的表达式不能存在,必须加;变成语句
    }

    public void method(){
        System.out.println("method方法");
    }

    public static void test(TestInner t){
        //....
    }
}

4、匿名内部类
(1)为什么需要匿名内部类?
当某些时候我们需要编写一个“父类的子类”或“接口的实现类”时,这个类的代码比较简单,并且这个类只在这里用一次。
这样的时候,我们可以考虑使用匿名内部类,来减少.java的数量,并且还可以增强代码的内聚力。

为什么不用有名的局部内部类?因为如果这个类只用一次,还给它取个名字,还麻烦了,命名是一件挺难的事情。

(2)如何声明匿名内部类?
形式一:
new 父类(){
    //匿名子类的成员列表
}

形式二:
new 父类(实参列表){
    //匿名子类的成员列表
}

形式三
new 父接口(){
    //匿名实现类的成员列表
}

说明:
A:形式一这种格式声明的匿名内部类,它是这个父类的子类,它指明了它的直接父类是谁。
        并且表名子类的构造器首行用了super()调用了父类的无参构造
B:形式二这种格式声明的匿名内部类,它是这个父类的子类,它指明了它的直接父类是谁。
        并且表名子类的构造器首行用了super(实参列表)调用了父类的有参构造
C:形式三这种格式声明的匿名内部类,它是这个接口的实现类,它指明了它实现了xx接口,它的直接父类是Object。
       并且表名子类的构造器首行用了super()调用了父类Object的无参构造

(3)如何在匿名内部类中声明成员?
  和其他类差不多,区别在于以下几点:
  A:匿名内部类不能手动编写构造器。
    因为类没有名字,写不了。只能使用默认的无参构造。
    虽然new后面不是写匿名内部类的构造器名字,因为写不了,所以只能写父类或父接口的名字。
    因为无法手动编写构造器,所以只能在父类或父接口名的后面通过()或(实参列表)的形式来表名子类构造器中用了父类的哪个构造器。
 B:匿名内部类中不能声明静态成员:静态变量、静态方法、静态代码块等。
 C:匿名内部类中倒是可以声明静态的常量 (一般也不会这么做)

 在匿名内部类中,可以声明自己扩展的方法,或者重写父类或者实现父接口的方法。更多的时候都是重写父类或父接口的方法。

 (4)如何使用匿名内部类中的成员?
 A:调用匿名内部类扩展的自己的方法:
   只能通过匿名内部子类的匿名对象直接调用扩展的方法。
B:可以把匿名内部类的对象,赋值给他的父类或父接口类型的变量,
构成多态引用,然后通过 父类或父接口类型的变量调用 匿名子类重写的方法。

public class TestAnonymousInner {
    public static void main(String[] args) {
        new Object(){

        }; //这个匿名内部类的直接父类是Object
            //这个匿名子类的构造器首行,相当于是用了super()调用了父类Object的无参构造

        new Father(){

        };//这个匿名内部子类的直接父类是Father
            //这个匿名子类的构造器首行,相当于是用了super()调用了父类的无参构造

        new Father(1){

        };//这个匿名内部类的直接父类是Father
         //这个匿名子类的构造器首行,相当于是用了super(实参列表)调用了父类的有参构造

        new MyInter(){

        };//这个匿名内部类的直接父类是Object,它实现类MyInter接口。
         //这个匿名子类的构造器首行,相当于是用了super()调用了父类Object的无参构造
    }
}

abstract class Father{
    private int a;

    public Father() {
    }

    public Father(int a) {
        this.a = a;
    }
}

interface MyInter{

}

5、有名字的局部内部类(开发中使用最少的)
(1)局部内部类的使用仅限于声明它的方法中使用,甚至在类声明前面都不能用。
(2)局部内部类中可以使用外部类的成员,包括私有的。
如果局部内部类在静态方法中,那么不能直接使用外部类的非静态成员。
(3)局部内部类可以访问所在方法的使用final声明的局部变量。
在JDK1.8之前,必须手动加final;
在JDK1.8之后,如果某个局部变量在局部内部类中使用了,自动加final。

面试题:
(1)局部内部类的对象是否能够在声明它的方法以外的地方使用?
可以
(2)怎么用
这个局部内部类的对象,必须通过方法返回,或者通过外部类的成员变量传递一下。

public class TestHasNameLocalInner {
    private static int a = 1;
    private int b = 1;
    private static Object four;

    public static void main(String[] args) {
        Object obj = test();
        System.out.println(obj.getClass());//这里使用Object类型表示,那么返回的对象只能调用Object里面的方法

        fun();
        System.out.println(four.getClass());//这里使用Object类型表示,那么返回的对象只能调用Object里面的方法

        Father fang = fang();
        fang.ff();
    }

    public static Object test(){
        class Three {
            //...
        }
        return new Three();
    }

    public static void fun(){
        class Four{
            //...
        }

        four = new Four();//用成员变量,存储Four的对象
    }

    public static Father fang(){
        class Three extends Father {
            //...
        }
        return new Three();
    }


    /*
    在method1方法中,声明了一个有名字的局部内部类,内部类中声明了一个fun方法
     */
    public static void method1(){
//        One one;//这里在One声明的前面,不能使用
        int num = 3;

        class One{
            public void fun(){
                System.out.println("有名字的局部内部类1");
                System.out.println("a = " + a);//直接使用外部类的成员,包括私有的
               // System.out.println("b = " + b);//这里报错的原因,静态方法method1不能直接访问非静态的b,这个和内部类无关
                System.out.println("num="+num);//直接访问外部类方法的局部变量
            }
        }

        One one = new One();
        one.fun();
    }

    public void method2(){
        class Two{
            public void fun(){
                System.out.println("有名字的局部内部类1");
                System.out.println("a = " + a);//直接使用外部类的成员,包括私有的
                System.out.println("b = " + b);//直接使用外部类的成员,包括私有的
            }
        }
    }

}

class Father{
    public void ff(){
        System.out.println("父类的方法");
    }
}

6、静态内部类
(1)如何什么声明?
【修饰符】 class 外部类静态内部类{
    【其他修饰符】 static class 静态内部类{
    }
}

(2)作为类来说
A:它有自己的字节码文件
其实所有的类包括匿名内部类,和局部内部类都有自己的字节码文件。

对比一下几种内部类的字节码文件?
静态内部类:外部类名$静态内部类.class
有名字的局部内部类:外部类名$编号局部内部类.class        因为在不同的方法中,局部内部类可能重名,所以有编号+局部内部类名
匿名的局部内部类:外部类名$编号.class                  因为匿名内部类没有名字,所以用编号代替。

B:成员
只有静态内部类可以声明静态成员,其他的内部类都不能声明静态成员,除非是静态常量。
除了匿名内部类不能手动编写自己的构造器以外,其他内部类都可以编写自己的构造器。
其他的成员,和之前讲的类的成员一样编写即可。

C:可以继承自己的父类,也可以实现自己的接口

(3)作为成员来说
静态内部类可以使用外部类的成员,包括私有的,但是不能使用外部类的非静态成员。
反过来,外部类也可以直接使用静态内部类的成员,包括私有的。

(4)静态内部类如果在外部类的外面是可见的话,也可以在外部类的外面使用
    如果使用静态内部类的静态成员:外部类名.静态内部类名.静态成员
    如果使用静态内部类的非静态成员:
        外部类名.静态内部类名 变量 = new 外部类名.静态内部类名();
        变量.非静态成员形式使用

    如果使用某个静态内部类多次,那么可以简化它的形式,
    内部类的完整名字是包.外部类.静态内部类
    现在可以使用import 语句 + 静态内部类的简名称

public class TestStaticInner {
    public static void main(String[] args) {
        //在这里调用Inner的method()方法
        //创建静态内部类的对象,再调用它的非静态方法
        Outer.Inner in = new Outer.Inner();
        in.method();

        //在这里调用Inner的test()方法
        Outer.Inner.test();
        //Inner是作为Outer的静态成员存在的
        //test方法是作为Inner的静态成员存在的

        System.out.println("----------------");
        //注意一下写法是基于上面有import语句
        Inner in2 = new Inner();
        in.method();

        Inner.test();
    }
}

class Outer{
    private static int a = 1;
    private int b = 1;
    static class Inner{
        private static int c = 2;
        private int d = 2;
        public void method(){
            System.out.println("静态内部类的非静态方法");
            System.out.println("a = "  +a);
//            System.out.println("b = "  +b);//因为Inner是静态的,不能直接使用外部类的非静态成员
        }

        public static void test(){
            System.out.println("静态内部类的静态方法");
        }
    }

    public static void wai(){
        System.out.println(Inner.c);//外部类也可以访问静态内部类的私有的成员
        Inner in = new Inner();
        System.out.println(in.d);//外部类也可以访问静态内部类的私有的成员
    }
}

7、非静态的内部类
(1)如何什么声明?
【修饰符】 class 外部类静态内部类{
    【其他修饰符】 class 非静态内部类{
    }
}

(2)作为类来说
A:它有自己的字节码文件
对比一下几种内部类的字节码文件?
静态内部类:外部类名$静态内部类.class
非静态内部类:外部类名$非静态内部类.class
有名字的局部内部类:外部类名$编号局部内部类.class        因为在不同的方法中,局部内部类可能重名,所以有编号+局部内部类名
匿名的局部内部类:外部类名$编号.class                  因为匿名内部类没有名字,所以用编号代替。

B:成员
只有静态内部类可以声明静态成员,其他的内部类都不能声明静态成员,除非是静态常量。
除了匿名内部类不能手动编写自己的构造器以外,其他内部类都可以编写自己的构造器。
其他的成员,和之前讲的类的成员一样编写即可。

C:可以继承自己的父类,也可以实现自己的接口

(3)作为成员来说
非静态内部类可以使用外部类的成员,包括私有的。
反过来,外部类也可以直接使用非静态内部类的成员,包括私有的。

(4)非静态内部类如果在外部类的外面是可见的话,也可以在外部类的外面使用
    使用非静态内部类的非静态成员

public class TestNonStaticInner {
    public static void main(String[] args) {
        //如何使用Inner的method方法
        //(1)必须先创建外部类的对象
        Outer out = new Outer();
        //(2)借助外部类的对象,创建非静态内部类的对象
       // Outer.Inner in = out.new Inner(); //这段代码的目的是为了得到非静态内部类的对象

        Outer.Inner in = out.getInner();//可以在外部类中提供一个方法,返回非静态内部类的对象,我们接收它

        //(3)调用方法
        in.method();
    }
}

class Outer{
    private static int a;
    private int b;

    class Inner{
        private int c;
        public void method(){
            System.out.println("a = " + a);
            System.out.println("b = " + b);
        }
    }

    public void test(){
        Inner in = new Inner();
        System.out.println(in.c);//外部类可以直接使用内部类的私有的成员
    }

    public Inner getInner(){
        return new Inner();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值