漫谈反射
反射
理解反射的概念
反射就是把Java类中的各种成分映射相成Java类。
比如:众多的人用一个Person类来表示,那么众多的Java类就用一个Class类来表示。
反射也称为对类的解剖。把类的各个组成部分映射成一个个对应的Java类。
比如:一个类有:成员变量,方法,构造方法,包等等信息。
利用反射技术能够对一个类进行解剖。
事实上仅仅要拿到Java类的字节码相应的Class对象,就等于拿到了Java类中的各个成分。
反射的基石就是Class。
Class类
Class类用于表示.class文件,是全部载入进内存的字节码对象的父类。所以能够通过Class得到执行时的类。
怎样得到某个class文件相应的class对象呢?
方法有3种:
1)类名.class 比如,Person.class。
2)对象.getClass() 比如。new Data().getClass()。
3)Class.forName("包名.类名"); 比如,Class.forName("java.lang.String");
代码演示样例:
public class ClassTest{
public static void main(String...args)throws Exception{
String str = "abc";
Class cls1 =String.class;
Class cls2 = str.getClass();
Class cls3 = Class.forName("java.lang.String");
System.out.println(cls1 == cls2);//true
System.out.println(cls2 == cls3);//true
}
}
注意:字节码文件是唯一的,所以不管怎么获取。都是同一份字节码文件。
九个提前定义Class实例对象(八大原始类型+void)
Class 类的实例表示正在执行的 Java 应用程序中的类和接口。
枚举是一种类,凝视是一种接口。
每一个数组属于被映射为 Class 对象的一个类,全部具有同样元素类型和维数的数组都共享该Class 对象。
主要的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和keyword void也表示为 Class 对象。
注意:
1、int.class == Integer.TYPE。
2、数组类型的Class实例对象Class.isArray()为true。
3、反射并非Java 5.0的新特性。
Java 5.0 代码演示样例:
package com.itheima.day01;
public class ReflectTest {
public static void main(String[] args) {
System.out.println("推断int基本数据类型和Integer的基本数据类型"
}System.out.println("推断int.class是不是基本数据类型 结果为:"+int.class.isPrimitive());+ "是不是同一个Class对象 结果为:"+(int.class == Integer.TYPE));
System.out.println("推断是不是数组类型 结果为:"+int[].class.isArray());
}
总之,仅仅要是在源程序中出现的类型,都有各自的Class实例对象,比如。int.class,int[].class。
一个类中的每一个成员都能够用对应的反射API类的一个实例对象来表示。通过调用Class类的方法能够得到这些实例对象后。得到这些实例对象后有什么用呢?怎么用呢?这正是学习和应用反射的要点。
构造方法的反射应用(Coustructor类)
Constructor类的实例对象代表类的一个构造方法。
反射公共。私有和保护的构造方法:
反射公共的须要的方法是:getConstructor();
反射私有的须要的方法是:getDeclaredConstructor();
Constructor对象代表一个构造方法。Constructor对象有的方法:得到构造方法名字。得到所属于的类,产生实例对象。
得到某个类空參数构造方法。例:
Constructor constructor = Class.forName("java.lang.String").getConstructor();
得到某个类全部的构造方法,例:
Constructor [] constructors= Class.forName("java.lang.String").getConstructors();
得到某一个带參数的构造方法,例:
Constructor constructor =Class.forName("java.lang.String").getConstructor(StringBuffer.class);
注意:一个类有多个构造方法,用什么方式能够区分清楚想得到当中的哪个方法呢?依据參数的个数和类型,比如,Class.getMethod(name,Class... args)中的args參数就代表所要获取的那个方法的各个參数的类型的列表。重点:參数类型用什么方式表示?用Class实例对象。
利用构造方法创建实例对象:
通常方式:String instance = new String(new StringBuffer("黑马程序猿"));
反射方式:String instance = (String)constructor.newInstance("黑马程序猿");
//调用获得的方法时要用到上面同样类型的实例对象
通过Class类中的newInstance()方法也可创建类的实例,其内部工作原理是先得无參的构造方法,再用构造方法创建实例对象。
代码演示样例:
package com.itheima.day01;
import java.lang.reflect.Constructor;
public class ReflectTest {
public static void main(String[] args) throws Exception {
Class clszz = Class.forName("java.lang.String");
Constructor constructor = clszz.getConstructor(StringBuffer.class);
String str = (String) constructor.newInstance(new StringBuffer("黑马程序猿"));
char [] chs = str.toCharArray();
for(int x = 0; x<chs.length;x++){
}System.out.println(chs[x]);
}
}
成员变量的反射(Field类)
Field类代表反射某个类中的一个成员变量。
问题:得到的Field对象是相应到类上面的成员变量。还是相应到对象上的成员变量?类仅仅有一个。而该类的实例对象有多个,假设是与对象关联,那关联的是哪个对象呢?所以字段fieldAge 代表的是Age的定义,而不是详细的Age变量。
(注意訪问权限的问题)也就是说,定义的是类对象,而非对象的对象。
当我们须要对其操作的时候。须要确定是那个详细的对象。
代码演示样例:
package com.itheima.study;
public class Person {
public int age;
private int height;
public Person(int age,int height){
this.age=age;
this.height=height;
}
}
package com.itheima.study;
import java.lang.reflect.Field;
public class ReflectTest{
public static void main(String...args) throws Exception{
Person p = new Person(20,30);
Field fieldAge = p.getClass().getField("age");
System.out.println(fieldAge.get(p));
}
}
练习:将随意一个对象中的全部String类型的成员变量所相应的字符串内容中的“b"改为"a"。
public class ReflectPoint
public class Reflect {
{
private int x;
public int y;
public String str1="blue";
private String str2="bubble";
public String str3="soso";
public ReflectPoint(int x, int y)
{
super();
this.x = x;
this.y = y;
}
public String toString()
{
return str1+" "+str2+" "+str3;
}
}
/**
* 练习:将随意一个对象中的全部String类型的成员变量所相应的字符串内容中的“b"改为"a"。
*
*/
public static void main(String[] args) throws Exception {
Constructor[] constructors=String.class.getConstructors();//得到String类的全部公共构造方法
Constructor constructor=String.class.getConstructor(StringBuffer.class);//得到指定參数的构造方法
//用构造方法创建实例
String s=(String)constructor.newInstance(new StringBuffer("黑马"));
System.out.println(s);
//Class类里面的无參构造方法
Date date=(Date)Class.forName("java.util.Date").newInstance();
System.out.println(date);
ReflectPoint rp=new ReflectPoint(3,3);
Field fieldY=rp.getClass().getField("y");//注意fieldY不是对象的变量,是所在类的变量
int rpY=(Integer) fieldY.get(rp);//依据对象取得变量的值
System.out.println(rpY);
Field fieldX=rp.getClass().getDeclaredField("x");//由于x被private修饰
fieldX.setAccessible(true);//把私有的变量设置成能够訪问
System.out.println(fieldX.get(rp));
value(rp);
System.out.println(rp);
}
private static void value(Object obj) throws Exception {
Field[] fields=obj.getClass().getFields();
for (Field field : fields) {
if (field.getType()==String.class) {//getType()返回一个 Class 对象。它标识了此 Field 对象所表示字段的声明类型
String oldvalue=(String) field.get(obj);// 返回指定对象上此 Field 表示的字段的值
String newvalue=oldvalue.replace('b','a');//把b变成a
field.set(obj, newvalue);//将指定对象变量上此 Field 对象表示的字段设置为指定的新值。
}
}
}
}