目录
面向对象中创建对象,调用指定结构(属性和方法),可以不使用反射,也可以使用。有什么区别?(面试题)
以前创建对象并调用方法的方式和反射调用的方式,哪种用的比较多?场景?
单例模式的饿汉式和懒汉式,私有化了构造器!那通过反射能创建多个单例模式的对象吗?
通过反射,可以调用类中私有的结构,是否与面向对象的封装性有冲突呢?java有BUG?
回忆:JavaBean中要求给当前类提供一个公共的空参构造器。有什么用?(讲super时也讲过)
获取运行时类的内部结构:父类、接口们、包、带泛型的父类、父类的泛型等(熟悉)
前言:此章内容在之后的学习中会经常使用,所以很重要,大多“便捷操作”的源码里都用到了反射机制
反射的理解和使用举例
概述
- JAVA给我提供了一套API,用这套API我们可以在运行时动态的获取指定对象所属的类,创建运行时类的对象,调用指定结构(属性、方法)等。
- API:java.lang.Class:代表一个类(现在只用学)
- 反射被视为动态语言的关键
优缺点
- 优点
- 提高了Java程序的灵活性和扩展性,降低耦合性,提高自适应能力
- 允许程序创建和控制任何类的对象,无需提前硬编码目标类
- 缺点
- 反射的性能较低
- 反射机制主要应用在对灵活性和扩展性要求很高的系统框架上
- 可读性差
- 反射的性能较低
反射:平时开发中使用并不多,主要在框架底层使用
问答
面向对象中创建对象,调用指定结构(属性和方法),可以不使用反射,也可以使用。有什么区别?(面试题)
- 不使用反射:需要考虑封装性,比如:出了Person类之后,就不能调用Person类中的私有结构
- 使用反射:可以调用运行时类中任意的构造器、属性、方法。包括了私有的
以前创建对象并调用方法的方式和反射调用的方式,哪种用的比较多?场景?
- 非反射的方式多一些
- 因为反射体现了动态性(可以在运行时动态的获取对象所属的类,动态的调用相关的方法),所以在设计框架的时候会大量的使用反射。意味着如果需要学习框架的源码,就需要学习反射(框架=注解+反射+设计模式)
单例模式的饿汉式和懒汉式,私有化了构造器!那通过反射能创建多个单例模式的对象吗?
对的!
通过反射,可以调用类中私有的结构,是否与面向对象的封装性有冲突呢?java有BUG?
- 不存在BUG,
- 封装性:体现的是是否建议我们调用内部API的问题,比如private代表不建议调用
- 反射:体现的是我们能否调用的问题,因为类的完整结构都加载到了内存中,所以我们就有能力进行调用。
Class:反射的源头
理解:
- 针对于编写好的.java源文件进行编译(使用javac.exe),会生成一个或多个.class的字节码文件。接着我们使用java.exe命令对指定的.class文件进行解释运行。这个解释运行的过程中,我们需要将.class字节码文件加载(类的加载器)到内存(存放到方法区)中。加载到内存中的.class文件对应的结构即为Class的一个实例。
- 比如加载到内存中的Person类,String类等,都作为Class的一个一个的实例
- 如:Class clazz1 = Person.class;Class clazz = Comparable.class//运行时类
- 说明:运行时类在内存中会缓存起来,在整个执行期间,只会加载一次
获取Class实例的方式(前三个掌握)
- 1.调用运行时类的静态属性(.class)
- 2.调用运行时类的对象的getClass()方法
- 3.调用Class的静态方法forName(String className)
- 4.使用类的加载器(了解)
Class的实例都可以指向哪些类型?
- class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类
- interface:接口
- [ ] :数组
- enum:枚举
- annotation:注解
- primitive type:基本数据类型
- void
-
类的加载过程(了解)与类的加载器(了解)
类的加载(了解)
- 装载(Loading):将类的class文件读入内存,并为之创建一个java.lang.Class对象。此过程由类的加载器完成
- 链接(Linking)
- 验证(Verify):确保加载的类的信息符合JVM规范.比如:字节码文件都以cafebabe开头,看是否符合
- 准备(Prepare):正式为类变量分配内存并设置类变量默认初始化值的阶段,这些内存将在方法区中进行分配
- 解析(Resolve):虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程。
- 初始化(Initialization)
- 执行类构造器<clinit>()方法的过程
- 类构造器<clinit>()方法是由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并构成的
类的加载器(classLoader)(了解)
作用:负责类的加载,并对应一个Class的实例
分类:
-
JVM支持两种类型的类加载器 。分别为引导类加载器(Bootstrap ClassLoader)和自定义类加载器(User-Defined ClassLoader)。
-
从概念上来讲,自定义类加载器一般指的是程序中由开发人员自定义的一类类加载器,但是Java虚拟机规范却没有这么定义,而是将所有派生于抽象类ClassLoader的类加载器都划分为自定义类加载器。
常见类加载器
BootstrapClassLoader:引导类加载器、启动类加载器
- 使用C/C++语言编写,不能通过java代码获取实例
- 用来加载Java的核心库:(如JAVA_HOME/jre/lib/rt.jar)
继承于ClassLoader的类的加载器
- ExtensionClassLoader:扩展类加载器
- 负责加载从java.ext.dirs系统属性所指定的目录中加载类库
- SystemClassLoader/ApplicationClassLoader:系统类加载器、引用程序加载器
- 我们自定义的类,默认使用的类的加载器
- 用户自定义类的加载器
- 实现应用的隔离(同一个类在一个应用程序中需要加载多份);数据加密
实用类的加载器获取流,并读取配置文件
反射的应用:1.创建运行时类的对象
如何实现:通过Class的实例调用newInstance( )方法即可。
需要满足的条件:
- 条件1:要求运行时类中必须提供一个空参构造器
- 条件2:要求提供的空参构造器的权限要足够
JavaBean
- 当前类是公共的
- 提供一个当前类的公共的空参构造器
- 有属性和其get、set方法
回忆:JavaBean中要求给当前类提供一个公共的空参构造器。有什么用?(讲super时也讲过)
- 场景1:子类对象在实例化时,子类的构造器的首行默认调用父类的空参构造器。
- 场景2:在反射中,经常用来创建运行时类的对象,那么我们要求各个运行时类都提供一个空参构造器。便于我们编写创建运行时类对象的代码。
如果当前类没有提供空参构造器
如果空参构造器用private修饰
注意
JDK9之后标识为过时,替换为通过Constructor类调用newInstance(...)
反射的应用:2.获取运行时类的完整结构
获取运行时类的结构:所有的属性、方法、构造器、注解等
1.属性:
getDeclaredFields():所有属性
getFields():声明为public的所有属性
getModifiers():权限修饰符
getType():数据类型
getName():变量名
2.方法:
getDeclaredMethods():所有方法
getFields():声明为public的所有方法
3.注解:
getAnnotations():
获取运行时类的内部结构:父类、接口们、包、带泛型的父类、父类的泛型等(熟悉)
- 获取运行时父类
-
- 获取带泛型的父类
-
- 获取运行时类实现的接口
-
- 获取运行时类所在的包
-
- 获取运行时类的父类的泛型(难)
-
反射的应用3(调用指定的属性、方法、构造器)(最重要)
调用属性
步骤:
1.通过Class的实例调用getDeclaredField(String fieldName)获取运行时类指定名的属性(Filed类的实例)
2.确保此属性可以访问(.setAccessible(true))
3.获取Field类的实例调用get(Object obj)或set(Object obj , Object value)进行操作
public修饰的属性
private修饰的属性
- 无权限时:会抛异常
- 有权限时:(按照步骤)
对于static修饰的属性(null或.Class)
调用方法
步骤
- 1.通过Class的实例调用getDeclaredMethod(String MethodName, Class…args )获取指定的方法(Method类的实例)
- 2.确保此方法可以访问(.setAccessible(true))
- 3.通过Method实例调用invoke(Objetc obj,Object...objs ),即为对Method对应的方法的调用。
- invoke()的返回值时Method对应的方法的返回值
- 特别的:如果Method对应的方法的返回值类型为void,则invoke()返回值为null。
-
调用构造器
- 步骤:
- 1.通过Class的实例调用getDeclaredConstructor(Class…args )获取指定参数类型的构造器
- 2.确保此构造器可以访问(.setAccessible(true))
- 3.通过Constructor实例调用newInstance(Object...objs ),返回一个运行时类的实例。
-
反射的应用4:注解的使用(了解,学框架时再学)
- 这是框架里面学的,不重要,现在只需要掌握怎么获得注解
- 比如:Table是注解(体会一下,可以完全不用看)
- @Test,@Table等
反射的动态性
还是只用了解即可,后期会学习