JAVA深化篇_40—— 反射机制介绍

反射机制介绍

什么是反射

Java 反射机制是Java语言一个很重要的特性,它使得Java具有了“动态性”。在Java程序运行时,对于任意的一个类,我们能不能知道这个类有哪些属性和方法呢?对于任意的一个对象,我们又能不能调用它任意的方法?答案是肯定的!这种动态获取类的信息以及动态调用对象方法的功能就来自于Java 语言的反射(Reflection)机制。

反射的作用

简单来说两个作用,RTTI(运行时类型识别)和DC(动态创建)。

我们知道反射机制允许程序在运行时取得任何一个已知名称的class的内部信息,包括其modifiers(修饰符),fields(属性),methods(方法)等,并可于运行时改变fields内容或调用methods。那么我们便可以更灵活的编写代码,代码可以在运行时装配,无需在组件之间进行源代码链接,降低代码的耦合度;还有动态代理的实现等等;但是需要注意的是反射使用不当会造成很高的资源消耗!


创建对象过程

Java创建对象的三个阶段

创建对象时内存结构

Users user = new Users();

实际上,我们在加载任何一个类时都会在方法区中建立“这个类对应的Class对象”,由于==“Class对象”包含了这个类的整个结构信息==,所以我们可以通过这个“Class对象”来操作这个类。

我们要使用一个类,首先要加载类;加载完类之后,在堆内存中,就产生了一个 Class 类型的对象(一个类只有一个 Class 对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象知道类的结构。这个对象就像一面镜子,透过这个镜子可以看到类的结构,所以,我们形象的称之为:反射。 因此,“Class对象”是反射机制的核心。

反射的具体实现

获取Class对象的三种方式

  • 通过getClass()方法;
  • 通过.class 静态属性;
  • 通过Class类中的静态方法forName();

创建Users类

public class Users {
  private String username;
  private int userage;


  public String getUsername() {
    return username;
   }


  public void setUsername(String username) {
    this.username = username;
   }


  public int getUserage() {
    return userage;
   }


  public void setUserage(int userage) {
    this.userage = userage;
   }
}



通过getClass()方法获取Class对象
/*
* 通过getClass()方法获取该类的Class对象
getClass()为Object类下的非静态方法,在使用时需要先实例化对象
 */
public class GetClass1 {
  public static void main(String[] args) {
    Users users = new Users();
    Users users1 = new Users();
    Class clazz = users.getClass();
    System.out.println(clazz);
    System.out.println(clazz.getName());
    System.out.println(users.getClass() == users1.getClass());
   }
}

通过forName()获取Class对象
/**
 * 通过Class.forName("class Name")获取Class对象
 */
public class GetClass3 {
  public static void main(String[] args)throws Exception {
    Class clazz = Class.forName("com.java.Users");
    Class clazz2 = Class.forName("com.java.Users");
    System.out.println(clazz);
    System.out.println(clazz.getName());
    System.out.println(clazz == clazz2);
   }
}

通过.class 静态属性获取Class对象
/**
 * .class静态属性获取Class对象
 */
public class GetClass2 {
  public static void main(String[] args) {
    Class clazz = Users.class;
    Class clazz2 = Users.class;
    System.out.println(clazz);
    System.out.println(clazz.getName());
    System.out.println(clazz == clazz2);
   }
}


获取类的构造方法

方法介绍

方法名描述
getDeclaredConstructors()返回 Constructor 对象的一个数组,这些对象反映此 Class 对象表示的类声明的所有构造方法
getConstructors()返回一个包含某些 Constructor 对象的数组,这些对象反映此 Class 对象所表示的类的所有公共(public)构造方法
getConstructor(Class<?>… parameterTypes)返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共(public)构造方法。
getDeclaredConstructor(Class<?>… parameterTypes)返回一个 Constructor 对象,该对象反映此 Class 对象所表示的类或接口的指定构造方法。

方法使用

修改Users类

public class Users {
  private String username;
  private int userage;
  public Users(){
   }
    
  public Users(String username,int userage){
    this.username= username;
    this.userage=userage;
   }
    
  public Users(String username){
    this.username= username;
   }
    
  private Users(int userage){
    this.userage = userage;
   }


  public String getUsername() {
    return username;
   }


  public void setUsername(String username) {
    this.username = username;
   }


  public int getUserage() {
    return userage;
   }


  public void setUserage(int userage) {
    this.userage = userage;
   }
}

获取构造方法

public class GetConstructor {
  public static void main(String[] args)throws Exception {
    Class clazz = Users.class;
    Constructor[] arr = clazz.getDeclaredConstructors();
    for(Constructor c:arr){
      System.out.println(c);
     }
    System.out.println("---------------------");
      
    Constructor[] arr1 = clazz.getConstructors();
    for(Constructor c:arr1){
      System.out.println(c);
     }
    System.out.println("------------------------");
      
    Constructor c =  clazz.getDeclaredConstructor(int.class);
    System.out.println(c);
    System.out.println("------------------------");
      
    Constructor c1 =  clazz.getConstructor(null);
    System.out.println(c1);
   }
}

通过构造方法创建对象 newInstance()
/**
**通过反射实例化对象
*/
public class GetConstructor2 {
    
    public static void main(String[] args) throws Exception{
        //创建类对象
        Class<Users> usersClass = Users.class;
        //通过类对象获取指定的构造方法对象
        Constructor<Users> constructor = usersClass.getConstructor(String.class, int.class);
        //通过指定的构造方法对象创建对象
        Users users = constructor.newInstance("zhangsan", 20);
		
        System.out.println(users);
    }
}

获取类的成员变量

方法介绍

方法名描述
getFields()返回Field类型的一个数组,其中包含 Field对象的所有公共(public)字段。
getDeclaredFields()返回Field类型的一个数组,其中包含 Field对象的所有字段。
getField(String fieldName)返回一个公共成员的Field指定对象。
getDeclaredField(String fieldName)返回一个 Field指定对象。

方法使用

修改Users类

public class Users {
  private String username;
  public int userage;
  public Users(){
   }
  public Users(String username,int userage){
    this.username= username;
    this.userage=userage;
   }
  public Users(String username){
    this.username= username;
   }
  private Users(int userage){
    this.userage = userage;
   }


  public String getUsername() {
    return username;
   }


  public void setUsername(String username) {
    this.username = username;
   }


  public int getUserage() {
    return userage;
   }


  public void setUserage(int userage) {
    this.userage = userage;
   }
}



获取成员变量

public class GetField {
    
    public static void main(String[] args) throws Exception{
        Class<Users> usersClass = Users.class;
        Field[] fields = usersClass.getFields();
        for(Field field:fields){
            System.out.println(field);
        }
        System.out.println("===================");
        Field[] declaredFields = usersClass.getDeclaredFields();
        for(Field field:declaredFields){
            System.out.println(field);
        }
        System.out.println("=================");
        Field userName = usersClass.getDeclaredField("userName");
        System.out.println(userName);
        System.out.println("=================");
        Field userAge = usersClass.getField("userAge");
        System.out.println(userAge);
    }
}

操作成员变量 先实例化对象

public class GetField2 {
    public static void main(String[] args)throws Exception {
        //获取Users类的类对象
        Class<Users> usersClass = Users.class;
        //获取类的成员变量
        Field userAge = usersClass.getField("userAge");
        //通过构造方法实例化users对象
        Users users = usersClass.getConstructor(null).newInstance();
        //给指定成员变量赋值
        userAge.set(users,20);
        System.out.println(userAge.get(users));
    }
}


获取类的方法

方法介绍

方法名描述
getMethods()返回一个Method类型的数组,其中包含 所有公共(public)方法。包含父类中的(public)方法!!!!!
getDeclaredMethods()返回一个Method类型的数组,其中包含 所有方法。
getMethod(String name, Class<?>… parameterTypes)返回一个公共的Method方法对象。
getDeclaredMethod(String name, Class<?>… parameterTypes)返回一个方法Method对象

方法使用

修改Users类

public class Users {
    private String userName;
    public int userAge;

    private Users(String userName){
        this.userName = userName;
    }
    public Users(String userName, int userAge){
        this.userName = userName;
        this.userAge = userAge;
    }

    public Users(){

    }

    public Users(int userAge){
        this.userAge = userAge;
    }


    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public int getUserAge() {
        return userAge;
    }

    public void setUserAge(int userAge) {
        this.userAge = userAge;
    }


    private void sing(){
        System.out.println("zhangsan爱唱歌");
    }


    @Override
    public String toString() {
        return "Users{" +
                "userName='" + userName + '\'' +
                ", userAge=" + userAge +
                '}';
    }
}

获取方法

public class GetMethod {
    
    public static void main(String[] args) throws Exception{
        Class<Users> usersClass = Users.class;
        Method[] classMethods = usersClass.getMethods();
        for(Method method : classMethods){
            System.out.println(method);
            System.out.println(method.getName());
        }
        System.out.println("----------------");
        Method[] declaredMethods = usersClass.getDeclaredMethods();
        for(Method method : declaredMethods){
            System.out.println(method);
            System.out.println(method.getName());
        }
        System.out.println("=================");
        Method setUserAge = usersClass.getMethod("setUserAge", int.class);
        System.out.println(setUserAge.getName());
        System.out.println(setUserAge);
        System.out.println("===============");
        Method sing = usersClass.getDeclaredMethod("sing");
        System.out.println(sing);
        System.out.println(sing.getName());
    }
}

调用方法 invoke( )
public class GetMethod2 {
    
    public static void main(String[] args)throws Exception {
        //实例化类对象
        Class<Users> usersClass = Users.class;
        Method setUserName = usersClass.getMethod("setUserName", String.class);
        //实例化对象
        Users users = usersClass.getConstructor(null).newInstance();
        //通过setUserName赋值
        setUserName.invoke(users,"zhangsan");
        //通过getUserName获取值
        Method getUserName = usersClass.getMethod("getUserName");
        Object userName = getUserName.invoke(users);
        System.out.println(userName);

    }
}


获取类的其他信息

public class GetClassInfo {

    public static void main(String[] args) {
        Class<Users> usersClass = Users.class;

        //获取包名
        Package usersClassPackage = usersClass.getPackage();
        System.out.println(usersClassPackage);
        System.out.println(usersClassPackage.getName());

        //获取类名
        String usersClassName = usersClass.getName();
        System.out.println(usersClassName);

        //获取超类
        Class<? super Users> superclass = usersClass.getSuperclass();
        System.out.println(superclass.getName());

        //获取所有接口
        Class<?>[] classInterfaces = usersClass.getInterfaces();
        for(Class interfaces:classInterfaces){
            System.out.println(interfaces.getName());
        }
    }
}



反射机制的效率

由于Java反射是要解析字节码,将内存中的对象进行解析,包括了一些动态类型,而JVM无法对这些代码进行优化。因此,反射操作的效率要比那些非反射操作低得多!

接下来我们做个简单的测试来直接感受一下反射的效率。

反射机制的效率测试

public class Test {
    public static void main(String[] args) throws Exception{

        Class<?> aClass = Class.forName("com.itbaizhan.Users");
        Object o = aClass.getConstructor(null).newInstance();
        Method setUsername = aClass.getMethod("setUserName",String.class);
        long reflectStart = System.currentTimeMillis();
        for(int i =0;i<100000000;i++){
            setUsername.invoke(o,"zhangsan");
        }
        long reflectEnd = System.currentTimeMillis();

        long start = System.currentTimeMillis();
        Users u =new Users();
        for(int i=0;i<100000000;i++){
            u.setUserName("zhangsan");
        }
        long end = System.currentTimeMillis();

        System.out.println("反射执行时间:"+(reflectEnd-reflectStart));
        System.out.println("普通方式执行时间:"+(end-start));
    }
}
反射执行时间:169
普通方式执行时间:0


setAccessible方法

setAccessible()方法:

setAccessible是启用和禁用访问安全检查的开关。值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。值为 false 则指示反射的对象应该实施 Java 语言访问检查;默认值为false。

由于JDK的安全检查耗时较多.所以通过setAccessible(true)的方式关闭安全检查就可以达到提升反射速度的目的。

public class Test2 {
    public static void main(String[] args) throws Exception{
        Class<Users> usersClass = Users.class;
        Users users = usersClass.getConstructor(null).newInstance();
        Field userName = usersClass.getDeclaredField("userName");
        //忽略安全检查
        userName.setAccessible(true);
        userName.set(users,"zhangsan");
        Object o = userName.get(users);
        System.out.println(o);

        Method sing = usersClass.getDeclaredMethod("sing");
        //忽略安全检查
        sing.setAccessible(true);
        sing.invoke(users);

    }
}

反射总结

  • Java 反射机制是Java语言一个很重要的特性,它使得Java具有了==“动态性”。==
  • 反射机制的优点:
    • 更灵活。
    • 更开放。
  • 反射机制的缺点:
    • 降低程序执行的效率
    • 增加代码维护的困难。
  • 获取Class类的对象的三种方式:
    • 运用getClass()。(非静态方法,需要先实例化)
    • 运用.class 语法
    • 运用Class.forName()(最常被使用)
  • 反射机制的常见操作
    • 动态加载类、动态获取类的信息(属性、方法、构造器)。
    • 动态构造对象。
    • 动态调用类和对象的任意方法。
    • 动态调用和处理属性。
    • 获取泛型信息。
    • 处理注解。
posted @ 2023-11-13 07:33  Gjq-  阅读(10)  评论(0编辑  收藏  举报  来源