8.2(java学习笔记)反射
一、基础知识
对象是表示或封装一些数据,一个类被加载后JVM会创建一个对应该类的Class对象,
类的整个结构信息会被放在对应的对象中,通过这个对象我们可以获取改类的全部信息,
而这些操作称为反射。
二、反射基本操作
2.1获取对象类
上面说了每一个类在加载时会创建一个对应该类的Class对象,这个对象中存放着这个类相对应的信息。我们通过反射可以对这个类进行操作。
那么首先我们要获取这个类对应的Class对象,
我们可以通过Class.forName(path);也可以直接调用该对象getClass()方法。
User类
public class User { private String userName; private int age; private String sex; private String pass; public User() { } public User(String userName, int age, String sex, String pass) { super(); this.userName = userName; this.age = age; this.sex = sex; this.pass = pass; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public String getPass() { return pass; } public void setPass(String pass) { this.pass = pass; } }
public class TestGetInfo { public static void main(String[] args) throws ClassNotFoundException{ User u = new User(); 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);//获取指定属性
import java.lang.reflect.Field; public class TestGetInfo { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException{ User u = new User(); 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:private java.lang.String GetClassInfo.User.userName f1:private int GetClassInfo.User.age f1:private java.lang.String GetClassInfo.User.sex f1:private java.lang.String GetClassInfo.User.pass f3:private int GetClassInfo.User.age
2.3获取方法
Method[] getDeclaredMethods();//获取该类所有方法
//获取指定方法,name为方法名,paremeterTypes为参数类型对应的Class对象
方法可能有重名的情况,这时需要方法名加参数类型确定具体方法。
Method[] getDeclaredMethod(String name, Class<?>... parameterTypes);
import java.lang.reflect.Method; public class TestGetInfo { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException, InstantiationException, IllegalAccessException{ User u = new User(); Class<User> u1 = (Class<User>) 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 void GetClassInfo.User.setSex(java.lang.String) m:public void GetClassInfo.User.setPass(java.lang.String) m:public java.lang.String GetClassInfo.User.getSex() m:public java.lang.String GetClassInfo.User.getPass() m:public int GetClassInfo.User.getAge() m:public void GetClassInfo.User.setUserName(java.lang.String) m:public java.lang.String GetClassInfo.User.getUserName() m1:public void GetClassInfo.User.setAge(int)
由于类中方法都是public修饰的所以也可以用getMethod等方法获取。
2.4获取构造器
Constructor[] getDeclaredConstructors();//获取所有构造器
Constructor<T> getConstructor(Class<?>... parameterTypes);//获取指定参数的构造器
public class TestGetInfo { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException, InstantiationException, IllegalAccessException{ User u = new User(); Class<User> u1 = (Class<User>) Class.forName("GetClassInfo.User"); Constructor<User>[] con = (Constructor<User>[]) u1.getDeclaredConstructors();//获取所有构造器 Constructor<User> 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(Constructor<User> temp:con){ System.out.println("con:" + temp); } System.out.println("con1:"+con1); } }
运行结果: con:public GetClassInfo.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方法实例化对象(此时调用的是该类的无参构造进行实例化)
import java.lang.reflect.Constructor; public class TestGetInfo { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException, InstantiationException, IllegalAccessException{ User u = new User(); Class<User> u1 = (Class<User>) Class.forName("GetClassInfo.User"); User user = u1.newInstance(); user.setAge(19); System.out.println(user.getAge()); } }
运行结果:
19
2.5.2调用指定有参构造方法构造对象
1)、获取对应有参构造方法
2)、通过获取的有参构造方法构造对象
import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; public class TestGetInfo { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException, InstantiationException, IllegalAccessException{ User u = new User(); Class<User> u1 = (Class<User>) Class.forName("GetClassInfo.User"); Constructor<User> 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...
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class TestGetInfo { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException, InstantiationException, IllegalAccessException{ User u = new User(); Class<User> u1 = (Class<User>) 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);//表示不做访问检查,直接访问。
import java.lang.reflect.Field; public class TestGetInfo { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException, InstantiationException, IllegalAccessException{ User u = new User(); Class<User> u1 = (Class<User>) 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
使用反射可以完成平常无法完成的一些操作,但反射会造成程序效率低下。
下面举个例子看下使用反射与不使用反射直接的差距:
import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Date; public class TestGetInfo { public static void test1(){ Date start = new Date(); User u = new User(); for(int i = 0; i < 1000000000L; i++){ u.getAge(); } Date end = new Date(); System.out.println("不使用反射直接调用所消耗时间:" + (end.getTime() - start.getTime())); } public static void test2(){ Date start = new Date(); User u = new User(); Class<User> clazz = (Class<User>) 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 = new Date(); System.out.println("使用反射调用所消耗时间(做访问检查):" + (end.getTime() - start.getTime())); } public static void test3(){ Date start = new Date(); User u = new User(); Class<User> clazz = (Class<User>) 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 = new Date(); System.out.println("使用反射调用所消耗时间(不做访问检查):" + (end.getTime() - start.getTime())); } public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException, InstantiationException, IllegalAccessException{ test1(); test2(); test3(); } }
运行结果: 不使用反射直接调用所消耗时间:607 使用反射调用所消耗时间(做访问检查):2529 使用反射调用所消耗时间(不做访问检查):1967
我们可以看到使用反射后效率非常低,但是如果设置不进行访问检查效率会有一定的提高。
如果某一段反射进行频繁的操作,设置不进行安全检查,效率会有较大提升。
3.反射机制读取注解
到这里我们就可以结合前面的注解,将一个类和SQL表关联起来(ORM对象关系映射)。
首先我把一个类看做是一个表中一行数据的集合,那么我们要为这个类和对应的表关联起来。
其次我们还需要类中的属性与表中的属性关联起来。
例如我有这样一张表,其中id为主键会自动设置,regTime数据库会自动设置这两个不需要我们负责。
那么就剩下username 和pwd了
我们可以建一个User类,其中有username和pwd。
再为这个类设置一个注解主要标识表名,为类中属性也设置一个注解主要标识列名和类型。
这样我们创建的类就和这个表对应起来了。
注解:
1.@TableName
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(value={ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME)//可以被反射读取 public @interface TableName { String value(); }
2.@FieldName
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 FieldName { String cloName(); String type(); }
User类
@TableName(value="t_user")//为类添加注解 public class User { //为属性添加注解 @FieldName(cloName = "username", type = "varchar") private String userName; @FieldName(cloName = "pwd", type = "varchar") private String pass; public User() { } public User(String userName, int age, String sex, String pass) { super(); this.userName = userName; this.pass = pass; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getPass() { return pass; } public void setPass(String pass) { this.pass = pass; } }
读取注解,建立SQL插入语句:
读取注解需要用到:public <T extends Annotation> T getAnnotation(Class<T> annotationClass)获取注解;
annotationClass为注解对应的类对象(注解名.Class),返回类型是注解类型。
例如调用的是xxx.getAnnotation(TableName.class),那么返回的就是TableName类型,即对应的注解,通过这个就可以获取注解中具体的值。
要想获取类的注解,要先获取类,同样获取属性的注解要想获取属性。
import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedType; import java.lang.reflect.Field; public class TestGetInfo { public static void main(String[] args) throws ClassNotFoundException, 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的用户。