JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象。
总结:反射就是把java类中的各种成分映射成一个个的Java对象。
Java反射机制可以让我们在编译期(Compile Time)之外的运行期(Runtime)获得任何一个类的字节码。包括接口、变量、方法等信息。还可以让我们在运行期实例化对象,通过调用get/set方法获取变量的值。
如果由Java的类实例化对象叫做"正",那么由一个对象反推出的类就叫做"反射"。
反射机制主要提供了以下的功能:
在程序运行状态中,对任意一个类(指class文件),都能够知道这个类的所有属性和方法。
反射对于某一个类的一个对象,都能够调用它的任意一个方法和属性。
通俗地描述Java反射机制:能够动态地获取对象的信息,就称之为反射。
极大的提高了应用程序的扩展性。在反射以前,一般使用多态来提高扩展性,将子类对象传递给父类引用来实现的。但这种方式必须要通过new来建立子对象,子类对象必须写死在代码中。而有了反射之后,就可以通过反射来省略掉new子类对象这一个步骤。
反射直接将子类对象的类名以字符串的形式传递给反射技术框架并由反射技术框架来创建这个字符串代表的类的实例。
虚拟机在class文件的加载阶段,把类的信息保存在方法区数据结构中,并在java堆中生成一个Class对象,作为类信息的入口。
定义两个类,Student.java
和Teacher.java
class Student{
private String name;
private int age;
static{
System.out.println("Student");
}
}
class Teacher{
private String name;
private int age;
static{
System.out.println("Teacher");
}
}
public class Test{
public static void main(Strint args[]){
Student student = new Student();
Class clazz = student.getClass();
}
}
public Class Test{
public static void main(String args[]){
Class clazz = Student.class;
}
}
使用这种方法时,只会加载Student类,不会触发其类构造器的初始化。
Class.forName()
的方式public class Test{
public static void main(String args[]){
Class clazz = Class.forName("com.Student");
}
}
在JDK源码的实现中,forName方法会调用Native方法forName0(),它在JVM中调用findClassFromClassLoader()加载Student类,其原理和ClassLoader相同,将会触发Student类的类构造器初始化。
forName0()方法声明如下:
private static native Class<?> forName0 (String name,booelan intialize,ClassLoader loader,Class<?> caller)
其中intialize参数用来告诉虚拟机是否要对加载的类进行初始化,如果为false,则不会进行初始化。
Class class_student = Student.class;
Field[] field = class_student.getDeclaredFields();
for(Field field : fields){
System.out.println(field.getName());
}
Class class_student = Student.class;
Method[] methods = class_student.getDeclaredMethods();
for(Method method : methods){
System.out.println(method);
}
public class ClassTest{
public static void main(String args[]) throws NoSuchMethodException {
Class class_student = Student.class;
Constructor constructor = class_student.getConstructor(String.class,int.class);
constructor.newInstance("Tom",10);
}
}
class Student{
private String name;
private int age;
public Student(String name, int age){
this.name=name;
this.age=age;
}
}
如果没有显示的声明默认构造器,class_student.getConstructor()会抛出NoSuchMethodException异常。
newInstance()
方法生成类实例Class class_student = Student.class;
Student student = class_student.newInstance();
Class class_student = Student.class;
Field name = class_student.getDeclaredField("name");
name.setAccessible(true);
Student student = (Student)class_student.newInstance();
name.set(student,"name");
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
return (Unsafe)f.get(null);
这种方式在使用Unsafe类进行黑魔法时经常用到。
Class.forName
的一个很常见的方法是在加载数据库驱动的时候。
例:
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
Connection conn = DriverManager.getConnection("jdbc.sqlserver://localhost:1433;DatabaseName=jieshou","jieshou","jieshou");
为什么我们在加载数据库驱动包的时候没有使用newInstance()方法呢?即有的jdbc数据库连接的写法为Class.forName("xx.xx.xx");
,而也有的写法为Class.forName("xx.xx.xx").newInstance();
.这是因为Class.forName("");的作用为要求JVM查找并加载指定的类,如果在类中有静态初始器的话,JVM就会执行该类的静态代码段。而在jdbc的规范中明确要求这个Driver类必须向DriverManager注册自己,即任何一个JDBCDriver的Driver类代码都必须类似如下:
public class MyJDBCDriver implements Driver{
static{
DriverManager.registerDriver(new MyJDBCDriver());
}
}
既然在静态初始化器中已经完成注册,所以在使用JDBC的时候只需要写Class.forName("");
就可以了。
原文:https://www.cnblogs.com/jieshou/p/14529388.html