zoukankan      html  css  js  c++  java
  • Java 内部类详解

    什么

    定义在一个类内部的类,称为内部类(累不累),如下:

    public class A {
    	private int c = 1;
            
    	public class C {
    		public void test() {
    			System.out.println("c:" + c);
    		}
    	}
    }
    

    C称为A的内部类,简称内部类

    A称为C的外部类,简称外部类

    而且内部类能访问外部类的成员(静态成员、实例成员),当然有一些限制,限制如下

    4种声明方式

    按照内部类的声明方式,分为4种内部类:

    1. 静态内部类

      像类的静态成员一样声明的类,就称呼为“静态内部类”

       public class A {
       	private static String b = "b";
               private int c = 1;
      
               // B是A的静态内部类
       	public static class B {
       		public void test() {
       			System.out.println(b);
       		}
       	}
       }  
      

      静态内部类,只能访问外部类的静态成员(方法和变量),并且可以像类的成员一样使用修饰符(public/protected/private);

      创建静态内部类对象的方式:A.B b = new A.B();

    2. 成员内部类

      新类的实例成员(未加static修饰)声明的类,称为“成员内部类”

       public class A {
       	private static String b = "b";
               private int c = 1;
      
               // C是A的成员内部类
       	public class C {
       		public void test() {
       			System.out.println(c);
                               System.out.println(b);
       		}
       	}
       }
      

      成员内部类,访问外部类的一切(静态,还是实例),就像成员方法一样,并且可以像类的成员一样使用修饰符(public/protected/private)

      创建成员内部类对象的方式:

       A a = new A();
       A.C c = a.new C();
      
    3. 方法内部类

      在一个代码块声明的类称为方法内部类,代码块包括(方法内、静态代码块内、实例代码块内)

       public class A {
       	private static String b;
       	private int c;
      
               // 成员方法
       	public void test() {
       		final int d = 1;
       		// 方法内部类
       		class D {
       			public void test() {
       				// 访问静态变量
       				System.out.println(b);
       				// 访问实例变量
       				System.out.println(c);
       				// 访问方法final类型的局部变量
       				System.out.println(d);
       			}
       		}
       	}
       }
      

      方法内部类,和它所在的方法(代码块),具有相同的访问能力,如果上面代码是在static方法中声明的,那么内部类D不能访问c变量。

      jdk1.8 方法内部类,能够访问非final类型的局部变量,本质相当有在内部类D内保存了副本

    4. 匿名内部类

      匿名内部类也就是没有名字的内部类

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

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

    内部类的本质

    内部类的语法颇为奇怪,我们来看看如下代码,编译后的字节码文件!

    public class A {
    	private static String b = "b";
    	private int c = 1;
            // 静态内部类
    	public static class B {
    		public void b() {
    			System.out.println(b);
    		}
    	}
            // 成员内部类
    	class C {
    		public void c() {
    			System.out.println(c);
    		}
    	}
    }
    

    字节码文件

    1. A.java文件被编译成了多个class文件
    2. A类对应A.class
    3. B类对应A$B.class
    4. C类对应A$C.class

    内部类会被编译成单独的class文件,那意味JVM解释执行class文件时类“B”和类A是独立的,由此可以见内部类也是一种语法糖!

    对于JVM来说,类A的private b和c 成员,怎么能分别被类B和类C访问到的了!

    用javap命令反编译类A.class来看看:

    秘密就来自,编译器为外部类生的两个静态访问方法,Stinrg access$000()返回b变量的值,int access$100(A a)返回a对象的c成员变量值;

    而在静态内部类B中,编译器将访问静态变量b的地方替换为如上方法:

    // 静态内部类
    public static class A$B {
    	public void b() {
    		System.out.println(A.access$000());
    	}
    }
    

    在成员内部类C中,原理也是如此,不过增加了更多的东西,反编译A$C.class:

    1. 新增了成员字段final A $this;
    2. 构造方法添加形参 `A$C(A obj);
    3. 访问外部类成员变量的地方会被替换成:System.out.println(A.access$100($this));

    你一定会好奇成员构造方法中的外部类对象的参数从哪里传入的!看看我们是怎么声明内部类的对象的

    A a = new A();
    A.C c = a.new C();
    

    将会被编译器替换成:

    A a = new A();
    A$C c = new A$C(a);
    

    内部类的使用时机

    两个类之间紧密联系时,可以使用内部类:

    1. 当一个类需要访问另外一个类的许多属性时,内部类可以简化访问代码
    2. 实现更好封装性,比如:B 类仅仅被A类访问时,可以将B类作为A的私有内部类
    3. 使代码更简洁,匿名内部类
  • 相关阅读:
    Java通过Mybatis实现批量插入数据到Oracle中
    SpringMVC+Spring+Mybatis整合,使用druid连接池,声明式事务,maven配置
    图片懒加载原理
    js参考---sort函数
    css参考---纯css实现三角形
    css参考---实现元素水平垂直居中
    javascript疑难问题---13、函数防抖
    javascript疑难问题---12、函数节流
    闭包执行注意(函数节流为例)
    javascript疑难问题---11、回调函数
  • 原文地址:https://www.cnblogs.com/lkqm/p/6483112.html
Copyright © 2011-2022 走看看