一、什么是反射机制
一般情况下,我们使用一个类时必定是知道这个类是用来干什么的,然后对它进行实例化,之后使用这个类的对象直接直接进行操作。
Student student = new Student();
student.setName("张三");
这种我们可以理解为正,反射就是反着来。
Java反射机制是指在运行状态下,对于任意一个类我们都能够知道这个类的所有的属性和方法;对于任意一个对象都能够调用它的所有的方法和属性(包括私有的),这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
二、反射的原理
你的程序执行的时候jvm读取加载的是你的源代码生成的.class文件,而你类的属性和方法什么的都会包含在.class文件中
所以jvm可以通过解析.class文件实现反射的那些功能
三、为什么要有反射
现在也只是一个初学者,没有做过实际的项目开发,也不是很理解反射机制的作用,但是看到了几个例子让我有点茅塞顿开的感觉,我也就贴上来吧
1. 操作数据库
比如链接jdbc 我们Class.forName(“com…”),不就是个驱动类么,为什么需要反射创建。
因为Java本身只是提供的接口,具体的实现是由mysql,oracel,sql server这些数据库厂商实现的,如果你想要使用java连接到这些数据库,必然要使用这些厂商提供的驱动,厂商提供的驱动都是一些类,类是不会执行的,jvm要为他们生成对象。
Class.forName(“com…”)这句话调用后会生成一个驱动的对象会被加载到jvm里,进而可以完成数据库的操作。
2. 方便代码的编写以及维护更新
- 假如我们有两个程序员,一个程序员在写程序的时候,需要使用第二个程序员所写的类,但第二个程序员并没完成他所写的类。那么第一个程序员的代码能否通过编译呢?这是不能通过编译的。但是利用Java反射的机制,只要知道类名,就可以让第一个程序员在没有得到第二个程序员所写的类的时候,来完成自身代码的编译。
- 如果你的项目成规模。要调用的对象很多或很多方法需要掉同一个对象,这时候你总不能一个个new吧。你可以直接通过反射 通过java类得名字和方法的名字就可以获得该方法的返回数据。
- 比如,一个大型的软件,不可能一次就把把它设计的很完美,当这个程序编 译后,发布了,当发现需要更新某些功能时,我们不可能要用户把以前的卸载,再重新安装新的版本,假如 这样的话,这个软件肯定是没有多少人用的。采用静态的话,需要把整个程序重新编译一次才可以实现功能 的更新,而采用反射机制的话,它就可以不用卸载,只需要在运行时才动态的创建和编译,就可以实现该功能。
3.使用框架
很少情况下是非用反射不可的。大多数情况下反射是为了提高程序的灵活性。
因此一般框架中使用较多。因为框架要适用更多的情况。对灵活性要求较高。
- 黑客利用反射机制来绕过private保护机制。
四、反射的缺点
尽管反射非常强大,但也不能滥用。如果一个功能可以不用反射完成,那么最好就不用。
在我们使用反射技术时,下面几条内容应该牢记于心:
1. 性能第一
反射包括了一些动态类型,所以 JVM 无法对这些代码进行优化。因此,反射操作的效
率要比那些非反射操作低得多。我们应该避免在经常被 执行的代码或对性能要求很高的程
序中使用反射。
2. 安全限制
使用反射技术要求程序必须在一个没有安全限制的环境中运行。如果一个程序必须在有
安全限制的环境中运行,如 Applet,那么这就是个问题了。
3. 内部暴露
由于反射允许代码执行一些在正常情况下不被允许的操作(比如访问私有的属性和方
法),所以使用反射可能会导致意料之外的副作用--代码有功能上的错误,降低可移植性。
反射代码破坏了抽象性,因此当平台发生改变的时候,代码的行为就有可能也随着变化。
搬运工—原博地址
五、反射机制的使用
1. 获取类的字节码文件对象
要使用反射机制首先我们得拿到所需要得该类的字节码文件,获取.class 文件对应的字节码对象Class主要有三种方式:
注意:一个类不同的实例对象所获取得到的字节码文件不是一个
1. 根据类名:类名.class
Class studentClass = Student.class;
Class studentClass2 = Student.class;
Class studentClass3 = Student.class;
System.out.println(studentClass==studentClass2);//true
System.out.println(studentClass2 == studentClass3);//true
2. 根据对象:对象.getClass()
Student student = new Student(); //Student.class ----->Class
student.setName("张三");
//方式1 通过该方法,获取该类对应的字节码文件对象
Class aClass = student.getClass();
Class aClass2 = student.getClass();
System.out.println(aClass == aClass2); //true
Student student1 = new Student();
Class aClass3 = student1.getClass();
System.out.println(aClass2 == aClass3);//true
3. 根据全限定类名:Class.forName(全限定类名)
//通过Class 类中的静态方法来获取一个类的字节码文件对象
//一个类的全路径:包含包名demo3.Student
//全路径:包名+类名
Class aClass = Class.forName("demo3.Student");
Class aClass2 = Class.forName("demo3.Student");
System.out.println(aClass==aClass2);//true
- 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
Student student = new Student();
Class aClass = student.getClass();
Class<Student> studentClass = Student.class;
Class<?> aClass1 = Class.forName("demo3.Student");
System.out.println(aClass==aClass1);
System.out.println(studentClass==aClass);
当我们获取到一个类的字节码文件对象后,我们要就可以用这个字节码文件对象,去剖析这个类的构成
一个类的构成,有
构造方法 Constructor
成员变量 Field
成员方法 Method
获取构造方法
1. 获取所有的构造方法
- 获取所有的构造方法不包含私有的
public Constructor<?>[] getConstructors () - 获取所有的构造方法 包括私有的
public Constructor<?>[] getDeclaredConstructors ()
代码演示:
public class MyTest {
public static void main(String[] args) throws ClassNotFoundException {
Class aClass = Class.forName("demo4.Student");
//获取所有构造方法
//public Constructor<?>[] getConstructors () 获取所有的构造方法不包含私有的
//public Constructor<?>[] getDeclaredConstructors () 获取所有的构造方法 包括私有的
Constructor[] constructors = aClass.getConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
System.out.println("--------------------------");
//获取所有构造方法包括私有
Constructor[] declaredConstructors = aClass.getDeclaredConstructors();
for (Constructor declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor);
}
}
}
2. 获得单个的构造方法
获取单个构造方法
获取单个的构造方法 不包含私有的:
public Constructor getConstructor (Class < ? >…parameterTypes)
获取单个的构造方法包含私有的:
public Constructor getDeclaredConstructor (Class < ? >…parameterTypes)
具体选择哪一个构造方法这个取决于你传递的参数,想当于方法重载;
代码演示:
Student类:
package demo4;
public class Student {
public Student() {
System.out.println("空参构造执行了");
}
public Student(String name) {
System.out.println("一个参构造执行了" + name);
}
public Student(String name, int age) {
System.out.println("两个个参构造执行了" + name + "==" + age);
}
private Student(String name, int age, char sex) {
System.out.println("三个参构造执行了" + name + "==" + age + "===" + sex);
}
}
非私有构造方法:
Class aClass = Class.forName("demo4.Student");//通过全类名获得字节码文件对象
//获取空参构造方法对象
Constructor constructor = aClass.getConstructor();
//Student student = new Student(); //以前创建对象的方式
//通过反射创建出一个类的对象
Object o = constructor.newInstance(); //通过反射方式创建对象的方式
//选择一个参数的构造方法
Constructor constructor = aClass.getConstructor(String.class);
//传统方法
//Student student = new Student("abc");
//通过反射的方式,创建出该类的对象
Object o1 = constructor.newInstance("bbb");
//选择两个参数的构造方法
Constructor constructor = aClass.getConstructor(String.class, int.class);
私有构造方法:
- 当要获得私有的构造方法创建实例时,除了使用
public Constructor getDeclaredConstructor (Class < ? >…parameterTypes),还需要取消语法检测;
代码演示:
public class MyTest5 {
public static void main(String[] args) throws Exception {
//new String("wangwu",25,'男');
Class aClass = Class.forName("demo4.Student");
// Constructor constructor = aClass.getConstructor(String.class, int.class, char.class);
//获取单个的私有的构造方法对象
Constructor declaredConstructor = aClass.getDeclaredConstructor(String.class, int.class, char.class);
declaredConstructor.setAccessible(true);//取消语法检测
Object o = declaredConstructor.newInstance("zhaoliu", 26, '女');
}
}
3. 使用反射获得通过空参构造创建一个类的两种方式
public class MyTest6 {
public static void main(String[] args) throws Exception {
//使用反射时通过空参构造创建一个类的对象,有两种方式
Class aClass = Class.forName("org.westos.demo4.Student");
//aClass.getConstructor();
//方式1:通过构造方法对象里面的newInstance()方法来创建对象的
Constructor constructor = aClass.getDeclaredConstructor();
Object o = constructor.newInstance();
//方式2通过 Class里面的newInstance()来创建对象
Object o1 = aClass.newInstance();
System.out.println(o==o1);//true
}
}
获取属性
一通百通,属性还有成员方法都是与构造方法类似的
Student类
public class Student {
//定义成员变量
public String name;
private int age;
public char sex;
private Student() {
}
}
1. 获取所有的属性
Class aClass = Class.forName("org.westos.demo.Student");
//获取该类中所有的字段对象,私有字段获取不到
Field[] fields = aClass.getFields();
for (Field field : fields) {
System.out.println(field);
}
System.out.println("----------------------");
//获取该类中所有的字段对象,包括私有的
Field[] declaredFields = aClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
2. 获取单个的属性
Class aClass = Class.forName("org.westos.demo.Student");
//获取某个非私有的字段对象
Field name = aClass.getField("name");
System.out.println(name.getName());
//获取私有的字段对象
Field sex = aClass.getDeclaredField("sex");
System.out.println(sex);
以前给成员变量设置值
//Student student = new Student();
//student.name="zhangsan";
Object o = aClass.newInstance();
// Object o1 = aClass.getConstructor().newInstance();
name.set(o,"zhangsan");
Student student= (Student) o;
System.out.println(student.name);
3. 对私有属性进行设值
Class aClass = Class.forName("demo.Student");
//通过反射,把该类的对象创建出来
Constructor declaredConstructor = aClass.getDeclaredConstructor();
declaredConstructor.setAccessible(true); //取消语法检测
Object obj = declaredConstructor.newInstance();
//给一个私有字段设置
Field ageField = aClass.getDeclaredField("age");
ageField.setAccessible(true); //取消语法检测
ageField.set(obj,10);
Student student= (Student) obj;
//System.out.println(student.age);
Object o = ageField.get(obj);
Integer integer= (Integer) o;
System.out.println(integer);
获取成员方法
Student类
public class Student {
public void test(){
System.out.println("这是一个空参的方法");
}
public void test2(String name,int age) {
System.out.println("这是一个有参的方法"+name+"==="+age);
}
public String show(String name, int age) {
System.out.println("这是一个有参的方法" + name + "===" + age);
return name+"哈哈";
}
private String show2(String name, int age) {
System.out.println("这是一个有参的方法" + name + "===" + age);
return name + "哈哈";
}
}
** 1. 获取所有的成员方法**
Class aClass = Class.forName("org.westos.demo2.Student");
Object obj = aClass.newInstance();
//获取该类所有的成员方法对象,不包括私有的方法,包括父类继承下来方法
Method[] methods = aClass.getMethods();
for (Method method : methods) {
System.out.println(method.getName());
}
System.out.println("-----------------------");
//获取该类中所有的方法,包括私有的
Method[] declaredMethods = aClass.getDeclaredMethods();
for (Method m : declaredMethods) {
System.out.println(m.getName());
}
** 获取单个的成员方法**
Class aClass = Class.forName("org.westos.demo2.Student");
Object obj = aClass.newInstance();
//获取单个的非私有方法的对象
Method test = aClass.getMethod("test");
System.out.println(test);
以前要调用一个方法
//Student student = new Student();
//student.test();
test.invoke(obj);
System.out.println("--------------------");
//获取一个有参数的方法对象
Method test2 = aClass.getMethod("test2", String.class, int.class);
test2.invoke(obj,"abc",100);
//获取一个有参数,有返回值的方法对象,并调用该方法执行
Method show = aClass.getMethod("show", String.class, int.class);
Object result = show.invoke(obj, "张三", 19);
String str= (String) result;
System.out.println(str);
//获取一个私有的方法对象
Method show2 = aClass.getDeclaredMethod("show2", String.class, int.class);
show2.setAccessible(true);//取消语法检测
show2.invoke(obj, "王五", 25);
总结
第一感觉就是这货在Java中简直就是强大到一个Bug级别,上天无所不能,完全是以上帝视角的存在,后来看别人的文章,总结下来就是一句话:Java反射给我们带来极大灵活性的同时,极大的方便了我们的编程,而且反射堪称各大框架的基础。