反射
Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制,反射被视为动态语言的关键。
注:字节码对象其实就是类加载到java虚拟机中的⼀种状态/另⼀种表现形式(因为jvm不能直接运⾏自己写的类,所以需要转换字节码Class)
Java反射的原理就是获取Class
对象(类在运⾏时叫字节码对象java.lang.Class
)然后使用java.lang.reflect
里提供的方法操作Class
对象,Class
与java.lang.reflect
构成了java的反射技术,反射代码的⼊⼝就是字节码对象,所以如何获取到字节码对象是关键。
-
优点:
⾮常灵活,功能强⼤(可以获取私有的信息,破坏别⼈代码)
-
缺点:
破坏封装(单例),影响性能(反射的效率要⽐⾮反射操作要低)
反射的使用
字节码对象的获取⽅式:
-
类名.class
-
对象名.getClass()
-
Class.forName(全类名)
public class TestReflect {
/**
* Constructor<?>[] getConstructors():返回所有公共构造方法对象的数组
*/
@Test
public void testContructor1() throws Exception {
//1.获取Student类的Class对象
Class<?> clazz = Class.forName("reflect.constructor.Student");
//2.获取clazz的所有的公有构造方法
Constructor<?>[] constructors = clazz.getConstructors();
//3.遍历打印所有的构造方法
for (Constructor<?> constructor:constructors) {
System.out.println(constructor);
}
}
/**
* Constructor<?>[] getDeclaredConstructors():返回所有构造方法对象的数组(包括私有)
*/
@Test
public void testContructor2() throws Exception {
//1.获取Student类的Class对象
Class<?> clazz = Class.forName("reflect.constructor.Student");
//2.获取clazz的所有的公有构造方法
Constructor<?>[] Constructors = clazz.getDeclaredConstructors();
//3.遍历打印所有的构造方法
for (Constructor<?> constructor:Constructors) {
System.out.println(constructor);
}
}
/**
* Constructor<T> getConstructor(Class<?>... parameterTypes):返回单个公共构造方法对象
*/
@Test
public void testContructor3() throws Exception {
//1.获取Class对象
Class<?> clazz = Class.forName("reflect.constructor.Student");
//2.获取公有的无参构造方法 --注意:小括号中,一定要跟构造方法的形参保持一致.
Constructor<?> constructor = clazz.getConstructor(String.class,int.class);
//3.获取公有的有参构造方法
System.out.println(constructor);
}
/**
* Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes):返回单个构造方法对象
*/
@Test
public void testContructor4() throws Exception {
//1.获取Class对象
Class<?> clazz = Class.forName("reflect.constructor.Student");
//2.获取私有的构造方法 --注意:小括号中,一定要跟构造方法的形参保持一致.
Constructor<?> constructor = clazz.getDeclaredConstructor(String.class);
}
}
反射获取构造⽅法并使⽤:
方法 | 描述 |
---|---|
Constructor<?>[] getConstructors() |
返回所有公共构造⽅法对象的数组 |
Constructor<?>[] getDeclaredConstructors() |
返回所有构造⽅法对象的数组 |
Constructor getConstructor(Class<?>... parameterTypes) |
返回单个公共构造⽅法对象 |
Constructor getDeclaredConstructor(Class<?>... parameterTypes) |
返回单个构造⽅法对象 |
T newInstance(Object...initargs) |
根据指定的构造⽅法创建对象 |
setAccessible(boolean flag) |
设置为true,表示取消访问检查 |
public class TestReflect2 {
/**
* T newInstance(Object...initargs) | 根据指定的构造方法创建对象
*/
@Test
public void testNewInstance1() throws Exception {
//1.获取class对象
Class<?> clazz = Class.forName("reflect.constructor.Student");
//2.获取构造方法对象-公共有参
Constructor<?> constructor = clazz.getConstructor();
//3.利用newInstance创建Student的对象
Object o = constructor.newInstance();
System.out.println(o);
}
/**
* T newInstance(Object...initargs) | 根据指定的构造方法创建对象
*/
@Test
public void testNewInstance2() throws Exception {
//1.获取Student类的Class对象
Class<?> clazz = Class.forName("reflect.constructor.Student");
//2.获取clazz的所有的公有构造方法-空参
Constructor<?> constructor = clazz.getConstructor();
//3.利用空参来创建Student的对象
Student stu = (Student)constructor.newInstance();
System.out.println(stu);
//补充:在Class类中,有一个newInstance方法,可以利用空参直接创建一个对象
}
/**
* T newInstance(Object...initargs) | 根据指定的构造方法创建对象
* setAccessible(boolean flag) | 设置为true,表示取消访问检查
*/
@Test
public void testNewInstance3() throws Exception {
//获取一个私有的构造方法并创建对象
//1.获取Class对象
Class<?> clazz = Class.forName("reflect.constructor.Student");
//2.获取一个私有化的构造方法.
Constructor<?> constructor = clazz.getDeclaredConstructor(String.class);
//3.被private修饰的成员,不能直接使用的,如果用反射强行获取并使用,需要临时取消访问检查
constructor.setAccessible(true);
//4.创建对象
Student stu = (Student)constructor.newInstance("Alex");
System.out.println(stu);
}
}
反射获取成员变量并使⽤:
方法 | 描述 |
---|---|
Field[] getFields() |
返回所有公共成员变量对象的数组 |
Field[] getDeclaredFields() |
返回所有成员变量对象的数组 |
Field getField(String name) |
返回单个公共成员变量对象 |
Field getDeclaredField(String name) |
返回单个成员变量对象 |
void set(Object obj, Object value) |
给成员变量赋值 |
Object get(Object obj) |
给成员变量获取值 |
反射获取成员方法并使⽤:
方法 | 描述 |
---|---|
Method[] getMethods() |
返回所有公共成员⽅法对象的数组,包括继承的 |
Method[] getDeclaredMethods() |
返回所有成员⽅法对象的数组,不包括继承的 |
Method getMethod(String name, Class<?>...parameterTypes) |
返回单个公共成员⽅法对象 |
Method getDeclaredMethod(String name,Class<?>... parameterTypes) |
返回单个成员⽅法对象 |
Object invoke(Object obj, Object... args) |
运⾏⽅法 |
注解
注解(Annotation),也叫元数据,标签,注释。 它是JDK1.5及以后版本引⼊的⼀个特性,与类、接⼝、枚举是在同⼀个层次。
可以声明在包、类、字段、⽅法、局部变量、⽅法参数等的前⾯,⽤来对这些元素进⾏说明,注释。
-
注解与注释的区别
注释://,/**/ 写给程序员看的;
注解:@Override 写给程序看的;
-
注解的作用:
-
编写⽂档:事实上,在Java中我们可以通过注解去⽣成API⽂档,例如我们常⻅的参数值
@parameter
,返回值@return
等,只有标识了这些,才能通过API⽂档更快速和有条理的查看到对应的相关信息。 -
分析代码:(单独使⽤)是主要作⽤,通过代码⾥标识的注解对代码进⾏分析使⽤反射
(⽐如
@Data
、@Service
、@Controller
等)极⼤的提⾼开发效率- 编译检查:通过代码⾥标识的注解让编译器能够实现基本的编译检查,⽐如
@Override
、@SuppressWarnings
-
注解的语法
@ + 注解的名称 – ⽐如@Override
,@SuppressWarnings
等;
@Override
:编译检查,⽤来检查标记的⽅法是否复写了⽗类的⽅法
- 只能⽤在⽅法上,写在其他上⾯会报错(编译器报错);
- 可以不写,但是不进⾏检查,如果是复写的⽅法,写错了也不报错;
- 写上注解之后,复写⽅法⽅法名字和参数列表就必须和⽗类的⽅法⼀致,如果写错,编译器会
帮你检查,会报错;
@SuppressWarnings
:抑制/增压警告
- 可以写在代码中,写在⽅法上,写在类上⾯,三者作⽤域不⼀样;
- 可以不写,但是代码/包/⼯程上会出现⻩⾊警告
- 写上之后,代码/包/⼯程上不会出现⻩⾊警告
@Deprecated
:标志当前⽅法或者类已经过时
- 可以使⽤在类中,⽅法中,⽤在⽅法中居多;
- 过时的⽅法有可能在⽤,所以不能删除,不能注释掉。只是建议以后不要使⽤;
- Java中也有已经过时的⽅法(Date对象的
toLocaleString()
⽅法)
@SafeVarargs
:抑制堆污染
堆污染:当使⽤带泛型和可变参数的⽅法时,就会出现编译警告,可以使⽤@SafeVarargs
抑制该
警告。@SafeVarargs
是jdk1.7才出现的,之前使⽤@SuppressWarings
解决
元注解
注解上⾯的注解,写在注解上⾯的,对注解进⾏说明的。 元注解在注解上才有作⽤,如果⾃定义注解,需要了解元注解;
分类:
@Target
:指定该注解的使⽤范围,没有指定什么地⽅都可以⽤;
-
CONSTRUCTOR:⽤于描述构造器
-
FIELD:⽤于描述字段
-
LOCAL_VARIABLE:⽤于描述局部变量
-
METHOD:⽤于描述⽅法
-
PACKAGE:⽤于描述包
-
PARAMETER:⽤于描述参数
-
TYPE:⽤于描述类、接⼝(包括注解类型) 或enum声明
@Retention
:指定⾃定义注解在代码中⽣效的⽣命周期;
-
SOURCE:源码阶段,⼀旦编译成字节码⽂件就不存在了, 在字节码⽂件中该注解是不存在的 。
-
Class:字节码阶段,⼀旦加载到JVM就不存在了。
-
Runtime:运⾏时,在JVM执⾏时还存在。
@Documented
:指定⾃定义注解在⽣成⽂档时,是否显示。
- 如果某个注解使⽤了
@Deprecated
元注解,那么这个注解会显示在⽂档中,否则不出现; 相反@Override
注解上没有@Documented
⽂档,那么所有使⽤@Override
注解的⽅法上,在⽣成 ⽂档时,都看不⻅@Override
注解 。
@Inherited
:指定⾃定义注解是否被继承
- 例如⾃定义注解上有元注解
@Inherited
假设⼀个注解在定义时,使⽤了@Inherited
,然后该注解在⼀个类上使⽤,如果这个类有⼦类, 那么通过反射我们可以从类的⼦类上获取到同样的注解。
注解的三要素
第⼀要素:⾸先需要⼀个注解(其他⼈写的注解 or ⾃定义的注解)。写好的注解:不需要⾃⼰去实现,别⼈已经写好了功能,直接使⽤就是;⾃定义注解:需要⾃⼰写代码去实现功能;
第⼆要素:使⽤方式,可以在哪些地方使用
第三要素:功能实现