您的位置:首页 > 编程语言 > Java开发

我爱学Java之内部类

2016-04-11 00:28 447 查看
 在Java中,可以将一个类的定义放在另一个类的内部,这就是内部类。

 

 内部类可以被private,default,protected,public修饰,且内部类是编译器全权负责的,虚拟机并不知道内部类与常规类有什么不同,一旦编译成功,就会与外部类成为完全不同的两类,所以内部类的成员变量和成员方法可以与外部类相同 。

 

 内部类一般来说包括这四种:成员内部类、局部内部类、匿名内部类和静态内部类,下面就了解一下这四种内部类的用法:

1.成员内部类:位于另一个类的内部且没有用static修饰的最普通的内部类。

(1)成员内部类可以直接使用外部类的成员变量和方法,是因为用外围类创建内部类对象时,编译器会默认给内部类构造器添加一个参数,该参数的类型为指向外部类对象的一个引用。从这里也间接说明了成员内部类是依赖于外部类的,如果没有创建外部类的对象,也就无法创建成员内部类的对象了。

(2)外部类要访问成员内部类的所有成员变量和方法,需要通过成员内部类的对象来获取,如想从外部类非静态方法之外的任意位置创建某个成员内部类对象,则需要先创建外部类对象,再通过OutClass.InnerClass指命类型,再用OutClass.new完成成员内部类对象创建。

(3)在成员内部类中要引用外部类对象时,使用OuterClass.this来表示外部类对象。

(4)成员内部类不能含有static的变量和方法,原因是:非static的内部类在外部类加载的时候并不会加载它,而static类型的属性和方法,在类加载的时候就会存在于内存中,也就是说要使用某个类的static属性或者方法,那么这个类必须要加载到jvm中,所以说如果一个非static的内部类如果具有static的属性或者方法,那么就会出现内部类未加载但却试图在内存中创建static的属性和方法,因此它里面不能有静态变量或者静态方法。

public class OuterClass {
class InnerClass{
public void test(){
System.out.println("HelloWorld");
}

public OuterClass getOuter(){
return OuterClass.this;
}
}
public InnerClass getInner(){
return new InnerClass();
}

public static void main(String[] args){
OuterClass out = new OuterClass();
OuterClass.InnerClass inner = out.new InnerClass();
OuterClass.InnerClass inner1 = inner.getOuter().getInner();//也可以这样获取成员内部类对象
inner.test();
inner1.test();

}
}//都输出HelloWorld


2.局部内部类:是定义在一个方法或者一个作用域里面的类,也可以访问外部类成员,局部内部类也像别的类一样进行编译,但与成员内部类不同的是它的访问仅限于方法内或者该作用域内。局部内部类,就像局部变量一样,在定义的方法体外不能创建局部内部类的对象,并且不能有public、protected、private以及static修饰符的。

(1)定义在方法内:

public class OuterClass {
private String test = "HelloWorld";
public String getString(){
class InnerClass{
public String test(){

return test;
}

}
return new InnerClass().test();
}

public static void main(String[] args){
OuterClass outer = new OuterClass();
System.out.println(outer.getString());
}
}//输出HelloWorld


(2)定义在作用域内:

public class OuterClass {
private String test = "HelloWorld";
public String getString(boolean b){
if(b){
class InnerClass{
public String test(){

return test;
}

}
return new InnerClass().test();
}
return null;

}
public static void main(String[] args){
OuterClass outer = new OuterClass();
System.out.println(outer.getString(true));
}
}//输出HelloWorld


3.嵌套内部类(静态内部类):就是修饰为static的内部类。

(1)静态内部类是不需要依赖于外部类的,这点和类的静态成员属性有点类似,并且它不能使用外部类的非static成员变量或者方法,这点很好理解,因为在没有外部类的对象的情况下,可以创建静态内部类的对象,如果允许访问外部类的非static成员就会产生矛盾,因为外部类的非static成员必须依附于具体的对象。

(2)声明为static的内部类,不需要内部类对象和外部类对象之间的联系,就是说我们可以直接引用outer.inner。

(3)普通内部类不能有static数据和static属性,也不能包含嵌套类,但嵌套类可以,并且嵌套类一般不声明为private,而声明为public,方便调用。

public class OuterClass {
private static String test = "Hello";
public static class InnerClass{
private String s = "World";
private static String ss = "HelloWorld";
public void print(){
System.out.println(test+s);
}

public static void print1(){
System.out.println(ss);
}
}
public static void main(String[] args){
OuterClass.InnerClass inner = new OuterClass.InnerClass();
inner.print();

System.out.println(OuterClass.InnerClass.ss);
OuterClass.InnerClass.print1();

}
}//输出三个HelloWorld


4.匿名内部类:定义类的最终目的是创建一个类的实例,但是如果某个类的实例只是用一次,则可以将类的定义与类的创建,放到与一起完成,或者说在定义类的同时就创建一个类,以这种方法定义的没有名字的类成为匿名内部类。

(1) 匿名内部类必须继承一个抽象类或一个普通类或实现一个接口,但匿名内部类不能同时实现一个接口和继承一个类,也不能实现多个接口。

(2) 由于匿名内部类没有名称,所以类体中不能定义构造方法,由于不知道类名也不能使用关键字来创建该类的实例。实际上匿名内部类的定义、构造、和第一次使用都发生在同样一个地方。

public class OuterClass {

public InnerClass getInner(final String s){
return new InnerClass(){
public void print(){
System.out.println(s);
}
};
}
public static void main(String[] args){
new OuterClass().getInner("HelloWorld").print();
}
}

interface InnerClass{
public void print();
}//输出HelloWorld


注:当所在的方法的形参需要被内部类里面使用时,该形参必须为final,但在jdk1.8中,final可以省略,但参数值还是不可变的,否则编译会报错。必须用final的原因是:

首先,内部类被编译的时候会生成一个单独的内部类的.class文件,这个文件并不与外部类在同一class文件中。 当外部类传的参数被内部类调用时,从Java程序的角度来看是直接的调用,但实际上并不是,内部类并不是直接调用方法传进来的参数,而是内部类将传进来的参数通过自己的构造器备份到了自己的内部,自己内部的方法调用的实际是自己的属性而不是外部类方法的参数。 这样理解就很容易得出为什么要用final了,如果内部类改掉了这些参数的值也不可能影响到原参数,然而这样却失去了参数的一致性,为了避免这种尴尬的问题存在,所以编译器设计人员把内部类能够使用的参数设定为必须是final来规避这种莫名其妙错误的存在。

(简单理解就是,拷贝引用,为了避免引用值发生改变,例如被外部类的方法修改等,而导致内部类得到的值不一致,于是用final来让该引用不可改变)

因为匿名内部类,没名字,是用默认的构造函数的,无参数的,那如果需要参数呢?则需要该类有带参数的构造函数:

public class OuterClass{

public InnerClass getInner(final String name){
return new InnerClass(name){
public String getName() {
return name;
}
};
}

public static void main(String[] args) {
new OuterClass().getInner("HelloWorld").getName();
}
}
//不能是接口,可以为抽象类或普通类
abstract class InnerClass{
InnerClass(String name) {
System.out.println(name);
}
abstract String getName();
} //输出HelloWorld


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

public class OuterClass{

public InnerClass getInner(final String name){
return new InnerClass(){
private String sName;
public String getName() {
sName = name + "World";
return sName;
}
};
}

public static void main(String[] args) {
System.out.println(new OuterClass().getInner("Hello").getName());
}
}
//不能是接口,可以为抽象类或普通类
interface InnerClass{
String getName();
} //输出HelloWorld


内部类继承:指内部类被普通类继承

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

“`

public class OuterClass{

class InnerClass{

}


}

class Son extends OuterClass.InnerClass{

Son(OuterClass outer){
outer.super();
}

public static void main(String[] args) {
OuterClass outer = new OuterClass();
Son son = new Son(outer);
}


}

内部类标识符:

每个类会产生一个.class文件,文件名即为类名,同样,内部类也会产生这么一个.class文件,但是它的名称却不是内部类的类名,而是有着严格的限制:外围类的名字,加上$,再加上内部类名字,而如果是匿名内部类,则是$再加上1,…..,n。

内部类的使用场景和好处:

1.每个内部类都能独立的继承一个接口,无论外部类是否已经继承了某个接口实现,对于内部类都没有影响。

2.方便将存在一定逻辑关系的类组织在一起,又可以对外界隐藏。

3.在实际应用中,需要多继承,如果是两个接口,那么可以直接实现两个接口,而如果是两个类,那么就只有使用内部类了, 因此,内部类使多重继承的解决方案变得更加完整。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: