文章目录
- 1. 创建运行时类的对象
- 2. 获取运行时类的完整结构
- 2.1 相关API
- 2.2 获取所有的属性及相关细节
- 2.3 获取所有的方法及相关细节
- 2.4 获取其他结构(构造器、父类、接口、包、注解等)
- 2.5 获取泛型父类信息
- 2.6 获取内部类或外部类信息
- 2.7 总 结
- 3. 调用运行时类的指定结构
- 3.1 调用指定的属性
- 3.2 调用指定的方法
- 3.3 关于setAccessible方法的使用
- 4. 读取注解信息
- 4.1 声明自定义注解
- 4.2 使用自定义注解
- 4.3 读取和处理自定义注解
- 5. 体会反射的动态性
1. 创建运行时类的对象
创建运行时类的对象是反射机制应用最多的地方。创建运行时类的对象有两种方式:
方式1:直接调用Class对象的newInstance()方法
要求:
1)类必须有一个无参数的构造器。
2)类的构造器的访问权限需要足够。
方式一的步骤:
1)获取该类型的Class对象
2)调用Class对象的newInstance()
方法创建对象
方式2:通过获取构造器对象来进行实例化
方式二的步骤:
1)通过Class类的getDeclaredConstructor(Class ... parameterTypes)
取得本类的指定形参类型的构造器
2)向构造器的形参中传递一个对象数组进去,里面包含了构造器中所需的各个参数。
3)通过Constructor
实例化对象。
如果构造器的权限修饰符修饰的范围不可见,可以调用setAccessible(true)
示例代码:
import org.junit.Test;
import java.lang.reflect.Constructor;
public class TestCreateObject {
@Test
public void test1() throws Exception{
Class<?> clazz = Class.forName("com.example.ext.demo.Person");
Object obj = clazz.newInstance();
System.out.println(obj);
}
@Test
public void test2()throws Exception{
Class<?> clazz = Class.forName("com.example.ext.demo.Person");
Object stu = clazz.newInstance();
System.out.println(stu);
}
@Test
public void test3()throws Exception{
Class<?> clazz = Class.forName("com.example.ext.demo.Person");
Constructor<?> constructor = clazz.getDeclaredConstructor(String.class,int.class);
Object obj = constructor.newInstance("张三", 18);
System.out.println(obj);
}
}
2. 获取运行时类的完整结构
可以获取:包、修饰符、类型名、父类(包括泛型父类)、父接口(包括泛型父接口)、成员(属性、构造器、方法)、注解(类上的、方法上的、属性上的)。
2.1 相关API
public Class<?>[] getInterfaces()
public Class<? Super T> getSuperclass()
public Constructor<T>[] getConstructors()
public Constructor<T>[] getDeclaredConstructors()
public int getModifiers();
public String getName();
public Class<?>[] getParameterTypes();
public Method[] getDeclaredMethods()
public Method[] getMethods()
public Class<?> getReturnType()
public Class<?>[] getParameterTypes()
public int getModifiers()
public Class<?>[] getExceptionTypes()
public Field[] getFields()
public Field[] getDeclaredFields()
public int getModifiers()
public Class<?> getType()
public String getName()
get Annotation(Class<T> annotationClass)
getDeclaredAnnotations()
Type getGenericSuperclass()
getActualTypeArguments()
Package getPackage()
2.2 获取所有的属性及相关细节
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import org.junit.Test;
import com.example.java1.Person;
public class FieldTest {
@Test
public void test1(){
Class clazz = Person.class;
Field[] declaredFields = clazz.getDeclaredFields();
for(Field f : declaredFields){
System.out.println(f);
}
}
@Test
public void test2(){
Class clazz = Person.class;
Field[] declaredFields = clazz.getDeclaredFields();
for(Field f : declaredFields){
int modifier = f.getModifiers();
System.out.print(Modifier.toString(modifier) + "\t");
Class type = f.getType();
System.out.print(type.getName() + "\t");
String fName = f.getName();
System.out.print(fName);
System.out.println();
}
}
}
2.3 获取所有的方法及相关细节
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import org.junit.Test;
import com.example.java1.Person;
public class MethodTest {
@Test
public void test1() {
Class clazz = Person.class;
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method m : declaredMethods) {
System.out.println(m);
}
}
@Test
public void test2() {
Class clazz = Person.class;
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method m : declaredMethods) {
Annotation[] annos = m.getAnnotations();
for (Annotation a : annos) {
System.out.println(a);
}
System.out.print(Modifier.toString(m.getModifiers()) + "\t");
System.out.print(m.getReturnType().getName() + "\t");
System.out.print(m.getName());
System.out.print("(");
Class[] parameterTypes = m.getParameterTypes();
if (!(parameterTypes == null && parameterTypes.length == 0)) {
for (int i = 0; i < parameterTypes.length; i++) {
if (i == parameterTypes.length - 1) {
System.out.print(parameterTypes[i].getName() + " args_" + i);
break;
}
System.out.print(parameterTypes[i].getName() + " args_" + i + ",");
}
}
System.out.print(")");
Class[] exceptionTypes = m.getExceptionTypes();
if (exceptionTypes.length > 0) {
System.out.print("throws ");
for (int i = 0; i < exceptionTypes.length; i++) {
if (i == exceptionTypes.length - 1) {
System.out.print(exceptionTypes[i].getName());
break;
}
System.out.print(exceptionTypes[i].getName() + ",");
}
}
System.out.println();
}
}
}
2.4 获取其他结构(构造器、父类、接口、包、注解等)
package com.example.java2;
import com.example.java1.Person;
import org.junit.Test;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
public class OtherTest {
@Test
public void test1(){
Class clazz = Person.class;
Constructor[] cons = clazz.getDeclaredConstructors();
for(Constructor c :cons){
System.out.println(c);
}
}
@Test
public void test2(){
Class clazz = Person.class;
Class superclass = clazz.getSuperclass();
System.out.println(superclass);
}
@Test
public void test3(){
Class clazz = Person.class;
Package pack = clazz.getPackage();
System.out.println(pack);
}
@Test
public void test4(){
Class clazz = Person.class;
Annotation[] annos = clazz.getAnnotations();
for (Annotation anno : annos) {
System.out.println(anno);
}
}
@Test
public void test5(){
Class clazz = Person.class;
Class[] interfaces = clazz.getInterfaces();
for (Class anInterface : interfaces) {
System.out.println(anInterface);
}
}
@Test
public void test6(){
Class clazz = Person.class;
Type genericSuperclass = clazz.getGenericSuperclass();
System.out.println(genericSuperclass);
}
}
2.5 获取泛型父类信息
示例代码获取泛型父类信息:
public class TestGeneric {
public static void main(String[] args) {
Class clazz = Son.class;
Type type = clazz.getGenericSuperclass();
ParameterizedType pt = (ParameterizedType) type;
Type[] typeArray = pt.getActualTypeArguments();
for (Type type2 : typeArray) {
System.out.println(type2);
}
}
}
class Father<T,U>{
}
class Son extends Father<String,Integer>{
}
2.6 获取内部类或外部类信息
public Class<?>[] getClasses()
:返回所有公共内部类和内部接口。包括从超类继承的公共类和接口成员以及该类声明的公共类和接口成员。
public Class<?>[] getDeclaredClasses()
:返回 Class 对象的一个数组,这些对象反映声明为此 Class 对象所表示的类的成员的所有类和接口。包括该类所声明的公共、保护、默认(包)访问及私有类和接口,但不包括继承的类和接口。
public Class<?> getDeclaringClass()
:如果此 Class 对象所表示的类或接口是一个内部类或内部接口,则返回它的外部类或外部接口,否则返回null。
Class<?> getEnclosingClass()
:返回某个内部类的外部类
@Test
public void test5(){
Class<?> clazz = Map.class;
Class<?>[] inners = clazz.getDeclaredClasses();
for (Class<?> inner : inners) {
System.out.println(inner);
}
Class<?> ec = Map.Entry.class;
Class<?> outer = ec.getDeclaringClass();
System.out.println(outer);
}
2.7 总 结
-
了解了反射这么多API,其实在实际的操作中,通过反射获取类的信息的操作代码,并不会经常开发,在框架的设计中才会被频繁使用。
-
主要要熟悉java.lang.reflect
包的作用,反射机制。
3. 调用运行时类的指定结构
3.1 调用指定的属性
在反射机制中,可以直接通过Field
类操作类中的属性,通过Field
类提供的set()
和get()
方法就可以完成设置和取得属性内容的操作。
(1)获取该类型的Class对象
Class clazz = Class.forName("包.类名");
(2)获取属性对象
Field field = clazz.getDeclaredField("属性名");
(3)如果属性的权限修饰符不是public
,那么需要设置属性可访问
field.setAccessible(true);
(4)创建实例对象:如果操作的是非静态属性,需要创建实例对象
Object obj = clazz.newInstance();
Object obj = 构造器对象.newInstance(实参...);
(4)设置指定对象obj
上此Field
的属性内容
field.set(obj,"属性值");
如果操作静态变量,那么实例对象可以省略,用null表示
(5)取得指定对象obj
上此Field
的属性内容
Object value = field.get(obj);
如果操作静态变量,那么实例对象可以省略,用null表示
示例代码:
package com.example.reflect;
public class Student {
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
package com.example.reflect;
import java.lang.reflect.Field;
public class TestField {
public static void main(String[] args)throws Exception {
Class clazz = Class.forName("com.example.reflect.Student");
Field idField = clazz.getDeclaredField("id");
idField.setAccessible(true);
Object stu = clazz.newInstance();
Object value = idField.get(stu);
System.out.println("id = "+ value);
idField.set(stu, 2);
value = idField.get(stu);
System.out.println("id = "+ value);
}
}
3.2 调用指定的方法
(1)获取该类型的Class对象
Class clazz = Class.forName("包.类名");
(2)获取方法对象
Method method = clazz.getDeclaredMethod("方法名",方法的形参类型列表);
(3)创建实例对象
Object obj = clazz.newInstance();
(4)调用方法
Object result = method.invoke(obj, 方法的实参值列表);
如果方法的权限修饰符修饰的范围不可见,也可以调用setAccessible(true)
如果方法是静态方法,实例对象也可以省略(写上也没问题),用null代替
示例代码:
package com.example.reflect;
import org.junit.Test;
import java.lang.reflect.Method;
public class TestMethod {
@Test
public void test()throws Exception {
Class<?> clazz = Class.forName("com.example.reflect.Student");
Method setNameMethod = clazz.getDeclaredMethod("setName", String.class);
Object stu = clazz.newInstance();
Object setNameMethodReturnValue = setNameMethod.invoke(stu, "张三");
System.out.println("stu = " + stu);
System.out.println("setNameMethodReturnValue = " + setNameMethodReturnValue);
Method getNameMethod = clazz.getDeclaredMethod("getName");
Object getNameMethodReturnValue = getNameMethod.invoke(stu);
System.out.println("getNameMethodReturnValue = " + getNameMethodReturnValue);
}
@Test
public void test02()throws Exception{
Class<?> clazz = Class.forName("com.example.ext.demo.Student");
Method printInfoMethod = clazz.getMethod("printInfo", String.class);
printInfoMethod.invoke(null,"北大");
}
}
3.3 关于setAccessible方法的使用
Method
和Field
、Constructor
对象都有setAccessible()
方法。setAccessible
启动和禁用访问安全检查的开关。- 参数值为
true
则指示反射的对象在使用时应该取消Java
语言访问检查。
- 提高反射的效率。如果代码中必须用反射,而该句代码需要频繁的被调用,那么请设置为
true
。 - 使得原本无法访问的私有成员也可以访问
- 参数值为
false
则指示反射的对象应该实施Java语言访问检查。
4. 读取注解信息
一个完整的注解应该包含三个部分:
(1):声明
(2):使用
(3):读取
4.1 声明自定义注解
package com.example.annotation;
import java.lang.annotation.*;
@Inherited
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Table {
String value();
}
package com.example.annotation;
import java.lang.annotation.*;
@Inherited
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Column {
String columnName();
String columnType();
}
- 自定义注解可以通过四个元注解
@Retention
,@Target
,@Inherited
,@Documented
,分别说明它的声明周期,使用位置,是否被继承,是否被生成到API文档中。 Annotation
的成员在 Annotation
定义中以无参数有返回值的抽象方法的形式来声明,我们又称为配置参数。返回值类型只能是八种基本数据类型、String类型、Class类型、enum类型、Annotation类型、以上所有类型的数组- 可以使用
default
关键字为抽象方法指定默认返回值 - 如果定义的注解含有抽象方法,那么使用时必须指定返回值,除非它有默认值。格式是“方法名 = 返回值”,如果只有一个抽象方法需要赋值,且方法名为
value
,可以省略“value=”,所以如果注解只有一个抽象方法成员,建议使用方法名value。
4.2 使用自定义注解
package com.example.annotation;
@Table("t_stu")
public class Student {
@Column(columnName = "sid",columnType = "int")
private int id;
@Column(columnName = "sname",columnType = "varchar(20)")
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
4.3 读取和处理自定义注解
自定义注解必须配上注解的信息处理流程才有意义。
我们自己定义的注解,只能使用反射的代码读取。所以自定义注解的声明周期必须是RetentionPolicy.RUNTIME
。
package com.example.annotation;
import java.lang.reflect.Field;
public class TestAnnotation {
public static void main(String[] args) {
Class studentClass = Student.class;
Table tableAnnotation = (Table) studentClass.getAnnotation(Table.class);
String tableName = "";
if(tableAnnotation != null){
tableName = tableAnnotation.value();
}
Field[] declaredFields = studentClass.getDeclaredFields();
String[] columns = new String[declaredFields.length];
int index = 0;
for (Field declaredField : declaredFields) {
Column column = declaredField.getAnnotation(Column.class);
if(column!= null) {
columns[index++] = column.columnName();
}
}
String sql = "select ";
for (int i=0; i<index; i++) {
sql += columns[i];
if(i<index-1){
sql += ",";
}
}
sql += " from " + tableName;
System.out.println("sql = " + sql);
}
}
5. 体会反射的动态性
体会1:
public class ReflectionTest {
public <T> T getInstance(String className) throws Exception {
Class clazz = Class.forName(className);
Constructor constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
return (T) constructor.newInstance();
}
@Test
public void test1() throws Exception {
String className = "com.example.java1.Person";
Person p1 = getInstance(className);
System.out.println(p1);
}
}
体会2:
public class ReflectionTest {
public Object invoke(String className,String methodName) throws Exception {
Class clazz = Class.forName(className);
Constructor constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
Object obj = constructor.newInstance();
Method method = clazz.getDeclaredMethod(methodName);
method.setAccessible(true);
return method.invoke(obj);
}
@Test
public void test2() throws Exception {
String info = (String) invoke("com.example.java1.Person", "show");
System.out.println("返回值为:" + info);
}
}
体会3:
public class ReflectionTest {
@Test
public void test1() throws Exception {
Properties pros = new Properties();
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("config.properties");
pros.load(is);
String fruitStr = pros.getProperty("fruitName");
Class clazz = Class.forName(fruitStr);
Constructor constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
Fruit fruit = (Fruit) constructor.newInstance();
Juicer juicer = new Juicer();
juicer.run(fruit);
}
}
interface Fruit {
public void squeeze();
}
class Apple implements Fruit {
public void squeeze() {
System.out.println("榨出一杯苹果汁儿");
}
}
class Orange implements Fruit {
public void squeeze() {
System.out.println("榨出一杯桔子汁儿");
}
}
class Juicer {
public void run(Fruit f) {
f.squeeze();
}
}
其中,配置文件【config.properties】存放在当前Module的src下
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)