内部类

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了内部类相关的知识,希望对你有一定的参考价值。

    在Java中,可以将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类。广泛意义上的内部类一般来

说包括这四种:成员内部类局部内部类匿名内部类静态内部类

一、成员内部类

    将一个类定义在一个类的内部就称为成员内部类。如下代码中的InnterClass就是一个成员内部类:

//成员内部类
public class OutterClass {

	private int a = 0;
	public void method() {
		System.out.println("外部类方法。。");
	}
	
	public class InnterClass{
		private int b = 0;
		public void method() {
			System.out.println("内部类方法。。");
		}
	}
}

   对于成员内部类来说他可以访问外部类中的任何方法任何属性,包括private修饰的和static修饰的,但外部类访问

内部类中的成员,就不是随心所欲的了:

//成员内部类
public class OutterClass {

	private int a = 0;
	public static String s = "123";
	public void method() {
//		b++;        //报错
		System.out.println("外部类方法。。");
	}
	
	public class InnterClass{
		private int b = 0;
		public void method() {
			a++;
			System.out.println(s);
			System.out.println("内部类方法。。");
		}
	}
}

            而外部类访问内部类成员需要先创建一个内部类的对象,通过对象再来访问。

//成员内部类
public class OutterClass {

	InnterClass in = new InnterClass();
	private int a = 0;
	public static String s = "123";
	public void method() {
//		b++;        //报错
		in.b++;
		System.out.println("外部类方法。。");
	}
	
	public class InnterClass{
		private int b = 0;
		public int c = 1;
		public void method() {
			a++;
			System.out.println("内部类方法。。");
		}
	}
}

    内部类是依附于外部类存在的,因此,我们在main'方法中创建内部类对象时,首先要创建一个外部类的对象:

//成员内部类
public class OutterClass {

	InnterClass in = new InnterClass();
	private int a = 0;
	public static String s = "123";
	public void method() {
//		b++;
		in.b++;
		System.out.println("外部类方法。。");
	}
	
	public class InnterClass{
		private int b = 0;
		public int c = 1;
		public void method() {
			a++;
			System.out.println(s);
			System.out.println("内部类方法。。");
		}
	}
	
	public static void main(String[] args) {
		OutterClass out = new OutterClass();
		InnterClass in = out.new InnterClass();
	}
}

    有一种特殊情况,当外部类的成员名称和内部类的成员名称相同时,会发生隐藏现象,当内部类对象调用该方法时

会默认调用内部类的方法,当我们访问外部类方法时应该‘外部类.this.成员变量’调用,如下测试:

//成员内部类
public class OutterClass {

	InnterClass in = new InnterClass();
	private int a = 0;
	public void method() {
//		b++;
		in.b++;
		System.out.println("外部类方法。。");
	}
	
	public class InnterClass{
		private int b = 0;
		public int c = 1;
		public void method() {
			a++;
			System.out.println("内部类方法。。");
		}
		
		public void test() {
			method();      			//默认访问内部类方法
			OutterClass.this.method();//访问外部类方法
		}
	}
	
	public static void main(String[] args) {
		OutterClass out = new OutterClass();
		InnterClass in = out.new InnterClass();
		in.test();
	}
}

结果:

内部类方法。。
外部类方法。。

总结:内部类可以拥有private访问权限、protected访问权限、public访问权限及包访问权限。比如上面的例子,

如果成员内部类Inner用private修饰,则只能在外部类的内部访问,如果用public修饰,则任何地方都能访问;

如果用protected修饰,则只能在同一个包下或者继承外部类的情况下访问;如果是默认访问权限,则只能在同一

个包下访问。这一点和外部类有一点不一样,外部类只能被public和包访问两种权限修饰。我个人是这么理解的,

由于成员内部类看起来像是外部类的一个成员,所以可以像类的成员一样拥有多种权限修饰。

二、局部内部类

    局部内部类就是在一个方法里或者一个作用域内定义的类,就像是一个局部变量一样:

public class OutterClass {

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

    它就相当于一个局部变量一样,作用域已经被限制在一个方法里面或者一个作用域里面,因此他不存在

访问修饰符的问题,它不能被public、protected、private以及static修饰符修饰。。但是他的内部成员可以被public、protected、

private修饰符修饰:

public class OutterClass {

	public void method() {
		class InnterClass {
			//局部内部类
			protected int a = 0;
			
			public void method() {
				System.out.println("...");
			}
		}
		InnterClass in = new InnterClass();
		in.method();
	}
	
	public static void main(String[] args) {
		OutterClass out = new OutterClass();
		out.method();
	}
}

三、匿名内部类

    匿名内部类就是没有名字的内部类。它的创建格式如下:

new 父类构造器(参数列表)|实现接口()      
{       
    //匿名内部类的类体部分      
}

 在使用匿名内部类的过程中,我们需要注意如下几点:、、、

          1、使用匿名内部类时,我们必须是继承一个类或者实现一个接口,但是两者不可兼得,同时也只能继承一个类或者实现一个接口。

          2、匿名内部类中是不能定义构造函数的。

           3、匿名内部类中不能存在任何的静态成员变量和静态方法。

          4、匿名内部类为局部内部类,所以局部内部类的所有限制同样对匿名内部类生效。

           5、匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法。

    总结:匿名内部类是唯一一种没有构造器的类。正因为其没有构造器,所以匿名内部类的使用范围非常有限,大部分匿名内部类

用于接口回调。匿名内部类在编译的时候由系统自动起名为Outter$1.class。一般来说,匿名内部类用于继承其他类或是实

现接口,并不需要增加额外的方法,只是对继承方法的实现或是重写。

还有一点需要注意,当我们给匿名内部类传递参数的时候,若该参数被匿名累不累所使用,该参数必须为final修饰的,代码例子如下:

public class OutterClass {

	public static void main(final String[] args) {
		new Person() {
			@Override
			public void method() {
				System.out.println(args[0]);
			}
		};
	}
}

abstract class Person {
	public abstract void method();
}

那么有的小伙伴会疑惑不解,为什么要用final修饰形参,这看起来没有任何意义,因为我们知道方法里面修改形参的值并不会

影响方法外面的值的:

    首先我们知道在内部类编译成功后,它会产生一个class文件,该class文件与外部类并不是同一class文件,仅仅只保留对外部类的引用

。当外部类传入的参数需要被内部类调用时,从java程序的角度来看是直接被调用:

public class OuterClass {    
    public void display(final String name,String age){        
    class InnerClass{            
        void display(){                 
            System.out.println(name);             
        }         
    }     
    } 
}

从上面代码中看好像name参数应该是被内部类直接调用?其实不然,在java编译之后实际的操作如下:

public class OuterClass$InnerClass {    
    public InnerClass(String name,String age){        
        this.InnerClass$name = name;        
        this.InnerClass$age = age;     
    }              
    public void display(){         
        System.out.println(this.InnerClass$name + 
            "----" + this.InnerClass$age );     
    } 
}

           所以从上面代码来看,内部类并不是直接调用方法传递的参数,而是利用自身的构造器对传入的

参数进行备份,自己内部方法调用的实际上时自己的属性而不是外部方法传递进来的参数。

          直到这里还没有解释为什么是final?在内部类中的属性和外部方法的参数两者从外表上看是同一个

东西,但实际上却不是,所以他们两者是可以任意变化的,也就是说在内部类中我对属性的改变并不会

影响到外部的形参,而然这从程序员的角度来看这是不可行的,毕竟站在程序的角度来看这两个根本就

是同一个,如果内部类该变了,而外部方法的形参却没有改变这是难以理解和不可接受的,所以为了保

持参数的一致性,就规定使用final来避免形参的不改变。

          简单理解就是,拷贝引用,为了避免引用值发生改变,例如被外部类的方法修改等,而导致内部

类得到的值不一致,于是用final来让该引用不可改变。

          故如果定义了一个匿名内部类,并且希望它使用一个其外部定义的参数,那么编译器会要求该参数

引用是final的。


四、静态内部类

    静态内部类也是定义在另一个类里面的类,只不过在类的前面多了一个关键字static。。静态内部类是不需要依赖于外部类的,这点和类的静态

成员属性有点类似,并且它不能使用外部类的非static成员变量或者方法,这点很好理解,因为在没有外部类的对象的情况下,可以创建静态内部类的对象,

如果允许访问外部类的非static成员就会产生矛盾,因为外部类的非static成员必须依附于具体的对象。

public class OutterClass {

	InnterClass in = new InnterClass();
	public static class InnterClass {
		public static void method() {
			System.out.println("静态内部类的静态方法");
		}
		public void method2() {
			System.out.println("静态内部类的非静态方法");
		}
	}
	
	public static void main(String[] args) {
		OutterClass.InnterClass.method();
		//调用非静态方法方式一
		InnterClass in = new OutterClass.InnterClass();
		in.method2();
		//调用非静态方法方式二
		new OutterClass().in.method2();;
	}
}






参考资料:

http://mp.weixin.qq.com/s?src=11&timestamp=1517114603&ver=663&signature=2ElILgkZ6f8ZiubX8fmYWoRIpPWrwoEIkFN59QN4nfx*LQBjuRndx*aUuIx03bWUx*V58GZHedN*2GKgaZZXh9sr0HRHh26VpK--90D9Wepf8jMDRRPyf3EDcq-E-YbJ&new=1

http://www.cnblogs.com/dolphin0520/p/3811445.html

以上是关于内部类的主要内容,如果未能解决你的问题,请参考以下文章

片段 - 全局视图变量与本地和内部类侦听器和内存泄漏

为啥片段类应该是公开的?

ForegroundService没有从片段开始?

在内部片段类中使用ListView

自定义无内存泄漏的Handler内部类

底部导航 如何从片段内部更改片段