Java基础之反射

2019-08-09 00:00:00 java 反射 基础

个人理解:

  反射就是将文件的所有内容都清晰的进行的查看,尤其是可以通过暴力反射将私有的内容进行呈现出来!同时可以通过反射进行对象的创建。要知晓类的加载形成.class文件是最先执行的(在类进内存的时候),然后再开始执行静态的内容。通过反射获取成员变量、成员方法或者构造方法都有四种方法!可以利用泛型擦除和反射在集合中添加任意类型的元素,也可以利用反射配置文件,运行指定类的指定方法!

一、类加载器:

1、类的加载:

  当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。

①、加载:

就是指将class文件读入内存,并为之创建一个Class对象。任何类被使用时系统都会建立一个Class对象

②、连接:

验证 :是否有正确的内部结构,并和其他类协调一致

准备 :负责为类的静态成员分配内存,并设置默认初始化值

解析 :将类的二进制数据中的符号引用替换为直接引用

③、初始化:

2、类初始化时机:(进内存)

①. 创建类的实例

②. 类的静态变量,或者为静态变量赋值

③. 类的静态方法

④. 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象

⑤. 初始化某个类的子类

⑥. 直接使用java.exe命令来运行某个主类

3、类加载器:

负责将.class文件加载到内存中,并为之生成对应的Class对象。

4、类加载器的组成:

①、Bootstrap ClassLoader 根类加载器:也被称为引导类加载器,负责Java核心类的加载

比如System,String等。在JDK中JRE的lib目录下rt.jar文件中

②、Extension ClassLoader 扩展类加载器:负责JRE的扩展目录中jar包的加载。

在JDK中JRE的lib目录下ext目录

③、 System ClassLoader 系统类加载器

负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径。

 二、反射:

1、反射:

  JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

2、Class类:

  Class没有公共构造方法。Class对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的。

 3、获取Class对象的三种方式:

①、通过object类中的getobject()方法;

②、通过 类名.class获取到字节码文件对象(任意数据类型都具备一个class静态属性);

③、通过Class类中的方法(将类名作为字符串传递给Class类中的静态方法forName即可)。

《Java基础之反射》

package com.oracle.demo01;

public class Person {
    private String name;
    public int age;

    public Person() {
        System.out.println("public Person()");
    }

    public Person(String name) {
        this.name = name;
        System.out.println("public Person(String name)");
    }

    private Person(int age) {
        this.age = age;
        System.out.println("private Person(int age)");
    }
    static
    {
        System.out.println("静态代码块");
    }
    public void eat(){
        
    }
    public void sleep(String name,int age){
        System.out.println("public void sleep(String name,int age)");
    }
    public void sleep(String name){
        System.out.println("public void sleep(String name)");
    }
    private void smoke(int age){
        System.out.println("private void smoke(int age)");
    }

    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }
}

 

public class Demo02 {
    public static void main(String[] args) throws ClassNotFoundException {
        //获取字节码文件对象
        //1.通过对象获取
        Class c1=new Person().getClass();
        System.out.println(c1);
        //2.通过类名获取
        Class c2 = Person.class;
        System.out.println(c2);
        System.out.println(c1==c2);//true
        //3.通过class类的静态方法获取
        Class c3 = Class.forName("com.oracle.demo01.Person");
        System.out.println(c3);
    }
}

 

三、通过反射获取构造方法并使用:

构造方法使用类Constructor表示。可通过Class类中提供的方法获取构造方法:

1、返回一个构造方法

 public Constructor<T> getConstructor(Class<?>… parameterTypes) 获取public修饰, 指定参数类型所对应的构造方法

   public Constructor<T> getDeclaredConstructor(Class<?>… parameterTypes) 获取指定参数类型所对应的构造方法(包含私有的)

2、 返回多个构造方法

 public Constructor<?>[] getConstructors() 获取所有的public 修饰的构造方法

 public Constructor<?>[] getDeclaredConstructors() 获取所有的构造方法(包含私有的)

 

public class Demo01 {
    public static void main(String[] args) throws NoSuchMethodException, SecurityException, ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        method04();
    }

    public static void method01() throws ClassNotFoundException {
        // 获取字节码文件对象
        Class c = Class.forName("com.oracle.demo01.Person");
        // 获取所有公共的构造方法对象数组
        // Constructor []cons=c.getConstructors();
        // 获取所有的构造方法
        Constructor[] cons = c.getDeclaredConstructors();
        for (Constructor con : cons) {
            System.out.println(con);
        }
    }
    public static void method02() throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{
        //获取字节码文件对象
        Class c=Class.forName("com.oracle.demo01.Person");
        //获取空参构造方法对象
        Constructor con=c.getConstructor();
        //调用和构造方法创建对象
        Object obj=con.newInstance();
        //向下转型
        Person p=(Person) obj;
        p.eat();
    }
    public static void method03() throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{
        //获取字节码文件对象
        Class c=Class.forName("com.oracle.demo01.Person");
        //获取有参构造方法对象(String 的字节码文件对象)
        Constructor con=c.getConstructor(String.class);
        //调用和构造方法创建对象
        Object obj=con.newInstance("张三");
        //向下转型
        Person p=(Person) obj;
        p.eat();
    }

四、通过反射的方式,获取构造方法,创建对象:

通过构造方法类Constructor中的方法,创建对象

public T newInstance(Object… initargs)

public class Demo03 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException {
        //获取字节码文件对象
        Class c=Class.forName("com.oracle.demo01.Person");
        //获取公共的有参成员方法
        Method method=c.getMethod("sleep", String.class,int.class);
//        System.out.println(method);
        //快速创建对象
        Object obj =c.newInstance();
        //调用方法
        method.invoke(obj, "张三",18);
    }
    
}

五、通过反射方式,获取私有构造方法,创建对象

l  public void setAccessible(boolean flag) throws SecurityException

参数值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。参数值为 false 则指示反射的对象应该实施 Java 语言访问检查。

    //暴力反射:抢银行!
    //违反了面向对象的封装思想,破坏了程序的安全性(不推荐使用)
    public static void method04() throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{
        //获取字节码文件对象
        Class c=Class.forName("com.oracle.demo01.Person");
        //获取私有构造方法对象
        Constructor con=c.getDeclaredConstructor(int.class);
        //暴力反射:取消JAVA语言检测访问---可以访问私有的!
        con.setAccessible(true);
        //调用和构造方法创建对象
        Object obj=con.newInstance(18);
        //向下转型
        Person p=(Person) obj;
        p.eat();
    }
}

 六、通过反射获取成员变量并使用:

在反射机制中,把类中的成员变量使用类Field(成员变量类)表示。可通过Class类中提供的方法获取成员变量:

1、 返回一个成员变量

 public Field getField(String name) 获取指定的 public修饰的变量

 public Field getDeclaredField(String name) 获取指定的任意变量

2、  返回多个成员变量

public Field[] getFields() 获取所有public 修饰的变量

 public Field[] getDeclaredFields() 获取所有的 变量 (包含私有)

 

package com.oracle.demo02;

import java.lang.reflect.Field;

public class Demo02 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException, InstantiationException, IllegalAccessException {
        method02();
    }

    public static void method01() throws ClassNotFoundException, NoSuchFieldException, SecurityException, InstantiationException, IllegalAccessException {
        // 获取字节码文件对象
        Class c = Class.forName("com.oracle.demo01.Person");
        // 获取公共的成员变量
        Field f = c.getField("age");
//        System.out.println(f);
        //快速创建对象----只能是空参的!
        Object obj=c.newInstance();
        //给对象的成员变量赋值
        f.set(obj, 18);
        
        System.out.println(obj);
    }
    public static void method02() throws ClassNotFoundException, NoSuchFieldException, SecurityException, InstantiationException, IllegalAccessException {
        // 获取字节码文件对象
        Class c = Class.forName("com.oracle.demo01.Person");
        // 获取私有的成员变量
        Field f = c.getDeclaredField("name");
//        System.out.println(f);
        //暴力反射
        f.setAccessible(true);
        //快速创建对象----只能是空参的!
        Object obj=c.newInstance();
        //给对象的成员变量赋值
        f.set(obj, "张三");
        
        System.out.println(obj);
    }
}

 七、通过反射获取成员方法并使用:

在反射机制中,把类中的成员方法使用类Method表示。可通过Class类中提供的方法获取成员方法:

1、返回获取一个方法:

 public Method getMethod(String name, Class<?>… parameterTypes)

                        获取public 修饰的方法

  public Method getDeclaredMethod(String name, Class<?>… parameterTypes)

                        获取任意的方法,包含私有的

  参数1: name 要查找的方法名称; 参数2: parameterTypes 该方法的参数类型

2、  返回获取多个方法:

  public Method[] getMethods() 获取本类与父类中所有public 修饰的方法

public Method[] getDeclaredMethods() 获取本类中所有的方法(包含私有的)

 public Object invoke(Object obj,  Object… args)

执行指定对象obj中,当前Method对象所代表的方法,方法要传入的参数通过args指定。

八、泛型擦除:(注释和泛型不进class文件)

  程序编译后产生的.class文件中是没有泛型约束的,这种现象我们称为泛型的擦除。那么,我们可以通过反射技术,来完成向有泛型约束的集合中,添加任意类型的元素。

public class Demo01 {    
    public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        //泛型擦除
        ArrayList<String> arr=new ArrayList<String>();
        //反射(泛型不进class文件)
        Class c= arr.getClass();
        Method method=c.getMethod("add", Object.class);
        //调用
        method.invoke(arr, 123);
        System.out.println(arr);
    }
}

九、反射配置文件:

通过反射配置文件,运行配置文件中指定类的对应方法

读取Properties.txt文件中的数据,通过反射技术,来完成Person对象的创建

classname=com.oracle.demo04.Student
methodname=study
public class Demo {
    public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
        //准备Properties集合存储键值对
        Properties pro=new Properties();
        //明确数据源
        FileReader fr=new FileReader("src/com/oracle/demo04/pro.properties");
        //将properties文件中的键值对读取到properties集合中
        pro.load(fr);
        //从properties集合中读取完整的包名+类名
        String className=pro.getProperty("classname");
        //从集合中读取方法名
        String methodName=pro.getProperty("methodname");
        //获取字节码文件对象
        Class c =Class.forName(className);
        //快速创建对象
        Object obj=c.newInstance();
        //获取成员方法对象
        Method method=c.getMethod(methodName);
        //调用方法
        method.invoke(obj);
        //释放资源
        fr.close();
    
    }
}

 

相关文章