java中内部类使用小结

内部类是指在一个外部类中再定义一个类,类名不需要和文件名相同

内部类可以是静态的,类的修饰符可以是private,default,protect,public修饰 ,而外部类只能是public 和 default修饰

注意:内部类是一个编译时的概念,一旦编译成功,就会成为完全不同的两类。对于一个名为outer的外部类和其内部定义的名为inner的内部类。编译完成后出现outer.class和outer$inner.class两类。所以内部类的成员变量/方法名可以和外部类的相同。

内部类可以按照以下划分:成员内部类,局部内部类,嵌套内部类,匿名内部类

1、成员内部类,就是作为外部类的成员,可以直接使用外部类的所有成员和方法,即使是private的。同时外部类要访问内部类的所有成员变量/方法,则需要通过内部类的对象来获取。

要注意的是,成员内部类不能含有static的变量和方法。因为成员内部类需要先创建了外部类,才能创建它自己的,了解这一点,就可以明白更多事情,在此省略更多的细节了。

//外部类
class Out {
	private int age = 12;
	
	//内部类
	class In {
		public void print() {
			System.out.println(age);
		}
	}
}

public class Demo {
	public static void main(String[] args) {
		Out.In in = new Out().new In();
		in.print();
		//或者采用下种方式访问
		/*
		Out out = new Out();
		Out.In in = out.new In();
		in.print();
		*/
	}
}

运行结果:12

从上面代码可知,内部类其实严重破坏了良好的代码结构,但为什么还要使用内部类呢?

因为内部类可以随意使用外部类的成员变量(包括私有)或方法而不用生成外部类的对象,这也是内部类的唯一优点

如同心脏可以直接访问身体的血液,而不是通过医生来抽血

实例2:内部类中的变量访问形式

class Out {
	private int age = 12;
	
	class In {
		private int age = 13;
		public void print() {
			int age = 14;
			System.out.println("局部变量:" + age);
			System.out.println("内部类变量:" + this.age);
			System.out.println("外部类变量:" + Out.this.age);
		}
	}
}

public class Demo {
	public static void main(String[] args) {
		Out.In in = new Out().new In();
		in.print();
	}
}

运行结果:

局部变量:14

内部类变量:13

外部类变量:12

从实例1中可发现,内部类在没有同名成员变量和局部变量的情况下,内部类会直接访问外部类的成员变量,而无需指定Out.this.属性名

否则,内部类中的局部变量会覆盖外部类的成员变量

而访问内部类本身的成员变量可用this.属性名,访问外部类的成员变量需要使用Out.this.属性名

实例3:静态内部类

class Out {
	private static int age = 12;
	
	static class In {
		public void print() {
			System.out.println(age);
		}
	}
}

public class Demo {
	public static void main(String[] args) {
		Out.In in = new Out.In();
		in.print();
	}
}

运行结果:12

可以看到,如果用static 将内部内静态化,那么内部类就只能访问外部类的静态成员变量,具有局限性

其次,因为内部类被静态化,因此Out.In可以当做一个整体看,可以直接new 出内部类的对象(通过类名访问static,生不生成外部类对象都没关系)

实例4:私有内部类

class Out {
	private int age = 12;
	
	private class In {
		public void print() {
			System.out.println(age);
		}
	}
	public void outPrint() {
		new In().print();
	}
}

public class Demo {
	public static void main(String[] args) {
		//此方法无效
		/*
		Out.In in = new Out().new In();
		in.print();
		*/
		Out out = new Out();
		out.outPrint();
	}
}

运行结果:12

如果一个内部类只希望被外部类中的方法操作,那么可以使用private声明内部类

上面的代码中,我们必须在Out类里面生成In类的对象进行操作,而无法再使用Out.In in = new Out().new In() 生成内部类的对象

也就是说,此时的内部类只有外部类可控制

如同是,我的心脏只能由我的身体控制,其他人无法直接访问它

实例5:方法内部类

class Out {
	private int age = 12;

	public void Print(final int x) {
		class In {
			public void inPrint() {
				System.out.println(x);
				System.out.println(age);
			}
		}
		new In().inPrint();
	}
}

public class Demo {
	public static void main(String[] args) {
		Out out = new Out();
		out.Print(3);
	}
}

运行结果:

3

12

在上面的代码中,我们将内部类移到了外部类的方法中,然后在外部类的方法中再生成一个内部类对象去调用内部类方法

如果此时我们需要往外部类的方法中传入参数,那么外部类的方法形参必须使用final定义

至于final在这里并没有特殊含义,只是一种表示形式而已

4.匿名内部类

      有时候我为了免去给内部类命名,便倾向于使用匿名内部类,因为它没有名字。例如:

 ((Button) findViewById(R.id.start)).setOnClickListener(new Button.OnClickListener() { 

    @Override 

    public void onClick(View v) { 

        new Thread() {  

            @Override 

            public void run() { 

                // TODO Auto-generated method stub 

            } 

        }.start(); 

    } 

}); 

匿名内部类不能加修饰符的,new匿名类,这个匿名类要先定义

public class Outer { 

    public static void main(String[] args) { 

        Outer outer = new Outer(); 

        Inner inner = outer.getInner("Inner", "gz"); 

        System.out.println(inner.getName()); 

    } 

    public Inner getInner(final String name, String city) { 

        return new Inner() { 

            private String nameStr = name; 

            public String getName() { 

                return nameStr; 

            } 

        }; 

    } 

留意外部类中方法的形参,当所在方法的形参被内部类使用是时,这个形参必须为final,为什么必须要final呢,这是因为:首先内部类在编译后

生成的是独立的一个文件,

当外部类传的参数被内部类调用时,从java程序的角度来看是直接的调用例如:  
public void dosome(final String a,final int b){  
  class Dosome{public void dosome(){System.out.println(a+b)}};  
  Dosome some=new Dosome();  
  some.dosome();  
}  
从代码来看好像是那个内部类直接调用的a参数和b参数,但是实际上不是,在java编译器编译以后实际的操作代码是
class Outer$Dosome{  
  public Dosome(final String a,final int b){  
  this.Dosome$a=a;  
  this.Dosome$b=b;  
}  
  public void dosome(){  
  System.out.println(this.Dosome$a+this.Dosome$b);  
}  
}}  
而是内部类将传进来的参数通过自己的构造器备份到了自己的内部,自己内部的方法调用的实际是自己的属性而不是外部类方法的参数
这样理解就很容易得出为什么要用final了,因为两者从外表看起来是同一个东西,实际上却不是这样,如果内部类改掉了这些参数的值也不可能影响到原参数,然而这样却失去了参数的一致性,因为从编程人员的角度来看他们是同一个东西,如果编程人员在程序设计的时候在内部类中改掉参数的值,但是外部调用的时候又发现值其实没有被改掉,这就让人非常的难以理解和接受,为了避免这种尴尬的问题存在,所以编译器设计人员把内部类能够使用的参数设定为必须是final来规避这种莫名其妙错误的存在。”
因为匿名内部类,没名字,是用默认的构造函数的,无参数的,那如果需要参数呢?则需要该类有带参数的构造函数:
public class Outer { 

    public static void main(String[] args) { 

        Outer outer = new Outer(); 

        Inner inner = outer.getInner("Inner", "gz"); 

        System.out.println(inner.getName()); 

    } 

    public Inner getInner(final String name, String city) { 

        return new Inner(name, city) { 

            private String nameStr = name; 

            public String getName() { 

                return nameStr; 

            } 

        }; 

    } 

abstract class Inner { 

    Inner(String name, String city) { 

        System.out.println(city); 

    } 

    abstract String getName(); 

注意这里的形参city,由于它没有被匿名内部类直接使用,而是被抽象类Inner的构造函数所使用,所以不必定义为final。

而匿名内部类通过实例初始化,可以达到类似构造器的效果:

内部类的继承,是指内部类被继承,普通类 extents 内部类。而这时候代码上要有点特别处理,具体看以下例子:

public class InheritInner extends WithInner.Inner {  

    // InheritInner() 是不能通过编译的,一定要加上形参 

    InheritInner(WithInner wi) { 

        wi.super(); 

    }  

    public static void main(String[] args) { 

        WithInner wi = new WithInner(); 

        InheritInner obj = new InheritInner(wi); 

    } 

}  

class WithInner { 

    class Inner {  

    } 

可以看到子类的构造函数里面要使用父类的外部类对象.super();而这个对象需要从外面创建并传给形参。

要点总结:

非静态内部类中不能含有静态的变量或方法,静态内部类下的非静态变量赋值时,如果赋的值是外部类定义的变量,这个外部类的变量必须是静态的,因为此时没有创建外部类对象

 

 

原文地址:https://www.cnblogs.com/muliu/p/5501191.html