• Java学习笔记:内部类/匿名内部类的全面介绍


    编写java程序时,一般一个类(或者接口)都是放在一个独立的java文件中,并且类名同文件名(如果类是public的,类名必须与文件名一致;非public得,无强制要求)。如果想把多个java类放在一个java文件中,则只能有一个public类。如下面的两个类放在同一个文件中就会报错,无法编译通过。

    可以看出,因为TestOne.java文件中已经有一个public类TestOne,这时再加了一个public类TestTwo就报错了。如果将类TestTwo前面的public修饰符去掉就没有问题了。

    我们下面介绍内部类的概念和使用,所谓内部类,简单的说,就是一个类定义在另一个类的内部。与上面的两个类在同一个文件中不同(TestOne和TestTwo虽然在一个文件中,但相互没有嵌套,是并列的)。

    采用内部类,有时会对代码的可读性带来一点问题,但很多场景下还是很有必要的。尤其在使用一些框架(如java的swing,集合框架中的排序操作)等,使用内部类(尤其是匿名内部类)会带来很多便利;再比如我们在开发Android app时,就会大量的使用内部类,如果不了解内部类的含义和使用规则,几乎无法顺利的进行Android app的开发。

    内部类有其特别的地方,其中核心之一是,内部类实例可以访问包含它的外部类的所有成员,包括private成员。另外非常关键的一点是,内部类的使用必须与一个外部类的实例绑定,注意是实例,后面的例子中会说明这点。

    内部类也分好几种情况,下面一一来解释。

    一、一般内部类

    例1:TestTwo就是一个内部类,它可以直接访问外部类中的私有成员a。

    public class TestOne {
    
    	private int a = 2;
    
    	public static void main(String[] args) {
    		new TestOne().call();
    	}
    	
    	public void call(){
    		TestTwo two = new TestTwo();
    		two.show();
    	}
    
    	class TestTwo {
    		public void show(){
    			System.out.println(a);
    		}
    	}
    }
    

     例2:如果需要在外部类的静态方法中使用内部类,必须创建一个外部类的实例,通过该实例来new内部类。不能直接new,否则会报编译错误。

    public class TestOne {
    
    	private int a = 2;
    
    	public static void main(String[] args) {
    		TestOne testOne = new TestOne();
    		TestTwo testTwo = testOne.new TestTwo();
    		testTwo.show();
    	}
    	
    	public void call(){
    		TestTwo two = new TestTwo();
    		two.show();
    	}
    
    	class TestTwo {
    		public void show(){
    			System.out.println(a);
    		}
    	}
    }
    

     例3:如何在内部类中引用外部类的实例。有两种方法,一是普通的方法,在new内部类时,将外部类的this作为内部类的构造函数参数传给内部类;还有一种更简单的方法,直接在内部类中通过 "外部类类名.this" 就可以使用外部类的实例。如下面代码:

    public class TestOne {
    
    	private int a = 2;
    
    	public static void main(String[] args) {
    		new TestOne().call();
    	}
    	
    	public void call(){
    		new TestTwo(this).show();
    	}
    
    	class TestTwo {
    		private TestOne one;
    		
    		public TestTwo(TestOne one){
    			this.one = one;
    		}
    		public void show(){
    			System.out.println(a);
    			System.out.println(one.a);
    			System.out.println(TestOne.this.a);
    		}
    	}
    }
    

    上述情况下使用内部类,通常该内部类只是被该外部类使用,这样可以对内部类起到隐藏作用,并且跟外部类绑定的更紧密。

    二、方法内的内部类

    就是内部类定义在一个方法的内部。这种情况下,只有在该方法内才能使用该内部类;内部类也可以访问外部类的成员;但注意不能访问包含它的方法的局部变量(包括参数),除非方法内的变量(或参数)是final的。 举例:

    public class TestOne {
    	private int a = 2;
    
    	public static void main(String[] args) {
    		new TestOne().call();
    	}
    	
    	public void call(){
    		final int b=3;
    		class TestTwo {
    			private TestOne one;
    			
    			public TestTwo(TestOne one){
    				this.one = one;
    			}
    			public void show(){
    				System.out.println(a);
    				System.out.println(one.a);
    				System.out.println(TestOne.this.a);
    				System.out.println(b);
    			}
    		}
    		new TestTwo(this).show();
    	}
    }
    

      需要注意的是,如果是在静态方法中定义内部类,则该内部类只能使用外部类的静态成员。

    三、匿名内部类

    匿名内部类,其实也分很多情况。最常见的情况是,直接通过父类(比如抽象类)创建子类的实例,或直接通过接口创建对象,该对象可传递作为方法的参数,或者赋值给某个变量。实际上在内部类的使用中,匿名类是用途最广泛的。

    因为我们都知道,在java中,一般情况下,不像c/c++一样,我们可以把一个方法作为一个变量进行传递或引用。

    我们只能传递和引用java对象。比如我们定义了一个方法,有个参数是接口类型或抽象类,这样不使用匿名内部类,我们每次都需要先定义一个类实现该接口或抽象类,再创建该类的实例传递给该方法。当接口或抽象类中需要实现的方法数量比较少时(比如只有1个),这个先创建类,再使用就显的比较啰嗦了。而且这种场景下,往往创建的类就这一个地方使用。而通过匿名内部类,就可以让编写的代码简洁很多。 实际上,java本身提供的一些api(如多线程、io、集合框架等)就大量的使用了这个特性。 我们来看几个例子:

    例1:通过接口创建对象

    public class TestThread {
    	public static void main(String[] args) {
    		TestThread testThread = new TestThread();
    		testThread.fun1();
    		testThread.fun2();
    	}
    	public void fun1(){
    		Runnable runnable = new Runnable() {
    			@Override
    			public void run() {
    				System.out.println("fun1");
    			}
    		};
    		new Thread(runnable).start();
    	}
    	
    	public void fun2(){
    		new Thread(new Runnable() {
    			@Override
    			public void run() {
    				System.out.println("fun2");
    			}
    		}).start();
    	}
    }
    

    可以看出,方法fun1中直接通过Runnable接口创建了对象,供Thread类使用。fun2更简化了下,直接连变量名也省掉了,对象的创建直接在传入参数时创建。

    例2:直接创建父类的子类对象

    public class TestChild {
    	public static void main(String[] args) {
    		new Thread() {
    			@Override
    			public void run() {
    				System.out.println("thread is run");
    			}
    		}.start();
    
    	}
    }
    

      可以看出,Thread类有一个run方法,但这个run方法是空的,我们如果要用继承Thread类来实现多线程,必须在子类中重载run方法。上面的代码我们实际上没有显示的定义一个Thread的子类,而是直接通过上述形式创建了一个线程实例,并启动它。

    四、静态嵌套类(内部类)

    也就是在一个类中定义一个静态类(注意不能在方法中定义)。 与前面介绍的内部类不同。这个嵌套类只是命名空间限制在外部类中。实质上它不需要与外部类的实例绑定,也无法访问外部类的实例成员,只能访问外部类的静态成员。代码如:

    public class TestOne {
    	private int a = 2;
    	static private int b = 2;
    
    	public static void main(String[] args) {
    		TestTwo testTwo = new TestTwo();
    	}
    	
    	public void call(){
    		TestTwo testTwo = new TestTwo();
    	}
    	
    	static class TestTwo {
    		public void show(){
    			System.out.println(b);
    		}
    	}
    }
    
    class TestThree{
    	public static void main(String[] args) {
    		TestOne.TestTwo testTwo = new TestOne.TestTwo();
    	}
    	
    	public void call(){
    		TestOne.TestTwo testTwo = new TestOne.TestTwo();
    	}
    }
    

    可以看出,TestTwo的使用不需要依赖外部类TestOne的实例。

    上面介绍了内部类的几种情况,使用的时候稍加注意即可。  

  • 相关阅读:
    线段树优化dp——牛客多校第一场I(好题)
    字符串dp——牛客多校第五场G
    凑出和相等的k组数,玄学结论——hdu6616
    主席树/线段树模拟归并排序+二分答案(好题)——hdu多校第4场08
    思维题+贪心——牛客多校第一场C
    线性基算贡献——19牛客多校第一场H
    俞敏洪语录
    睡眠长短决定寿命!人每天应该睡多少小时
    Java程序员从笨鸟到菜鸟之(三十一)大话设计模式(一)设计模式遵循的七大原则
    spring源码剖析(五)利用AOP实现自定义Spring注解
  • 原文地址:https://www.cnblogs.com/51kata/p/4125704.html
Copyright © 2020-2023  润新知