Java反射机制

一、反射机制有什么用?

通过java语言中的反射机制可以操作字节码文件。

有点类似于黑客。(可以读和修改字节码文件)。

通过反射机制 可以操作代码片段(class文件)。

 

二、反射机制在哪个包下?

java.lang.reflect.*;

 

三、反射机制相关的重要的类有哪些?

java.lang.Class:代表整个字节码,代表一个类型,代表整个类                                  

java.lang.reflect.Method:代表字节码中的方法字节码。代表类中的方法

java.lang.reflect.Constructor:代表字节码中的构造方法字节码。代表类中的构造方法

java.lang.reflect.Field:代表字节码码中的属性字节码。代表类中的成员变量(静态变量+实例变量)

java.lang.Class

一、获取Class的三种方式

要操作一个类的字节码,需要获取到这个类的字节码,怎样获取 java.lang.Class 实例?

① 方式一:Class c = Class.forName("完整类名带包名");

② 方式二:Class c = 引用.getClas();

③ 方式三:Class c = 任何类型.class;

public class ReflectTest01 {
    public static void main(String[] args) {
        try {
            //第一种方法
            Class c1 = Class.forName("java.lang.String");//c1代表String.class文件,或者说c1代表String类型
            Class c2 = Class.forName("java.util.Date");//c2代表Date类型
            Class c3 = Class.forName("java.lang.Integer");//c3代表Integer类型
            Class c4 = Class.forName("java.lang.System");//c4代表System类型
            System.out.println(c1);//class java.lang.String

            //第二种方法
            //java中任何一个对象都有一个方法:getClass()
            Class x = "abc".getClass();//x代表 String.class字节码文件,x代表String类型。
            System.out.println(c1 == x);//true,字节码文件转载到JVM中时,只加载一份

            Date date = new Date();
            Class y = date.getClass();
            System.out.println(c2 == y);//true


            //第三种方法
            //java语言中任何一种类型,包括基本数据类型,它都有.class属性
            Class z = String.class;
            Class k = Date.class;
            Class f = int.class;
            Class e = double.class;
            System.out.println(x == z);//true

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

二、获取到class,能干什么?

通过Class的newInstance()方法来实例化对象。
注意:newInstance()方法内部实际上是调用了无参构造方法,必须保证无参构造方法存在才可以

public class ReflectTest02 {
    public static void main(String[] args) {
        //通过反射机制,获取Class,通过Class来实例化对象
        try {
            //不用反射机制创建对象
            User  user = new User();

            //用反射机制创建对象
            //newInstance()这个方法会调用User这个类的无参数构造方法,完成对象的创建。
            //重点:必须保证无参构造是存在的
            //建议都写无参构造方法,如果写了有参的构造方法,没写无参构造方法
            //newInstance()方法会出现异常:java.lang.InstantiationException
            //无参构造
            Object o1 = Class.forName("main.java.cn.edu.mju.bean.User").newInstance();//新建实例

            Object o2 = Class.forName("main.java.cn.edu.mju.bean.User")
                    .getConstructor(null).newInstance(null);

            //有参构造
            Object o3 = Class.forName("main.java.cn.edu.mju.bean.User")
                    .getConstructor(Integer.class).newInstance(20);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

三、验证反射机制的灵活性

Java代码写一遍,在不改变java代码的基础之上,可以做到不同对象的实例化。
非常之灵活。(符合OCP开闭原则:对外开放,对修改关闭。)
创建的对象不一定是User对象,创建的对象取决于配置文件中的类名。
User user = new User();这种方式是写死了,没有灵活性。

高级框架底层实现原理:都采用了反射机制,所以反射机制还是很重要的额。
学会了反射机制原理有利于你理解剖析框架底层的源代码。

public class ReflectTest03 {
    public static void main(String[] args) throws Exception {
        //这种方式代码就写死了,只能创建一个User类型的对象
        User user = new User();
        //以下代码是灵活的,代码不需要改动。只需修改配置文件就可以创建出不同的实例对象
        //通过IO流读取classinfo.properties文件
        FileReader reader = new FileReader("D:\\Data\\java\\src\\classinfo.properties");
        //创建属性类对象Map
        Properties pro = new Properties();
        //加载
        pro.load(reader);
        reader.close();
        //通过key获取value
        String className = pro.getProperty("className");
        System.out.println(className);
        //通过反射机制实例化对象
        //创建的对象不一定是User对象,创建的对象取决于类名
        Class c = Class.forName(className);
        Object obj = c.newInstance();
        System.out.println(obj);
    }
}

java.lang.reflect.Field

  • A Field提供有关类或接口的单个字段的信息和动态访问。 反射的字段可以是类(静态)字段或实例字段。

反射User类中的所有的Field

  • 获取类
Class user = Class.forName("main.java.cn.edu.mju.user.User");
  • 获取类名
user.getName()//完整类名
user.getSimpleName()//简类名

 

  • 获取类中所有public的Field对象
Field[]  field = user.getFields();

 

  • 获取类中的所有Field对象
Field[] fields = user.getDeclaredFields();//获取声明字段
  • 获取Field对象的访问修饰符(Modifiers)
Modifier.toString(fields[i].getModifiers());
/*
fields[i].getModifiers();//返回的修饰符是一个数字,每个数字是修饰符的代号
*/
  • 获取Field对象的数据类型 
fields[i].getType().getSimpleName();//简类型名
fields[i].getType().getName();//完整类型名
  • 获取Field对象的字段名
fields[i].getName()

①、获取任何一个类的所有Field(反编译)

//通过反射机制,反编译一个类的所有属性Field
public class ReflectTest02 {
    public static void main(String[] args) throws ClassNotFoundException {
        //拼串
        StringBuilder s  = new StringBuilder();
        //只要给个类名,就可以反编译出字段信息
        //加载类
        Class intClass = Class.forName("java.lang.Integer");
        //获取该类的所有Field对象
        Field[] fields = intClass.getDeclaredFields();
        //类的访问修饰符和简类名
        s.append(Modifier.toString(intClass.getModifiers()) +" class "+ intClass.getSimpleName() + "{\n");
        for (Field field:fields) {
            s.append("\t");
            s.append(Modifier.toString(field.getModifiers()));//Field对象的访问修饰符
            s.append(" ");
            s.append(field.getType().getSimpleName());//Fidel数据类型的简名称
            s.append(" ");
            s.append(field.getName());//Field对象的名称
            s.append(";\n");
        }
        s.append("}");
        System.out.println(s);
    }
}

 

②、(重点)通过反射机制访问Java对象属性

给属性赋值:set
获取属性的值:get

通过反射机制获取获取类中某一具体Field(属性)对象。:Field nameField = userClass.getField("name");

通过该Field对象可以给任意Java对象的Field对象进行赋值或取值操作。: nameField.set(u1,"张三");    nameField.get(u1)

public class ReflectTest03 {
    public static void main(String[] args) throws Exception {
        //类加载,获取类
        Class userClass = Class.forName("main.java.cn.edu.mju.user.User");
        //反射机制创建对象
        User u1 =  (User) userClass.newInstance();
        //获取Java对象的某一具体属性
        Field nameField = userClass.getField("name");
        //给对象的具体属性赋值
        //赋值,给u1对象的name属性赋值"张三"
        nameField.set(u1,"张三");
        //取值
        System.out.println(nameField.get(u1));//通过反射机制的方式
        //不可以访问私有的属性
        /*
        java.lang.NoSuchFieldException异常
            1.本身就没有该Field;
            2.有该Field,但是该Field是使用private修饰的,而在获取该Field的时候,需要使用getDeclaredField这个方法。
         */
        Field noField = userClass.getDeclaredField("phone");
        //打破封装(反射的缺点,可能给不法分子留下机会)
        noField.setAccessible(true);//某一具体属性打破封装
        noField.set(u1,"00000000");
        System.out.println(noField.get(u1));
    }
}

反射机制只能对publiu修饰的Fidel对象进行值的操作(赋值|取值)

有两种方式可以访问private修饰的Field:

  1. 通过该类提供的封装的Getter和Setter方法。
  2. 通过反射机制打破封装的方式。

java.lang.reflect.Method

一、可变长度参数:

  1. 要求可变长度参数是0~n个
  2. 要求可变长度参数在参数列表中,必须最后一个位置上
  3. 可变长度参数只能有一个
  4. 可变长度参数可以看作一个数组看待
public class ArgsTest {
    public static void main(String[] args) {
        m();
        m(10);
        m(10,20,30);
        m1("哈哈哈");
        m1("哈哈哈",10);
        m2(10,20,30,40,50,60);
        m2(new int[]{50,6,07,7,98,78,45});
    }

    public static void m(int... args){
        System.out.println("m方法执行了");
    }
    public static void m1(String name,int... args){
        System.out.println("m1方法执行了");
    }
    public static void m2(int... args){
        for (int i:args) {
            System.out.print(i+" ");
        }
    }
}

二、反射method

 

public class MethodTest01 {
    public static void main(String[] args) throws Exception {
        //获取类
        Class userServiceClass = Class.forName("main.java.cn.edu.mju.user.UserService");
        //获取所有的method(包括私有的)
        Method[] methods = userServiceClass.getDeclaredMethods();
        System.out.println(methods.length);
        //遍历methods
        for (Method method:methods) {
            System.out.println
                    (
                            Modifier.toString(method.getModifiers())    //方法的访问修饰符
                            + " " +
                            method.getReturnType()                     //方法的返回值类型
                            + " " +
                            method.getName()                           //方法名
                    );
            Class[] parameterTypes = method.getParameterTypes();        //获取方法对象的形参,一个方法可能有好几个形参
        for (Class parameterType : parameterTypes){                     //遍历形式参数对象
                System.out.println(parameterType.getSimpleName());      //遍历输出形式参数数据类型的简称
            }
        }
    }
}

一、获取一个类中的所有method对象(反编译

//反编译 method
public class MethodTest02 {
    public static void main(String[] args) throws Exception {
        //拼串
        StringBuilder s = new StringBuilder();
        //获取类
        Class aClass = Class.forName("java.lang.String");
        //获取类的访问修饰符+类名
        s.append(Modifier.toString(aClass.getModifiers())+ " class " + aClass.getSimpleName()+" {\n");
        //获取类中所有的方法存在一个方法数组中
        Method[] methods = aClass.getDeclaredMethods();
        //遍历方法数组,获取每个方法的 访问修饰符 + 返回值类型 + 方法名 + 参数列表
        for (Method method:methods) {
            s.append("\t");
            s.append(Modifier.toString(method.getModifiers()));     //访问修饰符
            s.append(" " );
            s.append(method.getReturnType());                       //返回值类型
            s.append( " " );
            s.append(method.getName());                            //方法名
            s.append("(");
            Class[] parameterTypes = method.getParameterTypes();     //参数列表
            for (Class parameterType:parameterTypes) {
                s.append(parameterType.getSimpleName());            //参数类型
                s.append(",");
            }
            if (s.length()>0){
                s.deleteCharAt(s.length()-1);//删除指定下标的字符
            }
            s.append("){");
            s.append("\t}\n");
        }
        s.append("}");
        System.out.println(s);
    }
}

 

二、(重点)通过反射机制调方法

public class MethodTest03 {
    public static void main(String[] args) throws Exception {
        //不用反射机制怎么调用方法
        UserService userService = new UserService();
        boolean b = userService.login("admin","123");
        System.out.println(b);
        userService.logout();

        //使用反射机制调用方法
        Class userServiceClass = Class.forName("main.java.cn.edu.mju.user.UserService");
        //创建对象
        UserService userService1 = (UserService) userServiceClass.newInstance();
        //获取Method
        Method loginMethod = userServiceClass.getDeclaredMethod("login",String.class,String.class);
        //调用方法
        Object retValue = loginMethod.invoke(userService1,"admin","123");//invoke:调用

        System.out.println(retValue);
    }
}

java.lang.reflect.Constructor

一、反编译Construct

//反编译constructor
public class ConstructorTest01 {
    public static void main(String[] args) throws Exception {
        StringBuilder s = new StringBuilder();//拼串
        Class vipClass = Class.forName("main.java.cn.edu.mju.bean.Vip");//获取类
        s.append(Modifier.toString(vipClass.getModifiers()));//获取类的访问修饰符
        s.append(" class ");
        s.append(vipClass.getSimpleName());//获取类名
        s.append("{\n");
        Constructor[] constructors = vipClass.getDeclaredConstructors();//获取所有构造方法,放在数组中
        for (Constructor constructor:constructors) {//遍历数组
            s.append("\t");
            s.append(Modifier.toString(constructor.getModifiers()));//构造方法,访问修符
            s.append(" ");
            s.append(vipClass.getSimpleName());//构造方法,方法名
            s.append("(");
            Class[] parameterTypes = constructor.getParameterTypes();//参数列表
            for (Class parameterType:parameterTypes) {
                s.append(parameterType.getSimpleName());
                s.append(",");
            }
            if (parameterTypes.length > 0){
                s.deleteCharAt(s.length()-1);
            }

            s.append("){}\n");
        }

        s.append("}");
        System.out.println(s);
    }
}

 

二、反射机制创建对象

public class ConstructorTest02 {
    public static void main(String[] args) throws Exception{
        //使用反射机制创建对象
        Vip v1 = (Vip)Class.forName("main.java.cn.edu.mju.bean.Vip").newInstance();
        Vip v2 = (Vip)Class
                .forName("main.java.cn.edu.mju.bean.Vip")
                .getConstructor(int.class,String.class,String.class,boolean.class)
                .newInstance(10,"pp","10月1日",false);

        Vip v3 = (Vip)Class.forName("main.java.cn.edu.mju.bean.Vip").getConstructor().newInstance();
        System.out.println(v1);
        System.out.println(v2);
    }
}

 

重点:给你一个类,怎么获取父类已经实现的接口

public class ReflectGetInterface {
    public static void main(String[] args) throws ClassNotFoundException {
        Class stringClass = Class.forName("java.lang.Class");

        //获取Sting类的父类
        System.out.println(stringClass.getSuperclass());

        //获取String类实现的接口

        Class[] interfaces = stringClass.getInterfaces();
        for (Class in:interfaces) {
            System.out.println(in.getName());
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值