反射
什么是反射
静态编译和动态编译
- 静态编译:在编译时确定类型,绑定对象
- 动态编译:运行时确定类型,绑定对象
反射机制优缺点
- 优点: 运行期类型的判断,动态加载类,提高代码灵活度。
- 缺点: 性能瓶颈:反射相当于一系列解释操作,通知 JVM 要做的事情,性能比直接的java代码要慢很多。
反射机制的应用场景有哪些?
Java获取反射的三种方法
public class Student { private int id; String name; protected boolean sex; public float score; } public class Get { //获取反射机制三种方式 public static void main(String[] args) throws ClassNotFoundException { //方式一(通过建立对象) Student stu = new Student(); Class classobj1 = stu.getClass(); System.out.println(classobj1.getName()); //方式二(所在通过路径-相对路径) Class classobj2 = Class.forName("fanshe.Student"); System.out.println(classobj2.getName()); //方式三(通过类名) Class classobj3 = Student.class; System.out.println(classobj3.getName()); } }
哪里用到反射机制?
反射机制的优缺点?
反射的功能:
1.反射机制介绍_Class 对象获取
1.1 反射机制是 Java 的动态性之一
动态语言:在程序运行时,可以改变程序的结构或变量的类型。
举例:JavaScript
Java 代码
典型的动态语言”Python、ruby、JavaScrip
- C,C++,Java 不是动态语言,但具有一定的动态性,可以称为”准动态语言”,具备类似动态语言的特性。传一块代码来动态的执行,动态的处理,Java 也能做,可以利用反射来实现类似的功能。Java的动态性让编程变得更加的灵活,功能就更加的强大。
1.2 反射机制
程序在运行的过程中加载一些“只知道相关名字”的类,以下代码,在程序运行时加载 User 类。
一个类被加载后,JVM 会创建一个对应类的 Class 对象,
类的整个结构信息会被放到 Class 对象中。
这个 Class 对象就像镜子一样,通过这面镜子,可以得到
对应类的全部信息
1.3 反射机制的常见作用
- 1) 动态的加载类、动态的获取类的信息(属性,方法,构造
- 器)
- 2) 动态构造对象
- 3) 动态调用类和对象的任意方法、构造器
- 4) 动态调用和处理属性
- 5) 获取泛型信息
- 6) 处理注解
1.4 获取 Class 对象的方式
- 1) 通过字节码文件
- 2) 对象的 getClass()方法
- 3) Class 类的静态方法 forName(….)
package com.bjsxt.entity; public class User { //类的属性 private int userId; private String userName; private String password; //公有的取值,赋值方法 public int getUserId() { return userId; } public void setUserId(int userId) { this.userId = userId; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } //构造方法 public User() { // TODO Auto-generated constructor stub } public User(int userId, String userName, String password) { super(); this.userId = userId; this.userName = userName; this.password = password; } }
package com.bjsxt.test; import com.bjsxt.entity.User; public class Test { public static void main(String[] args) throws ClassNotFoundException { System.out.println(int.class); System.out.println(void.class); int []arrA=new int[10]; int[] arrB=new int[30]; /**维数相同和类型相同的数组共享同一个Class对象*/ System.out.println(arrA.getClass()==arrB.getClass()); /**同一个类的N多对象,共享同一个Class对象*/ User u1=new User(); User u2=new User(); System.out.println(u1.getClass()==u2.getClass()); /**获取Class对象的三种方式*/ //(1)通过对象的getClass()方法获取 Class c1=u1.getClass(); //(2)通过字节码文件获取 Class c2=User.class; //(3)通过Class类的静态方法获取 Class c3=Class.forName("com.bjsxt.entity.User"); System.out.println((c1==c2)+"\t"+(c1==c3)); } }
2.反射机制动态操作_方法_属性_构造器
2.1 获取类的名字
2.2 获得类的属性
公共的指的是public类型的
2.3 获得类的方法
2.4 获得构造方法
JAVA 反射机制中,Field的getModifiers()方法返回int类型值表示该字段的修饰符。
其中,该修饰符是java.lang.reflect.Modifier的静态属性。
对应表如下:
PUBLIC: 1
PRIVATE: 2
PROTECTED: 4
STATIC: 8
FINAL: 16
SYNCHRONIZED: 32
VOLATILE: 64
TRANSIENT: 128
NATIVE: 256
INTERFACE: 512
ABSTRACT: 1024
STRICT: 2048
2.5 动态的操作属性、方法、构造方法
package reflect; public class User { //类的属性 private int userId; private String userName; private String password; //公有的取值,赋值方法 public int getUserId() { return userId; } public void setUserId(int userId) { this.userId = userId; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } //构造方法 public User() { // TODO Auto-generated constructor stub } public User(int userId, String userName, String password) { super(); this.userId = userId; this.userName = userName; this.password = password; } }
package reflect; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; public class Test { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException, NoSuchMethodException { String path="reflect.User"; //(1)获取类的名称 Class c=Class.forName(path); System.out.println("类的全名称:"+c.getName()); System.out.println("类的名称:"+c.getSimpleName()); //(2)获取父类的class对象 Class cSuper=c.getSuperclass(); System.out.println(cSuper.getName()); System.out.println(cSuper.getSimpleName()); //获取类的属性信息 //Field f=c.getField("userId");//只能获取公共属性 //System.out.println(f); Field[] fields=c.getFields();//只能获取公共属性 System.out.println(fields.length); Field[] fields2=c.getDeclaredFields(); //System.out.println(fields2.length); for (Field field : fields2) { //System.out.println(field);//调用了tostring方法 System.out.println(field.getModifiers()+"\t"+field.getType()+"\t"+field.getName()); } //获取类的方法信息 Method[] methods=c.getDeclaredMethods();//本类中的公共方法对象 System.out.println(methods.length); for(Method method:methods){ //System.out.println(method); System.out.println("访问权限:"+method.getModifiers()); System.out.println("返回值类型:"+method.getReturnType()); System.out.println("方法的名称:"+method.getName()); //方法的参数 Class<?>[] cPara = method.getParameterTypes(); for (Class c1 : cPara) { System.out.println(c1.getTypeName()+"\t"); } System.out.println("\n-----------"); } //获取类的构造器 Constructor[] cons = c.getConstructors(); for(Constructor constructor:cons){ System.out.println(constructor); } //获取指定的构造方法 Constructor con = c.getConstructor(null); System.out.println(con); Constructor con2=c.getConstructor(int.class,String.class,String.class); System.out.println(con2); } }
package reflect; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class Test2 { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchFieldException { //获取class类的对象 Class c=Class.forName("reflect.User"); //(1)得到无参构造方法的对象 Constructor cons = c.getConstructor(null); //通过无参构造方法的对象,创建User类的对象 User user =(User) cons.newInstance(); //(2)动态操作属性 Field field=c.getDeclaredField("userId"); field.setAccessible(true);//这个属性不需要做安全检查了,可以直接访问 field.set(user, 1001); System.out.println("取出userid这个属性的值"+field.get(user));//通过反射直接取值 //(3)动态操作方法 Method m = c.getDeclaredMethod("setUserName", String.class); //执行setUserName这个方法 m.invoke(user, "张三"); Method m2=c.getDeclaredMethod("getUserName", null); System.out.println(m2.invoke(user)); } }
3.提高反射效率
- 反射机制对程序的运行在性能上有一定的影响,速度慢
3.1 如何提高反射的性能
1) 通过 setAccessible 提高性能
- a) setAccessible 启用和禁用访问安全检查的开关,值为true 则指示反射的对象在使用时应该取消 Java 语言访问检查,值为 false 则指示反射的对象不实施 Java 语言访问检查,并不是为 true 就能访问为 false 就不能访问
- b) 禁止安全检查,可以提高反射的运行速度
package reflect; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class TestAccessbile { public static void test01(){ //User u=new User(); Object obj=new Object(); long startTime=System.currentTimeMillis(); for (int i = 0; i < 1000000000; i++) { obj.hashCode(); } long endTime=System.currentTimeMillis(); System.out.println("调用普通方法,循环执行一亿次"+(endTime-startTime)+"ms"); } public static void test02() throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{ Object obj=new Object(); Class c=obj.getClass(); //获取指定的方法 Method m=c.getDeclaredMethod("hashCode",null); long startTime=System.currentTimeMillis(); for(int i=0;i<1000000000;i++){ //执行这个方法 m.invoke(obj, null); } long endTime=System.currentTimeMillis(); System.out.println("调用反射,循环执行一亿次"+(endTime-startTime)+"ms"); } public static void test03() throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{ Object obj=new Object(); Class c=obj.getClass(); //获取指定的方法 Method m=c.getDeclaredMethod("hashCode",null); m.setAccessible(true);//不执行安全检查 long startTime=System.currentTimeMillis(); for(int i=0;i<1000000000;i++){ //执行这个方法 m.invoke(obj, null); } long endTime=System.currentTimeMillis(); System.out.println("调用反射,不启用安全检查循环执行一亿次"+(endTime-startTime)+"ms"); } public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { test01(); test02(); test03(); } }
4.反射操作泛型
4.1 泛型
Java 中的泛型仅仅是给编译器 javac 使用的,确保数据的安全性和免去强制类型转换的麻烦,但是一旦编译完成,所有与泛型有关的类型全部擦除。使用泛型直接读取泛型,是读取不到的,因为反射是操作加载以后的类的。
4.2Java 新增的数据类型
为了通过反射操作这些类型以迎合实际开发的需要
- 1) ParameterizedType: 表 示 一 种 参 数 化 的 类 型 ,比 如Collection<String>,可以获取 String 信息
- 2) GenericArrayType:泛型数组类型
- 3) TypeVariable:各种类型变量的公共父接口
- 4) WildcardType:代表一种通配符类型表达式,比如? extends Number,? super Integer(Wildcard 是一个单词,就是通配符)
-
package com.bjsxt.entity; public class User { }
package com.bjsxt.generice; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; public class Test { public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { List<String> alist=new ArrayList<String>(); Class c=alist.getClass();//得到Class对象 Method m=c.getDeclaredMethod("add", Object.class); //执行添加方法 m.invoke(alist, 123); //输出集合中元素的个数 System.out.println(alist.size()); } }
package com.bjsxt.generice; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.List; import java.util.Map; import com.bjsxt.entity.User; public class TestGeneric { public void test01(Map<String,User> map,List<User> list,String s){ System.out.println("TestGeneric.test01()"); } public Map<Integer,User> test02(){ System.out.println("TestGeneric.test02()"); return null; } public String test03(){ System.out.println("TestGeneric.test03()"); return null; } public static void main(String[] args) throws NoSuchMethodException, SecurityException { //获取test01方法的泛型参数信息 Class c=TestGeneric.class; Method test01=c.getMethod("test01", Map.class,List.class,String.class); //获取带泛型参数的类型 Type [] tytes=test01.getGenericParameterTypes(); System.out.println(tytes.length); for (Type type : tytes) { //System.out.println("#"+type); if (type instanceof ParameterizedType) { Type[] genericType= ((ParameterizedType) type).getActualTypeArguments(); //遍历每一个泛型参数中泛型的类型 for (Type genType : genericType) { System.out.println("泛型类型:"+genType); } System.out.println("\n--------------------------"); } } System.out.println("\n----------------------------\n"); //获取test02方法返回值的泛型信息 Method m2=c.getMethod("test02", null); Type returnType=m2.getGenericReturnType(); //判断是否带有泛型 if(returnType instanceof ParameterizedType){ Type [] types=((ParameterizedType) returnType).getActualTypeArguments(); for (Type type : types) { System.out.println("返回值的泛型类型:"+type); } } System.out.println("\n------------------------------\n"); Method m3=c.getMethod("test03", null); Type returnType3=m3.getGenericReturnType(); //System.out.println(returnType3); System.out.println(returnType3 instanceof ParameterizedType); } }
-
5.注解
5.1 注解的作用
- 1) 不是程序本身,可以对程序作出解释。(这一点跟注释没什么区别)
- 2) 可以被其他程序(比如:编译器等)读取。(注解信息处理流程,是注解和注释的重大区别,如果没有注解信息处理流程,则注解毫无意义)
5.2 注解的格式
- 1) 注解是以”@注释名”在代码中存在,还可以添加一些参数值,例如@SuppressWarnings(value=”unchecked”)。
5.3 注解在哪里使用
- 1) 可以附加在 package,class,method,field 等上面,相当于给它们添加了额外的辅助信息,我们可以通过反射机制编程实现对这些元素的访问。
5.4 内置的注解
- 1) @Override :标识方法是重写的方法
- 2) @Deprecated :标识的方法不建议使用
- 3) @SuppressWarnings:用来抑制编译时的警告信息
- @SuppressWarinings 需要提供参数才能正常使用,这些参数都是已经定义好的,我们只需要选择就可以了。
举例:
- @SuppressWarnings("unchecked")
- @SuppressWarnings(value={"unchecked","deprecation"}
-
package com.bjsxt.annotation; import java.util.ArrayList; import java.util.Date; import java.util.List; public class TestAnnotation { @Override public String toString() { // TODO Auto-generated method stub return super.toString(); } @Deprecated public void show(){ Date d=new Date(); System.out.println(d.getYear()); } @SuppressWarnings("all") public void method(){ List list=new ArrayList(); } @SuppressWarnings(value={"unchecked","path"}) public static void main(String[] args) { new TestAnnotation().show(); } }
6.自定义注解
6.1 自定义注解的语法
使 用 @interface 定 义 自 定 义 注 解 时 , 自 动 继 承 了java.lang.annotation.Annotation 接口
- 1) @interface 用来声明一个注解
-
2) 其中的每一个方法实际上是声明了一个配置参数
- a) 方法的名称就是参数的名称
- b) 返回值类型就是参数类型(返回值类型只能是基本类型、Class、String、enum)
- c) 可以通过 default 来声明参数的默认值
- d) 如果只有一个成员,一般参数名为 value
注意事项:注解元素必须要有值。我们定义注解元素时,经
常使用空字符串,0 作为默认值。
也经常使用负数(比如-1)表示不存在的含义
6.2 元注解
元注解的作用就是负责注解其他注解。在 Java 中定义了 4
个标准的 meta-annotation 类型,它们被用来提供对其它
annotation 类型作说明
这些类型和它们所支持的类在 java.lang.annotation 包中可
以找到
- 1) @Target
- 2) @Retention
- 3) @Documented
- 4) @Inherited
6.3 @Target
作用:用于描述注解的使用范围(即被描述的注解可以用在什
么地方)
举例:
@Target(value=ElementType.TYPE)
6.4 @Retention
作用:表示需要在什么级别保存该注解信息,用于描述注解的
生命周期
package com.bjsxt.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { String stuName() default ""; int age() default 0; String [] school () default {"清华大学","北京大学"}; }
package com.bjsxt.annotation; //@MyAnnotation 该注解只能应用到方法上 //@MyAnnotation2(value="aaaa") @MyAnnotation2("aaa") public class Test { @MyAnnotation(stuName="张小三") public void show(){ } @MyAnnotation(stuName="王一一",age=23,school={"北京交通大学","北京广播学院"}) public void method(){ } }
package com.bjsxt.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.TYPE,ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation2 { String value(); }
7.反射读取注解信息
7.1ORM (Object Relationship Mapping)
ORM:对象关系映射
- 1) 类与表结构对应
- 2) 属性和字段对应
- 3) 对象和记录对应
使用注解完成类和表结构的映射关系
7.2 功能描述
将Java中的Student类使用第三方程序通过读取注解生成数据库中的表
7.3 实现步骤
- 1) 编写 Student 类
- 2) 编写注解
- 3) 在类中使用注解
- 4) 通过解析程序将注解读取出来 (通过框架解析)
- 5) 拼接 SQL 语句,使用 JDBC 到数据库中执行创建表
-
package com.bjsxt.entity; import com.bjsxt.annotation.SxtField; import com.bjsxt.annotation.SxtTable; @SxtTable("tb_student") public class Student { //私有属性 @SxtField(columnName="id",type="int",length=10) private int id; @SxtField(columnName="stuname",type="varchar",length=20) private String stuName; @SxtField(columnName="age",type="int",length=10) private int age; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getStuName() { return stuName; } public void setStuName(String stuName) { this.stuName = stuName; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Student(int id, String stuName, int age) { super(); this.id = id; this.stuName = stuName; this.age = age; } public Student() { super(); } }
package com.bjsxt.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface SxtField {//属性的注解 String columnName(); //数据库中列的名称 String type(); //数据库中列的类型 int length(); //类型的长度 }
package com.bjsxt.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.TYPE) //注解的使用范围 @Retention(RetentionPolicy.RUNTIME) //在运行时起作用 public @interface SxtTable { String value(); }
package com.bjsxt.annotation.test; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import com.bjsxt.annotation.SxtField; import com.bjsxt.annotation.SxtTable; public class Test { public static void main(String[] args) throws Exception { //(1)创建Student类的Class对象 Class clazz=Class.forName("com.bjsxt.entity.Student"); //(2)得到Student类的所有注解 Annotation [] annotations=clazz.getDeclaredAnnotations(); for (Annotation a : annotations) { System.out.println(a); } System.out.println("\n----------------------------"); //(3)获取指定的注解 SxtTable st=(SxtTable) clazz.getDeclaredAnnotation(SxtTable.class); System.out.println(st); System.out.println("\n----------------------------"); //(4)获取属性的注解 Field field=clazz.getDeclaredField("stuName"); SxtField sf=field.getDeclaredAnnotation(SxtField.class); System.out.println(sf.columnName()+"--"+sf.type()+"--"+sf.length()); /**拼接SQL语句 DDL ,使用JDBC在数据库中执行,创建出了一张表,tb_student,表中的列就为id,stuname,age*/ } }