Java高级——Java反射机制
一. 什么是反射?
-
反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力。
-
Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的属性和方法;对于任意一个对象,都能够调用它的任意一个方法;这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。
-
简单的来说,反射机制指的是程序在运行时能够获取 自身的信息。在Java中只要给定类的名字,那么就可以根据反射来获取类的所有信息。包括访问修饰符、父类、实现的接口、属性和方法的所有信息,并可在运行时创建对象、修改属性(包括私有的)、调用方法(包括私有的)
二. 为什么要用反射机制?直接创建对象不就可以了吗? 这就涉及到了动态与静态的概念。
-
静态编译:在编译时确定类型,绑定对象,即通过。
如:Student student = new Student();
-
动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了Java的灵活性,体现了多态的应用,用以降低类之间的耦合性。
如:Class.forName("com.test.book.Student").newInstance();
-
优点:反射机制的优点就是可以实现动态创建对象和编译,体现出很大的灵活性。
-
缺点:它的缺点是对性能有影响,使用反射基本上是一种解释操作,这种操作总是慢于直接执行的相同操作。
三.Java反射机制主要提供了一下功能:
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的的成员变量和方法
- 在运行时调用任意一个对象的方法
四.Class对象:Class对象是Reflection的起源,想要操纵类中的属性和方法,都必须从获取Class对象开始。
- 类是程序的一部分,每个类都有一个Class对象。换言之,每当编写并编译了一个新类,就回产生一个与之对应的Class对象。
- Class类没有公共构造方法。Class对象是在加载类时由Java虚拟机以及通过调用类加载器中的方法自动构造的,因此不能显示的构造一个Class对象。
获取Class对象的方法:
//创建一个类
class Person {
private String name;
public Person() {
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
- object.getClass();获取指定实例的Class,例:
public static void main(String[] args) {
//通过 对象名.class 获取Class对象
Person person = new Person();
Class<?> personclass = person.getClass();
System.out.println(personclass.getName());
}
- class.getSuperclass();获取当前类的继承的父类的Class,如:
public static void main(String[] args) {
//通过 对象名.class 获取Class对象
Person person = new Person();
Class<?> personclass = person.getClass();
System.out.println(personclass.getName());
//获取personclass父类的Class
Class<?> P_class = personclass.getSuperclass();
System.out.println(P_class.getName());
}
- .class语法:
public static void main(String[] args) {
//通过 .class获取Class对象
Class<?> personclass = Person.class;
System.out.println(personclass.getName());
}
- Class.forName(类名); 用Class的静态方法,传入类的全称(包名+类名)即可。(此方法用的比较多)
public static void main(String[] args) {
//通过 Class.forName()获取Class对象
Class<?> personclass = null;
try {
personclass = Class.forName("com.test.book.Person");
System.out.println(personclass.getName());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
- Primitive.TYPE 基本类型的包装类获取Class的方式。
public static void main(String[] args) {
//通过 Primitive.TYPE获取基本类型的包装类的Class对象
Class<?> intclass = Integer.TYPE;
Class<?> doubleclass = Double.TYPE;
System.out.println(intclass.getName());
System.out.println(doubleclass.getName());
}
五.java.lang.reflect库和Java反射常用API
Class类与java.lang.reflect类库一起对反射的概念进行支持。
java.lang.reflect包下:
- Fidld类:代表成员变量(成员变量也成为类的属性)
- Method类:代表类的方法
- Constructor类:代表类的构造方法
- Array类:提供了动态创建数组,以及访问数组元素的静态方法
通过反射机制实例化对象:
-
实例化无参构造函数的对象,两种方式:
Class.newInstance();
Class.getConstructor(new Class[]{}).newInstance(new Object[]{});
-
实例化带参构造函数的对象
Class.getConstructor(Class<?>... parameterTypes).newInstance(Object... initargs);
通过反射获取并调用方法:
- 获取当前类及超类的public Method
Method[] arrMethods = classType.getMethods();
- 获取当前类申明的所有Method(包括私有的)
Method[] arrMethods = classType.getDeclaredMethod();
- 获取当前类及超类指定的public Method
Method arrMethod = classType.getMethod(String name,Class<?>... parameterTypes);
- 获取当前类及超类申明的指定的Method(包括私有的)
Method arrMethod = classType.getDeclaredMethod(String name,Class<?>... parameterTypes);
- 通过反射动态运行指定Method
Object obj = method.invoke(Object obj,Object... args)
通过反射获取并调用属性:
-
获取当前类及超类的public Field
Field[] arrFields = classType.getFields();
-
获取当前类申明的所有Field(包括私有的)
Field[] arrFields = classType.getDeclaredFields();
-
获取当前类及超类指定的public Field
Field field = classType.getField(String name);
-
获取当前类及超类申明的指定的Field(包括私有的)
Field field = classType.getDeclaredField(String name);
-
通过反射动态设定Field的值
field.set(Object obj,Object value)
-
通过反射动态获取Field的值
field.get(Object obj)
示例:
我们先新建一个类,我们等下用Java的反射机制来操作这个类:
public class Employee{
private String name;
private int age;
public Employee(){
}
public Employee(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Employee{" + "name='" + name + '\'' + ", age=" + age + '}';
}
private void Work(){
System.out.println("wroking...");
}
}
通过反射机制构建一个Employee实例对象:
- 通过Class.newInstance()构建Employee实例对象((默认调用无参数构造方法)
public static void main(String [] args) throws Exception{
//获取Employee类所关联的Class对象
Class<?> employeeClass = Class.forName("book.ly.com.testjava.Employee");
//通过Class.newInstance()构建Employee实例对象(默认调用无参数构造方法)
Employee employee = (Employee) employeeClass.newInstance();
}
- 通过Class.getConstructor(new Class[]{}).newInstance(new Object[]{});构建Employee实例对象
public static void main(String [] args) throws Exception{
//获取Employee类所关联的Class对象
Class<?> employeeClass = Class.forName("book.ly.com.testjava.Employee");
//调用指定的构造方法构建Employee实例对象(无参数构造方法)
Constructor<?> constructor = employeeClass.getConstructor(new Class[]{});
Employee employee = (Employee) constructor.newInstance(new Object[]{});
}
- 调用带参数构造方法构建Employee实例对象
public static void main(String [] args){
//获取Employee类所关联的Class对象
Class<?> employeeClass = Class.forName("book.ly.com.testjava.Employee");
Constructor<?> constructor = employeeClass.getConstructor(new Class[]{String.class,int.class});
Employee employee = (Employee) constructor.newInstance("张三",20);
}
获取Class对象申明的方法并调用(包括私有的)
public static void main(String [] args) throws Exception{
//获取Employee类所关联的Class对象
Class<?> employeeClass = Class.forName("book.ly.com.testjava.Employee");
Constructor<?> constructor = employeeClass.getConstructor(new Class[]{String.class,int.class});
Employee employee = (Employee) constructor.newInstance("张三",20);
//获取Class对象申明的所有方法(包括私有的)
Method[] methods = employeeClass.getDeclaredMethods();
for (Method method:methods){
//打印方法的方法名 + 方法的修饰符(1-public,2-private...)
System.out.println(method.getName() + "---" + method.getModifiers());
}
//获取Class对象指定的方法(包括私有的)
Method method = employeeClass.getDeclaredMethod("toString",new Class[]{});
//方法调用
String str = (String)method.invoke(employee,new Object[]{});
System.out.println(str);
//调用私有方法
Method method1 = employeeClass.getDeclaredMethod("Work");
method1.setAccessible(true);//设置方法能被访问
method1.invoke(employee);
}
获取Class对象申明的属性(包括私有的)
public static void main(String [] args) throws Exception{
//获取Employee类所关联的Class对象
Class<?> employeeClass = Class.forName("book.ly.com.testjava.Employee");
Constructor<?> constructor = employeeClass.getConstructor(new Class[]{String.class,int.class});
Employee employee = (Employee) constructor.newInstance("张三",20);
//获取Class对象申明的所有属性(包括私有的)
Field[] fields = employeeClass.getDeclaredFields();
for (Field field:fields){
//由于有私有的变量,我们设置为能被访问
field.setAccessible(true);
//打印获取到的属性的值
System.out.println( field.get(employee));
}
//获取Class对象指定的属性(包括私有的)
Field field = employeeClass.getDeclaredField("name");
field.setAccessible(true);//设置为能被访问
field.set(employee,"李四");//设置变量值
System.out.println( field.get(employee));
}
使用Java的反射机制创建数组:
public static void main(String [] args) throws Exception{
//创建一个一维数组(String)
Class<?> tClass = Class.forName("java.lang.String");
Object array = Array.newInstance(tClass, 5);//tClass 为数据类型的Class对象 5为数组长度
//给array数组下标为 3 的元素赋值为“abc”
Array.set(array,3,"abc");
//取出array数组下标为 3 的值 并打印
System.out.println(Array.get(array,3));
//创建一个二维数组
int [] dimens = {3,3};//3行 3列
//创建一个int 类型 3行3列 的数组
Object array2 = Array.newInstance(int.class,dimens);
//给第二行 第二列 的元素赋值
Object arrayobj = Array.get(array2,1);//获取第二行(一个一维数组)
//给第二列赋值 (给获取的第二行的一维数组的第二个元素赋值)
Array.set(arrayobj,1,10);
int [] [] arr = (int[][]) array2;//强转为二维数组对象
System.out.println(arr[1][1]);//取出第二行第二列的值并打印
}
总结:
- 只要用到反射,就必须先获取到Class对象
- 没有方法能够获得当前类的超类的private属性和方法,你必须先通过getSuperclass()找到超类以后再去尝试获取
- 通常情况下即使是当前类,private属性和方法也是不能访问的,你需要设置压制权限setAccessibleible(true)来获得private的访问权。但说实话,这已经破坏了面向对象的规则,所以除非万不得已,请尽量少用。