美文网首页
【android】Java反射

【android】Java反射

作者: 当时不是寻常 | 来源:发表于2018-07-23 19:35 被阅读8次

一、知识准备

现在我们首先来看三个问题。

1.什么是Class 类

类描述:描述JAVA类,众多的人可以用person类来表示,众多的JAVA类可以用一个类来描述,这个类就是Class。

2.怎么得到Class的对象。

如得到Person对象,可以这样做: Person p=new Person();

现在我们要知道一个概念:Class的实例对象就是内存中的一份字节码,或者说内存中的一份字节码对应Class的一个实例对象。现在解释一下什么是字节码?

我们常说JAVA编译后,会变成.class文件,而这里所说的字节码就是一个类编译而成的二进制代码,比如:

要得到Person对象时,是要先得到Person类的一份字节码(如果JVM中没有,需要先加载,如果有,可以直接返回);

要得到Set对象时,是要先得到Set类的一份字节码;

要得到Math对象时,是要先得到Math类的一份字节码。

注意:每个类的字节码,在内存中只有一份,每一份字节码就是一个Class的实例对象,比如要得到Person的字节码,可以有下面三种写法

A.   Class p=Person.class;  “Person.class”就代表Person的字节码,
     “Person.class”的所属类型的Class

B.   Class.forName("类的全路径名");

C.   对象名.getClass();`

例子:

public class ReflectTest {

     public static void main(String[] args) throws ClassNotFoundException {
                    
        String str1="abc";
                    
        //第一种方式:用类名.class
        Class class1=String.class;
                    
        //第二种方式:用String的对象获取String的字节码
        Class class2=str1.getClass();
                    
         //第二种方式:用String的对象获取String的字节码
        Class class3=Class.forName("java.lang.String");
                                      
        System.out.println("结果如下");
        System.out.println(class1==class2);
        System.out.println(class1==class3);
    }
}


这个类的结果输出是两个true,说明每个类的字节码,在内存中只有一份,
无论你用三种方式的哪一种方式去取,得到的都是同一份字节码。

3.基本类型的字节码

Java中有八种基本类型,所对应的字节码是基本数据类型的字节码,在程序中,可以种Class的isPrimitive()方法来判断它是不是基本数据类型的字节码。 值得一提的是,int[].class这不是基本数据类型的字节码,因为这是数组类型,当调用isArray的时候返回true。

二、反射深入分析

1.什么是反射?

定义:反射就是把JAVA类中的各种成分映射成映射成相应的JAVA类。

这句话怎么理解呢?

我们知道,一个类中可以有成员变量,成员方法,构造方法等信息,这些信息就用相应的类的实例对象来表示。

在反射中,有一些类用来表示反射以后类中的成分,比如:Filed,Method,Constructor,Package。

比如:System类中,有System.exit(),System.getProperties(),不管你的类中有什么方法,都可以用反射中Method来表示。

我们知道,一个类中可以有成员变量,成员方法,构造方法等信息,这些信息就用相应的类的实例对象来表示。

在反射中,有一些类用来表示反射以后类中的成分,比如:Filed,Method,Constructor,Package。

2.类构造函数的反射 Constructor

Constructor代表某一个类的构造方法

那么怎么得到一个类的构造方法? 可以先拿到这个类的CLass实例对象(这个类的字节码),实例对象中有两个方法。 得到类的一个构造函数的方法

 public Constructor<T> getConstructor(Class<?>... parameterTypes)  throws NoSuchMethodException,SecurityException

通过参数类型,得到想要的构造函数,因为接收的是可变参数,所以可以传多个,比如:想得到String的String(StringBuffer buf)的构造方法,可以这么写:

StringString.class.getConstructor(StringBuffer.class);

例子:

public class ConstructorReflect {
    public static void main(String[] args) throws Exception {       
        //通过字节码的方法得到Constructor对象
         Constructor<String> constructor = String.class.getConstructor(StringBuffer.class);
         String instance = constructor.newInstance(new StringBuffer("abc"));
        System.out.println(instance);
    }
}

注意在上面程序有两个地方用到StringBuffer,第一个StringBuffer指定得到哪个构造方法,第二个StringBuffer表示在用constructor实例化对象的时候 还要传递一个StringBuffer的对象,两个必须完全一致。 如果你这么写

String.class.newInstance();

那就是得到这个类中不带任何参数的构造方法。 得到一个类的所有构造函数的方法

public Constructor<?>[] getConstructors() throws SecurityException

3.类成员变量的反射 Filed

怎么通过反射得到类中字段的值呢? 例子1:下面的程序可以通过反射得到公有成员y,和私有成员x。

class Point{
    private int x;
    public int y;
    public Point(int x, int y) {
        super();
        this.x = x;
        this.y = y;
        }
}

public class FiledReflect {
    public static void main(String[] args) throws Exception {
                
        Point p1=new Point(5, 39);
        Point p2=new Point(3, 9);//对公有成员变量
        Field fieldY = p1.getClass().getField("y"); 
        int y = (int) fieldY.get(p1);//取对应p1对象的y字段的值,必须要有对象。
        System.out.println(y);
                
        //对私有成员变量,可以进行暴力反射
        Field fieldX = p1.getClass().getDeclaredField("x");
        fieldX.setAccessible(true);//暴力反射
        int x = (int) fieldX.get(p1);//取对应p1对象的x字段的值
        System.out.println(x);              
        }
}

例子2:需求:对一个Person类中的成员变量,把所有String类型的变量值字母a改成A;

import java.lang.reflect.Field;
class Person{
    private String name;
    private int age ;
    private String nickName;
    public Person(String name, int age, String nickName) {
         super();
        this.name = name;
        this.age = age;
        this.nickName = nickName;
        }
                    
        @Override
        public String toString() {
             return "Person [name=" + name + ", age=" + age + ", nickName="+ nickName + "]";
        }
}

 public class FiledReflect2 {
 
    public static void main(String[] args) throws Exception {
                        
        Person p=new Person("zhangsan", 25, "Amao");        
        //得到字节码,通过字节码得到这个类的所有方法
        Field[] fields = p.getClass().getDeclaredFields();
            for(Field field:fields){
            //因为一个类的字节码在内存中只有一份,所以用==比较更专业,此处用==,不用equals()
                if(field.getType()==String.class){
                    field.setAccessible(true);//进行暴力反射
                    String oldValue = (String) field.get(p);//获得字段的值
                    String newValue =oldValue.replace("a", "A");
                    field.set(p, newValue);
                    System.out.println(p);
                }
            }
                    
        }
}

执行结果是:Person [name=zhAngsAn, age=25, nickName=AmAo]

4.类成员方法的反射 Method

比如:想调用String类中的chatAt(int i)这个方法,该怎么么办呢?

public Method getMethod(String name, Class<?>... parameterTypes)hrows NoSuchMethodException,SecurityException

参数说明:

name:这个表示方法的名字 parameterTypes:这个参数的作用表示调用哪个方法,因为重载的原因,一个类中同名的方法可能不止一个 对于上面的问题,我们可以这样做。

Method  myStrCharAt=String.class.getMethod("charAt",int.class);  

例子:

public class MethodReflect {
 
    public static void main(String[] args) throws Exception{
        String str="abcdef";
                
        Method strMethod = str.getClass().getMethod("charAt", int.class);
                
        //得到方法之后,调用对象str的chatAt方法;
        char result = (char) strMethod.invoke(str, 1);
            
        System.out.println(result);//结果是b
    }
 }

当一个类XXX,我们已经通过反射得到它的方法xxxMethod,那么看下面一行代码
xxxMethod.invoke(null, 1);

这表示不知道谁的xxxMethod方法,因为传递的是null,说明调用的这个方法是属于类的,就是静态方法,所以就不需要传递参数了。

5.数组与反射

数组在 Java 语言中是一种特殊的类类型,一个数组的引用可以赋给 Object 引用。观察下面的例子看看数组是怎么工作的:

import java.lang.reflect.*;  
public class Array1 { 
   public static void main(String args[]) { 
      try { 
           Class cls = Class.forName("java.lang.String"); 
           Object arr = Array.newInstance(cls, 10); 
           Array.set(arr, 5, "this is a test"); 
           String s = (String) Array.get(arr, 5); 
           System.out.println(s); 
      } 
      catch (Throwable e) { 
           System.err.println(e); 
      } 
   } 
}

例中创建了 10 个单位长度的 String 数组,为第 5 个位置的字符串赋了值,最后将这个字符串从数组中取得并打印了出来。

本文转自https://blog.csdn.net/u013263323/article/details/45892711

相关文章

网友评论

      本文标题:【android】Java反射

      本文链接:https://www.haomeiwen.com/subject/hznjmftx.html