反射概述
反射:将类的各个组成部分封装为其他对象,这就是反射机制。了解反射先需要了解java代码的历经阶段和Class类。
java代码的经历阶段和Class类:
java代码经过javac命令会生成class文件,当需要生成对象或者加载类时会把class文件加载进内存(通过类加载器实现);在内存中将对应的class文件生成Class对象,一个类只有一个class对象。Class对象根据类中成员变量、构造方法、成员方法生成Class对象的成员对象;由于类的构造方法或者成员方法可以有多个,所以用对象数组的形式保存。“将类的各个组成部分封装为其他对象”说的就是这个意思。
当需要创建对象时就根据类的Class对象进行创建。
最常见的java反射机制:
当我们在eclipse中输入对象.
时,就会列出对象所有的成员方法;其实就是列出Method数组的所有对象。
使用反射的好处:
- 可以在程序运行过程中,操作这些对象。
- 可以解耦,提高程序的可扩展性。
获取class对象的方式:
-
Class.forName("全类名")
:将字节码文件加载进内存,返回Class对象
多用于配文件,将类名定义在配议文件中。读取文件,加载类 -
类名.class
:通过类名的属性class获取
多用于参数的传 -
对象. getClass()
: getClass()方法在object类中定义。
多用于对象的获取字节码的方式
代码示例:
public class ReflectTest {
public static void main(String[] agrs) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, SecurityException, InvocationTargetException, NoSuchMethodException{
//三种方式
//对象.getClass()
Iphone iphone = new Iphone();
System.out.println(iphone);
Class clz1 = iphone.getClass();
//类.class()
Class clz2 = Iphone.class;
//Class.forName("包名.类名");
Class clz3 = Class.forName("WebServer.Iphone");
System.out.println(clz1==clz2);
System.out.println(clz3==clz2);
}
}
class Iphone{
public Iphone(){
}
}
结论:
同一个字节码文件( .class)在一次程序运行过程中, 只会被加载一次, 即一个类只有一个Class对象。不论通过哪一种方式获取的Class对象都是同一 个。
Class对象功能:
1. 获取成员变量
//获取public的成员变量
Field[] getFields()
Field getField(String name)
Field[] getDeclaredFields()
Field getDeclaredField(String name)
Field对象的操作:
1.设置值
void set(object obj, object value)
2.获取值
get(Object obj)
3.忽略访问权限修饰符的安全检查
setAcceslsible(true) //暴力反射
Person类:
public class Person {
private String name;
private int age;
public String a;
protected String b;
String c;
private String d;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return age;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void eat(){
System.out.println("eat...");
}
public void eat(String food){
System.out.println("eat..." + food);
}
}
测试代码如下:
import java.lang.reflect.Field;
import java.util.*;
public class 反射获取成员变量 {
public static void main(String[] args) throws Exception{
Class personClass = Person.class;
//获取所有public修饰的成员变量
Field[] fields = personClass.getFields();
for(Field field:fields){
System.out.println(field);
}
System.out.println("-----------------------");
//获取名字为a的成员变量(public修饰的)
Field a = personClass.getField("a");
//利用得到的对象a获取Person对象的成员变量a
Person p = new Person();
Object value = a.get(p);
System.out.println(value);
//利用对象a设置Person对象的成员变量
a.set(p, "法外狂徒张三");
System.out.println(p.a);
System.out.println("---------------------------");
//获取所有的成员变量,不考虑修饰符
Field[] declaredFilds = personClass.getDeclaredFields() ;
for(Field decalredFild:declaredFilds){
System.out.println(decalredFild);
}
//通过Field对象访问Person对象的私有成员变量d
Field d = personClass.getDeclaredField("d");
//访问私有成员要先忽略访问权限修饰符的安全检查,否则会报错
d.setAccessible(true);
Object value2 = d.get(p);
System.out.println(value2);
}
}
运行结果:
public java.lang.String WebServer.Person.a
-----------------------
null
法外狂徒张三
---------------------------
private java.lang.String WebServer.Person.name
private int WebServer.Person.age
public java.lang.String WebServer.Person.a
protected java.lang.String WebServer.Person.b
java.lang.String WebServer.Person.c
private java.lang.String WebServer.Person.d
null
2. 获取构造方法
Constructor<?>[ ] getConstructors()
Constructor<T> getConstructor(类<?>... parameterTypes)
Constructor<T> getDeclaredConstructor(类<?>... parameterTypes )
Constructor<?>[ ] getDeclaredConstructors()
Constructor对象的操作:
- 创建对象:
T newInstance(object... initargs)
- 如果使用空参数构造方法创建对象,操作可以简化: Class对象的newInstance方法
测试代码如下:
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
public class 反射获取构造方法 {
public static void main(String[] args) throws Exception{
Class personClass = Person.class;
//获取构造方法,根据参数不同获取不同的构造方法
Constructor constructor = personClass.getConstructor(String.class, int.class);
System.out.println(constructor);
//使用Constructor对象创建Person对象
Object person = constructor.newInstance("张三", 23);
System.out.println(person);
System.out.println("-----------------");
//获取无参数的构造方法
Constructor constructor1 = personClass.getConstructor();
System.out.println(constructor1);
//创建对象
Object person2 = constructor1.newInstance();
System.out.println(person2);
//空参构造的另一方式
Object person3 = constructor1.newInstance();
System.out.println(person3);
}
}
运行结果:
public WebServer.Person(java.lang.String,int)
WebServer.Person@61de33
-----------------
public WebServer.Person()
WebServer.Person@14318bb
WebServer.Person@ca0b6
3. 获取成员方法
Method[] getMethods4
Method getMethod(string name, 类<?>... parameterTypes)
Method[ ] getDec laredMethods()
Method getDec laredMethod(String name, 类<?>... parameterTypes)
Method对象的操作:
- 执行方法:
object invoke(object obj, object... args)
- 获取方法名称:
String getName()
测试代码如下:
import java.lang.reflect.Method;
public class 反射获取成员方法 {
public static void main(String[] args) throws Exception {
Class personClass = Person.class;
//通过指定方法名和参数获取成员方法,获取无参方法不参数,只指定方法名
Method eat_method = personClass.getMethod("eat");
Person p = new Person();
//指定person对象执行方法
eat_method.invoke(p);
//获取有参的成员方法
Method eat_method2 = personClass.getMethod("eat", String.class);
eat_method2.invoke(p, "刀削面");
System.out.println("----------------------");
//获取所有public修饰的方法,包括从Object类中继承的方法
Method[] methods = personClass.getMethods();
for(Method method:methods){
System.out.println(method);
//获取方法名
String name = method.getName();
System.out.println(name);
//method.setAccessible(True);
}
}
}
运行结果:
eat…
eat…刀削面
----------------------
public void WebServer.Person.eat(java.lang.String)
eat
public void WebServer.Person.eat()
eat
public void WebServer.Person.setAge(int)
setAge
public int WebServer.Person.getAge()
getAge
public java.lang.String WebServer.Person.getName()
getName
public void WebServer.Person.setName(java.lang.String)
setName
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
wait
public final void java.lang.Object.wait() throws java.lang.InterruptedException
wait
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
wait
public boolean java.lang.Object.equals(java.lang.Object)
equals
public java.lang.String java.lang.Object.toString()
toString
public native int java.lang.Object.hashCode()
hashCode
public final native java.lang.Class java.lang.Object.getClass()
getClass
public final native void java.lang.Object.notify()
notify
public final native void java.lang.Object.notifyAll()
notifyAll
注意:
-
无论是Field、Constructor还是Method,都可以使用
setAcceslsible(true)
方法来忽略访问权限修饰符的安全检查。所以反射机制中,权限修饰符不会造成限制。 -
而且使用
setAcceslsible(true)
还可以提高反射的效率,因为忽略安全检查可以省去很多步骤;使用反射比不使用反射慢了近30倍这样,如果忽略安全检查可以提高三四倍的效率。