import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
-----------------------反射获取字节码与构造方法----------------------不知道怎么把带颜色代码拷贝过来所以,看起来可能有点费劲
/*
int.class:代表基本类型
编译只看左边
运行看右边
反射会导致程序性能严重下降
*/
public class ReflectTest {
public static void main(String[] args) throws Exception {
String str1="abc";
//得到他的字节码的3种方法
Class cls1=str1.getClass();
Class cls2=String.class;
Class cls3=Class.forName("java.lang.String");
System.out.println(cls1==cls2);
System.out.println(cls1==cls3);
System.out.println(cls2==cls3);
//isPrimitive:判定指定的 Class 对象是否表示一个基本类型。
//false
System.out.println(cls1.isPrimitive());
//它的字节码是不是基本类型的,,true
System.out.println(int.class.isPrimitive());
//基本类型的那份字节码是不是和包装类的字节码相同的,false
System.out.println(int.class==Integer.class);
//Integer.TYPE表示基本类型 int 的 Class 实例。
//所以他们相同:true
System.out.println(int.class==Integer.TYPE);
//int[].class它的字节码是不是一个基本类型呢?false
//数组也是一种类型。
System.out.println(int[].class.isPrimitive());
//判断一个class是不是数组应该这样判断,true
System.out.println(int[].class.isArray());
/*总之,只要是在源程序中出现的类型,都有各自的Class实例对象
例如int[],void...*/
/*newInstance(Object... initargs)
使用此 Constructor
对象表示的构造方法来创建该构造方法的声明类的新实例,
并用指定的初始化参数初始化该实例。
Constructor类代表某个类中的一个构造方法
得到某个类所有的构造方法:
例子:Constructor[] constructors=
Class.forName("java.lang.String").getConstructors();
得到某一个构造方法:
例子:Constructor constructor=
Class.forName("java.lang.String").getConstructor(StringBuffer.class);
Class里也有一个 newInstance()
T newInstance()
创建此 Class 对象所表示的类的一个新实例。
这个比Construction里的少走了很多弯路
它是找那个不带参数的构造方法然后用那个构造方法创建实例对象
*/
//new String(new Stringbuffer("abc"));
//用反射来做
Constructor constructor1=
//获得此类的构造方法:getConstructor(可变参数列表,每一个参数.class代表一个类型):
String.class.getConstructor(StringBuffer.class);
String str2=
(String)constructor1.newInstance(new StringBuffer("abc"));
// 之所以加强转是因为编译器只知道它是构造方法,
//却不知道你是哪个类身上的构造方法
// 第一个StringBuffer表示选择哪一个构造方法
// 第二个StringBuffer表示用这构造方法时还传一个StringBuffer对象进去
System.out.println(str2.charAt(2));//c
//反射就是把java类中的各种成分映射成相应的java类
}
----------------------------------反射调用成员变量----------------------------------------------------------------------------------------------------------------
public class Reflect {
private int x;public int y;
public String str1="ball";
public String str2="basketball";
public String str3="itsast";
public Reflect(int x, int y) {
super();
this.x = x;
this.y = y;
}
@Override
public String toString() {
return "Reflect [x=" + x + ", y=" + y + ", str1=" + str1 + ", str2="
+ str2 + ", str3=" + str3 + "]";
}
}
public class ReflectTest {
public static void main(String[] args) throws Exception
{
Reflect re=new Reflect(3,5);
//想得到一个字段。先得到字节码,有了字节码就可以getField(得到某个字段(也就是变量名,变量名是字符串 “变量名”))。
Field fieldY=re.getClass().getField("y");
/*fieldY的值是多少?是5,错!
fieldY不是对象身上的变量,而是类上,要用它去取某个对象上对应的值
它只是代表这个类字节码身上的变量,它没有对应到对象身上z
fieldY不代表一个具体的值,只代表一个变量,
不代表那个变量在某个对象上具体的值*/
//你现在想取出这个变量在某个对象的值应该这样
System.out.println(fieldY.get(re));
//getField只能获得那些可见的,
//getDeclaredField不管可不可见都能获得
Field fieldX=re.getClass().getDeclaredField("x");
//虽然看得到却依旧获取不到
//这时就要加个暴力反射,设置成可以访问
/*
setAccessible
public void setAccessible(boolean flag)
throws SecurityException
将此对象的 accessible 标志设置为指示的布尔值。
值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。
值为 false 则指示反射的对象应该实施 Java 语言访问检查。
*/
fieldX.setAccessible(true);
System.out.println(fieldX.get(re));
//为了美观,定义一个独立的方法并产生它
//这个方法还要接收一个对象
changStringValue(re);
//对结果进行打印
System.out.println(re);
}
// 由于要接收的对象不知道什么类型所以定成Object最为合适
private static void changStringValue(Object obj) throws Exception
{
//得到所有字段。先获得它所属的那份字节码,再获得字段,
//因为要获得所有,就用那个带数组的获取方法
//把获得的逐一存到数组里
Field[] fields=obj.getClass().getFields();
//然后进行遍历存到数组里的字段。
for(Field field:fields)
{
/*
getType()返回一个 Class 对象,
它标识了此 Field 对象所表示字段的声明类型
void set(Object obj, Object value)
将指定对象变量上此 Field 对象表示的字段设置为指定的新值。
*/
//if(field.getType().equals(String.class)){
//field是一个字段,这个字段它身上有类型就getType()在与String那种类型进行比较
//对字节码的的比较应该拿==比。因为是同一份字节码
if(field.getType()==String.class)
{
//假如是String那种字节码,那就要取得它的值 用get()方法
//这时需要把obj强转,它的返回类型是String
String oldValue=(String)field.get(obj);
//在用String类的替换方法replace,把值里的"b"换成"c";
/*
replace(char oldChar,char newChar)
返回一个新的字符串,
它是通过用 newChar 替换此字符串中出现的所有 oldChar 得到的。
*/
String newValue=oldValue.replace("b", "c");
//并且把对象里的字段值set成刚刚替换好的新值
//用set(原对象,新值)方法
field.set(obj, newValue);
}
}
}
---------------------反射调用成员方法-------------------------------------------------------------------------------
/*
//str1.charAt(1);用反射来做
是从String字节码身上拿出来的方法
Method getMethod(String name, Class<?>... parameterTypes)
name - 方法名
parameterTypes - 参数列表
返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。
Object invoke(Object obj, Object... args)
带有指定参数的指定对象调用由此 Method 对象表示的底层方法。
obj - 从中调用底层方法的对象
args - 用于方法调用的参数
如果底层方法是静态的,那么可以忽略指定的 obj 参数。该参数可以为 null。
如果底层方法所需的形参数为 0,则所提供的 args 数组长度可以为 0 或 null。
getMethod(方法名,(由于一个方法有多个重载所以要指定我们那个的参数列表和类型)有多少参数就写几个class进去)
*/
Method methodCharAt=String.class.getMethod("charAt", int.class);
/*
例如:你找的方法是从指定位置找一个字符,返回的是int角标值
indexOf(String str,int IndexOf),那么应该这么做
Method methodIndexOf=String.class.getMethod("indexOf",String.class,int.class)
methodIndexOf.invoke(str1,(需要找的字符)"b",(从哪开始)0)
*/
//用拿到方法的对象去做这件事
//methodCharAt.invoke(字符串对象,刚刚那拿的方法有几个参数就传几个参数)
System.out.println(methodCharAt.invoke(str1, 1));
//如果第一个参数是null,这还有什么意义呢?
//说明该Mehtod对象对应的是一个静态方法
/*
例如:
static String valueOf(long l)
返回 long 参数的字符串表示形式。
Method methodCharAt=String.class.getMethod("valueOf",long.class);
System.out.println("String:"+methodCharAt.invoke(null,131314142));
*/
/*人在黑板上画圆,画是圆的动作,
* 人关门,关门是门的动作,人只是推了一下,
* 相当于给门发了个信号然后门开始旋转把自己关上
* 列车司机把列车停住了,停住是列车的动作,
* 司机只是踩下刹车(相当于给列车发了个信号,让列车去完成)*/
-------------------------------反射调用main方法-----------------------------------------------------------------------------------------------
//用反射方式执行某个类中的main方法
class TestMain{
public static void main(String[] args){
for(String arg:args){
System.out.println(arg);
}
}
}
public class ReflectTest {
public static void main(String[] args) throws Exception
{
//正常调用方法
//TestMain.main(new String[]{"111","222","333"});
//在不知道类名的情况下用反射来调
//假设args就是类的名字
//我的源程序根本不知道要执行那个类
//加数组是传那个参数就执行那个参数的类
String className=args[0];
//接下来你把这个类名给我,我知道一定有一个main方法
Method mainMethod=Class.forName(className).getMethod("main", String[].class);
//获取到后就调用它,由于main它是静态的所以不需要传对象
//只需要(null,new一个新数组并传参数进去)
//因为main方法就接收一个数组对象,接收后会拆包一次
//,就出角标越界异常了而再加个数组,它一拆得到的还是一个数组对象就不会报异常了
//mainMethod.invoke(null, new Object[]{new String[]{"111","222","333"}});
//另一种方法直接告诉它就是一个对象,你不要去拆。
mainMethod.invoke(null, (Object)new String[]{"111","222","333"});
}
}
//用Dos命令行时,运行时是这样 java ReflectTest TestMain(被调用main方法的那个类名)