导航

Java反射

Posted on 2020-07-04 14:51  乔伊_413  阅读(156)  评论(0编辑  收藏  举报

11.反射

 11.1静态语言&动态语言

动态语言:

  一类运行时可以改变其结构的语言:Object-C、C#、JavaScript、Python

静态语言:

  运行时结构不变的语言;Java(准动态性)、C、C++

  利用Java中的反射机制获得类似动态语言的特性。

11.2 Java Reflection

  Reflection(反射),是Java被视为动态语言的关键,反射机制允许程序在执行期间借助于Reflection API 取得任何类的内部信息,并能直接操作任意对象的内部属性及方法

  加载完类后,在堆内存的方法区中产生了一个Class类型的对象(包含了完整的类结构信息),这个对象类似于一个镜子,通过该“镜子”看到的类结构称为反射

 

 

反射的优缺点:

  优点:实现动态创建对象和编译,体现出很大的灵活性

  缺点:对性能有影响。使用反射基本上是一种解释操作

 package com.reflection;
 ​
 //什么叫反射
 public class Demo01 {
     public static void main(String[] args) throws ClassNotFoundException {
         //通过反射获取类的class对象
         Class c1 = Class.forName("com.reflection.User");
         System.out.println(c1);
         Class c2 = Class.forName("com.reflection.User");
         Class c3 = Class.forName("com.reflection.User");
         Class c4 = Class.forName("com.reflection.User");
         //一个类在内存中只有一个class对象
         //一个类被加载后,类的整个结构都会被封装在class对象中
         System.out.println(c2.hashCode());
         System.out.println(c3.hashCode());
         System.out.println(c4.hashCode());
    }
 }
 ​
 //实体类pojo
 class User{
     private String name;
     private int age;
     private int id;
 ​
     public User() {
    }
 ​
     public String getName() {
         return name;
    }
 ​
     public void setName(String name) {
         this.name = name;
    }
 ​
     public int getAge() {
         return age;
    }
 ​
     public void setAge(int age) {
         this.age = age;
    }
 ​
     public int getId() {
         return id;
    }
 ​
     public void setId(int id) {
         this.id = id;
    }
 ​
     public User(String name, int age, int id) {
         this.name = name;
         this.age = age;
         this.id = id;
    }
 ​
     @Override
     public String toString() {
         return "User{" +
                 "name='" + name + '\'' +
                 ", age=" + age +
                 ", id=" + id +
                 '}';
    }
 }

Class类:  

  在Object类中定义了以下的方法,此方法被所有的子类继承

  public final Class getClass()

以上方法返回值类型是一个Class类,该类是Java反射的源头

 

 

 

 

如何获取Class类的实例

  1、Class class = Person.class; //若已知具体的类,通过类的class属性获取

  2、Class class = person.getClass(); //若已知类的实例,调用该实例的getClass方法获取

  3、Class class = Class.forName("demo01.Student"); //若已知一个类的全类名且该类在类路径下,可通过Class类的静态方法forName获取

  4、内置基本数据类型可以直接使用类名.Type

  5、还可以利用ClassLoader

 package com.reflection;
 ​
 import com.sun.xml.internal.ws.api.model.wsdl.WSDLOutput;
 ​
 //测试Class类的创建方式有哪些
 public class Demo02 {
     public static void main(String[] args) throws ClassNotFoundException {
         Person p1 = new Student();
         System.out.println("这个人是"+p1.name);
 ​
         //方式一:通过对象获得
         Class c1 = p1.getClass();
         System.out.println(c1.hashCode());
 ​
         //方式二:通过class属性获得
         Class c2 = Student.class;
         System.out.println(c2.hashCode());
 ​
         //方式三:通过Class类的静态方法forName获取
         Class c3 = Class.forName("com.reflection.Student");
         System.out.println(c3.hashCode());
 ​
         //方式四:基本内置的包装类Type属性
         Class c4 = Integer.TYPE;
         System.out.println(c4);
 ​
         //方式五:获得父类类型
         Class c5 = c1.getSuperclass();
         System.out.println(c5);
 ​
    }
 }
 ​
 //实体类Person
 class Person{
     public String name;
 ​
     public Person() {
    }
 ​
     public Person(String name) {
         this.name = name;
    }
 ​
 ​
     @Override
     public String toString() {
         return "Person{" +
                 "name='" + name + '\'' +
                 '}';
    }
 }
 ​
 class Student extends Person{
     public Student(){
         this.name = "学生";
    }
 }
 ​
 class Teacher extends Person{
     public Teacher(){
         this.name = "老师";
    }
 }
 /*
 * 这个人是学生
 1163157884
 1163157884
 1163157884
 int
 class com.reflection.Person
 * */
 package com.reflection;
 ​
 import java.lang.annotation.ElementType;
 ​
 public class Demo03 {
     public static void main(String[] args) {
         Class c1 = Object.class;    //类
         Class c2 = Comparable.class;    //接口
         Class c3 = String[].class;  //数组
         Class c4 = int[][].class;   //二维数组
         Class c5 = Override.class;  //注解
         Class c6 = ElementType.class;   //枚举
         Class c7 = Integer.class;   //基本数据类型
         Class c8 = void.class;  //空
         Class c9 = Class.class; //Class
 ​
         System.out.println(c1);
         System.out.println(c2);
         System.out.println(c3);
         System.out.println(c4);
         System.out.println(c5);
         System.out.println(c6);
         System.out.println(c7);
         System.out.println(c8);
         System.out.println(c9);
    }
 }
 ​
 /*
 class java.lang.Object
 interface java.lang.Comparable
 class [Ljava.lang.String;
 class [[I
 interface java.lang.Override
 class java.lang.annotation.ElementType
 class java.lang.Integer
 void
 class java.lang.Class
 * */

11.3类加载内存分析

 

 

类加载过程:

 

 

加载

  将class字节码文件加载到内存中

链接

  将Java类中的二进制代码合并到JVM的运行状态中的过程

  验证--准备--解析

初始化:  

  执行类构造器方法的过程

  类初始化的条件:

 package com.reflection;
 //什么时候类初始化
 public class Demo04 {
     static {
         System.out.println("主类被加载");
    }
 ​
     public static void main(String[] args) throws ClassNotFoundException {
         //1、主动引用
         //Son son = new Son();
 ​
         //反射产生主动引用
         //Class.forName("com.reflection.Son");
 ​
         //不会产生类初始化的方法
         //System.out.println(Son.b); //通过子类引用父类的静态变量
 ​
         //Son[] array = new Son[4]; //通过数组定义,不会触发类初始化
 ​
         System.out.println(Son.M); //引用常量不会触发类初始化
 ​
    }
 }
 ​
 class Father{
     static int b = 1;
     static {
         System.out.println("父类被加载");
    }
 }
 ​
 class Son extends Father{
     static {
         System.out.println("子类被加载");
         m = 300;
    }
     static int m = 100;
     static final int M = 1;
 }

  类加载器作用:

  将class文件字节码内容加载到内存中;并将这些静态数据转换为方法区的运行时数据结构,然后在堆中生成代表这个类的java.lang.Class对象;作为方法区中类类数据的访问入口

  类缓存:

  标准的JavaSE类加载器可以按要求查找类;JVM垃圾回收机制可以回收这些Class对象

 package com.reflection;
 ​
 public class Demo05 {
     public static void main(String[] args) throws ClassNotFoundException {
         //获取系统类的加载器
         ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
         System.out.println(systemClassLoader);
 ​
         //获取系统类加载器的父类加载器->扩展类加载器
         ClassLoader parent = systemClassLoader.getParent();
         System.out.println(parent);
 ​
         //获取扩展类加载器的父类-->根加载器
         ClassLoader parent1 = parent.getParent();
         System.out.println(parent1);
 ​
         //获得当前测试类是哪个类加载器加载的
         ClassLoader classLoader = Class.forName("com.reflection.Demo05").getClassLoader();
         System.out.println(classLoader);
    }
 }

11.4获取类的运行时结构

  创建运行时类的对象:

  通过反射获取运行时类的完整结构

  关键词:Field、Method、Constructor、SuperClass、Interface、Annotation

 package com.reflection;
 ​
 import java.lang.reflect.Field;
 ​
 //获得类的信息
 public class Demo06 {
     public static void main(String[] args) throws ClassNotFoundException {
         Class c1 = Class.forName("com.reflection.User");
 ​
         //获得类的名字
         System.out.println(c1.getName());  //获得包名+类名
         System.out.println(c1.getSimpleName());  //只获得类名
 ​
         System.out.println("==================");
         //获得类的属性
         Field[] fields = c1.getFields();
         fields = c1.getDeclaredFields();
         for (Field field:
                 fields) {
             System.out.println(field);
        }
    }
 }

有了Class对象能做什么:

创建类对象,调用Class对象的newInstance()方法

  1、类必须有一个无参构造器

  2、类的构造器的访问权限需要足够

在没有无参构造器中,只要在操作的时候调用类中的构造器,并将参数传递下去,才可以实例化操作

  1、通过Class类的getDeclaredConstructor(Class ... ParameterTypes)取得本类的指定形参类型构造器

  2、向构造器中的形参传递一个对象数组进去,里面包含了构造器中所需要的各个参数

  3、通过Constructor实例化对象

 setAccessible
  -Method、Field、Constructor对象都有setAccessible()方法
  -setAccessible作用是启动和禁用访问检查的开关
  -参数值为true则知识反射的对象在使用时应该取消Java语言访问检查
  提高反射效率;可以访问私有属性
  -参数值为false则指示反射的对象应该实施Java语言访问检查
 package com.reflection;
 ​
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 ​
 //通过反射动态的创建对象
 public class Demo07 {
     public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
         //获得Class对象
         Class c1 = Class.forName("com.reflection.User");
 ​
         //构造一个对象
 //       User user = (User)c1.newInstance(); //本质是调用了类的无参构造器
 //       System.out.println(user);
         /*
         * User{name='null', age=0, id=0}
         * */
 ​
         //通过构造器创造对象
 //       Constructor constructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
 //       User user1 = (User) constructor.newInstance("Joey", 001, 21);
 //       System.out.println(user1);
 ​
         //通过反射调用普通方法
         User user2 = (User)c1.newInstance();
         //通过反射获取方法
         Method setName = c1.getDeclaredMethod("setName", String.class);
         setName.invoke(user2,"Joey");
         /*
         * invoke:激活
         * 用法:(对象,“方法的值”)
         * */
         //System.out.println(user2.getName());
 ​
         //通过反射操作属性
         User user3 = (User) c1.newInstance();
         Field name = c1.getDeclaredField("name");
 ​
         /*
         * 不能直接操作私有(private)属性
         * 需要用name.setAccessible(true)关闭程序的安全检测
         * */
         name.setAccessible(true);
         name.set(user3,"Rose");
         System.out.println(user3.getName());
    }
 }

11.5获取注解信息

 package com.reflection;
 ​
 import java.lang.annotation.*;
 import java.lang.reflect.Field;
 ​
 //联系反射操作注解
 public class Demo08 {
     public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
         Class c1 = Class.forName("com.reflection.Student1");
 ​
         //通过反射获取注解
 //       Annotation[] annotations = c1.getAnnotations();
 //       for (Annotation annotation:
 //             annotations) {
 //           System.out.println(annotation);
 //       }
 ​
         //获取注解value的值
 //       TableJoey tableJoey = (TableJoey)c1.getAnnotation(TableJoey.class);
 //       String value = tableJoey.value();
 //       System.out.println(value);
 ​
         //获取类指定注解
         Field name = c1.getDeclaredField("name");
         FieldJoey annotation = name.getAnnotation(FieldJoey.class);
         System.out.println(annotation.columnName());
         System.out.println(annotation.type());
         System.out.println(annotation.length());
    }
 }
 ​
 @TableJoey("db_student")
 class Student1{
     @FieldJoey(columnName = "db_id",type = "int",length = 10)
     private int id;
     @FieldJoey(columnName = "db_name",type = "String",length = 20)
     private String name;
 ​
     public Student1(int id, String name) {
         this.id = id;
         this.name = name;
    }
 ​
     public int getId() {
         return id;
    }
 ​
     public void setId(int id) {
         this.id = id;
    }
 ​
     public String getName() {
         return name;
    }
 ​
     public void setName(String name) {
         this.name = name;
    }
 ​
     @Override
     public String toString() {
         return "Student1{" +
                 "id=" + id +
                 ", name='" + name + '\'' +
                 '}';
    }
 }
 ​
 ​
 //类名的注解
 @Target(ElementType.TYPE)
 @Retention(RetentionPolicy.RUNTIME)
 @interface TableJoey{
     String value();
 }
 ​
 //属性的注解
 @Target(ElementType.FIELD)
 @Retention(RetentionPolicy.RUNTIME)
 @interface FieldJoey{
     String columnName();
     String type();
     int length();
 }