一、基础知识
对象是表示或封装一些数据,一个类被加载后JVM会创建一个对应该类的Class对象,
类的整个结构信息会被放在对应的对象中,通过这个对象我们可以获取改类的全部信息,
而这些操作称为反射。
二、反射基本操作
2.1获取对象类
上面说了每一个类在加载时会创建一个对应该类的Class对象,这个对象中存放着这个类相对应的信息。我们通过反射可以对这个类进行操作。
那么首先我们要获取这个类对应的Class对象,
我们可以通过Class.forName(path);也可以直接调用该对象getClass()方法。
User类
public classUser {privateString userName;private intage;privateString sex;privateString pass;publicUser() {
}public User(String userName, intage, String sex, String pass) {super();this.userName =userName;this.age =age;this.sex =sex;this.pass =pass;
}publicString getUserName() {returnuserName;
}public voidsetUserName(String userName) {this.userName =userName;
}public intgetAge() {returnage;
}public void setAge(intage) {this.age =age;
}publicString getSex() {returnsex;
}public voidsetSex(String sex) {this.sex =sex;
}publicString getPass() {returnpass;
}public voidsetPass(String pass) {this.pass =pass;
}
}
public classTestGetInfo {public static void main(String[] args) throwsClassNotFoundException{
User u= newUser();
Class u1=u.getClass();//对应对象.getClass()获取类对象
Class u2= Class.forName("GetClassInfo.User");//Class.forName("包名+类名")获取对象
System.out.println(u2.getName()+"\n"+u1.getName());//getName是获取完整路径名
System.out.println(u1.getSimpleName());//只获取类名
}
}
运行结果:
GetClassInfo.User
GetClassInfo.User
User
2.2获取属性名称
Filed[] getFields()//获取该类对象所代表类中所有属性,只能获取public修饰的属性,不能获取private修饰的属性。
Filed[] getDeclaredFields();//获取该类对象所代表类中所有属性和方法,包括private修饰的属性。
Filed getDeclaredFields(String name);//获取指定属性
importjava.lang.reflect.Field;public classTestGetInfo {public static void main(String[] args) throwsClassNotFoundException, NoSuchFieldException, SecurityException{
User u= newUser();
Class u1= Class.forName("GetClassInfo.User");
Field[] f1=u1.getDeclaredFields();
Field[] f2= u1.getFields();//只能访问public修饰的属性
Field f3 = u1.getDeclaredField("age");//如果用getField("age")获取会出错,无法访问私有属性
for(Field temp:f1){
System.out.println("f1:"+temp);
}for(Field temp:f2){
System.out.println("f2:"+temp);
}
System.out.println("f3:" +f3);
}
}
运行结果:
f1:privatejava.lang.String GetClassInfo.User.userName
f1:private intGetClassInfo.User.age
f1:privatejava.lang.String GetClassInfo.User.sex
f1:privatejava.lang.String GetClassInfo.User.pass
f3:private int GetClassInfo.User.age
2.3获取方法
Method[] getDeclaredMethods();//获取该类所有方法
//获取指定方法,name为方法名,paremeterTypes为参数类型对应的Class对象
方法可能有重名的情况,这时需要方法名加参数类型确定具体方法。
Method[] getDeclaredMethod(String name, Class>... parameterTypes);
importjava.lang.reflect.Method;public classTestGetInfo {public static void main(String[] args) throwsClassNotFoundException, NoSuchFieldException, SecurityException, InstantiationException, IllegalAccessException{
User u= newUser();
Class u1 = (Class) Class.forName("GetClassInfo.User");
Method[] m=u1.getDeclaredMethods();
Method m1= null;try{
m1= u1.getDeclaredMethod("setAge",int.class);//如果对应类中除了setAget(int age)还有setAge(){}方法,将参数设置为null获取的是setAge()方法
}catch(NoSuchMethodException e) {//TODO Auto-generated catch block
e.printStackTrace();
}for(Method temp:m){
System.out.println("m:" +temp);
}
System.out.println("m1:" +m1);
}
}
运行结果:
m:public void GetClassInfo.User.setAge(int)
m:public voidGetClassInfo.User.setSex(java.lang.String)
m:public voidGetClassInfo.User.setPass(java.lang.String)
m:publicjava.lang.String GetClassInfo.User.getSex()
m:publicjava.lang.String GetClassInfo.User.getPass()
m:public intGetClassInfo.User.getAge()
m:public voidGetClassInfo.User.setUserName(java.lang.String)
m:publicjava.lang.String GetClassInfo.User.getUserName()
m1:public void GetClassInfo.User.setAge(int)
由于类中方法都是public修饰的所以也可以用getMethod等方法获取。
2.4获取构造器
Constructor[] getDeclaredConstructors();//获取所有构造器
Constructor getConstructor(Class>... parameterTypes);//获取指定参数的构造器
public classTestGetInfo {public static void main(String[] args) throwsClassNotFoundException, NoSuchFieldException, SecurityException, InstantiationException, IllegalAccessException{
User u= newUser();
Class u1 = (Class) Class.forName("GetClassInfo.User");
Constructor[] con = (Constructor[]) u1.getDeclaredConstructors();//获取所有构造器
Constructor con1 = null;try{
con1= u1.getDeclaredConstructor(String.class,int.class,String.class,String.class);//获取指定构造器
}catch(NoSuchMethodException e) {//TODO Auto-generated catch block
e.printStackTrace();
}for(Constructortemp:con){
System.out.println("con:" +temp);
}
System.out.println("con1:"+con1);
}
}
运行结果:
con:publicGetClassInfo.User()
con:public GetClassInfo.User(java.lang.String,int,java.lang.String,java.lang.String)
con1:public GetClassInfo.User(java.lang.String,int,java.lang.String,java.lang.String)
2.5反射构造对象
2.5.1调用无参构造方法构造对象
1)、获取对应类对象
2)、调用newInstance方法实例化对象(此时调用的是该类的无参构造进行实例化)
importjava.lang.reflect.Constructor;public classTestGetInfo {public static void main(String[] args) throwsClassNotFoundException, NoSuchFieldException, SecurityException, InstantiationException, IllegalAccessException{
User u= newUser();
Class u1 = (Class) Class.forName("GetClassInfo.User");
User user=u1.newInstance();
user.setAge(19);
System.out.println(user.getAge());
}
}
运行结果:19
2.5.2调用指定有参构造方法构造对象
1)、获取对应有参构造方法
2)、通过获取的有参构造方法构造对象
importjava.lang.reflect.Constructor;importjava.lang.reflect.InvocationTargetException;public classTestGetInfo {public static void main(String[] args) throwsClassNotFoundException, NoSuchFieldException, SecurityException, InstantiationException, IllegalAccessException{
User u= newUser();
Class u1 = (Class) Class.forName("GetClassInfo.User");
Constructor con1 = null;
User user= null;try{//获取对应构造器
con1 = u1.getDeclaredConstructor(String.class,int.class,String.class,String.class);//通过构造器实例化对象
user = con1.newInstance("hcf",19,"man","123455");
}catch(IllegalArgumentException e) {//TODO Auto-generated catch block
e.printStackTrace();
}catch(InvocationTargetException e) {//TODO Auto-generated catch block
e.printStackTrace();
}catch(NoSuchMethodException e) {//TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("姓名:" + user.getUserName()+ "年龄" +user.getAge());
}
}
运行结果:
姓名:hcf年龄19
2.6反射调用方法
1)、获取对应方法
2)、通过方法对象的invoke()方法调用
Object invoke(Object obj, Object... args);//将该方法作用于obj对象,参数为args...
importjava.lang.reflect.InvocationTargetException;importjava.lang.reflect.Method;public classTestGetInfo {public static void main(String[] args) throwsClassNotFoundException, NoSuchFieldException, SecurityException, InstantiationException, IllegalAccessException{
User u= newUser();
Class u1 = (Class) Class.forName("GetClassInfo.User");
User user=u1.newInstance();
Method m= null;try{//获取setAge方法
m = u1.getDeclaredMethod("setAge", int.class);//调用setAget方法,作用对象是user,参数为19
m.invoke(user, 19);
}catch(NoSuchMethodException e) {//TODO Auto-generated catch block
e.printStackTrace();
}catch(IllegalArgumentException e) {//TODO Auto-generated catch block
e.printStackTrace();
}catch(InvocationTargetException e) {//TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(user.getAge());
}
}
运行结果:19
2.7反射操作属性
1)、获取指定属性
2)、调用void set(Object obj, Object value)方法设置属性值
ps:如果属性是private修饰的则需要添加setAccessible(true);//表示不做访问检查,直接访问。
importjava.lang.reflect.Field;public classTestGetInfo {public static void main(String[] args) throwsClassNotFoundException, NoSuchFieldException, SecurityException, InstantiationException, IllegalAccessException{
User u= newUser();
Class u1 = (Class) Class.forName("GetClassInfo.User");//获取对应类对象
User user=u1.newInstance();//通过反射实例化对象
Field f= u1.getDeclaredField("age");
f.setAccessible(true);//true表示反射对象在使用时不进行访问检查
f.set(user,20);
System.out.println(user.getAge());
}
}
运行结果:20
使用反射可以完成平常无法完成的一些操作,但反射会造成程序效率低下。
下面举个例子看下使用反射与不使用反射直接的差距:
importjava.lang.reflect.Field;importjava.lang.reflect.InvocationTargetException;importjava.lang.reflect.Method;importjava.util.Date;public classTestGetInfo {public static voidtest1(){
Date start= newDate();
User u= newUser();for(int i = 0; i < 1000000000L; i++){
u.getAge();
}
Date end= newDate();
System.out.println("不使用反射直接调用所消耗时间:" + (end.getTime() -start.getTime()));
}public static voidtest2(){
Date start= newDate();
User u= newUser();
Class clazz = (Class) u.getClass();
User user= null;
Method m= null;try{
user=clazz.newInstance();
m= clazz.getDeclaredMethod("getAge",null);for(int i = 0; i < 1000000000L; i++){
m.invoke(user,null);
}
}catch(InstantiationException e) {//TODO Auto-generated catch block
e.printStackTrace();
}catch(IllegalAccessException e) {//TODO Auto-generated catch block
e.printStackTrace();
}catch(NoSuchMethodException e) {//TODO Auto-generated catch block
e.printStackTrace();
}catch(SecurityException e) {//TODO Auto-generated catch block
e.printStackTrace();
}catch(IllegalArgumentException e) {//TODO Auto-generated catch block
e.printStackTrace();
}catch(InvocationTargetException e) {//TODO Auto-generated catch block
e.printStackTrace();
}
Date end= newDate();
System.out.println("使用反射调用所消耗时间(做访问检查):" + (end.getTime() -start.getTime()));
}public static voidtest3(){
Date start= newDate();
User u= newUser();
Class clazz = (Class) u.getClass();
User user= null;
Method m= null;try{
user=clazz.newInstance();
m= clazz.getDeclaredMethod("getAge",null);
m.setAccessible(true);//设置为true不做访问检查
for(int i = 0; i < 1000000000L; i++){
m.invoke(user,null);
}
}catch(InstantiationException e) {//TODO Auto-generated catch block
e.printStackTrace();
}catch(IllegalAccessException e) {//TODO Auto-generated catch block
e.printStackTrace();
}catch(NoSuchMethodException e) {//TODO Auto-generated catch block
e.printStackTrace();
}catch(SecurityException e) {//TODO Auto-generated catch block
e.printStackTrace();
}catch(IllegalArgumentException e) {//TODO Auto-generated catch block
e.printStackTrace();
}catch(InvocationTargetException e) {//TODO Auto-generated catch block
e.printStackTrace();
}
Date end= newDate();
System.out.println("使用反射调用所消耗时间(不做访问检查):" + (end.getTime() -start.getTime()));
}public static void main(String[] args) throwsClassNotFoundException, NoSuchFieldException, SecurityException, InstantiationException, IllegalAccessException{
test1();
test2();
test3();
}
}
运行结果:
不使用反射直接调用所消耗时间:607使用反射调用所消耗时间(做访问检查):2529使用反射调用所消耗时间(不做访问检查):1967
我们可以看到使用反射后效率非常低,但是如果设置不进行访问检查效率会有一定的提高。
如果某一段反射进行频繁的操作,设置不进行安全检查,效率会有较大提升。
3.反射机制读取注解
到这里我们就可以结合前面的注解,将一个类和SQL表关联起来(ORM对象关系映射)。
首先我把一个类看做是一个表中一行数据的集合,那么我们要为这个类和对应的表关联起来。
其次我们还需要类中的属性与表中的属性关联起来。
例如我有这样一张表,其中id为主键会自动设置,regTime数据库会自动设置这两个不需要我们负责。
那么就剩下username 和pwd了
我们可以建一个User类,其中有username和pwd。
再为这个类设置一个注解主要标识表名,为类中属性也设置一个注解主要标识列名和类型。
这样我们创建的类就和这个表对应起来了。
注解:
1.@TableName
importjava.lang.annotation.ElementType;importjava.lang.annotation.Retention;importjava.lang.annotation.RetentionPolicy;importjava.lang.annotation.Target;
@Target(value={ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)//可以被反射读取
public @interfaceTableName {
String value();
}
2.@FieldName
importjava.lang.annotation.ElementType;importjava.lang.annotation.Retention;importjava.lang.annotation.RetentionPolicy;importjava.lang.annotation.Target;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)public @interfaceFieldName {
String cloName();
String type();
}
User类
@TableName(value="t_user")//为类添加注解
public classUser {//为属性添加注解
@FieldName(cloName = "username", type = "varchar")privateString userName;
@FieldName(cloName= "pwd", type = "varchar")privateString pass;publicUser() {
}public User(String userName, intage, String sex, String pass) {super();this.userName =userName;this.pass =pass;
}publicString getUserName() {returnuserName;
}public voidsetUserName(String userName) {this.userName =userName;
}publicString getPass() {returnpass;
}public voidsetPass(String pass) {this.pass =pass;
}
}
读取注解,建立SQL插入语句:
读取注解需要用到:public T getAnnotation(Class annotationClass)获取注解;
annotationClass为注解对应的类对象(注解名.Class),返回类型是注解类型。
例如调用的是xxx.getAnnotation(TableName.class),那么返回的就是TableName类型,即对应的注解,通过这个就可以获取注解中具体的值。
要想获取类的注解,要先获取类,同样获取属性的注解要想获取属性。
importjava.lang.annotation.Annotation;importjava.lang.reflect.AnnotatedType;importjava.lang.reflect.Field;public classTestGetInfo {public static void main(String[] args) throwsClassNotFoundException, InstantiationException, IllegalAccessException {
Class clazz= Class.forName("GetClassInfo.User");
User user= (User)clazz.newInstance();//实例化对象
user.setUserName("gcmh");//设置信息
user.setPass("123456");
Field fName= null;
Field fPass= null;
FieldName annoFieldName= null;//属性对应的注解类型
FieldName annoFieldPass = null;//属性对应的注解类型
TableName tableName = null;//类对应的注解类型
try{//获取类的注解
tableName = (TableName) clazz.getAnnotation(TableName.class);
fName= clazz.getDeclaredField("userName");//获取Name属性
annoFieldName = fName.getAnnotation(FieldName.class);//获取Name属性的注解
fPass = clazz.getDeclaredField("pass");//获取pass属性
annoFieldPass = fPass.getAnnotation(FieldName.class);//获取pass属性的注解
} catch(NoSuchFieldException e) {//TODO Auto-generated catch block
e.printStackTrace();
}catch(SecurityException e) {//TODO Auto-generated catch block
e.printStackTrace();
}
String sqlName= "testjdbc";//数据库名//拼凑SQL插入语句
String sqlInsert = "INSERT INTO" +" `" + sqlName +"`." +
"`"+ tableName.value() + "`" +"(`" +annoFieldName.cloName()+ "`,`" +annoFieldPass.cloName()+ "`)Value("+
"'"+user.getUserName()+"','" + user.getPass()+"');";
System.out.println(sqlInsert);//输出插入语句
}
}
运行结果:
INSERT INTO `testjdbc`.`t_user`(`username`,`pwd`)Value('gcmh','123456');
在SQL中执行这个语句即可插入一个名称为gcmh密码为 123456的用户。