4.1java反射

    技术2023-12-02  74

    反射的作用:反射是为了解决在运行期,对某个实例一无所知的情况下,如何调用其方法。 反射的目的:获取一个class对应的Class实例。 反射的实现:

    通过一个class的静态变量class获取:Class cls = String.class; 若有实例变量,可用实例变量的.getClass()方法获取:String s = "Hello"; Class cls = s.getClass(); 若知道class的完整类名,通过Class.forName()获取:Class cls = Class.forName("java.lang.String");

    JVM的动态加载:JVM在执行java程序时,不是一次性将用到的class全部加载到内存,而是第一次使用才加载。 如何使用反射获取字段: 需要注意的时,获取字段的field会经常出现错误,因此需要在方法之前抛出异常。

    Field getField(name):根据字段名获取某个public的field(包括父类)Field getDeclaredField(name):根据字段名获取当前类的某个field(不包括父类)Field[] getFields():获取所有public的field(包括父类)Field[] getDeclaredFields():获取当前类的所有field(不包括父类) 返回的Field类似: public int Student.score public java.lang.String Person.name private int Student.grade

    一个Field对象包含了字段的所有信息,包括:

    getName():返回字段名称,例如,“name”;getType():返回字段类型,也是一个Class实例,例如,String.class;getModifiers():返回字段的修饰符,它是一个int,不同的bit表示不同的含义。

    获取字段的值:

    字段值的获取是需要在有值时才能执行。 因此使用反射的第二种方法,根据实例值获取字段值。

    public class Main { public static void main(String[] args) throws Exception { Object p = new Person("Xiao Ming"); Class c = p.getClass(); Field f = c.getDeclaredField("name"); f.setAccessible(true);//设置该字段在此类中可访问 Object value = f.get(p); System.out.println(value); // "Xiao Ming" } } class Person { private String name; public Person(String name) { this.name = name; } }

    根据以上demo,步骤为:

    创建Obj实例对象p。用.getClass()方法获取到实例的Class。根据Class的.getField()方法获取到field。根据获取到的field的get()方法获取到字段的值。 注意:在获取private字段时用到getDeclaredField()方法,但即便获取到,在改类中也用不了,因此为了解决这个问题,将该字段设置为可访问即:f.setAccessible(true);,否则会出现异常。 这是一个获取字段值的方法,体现了反射中不知道该类名并且能够获取到字段值的特点: public static Object getFieldValue(Object obj,String name) throws Exception { //设置为静态方法,可以不通过任何实例调用此方法。 //方法参数有两个,第一个obj是传递的类,第二个是传递的类的属性。 Class cla = obj.getClass();//获取到Class Field field = cla.getDeclaredField(name);//获取到field field.setAccessible(true);//设置field可访问 return field.get(obj);//返回获取到的字段值 }

    设置字段值

    有获取就有设置: 设置是通过Field.set(Object, Object)方法实现的。其中第一个是参数指定的实例,第二个是待修改的参数。

    public static void setFieldValue(Object obj,String name,String setName) throws Exception { //方法参数有三个,第一个为传递的类,第二个为传递类的字段名,第三个为设置的字段的值。 Class cla = obj.getClass(); Field field = cla.getDeclaredField(name); field.setAccessible(true); field.set(obj, setName); }

    反射中的方法

    和字段一样,Class类也提供了获取Method的方法:

    Method getMethod(name, Class…):获取某个public的Method(包括父类)Method getDeclaredMethod(name, Class…):获取当前类的某个Method(不包括父类)Method[] getMethods():获取所有public的Method(包括父类)Method[] getDeclaredMethods():获取当前类的所有Method(不包括父类) 与字段不同的是,getMethod()的参数可以有两个,第一个是方法名,第二个是参数类型,可以无参数。 public class Main { public static void main(String[] args) throws Exception { Class stdClass = Student.class; // 获取public方法getScore,参数为String: System.out.println(stdClass.getMethod("getScore", String.class)); // 获取继承的public方法getName,无参数: System.out.println(stdClass.getMethod("getName")); // 获取private方法getGrade,参数为int: System.out.println(stdClass.getDeclaredMethod("getGrade", int.class)); } } class Student extends Person { public int getScore(String type) { return 99; } private int getGrade(int year) { return 1; } } class Person { public String getName() { return "Person"; } }

    一个Method的对象包含的方法有:

    getName():返回方法名称,例如:“getScore”;getReturnType():返回方法返回值类型,也是一个Class实例,例如:String.class;getParameterTypes():返回方法的参数类型,是一个Class数组,例如:{String.class, int.class};getModifiers():返回方法的修饰符,它是一个int,不同的bit表示不同的含义。

    获取到的方法调用:

    使用Method.invoke(实例对象,参数);

    public class Main { public static void main(String[] args) throws Exception { // String对象: String s = "Hello world"; // 获取String substring(int)方法,参数为int: Method m = String.class.getMethod("substring", int.class); // 在s对象上调用该方法并获取结果: String r = (String) m.invoke(s, 6); // 打印调用结果: System.out.println(r); } }

    如果调用的是一个静态方法,则无需传入对象,invoke()方法的第一个参数填null 如果设置方法属性为public,则用.setAccessible()方法,参数为true,但注意有时候会报错。 接下来是一个例子,用于演示反射调用方法:

    package test; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class testMethod { public static void main(String[] args) throws Exception { testMethod t = new testMethod(); Method method1 = getMethod(t, "print", int.class); method1.invoke(t, 1); Method method = getMethod(t, "print", int.class,String.class); method.invoke(t, 1,"这是一首简单的小情歌"); } //使用java中的可变参数,注意可变参数一定要放在最后,因为当你可变参数之后还有参数的话,那么最后一个参数一定不能成功赋值 public static Method getMethod(Object obj,String methodname,Class... classes) throws Exception { //参数:第一个实例对象,第二个获取的方法名,第三个参数类型,第四个参数值。 Class class1 = obj.getClass();//获取实例的类 //根据方法名和参数类型获取方法 Method method = class1.getMethod(methodname, classes); //调用方法 return method; } //重载,一个参数 public void print(int a) { // TODO Auto-generated method stub System.out.println(a); } //重载,两个参数 public void print(int a,String text) { // TODO Auto-generated method stub System.out.println(a+text); } }

    结果

    1 1这是一首简单的小情歌

    反射如何调用构造方法

    反射中调用无参构造方法: Class提供了一个方法是newInstance():供其创建一个新的实例,仅限于无参构造方法

    Person p = Person.class.newInstance();

    调用有参数的构造方法: java反射的API提供了Constructor对象,它包含了构造方法的所有信息,能够去创建一个实例。 Constructor与Method非常相似,都是可以创建一个方法,不同的是Constructor创建的是一个构造方法。 使用Method和Constructor返回的都是该对象方法的一个实例,获得实例之后,调用的invoke和newInstance方法是Method和Constructor的方法(这里的newInstance和Class提供的newInstance方法不同。)

    通过Class实例获取Constructor的方法:

    getConstructor(Class…):获取某个public的Constructor;getDeclaredConstructor(Class…):获取某个Constructor;getConstructors():获取所有public的Constructor;getDeclaredConstructors():获取所有Constructor。 public class TestConstructor { public static void main(String[] args) throws Exception { Class class1 = Person.class; //参数和Method一样,是该构造方法传递的参数类型 Constructor constructor = class1.getConstructor(String.class); //创建实例对象 Object object = constructor.newInstance("张三"); //创建getName方法对象 Method m = getMethod(object, "getName"); //调用getName方法 System.out.println(m.invoke(object)); } public static Method getMethod(Object obj,String methodname,Class... classes) throws Exception { //参数:第一个实例对象,第二个获取的方法名,第三个参数类型,第四个参数值。 Class class1 = obj.getClass();//获取实例的类 //根据方法名和参数类型获取方法 Method method = class1.getMethod(methodname, classes); //调用方法 return method; } }

    向上获取类(父类或接口)

    获取父类:用Class的getSuperclass()方法 获取接口:用Class的getInterface()方法 继承关系:instanceof判断的是该实例是否属于该类型 isAssignableForm()判断的是前者是否能够向上转型为后者

    Object n = Integer.valueOf(123); boolean isDouble = n instanceof Double; // false boolean isInteger = n instanceof Integer; // true boolean isNumber = n instanceof Number; // true boolean isSerializable = n instanceof java.io.Serializable; // true // Integer i = ? Integer.class.isAssignableFrom(Integer.class); // true,因为Integer可以赋值给Integer // Number n = ? Number.class.isAssignableFrom(Integer.class); // true,因为Integer可以赋值给Number // Object o = ? Object.class.isAssignableFrom(Integer.class); // true,因为Integer可以赋值给Object // Integer i = ? Integer.class.isAssignableFrom(Number.class); // false,因为Number不能赋值给Integer

    代理

    借鉴链接 静态代理: 一个接口,一个被代理类,一个代理类,代理和被代理类均继承这个接口,被代理类不做实际操作,一切由代理类来完成,在代理类中创建被代理类的对象进行操作。 接口:

    public interface HelloInterface { void sayHello(); }

    被代理类:

    public class Hello implements HelloInterface{ @Override public void sayHello() { System.out.println("Hello zhanghao!"); } }

    代理类:

    public class HelloProxy implements HelloInterface{ private HelloInterface helloInterface = new Hello(); @Override public void sayHello() { System.out.println("Before invoke sayHello" ); helloInterface.sayHello(); System.out.println("After invoke sayHello"); } }

    调用代理类:

    public static void main(String[] args) { HelloProxy helloProxy = new HelloProxy(); helloProxy.sayHello(); } 输出: Before invoke sayHello Hello zhanghao! After invoke sayHello

    使用静态代理很容易就完成了对一个类的代理操作。但是静态代理的缺点也暴露了出来:由于代理只能为一个类服务,如果需要代理的类很多,那么就需要编写大量的代理类,比较繁琐。 动态代理: 动态创建接口对象的方式叫做动态代理。 在运行期动态创建一个interface实例的方法如下:

    定义一个InvocationHandler实例,它负责实现接口的方法调用;通过Proxy.newProxyInstance()创建interface实例,它需要3个参数: - 使用的ClassLoader,通常就是接口类的ClassLoader; - 需要实现的接口数组,至少需要传入一个接口进去; - 用来处理接口方法调用的InvocationHandler实例。将返回的Object强制转型为接口。 public class Main { public static void main(String[] args) { InvocationHandler handler = new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println(method); if (method.getName().equals("morning")) { System.out.println("Good morning, " + args[0]); } return null; } };//创建接口的对象 Hello hello = (Hello) Proxy.newProxyInstance( Hello.class.getClassLoader(), // 传入ClassLoader new Class[] { Hello.class }, // 传入要实现的接口 handler); // 传入处理调用方法的InvocationHandler hello.morning("Bob"); } } //一个接口 interface Hello { void morning(String name); }
    Processed: 0.017, SQL: 9