Java Inner Class

Nested Classes

The Java programming language allows you to define a class within another class. Such a class is called a nested class, and the class that holds the inner class is called the outer class.
Java允许1个类里定义类,此类叫内部类。

Nested classes are divided into two categories: static and non-static. Nested classes that are declared static are called static nested classes. Non-static nested classes are called inner classes.
静态内部类就是用static修饰的类;非静态内部类就是不用staitc修饰的类,也叫inner class。

内部类访问修饰符
可以是public, protected, private, or with default package access.
常规的类是不能用private、protecteded修饰的,但是内部类可以用4种访问权限。
If the inner class is public & the outer class as well, then code in some other unrelated class can as well create an instance of the inner class.
(也就是说可以控制内部类使用位置:本类、包内、任何位置)

There are four kinds of nested class in Java. In brief, they are:

  • inner class: declared as an instance member of another class
  • local inner class: declared inside an instance method of another class
  • anonymous inner class: like a local inner class, but written as an expression which returns a one-off object
  • static class: declared as a static member of another class

一、Inner class

[modifiers] class OuterClassName {
    code...
    [modifiers] class InnerClassName {
        code....
    }
}

if the inner class has a varible with same name then the outer class’s variable can be accesse like this:

<OuterClassName>.this.<variableName>

An object of an inner class has an implicit reference to the outer class object that instantiated it. Through this pointer, it has free access to all elements of the outer class object that contains it, by name (no matter what the access level of the elements is), not via this (this in the inner class refers to the inner class instance, not the associated outer class instance):
通过查看字节码文件,内部类的构造函数参数为this$0,也就是外部类对象。所以内部类可以获得外部类任何变量的访问权。

so,the inner class object must be associated with an instance of the outer class.

创建内部类对象方法:①创建外部类实例 ②创建内部类实例

<OuterClassName> outerObj = new <OuterClassName>(arguments);
	outerObj.<InnerClassName> innerObj = 
	   outerObj.new <InnerClassName>(arguments);
class TestMemberOuter1{  
 private int data=30;  
 class Inner{  
  void msg(){data=2;} //这里直接用的外部类的name,而不是this
 }  
 public static void main(String args[]){  
  TestMemberOuter1 obj=new TestMemberOuter1();  
  TestMemberOuter1.Inner in=obj.new Inner();  
  in.msg();  
 }  
}  

编译后会生成两个class文件:TestMemberOuter1.classTestMemberOuter1$Inner.class
内部类的名字是外部类$内部类
TestMemberOuter1$Inner.class反编译的代码:

class TestMemberOuter1$Inner 
{  
    final TestMemberOuter1 this$0;  
    TestMemberOuter1$Inner(TestMemberOuter1 this$0)  
    {   super();  
        this$0 = this$0;  
    }  
    void msg()  
    {  
      TestMemberOuter1.access$002(this.this$0, 2);
    }  
}  

TestMemberOuter1$Inner.class字节码文件分析
在这里插入图片描述
结合上面的字节码文件:

  • If you want to instantiate inner class, you must have to create the instance of outer class.
    如果要实例化内部类(也就是new Inner()),则必须创建外部类的实例(成员内部类实例化依赖外部类this)。
  • The Member inner class have the reference of Outer class that is why it can access all the data members of Outer class including private.
    成员内部类有外部类的引用,这也是内部类能访问外部类所有数据成员(变量、方法)的原因。

二、Local inner class

A class i.e. created inside a method is called local inner class in java. If you want to invoke the methods of local inner class, you must instantiate this class inside the method.
类在方法里面叫局部内部类。

语法:

<access-specifier> class <OuterClassName> {
	code...
	<access-specifier> <return-type> <MethodName>(<arguments>){
		class <LocalInnerClassName>{
			code...
		}
		code...
	}
	code...
}

注意点:

  • 因为它不是包或者类的成员,所以没有访问修饰符,使用严格规定在此方法中,在方法外部是不可见的(也就是不能实例出来个对象)
  • 它能访问类的变量(因为会把this$0传入内部类中)也可以访问方法里的局部变量。
    注意局部变量要为final或effectively final。effectively final指被初始化后就不再发生改变。比如下例中,display()中先声明并初始化为i=2,在内部类中不能在赋值。若赋值,会出现编译错误“"variable i is accessed from inner class,needs to be final or effectively final”【从字节码文件,也可以看出,局部变量在内部类中是被final修饰的,不能改。】
  • 外部类成员与内部类成员重名,也是<OuterClassName>.this.<variableName>
  • 在jdk1.8后,内部类能访问方法的参数。
  • Local inner class是non-static nested classes,,所以里面不能包含静态的变量和方法。但是其外部类方法可以用static修饰,从而在内部类中可以访问外部类的静态变量,不可访问非静态变量(因为静态方法,可以由类直接调用,无需实例化,而内部类需要this$0传入,需要外部类实例);若外部类方法不是static修饰,从而在内部类中可以访问外部类的静态变量和非静态变量(因为外部类必定实例化,可访问静态变量)
  • 在方法内(block)中,不能声明接口,因为接口就是static的
  • Local inner class中可以包含常量(A constant variable is a variable of primitive type or type String that is declared final and initialized with a compile-time constant expression.)

详细看:https://docs.oracle.com/javase/tutorial/java/javaOO/localclasses.html

public class localInner1 {
    private int data=30;//instance variable
    void display(){
       // int i=2;
        class Local{
        
            void msg(){
            System.out.println(data);
            //    i=10;
            }
        }
        Local l=new Local();
        l.msg();
    }
}

public class Test2   {
    public static void main(String args[]) {
         localInner1 obj=new localInner1();
        obj.display();

    }
}

localInner1 编译后会生成两个class文件:localInner1 .classlocalInner1$1Local.class

localInner1$1Local.class文件解读 :与Inner class字节码相同,除了局部变量在这里被final修饰
在这里插入图片描述


class localInner1$1Local
{
  localInner1$1Local(localInner1 this$0) {}
  
  void msg()
  {
    System.out.println(localInner1.access$000(this.this$0));
  }
}

localInner1.class文件解读 :在display中创建了局部内部类,从字节码看是在display中以localInner1为参数创建了localInner1$Local对象。
在这里插入图片描述

三、Anonymous inner class

An anonymous inner class is a syntactically convenient way of writing a local inner class.
匿名内部类是local inner class的一种简写形式。

A class that have no name is known as anonymous inner class in java. It should be used if you have to override method of class or interface. Java Anonymous inner class can be created by two ways:
匿名内部类使用要覆盖接口或类方法。

  • Class (may be abstract or concrete).
  • Interface

因为新生成的字节码文件是implements interfaceName或者extends className。若是extends className,则在匿名内部类中,访问父类变量用super.variableName,访问父类方法类似;若是implements interfaceName,则不存在访问父类方法(因为接口未实现),访问接口定义的变量,直接访问即可,因为接口中的变量是public static final修饰的,若实现类中也定义了相同变量,则覆盖用实现类的(这也是接口实现的意义)。

所以实现类中没必要用this$0,就可以访问父类或者接口的变量或方法。

语法:

//类
new SuperType(construction parameters) {
	inner class methods and data
}
//接口
new InterfaceType () { methods and data }

匿名内部类写法 1.new 操作符 2.接口或类名 3.() 4.方法声明体
  • 内部类不能手写构造函数,因为类名取决于编译器,不是用户确定的。但是子类实例化,父类必定实例化。若父类有多个构造函数,选取“simplest”,这有一套规则,知道即可,不深究。
  • 若是实现类,也就是继承SuperType,则SuperType构造函数是可以有参数的(抽象类也可以有构造方法)。
  • 若是实现接口,则InterfaceType 接口没有构造函数之说,不过必须要有括号。编译后的实现的接口类有个无参的构造方法“do-nothing”。
  • 尽管匿名内部类不能用构造方法,但可以用初始化块。
  • 在访问类成员变量上与local inner class相同;在局部变量访问中,可以访问不被final修饰的(也就是内部类中,可以对其修改),在字节码中,局部变量并不会在内部类中定义为final。
  • 在{} 内使用与local inner class相同。

Interface

public interface Eatable {
    void eat();
}

public class Test2   {
    public static void main(String args[]) {
       Eatable e=new Eatable() {
           @Override
           public void eat() {
               System.out.println("nice fruits");
           }
       };
       e.eat();
    }
}

编译后会生成两个class文件:Test2.classTest2$1.class

  1. A class is created but its name is decided by the compiler which implements the Eatable interface and provides the implementation of the eat() method.
    会创建一个类,但类名取决于编译器
  2. An object of Anonymous class is created that is referred by p reference variable of Eatable type.
    p指向匿名类的对象
final class Test2$1 implements Eatable
{
  public void eat()
  {
    System.out.println("nice fruits");
  }
}

形成类Test2$1
在这里插入图片描述
Test2.class字节码文件:
在这里插入图片描述
Class (may be abstract or concrete)

public abstract class Person {
    int i=0;
    abstract void eat(int param); //抽象方法
    public Person(int i){  //含参构造函数
        this.i=i;
    }
    public void prin() //已经实现方法
    {
        System.out.println(i);
    }
}

public class Test2   {
   
    public static void main(String args[]) {
     Person p=new Person(2){
            void eat(int param){System.out.println(param);}
        };
        p.eat(2);
        p.prin();
        
    }
}

  1. A class is created but its name is decided by the compiler which extends the Person class and provides the implementation of the eat() method.
  2. An object of Anonymous class is created that is referred by p reference variable of Person type.
import java.io.PrintStream;

final class Test2$1
  extends Person
{
  void eat()
  {
    System.out.println("nice fruits");
  }
}

在这里插入图片描述
local inner class与anonymous inner class联系与区别
Be clear that an anonymous inner class is simply a less flexible way of creating a local inner class with one instance. If you want a local inner class which implements multiple interfaces or which implements interfaces while extending some class other than Object or which specifies its own constructor, you’re stuck creating a regular named local inner class.
需要清楚的是,anonymous inner只是local inner class的一种较不灵活的实现方式。 如果您想要一个local inner class来实现多个接口,或者想要在继承更多的类,而不仅仅是Object。那么您就必须创建一个带名的local inner class。

四、static nested class

A static class i.e. created inside a class is called static nested class in java. It cannot access non-static data members and methods. It can be accessed by outer class name.

  • It can access static data members of outer class including private.
    它能访问外部类的静态数据成员(变量或方法),包括private
  • Static nested class cannot access non-static (instance) data member or method.
    它不能访问非静态的数据成员(因为没有外部类实例)

语法:

<access-specifier> class OuterClassName {
    public static class <StaticInnerClassName> {
        . . .
    }
    . . .
}
//只能用这种方式创建static nested class对象,不能用inner class方式
OuterClass.StaticNestedClass nestedObject =
     new OuterClass.StaticNestedClass();

注意点:

  • 外部类的静态成员对静态内部类是可见的,无关权限
    外部类的非静态成员对内部类不可见,因为没有外部类的实例
  • 静态内部类中可以不含静态成员,而且从反编译得到的代码看出,此类并不是用static修饰的。
  • Sometimes static nested class are not refered to as inner class at all, as they don’t require outer classes instance.
    有时,静态嵌套类根本不需要外部类,因为它们不需要外部类实例。
    比如下面例子的TestOuter1.Inner obj=new TestOuter1.Inner();就没有实例化外部类。
public class TestOuter1 {
    static int data=30;
    static class Inner{
        void msg(){System.out.println("data is "+data);}
    }
}

public class Test2   {
    int a=2;
    public Test2()
    {
        a=3;
    }
    public static void main(String args[]) {
        TestOuter1.Inner obj=new TestOuter1.Inner();
        obj.msg();

    }
}

TestOuter1编译后会生成两个class文件:TestOuter1$Inner .classTestOuter1.class
TestOuter1$Inner .class文件分析

class TestOuter1$Inner
{
  void msg()
  {
    TestOuter1.data = 3;
  }
}

在这里插入图片描述

Why Use Nested Classes?

Compelling reasons for using nested classes include the following:

  • It is a way of logically grouping classes that are only used in one place: If a class is useful to only one other class, then it is logical to embed it in that class and keep the two together. Nesting such “helper classes” makes their package more streamlined.
    这是一种对类进行逻辑分组的方法:如果A仅对B有用,那么将A嵌入B并将两者保持在一起是合乎逻辑的。 嵌套A可使它们的包更加简化。

  • It increases encapsulation: Consider two top-level classes, A and B, where B needs access to members of A that would otherwise be declared private. By hiding class B within class A, A’s members can be declared private and B can access them. In addition, B itself can be hidden from the outside world.
    它增强了封装:考虑两个顶级类A和B,其中B需要访问A的成员,否则它们将被声明为私有。 通过将类B隐藏在类A中,可以将A的成员声明为私有,而B可以访问它们。 另外,B本身可以对外界隐藏。

  • It can lead to more readable and maintainable code: Nesting small classes within top-level classes places the code closer to where it is used.
    它可以导致更具可读性和可维护性的代码:在顶级类中嵌套小型类会使代码更靠近使用位置。

参考:https://www.viralpatel.net/inner-classes-in-java/
https://www.javatpoint.com/nested-interface
https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html
https://stackoverflow.com/questions/70324/java-inner-class-and-static-nested-class

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值