Java反射
回忆一下之前如何使用一个Java类?
- 已知一个类的类名、以及类中的方法属性、构造方法等
- 使用new关键字创建对象
- 使用对象调用方法或属性
这是正常的使用,已知某个类,创建该类的对象。正向使用,但是这种写法,把代码直接写死了,不能变了。但是框架需要为我们处理不同的类,我们配置什么类它都需要能够创建,new这种方式显然是不能满足的
问题:如果仅仅知道一个类的类名,能否动态得到类的定义信息,包括哪些方法、属性等? 答案:可以通过反射做到
引入案例支持
1.配置servlet,服务器启动时就会创建该类的对象,使用的就是反射机制创建该类的对象
XML
<servlet>
<servlet-name>
<servlet-class>com.ffyc.dorm.servlet.LoginServlet</servlet-class>
<servlet-class>com.ffyc.dorm.servlet.DormServlet</servlet-class>
2.mybatis,配置了哪个类就创建出该类的对象
XML
resultType="com.ffyc.mybatispro.model.Admin">
为什么用反射机制?就是向框架一样,可以写一套机制来处理任意的类,就使得程序更急灵活,以不变应万变。
什么是反射机制?JAVA反射机制是知道类的地址,在运行状态中,可以动态的获取任意一个类的信息,可以创建任意类的对象,调用任意对象中的方法、属性**,** 这种动态获取类信息的功能称为java反射机制,称为反向使用类
Car.class是在硬盘上存储的,把它加载到内存里面;在内存里面怎么表示Car.class类的信息呢?会在内存里面创建一个Class类的对象:用来表示Car类的信息,提供了各种方法来访问Class类的类表示正在运行的Java应用程序中的类和接口。
Java反射相关API
Java反射相关的类主要包括
- Class 类型
- Constructor 构造方法
- Method 方法
- Field 属性
除了Class外,其他类都位于java.lang.reflect包中。可见,反射API将类的类型、方法、属性都封装成了类,其中最重要的类是Class,可以说,反射的使用都是从Class开始。
Class类
一旦一个类被加载到内存中,都会为该类创建一个Class类的对象,可以通过该对象获取类中的信息。要使用反射机制动态获取,创建对象,首先就是要获得类的Class对象.
反射的起点,就是要获得类的Class对象,通过Class对象获取类的信息。
无论类创建了多少个对象,只有一个类的Class对象。
要使用Class类的方法,必须先获得该类的Class类的实例,获取类的Class对象方式有3种**:**
- 类名.class方式:适用于通过类名获得Class实例的情况
- Object类中的getClass方法:适用于通过对象获得Class实例的情况
- Class类的静态方法 forName(String name)
java
package com.ffyc.javareflect.model;
public class User {
private int id;
private String account;
private String password;
public User() {
System.out.println("User无参构造");
}
private User(int id, String account, String password) {
this.id = id;
this.account = account;
this.password = password;
System.out.println("有参构造");
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
java
package com.ffyc.javareflect.model;
public class Test {
public static void main(String[] args) throws ClassNotFoundException {
// new User(); 正向使用
Class c1=User.class;//第一种方式
System.out.println(c1);//class com.ffyc.javareflect.model.User
Class c2=new User().getClass();//第二种方式
System.out.println(c2);
System.out.println(c1==c2);//true,无论类创建了多少个对象,只有一个类的Class对象
//根据类的地址加载类,并获得该类的Class类的对象
String className="com.ffyc.javareflect.model.User";
Class c3=Class.forName(className);
System.out.println(c1==c3);//true
}
}
通过反射来创建对象
1.Class类中,c.newInstance()是创建对象的一种方式
java
public class Test1 {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
//根据类的地址加载类,并获得该类的Class类的对象
String className="com.ffyc.javareflect.model.User";
Class c=Class.forName(className);
Object obj=c.newInstance();
}
}
2.Constructor类中,constructor.newInstance()是创建对象的一种方式
java
public class Test1 {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
//根据类的地址加载类,并获得该类的Class类的对象
String className="com.ffyc.javareflect.model.User";
Class c=Class.forName(className);
Constructor constructor=c.getConstructor();//获取类中无参构造方法,封装到Constructor类的对象
Object obj=constructor.newInstance();//通过无参构造方法创建对象
Constructor constructor1=c.getConstructor(int.class,String.class,String.class);
System.out.println(Arrays.toString(constructor1.getParameters()));
}
}
getDeclaredConstructor:获得类中指定的构造方法(包含了私有方法),打破了封装性。
java
Constructor constructor=c.getDeclaredConstructor(int.class,String.class,String.class);
constructor.setAccessible(true);//设置可以访问私有权限的成员,不然报错
constructor.newInstance(1,"jim","jim");
getDeclaredConstructors:获得类中所有的构造方法
java
Constructor[] constructors=c.getDeclaredConstructors();
for (Constructor constructor1:constructors){
if(constructor1.isAccessible()){
constructor1.setAccessible(true);
}else {
}
}
通过Class类的对象获取属性(Field实例)
public Field getField(String name)通过指定Field名字,返回Field实例.
注意Field的访问权限
java
public class Test2 {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
//根据类的地址加载类,并获得该类的Class类的对象
String className="com.ffyc.javareflect.model.User";
Class c=Class.forName(className);
Object obj=c.newInstance();
Field[] fields=c.getDeclaredFields();//获取类中所有的属性
//Field[] methods=c.getDeclaredMethods();//获取类中所有的方法
for(Field f:fields){
System.out.println(f.getName());
Method m=c.getMethod("set"+f.getName().substring(0,1).toUpperCase()+f.getName().substring(1),f.getType());//所有属性的set方法
m.invoke(obj,"111");//为属性赋值
}
}
}
获得Method实例
Method实例都是通过Class类的方法获得
Method getMethod(String name, Class... parameterTypes):通过指定方法名,参数类型,返回一个Method实例
Method类的作用:
1.Method类将类中的方法进行封装,可以动态获得方法的信息,例如
- getName:获得方法名字
- getParameterTypes:获得方法参数类型
2.除了动态获得方法信息外,Method还能动态调用某一个对象的具体方法
nvoke(Object obj, Object... args) :使用obj调用该方法,参数为args
反射案例
自定义java对象转json工具类
java
package com.ffyc.javareflect.model;
public class MyJson {
//简单的反射案例
public static String objToJson(Object obj) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
String json="{";
Class c=obj.getClass();
Field[] fields=c.getDeclaredFields();//获取任意一个类中的所有属性
for(Field f:fields){
String name=f.getName();
json+=name+":";
Method m=c.getMethod("get"+f.getName().substring(0,1).toUpperCase()+f.getName().substring(1));//动态构成get方法名称,并获取到get方法
Object value=m.invoke(obj);//调用了属性的get方法
json+=value+",";
}
json=json.substring(0,json.length()-1);
json+="}";
return json;//{id:值,account:值}
}
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
User user=new User();
user.setId(10);
user.setAccount("admin");
user.setPassword("111");
String json=MyJson.objToJson(user);
System.out.println(json);//{id:10,account:admin,password:111}
}
}
优缺点
优点:
- 让程序更加灵活,具备动态性(框架、mybatis、servlet-class 可以动态创建操作对象)
- 可以在运行时轻松获取任意一个类的方法、属性,并且还能通过反射进行动态调用
缺点:
- 反射对类型进行判断解析,所以效率上低.
- 通过反射可以对私有成员进行访问操作,安全性低,打破了封装性