关闭 x
IT技术网
    技 采 号
    ITJS.cn - 技术改变世界
    • 实用工具
    • 菜鸟教程
    IT采购网 中国存储网 科技号 CIO智库

    IT技术网

    IT采购网
    • 首页
    • 行业资讯
    • 系统运维
      • 操作系统
        • Windows
        • Linux
        • Mac OS
      • 数据库
        • MySQL
        • Oracle
        • SQL Server
      • 网站建设
    • 人工智能
    • 半导体芯片
    • 笔记本电脑
    • 智能手机
    • 智能汽车
    • 编程语言
    IT技术网 - ITJS.CN
    首页 » JAVA »Java内部类的一些使用与梳理

    Java内部类的一些使用与梳理

    2015-01-31 00:00:00 出处:InfoQ - 臧秀涛
    分享

    序

    有这篇文章,纯属巧合;那天在使用中突然发现 Java 内部类中还分 static ,说实话平时都在用,但是就是没有注意到;感觉有必要总结一下。

    有必要说一下的是ITJS的这篇文章纯属浅析,如有补充还请在评论中指出,欢迎总结。

    内部类的位置

    public class A {
    	class B {
    
    	}
    
    	public void pint() {
    		class C {
    		}
    		new C();
    	}
    
    	public void pint(boolean b) {
    		if (b) {
    			class D {
    			}
    			new D();
    		}
    	}
    }

    从代码中可以看出,内部类可以定义到很多地方,常用的是成员变量中(B),方法中也叫局部内部类(C),作用域中(D)

    从上面来看似乎没有用到过在方法中和作用域中的情况啊,这就错了;再来看看这个:

    public interface AInterface {
    	void show();
    }
    public class B {
    
    	public void show() {
    		class Man implements AInterface {
    			@Override
    			public void show() {
    
    			}
    
    		}
    		Man man = new Man();
    		man.show();
    	}
    
    }

    其中我们定义了两个文件,一个文件是一个接口类,一个是B文件;在B类中,的 show()方法中我们使用了局部内部类的方式创建了类 Man ,Man class继承接口并实现方法,随后使用该类。

    内部类的权限

    为什么要有内部类的存在?

    在我看来类主要的就是封装、继承、多态;当然其中的回调思想我认为是很重要的;而内部类的出现就是为了简化多重继承的问题;一个A类,并不能继承多个其他类,但是在使用中又需要使用到其他类的方法,这个时候内部类就发挥作用了;典型的就是事件点击的回调实现。

    那么内部类的权限究竟有多大?

    至于答案是什么,代码上看看就知道了。

    public class C {
    	int a = 1;
    	private int b = 2;
    	protected int c = 3;
    	public int d = 4;
    
    	void a() {
    		System.out.println("A:" + a);
    	}
    
    	private void b() {
    		System.out.println("B:" + b);
    	}
    
    	protected void c() {
    		System.out.println("C:" + c);
    	}
    
    	public void d() {
    		System.out.println("D:" + d);
    	}
    
    	class D {
    
    		void show() {
    			int max = a + b + c + d;
    			a();
    			b();
    			c();
    			d();
    			System.out.println("Max:" + max);
    		}
    	}
    
    	public static void main(String[] args) {
    		D d = new C().new D();
    		d.show();
    	}
    }

    运行结果:

    可以看出,内部类 D 对类 C 具有完整的访问权限,等于全身脱光了给你看。

    那要是反过来呢?

    public class C {
    	class D {
    		private int a = 20;
    		private void a(){
    			System.out.println("D.A:" + a);
    		}
    	}
    
    	void show(){
    		D d = new D();
    		d.a();
    
    		System.out.println("D.A:" + d.a);
    	}
    
    	public static void main(String[] args) {
    		new C().show();
    	}
    }

    运行结果:

    可见也是完全可行的,也能直接访问私有属性 私有方法,在这里似乎私有的限制已经失效了一般,这个让我想起了以前看见过一个面试:在 Java 中 private 修饰何时会失效。

    这完全是两个人互相脱光光了啊~

    匿名内部类

    这个非常常见,特别是在按钮点击事件绑定中。

    public class D {
    	void initButton() {
    		Button b1 = new Button();
    		b1.setOnClickListener(new OnClickListener() {
    
    			@Override
    			public void onClick(Button v) {
    
    			}
    		});
    
    		Button b2 = new Button();
    		b2.setOnClickListener(new OnClickListener() {
    
    			@Override
    			public void onClick(Button v) {
    
    			}
    		});
    	}
    
    }

    其中的:

        new OnClickListener() {
    
    			@Override
    			public void onClick(Button v) {
    
    			}
    		}

    就是匿名内部类的使用方式,OnClickListener 是一个接口类,接口类是无法直接new 一个实例的;这里也并不是那样,而是new 了一个其他的类,该类是匿名的,也就是没有名字,只不过该类实现了 OnClickListener接口类中的方法。

    上面的添加回调部分可等同于:

    public class D {
    	void initButton1() {
    		Button b1 = new Button();
    		b1.setOnClickListener(new Listener1());
    
    		Button b2 = new Button();
    		b2.setOnClickListener(new Listener2());
    	}
    
    	class Listener1 implements OnClickListener {
    
    		@Override
    		public void onClick(Button v) {
    
    		}
    	}
    
    	class Listener2 implements OnClickListener {
    
    		@Override
    		public void onClick(Button v) {
    
    		}
    	}
    
    }

    这里就是先建立类,继承自接口;而后赋值到 Button 中。

    要说两者的区别与好处,这个其实看具体的使用情况吧;如果你的按钮很多,但是为了避免建立太多类;那么可以建立一个回调类,然后都赋值给所有的按钮,不过最后就是需要在 onClick方法中进行判断是那个按钮进行的点击。

    匿名内部类的使用地方很多;具体的使用应视使用情况而定~

    静态内部类/静态嵌套类

    这个其实并不应该叫做内部类了,因为其并不具备内部类的完全权限,在使用上与一般的类基本一样;那为什么会有这个的存在?

    在我看来这个类的存在是为其包括类服务;意思是可以单独服务,不被外面的类所知晓;如这样:

    public class E {
    	private void show(){
    		new A();
    	}
    
    	private static class A{
    
    	}
    }

    其中类 A 使用了 static ,所以是静态嵌套类,在这里使用private 修饰;那么该类只能在 E 类中进行实例化;无法在 其他文件中实例化。

    这样的情况使用外面的类能行么?不行吧?也许你会说在 E.java 文件夹中建立 A.java ,并使用protected修饰;但是在同样的包下,或者继承的类中同样能访问了;这也只是其中一个较为特殊的情况。

    我们来看看权限

    public class E {
    	int a1 = 0;
    	private int a2 = 0;
    	protected int a3 = 0;
    	public int a4 = 0;
    
    	private void show(){
    		A a =new A();
    		System.out.print("b1:"+a.b1);
    		System.out.print("b2:"+a.b2);
    		System.out.print("b3:"+a.b3);
    		System.out.print("b4:"+a.b4);
    
    	}
    
    	private static class A{
    		int b1 = 0;
    		private int b2 = 0;
    		protected int b3 = 0;
    		public int b4 = 0;
    
    		private void print(){
    			System.out.print("a1:"+a1);
    			System.out.print("a2:"+a2);
    			System.out.print("a3:"+a3);
    			System.out.print("a4:"+a4);
    
    		}
    	}
    }

    在这个中的结果是怎样?

    从图片中可以看出,其权限级别是单方向的;静态嵌套类 A 对其包含类 E 完全透明;但 E 并不对 A 透明。

    再来看看方法:

    可以看出同样的情况;这个是为什么呢?为什么就是多一个 static 的修饰就这么完全不同?其是很好理解,两个独立的类;本来就无法直接使用,必须有引用才能调用其属性与方法。

    我们或许可以这么调整一下就OK:

    public class E {
    	int a1 = 0;
    	private int a2 = 0;
    	protected int a3 = 0;
    	public int a4 = 0;
    
    	private void show() {
    		A a = new A();
    		System.out.print("b1:" + a.b1);
    		System.out.print("b2:" + a.b2);
    		System.out.print("b3:" + a.b3);
    		System.out.print("b4:" + a.b4);
    
    		a.b1();
    		a.b2();
    		a.b3();
    		a.b4();
    	}
    
    	void a1() {
    
    	}
    
    	private void a2() {
    
    	}
    
    	protected void a3() {
    
    	}
    
    	public void a4() {
    
    	}
    
    	private static class A {
    		int b1 = 0;
    		private int b2 = 0;
    		protected int b3 = 0;
    		public int b4 = 0;
    
    		void b1() {
    
    		}
    
    		private void b2() {
    
    		}
    
    		protected void b3() {
    
    		}
    
    		public void b4() {
    
    		}
    
    		private void print(E e) {
    			System.out.print("a1:" + e.a1);
    			System.out.print("a2:" + e.a2);
    			System.out.print("a3:" + e.a3);
    			System.out.print("a4:" + e.a4);
    
    			e.a1();
    			e.a2();
    			e.a3();
    			e.a4();
    		}
    	}
    }

    在其静态类中传递一个 E 的引用进去就能解决问题了:

    可以看出其中现在并没有报错了;能正常运行。

    两者之间的隐藏区别

    但是最开始上面的内部类是怎么回事?难道是闹鬼了?上面的内部类没有传递引用的啊;为啥加上一个 static 就不行了?

    在这里我们需要看看字节码,我们先建立一个简单的内部类:

    public class F {
    
    	class A{
    
    	}
    }

    这个够简单吧?别说这个都难了;汗~

    然后我们找到 class 文件,然后查看字节码:

    在这里分别查看了 F 类的字节码和 F$A 类的字节码。

    其中有这样的一句: final F this$0; 这句是很重要的一句,这句出现的地方在其内部类中,意思是当你 new 一个内部类的时候就同时传递了当前类进去;所以在内部类中能具有当前类的完全权限,能直接使用所有的东西;就是因为在隐藏情况下已经传递了当前类进去。

    那么我们再看看一个简单的静态内部类:

    public class G {
    	static class A {
    
    	}
    }

    与上面的区别唯一就是在于添加了一个 static 。此时我们看看字节码:

    可以看出其中无论是 G 类,还是 G$A 类的初始化中都没有其他多余的部分,也没有进行隐藏的传递进去当前类;所以这样的情况下并不具备访问权限,需要我们传递引用进去,可以通过接口也可以完全传递进去,具体取决于个人。所以加了static类的内部类除了在权限上比一般的类更加开放(与其包含类)外,与一般的类在使用上是一样的;所以准确的说应该叫做静态嵌套类。

    初始化的区别

    一个类中,同时包含了内部类与静态内部类,那么其初始化应该是怎么样的呢?

    都是直接 new ?还是看看代码:

    public class H {
    	int a = 1;
    
    	public class A {
    		public void Show() {
    			System.out.print("a:" + a);
    		}
    	}
    
    	public static class B {
    		public void Show(H h) {
    			System.out.print("a:" + h.a);
    		}
    	}
    
    	public static void main(String[] args) {
    		H h = new H();
    		//A a = new A();
    		A a1 = h.new A();
    		B b = new B();
    		//B b1 = h.new B();
    		B b3 = new H.B();
    	}
    }

    其中注释了的两种方式是不允许的方式,也就是无法正常运行。

    A 因为有一个隐藏的引用,所以必须是H 的实例才能进行初始化出A 类;而B 类则是因为是在H 类中以静态方式存在的类,所以需要 new H.B();之所以能直接使用new B(),与该 main 方法在 H 类中有关,因为本来就在 H类中,所以直接使用 H类的静态属性或者方法可以不加上:“H.” 在前面。

    内部类的继承

    直接继承的情况:

    可以看出报错了,为什么?因为需要传递一个 H 类进去,所以我们在继承的时候需要显示的指明:

    public class I extends H.A{
    	public I(H h){
    		h.super();
    	}
    }

    也就是在构造方法中,传递一个 H 的引用进去,并调用 H 实例的 super() 方法,才能进行实例化。
    使用的话应该这样:

    public static void main(String[] args) {
            H h = new H();
            I i = new I(h);
        }

    而,如果是继承其静态嵌套类,则不需要这样:

    public class J extends H.B{
    
    }

    就这样就OK。

    哎,差不多了~~整个内部类的东西差不多就是这些了,写了我3个小时42分钟~汗!!!!

    如果有没有写到的地方,还请补充~~

    不对的地方还请指正~~

    上一篇返回首页 下一篇

    声明: 此文观点不代表本站立场;转载务必保留本文链接;版权疑问请联系我们。

    别人在看

    帝国CMS7.5编辑器上传图片取消宽高的三种方法

    帝国cms如何自动生成缩略图的实现方法

    Windows 12即将到来,将彻底改变人机交互

    帝国CMS 7.5忘记登陆账号密码怎么办?可以phpmyadmin中重置管理员密码

    帝国CMS 7.5 后台编辑器换行,修改回车键br换行为p标签

    Windows 11 版本与 Windows 10比较,新功能一览

    Windows 11激活产品密钥收集及专业版激活方法

    如何从 Windows 11 中完全删除/卸载 OneNote?无解!

    抖音安全与信任开放日:揭秘推荐算法,告别单一标签依赖

    ultraedit编辑器打开文件时,总是提示是否转换为DOS格式,如何关闭?

    IT头条

    华为Pura80系列新机预热,余承东力赞其复杂光线下的视频拍摄实力

    01:28

    阿里千问3开源首战告捷:全球下载破千万,国产AI模型崛起新高度!

    01:22

    DeepSeek R1小版本试升级:网友实测编程能力已达到国际一线水平

    23:15

    NVIDIA 与 Dell 合作,大规模交付 Blackwell AI 系统

    20:52

    Cerebras 以最快的 Llama 4 Maverick 性能引领 LLM 推理竞赛

    20:51

    技术热点

    PHP中的随机性——你觉得自己幸运吗?

    搞定Ubuntu Linux下WPA无线上网

    Java使用内存映射实现大文件的上传

    MySQL安全性指南

    MySQL两项性能的基本测试浅谈

    教您使用UniqueIdentifier选取SQL Server主键

      友情链接:
    • IT采购网
    • 科技号
    • 中国存储网
    • 存储网
    • 半导体联盟
    • 医疗软件网
    • 软件中国
    • ITbrand
    • 采购中国
    • CIO智库
    • 考研题库
    • 法务网
    • AI工具网
    • 电子芯片网
    • 安全库
    • 隐私保护
    • 版权申明
    • 联系我们
    IT技术网 版权所有 © 2020-2025,京ICP备14047533号-20,Power by OK设计网

    在上方输入关键词后,回车键 开始搜索。Esc键 取消该搜索窗口。