java基础(十四)-----详解匿名内部类——Java高级开发必须懂的
在这篇博客中你可以了解到匿名内部类的使用、匿名内部类要注意的事项、匿名内部类使用的形参为何要为final。
使用匿名内部类内部类
匿名内部类由于没有名字,所以它的创建方式有点儿奇怪。创建格式如下:
new 父类构造器(参数列表)|实现接口() { //匿名内部类的类体部分 }
在这里我们看到使用匿名内部类我们必须要继承一个父类或者实现一个接口,当然也仅能只继承一个父类或者实现一个接口。同时它也是没有class关键字,这是因为匿名内部类是直接使用new来生成一个对象的引用。当然这个引用是隐式的。
public abstract class Bird {private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}public abstract int fly(); }public class Test {public void test(Bird bird){System.out.println(bird.getName() + "能够飞 " + bird.fly() + "米");}public static void main(String[] args) {Test test = new Test();test.test(new Bird() {public int fly() {return 10000;}public String getName() {return "大雁";}});} } ------------------ Output: 大雁能够飞 10000米
在Test类中,test()方法接受一个Bird类型的参数,同时我们知道一个抽象类是没有办法直接new的,我们必须要先有实现类才能new出来它的实现类实例。所以在mian方法中直接使用匿名内部类来创建一个Bird实例。
由于匿名内部类不能是抽象类,所以它必须要实现它的抽象父类或者接口里面所有的抽象方法。
对于这段匿名内部类代码其实是可以拆分为如下形式:
public class WildGoose extends Bird{public int fly() {return 10000;}public String getName() {return "大雁";} }WildGoose wildGoose = new WildGoose(); test.test(wildGoose);
在这里系统会创建一个继承自Bird类的匿名类的对象,该对象转型为对Bird类型的引用。
对于匿名内部类的使用它是存在一个缺陷的,就是它仅能被使用一次,创建匿名内部类时它会立即创建一个该类的实例,该类的定义会立即消失,所以匿名内部类是不能够被重复使用。对于上面的实例,如果我们需要对test()方法里面内部类进行多次使用,建议重新定义类,而不是使用匿名内部类。
注意事项
在使用匿名内部类的过程中,我们需要注意如下几点:
1、使用匿名内部类时,我们必须是继承一个类或者实现一个接口,但是两者不可兼得,同时也只能继承一个类或者实现一个接口。
2、匿名内部类中是不能定义构造函数的。
3、匿名内部类中不能存在任何的静态成员变量和静态方法。
4、匿名内部类为局部内部类,所以局部内部类的所有限制同样对匿名内部类生效。
5、匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法。
推荐博客
程序员写代码之外,如何再赚一份工资?
使用的形参为何要为final
我们给匿名内部类传递参数的时候,若该形参在内部类中需要被使用,那么该形参必须要为final。也就是说:当所在的方法的形参需要被内部类里面使用时,该形参必须为final。
为什么必须要为final呢?
首先我们知道在内部类编译成功后,它会产生一个class文件,该class文件与外部类并不是同一class文件,仅仅只保留对外部类的引用。当外部类传入的参数需要被内部类调用时,从java程序的角度来看是直接被调用:
public class OuterClass {public void display(final String name,String age){class InnerClass{void display(){System.out.println(name);}}} }
从上面代码中看好像name参数应该是被内部类直接调用?其实不然,在java编译之后实际的操作如下:
public class OuterClass$InnerClass {public InnerClass(String name,String age){this.InnerClass$name = name;this.InnerClass$age = age;}public void display(){System.out.println(this.InnerClass$name + "----" + this.InnerClass$age );} }
所以从上面代码来看,内部类并不是直接调用方法传递的参数,而是利用自身的构造器对传入的参数进行备份,自己内部方法调用的实际上时自己的属性而不是外部方法传递进来的参数。
直到这里还没有解释为什么是final?在内部类中的属性和外部方法的参数两者从外表上看是同一个东西,但实际上却不是,所以他们两者是可以任意变化的,也就是说在内部类中我对属性的改变并不会影响到外部的形参,而然这从程序员的角度来看这是不可行的,毕竟站在程序的角度来看这两个根本就是同一个,如果内部类该变了,而外部方法的形参却没有改变这是难以理解和不可接受的,所以为了保持参数的一致性,就规定使用final来避免形参的不改变。
简单理解就是,拷贝引用,为了避免引用值发生改变,例如被外部类的方法修改等,而导致内部类得到的值不一致,于是用final来让该引用不可改变。
故如果定义了一个匿名内部类,并且希望它使用一个其外部定义的参数,那么编译器会要求该参数引用是final的。
转载于:https://www.cnblogs.com/java-chen-hao/p/10412756.html
java基础(十四)-----详解匿名内部类——Java高级开发必须懂的相关推荐
- java访问修饰符详解——学java,零基础不怕,不只要理论,更要实践+项目,a href=http://www.bjweixin.com太原维信科技提供 /a...
java访问修饰符详解--学java,零基础不怕,不只要理论,更要实践+项目 <a href=http://www.bjweixin.com>太原维信科技提供 </a> pub ...
- Java基础:volatile详解
Java基础:volatile详解 1.volatile保证可见性 1.1.什么是JMM模型? 1.2.volatile保证可见性的代码验证 1.2.1.无可见性代码验证 1.2.1.volatile ...
- 【JAVA基础】java基础之-泛型详解
写在前面的话 脑子是个好东西,可惜的是一直没有搞懂脑子的内存删除机制是什么,所以啊,入行多年,零零散散的文章看了无数,却总是学习了很多也忘了很多. 痛定思痛的我决定从今天开始系统的梳理下知识架构,记录 ...
- Java高并发编程详解系列-Java线程入门
根据自己学的知识加上从各个网站上收集的资料分享一下关于java高并发编程的知识点.对于代码示例会以Maven工程的形式分享到个人的GitHub上面. 首先介绍一下这个系列的东西是什么,这个系列自己 ...
- [读书笔记]java基础与案例详解 主编徐明华
Java SE(标准版),SUN平台体系中最基础最底层的版本,它是各种应用平台的基础. Java SE包含了桌面应用API和Java EE企业级与Java ME嵌入式. Java是一种计算机编程语言: ...
- Java基础 面向对象的详解
1.1 万物皆对象 我们是怎么认识世界的? 人类从小就不断的接触到各种各类存在世界上的各种生物,然后通过事物的公共特性,将它们归类,所以以后就不会出现见到猫叫老虎.那么我们在现实生活中,是通过具体的某 ...
- Java基础——枚举 (详解)
什么是枚举 Java中的枚举是一种类型,顾名思义:就是一个一个列举出来.所以它一般都是表示一个有限的集合类型,它是一种类型,在维基百科中给出的定义是: 在数学和计算机科学理论中,一个集的枚举是列出某些 ...
- Java基础 - javac命令详解(1) 编译基本java项目
1.为什么要搞定 javac 命令 1.javac 是java中的编译源代码的命令工具,将.java文件编译成 .class 文件: 2.由于当下为了提高开发效率,大多数的开发工作都在 java的 集 ...
- Java经典面试题详解:Java字符串转成list
阿里巴巴Java岗面试题分享 1.HashMap 的内部结构?内部原理?和 HashTable 的区别,假如发⽣了 hash 碰撞,如何设计能让遍历效率⾼? 2.讲一讲讲讲 ConcurrentHas ...
- Java基础-注解机制详解
文章目录 1. 注解基础 1.1 Java内置注解 1.1.1 内置注解 - @Override 1.1.2 内置注解 - @Deprecated 1.1.3 内置注解 - @SuppressWarn ...
最新文章
- Laravel 引入自定义类库或第三方类库
- linux内核经典书籍--Linux内核设计与实现
- Windows驱动开发要点总结一
- 160个Crackme045
- 利用线程池为线程创建一个守护线程
- leetcode 73 矩阵置零 C++ 两种解法
- java set hashcode_Java学习笔记_180724_HashSet_hashCode()
- manjaro 搜狗输入法_Manjaro日常使用 之一:日常办公
- MIPS指令:常用R型、I型、J型指令编解码表
- MS SQL SERVER 读取数据库中每个表的描述/注释以及表中字段/列的字段名,字段类型,字段描述/注释/说明等信息...
- json数组转java集合
- 规范JavaScript注释
- 手机短信小额支付接口.Net实现
- 自制XILINX FPGA JTAG下载器
- GCD中的dispatch_barrier_async函数的使用(栅栏函数)
- 【SpringCloud 2021.0.0】12、路由网关Gateway之简介 (spring-boot 2.6.3)
- 植物大战僵尸——纯C实现无限阳光
- SMART目标管理方法
- 21年1.9c#halcon机器视觉软件系统框架源码visi onpro
- 学习笔记1--自动驾驶汽车介绍