java 内部类(inner class)详解

一、为何使用内部类

  • 内部类提供了更好的封装,只有外部类能访问内部类
  • 内部类可以独立继承一个接口,不受外部类是否继承接口影响
  • 内部类中的属性和方法即使是外部类也不能直接访问,相反内部类可以直接访问外部类的属性和方法,即使private
  • 利于回调函数的编写

一个内部类的例子

public class OuterClass {
    private String outerName;
    private int outerAge;
    public class InnerClass{
        private String innerName;
        private int innerAge;
    }
}

二、内部类与外部类的联系

2.1 内部类是一个相对独立的实体,与外部类不是is-a关系

内部类是一个编译时概念,编译后外部类及其内部类会生成两个独立的class文件: OuterClass.classOuterClass$InnerClass.class,我用javac编译器对上面的OuterClass进行编译:

 D:\>javac OuterClass.class

编译后的结果:

2.2 内部类可以直接访问外部类的元素,但是外部类不可以直接访问内部类的元素

public class OuterClass {

    private String outerName;
    private int outerAge;

    public class InnerClass{
        private int innerName;
        InnerClass(){
            //内部类可以访问外部类的元素
            outerName="I am outer class";
            outerAge=23;
        }
        public void display(){
            System.out.println(outerName+" and my age is "+outerAge);
        }
    }
    public static void main(String[] args) {
        OuterClass outerClass = new OuterClass();
        OuterClass.InnerClass innerClass = outerClass.new InnerClass();
        innerClass.display();
    }
}

在上面例子中我们可以看到,内部类可以直接访问外部类属性,尽管外部类属性是用private修饰的。这是因为在创建外部类时,内部类会自动捕获一个外部类的引用,所以内部类访问外部类元素,实际上是通过他所持有外部类引用访问的。在java中,我们可以通过OuterClass.this来获得外部类的引用,请看下面例子:

public class OuterClass {
    public void display(){
        System.out.println("this is OuterClass...");
    }
    public class InnerClass{
        //获取外部类的引用
        public OuterClass getOuterClass(){
            return OuterClass.this;
        }
        public void innerDisplay(){
            //内部类也可以通过外部类的引用访问外部元素
            getOuterClass().display();
        }
    }
    public static void main(String[] args) {
        OuterClass outerClass = new OuterClass();
        OuterClass.InnerClass innerClass = outerClass.new InnerClass();
        innerClass.innerDisplay();
    }
}

2.3 外部类可以通过内部类引用间接访问内部类元素

public class OuterClass {
    public void display(){
        //外部类访问内部类元素,需要通过内部类引用访问
        InnerClass innerClass=new InnerClass();
        innerClass.innerDisplay();
    }
    public class InnerClass{
        public void innerDisplay(){
            System.out.println("I am inner class");
        }
    }
    public static void main(String[] args) {
        OuterClass outerClass=new OuterClass();
        outerClass.display();
    }
}

三、创建内部类

3.1 在外部类外面(或外部类main方法)创建内部了对象

其实上面2.2例子中我们已经看到了如何创建内部类。如果要创建一个内部类对象,必须利用outerClass.new来创建:

OuterClass outerClass = new OuterClass();
OuterClass.InnerClass innerClass = outerClass.new InnerClass();

其实我们还可以一步到位:

OuterClass.InnerClass innerClass=new OuterClass().new InnerClass();

内部类创建方法示例:

public static void main(String[] args) {
    //先创建外部类对象,再创建内部类对象
    OuterClass outerClass = new OuterClass();
    OuterClass.InnerClass innerClass1 = outerClass.new InnerClass();
    innerClass1.innerDisplay();
    //一步到位创建
    OuterClass.InnerClass innerClass2=new OuterClass().new InnerClass();
    innerClass2.innerDisplay();
}

3.2 在外部类里面创建内部类

正如2.3代码中display()方法那样,在外部类里面创建内部类,就像创建普通对象一样直接创建:

InnerClass innerClass=new InnerClass()

四、内部类的种类:

在Java中内部类主要分为成员内部类、方法内部类、匿名内部类、静态内部类。

4.1 成员内部类

成员内部类也是最普通的内部类,它是外部类的一个成员,所以他是可以无限制的访问外围类的所有成员属性和方法,尽管是private的,但是外围类要访问内部类的成员属性和方法则需要通过内部类实例来访问。

在成员内部类中要注意两点:

  1. 成员内部类中不能存在任何static的变量和方法
  2. 成员内部类是依附于外围类的,所以只有先创建了外围类才能够创建内部类

4.2 方法内部类

方法内部类定义在外部类的方法中,局部内部类和成员内部类基本一致,只是它们的作用域不同,方法内部类只能在该方法中被使用,出了该方法就会失效。 对于这个类的使用主要是应用与解决比较复杂的问题,想创建一个类来辅助我们的解决方案,到那时又不希望这个类是公共可用的,所以就产生了局部内部类。

4.3 匿名内部类

匿名内部类其实就是一个没有名字的方法内部类,所以它符合方法内部类的所有约束,初次之外,还有一些地方需要注意:

  1. 匿名内部类是没有访问修饰符的。
  2. 匿名内部类必须继承一个抽象类或者实现一个接口
  3. 匿名内部类中不能存在任何静态成员或方法
  4. 匿名内部类是没有构造方法的,因为它没有类名。

一般使用匿名内部类的场景是,要继承或实现的接口只有一个抽象方法,比如添加一个监听器:

public class Button {
    public void click(){
        //匿名内部类,实现的是ActionListener接口
        new ActionListener(){
            public void onAction(){
                System.out.println("click action...");
            }
        }.onAction();
    }
    //匿名内部类必须继承或实现一个已有的接口
    public interface ActionListener{
        public void onAction();
    }

    public static void main(String[] args) {
        Button button=new Button();
        button.click();
    }
}

4.4 静态内部类

关键字static可以修饰成员变量、方法、代码块,其实它还可以修饰内部类,使用static修饰的内部类我们称之为静态内部类。静态内部类与非静态内部类之间存在一个最大的区别,我们知道非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外围内,但是静态内部类却没有。没有这个引用就意味着:

  1. 静态内部类的创建是不需要依赖于外围类,可以直接创建
  2. 静态内部类不可以使用任何外围类的非static成员变量和方法,而内部类则都可以
    public class OuterClass {
        private static String outerName;
        public  int age;
    
        static class InnerClass1{
            /* 在静态内部类中可以存在静态成员 */
            public static String _innerName = "static variable";
            public void display(){
                /*
                 * 静态内部类只能访问外部类的静态成员变量和方法
                 * 不能访问外部类的非静态成员变量和方法
                 */
                System.out.println("OutClass name :" + outerName);
            }
        }
        class InnerClass2{
            /* 非静态内部类中不能存在静态成员 */
            public String _innerName = "no static variable";
            /* 非静态内部类中可以调用外部类的任何成员,不管是静态的还是非静态的 */
            public void display(){
                System.out.println("OuterClass name:" + outerName);
                System.out.println("OuterClass age:" + age);
            }
        }
        public void display(){
            /* 外部类能直接访问静态内部类静态元素 */
            System.out.println(InnerClass1._innerName);
            /* 静态内部类可以直接创建实例不需要依赖于外部类 */
            new InnerClass1().display();
            /* 非静态内部的创建需要依赖于外部类 */
            OuterClass.InnerClass2 inner2 = new OuterClass().new InnerClass2();
            /* 非静态内部类的成员需要使用非静态内部类的实例访问 */
            System.out.println(inner2._innerName);
            inner2.display();
        }
    
        public static void main(String[] args) {
            OuterClass outer = new OuterClass();
            outer.display();
        }
    }
    
时间: 2024-12-20 22:40:54

java 内部类(inner class)详解的相关文章

Java中final关键字详解_php技巧

谈到final关键字,想必很多人都不陌生,在使用匿名内部类的时候可能会经常用到final关键字.另外,Java中的String类就是一个final类,那么今天我们就来了解final这个关键字的用法. 主要介绍:一.final关键字的基本用法.二.深入理解final关键字 一.final关键字的基本用法 在Java中,final关键字可以用来修饰类.方法和变量(包括成员变量和局部变量).下面就从这三个方面来了解一下final关键字的基本用法. 1.修饰类 当用final修饰一个类时,表明这个类不能

Tomcat与Java Web开发技术详解连载之一

web|详解 本章介绍如何在Tomcat上创建和发布Web应用.这里首先讲解Tomcat的目录结构以及Web应用的目录结构,接着介绍如何将HTML.Servlet.JSP和Tag Library部署到Web应用中,然后介绍把整个Web应用打包并发布的方法,最后介绍如何在Tomcat上配置虚拟主机. 本章侧重于讨论Web应用的结构和发布方法,所以没有对本章的Servlet和JSP的例子进行详细解释,关于Servlet和JSP的技术可以分别参考其它章节的内容. 2.1 Tomcat的目录结构 在To

Tomcat与Java Web开发技术详解连载之二

web|详解 2.2.4 部署HTML文件 在helloapp目录下加入index.htm文件,这个文件仅仅用来显示一串带链接的字符"Welcome to HelloApp", 它链接到login.jsp文件.以下是index.htm文件的代码: <html><head><title>helloapp</title></head><body ><p><font size="7"

Tomcat与Java Web开发技术详解连载之三

web|详解 2.2.8 创建并发布WAR文件 Tomcat既可以运行采用开放式目录结构的Web应用,也可以运行WAR文件.在本书配套光盘的sourcecode/chapter2/helloapp目录下提供了所有源文件,只要把整个helloapp目录拷贝到/webapps目录下,即可运行开放式目录结构的helloapp应用.在Web应用的开发阶段,为了便于调试,通常采用开放式的目录结构来发布Web应用,这样可以方便地更新或替换文件.如果开发完毕,进入产品发布阶段,应该将整个Web应用打包为WAR

怎样使用Java Servlet动态生成图片详解

servlet|动态|详解 在Web应用中,经常需要动态生成图片,比如实时股市行情,各种统计图等等,这种情况下,图片只能在服务器内存中动态生成并发送给用户,然后在浏览器中显示出来. 本质上,浏览器向服务器请求静态图片如JPEG时,服务器返回的仍然是标准的http响应,只不过http头的contentType不是text/html,而是image/jpeg而已,因此,我们在Servlet中只要设置好contentType,然后发送图像的数据流,浏览器就能正确解析并显示出图片. 在Java中,jav

Java 中的注解详解及示例代码_java

在Java中,注解(Annotation)引入始于Java5,用来描述Java代码的元信息,通常情况下注解不会直接影响代码的执行,尽管有些注解可以用来做到影响代码执行. 注解可以做什么 Java中的注解通常扮演以下角色 编译器指令 构建时指令 运行时指令 其中 Java内置了三种编译器指令,本文后面部分会重点介绍 Java注解可以应用在构建时,即当你构建你的项目时.构建过程包括生成源码,编译源码,生成xml文件,打包编译的源码和文件到JAR包等.软件的构建通常使用诸如Apache Ant和Mav

Java Serializable和Parcelable详解及实例代码_java

对 Serializable和Parcelable理解 1.首先他们两个接口都是为了实现对象的序列化,使之可以传递,所谓序列化就是将对象信息装换成可以存储的介质的过程. 2.Serializable是jdk所提供的序列化接口,该接口存在于io包下,可想用于输入输出,使用非常简单,只要让你的类实现此接口就ok了:可以使用transient关键字修饰你不想序列化的属性. 3.Parcelable是sdk所提供的序列化接口,使用较上者麻烦,实现此接口后,需要重写writeToParcel方法,将需要序

Java RandomAccessFile的用法详解_java

RandomAccessFile RandomAccessFile是用来访问那些保存数据记录的文件的,你就可以用seek( )方法来访问记录,并进行读写了.这些记录的大小不必相同:但是其大小和位置必须是可知的.但是该类仅限于操作文件. RandomAccessFile不属于InputStream和OutputStream类系的.实际上,除了实现DataInput和 DataOutput接口之外(DataInputStream和DataOutputStream也实现了这两个接口),它和这两个类系毫

java基础之:详解内部类(转载)

可以将一个类的定义放在另一个类的定义内部,这就是内部类.        内部类是一个非常有用的特性但又比较难理解使用的特性(鄙人到现在都没有怎么使用过内部类,对内部类也只是略知一二).        第一次见面        内部类我们从外面看是非常容易理解的,无非就是在一个类的内部在定义一个类.   [java] view plain copy print? public class OuterClass {       private String name ;       private 

Docker搭建前端Java的开发环境详解_java

一.解决的痛点       1.免搭建后端开发环境.       2.开发环境改变只需要改变镜像就能同步更新.       3.不需要eclipse等IDE工具.       4.切换开发项目 二.解决思路 利用docker启动Ubuntu镜像,在容器中搭建好项目需要的开发环境,使用挂载卷将本地代码挂载到容器中,使用容器中的环境编译运行代码,宿主机通过 docker 暴漏出的端口访问容器中的服务,这样前端的开发机上就只需要部署docker就搞定了. 三.关于docker 了解docker 本文并