JAVA 第二天 内部类

package com.company;

/**
 * Created by Administrator on 2016/8/23.
 */
public class Outter {//生成的字节码文件是Outter.class
    /**
     * 嵌套的概念
     * 类中除了变量和方法之外,还可以定义一个类
     * 这个类就是内部类,就是嵌套
     *
     * 调用内部类 需要创建内部类的对象
     * */
    private int a=1;     //内部类可以调用外部类的成员,外部类不可以调用内部类的成员。
    private int b=1;
    public void a(){
        System.out.println("外部类a方法");
    }

    class Inner{//生成的字节码文件Outter$Inner.class
        //内部类中的成员与外部类的成员同名的话,则采用就近原则,使用内部类的成员。
        int b=2;
        public void b(){
            int b=3;
            System.out.println("内部类b方法,调用外部类的变量a="+a + "\n内部类调用外部类的a方法:");
            a();
            //如果要调用外部类中与内部类中重名的成员则采用
            System.out.println("外部类中的b="+Outter.this.b);
            System.out.println("内部类中的b="+this.b);
            System.out.println("函数中的b="+b);
        }
    }
}
package com.company;
/**
 * 同一个包下面,普通类创建对象市可以直接调用
 * 但是内部类就需要使用import引入
 * */

//import com.company.Outter.Inner;

public class Main {
    public static void main(String[] args)
    {
        Outter outter=new Outter();
        outter.a();
        /*
        * 不可以通过外部类对象去调用内部类中的变量和方法。
        *
        * 创建内部类对象的错误写法:
        *   Inner a=new Inner(); //错误的写法
        *   没有父类,就没有子类。
        *   同样没有外部类的对象,就不能创建内部类的对象
        *
        * 创建内部类对象的正确方法:
        *     通过外部类的对象创建内部类的对象
        *     写法一:
        *       Outter.Inner b=outter.new Inner();//正确的写法
        *     写法二:
        *       Outter.Inner b=new Outter().new Inner();//正确的写法   匿名的概念
        *       通过这种方式创建的外部类对象没有引用,是不能使用外部类里面的变量和方法
        *
        * */
        Outter.Inner b=new Outter().new Inner();
        b.b();
    }
}

运行效果:

外部类a方法 内部类b方法,调用外部类的变量a=1 内部类调用外部类的a方法: 外部类a方法 外部类中的b=1 内部类中的b=2 函数中的b=3

Process finished with exit code 0

匿名内部类


package com.company;

/**
 * Created by Administrator on 2016/8/23.
 */
public abstract class Computer {
    /**
     * 抽象类不能够创建出抽象类的对象
     * */
    public abstract void Internet();
    public abstract void WatchTV();
}
package com.company;
/**
 * 同一个包下面,普通类创建对象市可以直接调用
 * 但是内部类就需要使用import引入
 * */

//import com.company.Outter.Inner;

public class Main {
    public static void main(String[] args)
    {
        /**
         * 内名内部类
         * 这个下面其实是有一个类存在的,但是我们却看不见类的任何影子 没有名字,也看不到class关键字
         * 这个类没有名字,是一个一次性的
         * 证据就是在编译后的目录中会发现多出来一个名为Main$1.class 这个是系统为它命名的
         * 相当于创建出了一个匿名的内部类,继承了抽象类Computer并且实现了抽象类的抽象方法。
         * 我们创建出的是抽象类的子类的对象
         * */
        Computer computer=new Computer() {
            /**
             * 不能够创建出抽象类对象的说法是正确的
             * 这里是匿名的概念
             * 内名内部类
             * */
            @Override
            public void Internet() {
                System.out.println("我是Computer的子类,我可以上网");
            }

            @Override
            public void WatchTV() {
                System.out.println("我是Computer的子类,我可以看电视");
            }
        };
        computer.Internet();
        computer.WatchTV();
    }
}

执行结果:

我是Computer的子类,我可以上网 我是Computer的子类,我可以看电视

Process finished with exit code 0


接口的匿名内部类的原理也是这样的。应该注意的是:形式上写法是一样的,但是接口是没有构造方法的。

package com.company;

/**
 * Created by Administrator on 2016/8/23.
 */
public interface Standard {
    public final static int a=5;   //前面的都可以省略
    int b=5;
    public abstract void USBStandard();
    void CableStandard();
    void PowerStandard();
}
package com.company;

public class Main {
    public static void main(String[] args) {
        Standard pc=new Standard() {
            @Override
            public void USBStandard() {
                System.out.println("我是USB接口!吼吼!");
            }

            @Override
            public void CableStandard() {
                System.out.println("我是网线接口!吼吼!");
            }

            @Override
            public void PowerStandard() {
                System.out.println("我是电源接口!吼吼!");
            }
        };
        pc.USBStandard();
     pc.CableStandard();
     pc.PowerStandard();

运行结果

我是USB接口!吼吼! 我是网线接口!吼吼! 我是电源接口!吼吼!

Process finished with exit code 0

提起Java内部类(Inner Class)可能很多人不太熟悉,实际上类似的概念在C++里也有,那就是嵌套类(Nested Class),关于这两者的区别与联系,在下文中会有对比。内部类从表面上看, 就是在类中又定义了一个类(下文会看到,内部类可以在很多地方定义),而实际上并没有那么简单,乍看上去内部类似乎有些多余,它的用处对于初学者来说可能并不是那么显著, 但是随着对它的深入了解,你会发现Java的设计者在内部类身上的确是用心良苦。学会使用内部类,是掌握Java高级编程的一部分,它可以让你更优雅地设计你的程序结构。 下面从以下几个方面来介绍: 第一次见面

public interface Contents {  
 int value();  
}  
 
public interface Destination {  
    String readLabel();  
}  
 
public class Goods {  
 private class Content implements Contents {  
 private int i = 11;  
 
 public int value() {  
 return i;  
        }  
    }  
 
 protected class GDestination implements Destination {  
 private String label;  
 
 private GDestination(String whereTo) {  
            label = whereTo;  
        }  
 
 public String readLabel() {  
 return label;  
        }  
    }  
 
 public Destination dest(String s) {  
 return new GDestination(s);  
    }  
 
 public Contents cont() {  
 return new Content();  
    }  
}  
 
class TestGoods {  
 public static void main(String[] args) {  
        Goods p = new Goods();  
        Contents c = p.cont();  
        Destination d = p.dest("Beijing");  
    }  
}  

在这个例子里类Content和GDestination被定义在了类Goods内部,并且分别有着protected和private修饰符来控制访问级别。Content代表着Goods的内容,而GDestination代表着Goods的目的地。它们分别实现了两个接口Content和Destination。在后面的main方法里,直接用 Contents c和Destination d进行操作,你甚至连这两个内部类的名字都没有看见!这样,内部类的第一个好处就体现出来了 隐藏你不想让别人知道的操作,也即封装性。同时,我们也发现了在外部类作用范围之外得到内部类对象的第一个方法,那就是利用其外部类的方法创建并返回。 上例中的cont()和dest()方法就是这么做的。那么还有没有别的方法呢?当然有,其语法格式如下:

outerObject=new outerClass(Constructor Parameters);  
outerClass.innerClass innerObject=outerObject.new InnerClass(Constructor Parameters);   

注意在创建非静态内部类对象时,一定要先创建起相应的外部类对象。至于原因,也就引出了我们下一个话题 非静态内部类对象有着指向其外部类对象的引用,对刚才的例子稍作修改:

public class Goods {  
 private valueRate = 2;  
 
 private class Content implements Contents {  
 private int i = 11 * valueRate;  
 
 public int value() {  
 return i;  
        }  
    }  
 
 protected class GDestination implements Destination {  
 private String label;  
 
 private GDestination(String whereTo) {  
            label = whereTo;  
        }  
 
 public String readLabel() {  
 return label;  
        }  
    }  
 
 public Destination dest(String s) {  
 return new GDestination(s);  
    }  
 
 public Contents cont() {  
 return new Content();  
    }  
}  

修改的部分用蓝色显示了。在这里我们给Goods类增加了一个private成员变量valueRate,意义是货物的价值系数,在内部类Content的方法value()计算价值时把它乘上。 我们发现,value()可以访问valueRate,这也是内部类的第二个好处 一个内部类对象可以访问创建它的外部类对象的内容,甚至包括私有变量! 这是一个非常有用的特性,为我们在设计时提供了更多的思路和捷径。要想实现这个功能,内部类对象就必须有指向外部类对象的引用。 Java编译器在创建内部类对象时,隐式的把其外部类对象的引用也传了进去并一直保存着。 这样就使得内部类对象始终可以访问其外部类对象,同时这也是为什么在外部类作用范围之外向要创建内部类对象必须先创建其外部类对象的原因。 有人会问,如果内部类里的一个成员变量与外部类的一个成员变量同名,也即外部类的同名成员变量被屏蔽了,怎么办?没事,Java里用如下格式表达外部类的引用: outerClass.this  有了它,我们就不怕这种屏蔽的情况了。 静态内部类 和普通的类一样,内部类也可以有静态的。不过和非静态内部类相比,区别就在于静态内部类没有了指向外部的引用。这实际上和C++中的嵌套类很相像了,Java内部类与C++嵌套类最大的不同就在于是否有指向外部的引用这一点上,当然从设计的角度以及以它一些细节来讲还有区别。 除此之外,在任何非静态内部类中,都不能有静态数据,静态方法或者又一个静态内部类(内部类的嵌套可以不止一层)。 不过静态内部类中却可以拥有这一切。这也算是两者的第二个区别吧。 局部内部类  是的,Java内部类也可以是局部的,它可以定义在一个方法甚至一个代码块之内。

public class Goods1 {  
 public Destination dest(String s) {  
 class GDestination implements Destination {  
 private String label;  
 
 private GDestination(String whereTo) {  
                label = whereTo;  
            }  
 
 public String readLabel() {  
 return label;  
            }  
        }  
 return new GDestination(s);  
    }  
 
 public static void main(String[] args) {  
        Goods1 g = new Goods1();  
        Destination d = g.dest("Beijing");  
    }  
}  

上面就是这样一个例子。在方法dest中我们定义了一个内部类,最后由这个方法返回这个内部类的对象。如果我们在用一个内部类的时候仅需要创建它的一个对象并创给外部,就可以这样做。当然,定义在方法中的内部类可以使设计多样化,用途绝不仅仅在这一点。
下面有一个更怪的例子:
[java] view plain copy 
public class Goods2 {  
 private void internalTracking(boolean b) {  
 if (b) {  
 class TrackingSlip {  
 private String id;  
 
                TrackingSlip(String s) {  
                    id = s;  
                }  
 
                String getSlip() {  
 return id;  
                }  
            }  
            TrackingSlip ts = new TrackingSlip("slip");  
            String s = ts.getSlip();  
        }  
    }  
 
 public void track() {  
        internalTracking(true);  
    }  
 
 public static void main(String[] args) {  
        Goods2 g = new Goods2();  
        g.track();  
    }  
}  

你不能在if之外创建这个内部类的对象,因为这已经超出了它的作用域。不过在编译的时候,内部类TrackingSlip和其他类一样同时被编译,只不过它由它自己的作用域,超出了这个范围就无效,除此之外它和其他内部类并没有区别。 匿名内部类  java的匿名内部类的语法规则看上去有些古怪,不过如同匿名数组一样,当你只需要创建一个类的对象而且用不上它的名字时,使用内部类可以使代码看上去简洁清楚。它的语法规则是这样的: new interfacename(){......}; 或 new superclassname(){......};  下面接着前面继续举例子:

public class Goods3 {  
 public Contents cont() {  
 return new Contents() {  
 private int i = 11;  
 
 public int value() {  
 return i;  
            }  
        };  
    }  
}   

这里方法cont()使用匿名内部类直接返回了一个实现了接口Contents的类的对象,看上去的确十分简洁。 在java的事件处理的匿名适配器中,匿名内部类被大量的使用。例如在想关闭窗口时加上这样一句代码:

frame.addWindowListener(new WindowAdapter(){  
public void windowClosing(WindowEvent e){  
System.exit(0);   
}  
}); 

有一点需要注意的是,匿名内部类由于没有名字,所以它没有构造函数(但是如果这个匿名内部类继承了一个只含有带参数构造函数的父类,创建它的时候必须带上这些参数,并在实现的过程中使用super关键字调用相应的内容)。如果你想要初始化它的成员变量,有下面几种方法: 如果是在一个方法的匿名内部类,可以利用这个方法传进你想要的参数,不过记住,这些参数必须被声明为final。 将匿名内部类改造成有名字的局部内部类,这样它就可以拥有构造函数了。  在这个匿名内部类中使用初始化代码块。  为什么需要内部类?  java内部类有什么好处?为什么需要内部类? 首先举一个简单的例子,如果你想实现一个接口,但是这个接口中的一个方法和你构想的这个类中的一个方法的名称,参数相同,你应该怎么办?这时候,你可以建一个内部类实现这个接口。由于内部类对外部类的所有内容都是可访问的,所以这样做可以完成所有你直接实现这个接口的功能。 不过你可能要质疑,更改一下方法的不就行了吗? 的确,以此作为设计内部类的理由,实在没有说服力。 真正的原因是这样的,java中的内部类和接口加在一起,可以的解决常被C++程序员抱怨java中存在的一个问题 没有多继承。实际上,C++的多继承设计起来很复杂,而java通过内部类加上接口,可以很好的实现多继承的效果。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏闪电gogogo的专栏

Python入门学习(二)

1 字典 1.1 字典的创建和访问 字典不同于前述的序列类型,它是一种映射类型。它的引入是为了简化定义索引值和元素值存在特定关系的定义和访问问题。 字典的定义形...

32780
来自专栏Java架构师历程

精选30道Java笔试题解答

都是一些非常非常基础的题,是我最近参加各大IT公司笔试后靠记忆记下来的,经过整理献给与我一样参加各大IT校园招聘的同学们,纯考Java基础功底,老手们就不用进来...

36140
来自专栏用户2442861的专栏

JAVA反射机制作用是什么

Java的反射机制是Java特性之一,反射机制是构建框架技术的基础所在。灵活掌握Java反射机制,对大家以后学习框架技术有很大的帮助。

1.7K20
来自专栏進无尽的文章

基础篇-ObjectC继承、类别、属性

    在实际的开发过程中,继承和类别都会得到很多用处。对于界面相似度很高的情况下,使用继承可以节省很多代码和设置,只需要在子类中重写父类中的方法,或者增加新的...

8610
来自专栏calmound

sscanf

sscanf与scanf类似,都是用于输入的,只是后者以键盘(stdin)为输入源,前者以固定字符串为输入源。    第一个参数可以是一个或多个 {%[*]...

54160
来自专栏java一日一条

Java中关于String类型的10个问题

简单来说,“==”是用来检测俩引用是不是指向内存中的同一个对象,而equals()方法则检测的是两个对象的值是否相等。只要你想检测俩字符串是不是相等的,你就必须...

7910
来自专栏灯塔大数据

技术 | Python从零开始系列连载(十一)

导读 为了解答大家初学Python时遇到各种常见问题,小灯塔特地整理了一系列从零开始的入门到熟练的系列连载,每周五准时推出,欢迎大家学积极习转载~ 上一期学习了...

403100
来自专栏架构之路

JAVA基础知识点:内存、比较和Final

1.java是如何管理内存的 java的内存管理就是对象的分配和释放问题。(其中包括两部分) 分配:内存的分配是由程序完成的,程序员需要通过关键字new为每个对...

48140
来自专栏python3

python3--面向对象进阶之内置方法

print执行时,是去内部寻找__str__方法,所以print没有输出不了的数据,因为每一个对象都有__str__方法

9110
来自专栏Zephery

2017-03-03学习笔记

一、String StringBuffer.toString()源码: @Override public synchronized String toStrin...

363110

扫码关注云+社区

领取腾讯云代金券