Java反射学习

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

反射的概念

Reflection(反射是Java被视为动态语言的关键
反射机制允许程序在执行期借助于Reflection API获得任何类的内部信息
并能直接操作任意对象的内部属性及方法。
加载完类之后在堆内存的方法区中就产生了一个Class类型的对象一个类只有一个Class对象。
这个对象就包含了整个的类的结构信息我们可以通过这个对象看到类的结构。
这个对象就像一面镜子透过这个镜子看到类的结构所以我们形象的称之为反射

正常方式引入需要的“包类名称”–>通过new实例化–>取得实例化对象

反射方式实例化对象–>getClass方法–》得到完整的“包类名称”

反射的功能

在运行时判断任意一个对象所属的类

在运行时构造任意一个类的对象

在运行时构造任意一个类所具有的成员变量和方法

在运行时获取泛型信息

在运行时处理注解

生成动态代理

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

缺点对性能有影响。使用反射基本上是一种解释操作我们可以告诉JVM我们希望做什么并且它满足我们的要求。这类操做总是慢于直接执行的相同操作

反射的使用

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

Class类详解

对象照镜子后可以得到的信息某个类的属性、方法和构造器某个类到底实现了哪些接口。对于每个类而言JRE都为其保留一个不变的Class类型的对象。一个Class对象包含了特定的某个结构class/interface/enum/annotation/primitive type/void[]的有关信息

·Class本身也是一个类

·Class对象只能由系统建立对象

·一个加载的类在JVM中只会有一个Class实例

·一个Class对象对应的是一个加载到JVM中的一个.class文件

·每个类的实例都会记得自己是由那个Class实例所生成

·通过Class可以完整地得到一个类中所有被加载 的结构

·Class类是Reflection的根源针对任何你想动态加载、运行的类唯有先获得相应的Class对象

Class类的创建方式

public class Reflect2 {
    public static void main(String[] args) throws ClassNotFoundException {
        Person person=new Student();
        System.out.println("这个人是"+ person.name);
        
        //方式一通过对象获得
        Class c1=person.getClass();
        System.out.println(c1.hashCode());
        
        //方式二forname 获得
        Class c2=Class.forName("Reflect.Reflect2");
        System.out.println(c2.hashCode());
        
        //方式三通过类名.class获得
        Class c3=Student.class;
        System.out.println(c3.hashCode());
        
        //方式四 基本内置类型的包装类都是有一个Type 属性
        Class c4=Integer.TYPE;
        System.out.println(c4);
        
        //获得父类类型
        Class c5=c1.getSuperclass();
        System.out.println(c5);
    }
}
 class Person{
   public String  name;
 
     public Person() {
     }
     public void setName(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 = "老师";
         }
     }
 }

在这里插入图片描述

三种创建方式的比较

一般使用Class.forName 方法去动态加载类 且使用forName就不需要导入其他类可以加载我们任意的类。

而使用 .class属性则需要导入类的包依赖性强且容易抛出编译错误。

而使用实例化对象的 getClass() 方法需要本身创建一个对象是静态的就体现不出反射机制意义了。

所以我们在获取class对象中 一般都会使用 Class.forName去获取

哪些类型可以有Class对象

class外部类成员成员内部类静态内部类局部内部类匿名内部类

interface接口

[ ]数组

enum枚举

annotation注解@interfac

primitive type基本数据类型

void

public class Reflect3 {
    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;
        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.
        int []a=new int[10];
        int []b=new int[100];
        System.out.println(a.getClass().hashCode());
        System.out.println(b.getClass().hashCode());
    }
}

在这里插入图片描述

利用反射获取成员变量Field

//返回 字段对象该对象反映此 类对象表示的类或接口的指定声明字段。
getDeclaredField(String name)

//返回 字段对象的数组 字段对象反映由此 类对象表示的类或接口声明的所有字段。
getDeclaredFields()
 
 
//返回 字段对象该对象反映此 类对象表示的类或接口的指定公共成员字段。
getField(String name)

//返回一个包含 字段对象的数组 字段对象反映此 类对象所表示的类或接口的所有可访问公共字段。
getFields()

例如

Class<?> cls = Class.forName("com.person");
System.out.println(cls.getName());

//只能获取public 的属性
Field[] field = cls.getFields();
for (Field field1 : field) {
    System.out.println(" 属性 "+field1.getName()); 
}

System.out.println("========");

//可以获取类全部属性
Field[] declaredFields = cls.getDeclaredFields();    
for (Field all : declaredFields) {
    System.out.println("属性" + all.getName());
}

利用反射获取方法Method

//返回 方法对象该对象反映此 类对象表示的类或接口的指定声明方法。
getDeclaredMethod(String name,<?>... parameterTypes);
//返回一个包含 方法对象的数组 方法对象反映此 类对象表示的类或接口的所有已声明方法包括publicprotecteddefaultpackage访问和私有方法但不包括继承的方法。
getDeclaredMethods();   //数组
//返回 方法对象该对象反映此 类对象表示的类或接口的指定公共成员方法。
getMethod(String name,<?>... parameterTypes);
//返回一个包含 方法对象的数组 方法对象反映此 类对象所表示的类或接口的所有公共方法包括由类或接口声明的那些以及从超类和超接口继承的那些。
getMethods();

用法 跟Field 类似。

但是要注意后面的 String name, 类<?>… parameterTypes
例如

  Class clazz = Class.forName("java.lang.Runtime");
  clazz.getMethod("exec", String.class);

Runtime 类中的 exec 实现了方法的重载 因此需要使用forMethod就要带上第二个参数才能准确的找到指定方法的对象。

利用反射获取构造函数

//返回一个 构造器对象该对象反映此 类对象所表示的类或接口的指定构造函数。
getDeclaredConstructor(<?>... parameterTypes)
//返回 构造器对象的数组 构造器对象反映由此 类对象表示的类声明的所有构造函数。
getDeclaredConstructors()
//返回一个 构造器对象该对象反映此 类对象所表示的类的指定公共构造函数。
getConstructor(<?>... parameterTypes)
//返回一个包含 构造器对象的数组 构造器对象反映了此 类对象所表示的类的所有公共构造函数。
getConstructors()

用法几乎差不多可以使用 getConstructors 再进行遍历取出

利用反射实例化对象

得到地址和类名后使用newInstance 实例化这个类

Class<?> cls = Class.forName("com.person");
Object o = cls.newInstance();
System.out.println(o);

也可以通过构造器实例化对象

 Constructor<?> cons = 
 cls.getDeclaredConstructor(String.class, double.class);
 
  Object snowy = cons.newInstance("snowy", 1000000000);
  System.out.println(snowy);

利用反射中调用对象中的方法

反射中的调用是用方法来调用对象如

方法.invoke(对象)

例子 比如要调用 person 类中的 hi() 方法首先要得到 hi() 方法的 Method对象其次 还需要得到 person类的对象 作为invoke的参数

Class<?> cls = Class.forName("com.person");//加载类
Constructor<?> cons = 
cls.getDeclaredConstructor(String.class, double.class); //获取构造器

Object snowy = cons.newInstance("snowy", 123);//实例化对象
Method m2 = cls.getMethod("m2");//得到Method 对象
Object name = m2.invoke(snowy); // 调用方法

如果调用的是普通方法第一个参数就是类对象

如果调用的这个方法是静态方法第一个参数是类或者可以护忽略设置为null

特殊权限方法的反射访问方式

如果得到的Field 、Method、Constructor 是私有属性的话受到权限影响不能直接调用或者读取需要设置 setAccessible(true)

setAccessible方法是AccessibleObejct类的一个方法它是Field、Method和Constructor类的公共超类。这个特性是为调式、持久存储和类似机制提供的。

例如Runtime类的构造函数是私有的可以这样获取对象

Class<?> cls = Class.forName("java.lang.Runtime");
Constructor<?> m = cls.getDeclaredConstructor();
m.setAccessible(true);
cls.getMethod("exec", String.class).invoke(m.newInstance(),"calc.exe");
阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: Java