Java基础--内部类

内部类

在Java中,可以将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类。广泛意义上的内部类一般来说包括这四种:成员内部类、局部内部类、匿名内部类和静态内部类。下面就先来了解一下这四种内部类的用法。

成员内部类

成员内部类就是在一个类的内部在定义一个类,这个内部户类就相当于是这个外部类的一个成员一样

成员内部类可以访问外部类的属性和方法包括private,而外部类要访问内部类的属性或者方法,则需要创个一个内部类对象,通过这个对象来访问内部类的成员

如果内部类和外部类有相同的成员变量,则会发生隐式转换,变量最终会变成内部类的变量,如果非要访问外部类的变量,则需要通过

外部类.this.成员变量
外部类.this.成员方法

public class Student {

    private String name;

    private int age;

    public void getStudent() {

        System.out.println("访问的是外部类, name = " + name + ", age = " + age);

        new Book().getBook();
    }

    class Book {

        private String name = "bookName";

        public void getBook() {

            System.out.println(
                "访问的是内部类方法, Student.name = " + Student.this.name
                    + ", name = " + name + ", age = " + age);
        }

        /**
         *  Getter  method for property  name.
         *
         *  @return property value of name
         */

        public String getName() {
            return name;
        }

        /**
         *  Setter method for property name
         *
         *  @param name value to be assigned to property name
         */

        public void setName(String name) {
            this.name = name;
        }
    }

    /**
     *  Getter  method for property  name.
     *
     *  @return property value of name
     */

    public String getName() {
        return name;
    }

    /**
     *  Setter method for property name
     *
     *  @param name value to be assigned to property name
     */

    public void setName(String name) {
        this.name = name;
    }

    /**
     *  Getter  method for property  age.
     *
     *  @return property value of age
     */

    public int getAge() {
        return age;
    }

    /**
     *  Setter method for property age
     *
     *  @param age value to be assigned to property age
     */

    public void setAge(int age) {
        this.age = age;
    }

    public static void main(String[] args) {

        Student stu = new Student();
        stu.setAge(10);
        stu.setName("student");

        stu.getStudent();
    }
}

结果:

访问的是外部类, name = student, age = 10
访问的是内部类方法, Student.name = student, name = bookName, age = 10

Process finished with exit code 0

成员内部类是依附外部类而存在的,也就是说,如果要创建成员内部类的对象,前提是必须存在一个外部类的对象。创建成员内部类对象的一般方式如下

public class Student {

    private String name;

    private int age;

    private Book book;

    public void getStudent() {

        System.out.println("访问的是外部类, name = " + name + ", age = " + age);

        new Book().getBook();
    }

    public Book getInstance() {

        if (book == null) {

            book = new Book();
        }

        return book;
    }

    class Book {

        private String name = "bookName";

        public void getBook() {

            System.out.println(
                "访问的是内部类方法, Student.name = " + Student.this.name
                    + ", name = " + name + ", age = " + age);
        }

        /**
         *  Getter  method for property  name.
         *
         *  @return property value of name
         */

        public String getName() {
            return name;
        }

        /**
         *  Setter method for property name
         *
         *  @param name value to be assigned to property name
         */

        public void setName(String name) {
            this.name = name;
        }
    }

    /**
     *  Getter  method for property  name.
     *
     *  @return property value of name
     */

    public String getName() {
        return name;
    }

    /**
     *  Setter method for property name
     *
     *  @param name value to be assigned to property name
     */

    public void setName(String name) {
        this.name = name;
    }

    /**
     *  Getter  method for property  age.
     *
     *  @return property value of age
     */

    public int getAge() {
        return age;
    }

    /**
     *  Setter method for property age
     *
     *  @param age value to be assigned to property age
     */

    public void setAge(int age) {
        this.age = age;
    }

    public static void main(String[] args) {

        Student stu = new Student();

        Student.Book book = stu.new Book();

        //或者
        book = stu.getInstance();

    }
}

成员内部类相当于外部类的一个成员属性,也会有访问权限的控制,如果内部类是private,则只能在外部类的内部访问;如果是protected则可以在通过一包内访问;如果是public则都可以访问。

默认是protected,只能在同一个包下访问。

局部内部类

局部内部类就是在一个方法体内或者一个代码块内部定义一个类。

局部内部类相当于一个方法内部的局部变量,没有private、public的感念,只能在方法体内被访问。

public class Student {

    private String name;

    private int age;


    public void getStudent() {

        System.out.println("访问的是外部类, name = " + name + ", age = " + age);

        class Book {

            private String name;

            public void getBook() {

                System.out.println(
                    "访问的是内部类方法");
            }

        }   
    }


    /**
     *  Getter  method for property  name.
     *
     *  @return property value of name
     */

    public String getName() {
        return name;
    }

    /**
     *  Setter method for property name
     *
     *  @param name value to be assigned to property name
     */

    public void setName(String name) {
        this.name = name;
    }

    /**
     *  Getter  method for property  age.
     *
     *  @return property value of age
     */

    public int getAge() {
        return age;
    }

    /**
     *  Setter method for property age
     *
     *  @param age value to be assigned to property age
     */

    public void setAge(int age) {
        this.age = age;
    }

}
匿名内部类

匿名内部类应该是平时我们编写代码时用得最多的

一般使用匿名内部类的方法来编写事件监听代码。

同样的,匿名内部类也是不能有访问修饰符和static修饰符的。

匿名内部类是唯一一种没有构造器的类。正因为其没有构造器,所以匿名内部类的使用范围非常有限,大部分匿名内部类用于接口回调。

匿名内部类在编译的时候由系统自动起名为Outter$1.class。一般来说,匿名内部类用于继承其他类或是实现接口,并不需要增加额外的方法,只是对继承方法的实现或是重写。

/** 这段代码是从网上拷贝的,通常匿名内部类在实现回调接口、监听接口功能中使用最为广泛 */

scan_bt.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub

            }
        });

        history_bt.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub

            }
        });
静态内部类

静态内部类也是定义在另一个类里面的类,只不过在类的前面多了一个关键字static。静态内部类是不需要依赖于外部类的,这点和类的静态成员属性有点类似,并且它不能使用外部类的非static成员变量或者方法,这点很好理解,因为在没有外部类的对象的情况下,可以创建静态内部类的对象,如果允许访问外部类的非static成员就会产生矛盾,因为外部类的非static成员必须依附于具体的对象。

public class Test {
    public static void main(String[] args)  {
        Outter.Inner inner = new Outter.Inner();
    }
}

class Outter {
    public Outter() {

    }

    static class Inner {
        public Inner() {

        }
    }
}

其它知识点

  • 内部类如何访问的外部类的成员

    我们在编译一个还有内部类的外部类时,最终会编译出两个文件

    外部类$内部类.class 和 外部类.class

    对第一个class进行反编译,我们会发现原因所在

    • 内部类会有一个指向外部类引用的指针,通过这个外部类的引用可以访问外部类的成员

    • 即使我们定义的内部类的构造器是无参的,编译器也会给构造器添加一个参数,这个参数就是外部类的一个引用参数,最终在创建内部类对象时候会把一个外部类的引用赋值给内部类,这样内部类就可以通过这个引用访问到外部类,从这个方面可以看到,内部类是要依赖外部类才能存在的。

  • 局部内部类和匿名内部类为什么只能访问final变量

我们首先看下如下这段代码

    public void getStudent(final int a) {

        final int b = 1;

        new Thread(){

            @Override
            public void run() {

                System.out.println(a);

                System.out.println(b);

            }
        }.start();
    }

首先上面的两个变量a/b,如果不加final则编译不过

我们先观察一下上面的代码,当方法执行结束后,局部变量就会立刻消失,而这个时候线程的生命周期极有可能还没有结束,这个时候如果线程要访问变量的值那该怎么办呢?

当编译器发现匿名内部类要依赖局部变量b时,首先会将这个 变量值拷贝一份到匿名内部类的常量池中,这样如果内部类要使用这些变量时,直接就能去常量池拿到值。

如果这个变量是一个表达式或者在编译期间无法确认值怎么办,比如上面例子中的a变量?

我们对匿名内部类反编译会发现这个,这个时候匿名内部类会在构造方法中增加一个参数,其中一个参数是指向外部类的引用参数,另一个参数就是上面的变量a的参数,即以参数的形势将a的值拷贝到匿名内部类中。

也就说如果局部变量的值在编译期间就可以确定,则直接在匿名内部里面创建一个拷贝。如果局部变量的值无法在编译期间确定,则通过构造器传参的方式来对拷贝进行初始化赋值。

以上只是说明了变量值和参数是怎么被匿名内部类使用的,还是没有说明final的问题,我们接着上面的说,a是以参数的形势传进来的,那么当我们在匿名内部类中对a进行修改操作后,会发生什么呢? 没错值不一致,这就是问题所在,因为外部方法调用结束后就退出了,留下了内部类自己在执行,这个时候如果内部类对传进来的变量进行了修改,那就会导致内外的值不一致,因此需要对变量值进行final修饰,确保值不被修改。

  • 静态内部类有特殊的地方吗?

静态内部类是不依赖于外部类的,也就说可以在不创建外部类对象的情况下创建内部类的对象。另外,静态内部类是不持有指向外部类对象的引用的,通过反编译可以发现这个问题。

用途

 1.每个内部类都能独立的继承一个接口的实现,所以无论外部类是否已经继承了某个(接口的)实现,对于内部类都没有影响。内部类使得多继承的解决方案变得完整,

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

最后补充一点知识:关于成员内部类的继承问题。一般来说,内部类是很少用来作为继承用的。但是当用来继承的话,要注意两点:

  1)成员内部类的引用方式必须为 Outter.Inner.

  2)构造器中必须有指向外部类对象的引用,并通过这个引用调用super()

public class Student {

    class Book{

    }

}

class Test extends Student.Book{


    //必须有外部类的引用参数
    public Test(Student stu){

        //必须有这句
        stu.super();
    }

}

如果构造方法中缺少上面标注的两点,则会直接报错

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值