我们都知道java是一门静态语言,通过class文件在虚拟机里面运行。但java里面有一个反射机制,可以使java语言变成一门“准动态语言”。使用反射机制可以使java语言变得更加灵活。
今天我们说一下java的反射机制。
一、什么是反射(Reflection)
- 反射(Reflection)是java被视为动态语言的关键,反射机制允许程序在执行期间,借助于Reflection Api 获取类的内部信息,并能够直接操作类的内部属性和方法。
二、使用反射调用类的方法和属性
1.使用反射创建类的对象 :调用Class对象的newInstance()方法
注意:
1.类必须有一个无参构造器。
2.无参构造器的访问权限要足够。
下面我们试一下:
package entity;
public class User {
private int age;
private String name;
private String job;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
//无参构造方法
public User() {
}
//有参构造方法
public User(int age, String name, String job) {
this.age = age;
this.name = name;
this.job = job;
}
@Override
public String toString() {
return "User{" +
"age=" + age +
", name='" + name + '\'' +
", job='" + job + '\'' +
'}';
}
}
首先我们准备一个User实体类,有age、name、job属性。
//反射
public class Test01 {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
//根据包名获取class对象
Class c1 = Class.forName("entity.User");
User user = (User) c1.newInstance();
System.out.println("通过反射获取的user:"+user);
}
}
通过反射获取的user:User{age=0, name='null', job='null'}
我们通过Class.forName(“包名”)
可以获取到一个class
对象c1,通过c1.newInstance()
可以创建一个User
对象。
》》》如果没有无参构造器的话:调用的时候需要通过Class
对象的getDeclaredConstructor()
,按照构造器参数顺序类型,获取构造器对象,通过构造器对象实例化User。
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
//根据包名获取class对象
Class c1 = Class.forName("entity.User");
//通过无参构造器获取对象
User user = (User) c1.newInstance();
System.out.println("有构造器的话:通过反射获取的user:"+user);
//通过有参构造器获取对象
Constructor declaredConstructor = c1.getDeclaredConstructor(int.class, String.class, String.class);
User user1 = (User) declaredConstructor.newInstance(10, "cpown", "程序员");
System.out.println("无构造器的话:通过反射获取的user:"+user1);
}
有构造器的话:通过反射获取的user:User{age=0, name='null', job='null'}
无构造器的话:通过反射获取的user:User{age=10, name='cpown', job='程序员'}
2.使用反射调用类的方法
使用Class对象c1的getDeclaredMethod(“方法名”,“参数类型”)
获取方法Method
对象,使用
.invoke
可以执行类的方法给user2进行赋值。invoke
可以理解为方法的激活执行。
User user2 = (User) c1.newInstance();
//通过getDeclaredMethod获取方法对象
Method setName = c1.getDeclaredMethod("setName", String.class);
//使用反射 invoke执行方法
setName.invoke(user2,"c-pown");
//
System.out.println("反射执行useName:"+user2.getName());
有构造器的话:通过反射获取的user:User{age=0, name='null', job='null'}
无构造器的话:通过反射获取的user:User{age=10, name='cpown', job='程序员'}
---------------华丽的分割线-------------
反射执行useName:c-pown
结果也是没有问题的。
3.使用反射给类的属性赋值
System.out.println("---------------华丽的分割线-------------");
User user3 = (User) c1.newInstance();
Field name = c1.getDeclaredField("name");
//权限检测,设置为true可以访问private方法
name.setAccessible(true);
name.set(user3,"张三");
System.out.println("反射给name赋值:"+user3.getName());
我们加上这段代码,
发现多了name.setAccessible(true);
这行;设置了setAccessible=true
可以关闭访问限制,可以设置到私有private
的变量。如果不加会报错,这个同样可以用来设置方法,构造器的权限,设置为true
可以访问私有方法。
有构造器的话:通过反射获取的user:User{age=0, name='null', job='null'}
无构造器的话:通过反射获取的user:User{age=10, name='cpown', job='程序员'}
---------------华丽的分割线-------------
反射执行useName:c-pown
---------------华丽的分割线-------------
反射给name赋值:张三
注:通过Class对象获取Fileds,Methods,Constructor,都有两种方法,一种是方法名包含Declared
的,这种方法会获取到对象的所有方法,包括私有方法。如果不包含Declared
,只能获取public方法或者字段。
三、使用反射的弊端
使用反射会给程序的安全性带来隐患,同时使用反射调用方法他的性能也是有很大差距的。
下面我们来测试一下性能上的比较:
package annotest;
import entity.User;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Test02 {
//普通创建对象调用方法
public static void test1(){
User user = new User();
long start = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
user.getName();
}
long end = System.currentTimeMillis();
System.out.println("new对象执行时间:"+(end-start)+"ms");
}
//使用反射调用方法
public static void test2() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Class aClass = Class.forName("entity.User");
User user = (User) aClass.newInstance();
Method getName = aClass.getDeclaredMethod("getName", null);
long start = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
getName.invoke(user,null);
}
long end = System.currentTimeMillis();
System.out.println("反射对象执行时间:"+(end-start)+"ms");
}
//使用反射调用方法 开启访问权限
public static void test3() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Class aClass = Class.forName("entity.User");
User user = (User) aClass.newInstance();
Method getName = aClass.getDeclaredMethod("getName", null);
getName.setAccessible(true);
long start = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
getName.invoke(user,null);
}
long end = System.currentTimeMillis();
System.out.println("反射对象开启权限执行时间:"+(end-start)+"ms");
}
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
test1();
test2();
test3();
}
}
new对象执行时间:6ms
反射对象执行时间:1577ms
反射对象开启权限执行时间:1135ms
我们发现使用普通对象访问方法是最快的,使用反射调用方法的效率要远远小于普通调用,开启访问权限后效率会更低。