且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

java学习笔记--内部类:(参考java核心技术卷1and转载)

更新时间:2022-06-01 06:29:30

做JavaEE即网站的 基本不接触内部类

做安卓的 基本天天接触内部类

内部类是定义在另一个类中的类
可以分为这四类:

  1. 局部内部类
  2. 成员内部类 与外部类有直接联系
  3. 静态内部类 与外部类没有直接联系
  4. 匿名内部类

特点:
1. 内部类可以访问外部类定义所在的作用域的数据,包括私有的数据
2. 内部类可以对同一个包中的其他类隐藏 设计成private同一个包的其他类无法访问,一般都是内部类private 内部类里的数据是public类型
3. 用匿名内部类定义一个回调函数可以节省大量代码

什么是回调函数?
简单意思就是自己定义一个类 交给其他类来调用
比如 A去超市向B买东西,B告诉A 没货 然后A留了一个电话给B说 到了告诉我,一段时间后 A收到电话去取货。

  1. 内部类既可以访问自身的数据域 也可以访问创建它的外部类对象的数据域
    因为内部类的对象有个隐式的引用,指向创建它的外部对象(这个引用是不可见的)
  2. 内部类可以为私有类 只有外部类的方法可以构造,其他常规类只可以具有包内可见或者公有可见性
  3. 内部类的所有静态域都必须是final的,因为每个外部对象都有单独的内部类实例 如果或者域不是final 那么它可能就不唯一了。
  4. 内部类不能有static方法(没强制要求 但不推荐)
  5. 编译器会把内部类翻译成TalkTimeTalk分隔外部类名和内部类名的文件例如:Talk内部的Time类被翻译成类文件TalkTime.class
  6. 内部类可以继承与外部类不一样的继承父类或者接口

局部内部类:
局部内部类 不能用public和private访问修饰符 他的作用域被限定在这个声明这个局部类的块中
局部类有个优势,就是对外部世界完全隐藏
内部类除了可以访问他们的外部类 ,还可以访问局部变量 不过和这些变量事实上都是final的

之前在电脑上的笔记 就直接粘贴了

/*
内部类:一个类定义在另外一个类的内部,那么该类就称作为内部类。

内部类的class文件名: 外部类$内部类.    
好处:便于区分该class文件是属于哪个外部类的。

内部类的类别:

    1. 成员内部类:

            成员内部类的访问方式:

            方式一:在外部类提供一个方法创建内部类的对象进行访问。

            方式2二:在其他类直接创建内部类的对象。  
            格式:外部类.内部类  变量名 = new 外部类().new 内部类();

            注意: 如果是一个静态内部类,那么在其他类创建 的格式:
                  外部类.内部类  变量名 = new 外部类.内部类();


内部类的应用场景: 我们在描述A事物的时候,发现描述的A事物内部还存在
另外一个比较复杂的事物B时候,而且这个比较复杂事物B还需要访问A事物的属性
等数据,那么这时候我们就可以使用内部类描述B事物。

           比如: 人--->心脏

           class 人{

            血

            氧气

            等....

            class 心脏{

            }       

           }

           内部类的好处:内部类可以直接访问外部类的所有成员。



内部类要注意的细节:
1. 如果外部类与内部类存在同名的成员变量时,在内部类中默认情况下是访问
内部类的成员变量。可以通过"外部类.this.成员变量名" 指定访问外部类的成员
2. 私有的成员内部类只能在外部类提供一个方法创建内部类的对象进行访问,
不能在其他类创建对象了。
3. 成员内部类一旦出现了静态的成员,那么该类也必须 使用static修饰。
创建静态类举例:
Outer.Inner zz=new Outer.Inner();



/*
局部内部类: 在一个类 的方法内部定义另外一个类,
那么另外一个类就称作为局部内部类。

局部内部类要注意的细节:
1.如果局部 内部类(可以是匿名内部类)访问了一个局部变量,那么该局部变量必须使用final修饰


*/
局部变量在方法结束后就被回收了 但是方法内创造的类还存在着 
这个时候 如果一个局部内部类访问一个局部变量的时候,那么就让该局部内部类
访问这个局部 变量 的复制品。
所以要对局部变量加 final这样子就不可以被修改的复制品
举例:
class A{

    public void zz(){
        final  int x=10;
        class test{
            public void xx(){
            System.out.println("这个是局部内部类的print方法.."+x);
            }

        }
                    test qq=new test();
            qq.xx();
    }
}
class Demo2{
    public static void main(String[] args){
        A xx=new A();
        xx.zz();
    }
}



abstract class A{
    public abstract void xx();
}
class D{
    public void zz(){
        /*new A(){
            public void xx(){
                System.out.println("11");
            }
        }.xx();*/



        /*A aa=new A(){//多态
//匿名内部类 :匿名内部类只是没有类名,其他的一概成员都是具备的。
// 匿名内部类与Animal是继承 的关系。  目前是创建Animal子类的对象. 
            public void xx(){
                System.out.print("qq");
            }
        };
        aa.xx();
        */
    }
}
记住  匿名类必须是继承或者实现 因为他没有名字 只能借用父亲的
A不是接口吗为什么可以实例化 
其实这个A不是接口因为它是接用了父亲名字的匿名类 
 举一个实现的例子:
interface A{
    public void xx();
}
class Demo2{
    public static void main(String[] args){
        /*new A(){
            public void xx(){
                System.out.print("xxx");
            }
        }.xx();*/
        zz(new A(){
            public void xx(){
                System.out.print("xZZ");
            }
        });
    }
    public static void zz(A zz){
        zz.xx();
    }
} //形参类型是一个接口引用..

匿名内部类总结:
匿名内部类是局部内部类
匿名函数虽然没有名字,但也是可以有构造函数的,它用构造函数块来代替

问:为什么匿名函数没有构造函数?

答:因为匿名类没有名字,而构造函数需要把类名作为方法名才能看成构造函数。  
原来是因为匿名类的构造函数特殊处理机制,一般类(也就是具有显式名字的类)的所  
有构造函数默认都是调用父类的无参构造的,而匿名类因为没有名字,只能由构造代码  
块代替,也就无所谓的有参和无参构造函数了,它在初始化时直接调用了父类的同参数  
构造,然后再调用了自己的构造代码块。

必须要强调注意的是 如果内部类调用了所在方法的参数或者局部变量,那么这些变量都必须为final的,这是为了维持变量的一致性,因为当你传递一个参数给内部类的时候 ,内部类会自动把这个参数拷贝一份到自身的变量,这是我们看不到的,如果我们对该变量进行操作,那么对原参数是不影响的,并不是对原参数进行操作行为,而是对原参数的复制品进行操作

内部类里的变量和外部环境的变量不同步,指向了不同的对象。
因此,编译器才会要求我们给传参的局部变量加上final(内部类用到了外部变量 (不是类中的成员变量)则外部变量要加final),防止这种不同步情况的发生。

具体的看这位大佬的博客
为什么需要final

为什么要拷贝
局部变量是位于方法内部的,因此局部变量是在虚拟机栈上,也就意味着这个变量无法进行共享,匿名内部类也就无法直接访问,因此只能通过值传递的方式,传递到匿名内部类中。
那一定需要拷贝吗?
答:不是的。
成员变量,对应的在虚拟机里是在堆的位置,而且无论在这个类的哪个地方,我们只需要通过 this.dog,就可以获得这个变量。因此,在创建内部类时,无需进行拷贝,甚至都无需将这个成员变量传递给内部类。

意思就是:

匿名内部类来自外部闭包环境的***变量必须是final的,除非***变量来自类的成员变量。

问:*局部内部类在访问他所在方法中的局部变量必须用final修饰,为什么?*
答:因为当调用这个方法时,局部变量如果没有用final修饰,他的生命周期和方法的生命周期是一样的,当方法弹栈,这个局部变量也会消失,那么如果局部内部类对象还没有马上消失想用这个局部变量,就没有了,如果用final修饰会在类加载的时候进入常量池,即使方法弹栈,常量池的常量还在,也可以继续使用

匿名内部类不能在匿名类里面定义接口。
匿名内部类不能定义静态初始化代码块
内部类中不能定义任何静态的东西。

参考:java 内部类为什么不能用静态方法
非static的内部类,在外部类加载的时候,并不会加载它,所以它里面不能有静态
变量或者  静态方法。static类型的属性和方法,在类加载的时候就会存在于内
存中。要使用某个类的static属性或者方法,那么这个类必须要加载到jvm中。 
基于以上两点,可以看出,如果一个非static的内部类如果具有static的属性或
者方法,那么就会出现一种情况:内部类未加载,但是却试图在内存中创建static
的属性和方法,这当然是错误的。原因:类还不存在,但却希望操作它的属性
和方法。

总结原因:因为要访问非静态内部类 你首先得实例化内部类 而静态可以直接通过类名来访问 你说非静态内部类没有类名无法访问 而且 都实例化了 你定义静态有何意义?静态是为了不用不用实例化 直接调用

结论:
1. 匿名内部类是局部内部类
2. 匿名内部类不能有构造函数 (可以用构造块代替) 就只有它没有 其他可以有
3. 匿名内部类不能有静态方法或成员
4. 匿名类可以直接访问成员变量 因为成员变量是在堆里的
5. 匿名类如果在一个函数内访问一个局部变量则需要加final 因为局部变量在栈中 所以jvm自动帮我们传值给匿名类 所以在匿名类中操作的其实是复制品 所以需要final保持一致
6. 内部类可以直接访问 成员变量 因为内部类对象必定会捕获一个指向那个外围类对象的引用 但外围类要访问内部类的成员属性和方法则需要通过内部类实例来访问。
7. 成员内部类可以有构造函数 分为非静态内部类 和静态内部类
8. 成员内部类中不能存在任何static的变量和方法;
9. 成员内部类是依附于外围类的,所以只有先创建了外围类才能够创建内部类。
10. 推荐使用getxxx()来获取成员内部类,尤其是该内部类的构造函数无参数时 。
11. 非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外围内,但是静态内部类却没有。没有这个引用就意味着:

  1、 它的创建是不需要依赖于外围类的。

  2、 它不能使用任何外围类的非static成员变量和方法。

12. 在类中 非静态内部类和匿名内部类都可以访问成员变量 因为成员变量是在堆中 两个内部类内部隐含地保存着指向外部的引用,所以没有拷贝成员变量 可以直接操作。
14. 静态内部类跟静态方法一样,只能访问外部类的静态的成员变量和方法,不能访问非静态的方法和属性,但是普通内部类可以访问任意外部类的成员变量和方法
15. 静态内部类可以声明普通成员变量和方法,而普通内部类不能声明static成员变量和方法。
16. 静态内部类使用场景一般是当外部类需要使用内部类,而内部类无需外部类资源,并且内部类可以单独创建的时候会考虑采用静态内部类的设计
17. 外部类实例不能访问静态类
18. 静态类内部可以定义普通方法 而且要使用该方法得实例化该静态类
19. 静态内部类可以直接访问自己里面的静态成员或者方法

java学习笔记--内部类:(参考java核心技术卷1and转载)

具体的还是看大佬的博客吧 这两篇挺好的
匿名内部类
内部类