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

浅析总结 Java 内部类的一些使用与梳理

2015-02-04 20:44 393 查看

有这篇文章,纯属巧合;那天在使用中突然发现 Java 内部类中还分 static ,说实话平时都在用,但是就是没有注意到;感觉有必要总结一下。

有必要说一下的是本文纯属浅析,如有补充还请在评论中指出,欢迎总结。

内部类的位置

[java]
view plaincopyprint?





public class A { 

    class B { 
 
    } 
 
    public void pint() { 

        class C { 

        } 
        new C(); 

    } 
 
    public void pint(boolean b) { 

        if (b) { 

            class D { 
            } 
            new D(); 
        } 
    } 


public class A {
class B {

}

public void pint() {
class C {
}
new C();
}

public void pint(boolean b) {
if (b) {
class D {
}
new D();
}
}
}
从代码中可以看出,内部类可以定义到很多地方,常用的是成员变量中(B),方法中也叫局部内部类(C),作用域中(D)

从上面来看似乎没有用到过在方法中和作用域中的情况啊,这就错了;再来看看这个:

[java]
view plaincopyprint?





public interface AInterface { 

    void show(); 

public class B { 

 
    public void show() { 

        class Man
implements AInterface { 
            @Override 
            public
void show() { 
 
            } 
 
        } 
        Man man = new Man(); 

        man.show(); 
    } 
 


public interface AInterface {
void show();
}
public class B {

public void show() {
class Man implements AInterface {
@Override
public void show() {

}

}
Man man = new Man();
man.show();
}

}
其中我们定义了两个文件,一个文件是一个接口类,一个是B文件;在B类中,的 show()方法中我们使用了局部内部类的方式创建了类 ManMan class继承接口并实现方法,随后使用该类。

内部类的权限

为什么要有内部类的存在?

在我看来类主要的就是封装、继承、多态;当然其中的回调思想我认为是很重要的;而内部类的出现就是为了简化多重继承的问题;一个A类,并不能继承多个其他类,但是在使用中又需要使用到其他类的方法,这个时候内部类就发挥作用了;典型的就是事件点击的回调实现。

那么内部类的权限究竟有多大?

至于答案是什么,代码上看看就知道了。

[java]
view plaincopyprint?





public class C { 

    int a = 1; 

    private int b =
2
4000

    protected int c =
3; 
    public int d =
4; 
 
    void a() { 
        System.out.println("A:" + a); 

    } 
 
    private void b() { 

        System.out.println("B:" + b); 

    } 
 
    protected void c() { 

        System.out.println("C:" + c); 

    } 
 
    public void d() { 

        System.out.println("D:" + d); 

    } 
 
    class D { 
 
        void show() { 

            int max = a + b + c + d; 

            a(); 
            b(); 
            c(); 
            d(); 
            System.out.println("Max:" + max); 

        } 
    } 
 
    public static
void main(String[] args) { 
        D d = new C().new D(); 

        d.show(); 
    } 


public class C {
int a = 1;
private int b = 2;
protected int c = 3;
public int d = 4;

void a() {
System.out.println("A:" + a);
}

private void b() {
System.out.println("B:" + b);
}

protected void c() {
System.out.println("C:" + c);
}

public void d() {
System.out.println("D:" + d);
}

class D {

void show() {
int max = a + b + c + d;
a();
b();
c();
d();
System.out.println("Max:" + max);
}
}

public static void main(String[] args) {
D d = new C().new D();
d.show();
}
}
运行结果:



可以看出,内部类 D 对类 C 具有完整的访问权限,等于全身脱光了给你看。

那要是反过来呢?

[java]
view plaincopyprint?





public class C { 

    class D { 
        private int a =
20; 
        private void a(){ 

            System.out.println("D.A:" + a); 

        } 
    } 
     
    void show(){ 

        D d = new D(); 
        d.a(); 
         
        System.out.println("D.A:" + d.a); 

    } 
 
    public static
void main(String[] args) { 
        new C().show(); 

    } 


public class C {
class D {
private int a = 20;
private void a(){
System.out.println("D.A:" + a);
}
}

void show(){
D d = new D();
d.a();

System.out.println("D.A:" + d.a);
}

public static void main(String[] args) {
new C().show();
}
}
运行结果:



可见也是完全可行的,也能直接访问私有属性 私有方法,在这里似乎私有的限制已经失效了一般,这个让我想起了以前看见过一个面试:在 Java 中 private 修饰何时会失效。

这完全是两个人互相脱光光了啊~

匿名内部类

这个非常常见,特别是在按钮点击事件绑定中。

[java]
view plaincopyprint?





public class D { 

    void initButton() { 
        Button b1 = new Button(); 

        b1.setOnClickListener(new OnClickListener() { 

 
            @Override 
            public
void onClick(Button v) { 
 
            } 
        }); 
 
        Button b2 = new Button(); 

        b2.setOnClickListener(new OnClickListener() { 

 
            @Override 

            public void onClick(Button v) { 

 
            } 
        }); 
    } 
 


public class D {
void initButton() {
Button b1 = new Button();
b1.setOnClickListener(new OnClickListener() {

@Override
public void onClick(Button v) {

}
});

Button b2 = new Button();
b2.setOnClickListener(new OnClickListener() {

@Override
public void onClick(Button v) {

}
});
}

}
其中的:

[java]
view plaincopyprint?





  new OnClickListener() { 

 
    @Override 

    public void onClick(Button v) { 

 
    } 


new OnClickListener() {

@Override
public void onClick(Button v) {

}
}
就是匿名内部类的使用方式,OnClickListener 是一个接口类,接口类是无法直接new 一个实例的;这里也并不是那样,而是new 了一个其他的类,该类是匿名的,也就是没有名字,只不过该类实现了 OnClickListener接口类中的方法。

上面的添加回调部分可等同于:

[java]
view plaincopyprint?





public class D { 

    void initButton1() { 
        Button b1 = new Button(); 

        b1.setOnClickListener(new Listener1()); 

 
        Button b2 = new Button(); 

        b2.setOnClickListener(new Listener2()); 

    } 
 
    class Listener1 implements OnClickListener { 

 
        @Override 
        public void onClick(Button v) { 

 
        } 
    } 
 
    class Listener2 implements OnClickListener { 

 
        @Override 
        public void onClick(Button v) { 

 
        } 
    } 
 


public class D {
void initButton1() {
Button b1 = new Button();
b1.setOnClickListener(new Listener1());

Button b2 = new Button();
b2.setOnClickListener(new Listener2());
}

class Listener1 implements OnClickListener {

@Override
public void onClick(Button v) {

}
}

class Listener2 implements OnClickListener {

@Override
public void onClick(Button v) {

}
}

}
这里就是先建立类,继承自接口;而后赋值到 Button 中。

要说两者的区别与好处,这个其实看具体的使用情况吧;如果你的按钮很多,但是为了避免建立太多类;那么可以建立一个回调类,然后都赋值给所有的按钮,不过最后就是需要在 onClick方法中进行判断是那个按钮进行的点击。

匿名内部类的使用地方很多;具体的使用应视使用情况而定~

静态内部类/静态嵌套类

这个其实并不应该叫做内部类了,因为其并不具备内部类的完全权限,在使用上与一般的类基本一样;那为什么会有这个的存在?

在我看来这个类的存在是为其包括类服务;意思是可以单独服务,不被外面的类所知晓;如这样:

[java]
view plaincopyprint?





public class E { 

    private void show(){ 

        new A(); 

    } 
     
    private static
class A{ 
         
    } 


public class E {
private void show(){
new A();
}

private static class A{

}
}
其中类 A 使用了 static ,所以是静态嵌套类,在这里使用private 修饰;那么该类只能在 E 类中进行实例化;无法在 其他文件中实例化。

这样的情况使用外面的类能行么?不行吧?也许你会说在 E.java 文件夹中建立 A.java ,并使用protected修饰;但是在同样的包下,或者继承的类中同样能访问了;这也只是其中一个较为特殊的情况。

我们来看看权限

[java]
view plaincopyprint?





public class E { 

    int a1 = 0; 

    private int a2 =
0; 
    protected int a3 =
0; 
    public int a4 =
0; 
     
    private void show(){ 

        A a =new A(); 
        System.out.print("b1:"+a.b1); 

        System.out.print("b2:"+a.b2); 

        System.out.print("b3:"+a.b3); 

        System.out.print("b4:"+a.b4); 

         
    } 
     
    private static
class A{ 
        int b1 =
0; 
        private int b2 =
0; 
        protected
int b3 = 0; 

        public int b4 =
0; 
         
        private void print(){ 

            System.out.print("a1:"+a1); 

            System.out.print("a2:"+a2); 

            System.out.print("a3:"+a3); 

            System.out.print("a4:"+a4); 

 
        } 
    } 


public class E {
int a1 = 0;
private int a2 = 0;
protected int a3 = 0;
public int a4 = 0;

private void show(){
A a =new A();
System.out.print("b1:"+a.b1);
System.out.print("b2:"+a.b2);
System.out.print("b3:"+a.b3);
System.out.print("b4:"+a.b4);

}

private static class A{
int b1 = 0;
private int b2 = 0;
protected int b3 = 0;
public int b4 = 0;

private void print(){
System.out.print("a1:"+a1);
System.out.print("a2:"+a2);
System.out.print("a3:"+a3);
System.out.print("a4:"+a4);

}
}
}
在这个中的结果是怎样?



从图片中可以看出,其权限级别是单方向的;静态嵌套类 A 对其包含类 E 完全透明;但 E 并不对 A 透明。

再来看看方法:



可以看出同样的情况;这个是为什么呢?为什么就是多一个 static 的修饰就这么完全不同?其是很好理解,两个独立的类;本来就无法直接使用,必须有引用才能调用其属性与方法。

我们或许可以这么调整一下就OK

[java]
view plaincopyprint?





public class E { 

    int a1 = 0; 

    private int a2 =
0; 
    protected int a3 =
0; 
    public int a4 =
0; 
 
    private void show() { 

        A a = new A(); 
        System.out.print("b1:" + a.b1); 

        System.out.print("b2:" + a.b2); 

        System.out.print("b3:" + a.b3); 

        System.out.print("b4:" + a.b4); 

 
        a.b1(); 
        a.b2(); 
        a.b3(); 
        a.b4(); 
    } 
 
    void a1() { 
 
    } 
 
    private void a2() { 

 
    } 
 
    protected void a3() { 

 
    } 
 
    public void a4() { 

 
    } 
 
    private static
class A { 
        int b1 =
0; 
        private int b2 =
0; 
        protected
int b3 = 0; 

        public int b4 =
0; 
 
        void b1() { 
 
        } 
 
        private void b2() { 

 
        } 
 
        protected void b3() { 

 
        } 
 
        public void b4() { 

 
        } 
 
        private void print(E e) { 

            System.out.print("a1:" + e.a1); 

            System.out.print("a2:" + e.a2); 

            System.out.print("a3:" + e.a3); 

            System.out.print("a4:" + e.a4); 

 
            e.a1(); 
            e.a2(); 
            e.a3(); 
            e.a4(); 
        } 
    } 


public class E {
int a1 = 0;
private int a2 = 0;
protected int a3 = 0;
public int a4 = 0;

private void show() {
A a = new A();
System.out.print("b1:" + a.b1);
System.out.print("b2:" + a.b2);
System.out.print("b3:" + a.b3);
System.out.print("b4:" + a.b4);

a.b1();
a.b2();
a.b3();
a.b4();
}

void a1() {

}

private void a2() {

}

protected void a3() {

}

public void a4() {

}

private static class A {
int b1 = 0;
private int b2 = 0;
protected int b3 = 0;
public int b4 = 0;

void b1() {

}

private void b2() {

}

protected void b3() {

}

public void b4() {

}

private void print(E e) {
System.out.print("a1:" + e.a1);
System.out.print("a2:" + e.a2);
System.out.print("a3:" + e.a3);
System.out.print("a4:" + e.a4);

e.a1();
e.a2();
e.a3();
e.a4();
}
}
}
在其静态类中传递一个 E 的引用进去就能解决问题了:



可以看出其中现在并没有报错了;能正常运行。

两者之间的隐藏区别

但是最开始上面的内部类是怎么回事?难道是闹鬼了?上面的内部类没有传递引用的啊;为啥加上一个 static 就不行了?

在这里我们需要看看字节码,我们先建立一个简单的内部类:

[java]
view plaincopyprint?





public class F { 

     
    class A{ 
         
    } 


public class F {

class A{

}
}
这个够简单吧?别说这个都难了;汗~

然后我们找到 class 文件,然后查看字节码:



在这里分别查看了 F 类的字节码和 F$A 类的字节码。

其中有这样的一句:
final F this$0; 这句是很重要的一句,这句出现的地方在其内部类中,意思是当你 new 一个内部类的时候就同时传递了当前类进去;所以在内部类中能具有当前类的完全权限,能直接使用所有的东西;就是因为在隐藏情况下已经传递了当前类进去。

那么我们再看看一个简单的静态内部类:

[java]
view plaincopyprint?





public class G { 

    static class A { 

 
    } 


public class G {
static class A {

}
}
与上面的区别唯一就是在于添加了一个 static 。此时我们看看字节码:



可以看出其中无论是 G 类,还是 G$A 类的初始化中都没有其他多余的部分,也没有进行隐藏的传递进去当前类;所以这样的情况下并不具备访问权限,需要我们传递引用进去,可以通过接口也可以完全传递进去,具体取决于个人。所以加了static类的内部类除了在权限上比一般的类更加开放(与其包含类)外,与一般的类在使用上是一样的;所以准确的说应该叫做静态嵌套类。

初始化的区别

一个类中,同时包含了内部类与静态内部类,那么其初始化应该是怎么样的呢?

都是直接 new ?还是看看代码:

[java]
view plaincopyprint?





public class H { 

    int a = 1; 

 
    public class A { 

        public void Show() { 

            System.out.print("a:" + a); 

        } 
    } 
 
    public static
class B { 
        public void Show(H h) { 

            System.out.print("a:" + h.a); 

        } 
    } 
 
    public static
void main(String[] args) { 
        H h = new H(); 

        //A a = new A(); 
        A a1 = h.new A(); 

        B b = new B(); 
        //B b1 = h.new B(); 

        B b3 = new H.B(); 
    } 


public class H {
int a = 1;

public class A {
public void Show() {
System.out.print("a:" + a);
}
}

public static class B {
public void Show(H h) {
System.out.print("a:" + h.a);
}
}

public static void main(String[] args) {
H h = new H();
//A a = new A();
A a1 = h.new A();
B b = new B();
//B b1 = h.new B();
B b3 = new H.B();
}
}
其中注释了的两种方式是不允许的方式,也就是无法正常运行。

A 因为有一个隐藏的引用,所以必须是H 的实例才能进行初始化出A 类;而B 类则是因为是在H 类中以静态方式存在的类,所以需要 new H.B();之所以能直接使用new B(),与该
main
方法在 H 类中有关,因为本来就在 H类中,所以直接使用 H类的静态属性或者方法可以不加上:“H.”  在前面。

内部类的继承

直接继承的情况:



可以看出报错了,为什么?因为需要传递一个 H 类进去,所以我们在继承的时候需要显示的指明:

[java]
view plaincopyprint?





public class I
extends H.A{ 
    public I(H h){ 
        h.super(); 

    } 


public class I extends H.A{
public I(H h){
h.super();
}
}
也就是在构造方法中,传递一个 H 的引用进去,并调用 H 实例的 super() 方法,才能进行实例化。

使用的话应该这样:

[java]
view plaincopyprint?





public static
void main(String[] args) { 
       H h = new H(); 
       I i = new I(h); 

   } 

public static void main(String[] args) {
H h = new H();
I i = new I(h);
}


而,如果是继承其静态嵌套类,则不需要这样:

[java]
view plaincopyprint?





public class J
extends H.B{ 
 


public class J extends H.B{

}
就这样就OK。

哎,差不多了~~整个内部类的东西差不多就是这些了,写了我3个小时42分钟~汗!!!!

如果有没有写到的地方,还请补充~~

不对的地方还请指正~
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: