java-反射

反射处理的问题:即通过外部文件配置,在不修改源码情况下,来控制程序,也符合设计模式ocp原则(开闭原则)

反射快速入门

re.properties

classfullpath=com.xxb.Cat
method=cry
package com.xxb.reflection.question;

import java.io.FileInputStream;
import java.lang.reflect.Method;
import java.util.Properties;

/**
 * 反射问题
 */
public class ReflectionQuestion {
    public static void main(String[] args) throws Exception {
        //根据配置文件re.properties指定信息,创建Cat对象并调用方法hi
        Properties properties = new Properties();
        properties.load(new FileInputStream("D:\\xxb\\java-base-learning\\chapter23\\src\\re.properties"));
        String classfullpath = properties.get("classfullpath").toString();
        String methodName = properties.get("method").toString();
        //使用反射机制解决
        //1.加载类 返回class类型的对象cls
         Class cls = Class.forName(classfullpath);
         //2.通过cls得到你加载的类com.xxb.Cat的对象实例
        Object o = cls.newInstance(); //运行类型 Cat
        System.out.println(o.getClass());
        //3. 通过cls得到你加载的类 com.xxb.Cat的methodName的方法对象
        //即: 在反射中,可以把方法视为对象(万物皆对象)
        Method method1=cls.getMethod(methodName);
        //4. 通过method1调用方法,即通过方法对象来实现调用方法
        //传统方法 对象.方法(),  反射机制 方法.invoke(对象)
        method1.invoke(o); //hi 招财猫
    }
}

image.png

反射机制

Java Reflection

image.png

备注: class类就是reflectClass类(反射类)

image.png

反射应用场景

image.png

反射相关的类

image.png

package com.xxb.reflection.question;

import java.io.FileInputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Properties;

/**
 * 反射问题
 */
public class ReflectionQuestion {
    public static void main(String[] args) throws Exception {
        //根据配置文件re.properties指定信息,创建Cat对象并调用方法hi
        Properties properties = new Properties();
        properties.load(new FileInputStream("D:\\xxb\\java-base-learning\\chapter23\\src\\re.properties"));
        String classfullpath = properties.get("classfullpath").toString();
        String methodName = properties.get("method").toString();
        //使用反射机制解决
        //1.加载类 返回class类型的对象cls
         Class cls = Class.forName(classfullpath);
         //2.通过cls得到你加载的类com.xxb.Cat的对象实例
        Object o = cls.newInstance(); //运行类型 Cat
        System.out.println(o.getClass());
        //3. 通过cls得到你加载的类 com.xxb.Cat的methodName的方法对象
        //即: 在反射中,可以把方法视为对象(万物皆对象)
        Method method1=cls.getMethod(methodName);
        //4. 通过method1调用方法,即通过方法对象来实现调用方法
        //传统方法 对象.方法(),  反射机制 方法.invoke(对象)
        method1.invoke(o); //hi 招财猫

        //java.lang.reflect.Field 代表类的成员变量,Field对象表示某个类的成员变量
        //得到name
        //getField不能得到私有的属性
//        Field name = cls.getField("name");//NoSuchFieldException
        Field age = cls.getField("age");
        //得到name字段的值
        //传统写法 对象.成员变量  反射: 成员变量对象.get(对象)
        System.out.println(age.get(o));//12

        //java.lang.reflect.Constructor 代表类的构造方法 Constructor对象表示构造器
        Constructor constructor1 = cls.getConstructor();//返回无参构造器
        Constructor constructor = cls.getConstructor(String.class);//返回有参构造器
        System.out.println(constructor1);
        System.out.println("------");
        System.out.println(constructor);
    }
}

image.png

反射调用优化

反射的优缺点

image.png

package com.xxb.reflection.question;

import java.lang.reflect.Method;

/**
 * 测试反射调用的性能,和优化方案
 */
public class Reflection02 {
    public static void main(String[] args) throws Exception {
        m2();
    }
    //反射机制调用方法hi
    public static  void m2() throws Exception{
        Class cls = Class.forName("com.xxb.Cat");
        Object o = cls.newInstance();
        Method hi = cls.getMethod("hi");

        long start = System.currentTimeMillis();
        for (int i = 0; i < 9000000; i++) {
            hi.invoke(o);
        }
        long end = System.currentTimeMillis();
        System.out.println("反射方法调用hi "+(end-start));//412ms

    }

}

反射调用优化

image.png

package com.xxb.reflection.question;

import java.lang.reflect.Method;

/**
 * 测试反射调用的性能,和优化方案
 */
public class Reflection02 {
    public static void main(String[] args) throws Exception {
        m2();
    }
    //反射机制调用方法hi
    //反射调用优化+关闭访问检查
    public static  void m2() throws Exception{
        Class cls = Class.forName("com.xxb.Cat");
        Object o = cls.newInstance();
        Method hi = cls.getMethod("hi");
        //取消访问检查
        hi.setAccessible(true);

        long start = System.currentTimeMillis();
        for (int i = 0; i < 9000000; i++) {
            hi.invoke(o);
        }
        long end = System.currentTimeMillis();
        System.out.println("反射方法调用hi "+(end-start));//154ms

    }

}

class类分析

image.png

image.png

class类的常用方法

image.png

package com.xxb.reflection.class_;

import com.xxb.Car;

import java.lang.reflect.Field;

public class Class02 {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException {
        String classAllPath="com.xxb.Car";
        //获取到Car类对应的Class对象
        //<?> 表示不确定的Java类型
        Class<?> cls = Class.forName(classAllPath);
        //运行类型
        System.out.println(cls.getClass());//class java.lang.Class
        //得到包名
        System.out.println(cls.getPackage().getName());
        //得到类名
        System.out.println(cls.getName());
        //通过cls创建一个对象实例
         Car car = (Car) cls.newInstance();
        //通过反射获取属性
        Field brand = cls.getField("brand");
        //通过反射给属性赋值
        brand.set(car,"奔驰");

        System.out.println(brand.get(car));//奔驰

        //获取所有属性
        Field[] fields = cls.getFields();
        for (int i = 0; i <fields.length; i++) {
            System.out.println(fields[i].getName());
        }

    }
}

image.png

获取class对象的6种方式

image.png

image.png

image.png

image.png

package com.xxb.reflection.class_;

import com.xxb.Car;

/**
 * 获取Class对象的各种方式(6)
 */
public class GetClass_ {
    public static void main(String[] args) throws ClassNotFoundException {
        //1. Class.forName
        //通过读取配置文件获取
        String classAllPath="com.xxb.Car";
        Class<?> cls1 = Class.forName(classAllPath);
        System.out.println(cls1);

        //2.类名.class  应用场景 用于参数传递
        Class cls2= Car.class;
        System.out.println(cls2);

        //3. 对象.getClass()  应用场景 有对象实例
        Car car = new Car();
        Class<? extends Car> cls3 = car.getClass();
        System.out.println(cls3);

        //4. 通过类加载器来获取到类的Class对象
//        1.先得到类加载器 car
        ClassLoader classLoader = car.getClass().getClassLoader();
//        2.通过类加载器得到Class对象
        Class<?> cls4 = classLoader.loadClass(classAllPath);
        System.out.println(cls4);

        //打印 同一个类20119787
        System.out.println(cls1.hashCode());
        System.out.println(cls2.hashCode());
        System.out.println(cls3.hashCode());
        System.out.println(cls4.hashCode());

        //基本数据(int char boolean  float double, byte, long,short) 按如下方式得到Class对象
        Class<Integer> integerClass = int.class;
        Class<Character> characterClass = char.class;
        Class<Boolean> booleanClass = boolean.class;
        System.out.println(integerClass);

        //包装类型 通过.TYPE得到Class类对象
        Class<Integer> type = Integer.TYPE;
        Class<Character> type1 = Character.TYPE;
        System.out.println(type1);
    }
}

image.png

那些类型有Class对象

image.png

package com.xxb.reflection.class_;

import java.io.Serializable;

public class AllTypeClass {
    public static void main(String[] args) {
        //外部类
        Class<String> stringClass = String.class;
        //接口
        Class<Serializable> serializableClass = Serializable.class;
        //数组
        Class<Integer[]> aClass = Integer[].class;
        //二维数组
        Class<float[][]> aClass1 = float[][].class;
        //注解
        Class<Deprecated> deprecatedClass = Deprecated.class;
        //枚举
        Class<Thread.State> stateClass = Thread.State.class;
        //返回类型void
        Class<Void> voidClass = void.class;
    }
}

类加载

反射机制是java实现动态语言的关键,也就是通过反射实现类动态加载。

  • 静态加载:编译时加载相关的类,如果没有则报错,依赖性太强
  • 动态加载:运行时加载需要的类,如果运行时不用该类,则不报错,降低了依赖性。
//因为new Dog()是静态加载,因此必须编写Dog类 (静态加载)

//class.forName("Person");//加载Person类(动态加载),没有编写Person类也不会报错,只有动态加载该类
//时才会报错。

类加载时机

  1. 当创建对象时(new) 静态加载
  2. 当子类被加载时,父类也加载 静态加载
  3. 通过类中的静态成员时 静态加载
  4. 通过反射 动态加载

类加载流程图

image.png

image.png

类加载的五个阶段

  1. 加载阶段

    1. JVM在该阶段的主要目的是将字节码从不同的数据源(可能是class文件、也可能是jar包,甚至网络)转化为二进制字节流加载到内存中,并生成一个代表该类的java.lang.Class对象
  2. 连接阶段-验证

    1. 目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且危害虚拟机自身的安全。
    2. 包括:文件格式验证(是否以魔数oxcafebabe开头)、元数据验证、字节码验证和符号引用验证[举例说明]
    3. 可以考虑使用-Xverify:none参数来关闭大部分的类验证措施,缩短虚拟机类加载的时间。
  3. 连接阶段-准备

    1. JVM会在该阶段对静态变量,分配内存并默认初始化(对应数据类型的默认初始值,如0、0L、null、false等)。这些变量所使用的内存都将在方法区中进行分配。
class A{
	//属性-成员变量-字段
    //1. n1 是实例属性, 不是静态变量,因此在准备阶段,是不会分配内存
    //2. n2 是静态变量,分配内存n2是默认初始化0, 而不是20
    //3. n3 是 static final是常量 ,他和静态常量不一样,因为一旦赋值就不变,那=30
    
    public int n1=10;
    public static int n2=20;
    public static final int n3=30;
}
  1. 连接阶段-解析

    1. 虚拟机将常量池内的符号引用替换为直接引用的过程
  2. Initialization(初始化)

    1. 到初始化阶段,才真正开始执行类中定义的java程序代码,此阶段是执行() 方法的过程
    2. ()方法是由编译器按语句在源文件中出现的顺序,依次自动收集类中的所有静态变量的赋值动作和静态变量的赋值动作和静态代码块中的语句,并进行合并。
    3. 虚拟机会保证一个类的() 方法在多线程环境中被正确地加锁、同步、如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的() 方法,其他线程都需要阻塞等待,知道活动线程执行()方法完毕。

通过反射获取类的结构信息

image.png

image.png

image.png

image.png

反射暴破

反射暴破操作创建实例

image.png

package com.xxb.reflection;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class ReflectCreateInstance {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
        //1. 先获取到User类的Class对象
        Class<?> userClass = Class.forName("com.xxb.reflection.User");
        //2. 通过public的无参构造器创建实例
        Object o = userClass.newInstance();
        //3. 通过public的有参构造器创建实例

        /**
         * public User(String name){
         *         this.name=name;
         *     }
         */
        Constructor<?> constructor = userClass.getConstructor(String.class);
        //实例化public有参构造器
        Object xxx = constructor.newInstance("xxx");
        System.out.println("yyy= "+xxx);
        //4. 通过非public的有参构造器创建实例
        Constructor<?> declaredConstructor = userClass.getDeclaredConstructor(int.class, String.class);
        //反射暴破  使用反射可以访问private构造器
        declaredConstructor.setAccessible(true);
        Object xxg = declaredConstructor.newInstance(100, "xxg");
        System.out.println(xxg);
    }
}
class User{
    private int age=10;
    private String name="学习";
    //无参构造器
    public  User(){

    }
    //public 的有参构造器
    public User(String name){
        this.name=name;
    }
    //
    private User(int age,String name){
        this.age=age;
        this.name=name;
    }

    @Override
    public String toString() {
        return "User{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}

image.png

反射暴破操作属性

image.png

package com.xxb.reflection;

import java.lang.reflect.Field;

public class ReflectAccessProperty {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException {
        //1.得到Student类对应的Class 对象
        Class<?> stuClass = Class.forName("com.xxb.reflection.Student");
        //2.创建对象
        // o的运行类型就是Student
        Object o = stuClass.newInstance();
        System.out.println(o.getClass());//Student
        //3. 使用反射得到age对象
        Field age = stuClass.getField("age");
        //通过反射来操作属性
        age.set(o,88);
        System.out.println(o);//Student[ age=88,name=null]

        //4.使用反射操作name 属性
        Field name = stuClass.getDeclaredField("name");
        //对name进行暴破,可以操作private属性
        name.setAccessible(true);
//        name.set(o,"测试");
        //静态属性也可以设置成null
        name.set(null,"测试");
        System.out.println(o);
        //获取属性
        System.out.println(name.get(o)); //测试
        System.out.println(name.get(null)); //测试



    }
}
//类
class Student{
    public int age;
    private static String name;
    public Student(){//构造器

    }

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

image.png

反射暴破操作方法

image.png

package com.xxb.reflection;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class ReflectAccessMethod {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        //1.得到Boss类对应的Class对象
        Class<?> bossCls = Class.forName("com.xxb.reflection.Boss");
        //2.创建对象
        Object o = bossCls.newInstance();
        //3.调用public的hi方法
        Method hi = bossCls.getMethod("hi",String.class);
        //调用方法
        hi.invoke(o,"测试");

        //调用private static 方法
        Method say = bossCls.getDeclaredMethod("say", int.class, String.class, char.class);
        //反射方法暴破
        say.setAccessible(true);


        //静态方法可以使用null
        System.out.println(say.invoke(null, 100, "狗", 'z'));

        //在反射中,如果方法有返回值,同一返回Object
        Object relVal = say.invoke(o, 100, "狗", 'z');
        System.out.println("reVal 的运行类型="+ relVal.getClass());


    }
}
//类
class Boss{
    public int age;
    private static String name;
    //构造器
    public Boss(){

    }
    //静态方法
    private static String say(int n, String s, char c){
        return  n+" "+s+" "+c;
    }

    public void hi(String s){
        System.out.println("hi "+s);
    }
}

image.png

posted @ 2021-09-28 21:26  sprite5521  阅读(31)  评论(0)    收藏  举报