Java 内部类(成员内部类)

内部类:将一个类的定义放在另一个类的内部,这个类就是内部类。

内部类是一种非常有用的特性,他允许你将一些逻辑相关的类组合在一起,并控制位于内部的类的可视性。他了解外部类,并且与之通信。

内部类的分类:

成员内部类

静态内部类

局部内部类

匿名内部类

创建一个内部类

public class OuterClass {

	class InnerClass1 {
		private int i = 11;

		public int value() {
			return i;
		}
	}

	class InnerClass2 {
		private String str;

		public InnerClass2(String str) {
			this.str = str;
		}

		public String readStr() {
			return str;
		}
	}

	public void ship(String str) {
		InnerClass1 class1 = new InnerClass1();
		InnerClass2 class2 = new InnerClass2(str);
		System.out.println(class2.readStr());
	}

	public static void main(String[] args) {
		OuterClass outerClass = new OuterClass();
		outerClass.ship("test");
	}

}

在简单的内部类实现上,与其他类并没有什么区别。只是InnerClass1和InnerClass2类的名字嵌套在OuterClass类中。

上述情况的另一种实现方式,外部类中方法的返回值是内部类的一个引用。

	public InnerClass1 getInnerClass1(){
		return new InnerClass1();
	}
	
	public InnerClass2 getInnerClass2(String str){
		return new InnerClass2(str);
	}

	public void ship(String str) {
		InnerClass1 class1 = getInnerClass1();    
      //实例化的时候直接调用方法
		InnerClass2 class2 = getInnerClass2(str);
		System.out.println(class2.readStr());
	}

   public static void main(String[] args) {
		OuterClass outerClass = new OuterClass();
		outerClass.ship("test1");
		OuterClass.InnerClass1 inner1 = outerClass.getInnerClass1();
		OuterClass.InnerClass2 inner2 = outerClass.getInnerClass2("test");
	}

上边main函数中所展示的代码向我们展示了,如果想在外部类的非静态方法之外的任意位置创建某个内部类的对象,必须具体指明这个对象的类型:外部对象名.内部对象名    该类型就是内部类的类型。

链接到外部类

当生成一个内部类的对象的时候,该对象就与他的外围对象产生了一种联系,它能访问外围对象的所有成员,而不需要任何外部条件,此外,内部类还拥有外围类的所有元素的访问权。

public interface Selector {
   boolean end();
   Object current();
   void next();
}

public class Sequence {
	private Object[] items;
	private int next = 0;
	
	public Sequence(int size) {
		items = new Object[size];
	}
	
	public void add(Object x){
		if(next < items.length){
			items[next++] = x;
		}
	}
	
	private class SequenceSelector implements Selector{
        private int i = 0;
		
		@Override
		public boolean end() {
			return i == items.length;
		}

		@Override
		public Object current() {
			return items[i];
		}

		@Override
		public void next() {
			if(i < items.length){
				i ++;
			}
		}
		
	}
	
	public Selector getSelector(){
		return new SequenceSelector();
	}
	
	public static void main(String[] args) {
	    Sequence sequence = new Sequence(10);
	    for (int i = 0; i < 10; i++) {
			sequence.add(Integer.toString(i));
		}
	    
	    Selector selector = sequence.getSelector();
	    
	    while(!selector.end()){
	    	System.out.println(selector.current());
	    	selector.next();
	    }
	}
}

上述代码是一个往数组添加元素,并打印出来的例子,Selector接口的实现类SequenceSelector中的三个方法表示打印的过程

SequenceSelector是Sequence的一个private 的内部类,在SequenceSelector中的三个方法都引用了Sequence的private 成员items.表示内部类对于外部类的所有成员都具有访问权。

使用.this和.new

如果你想在内部类中生成外部类对象的引用,可以使用外部类的名称后面紧跟.this,这样产生的引用自动的具有正确的类型

public class DotThis {
	public void f(){
		System.out.println("DotThis.f()");
	}
    
	public class InnerClass{
		public DotThis outClass(){
			return DotThis.this;
		}
	}
	
	public innerClass getInnerClass(){
		return new innerClass();
	}
	
	public static void main(String[] args) {
		DotThis dotThis = new DotThis();
		DotThis.InnerClassclass1 = dotThis.getInnerClass();
		class1.outClass().f();
	}
}

在InnerClass类中的outClass()返回值是外部类对象,可以进行调用f();所以当内部类要调用外部类的对象的时候,使用外部类名.this

有时候想要告知某些其他对象去创建内部类的对象时,必须在new 表达式中提供其他外部类对象的引用

public class DotNew {
	public class InnerClass{}
    public static void main(String[] args) {
		DotNew dotNew = new DotNew();
		DotNew.InnerClass innerClass = dotNew.new InnerClass();//表示是该外部类对象中建立的内部类对象
	}
}

想要直接创建内部类对象,必须使用外部类对象去创建该内部类的对象。在拥有外部类对象之前是不可能拥有内部类对象的。除非是静态内部类。

内部类与向上转型

将内部类向上转型成为其基类:与从实现了某个接口的对象,得到对此接口的引用,实质上是一致的。就是可以在使用的时候通过其基类进行访问,而看不到具体的实现,只是提供统一的接口。


public interface Contents {
     int value();
}

public interface Destination {
    String readLabel();
}

public class Parcel {
    //此内部类是一个实现了Contents接口的private修饰的类,表明此类有相应的可调用的类,但是只能被 
    //Parcel类访问,其他类不可访问ContentsImp 类
    private class ContentsImp implements Contents{
        private int i ;
		@Override
		public int value() {
			return i;
		}
    }
    //DestinationImpl类是实现了Destination接口的类,并且被protected修饰符修饰,只有Parcel类
    //及其子类还有同包下的类可对他进行访问。
    protected class DestinationImpl implements Destination{
        private String label;
        
        public DestinationImpl(String label) {
        	this.label = label;
		}
		@Override
		public String readLabel() {
			return label;
		}
    	
    }
    
    public Contents getContents() {
           return  new ContentsImp();
	}
    
    public DestinationImpl getDestination(String label) {
		return new DestinationImpl(label);
	}
   
}
//Test类与Parcel 在同一个包下
public class Test {
	 public static void main(String[] args) {
			Parcel parcel = new Parcel();
//			Parcel.ContentsImp contentsImp = parcel.new ContentsImp();   报错 因为正在
//Test类中对ContentsImp进行访问
		    Parcel.DestinationImpl destinationImpl =parcel.new DestinationImpl("test");
//可以访问 因为当前类和他在同一个包下。
//			System.out.println(contents.value());
			System.out.println(destinationImpl.readLabel());
		}
}
//Test1类与Parcel  不在一个包下
public class Test1 {
      public static void main(String[] args) {
		Parcel parcel = new Parcel();
//		Parcel.DestinationImpl destinationImpl =parcel.new DestinationImpl("test");  
//报错 因为现在	Test1类与Parcel  不在一个包下   不能访问内部类 DestinationImpl 	
		
		Contents contents = parcel.getContents();
		Destination destination = parcel.getDestination("test");
//正确的访问方式,这种情况即是向上转型,当取得了基类或者接口的引用时,可能无法找到他的正确类型
//不可直接去访问具体实现类,具体实现的细节也被隐藏起来了。
		System.out.println(contents.value());
		System.out.println(destination.readLabel());
	}
}

在一个类与外部类同级并且继承自内部类,那么在该类的构造方法中应该传入的是外部类的对象,并且在其中调用外部类对象.super(内部类构造方法所需参数)

class NewClass extends Parcel.DestinationImpl{

	public NewClass(Parcel parcel, String label) {
		parcel.super(label);
	}
	
}

最后对于成员内部类做一个总结:

成员内部类不能存在static 关键字,即,不能声明静态方法,静态属性,静态代码块等。【非静态内部类也可以定义静态成员但需要同时有final关键词修饰,静态方法鉴于无法用final修饰,仍必须是在静态内部类 或者非内部类中定义。
创建成员内部类的实例使用:外部类名.内部类名 实例名 = 外部类实例名.new 内部类构造方法(参数),可以理解为饮食的保存了一个引用,指向创建他的外部类对象。
在内部类中访问外部类的成员或方法的时候:外部类名.this.成员名/方法
内部类在编译之后生成一个单独的class文件,里面包含该类的定义。所以内部类中定义的方法或者成员名称可以跟父类的相同。生成的class文件名称是外部类名$内部类名.class
外部类无法直接访问成员内部类的方法和属性,需要通过内部类的一个实例来访问。
与外部类平级的类继承内部类时,其构造方法需要传入父类的实例对象,且在构造方法的第一句调用“外部类实例.super(内部类参数)”

你可能感兴趣的:(Java基础知识)