浅谈java内部类

浅谈java内部类

前言:这篇文章是在我自己看java核心技术这一书关于内部类的内容后的总结,如果有不对的地方请指正!
  • 定义:

内部类(inner class)是定义在另一个类当中的类。内部类又分为普通内部类,局部内部类,匿名内部类以及静态内部类。

  • 为什么使用内部类?
  1. 内部类方法可以访问该类所在的作用域中的数据,包括私有数据。内部类的常量必须用final关键字修饰
  2. 内部类可以对同一个包中的其他类隐藏起来
  3. 当想要定义一个回掉函数并且不想编写大量代码,可以使用匿名内部类更加便捷。
  1. 普通内部类
import java.util.Date;

class People{
    private int age;
    private String name;
    
    public People(int age, String name) {
        super();
        this.age = age;
        this.name = name;
    }   
    class PeopleClock{
      //final static String clockName = "gucci";静态变量必须用final关键字声明
        public void peopleGetTime(){
            System.out.println(name+" get time:"+new Date());
          // System.out.println(People.this.name+" get time:"+new Date());这样也是同一个意思
        }
    }
}

public class MyInnerClassTest {
    public static void main(String[] args) {
        People jack = new People(12,"jack");
        People.PeopleClock clock = jack.new PeopleClock();
        clock.peopleGetTime();
    }
}
总结:
  1. 我们可以看到的是,PeopleClock类可以访问外部类的private的属性,这是其他类做不到的,因为private是私有的。另外OuterClass.this.field == field,OuterClass.this就是为外部类对象。在上述例子当中 People.this.name == name,同时在创建内部类的对象时先创建外部类对象在创建内部类对象即可,如同上述例子main方法中所示。

  2. 内部类如何持有外部对象变量?:
    可以经过反编译看到在普通内部类的构造方法当中传入了外部类对象,所以能在普通内部类当中使用外部类的private变量。另外在外部类当中,对于需要被内部类使用的变量,会在外部类当中添加一个静态方法,该方法的返回值将会作用内部类需要调用的地方

  3. 内部类当中的静态变量必须用final修饰
    个人拙见:每个外部对象都会有自己的内部类,如果内部类中的静态变量不是final类型的话,可能会因为其他外部类的内部类对象而被修改,这样无法保证数据的统一,因此如果是普通变量,一旦对象被销毁,就释放了不会造成任何影响。

  4. 内部类当中的外部类对象也是final修饰的
    每个内部类都属于它自身所对应的外部类对象的,这个也是无法改变的,因此肯定也需要为final的

对People类以及PeopleClock都执行反编译,如图所示:


浅谈java内部类_第1张图片
1.png
浅谈java内部类_第2张图片
2.png
  1. 局部内部类
import java.util.Date;

class People {
    public void getTime(String name, int age) {
        class PeopleClock {
            public void peopleGetTime() {
                System.out.println(name + " get time:" + new Date());
            }
        }
        PeopleClock clock = new PeopleClock();
        clock.peopleGetTime();
    }

}

public class MyInnerClassTest {
    public static void main(String[] args) {
        People people = new People();
        people.getTime("jack", 12);
    }
}
总结

1.可以看到的是我们把原先内部类放入了方法体内部,这种情况下的内部类被称为局部内部类
2.局部内部类的参数引用必须为final声明的,但是这是在java7之前的,java8开始隐式的会加上final关键,并且会被拷贝一份到内部类的构造方法当中。
3.为什么方法参数必须为final类型?
个人拙见:因为方法中的参数以及方法内部的局部变量,可能已经过方法的调用,便被释放了,方法周期较短。然而在JAVA当中一个对象的生命周期可能比某些方法是更加长,一旦对于被释放的变量某个对象再去调用那就行不通了。另外如前文所说,java会在局部内部类和匿名内部类都会对局部变量持有一份自己的拷贝。如果局部变量不是final类型,万一经过其他方法而导致外部的局部变量被修改,那么将无法保证数据的一致性,结果是无法预见的。
stackoverflow上面也有关于这方面的解释,点击这里查看
反编译:

浅谈java内部类_第3张图片
局部内部类反编译

  1. 匿名内部类
interface Speak{
    void  sayHello();
}

class People{
    public Speak getSpeak(String name) {
      int a = 6;//a这个变量如果被匿名内部类使用,那么一定会隐式的加上final关键字
        return new Speak(){
            public void sayHello() {
                System.out.println("hello world "+name+a);
            }
        };
    }
}

public class MyAnonymousClass{
    public static void main(String[] args){
        People people = new People();
        Speak speak = people.getSpeak("jack");
        speak.sayHello();
    }
//注意,getSpeak方法已经调用完毕,但是a变量并没有被释放,使得sayHello()方法可以持续的持有外部的变量并且不会被释放,这个就是闭包.
}

总结

1.例如上述例子实现了一个匿名内部类,一个匿名类实现了Speak这个接口,并且将这个内部类包裹在getSpeak方法当中。和下面这种局部内部类写法是等价的

class People{
    public Speak getSpeak(String name) {
        class JackSpeak implements Speak{

            @Override
            public void sayHello() {
                // TODO Auto-generated method stub
                System.out.println("hello wolrd "+name);
            }
        }
        Speak speak = new JackSpeak();
        return speak;
    }
}

所以我们可以看到的是,匿名内部类语法更加简洁
2.如同局部内部类一样,匿名内部类的局部变量都是隐式声明final的,并且在其构造方法内部也有一个拷贝。我们可以看到当getSpeak()方法执行完毕后,其内部的a变量并没有被“释放”,因为下面的sayHello()方法还可以调用,这就要归功于final的功劳了,内部函数持有外部函数的变量并且不会随着外部类掉用而被释放称为闭包,但是在java当中并不是完全的闭包。

  • 关于闭包的一些文章
    点击这里
    stackoverflow上面的回答

A closure is a persistent scope which holds on to local variables even after the code execution has moved out of that block
反编译:


浅谈java内部类_第4张图片
匿名内部类
  1. 静态内部类

class People {
    private String name;
    private int age;
    
    static class Student{
        Student(){
            System.out.println("new student");
        }
    }
}

public class MyInnerClassTest {
    public static void main(String[] args) {
        People.Student student = new People.Student();
    }
}

总结

1.静态内部类语法较为简单,我并没有十分深刻的理解,请赐教!
2.静态内部类并不会持有一个外部类对象

全文总结:java一直让人诟病的就是语法啰嗦,一件很简单的事情却需要多行代码来实现功能。很难向动态语言看齐,内部类的出现也让java更加的动态起来,并且在java8当中加入了lambda表达式能够更好的实现函数式编程。我对java还是学习不精初学者一个,文章有纰漏之处希望各位指正

你可能感兴趣的:(浅谈java内部类)