java 四大内部类

  • 成员内部类
  • 静态内部类
  • 局部内部类
  • 匿名内部类

 

1、成员内部类

定义在另一个类(外部类)的内部,而且与成员方法和属性平级叫成员内部类,......相当于外部类的非静态方法,如果被static修饰,就变成静态内部类了。

注意事项:

  1. 成员内部类中不能存在static关键字,即,不能声明静态属性、静态方法、静态代码块等。【非静态内部类也可以定义静态成员但需要同时有final关键词修饰,静态方法鉴于无法用final修饰,仍必须是在静态内部类 或者非内部类中定义。
  2. 创建成员内部类的实例使用:外部类名.内部类名 实例名 = 外部类实例名.new 内部类构造方法(参数),可以理解为隐式地保存了一个引用,指向创建它的外部类对象。
  3. 在成员内部类中访问外部类的成员方法和属性时使用:外部类名.this.成员方法/属性。
  4. 内部类在编译之后生成一个单独的class文件,里面包含该类的定义,所以内部类中定义的方法和变量可以跟父类的方法和变量相同。例如上面定义的三个类的class文件分别是:MyTest.class、Outer.class和Outer$Inner.class三个文件。
  5. 外部类无法直接访问成员内部类的方法和属性,需要通过内部类的一个实例来访问。
  6. 与外部类平级的类继承内部类时,其构造方法中需要传入父类的实例对象。且在构造方法的第一句调用“外部类实例名.super(内部类参数)”。
public class InClass {
    public static void main(String args[]){
        // 5、非静态内部类要new实例
        Outer.Inner inner = new Outer().new Inner();
        inner.fun5();
    }
}

class Outer{
    int out=2;
    static int cc=5;

    /*成员内部类、非静态内部类*/
    public class Inner{
        //1、变量
        int in=4;
        static final int field1=7; //不报错
        /*
        static  int field0=7;//报错:Inner classes cannot have static declaration
         */

        //2、函数
        public void fun0(){
            System.out.print("fun0");
        }
        /*
        public static void fun1(){//报错:Inner classes cannot have static declaration
            System.out.print("fun1");
        }
        public static final void fun2(){//报错:Inner classes cannot have static declaration
            System.out.print("fun2");
        }
        */

        //3、内部类访问外部变量
        public void fun5(){
            System.out.println(Outer.this.out);
            System.out.println(Outer.cc);//访问静态的
            System.out.println(Outer.this.cc);
        }
    }

    //4、外部类访问内部类变量,需要生成实例
    Inner inner=new Inner();
    int a=inner.in;

    /*int b=Outer.Inner.field0;//访问内部类静态变量,---  报错*/

    /*int c=Outer.Inner.in;//访问内部类成员变量,---  报错*/
}
//6、继承内部类时,其构造方法中需要传入父类的实例对象。且在构造方法的第一句调用“外部类实例名.super(内部类参数)”。
class innerExtends extends  Outer.Inner{
    public innerExtends(Outer outer){
        //外部类实例名.super(内部类参数列表)
        outer.super();
    }
}

2、静态内部类

先看程序

/**
 * Created by Administrator on 2018/9/15 0015.
 */
public class InClass {
    public static void main(String args[]){
        // 5、静态内部类不要new外面的实例
        Outer.Inner inner = new Outer.Inner();
        inner.fun5();
    }
}

class Outer{
    int out=2;
    static int cc=5;

    /*静态内部类*/
    public static class Inner{
        //1、变量
        int in=4;
        static final int field1=7; //不报错
        static  int field0=7;//不报错


        //2、函数
        public void fun0(){
            System.out.print("fun0");
        }

        public static void fun1(){ //不报错
            System.out.print("fun1");
        }
        public static final void fun2(){ //不报错
            System.out.print("fun2");
        }


        //3、内部类访问外部变量
        public void fun5(){
            /*System.out.println(Outer.this.out);//不可访问外部类非static属性/方法*/
            System.out.println(Outer.cc);//访问静态的
            /*System.out.println(Outer.this.cc);//不能出现this,出现this说明是成员变量*/
        }
    }

    //4、外部类访问内部类变量,需要生成实例
    Inner inner=new Inner();
    int a=inner.in;
    
    int b=Outer.Inner.field0;//访问内部类静态变量

    /*int c=Outer.Inner.in;//访问内部类成员变量,---  报错*/

}

在做对比:

说明

成员内部类

静态内部类

静态成员

静态成员需同时有final关键词修饰

可以

静态方法

不可定义

可以

访问外部类非static属性/方法

外部类名.this.成员方法/属性

不可以

外部类访问内部类

需要通过内部类的一个实例来访问

需要通过内部类的一个实例来访问

创建实例

外部类名.内部类名 实例名 = 外部类实例名.new 内部类构造方法(参数)

外部类名.内部类名 实例名 = new 外部类名.内部类名(参数)

编译后的class文件

单独的class文件(so内部类中的方法和变量可以跟父类的方法和变量同名),外部类$内部类.class

单独的class文件(so内部类中的方法和变量可以跟父类的方法和变量同名),外部类$内部类.class

其他

与外部类平级的类继承内部类时,其构造方法中需要传入父类的实例对象。且在构造方法的第一句调用“外部类实例名.super(内部类参数)”

3、局部内部类

定义在代码块、方法体内、作用域(使用花括号“{}”括起来的一段代码)内的类叫局部内部类。

  1. 局部内部类只能在代码代码块、方法体内和作用域中使用(如创建对象和使用类对象等)
  2. 1、内部类不能被public、private、static修饰,可以使用abstract修饰,声明为抽象类;

    2、在外部类中不能创建内部类的实例;

    3、创建内部类的实例只能在包含他的方法中;

    4、内部类访问包含他的方法中的变量必须有final修饰;

    5、外部类不能访问局部内部类,只能在方法体中访问局部内部类,且访问必须在内部类定义之后。

    6、可知局部内部类可随意访问外部类的成员变量和方法,即使是私有的。

    7、内部类和外部类有同名变量和方法时,需要通过Outer.this方式来访问外部类成员变量或方法。


public class InClass {
    public static void main(String args[]){

    }
}

class Outer{
    int out=2;
    static int cc=5;
    private  int gg=6;

    /*局部内部类*/
    //代码块局部内部类
    {
        //1、不能被public,private,protected,static修饰符修饰,但是可以声明为abstract
        abstract class Innerr{}
        /*public class Innner{}*/
        class Inner1{
            int a=5;
            //2、访问外部类变量
            int b=Outer.this.out;//代码块中可以正常访问外部类变量
            int c=Outer.cc;
            int g=Outer.this.gg;//私有的也可访问
        }
    }
    //方法区局部内部类
    public void fun(){
        int funa=7;
        final int funb =8;
        class Inner1{
            int a=5;
            int b=Outer.this.out;//局部区中可以正常访问外部类变量
            int c=Outer.cc;
            int g=Outer.this.gg;//私有的也可访问
            //3、只能赋值final的
            /*int fa=funa;-----报错,因为不是final类型变量*/
            int fb=funb;//final的赋值成功
        }
        //4、可在方法里面生成局部内部类的对象
        Inner1 inner =new Inner1();
        int f=inner.a;
    }
    //5、外部类不能创建内部类的实例
    /*Inner1 in=new Outer().new Inner();-----报错*/
}

4、匿名内部类

匿名内部类定义和实例化形式如下: 
new 父类构造方法(参数){ 
         //注:该方法名必须在父类中已经存在 
     修饰符 返回参数类型 方法名(参数列表){ 
           。。。
     } 
}
  1. 只能使用一次,创建实例之后,类定义会立即消失(想要多次使用就要用到反射的知识了)
  2. 必须继承一个类(抽象的、非抽象的都可以)或者实现一个接口。如果父类(或者父接口)是抽象类,则匿名内部类必须实现其所有抽象方法。
  3. 不能是抽象类,因为匿名内部类在定义之后,会立即创建一个实例。
  4. 不能定义构造方法,匿名内部类没有类名,无法定义构造方法,但是,匿名内部类拥有与父类相同的所有构造方法。
  5. 可以定义代码块,用于实例的初始化,但是不能定义静态代码块。
  6. 可以定义新的方法和属性(不能使用static修饰),但是无法显式的通过“实例名.方法名(参数)”的形式调用,因为使用new创建的是“上转型对象”(即父类声明指向子类对象)。
  7. 是局部内部类,所以要符合局部内部类的要求。

说明

成员内部类

匿名内部类

静态成员

静态成员需同时有final关键词修饰

不可定义

静态方法

不可定义

不可定义

访问外部类非static属性/方法

外部类名.this.成员方法/属性

外部类名.this.成员方法/属性

外部类访问内部类

需要通过内部类的一个实例来访问

需要通过内部类的一个实例来访问

创建实例

外部类名.内部类名 实例名 = 外部类实例名.new 内部类构造方法(参数)

如上:父类 实例名 = new 父类(){}

编译后的class文件

单独的class文件(so内部类中的方法和变量可以跟父类的方法和变量同名),外部类$内部类.class

单独的class文件,使用类$数字.class

其他

与外部类平级的类继承内部类时,其构造方法中需要传入父类的实例对象。且在构造方法的第一句调用“外部类实例名.super(内部类参数)”

有时候我为了免去给内部类命名,便倾向于使用匿名内部类,因为它没有名字。例如:
((Button) findViewById(R.id.start)).setOnClickListener(new Button.OnClickListener() { 
    @Override 
    public void onClick(View v) { 
        new Thread() { 
 
            @Override 
            public void run() { 
                // TODO Auto-generated method stub 
            } 
 
        }.start(); 
    } 
}); 
      匿名内部类是不能加访问修饰符的。要注意的是,new 匿名类,这个类是要先定义的,看下面例子:
public class Outer { 
    public static void main(String[] args) { 
        Outer outer = new Outer(); 
        Inner inner = outer.getInner("Inner", "gz"); 
        System.out.println(inner.getName()); 
    } 
 
    public Inner getInner(final String name, String city) { 
        return new Inner() { 
            private String nameStr = name; 
 
            public String getName() { 
                return nameStr; 
            } 
        }; 
    } 
} 
 
//注释后,编译时提示类Inner找不到 
/* interface Inner { 
    String getName(); 
} */ 
同时在这个例子,留意外部类的方法的形参,当所在的方法的形参需要被内部类里面使用时,该形参必须为final。这里可以看到形参name已经定义为final了,而形参city 没有被使用则不用定义为final。为什么要定义为final呢?在网上找到本人比较如同的解释:
 “这是一个编译器设计的问题,如果你了解java的编译原理的话很容易理解。  
首先,内部类被编译的时候会生成一个单独的内部类的.class文件,这个文件并不与外部类在同一class文件中。  
当外部类传的参数被内部类调用时,从java程序的角度来看是直接的调用例如:  
public void dosome(final String a,final int b){  
  class Dosome{public void dosome(){System.out.println(a+b)}};  
  Dosome some=new Dosome();  
  some.dosome();  
}  
从代码来看好像是那个内部类直接调用的a参数和b参数,但是实际上不是,在java编译器编译以后实际的操作代码是  
class Outer$Dosome{  
  public Dosome(final String a,final int b){  
  this.Dosome$a=a;  
  this.Dosome$b=b;  
}  
  public void dosome(){  
  System.out.println(this.Dosome$a+this.Dosome$b);  
}  
}}  
从以上代码看来,内部类并不是直接调用方法传进来的参数,而是内部类将传进来的参数通过自己的构造器备份到了自己的内部,自己内部的方法调用的实际是自己的属性而不是外部类方法的参数。  
这样理解就很容易得出为什么要用final了,因为两者从外表看起来是同一个东西,实际上却不是这样,如果内部类改掉了这些参数的值也不可能影 响到原参数,然而这样却失去了参数的一致性,因为从编程人员的角度来看他们是同一个东西,如果编程人员在程序设计的时候在内部类中改掉参数的值,但是外部 调用的时候又发现值其实没有被改掉,这就让人非常的难以理解和接受,为了避免这种尴尬的问题存在,所以编译器设计人员把内部类能够使用的参数设定为必须是 final来规避这种莫名其妙错误的存在。”
 (简单理解就是,拷贝引用,为了避免引用值发生改变,例如被外部类的方法修改等,而导致内部类得到的值不一致,于是用final来让该引用不可改变)
 
      因为匿名内部类,没名字,是用默认的构造函数的,无参数的,那如果需要参数呢?则需要该类有带参数的构造函数:
public class Outer { 
    public static void main(String[] args) { 
        Outer outer = new Outer(); 
        Inner inner = outer.getInner("Inner", "gz"); 
        System.out.println(inner.getName()); 
    } 
 
    public Inner getInner(final String name, String city) { 
        return new Inner(name, city) { 
            private String nameStr = name; 
 
            public String getName() { 
                return nameStr; 
            } 
        }; 
    } 
} 
 
abstract class Inner { 
    Inner(String name, String city) { 
        System.out.println(city); 
    } 
 
    abstract String getName(); 
} 
注意这里的形参city,由于它没有被匿名内部类直接使用,而是被抽象类Inner的构造函数所使用,所以不必定义为final。
 
      而匿名内部类通过实例初始化,可以达到类似构造器的效果:
public class Outer { 
    public static void main(String[] args) { 
        Outer outer = new Outer(); 
        Inner inner = outer.getInner("Inner", "gz"); 
        System.out.println(inner.getName()); 
        System.out.println(inner.getProvince()); 
    } 
 
    public Inner getInner(final String name, final String city) { 
        return new Inner() { 
            private String nameStr = name; 
            private String province; 
 
            // 实例初始化 
            { 
                if (city.equals("gz")) { 
                    province = "gd"; 
                }else { 
                    province = ""; 
                } 
            } 
 
            public String getName() { 
                return nameStr; 
            } 
 
            public String getProvince() { 
                return province; 
            } 
        }; 
    } 
} 
 
interface Inner { 
    String getName(); 
    String getProvince(); 
} 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值