反射机制介绍
_Class
对象获取
- 反射机制是 Java 的动态性之一
动态语言 : 在程序运行时,可以改变程序的结构或变量的类型。典型的动态语言 ”Python 、 ruby 、 JavaScripC,C++, Java 不是动态语言,但具有一定的动态性,可以称为” 准动态语言 ”, 具备类似动态语言的特性。传一块代码来动态的执行,动态的处理,Java 也能做,可以利用反射来实现类似的功能。Java 的动态性让编程变得更加的灵活,功能就更加的强大。
- 反射机制
程序在运行的过程中加载一些“只知道相关名字”的类,以下代码,在程序运行时加载 User 类。Class c=Class.forName("com.zb.reflect.User");一个类被加载后, JVM 会创建一个对应类的 Class 对象,类的整个结构信息会被放到 Class 对象中。这个 Class 对象就像镜子一样,通过这面镜子,可以得到对应类的全部信息。
- 反射机制的常见作用
1) 动态的加载类、动态的获取类的信息 ( 属性,方法,构造器)2) 动态构造对象3) 动态调用类和对象的任意方法、构造器4) 动态调用和处理属性5) 获取泛型信息6) 处理注解
- 获取 Class 对象的方式
1) 通过字节码文件2) 对象的 getClass() 方法 3) Class 类的静态方法 forName(….)
-
public class Test1 { public static void main(String[] args) throws ClassNotFoundException { System.out.println(Integer.class); System.out.println(int.class); System.out.println(void.class); //维数相同和类型相同的数组共享同一个Class对象 int [] array=new int[10]; int [] array2=new int[30]; System.out.println(array.getClass()==array2.getClass()); //同一个类的N多对象,共享同一个Class对象 User u1=new User(); User u2=new User(); System.out.println(u1.getClass()==u2.getClass()); //获取Class对象的三种方法 Class c1=u1.getClass(); Class c2=User.class; Class c3=Class.forName("day07.reflect.User"); } }
反射机制动态操作
_
方法
_
属性
_
构造器
- 获取类的名字
方法 描述 String getName()获得包名 + 类名String getSimpleName()获得类的名字 - 获得类的属性
方法 描述 Field getField(String fieldName)得到公共的属性对象Field getDeclareField(String fieldName)得到指定名称的属性对象Field []c.getDeclaredFields()得到所有的属性对象 - 获得类的方法
方法 描述 Method[] getDeclaredMethods()得到公共的方法对象Method[] c.getMethods()得到父类及本类中的公共的方法对象 Method getDeclaredMethod(String methodName, Class …type)得到指定名称的本类中公共的方法Method getMethod(String methodName, Class type)得到本类或父类中的公共的方法对象 - 获得构造方法
方法 描述 Constructor[] getDeclaredConstructors()得到公共的构造方法的对象Constructor [] getConstructors()得到公共的构造方法对象Constructor getDeclaredConstructor(Class...type)得到指定参数的公共的构造方法对象 - 动态的操作属性、方法、构造方法
-
public class Test03 { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException { Class c=Class.forName("day07.reflect.User"); System.out.println(c.getName());//包名 System.out.println(c.getSimpleName());//类名 Class cSuper=c.getSuperclass();//得到父类 Field [] fields1=c.getFields();//只能获取公共属性 Field [] fields2=c.getDeclaredFields();//获取所有属性 for(Field field:fields2){ System.out.println(field); //访问权限 类型 方法名称 System.out.println(field.getModifiers()+"\t"+field.getType()+"\t"+field.getName()); } Method [] methods=c.getDeclaredMethods(); for(Method m1:methods){ System.out.println(m1); System.out.println(m1.getModifiers()+"\t"+m1.getReturnType()+"\t"+m1.getName()); Class [] aPara=m1.getParameterTypes();//获取方法的参数也是Class类型 for(Class cs:aPara){ System.out.println(cs.getTypeName()); } Constructor [] constructors=c.getConstructors(); c.getConstructor();//获取指定的构造方法 } } }
public class Test3 { public static void main(String[] args) throws Exception { Class c= Class.forName("day07.reflect.User"); //得到无参构造方法的对象 Constructor con=c.getConstructor(null); User user=(User) con.newInstance(); //动态操作属性 Field field = c.getDeclaredField("userid"); field.setAccessible(true);//这个属性不做安全检查,直接访问 field.set(user,1001); System.out.println(field.get(user)); //动态操作方法 Method m= c.getDeclaredMethod("setUsername", String.class); m.invoke(user,"张三"); Method m2= c.getDeclaredMethod("getUsername",null); System.out.println(m2.invoke(user)); } }
提高反射效率
反射机制对程序的运行在性能上有一定的影响,速度慢
如何提高反射的性能
1)
通过
setAccessible
提高性能
a) setAccessible
启用和禁用访问安全检查的开关,值为true 则指示反射的对象在使用时应该取消
Java
语言访问检查,值为 false
则指示反射的对象不实施
Java
语言访问检查,并不是为 true
就能访问为
false
就不能访问
b)
禁止安全检查,可以提高反射的运行速度
public class Test01 {
public static void test01(){
Object o=new Object();
long starttime=System.currentTimeMillis();
for(int i=0;i<1000000000L;i++){
o.hashCode();
}
long endtime=System.currentTimeMillis();
System.out.println("普通方法执行时间为:"+(endtime-starttime));
}
public static void test02() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Object o=new Object();
Class c=o.getClass();
Method method=c.getDeclaredMethod("hashCode",null);
long starttime=System.currentTimeMillis();
for(int i=0;i<1000000000L;i++){
method.invoke(o,null);
}
long endtime=System.currentTimeMillis();
System.out.println("反射方法执行时间为:"+(endtime-starttime));
}
public static void test03() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Object o = new Object();
Class c = o.getClass();
Method method = c.getDeclaredMethod("hashCode", null);
method.setAccessible(true);
long time1 = System.currentTimeMillis();
for(int i=0;i<1000000000L;i++) {
method.invoke(o, null);
}
long time2=System.currentTimeMillis();
System.out.println("禁用安全检查反射方法执行时间为:"+(time2-time1));
}
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
test01();
test02();
test03();
}
}
反射操作泛型
- 泛型
Java 中的泛型仅仅是给编译器 javac 使用的,确保数据的安全性和免去强制类型转换的麻烦,但是一旦编译完成,所有与泛型有关的类型全部擦除。使用反射直接读取泛型,是读取不到的,因为反射是操作加载以后的类的。
- Java 新增的数据类型
为了通过反射操作这些类型以迎合实际开发的需要1) ParameterizedType : 表示一种参数化的类型 , 比 如Collection<String>,可以获取 String 信息2) GenericArrayType :泛型数组类型3) TypeVariable :各种类型变量的公共父接口4) WildcardType :代表一种通配符类型表达式,比如? extends Number,? super Integer(Wildcard 是一个单词,就是通配符 )
public class Test02 { public void test01(Map<String,User> map,List<User> list,String s){ System.out.println("test01"); } public Map<Integer,User> test02(){ System.out.println("test02"); return null; } public String test03(){ System.out.println("test03"); return null; } public static void main(String[] args) throws NoSuchMethodException { //获取test01方法的泛型参数信息 Class c1=Test02.class; Method test01= c1.getMethod("test01",Map.class,List.class,String.class); //获取泛型参数的类型 Type[] types=test01.getGenericParameterTypes(); System.out.println(types.length);//3 for(Type type:types){ //System.out.println("#"+type); if(type instanceof ParameterizedType){//只获取带有泛型的参数 Type[] g=((ParameterizedType) type).getActualTypeArguments(); for(Type t:g){ //遍历每一个泛型参数中泛型的类型 System.out.println(t);//class java.lang.String class day07.reflect.User } System.out.println("------------"); } } System.out.println("test02方法返回值的泛型信息"); Method me=c1.getMethod("test02",null); Type returnType=me.getGenericReturnType(); //是否带有泛型 if(returnType instanceof ParameterizedType){ //返回值的泛型类型 Type[] types1=((ParameterizedType) returnType).getActualTypeArguments(); for(Type t:types1){ System.out.println("返回值的泛型类型:"+t); } } System.out.println("test03"); Method m=c1.getMethod("test03",null); Type returnType1 = m.getGenericReturnType(); System.out.println(returnType1 instanceof ParameterizedType); } }
注解
-
注解的作用1) 不是程序本身,可以对程序作出解释。(这一点跟注释没什么区别)2) 可以被其他程序(比如:编译器等)读取。 ( 注解信息处理流程,是注解和注释的重大区别,如果没有注解信息处理流程,则注解毫无意义)
-
注解的格式1) 注解是以 ”@ 注释名 ” 在代码中存在,还可以添加一些参数值,例如@SuppressWarnings(value=”unchecked”)
-
注解在哪里使用1) 可以附加在 package,class,method,field 等上面,相当于给它们添加了额外的辅助信息,我们可以通过反射机制编程实现对这些元素的访问。
-
内置的注解1) @Override : 标识方法是重写的方法2) @Deprecated :标识的方法不建议使用3) @SuppressWarnings :用来抑制编译时的警告信息 @SuppressWarinings 需要提供参数才能正常使用,这些参数都是已经定义好的,我们只需要选择就可以了。
自定义注解
- 自定义注解的语法
使 用 @interface 定义自定义注解时,自动继承了 java.lang.annotation.Annotation 接口1) @interface 用来声明一个注解2) 其中的每一个方法实际上是声明了一个配置参数a) 方法的名称就是参数的名称b) 返回值类型就是参数类型 ( 返回值类型只能是基本类型、Class 、 String 、 enum)c) 可以通过 default 来声明参数的默认值d) 如果只有一个成员,一般参数名为 value注意事项:注解元素必须要有值。我们定义注解元素时,经常使用空字符串,0 作为默认值。也经常使用负数( 比如 -1) 表示不存在的含义
- 元注解
元注解的作用就是负责注解其他注解。在 Java 中定义了 4个标准的 meta-annotation 类型,它们被用来提供对其它 annotation 类型作说明这些类型和它们所支持的类在 java.lang.annotation 包中可以找到
- @Target
用于描述注解的使用范围 ( 即被描述的注解可以用在什么地方)所修饰范围取值 ElementTypepackage 包 PACKAGEPACKAGE类、接口、枚举、 Annotation 类型TYPE类型成员(方法、构造方法、 成员变量、枚举值)CONSTRUCTOR: 用于描述构造器CONSTRUCTOR: 用于描述构造器FIELD: 用于描述域METHOD: 用于描述方法方法参数和本地变量LOCAL_VARIABLE: 用于描述局部变量PARAMETER: 用于描述参数@Target(value=ElementType.TYPE)
- @Retention
表示需要在什么级别保存该注解信息 , 用于描述注解的生命周期
取值 作用 SOURCE在源文件中有效 ( 即源文件中保留)CLASS在 class 文件中有效 ( 即 class保留)RUNTIME在运行时有效 ( 即运行时保留), 被加载到程序中,可以被反射机制读取@Target(ElementType.METHOD)//使用范围在方法上 @Retention(RetentionPolicy.RUNTIME)//注解的生命周期运行时有效,可以被反射机制读取 public @interface MyAnnotation { //方法的名称就是参数名称,返回值类型就是参数类型,default声明参数的默认值 String name() default ""; int age() default 0; String[] schoolName() default {"山西大学","太原理工大学"}; }
public class Test04 { @MyAnnotation(name="weiwei",age=20,schoolName = {"山西大学","太原理工大学"}) public static void test01(){ } }
反射读取注解信息
- ORM (Object Relationship Mapping)
1) 类与表结构对应2) 属性和字段对应3) 对象和记录对应使用注解完成类和表结构的映射关系
- 功能描述
将 Java 中的 Student 类使用第三方程序通过读取注解生成数据库中的表
- 实现步骤 1) 编写 Student 类
2) 编写注解3) 在类中使用注解4) 通过解析程序将注解读取出来 ( 通过框架解析 )5) 拼接 SQL 语句,使用 JDBC 到数据库中执行创建表
-
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface sxtTable { String value(); }
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface sxtField { String conlumeName(); String type(); int length(); }
@sxtTable("table_student") public class Student { @sxtField(conlumeName = "name",type="char",length=10) private String name; @sxtField(conlumeName = "age",type="int",length=10) private int age; }
public class Test05 { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException { Class c=Class.forName("day07.reflect.Student"); System.out.println("获取类的注解"); Annotation[] a=c.getDeclaredAnnotations(); for(Annotation a1:a){ System.out.println(a1);//@day07.reflect.sxtTable(value=table_student) } System.out.println("获取指定的注解"); sxtTable sxtTable =(sxtTable)c.getDeclaredAnnotation(sxtTable.class); System.out.println(sxtTable);//@day07.reflect.sxtTable(value=table_student) System.out.println("获取属性的注解"); Field field=c.getDeclaredField("name"); // SxtField s =field.getDeclaredAnnotations(SxtField.class); // System.out.println(s); } }