反射reflect
反射的概念
反射:通过Class获取字节码文件对象,使用字节码文件对象来获取类的一些描述信息
- 成员变量 Field
- 成员方法 Method
- 构造方法 Constructor
Java文件 -编译-> 字节码文件 -运行-> JVM执行Class文件(类加载)
类加载
- 当我们执行java.exe命令,这个字节码文件会被加载到内存当中
- 同时还会为这个字节码文件创建一个Class的实例
获取字节码文件对象的方式三种
1.Class类中一个静态方法
static Class<?> forName(String className)
方式一的好处: 因为传入的是一个字符串,而这个字符串可以通过配置文件配置
满足了设计原则: 开闭原则 对扩展开放,对修改关闭
2.通过数据类型的静态属性 class属性
* 如果形参需要我们传入Class类型,那么我们可以考虑使用方式二
* public void show(Class c){}
* show(int.class)
3.Object的getClass方法
如下案例:
public class T_getClass {
//获取class对象的3种方法
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
//方式一(常用)-------------------------
Class<Person> pcla = (Class<Person>) Class.forName("com.ljx.tx0911.Person");
System.out.println(pcla);
//方式2
Class<Person> pcla2=Person.class;
System.out.println(pcla2);
//方式3
Person person=new Person();
Class<? extends Person> cla3 = person.getClass();
System.out.println(cla3);
}
}
class类的方法:
通过字节码文件对象获取构造方法对象 Constructor
-
Constructor<?>[] getConstructors() // 获取公有修饰的构造方法对象
-
Constructor<?>[] getDeclaredConstructors() // 获取所有的构造方法对象,含构造
-
Constructor<T> getConstructor(Class<?>... parameterTypes) //获取单个构造方法
-
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) //获取私有单个构造方法
获取成员变量对象
-
Field[] getDeclaredFields() 获取所有成员变量对象
-
Field getDeclaredField(String name) 获取指定任意成员变量对象-》获取指定的成员变量,返回类型和名称
获取任意方法:
-
Method dm =pc.getDeclaredMethod("setId", int.class);
-
myc.getDeclaredMethod("service", null); 还有的形参对应的是null 说明service方法没有参数
-
Method[] dms = pc.getDeclaredMethods();//获取所有方法
生成Person对象:
-
pc.newInstance();
利用calss类方法创建对象的3中方式
/*
* 获取Person对象的三种方式:
* 方式1、2步骤:
* 1、获取class对象-》class.forname(uri)
* 2、获取特定构造器/构造器数组-》用class的getConstructor(类.class)
* 3、获取对象-》用构造器的newInstance
* 方式3、步骤:
* 1、获取class对象-》class.forname(uri)
* 2、获取对象-》用class对象的newInstance();//形参只能是空的,调用的是构造器的newInstance(null)
*/
Class<Person> pc = (Class<Person>) Class.forName("com.ljx.tx0911.Person");
Constructor<Person> c = pc.getConstructor(String.class,int.class);
//创建对象(有参),对应的是构造方法中的有参 ------------1
Person p = c.newInstance("ljx",1);
//创建对象(无参),对应的是构造的无参 ----------------------2
Constructor<Person> c2 = pc.getConstructor(null);
Person p2=c2.newInstance(null);
//创建对象,直接用class来创建(实质还是利用无参构造方法对象来创建的) ----------------3
Person p3 = pc.newInstance();
System.out.println(p3);
System.out.println("--------------------------");
Constructor构造器的方法:
-
c.toString://打印信息->public com.ljx.tx0911.Person()
-
c.getName():获取构造方法名com.ljx.tx0911.Person
-
c.getParameterCount():对应的方法中形参个数
-
c.getModifiers():权限修饰符 1->public 2->private 4->protected
所有案例演示的主方法和Person类:
主方法:只看第四行即可,目的是获取class对象
public static void main(String[] args) {
// TODO Auto-generated method stub
try {
Class<Person> pc = (Class<Person>) Class.forName("com.ljx.tx0911.Person");
//private java.lang.String com.ljx.tx0911.Person.name
//System.out.println(fname);
//WriteFanme(fname);
//Fieldtest(pc);
getMethods(pc);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Person类:
public class Person {
private String name;
private int id;
@Override
public String toString() {
return "Person [name=" + name + ", id=" + id + "]";
}
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 Person(String name, int id) {
super();
this.name = name;
this.id = id;
}
public Person() {
super();
}
}
案例演示
public static void test01(Class<Person> pc) throws Exception {
Constructor<Person>[] cs = (Constructor<Person>[]) pc.getConstructors();
for (Constructor<Person> c : cs) {
System.out.println(c);
System.out.println(c.getName()+" "+c.getParameterCount()+" "+c.getModifiers());//
}
System.out.println("-------------");
/* 打印信息:
* public com.ljx.tx0911.Person()
com.ljx.tx0911.Person 0 1
public com.ljx.tx0911.Person(java.lang.String,int)
com.ljx.tx0911.Person 2 1
*/
}
Field类方法结合 (class对象对属性的操作)
通过反射获取成员变量对象
利用class对象获取Field的方法:
* Field[] getFields() 获取公有修饰的成员变量对象-----不用!!
* Field[] getDeclaredFields() 获取所有成员变量对象
* Field getField(String name) 获取指定的公共成员变量对象--------不用!!
* Field getDeclaredField(String name) 获取指定任意成员变量对象-》获取指定的成员变量,返回类型和名称
Field方法:
-
Object get(Object obj) 返回指定对象上此 Field 表示的字段的值。
-
f.getname->获取属性名
-
f.getType->获取属性类型
-
给对象赋值:set(Object obj, Object value) 例子:f.set(e2, map.get(“name”));//e2为对象,value是String将指定对象变量上此 Field 对象表示的字段设置为指定的新值。(最底下的案例有体现)
-
setInt/setChar… (类似上面的函数)
-
判断是否有注解: f.isAnnotationPresent(myAnnotion.class)
-
获取注解对象:tostring打印结果@com.ljx.Override.myAnnotion(value=林惊羽)-》在下一章节注解中会详细讲解
Field操作成员变量
获取所有成员变量信息
public static void Fieldtest(Class<Person> pc) throws Exception{
/*
* * Field[] getDeclaredFields() 获取所有成员变量对象
* Field getDeclaredField(String name) 获取指定任意成员变量对象-》获取指定的成员变量,返回类型和名称
*/
Field[] dfs = pc.getDeclaredFields();
for (Field f : dfs) {
System.out.println(f);
}
/*打印信息如下:
* private java.lang.String com.ljx.tx0911.Person.name
private int com.ljx.tx0911.Person.id
*/
}
利用反射生成getName方法保存在文件中(放在a.txt中)
private static void WriteFanme(Field f) {//f:含有Person的name属性以及String类型
// TODO Auto-generated method stub
StringBuffer sb = new StringBuffer();
String name=f.getName();//name
String ftype=f.getType().getName();//java.lang.String
System.out.println(name+" "+ftype);//name java.lang.String
System.out.println("-----------------------------------");
sb.append("public ").append(ftype+" ").append("get");
sb.append(name.substring(0, 1).toUpperCase()).append(name.substring(1)).append("()");
sb.append("{\r\n").append("\treturn ").append(name).append(";\r\n}");
System.out.println(sb);
String s = new String(sb);
File fi = new File("a.txt");
try {
BufferedWriter bw = new BufferedWriter(new FileWriter(fi) );
bw.write(s);
bw.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
打印信息如下,且分割线之后的代码写入文件中!(这里的StringBuffer转String不熟练!!!)
name java.lang.String
-----------------------------------
public java.lang.String getName(){
return name;
}
Method类方法结合 (class对象对方法的操作)
Method类的获取
利用class对象获取如下
class获取方法:
-
Method dm =pc.getDeclaredMethod("setId", int.class);//class获取任意方法
-
myc.getDeclaredMethod("service", null); 还有的形参对应的是null 说明service方法没有参数
-
Method[] dms = pc.getDeclaredMethods();//获取所有方法
利用字节码对象生成Person对象:
-
pc.newInstance();
Method类的方法:
调用方法模板:
-
invoke(Object obj, Object... args) 对带有指定参数的指定对象调用对应方法
调用不带参数的方法(调用加获取)
-
Method dm = pc.getDeclaredMethod("getId", null);
-
System.out.println(dm.invoke(p, null));
调用带参数的方法:(调用加获取)
-
Method m1 = pc.getDeclaredMethod("setId", int.class);
-
m1.invoke(p, 18);
获取方法名称
-
getName() 以 String 形式返回此 Method 对象表示的方法名称
获取所有方法:
-
Method[] dms = pc.getDeclaredMethods();
案例:获取方法名、利用反射调用方法、获取方法数组
public static void getMethods(Class<Person> pc) throws Exception{
Person p = pc.newInstance();
Method m1 = pc.getDeclaredMethod("setId", int.class);
Method m2=pc.getDeclaredMethod("setName", String.class);
m1.invoke(p, 18);
m2.invoke(p, "连接线");
System.out.println(m1.getName());//setId
System.out.println(p);//Person [name=连接线, id=18]
Method[] dms = pc.getDeclaredMethods();
for (Method m : dms) {
System.out.println(m);
}
}
打印信息:
setId
Person [name=连接线, id=18]
public java.lang.String com.ljx.tx0911.Person.toString()
public java.lang.String com.ljx.tx0911.Person.getName()
public int com.ljx.tx0911.Person.getId()
public void com.ljx.tx0911.Person.setName(java.lang.String)
public void com.ljx.tx0911.Person.setId(int)
反射结合Properties
目的:利用反射获取properties中的类和方法信息,调用该类的方法
Properties类中的方法
(前提条件:先在src下创建reflect.properties)
-
Properties ps = new Properties();
-
ps加载字节流,读取文件入内存
-
ps.load(new FileInputStream(new File("src/reflect.properties"))); 获取ps中的配置信息(key->value)
-
String classpath=ps.getProperty("classPath");
案例演示:
//目的:利用反射获取properties中的类和方法信息,调用该类的方法
public static void main(String[] args) {
// TODO Auto-generated method stub
Properties ps = new Properties();
try {
ps.load(new FileInputStream(new File("src/reflect.properties")));
String classpath=ps.getProperty("classPath");
String method=ps.getProperty("method");
System.out.println(method);
//方法一:
Class<MyServlet> myc = (Class<MyServlet>) Class.forName(classpath);
Constructor<MyServlet> cs = myc.getConstructor(null);
MyServlet my = cs.newInstance(null);
my.service();//实现调用
//方法2:
Method m2 = myc.getDeclaredMethod("service", null);
m2.invoke(my, null);//实现调用
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
reflect.properties文件:
classPath=com.ljx.tx0911.MyServlet
method=service
反射结合map生成对象
以下案例有两种方法:建议仔细阅读
-
方式1 利用方法调用(invoke)-》setId/setName 设置值进对象中
-
//方式2利用Field的set方法将属性注入进对象中(就算是属性是Private修饰也一样能注入!!!!(很强))
原因:方式2中f.setAccessible(true);//跳过private安全检查
案例:通过反射 把一个map转成对象
public class T_06reflect {
//通过反射 把一个map转成对象
public static void main(String[] args) {
// TODO Auto-generated method stub
HashMap<String, String> map = new HashMap<>();
map.put("id", "001");
map.put("name", "张小凡");
Emp e =mapToEmp(map);
System.out.println(e);
}
@Test
private static Emp mapToEmp(HashMap<String, String> map) {
// TODO Auto-generated method stub
Emp e=null;
try {
Class<Emp> ec = (Class<Emp>) Class.forName("com.ljx.tx0911.Emp");
Constructor<Emp> c = ec.getConstructor();
e = c.newInstance();
//方式1 利用方法调用(invoke)-》setId/setName 设置值进对象中
Method[] dms = ec.getDeclaredMethods();
Method m1 = ec.getDeclaredMethod("setId", String.class);
Method m2 = ec.getDeclaredMethod("setName", String.class);
m1.invoke(e, map.get("id"));
m2.invoke(e, map.get("name"));
System.out.println(e);//Emp [name=张小凡, id=001]
System.out.println("--------------------");
//方式2利用Field的set方法将属性注入进对象中(就算是Private修饰也一样能注入!!!!(很强))
//通过比较f的name和Person中的name是否一致来向对象设置值
Emp e2=ec.newInstance();
Field[] fs = ec.getDeclaredFields();
for (Field f : fs) {
String key=f.getName();
f.setAccessible(true);//跳过private安全检查
if(f.getName().equals("name")){
f.set(e2, map.get("name"));
}
if(f.getName().equals("id")){
f.set(e2, map.get("id"));
}
}
System.out.println("打印e2:");
System.out.println(e2);//Emp [name=张小凡, id=001]
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
return e;
}
}