Java反射的应用
反射的应用
后续使用代码使用User类
class User{
private String name;
private int id;
private int age;
public User(){}
public User(String name, int id, int age) {
this.name = name;
this.id = id;
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", id=" + id +
", age=" + age +
'}';
}
//get&set
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public int getId() {return id;}
public void setId(int id) {this.id = id;}
public int getAge() {return age;}
public void setAge(int age) {this.age = age;}
}
创建运行时类的对象
获取运行时类的完整结构
通过反射获取运行时类的完整结构
类名、属性、指定属性、方法、指定方法、构造器、指定构造器
Class c1 = Class.forName("com.chachan53.class13annotation.reflection.User");
//获得类的名字
System.out.println(c1.getName()); //包+类名
System.out.println(c1.getSimpleName()); //类名
//类的属性
Field[] fields = c1.getFields(); //只能找到public属性
fields = c1.getDeclaredFields(); //得到所有属性
for (Field field : fields) {
System.out.println(field);
}
//获得指定属性的值(获得属性时传入属性)
//Field name1 = c1.getField("name");//只能找到public属性
Field name = c1.getDeclaredField("name");
System.out.println(name);
//获得类的方法
Method[] methods = c1.getMethods(); //获得本类及其父类的所有public方法
for (Method method : methods) {
System.out.println("getMethods:"+method);
}
methods = c1.getDeclaredMethods(); //获得本类的所有方法
for (Method method : methods) {
System.out.println("getDeclaredMethods:"+method);
}
//获得指定方法(类名,参数)
//参数是为了避免重载的情况
Method getName = c1.getMethod("getName", null);
System.out.println(getName);
Method setName = c1.getMethod("setName", String.class);
//获得构造器
Constructor[] constructors = c1.getConstructors();//获得public
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
Constructor[] declaredConstructors = c1.getDeclaredConstructors();//获得全部方法
for (Constructor declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor);
}
//获得指定构造器
Constructor constructor = c1.getConstructor(String.class, int.class, int.class);
动态创建对象
- 创建类的对象:调用Class类对象的
newInstance()
方法- 类必须有一个无参构造器
- 类的构造器的访问权限需要足够
- 没有无参构造器的对象创建
- 通过Class类的
getDeclaredConstructor(Class ... parameterTypes)
获得本类的指定形参类型的构造器 - 向构造器的形参中传递一个对象数组,其中包含构造器中所需的各个参数
- 通过
Construtor
实例化对象
- 通过Class类的
代码实现
- 获得class对象
//获得class对象
Class c1 = Class.forName("com.chachan53.class13annotation.reflection.User");
- 构造对象
//构造对象
//过时替代:.getDeclaredConstructor().newInstance()
User user = (User)c1.newInstance(); //调用了无参构造器,使用需要有无参构造器,权限最好是public
System.out.println(user);
//通过构造器创建对象(无参构造器不存在时也可使用)
Constructor constructor = c1.getConstructor(String.class, int.class, int.class);
User user2 = (User)constructor.newInstance("chachan53", 1, 20);
System.out.println(user2);
调用指定方法
-
通过反射调用类中的方法:
- 通过
getMethod()
方法获得Method对象,并设置此方法操作时需要的参数类型 - 使用
Object.invoke(Object obj, Object[] args)
进行调用,并向方法中传递要设置的参数信息
- 通过
-
Object.invoke(Object obj, Object[] args)
注意事项:- Object对应原方法的返回值,若原方法无返回值,返回
null
- 若原方法为静态方法,形参
Object obj
可为null
- 原方法形参列表为空,
Object[] args
为null
- 原方法声明为
private
,需要在调用此invoke()
方法前,显示调用方法对象的setAccessible(true)
方法,才可询问private方法
(降低使用效率)
- Object对应原方法的返回值,若原方法无返回值,返回
-
setAccessible(true)
:- 方法Method、属性Field、构造器Constructior都有
setAccessible(true)
方法 setAccessible()
是启动和禁用访问安全检测的开关,默认为false
,进行检查- 参数值为
true
则指示反射的对象在使用时应取消Java语言访问检查- 提高反射效率,代码中必须使用而该代码需要频繁调用时,应设为
true
- 使无法访问的
private
可被访问
- 提高反射效率,代码中必须使用而该代码需要频繁调用时,应设为
- 方法Method、属性Field、构造器Constructior都有
代码实现
- 获得class对象
//获得class对象
Class c1 = Class.forName("com.chachan53.class13annotation.reflection.User");
- 通过反射调用普通方法
//通过反射调用普通方法
User user3 = (User)c1.newInstance();
//通过反射获取一个方法
Method setName = c1.getDeclaredMethod("setName", String.class);
//激活:invoke(对象,"方法的值")
setName.invoke(user3,"chachan53");
System.out.println(user3.getName());
- 通过反射操作属性
User user4 = (User)c1.newInstance();
Field name = c1.getDeclaredField("name");
//不能直接操作私有属性,需要关闭程序的安全检测,使用属性或方法的setAccessible(ture)
name.setAccessible(true);
name.set(user4,"chachan53");
System.out.println(user4.getName());
性能分析
- 普通方法调用
public static void test1(){
User user = new User();
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
user.getName();
}
long endTime = System.currentTimeMillis();
System.out.println("普通方法:" + (endTime - startTime) + "ms");
}
- 反射调用
public static void test2() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
User user = new User();
Class c1 = user.getClass();
Method getName = c1.getMethod("getName",null);
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
getName.invoke(user,null);
}
long endTime = System.currentTimeMillis();
System.out.println("反射:" + (endTime - startTime) + "ms");
}
- 反射调用(关闭检测)
public static void test3() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
User user = new User();
Class c1 = user.getClass();
Method getName = c1.getMethod("getName",null);
getName.setAccessible(true);//关闭检测
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
getName.invoke(user,null);
}
long endTime = System.currentTimeMillis();
System.out.println("反射关闭权限:" + (endTime - startTime) + "ms");
}
实验结果
普通方法:4ms
反射:1576ms
反射关闭权限:948ms
操作泛型
- Java采用泛型擦除的机制引入泛型,其中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换问题,编译完成所有和泛型有关的类型全部擦除(class文件中留有信息)
- 通过反射操作这些类型,一些与原始类齐名的类型:
ParameterizedType
:表示一种参数化类型,比如Collection<String>
GenericArrayType
:表示一种元素类型是参数化类型或者类型变量的数组类型TypeVariable
:是各种类型变量的公共父接口WildcardType
:代表一种通配符类型的表达式
代码实现
- 设置方法
public void test01(Map<String,User> map, List<User> list){
System.out.println("test01");
}
public Map<String, User> test02(){
System.out.println("test02");
return null;
}
- 实现主函数
public static void main(String[] args) throws NoSuchMethodException {
//通过类获得方法
Method method1 = Test11.class.getMethod("test01", Map.class, List.class);
//获得方法的泛型参数
Type[] genericParameterTypes = method1.getGenericParameterTypes();
//输出泛型
for (Type genericParameterType : genericParameterTypes) {
System.out.println("#"+genericParameterType);
//判断是否属于ParameterizedType参数化类型(是不是泛型)
if (genericParameterType instanceof ParameterizedType){
//属于则进行类型强制转换,并用getActualTypeArguments获得其中参数的类型
Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}
}
}
Method method2 = Test11.class.getMethod("test02", null);
//获得返回值的泛型类型
Type genericReturnType = method2.getGenericReturnType();
if (genericReturnType instanceof ParameterizedType){
//属于则进行类型强制转换,并用getActualTypeArguments获得其中参数的类型
Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}
}
}
- 实验结果
class java.lang.String
class com.chachan53.class13annotation.reflection.User
#java.util.List<com.chachan53.class13annotation.reflection.User>
class com.chachan53.class13annotation.reflection.User
class java.lang.String
class com.chachan53.class13annotation.reflection.User
反射操作注解
- getAnnotations
- getAnnotation
练习:ORM
ORM:Object relationship Mapping,对象映射关系
- 类和标结构对应
- 属性和字段对应
- 对象和记录对象
代码实现
- 自定义注释和类
//类名的注解
@Target(ElementType.TYPE)//注解的使用位置
@Retention(RetentionPolicy.RUNTIME)//注解的有效范围
@interface Tabeltest{
String value();
}
//属性的注解
@Target(ElementType.FIELD)//注解的使用位置
@Retention(RetentionPolicy.RUNTIME)//注解的有效范围
@interface Fieldtest{
String columuName();//列名
String type();//类型
int length();//长度
}
@Tabeltest("test_table")
class Student2{
@Fieldtest(columuName = "table_id",type = "int", length = 10)
private int id;
@Fieldtest(columuName = "table_age",type = "int", length = 10)
private int age;
//字符串类型一般使用varchar
@Fieldtest(columuName = "table_name",type = "varchar", length = 3)
private String name;
public Student2() {}
public Student2(int id, int age, String name) {
this.id = id;
this.age = age;
this.name = name;
}
//get&set
public int getId() {return id;}
public void setId(int id) {this.id = id;}
public int getAge() {return age;}
public void setAge(int age) {this.age = age;}
public String getName() {return name;}
public void setName(String name) {this.name = name;}
@Override
public String toString() {
return "Student2{" +
"id=" + id +
", age=" + age +
", name='" + name + '\'' +
'}';
}
}
- 使用
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
Class c1 = Class.forName("com.chachan53.class13annotation.reflection.Student2");
//通过反射获得注解
Annotation[] annotations = c1.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}//@com.chachan53.class13annotation.reflection.Tabeltest(value=test_table)
//获得注解的value值
//获得指定注解,进行强制转换,通过value()获得方法的值
Tabeltest annotation = (Tabeltest)c1.getAnnotation(Tabeltest.class);
System.out.println(annotation.value());//test_table
//获得类指定的注解
Field field = c1.getDeclaredField("name");
Fieldtest annotation1 = field.getAnnotation(Fieldtest.class);
System.out.println(annotation1.columuName());//table_name
System.out.println(annotation1.type());//varchar
System.out.println(annotation1.length());//3
}