Java内部类

一、内部类简述

  • 内部类的结构和概述

    ​ 在一个A类中再定义一个B类,那么,这个B类就叫内部类,A类就叫外部类

  • 内部类的访问特点
    内部类可以直接访问外部类的成员,即使这个成员是私有的但是,如果外部类要访问内部类的成员,那么必须要创建对象,访问内部类本身的属性可以使用this.属性,而访问外部类的要使用外部类名.this.属性(因为内部类可以直接访问外部类的成员,所以这种方式没有什么意义,只是比较直观的体现访问的是哪里的成员),ava中的内部类大致分为五种,成员内部类,局部内部类,静态内部类,匿名内部类,私有内部类

class outer {
    private int num = 100;

    public void method() {
        System.out.println("外部类成员方法");
    }
    //内部类,并且是一个成员内部类
    class Inter{
        public void show() {
            method();
            System.out.println(num);//内部类可以直接访问外部类的成员
        }

    }
    public void test() {
        //show();//错误,外部类不能直接访问内部类,需要创建对象
        Inter i = new Inter();
        i.show();
    }
    public void test2() {
        //局部内部类
        class In{

        }
    }
    //静态内部类
    static class In {
        public void print() {
            System.out.println(num);
        }
    }
    //私有内部类
    private class heart{
        public void test() {
            System.out.println("这是我的心脏");
        }
    }
     }
public class InnerClass {

    public static void main(String[] args) {
        //访问内部类的成员的方式
        outer.Inter oi = new outer().new Inter();
        oi.show();
    }

}

二、内部类区分

2.1.成员内部类

访问方式:

外部类名.内部类名 对象名 = new 外部类对 象.new 内部类对象

成员内部类就是就是在外部类的成员位置上再定义一个类

2.2.静态内部类

静态内部类:被static修饰的内部类
因为内部类被静态化,所以内部类只能访问外部类的静态方法和变量,但是同时因为内部类被静态化,所以outer.In可以被看成一个整体,所以可以直接new出out.In(),所以被static修饰的内部类调用方式为:

外部类名.内部类名 对象名 = new 外部类名.内部类名();

这里还要提一下,如果静态内部类中是一个静态的方法(前提条件),那么可以直接通过外部类名.内部类名.静态方法名();来调用这个静态方法,强调:必须是内部类中的静态方法才能使用这种调用方式

class Out {
    private static int age = 12;
    private int age2 = 10
    static class In {
        public void print() {
            System.out.println(age);
            //System.out.println(age2);//报错,因为静态内部类只能访问静态的变量或者方法
        }
        //内部类中的静态方法
        public static void test(){
            system.out,println("这是一个内部类中的静态方法");
        }
    }
}
public class Demo {
    public static void main(String[] args) {
        Out.In oi= new Out.In();//静态内部类的调用方式
        oi.print();
        out.In.test();//静态内部类中静态方法的直接调用方式,可以直接通过类名调用
    }
}

输出结果:

12
这是一个内部类中的静态方法

2.3.私有内部类

私有内部类:私有内部类是被private修饰的内部类,私有内部类只能被外部类访问,为了方便理解,举一个身体与心脏的例子,就像身体是外部类,心脏是内部类,心脏只能被自己的身体使用,所以,私有内部类只能被该类所属的外部类访问

//外部类
class body{
    private class heart{
        public void test() {
            System.out.println("这是我的心脏");
        }
    }
    //因为method方法是外部类的 所以可以对私有的内部类进行访问,且要通过创建对象的方式访问内部类
    public void method() {
        heart H = new heart();//创建对象访问内部类
        H.test();
    }
}

public class InnerClass {

    public static void main(String[] args) {
        //body.heart bh = new body().new heart();
        //bh.heart();//这种访问方法是错误的,因为私有内部类只有他所属的外部类才能访问
        body b = new body();
        b.method();
    }

}

2.4.局部内部类

局部内部类是在外部类的局部对象创建的一个内部类,也可以直接访问外部类的对象,,也可以理解成在一个外部类方法内定义一个类,要使用局部内部类,可以在方法内部创建内部类对象,使用对象调用内部类,来达到使用内部类的目的,在关于局部内部类访问变量的时候发现一个问题,看视频时说到局部内部类要访问的局部变量必须是经过final修饰的,但是经过代码实验发现,即使不是final修饰的局部内部类也可以访问并且运行成功,经过上网查找找到原因,这是由于java8的新特性导致的,java8的新特性使得局部内部类可以使用非final修饰的变量但是java7中成员内部类必须要使用final修饰之后的变量,但是!这种新特性只是为了访问方便,而不等同于所访问的那个变量就不是final型的变量了,具体解释如下,摘抄于CSDN

Java 8的新特性,但其实只是让你方便一点,在你这个内部类里效果等同于final
比如,如果你想修改a或b
a = 100
那么会告诉你:
Local variable a defined in an enclosing scope must be final or effectively final
注意后面说的effectively final,就是说表面上没有final修饰,但实际上必须和final有相同的效果

class outer{
    private int num = 10;
    public void test() {
        final int num2 = 20;
        //num2 = 30;//默认最终态,值不可以被修改
        class Inter{
            public void show() {
                System.out.println(num);//因为num不是该类中的局部变量,所以不需要是final声明过的
                System.out.println(num2);//在局部类中访问局部变量num2,需要num2被声明为最终类型
                //但是要注意的是在Java8中即使没有被声明成final,也默认是final,且不可以被修改
            }
        }
        //在方法处创建内部类对象来使用内部类
        Inter i = new Inter();
        i.show();
    }
}
public class InnerClass {
    public static void main(String[] args) {
        outer o = new outer();
        o.test();
    }

}

2.5.匿名内部类(重点)

匿名内部类顾名思义就是没有名字的内部类,但是正因为没有名字,所以匿名内部类只能被使用一次, 一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类。
匿名内部类的使用前提是必须继承一个类或者实现一个接口,如果是类,那该类可以是具体类也可以是抽象类,如果是抽象类或者接口,那么和抽象类与接口的实现一样,该匿名内部类必须要重写抽象类或者接口中的抽象方法 同时它也是没有class关键字,这是因为匿名内部类是直接使用new来生成一个对象的引用。当然这个引用是隐式的。匿名内部类的本质就是一个继承了该类或者实现了该接口的子类匿名对象.
使用格式:

new 类名或者接口名(){
重写方法;

}

示例1:

直接在主方法里面使用匿名内部类

abstract class Person {
    public abstract void eat();
}
public class InnerClass {
    public static void main(String[] args) {
        //这是一个匿名内部类,直接new一个类或者一个接口
        new Person() {
            public void eat() {
                System.out.println("eat something");
            }
        }.eat();
    }
}
运行结果:eat someting

当要实现的是一个接口,其实也是一样的,为了展示不同的调用方法,这个采用的是主函数创建对象,当接口或者抽象类需要重写的方法比较多的时候采用下面这种较为方便

实例2

创建对象使用内部类

interface Person {
    public abstract void eat();
}
class Outer {
    public void method() {

        new Person() {
            public void eat() {
                System.out.println("show");
            }
        }.eat();
    }
}
public class InnerClass {
    public static void main(String[] args) {
        Outer o = new Outer();
        o.method();
    }
}

当接口需要重写的抽象方法比较多的时候,反复重写方法调用就会比较麻烦,比如

示例3:

interface Inter {
    public abstract void show();
    public abstract void show2();
}
class outer{
    public void Person(){
        new Inter() {
            public void show() {
                System.out.println("show");
            }

            public void show2() {
                System.out.println("show2");
            }
        }.show();

        new Inter() {
            public void show() {
                System.out.println("show");
            }

            public void show2() {
                System.out.println("show2");
            }
        }.show2();
    }
}

public class InnerClass {
    public static void main(String[] args) {
        outer o = new outer();
        o.Person();
    }
}

运行结果:

show
show2

所以在这种情况下,一般是采用多态去改进,这样就不用反复重写大段代码
示例4

interface Inter {
    public abstract void show();
    public abstract void show2();
}
class outer{
    public void Person(){
        Inter i=new Inter() {//采用多态
            public void show() {
                System.out.println("show");
            }
            public void show2() {
                System.out.println("show2");
            }
        };
        i.show();
        i.show2();
    }
}
public class InnerClass {
    public static void main(String[] args) {
        outer o = new outer();
        o.Person();
    }
}

这几种调用方式各有好处,视实际情况看使用哪种,如果需要被重写的抽象方法只有一个,且匿名内部类只需要使用一次使用示例1或者示例2较为方便,如果有多个抽象方法需要重写,那毫无疑问使用示例4的方法较为方便

在使用匿名内部类的过程中,我们需要注意如下几点:

1、使用匿名内部类时,我们必须是继承一个类或者实现一个接口,但是两者不可兼得,同时也只能继承一个类或者实现一个接口。
2、匿名内部类中是不能定义构造函数的。
3、匿名内部类中不能存在任何的静态成员变量和静态方法。
4、匿名内部类为局部内部类,所以局部内部类的所有限制同样对匿名内部类生效。
5、匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法
6、匿名内部类和局部内部类一样,内部类使用的参数必须是final类型的参数
posted @ 2019-12-21 15:43  穿黑风衣的牛奶  阅读(271)  评论(0编辑  收藏  举报