day9 9 内部类与匿名内部类

9、内部类与匿名内部类

在类内部可定义成员变量和方法,且在类内部也可以定义另一个类。如果在类 Outer 的内部再定义一个类 Inner,此时类 Inner 就称为内部类(或称为嵌套类),而类 Outer 则称为外部类(或称为宿主类)。

内部类可以很好地实现隐藏,一般的非内部类是不允许有 private 与 protected 权限的(只有public与默认),但内部类可以。内部类拥有外部类的所有元素的访问权限。

9.1 内部类的分类

在类 A 中定义类 B,那么类 B 就是内部类,也称为嵌套类,相对而言,类 A 就是外部类。如果有多层嵌套,例如类 A 中有内部类 B,而类 B 中还有内部类 C,那么通常将最外层的类称为顶层类(或者顶级类)。

内部类也可以分为多种形式,与变量非常类似,如图所示。

在这里插入图片描述

内部类的特点如下:

  1. 内部类仍然是一个独立的类,在编译之后内部类会被编译成独立的.class文件,但是前面冠以外部类的类名和$符号。

  2. 内部类不能用普通的方式访问。内部类是外部类的一个成员,因此内部类可以自由地访问外部类的成员变量,无论是否为 private 的。

  3. 内部类声明成静态的,就不能随便访问外部类的成员变量,仍然是只能访问外部类的静态成员变量。
    在这里插入图片描述

  4. 外部类只有两种访问级别:public 和默认;内部类则有 4 种访问级别:public、protected、 private 和默认。

  5. 在外部类中可以直接通过内部类的类名访问内部类。

InnerClass ic = new InnerClass();    // InnerClass为内部类的类名

在外部类以外的其他类中则需要通过内部类的完整类名访问内部类。

Test.InnerClass ti = newTest().new InnerClass();    // Test.innerClass是内部类的完整类名
  1. 内部类与外部类不能重名。

提示:使用内部类可以使程序结构变得紧凑,但是却在一定程度上破坏了 Java 面向对象的思想。

9.1.1 实例内部类(不用static修饰)

概念

实例内部类是指没有用 static 修饰的内部类,有的地方也称为非静态内部类。在实例内部类中不能定义 static 成员,除非同时使用 final 和 static 修饰。

Demo

package 内部类的三种类型;

/*非静态内部类不能有静态成员,实例内部类、
 */
public class Practice_Not_Static {

        class Inner1 {//非静态成员类,实例内部类
        }

        Inner1 i = new Inner1(); // 使用时不需要创建外部类实例

        public void method1() {
            Inner1 i = new Inner1(); // 不需要创建外部类实例
        }

        //静态方法需要创建外部类实例
        public static void method2() {
            Inner1 i = new Practice_Not_Static().new Inner1();
        }

        class Inner2 {
            Inner1 i = new Inner1(); // 不需要创建外部类实例
        }
    }
//在实例内部类中,可以访问外部类的所有成员。
    class OtherClass {
        public int a = 100;
        static int b = 100;
        final int c = 100;
        private int d = 100;

        public String method1() {
            return "实例方法1";
        }

        public static String method2() {
            return "静态方法2";
        }

        class Inner {
            int a2 = a + 1; // 访问public的a
            int b2 = b + 1; // 访问static的b
            int c2 = c + 1; // 访问final的c
            int d2 = d + 1; // 访问private的d
            String str1 = method1(); // 访问实例方法method1
            String str2 = method2(); // 访问静态方法method2
        }

        public static void main(String[] args) {
            Inner i = new OtherClass().new Inner(); // 创建内部类实例
            System.out.println(i.a2); // 输出101
            System.out.println(i.b2); // 输出101
            System.out.println(i.c2); // 输出101
            System.out.println(i.d2); // 输出101
            System.out.println(i.str1); // 输出实例方法1
            System.out.println(i.str2); // 输出静态方法2


            Practice_Not_Static.Inner2 l = new Practice_Not_Static().new Inner2(); // 需要创建外部类实例
            //打印hashcode
            System.out.println(l.hashCode());

            //打印类的包,$加类名加哈希码
            System.out.print(l);
        }
    }
    /*
    在外部类中不能直接访问内部类的成员,而必须通过内部类的实例去访问。
    如果类 A 包含内部类 B,类 B 中包含内部类 C,则在类 A 中不能直接访问类 C,
    而应该通过类 B 的实例去访问类 C。

    外部类实例与内部类实例是一对多的关系,也就是说一个内部类实例只对应一个外部类实例,
    而一个外部类实例则可以对应多个内部类实例。

    如果实例内部类 B 与外部类 A 包含有同名的成员 t,
    则在类 B 中 t 和 this.t 都表示 B 中的成员 t,而 A.this.t 表示 A 中的成员 t。
     */

编译结果:
在这里插入图片描述
运行结果:
在这里插入图片描述

9.1.2 静态内部类(使用static修饰)

概念
静态内部类是指使用 static 修饰的内部类。

Demo

package 内部类的三种类型;

public class Static_Ineer {

        static class Inner {//静态内部类
            int a = 0;    // 实例变量a
            static int b = 3;    // 静态变量 b
        }

    class OtherClass {//实例内部类,非静态内部类
           
        Static_Ineer.Inner oi = new Static_Ineer.Inner();//不用创建外部类实例,通过类名创建内部类
        int a2 = oi.a;    // 访问实例成员
        int b2 = Static_Ineer.Inner.b;    // 访问静态成员
        int b3 = oi.b;
    }
 }
 /*
 在创建静态内部类的实例时,不需要创建外部类的实例。

 静态内部类可以直接访问外部类的静态成员,如果要访问外部类的实例成员,则需要通过外部类的实例去访问。

 静态内部类中可以定义静态成员和实例成员。外部类以外的其他类需要通过完整的类名访问静态内部类中的静态成员,
 如果要访问静态内部类中的实例成员,则需要通过静态内部类的实例。
  */

9.1.3 局部内部类(在类方法内定义的类)

概念
局部内部类是指在一个方法中定义的内部类

Demo1

package 内部类的三种类型;

public class Local_Class {
    //局部内部类是指在一个方法中定义的内部类。
    public void sayHello(){
        //局部内部类只在当前方法中有效。
        class Local_inner{//局部内部类与局部变量一样,不能使用访问控制修饰符(public、private 和 protected)和 static 修饰符修饰。
            //局部内部类中不能定义 static 成员。既然是局部变量自然不能声明static成员
            public void sayHello(){
                System.out.println("我是局部内部类,在方法里边定义的类!");
            }
        }
        System.out.println("我是包含该局部内部类的方法,局部内部类我儿子!");

        new Local_inner().sayHello();//在该方法里面调用局部内部类的方法,使用匿名内部类一次

    }
    public static void main(String[]args){
        Local_Class local = new Local_Class();
        local.sayHello();

    }
}

在这里插入图片描述

Demo2演示局部内部类只能访问方法中的final成员

package 内部类的三种类型;
/*
在局部内部类中可以访问外部类的所有成员。

在局部内部类中只可以访问当前方法中 final 类型的参数与变量。如果方法中的成员与外部类中的成员同名,
则可以使用 <OuterClassName>.this.<MemberName> 的形式访问外部类中的成员。
 */
public class Local_Class_2 {
        int a = 0;
        int d = 0;
        public void method() {
            int b = 0;//不用final定义的局部变量,默认使用final,java8 里边叫做 Effectively final 功能
            //b=2;//一旦重写赋值就会改变b的属性不是final则报错
            /*
            如果在1.8的环境下,会很神奇的发现我们最开始的java文件编译和运行是完全没有问题的,
            也就是说内部类使用的局部变量是可以不声明为final?!
            也就是说规则没有改变,只是java1.8的编译变得更加智能了而已,
            在局部变量没有重新赋值的情况下,它默认局部变量为final型,
            认为你只是忘记加final声明了而已。如果你重新给局部变量改变了值或引用,
            那就无法默认为final了,所以报错。
             */
            final int c = 0;
            final int d = 10;
            class Inner {
                int a2 = a;    // 访问外部类中的成员
                int b2 = b;    //访问方法中的成员,没有编译错误?
                int c2 = c;    // 访问方法中的成员
                int d2 = d;    // 访问方法中的成员
                int d3 = Local_Class_2.this.d;    //访问外部类中的成员
            }
            Inner i = new Inner();//创建局部内部类
            System.out.println(i.d2);    // 输出10
            System.out.println(i.d3);    // 输出0
            System.out.print(i.b2);
        }
        public static void main(String[] args) {
            Local_Class_2 t = new Local_Class_2();
            t.method();
        }
    }

运行结果:
在这里插入图片描述

9.2 匿名内部类及Demo

9.2.1 概念

匿名类是指没有类名的内部类,必须在创建时使用 new 语句来声明类。
格式:

new <类或接口>() {
    // 类的主体
};

9.2.2 实现形式

匿名类有两种实现方式:

  1. 继承一个类,重写其方法。
  2. 实现一个接口(可以是多个),实现其方法。

例子1,通过重写类来实现

public class Out
{
    void show()
    {
        System.out.println("调用 Out 类的 show() 方法");
    }
}
public class TestAnonymousInterClass
{
    //在这个方法中构造一个匿名内部类
    private void show()
    {
        Out anonyInter=new Out()//其实这里算是有名字了。。。重写Out的方法void
        {
            //获取匿名内部类的实例
            void show()
            {
                System.out.println("调用匿名类中的 show() 方法");
            }
        };
        anonyInter.show();
    }
    public static void main(String[] args)
    {
        TestAnonymousInterClass test=new TestAnonymousInterClass();
        test.show();
    }
}

可以将其改为:

//在这个方法中构造一个匿名内部类
    private void show()
    {
        new Out()//这个时候就没有名字了
        {
            //获取匿名内部类的实例
            void show()
            {
                System.out.println("调用匿名类中的 show() 方法");
            }
        }.show();//直接使用,只使用一次
    }

程序的输出结果如下:
在这里插入图片描述
从输出结果可以看出,匿名内部类有自己的实现。

匿名内部类实现一个接口的方式与实现一个类的方式相同
例子2通过实现一个接口

public interface Caculation{
    int caculationInt(int x,int y);//这是默认的抽象方法,如果不加声明
}

package Lambda表达式;

public class Practice1  {//我们是通过匿名内部类来实现接口

    public static Caculation caculation(char op) {
        Caculation result;
        if (op == '+') {
             result = new Caculation() {//使用匿名内部类来实现该接口
                @Override
                public int caculationInt(int x, int y) {//重写接口方法
                    return x + y;
                }
            };

        } else{
            result = new Caculation() {        
                @Override
                public int caculationInt(int x, int y) {

                    return x - y;
                }

            };
        }

            return result;
    }
    public static void main(String[]args){
        //创建类
        Practice1 pr = new Practice1();
        //使用类的方法caculation()调用接口方法
        System.out.println(pr.caculation('+').caculationInt(3,4));

    }
}

运行结果:
在这里插入图片描述

9.2.3 与局部内部类的关系

(1) 匿名类和局部内部类一样,可以访问外部类的所有成员。如果匿名类位于一个方法中,则匿名类只能访问方法中 final 类型的局部变量和参数。

(2) 匿名类中允许使用非静态代码块进行成员初始化操作。

Out anonyInter=new Out()
{
    int i;
    {    //非静态代码块
        i=10;    //成员初始化
    }
    public void show()
    {
        System.out.println("调用了匿名类的 show() 方法"+i);
    }
};

(3) 匿名类的非静态代码块会在父类的构造方法之后被执行。

9.2.4 匿名内部类的应用

1.创建的类对象是匿名的

  1. 当我们只需要一次调用类的对象的时候,我们就可以考虑使用匿名类的创建方式创建匿名类对象。
  2. 创建的匿名类对象只能够调用一次!

2.匿名类只能够调用一次的原因

  1. 创建的时候只在jvm的堆空间新建了对象,并进行初始化,但栈空间没有一个变量名指向匿名对象。
  2. jvm垃圾回收机制在发现堆空间的对象,没有一个引用指向他,就给回收内存了。
    如:
//输出的时候,直接new一个Date();
System.out.println(new Date());

9.3 内部类实现多继承

多重继承指的是一个类可以同时从多于一个的父类那里继承行为和特征,然而我们知道 Java 为了保证数据安全,只允许单继承。有些时候我们会认为如果系统中需要使用多重继承,那往往都是糟糕的设想,这时开发人员往往需要思考的不是怎么使用多重继承,而是他的设计是否存在问题。但是,有时候开发人员确实需要实现多重继承,而且现实生活中真正地存在这样的情况,例如遗传,我们既继承了父亲的行为和特征,也继承了母亲的行为和特征。

Java 提供的两种方法让我们实现多重继承:接口和内部类。

Demo
1)Father class

public class Father {
    public int strong(){//帅气指数
        return 9;
    }
}

2)Mother class

public class Mother {
    public int kind(){//友好指数
        return 8;
    }
}

3)Son class

package 匿名内部类;

public class Son {
    public  class Father_1 extends Father{//内部类,而且是实例内部类,非静态内部类
        public int strong(){
            return super.strong()+1;//使用super调用父类方法
        }
    }
    public  class Mother_1 extends Mother{内部类,而且是实例内部类,非静态内部类
        public int kind(){
            return super.kind()+1;
        }
    }
    public int getStrong(){
        //使用匿名内部类
        return new Father_1().strong();//只用到了一次
    }
    public int getKind(){
        //使用匿名内部类
        return new Mother_1().kind();
    }

    public static void main(String[]args){
        Son son = new Son();
        System.out.println("强壮指数"+son.getKind());
        System.out.println("和蔼指数"+son.getStrong());
    }
}

运行结果:
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

雨夜※繁华

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值