反射
什么是反射?
能够获取当前某个类的字节码文件对象Class,那么就可以获取当前类的构造器并且创建当前类实例,
还可以获取当前类的成员变量并去赋值,或者获取当前类的成员方法并去调用!
1.字节码文件
如何获取一个类的字节码文件对象?
1)Object类的getClass()获取 获取当前某个类的实例(正在运行的类)
2)任意Java类型的class属性
3)Class类的静态功能
public static Class<?> forName(String className)
Person类—下同
public class Person {
//成员变量
private String name ;//姓名 私有
public int age ; //年龄 默认
String address ; //地址 默认修饰符
//无参构造方法:公共的
public Person(){}
//提供带两个参数的构造方法,默认修饰符
Person(String name,int age){
this.name = name ;
this.age = age ;
}
//提供三个参数的构造方法,私有的
private Person(String name,int age,String addrss){
this.name = name ;
this.age = age ;
this.address = addrss ;
}
//提供一些成员方法(非静态)
public void show(){
System.out.println("show Person");
}
private String functioin(String str){
return str ;
}
void method(String message,int num){
System.out.println(message+num);
}
public int method2(){
return 100 ;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", address='" + address + '\'' +
'}';
}
}
public class ReflectDemo {
public static void main(String[] args) throws ClassNotFoundException {
//获取Person类的字节码文件对象:Class
Person p = new Person() ;
Class c1 = p.getClass();
System.out.println(c1);//class 全限定名称
Person p2 = new Person() ;
Class c2 = p2.getClass() ;
System.out.println(c2);
System.out.println(c1==c2);
System.out.println(p==p2);//两个对象
System.out.println("----------------------------");
Class c3 = Person.class ;
System.out.println(c3);
System.out.println(c1==c3);
System.out.println("-----------------------------");
//知道某个一类的全限定名称了,也可以获取当前类的字节码文件对象
// Class c4 = Class.forName("Person"); com.qf.reflect_03.Person //参数字符串:全限定名称
Class c4 = Class.forName("com.qf.reflect_03.Person"); //com.qf.reflect_03.Person //参数字符串:全限定名称
System.out.println(c4);
System.out.println(c1==c4);
//第二种方式和第三种使用 推荐第三种 因为参数为String------>就可以存储在配置文件中
}
}
2.创建对象
1.获取构造方法:
1)public Constructor<?>[] getConstructors():获取当前字节码文件对象中(正在运行的这个类)里面所有的公共的构造方法所在的对象
2)public Constructor<?>[] getDeclaredConstructors():获取所有的构造器对象Constructor,返回构造器对象数组 包含私有化构造/默认的
3)public Constructor<T> getDeclaredConstructor(类<?>... parameterTypes) :参数都是参数类型的字节码文件对象
4)public Constructor<T> getConstructor(Class<?>... parameterTypes) :
( ... :jdk5以后 可变参数 (参数数量未知) )
获取指定的公共的单个的构造器对象 参数:需要书写的是当前参数类型的字节码文件对象
public void setAccessible(boolean flag)参数为true,取消Java语言访问检查
public T newInstance(Object... initargs): 参数为最终赋值的实际参数 (可变参数:实际参数未知)
/*
现在需要使用反射(底层)如何创建p对象的!
*/
public class ReflectDemo {
public static void main(String[] args) throws
ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//之前的写法
Person p = new Person() ;
System.out.println(p);
System.out.println("-----------------------------------------");
//使用反射的方式创建当前类实例
//1)获取Person类的字节码文件对象
Class personClass = Class.forName("com.qf.reflect_03.Person") ;//class com.qf.reflect_03.Person
//2)获取构造器对象
//Class类
//public Constructor<?>[] getConstructors():获取当前字节码文件对象中(正在运行的这个类)里面所有的公共的构造方法所在的对象
Constructor[] constructors = personClass.getConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);//public com.qf.reflect_03.Person()
}
//public Constructor<?>[] getDeclaredConstructors():获取所有的构造器对象Constructor,返回构造器对象数组 包含私有化构造/默认的
Constructor[] constructors = personClass.getDeclaredConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
//结果:
//private com.qf.reflect_03.Person(java.lang.String,int,java.lang.String)
//com.qf.reflect_03.Person(java.lang.String,int)
//public com.qf.reflect_03.Person()
}
//获取某一个构造器对象Constructor
//public Constructor<T> getConstructor(Class<?>... parameterTypes) :// ... :jdk5以后 可变参数 (参数数量未知)
//获取指定的公共的单个的构造器对象 参数:需要书写的是当前参数类型的字节码文件对象
Constructor constructor = personClass.getConstructor();//java.lang.String
//通过构造器对象创建当前类的实例
//public T newInstance(Object... initargs): 参数为最终赋值的实际参数 (可变参数:实际参数未知)
Object obj = constructor.newInstance();
System.out.println(obj); //p
}
}
public class ReflectDemo2 {
public static void main(String[] args) throws Exception {
//之前的写法
// Person p = new Person("高圆圆",20) ; //默认修饰符只能同一个包下访问
//反射创建p对象
//1)获取类的字节码文件对象
Class clazz = Class.forName("com.qf.reflect_03.Person") ;
//2)获取构造Constructor(构造方法所在对象)
//Person(String name,int age)
获取指定的构造函数对象 ,返回值都是单个构造函数
//public Constructor<T> getDeclaredConstructor(类<?>... parameterTypes) :参数都是参数类型的字节码文件对象
// 公共的构造函数对象:public Constructor<T> getConstructor(Class<?>... parameterTypes)
Constructor constructor = clazz.getDeclaredConstructor(String.class, int.class);
//通过构造器创建当前类的实例
//getDeclaredConstructor
//java.lang.IllegalAccessException: Class com.qf.reflect_04.ReflectDemo2 非法访问异常:因为当前构造函数式默认修饰符
// can not access a member of class com.qf.reflect_03.Person with modifiers ""
//取消Java语言访问检查(反射中使用到)
//public void setAccessible(boolean flag)参数为true,取消Java语言访问检查
constructor.setAccessible(true);
Object obj = constructor.newInstance("高圆圆", 20);
System.out.println(obj);//Person{name='高圆圆', age=20, address='null'}
}
}
3.成员变量
获取方法:
public Field[] getFields()throws SecurityException: 获取所有的公共的成员变量Field
public Field[] getDeclaredFields(): 所有的字段所在的Field对象,这包括公共,受保护,默认(包)访问和私有字段,但不包括继承的字段。
public Field getField(String name):参数为字段名称,获取单个成员变量
public Field getDeclaredField(String name): 获取指定的单个的字段所在的Field对象 , 参数为"当前字段名称",这包括公共,受保护,默认(包)访问和私有字段,但不包括继承的字段。
public class ReflectDemo {
public static void main(String[] args) throws Exception {
//没有使用反射之前
Person p = new Person() ;
System.out.println(p);
p.age = 20 ;//对象名.属性;
System.out.println(p);
System.out.println("-----------------------------");
//反射的方式
//1)获取Person类的字节码文件
Class clazz = Class.forName("com.qf.reflect_03.Person") ;
//方式1:直接获取当前类的实例
//public T newInstance()
Object obj = clazz.newInstance();
System.out.println(obj);
//方式2:通过获取构造函数对象,创建当前类实例
/* Constructor constructor = clazz.getConstructor();
Object obj = constructor.newInstance();
System.out.println(obj);*/
//2)Class类
//public Field[] getFields()throws SecurityException 获取所有的公共的成员变量Field
Field[] fields = clazz.getFields() ;
for (Field field : fields) {
System.out.println(field);
//public int com.qf.reflect_03.Person.age
}
//public Field[] getDeclaredFields():所有的字段所在的Field对象
// 这包括公共,受保护,默认(包)访问和私有字段,但不包括继承的字段。
Field[] fields = clazz.getDeclaredFields() ;
for (Field field : fields) {
System.out.println(field);
//private java.lang.String com.qf.reflect_03.Person.name
// public int com.qf.reflect_03.Person.age
// java.lang.String com.qf.reflect_03.Person.address
}
//现在访问name private String name ;
//public Field getDeclaredField(String name):获取指定的单个的字段所在的Field对象
//参数为"当前字段名称"
Field nameField = clazz.getDeclaredField("name"); //默认:null
//Field提供方法
//public void set(Object obj,Object value):给绑定在当前对象上的字段进行赋值
//参数1:创建当前类的实例
//参数2:value 赋值的实际参数
//取消Java语言访问检查
nameField.setAccessible(true);
//赋值
nameField.set(obj,"高圆圆");
System.out.println(obj);
System.out.println("-----------------------------------------------");
//public int age ;
//获取当前age所在Field对象
//public Field getField(String name):参数为字段名称
Field ageField = clazz.getField("age");
//直接赋值
ageField.set(obj,20);
System.out.println(obj);
System.out.println("-----------------------------------------");
// String address ; //地址 默认修饰符
//获取Field对象
Field addressField = clazz.getDeclaredField("address");
//取消Java语言访问检查
addressField.setAccessible(true);
//赋值
addressField.set(obj,"西安市");
System.out.println(obj);
}
}
4.成员方法
如何通过反射获取成员方法所在的Method,并能去调用
1)public Method[] getMethods(): 获取所有的成员方法:公共的(包含他父类的所有的公共的方法)
2)public 方法[] getDeclaredMethods(): 通过此表示类对象,包括公共,保护,默认(包)访问和私有方法,但不包括继承的方法
3)public Method getMethod(String name,Class<?>... parameterTypes)
参数1:方法名
参数2:当前这个方法所携带的参数类型的Class
4)public Method getDeclaredMetho(String name,Class<?> ..parameterTypes): 获取指定的单个的字段所在的成员方法 , 参数为"当前字段名称",这包括公共,受保护,默认(包)访问和私有字段,但不包括继承的字段。
public Object invoke(Object obj,Object... args):将方法的实际参数绑定在当前类的实例上
public class ReflectDemo {
public static void main(String[] args) throws Exception{
//没有使用反射之前
Person p = new Person() ;
p.show();
System.out.println("--------------------------");
//直接反射方式获取
//1)获取当前Person字节码文件对象
Class clazz = Class.forName("com.qf.reflect_03.Person") ;
Object obj =clazz.newInstance() ;//当前类实例
//2)获取所有的成员方法:公共的(包含他父类的所有的公共的方法)
//public Method[] getMethods()
//public 方法[] getDeclaredMethods():通过此表示类对象,包括公共,保护,默认(包)访问和私有方法,但不包括继承的方法。
// Method[] methods = clazz.getMethods();
/* Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method);
}*/
//获取指定的方法 调用public void show()
// public Method getMethod(String name,Class<?>... parameterTypes)
//参数1:方法名
//参数2:当前这个方法所携带的参数类型的Class
Method showMethod = clazz.getMethod("show");
//Method:底层调用方法
//public Object invoke(Object obj,Object... args):将方法的实际参数绑定在当前类的实例上
//参数1:就是当前类的实例
//参数2:可变参数, 方法传递实际参数 若无参,则实际不用传递
//返回值:如果当前方法本身有返回值,则返回Object
showMethod.invoke(obj) ;
System.out.println("--------------------------------------");
//private String functioin(String str){
// return str ;
// }
//调用Person类中function
//获取function方法的Method对象
//public Method getDeclaredMetho(String name,Class<?> ..parameterTypes).
Method functonMethod = clazz.getDeclaredMethod("functioin", String.class);
//取消Java语言访问检查:私有方法
functonMethod.setAccessible(true);
Object returnObj = functonMethod.invoke(obj, "hello,洪学佳,别睡了...");
System.out.println(returnObj);
// void method(String message,int num){
// System.out.println(message+num);
// }
//
// public int method2(){
// return 100 ;
// }
}
}
5.应用
/*
* 反射的应用:
* 现在有一个ArrayList<Integer>,里面有一些元素,
* 如何给ArrayList添加String类型的元素呢?
*/
public class ReflectTest {
public static void main(String[] rgs) throws Exception {
//有一个ArrayList集合对象
ArrayList<Integer> arrayList = new ArrayList<>() ;
arrayList.add(100) ;
arrayList.add(50) ;
arrayList.add(200) ;
System.out.println(arrayList);
// arrayList.add("hello") ;
//1)获取当前集合实例所在的字节码文件对象
//通过Object的getClass()获取当前实例所在的类的字节码对象
Class clazz = arrayList.getClass();
//System.out.println(obj);
//System.out.println(clazz);//class java.util.ArrayList
//获取当前类中 add方法所在的Method
//public boolean add(E e) ://参数就是任意Java类型 (Element)
Method addMethod = clazz.getMethod("add", Object.class);
//调用方法
addMethod.invoke(arrayList, "hello");
addMethod.invoke(arrayList, "world");
addMethod.invoke(arrayList, "javaEE");
System.out.println(arrayList);
}
}
需求;
现在有一个学生类以及工人类,两类都有一个love方法
ReflectTest2在测试类中进行测试
1)起初,创建的一个学生对象,调用love方法
2)代码更改,创建一个工人类对象,调用love方法
如何将上面的代码进行优化!
Java设计原则:
开闭原则:对修改关闭,对扩展开放 (在现有代码进程上,想办法进行扩展...)
如果提供配置文件,以后只需要修改配置文件中内容,而不更改当前的代码!
如果能将配置文件,如果能加载属性集合列表中
className=com.qf.reflect_06.Worker
methodName=love
就可以通过key--->value
com.qf.reflect_06.Worker:创建当前类的字节码文件对象
/*
* 通过反射获取Person类的字节码文件对象并获取当前构造函数(带三个参数的构造函数)创建当前类实例
*
* private Person(String name,int age,String addrss)
*/
public class ReflectDemo3 {
public static void main(String[] args) throws Exception {
//获取Person类的字节码文件对象
Class clazz = Class.forName("com.qf.reflect_03.Person");
//获取构造器对象
//public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
Constructor con = clazz.getDeclaredConstructor(String.class , int.class , String.class) ;
//取消Java语言访问检查
con.setAccessible(true);
//通过构造器对象创建当前类实例
Object obj = con.newInstance("高圆圆", 20, "西安市");
System.out.println(obj); //Person{name='高圆圆', age=20, address='西安市'}
}
}
/*
字节码文件:myClass.properties
className=com.qf.reflect_06.Worker
methodName=love
*/
/*
工人类
*/
public class Worker {
public void love(){
System.out.println("爱生活,爱drink...,爱足球");
}
}
/*
学生类
*/
public class Student {
public void love(){
System.out.println("爱学习,爱Java,爱高圆圆...");
}
}
public class ReflectTest2 {
public static void main(String[] args) throws Exception {
//1)现在学生类对象
/* Student s1 = new Student() ;
s1.love();*/
//需求改变:创建工人类对象
/*Worker worker = new Worker() ;
worker.love();*/
//优化:提供src(类路径下提供配置文件)myClass.properties
//1)读取src下面的myClass.properties
//获取资源文件所在的输入流对象
InputStream inputStream = ReflectTest2.class
.getClassLoader().
getResourceAsStream("myClass.properties");
//创建属性集合列表:空的
Properties prop = new Properties() ;
//将指定的配置文件所在的输入流加载属性列表中
prop.load(inputStream);
System.out.println(prop);
//可以通过可以key获取value
String className = prop.getProperty("className") ; //当前类的全限定名称
String methodName = prop.getProperty("methodName") ;
//通过反射创建当前类的字节码文件对象
Class clazz = Class.forName(className) ;
//创建当前类的实例 (解耦)
Object obj = clazz.newInstance() ;
//通过clazz字节码文件对象获取当前成员方法所在的Method类对象
Method method = clazz.getMethod(methodName) ;
method.invoke(obj) ;
}
}