Java~反射机制、获取Class的三种方式、利用反射获取字段、方法、创建对象

反射机制

  • 反射机制有什么用?

通过Java语言中的反射机制可以操作字节码文件
有点类似于黑客,可以读和修改字节码文件

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

  • 反射机制的相关类在哪个包下?

java.lang.reflect.*;

  • 反射机制相关的类有哪些?
    java.lang.Class 代表整个字节码,代表一个类型

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

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

java.lang.reflect.Field 代表字节码中的属性字节码

反射中的Class就是指整个类,Field就是表示类中的字段,Constructor表示方法中的构造器,而Method表示类中的方法

获取Class的三种方式

Class.forName方法
1.是个静态方法
2.方法的参数是一个字符串
3.字符串需要的是一个完整的类名
4.完整类名必须带有包名

方式一:forName静态方法获得

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");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

    }
}

方式二:对象.getClass方法

import java.util.Date;

public class ReflectTest01 {
    public static void main(String[] args){

         //方式一:
        Class c1=null;
        Class c2=null;
        Class c3=null;
        try {
            c1=Class.forName("java.lang.String"); //c1代表String.class文件,或者说是C1代表String类型
           c2=Class.forName("java.util.Date");   //c2代表Date类型
           c3=Class.forName("java.lang.Integer"); //c3代表Integer类型
            Class c4=Class.forName("java.lang.System");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        // java中任何一个对象都有一个方法:getClass方法
        String s="abc";
        Class x=s.getClass();  //x代表String.class 字节码文件,x代表String类型
        System.out.println(c1==x); //true  (==判断的是内存地址)


        Date time=new Date();
        Class y=time.getClass();
        System.out.println(c2==y);// true
        //c2和y两个变量中的保存的内存地址是一样的,都指向方法区中的字节码文件。
        
    }
}

在这里插入图片描述

方式三:任何类型.class属性

import java.util.Date;

public class ReflectTest01 {
    public static void main(String[] args){
        //第三种方式:java语言中任何一种类型,包括基本数据类型 都有.class属性
        Class z=String.class; // z代表String类型
        Class k=Date.class;  // k代表Date类型
        Class f=int.class;   //f代表int类型
        Class e=double.class;   // e代表double类型        
    }
}

利用反射实例化对象

  • 通过Class的newInstance方法来实例化对象

  • newInstance方法内部实际上调用了无参数构造方法,必须保证无参构造存在才可以

public class ReflectTest02 {
    public static void main(String[] args) throws Exception {
        //不适用反射机制,创建对象
        User user=new User();
        System.out.println(user);
        
        //使用反射来创建对象
        //通过反射机制,获取Class,通过Class来实例化对象
        Class c=Class.forName("Bean.User");
        Object obj=c.newInstance();
        //newInstance方法会调用User这个类的无参数构造方法,完成对象的创建.
       // System.out.println(obj);
    }
}

  • 代码这么多,为什么要用反射

使用反射机制创建对象更加灵活
在不改变源代码的基础上,可以做到不同对象的实例化
非常灵活,符合OCP开闭原则(对扩展开放,对修改关闭)
在工作中经常使用的高级框架,
包括:
ssh ssm
Spring SpringMVC MyBatis
Spring Struts Hibernate

高级框架的底层实现原理都采用了反射机制,所以反射机制是很重要的.

灵活性演示

    public static void main(String[] args) throws Exception{
        //直接创建对象,直接写死了,只能是User对象,无法修改成其他对象
        User user=new User();
        
        //用反射读取配置文件创建对象,非常灵活,只需在配置文件中修改就可以创建任意对象
         //通过IO流读取classinfo.properties文件
        FileReader reader=new FileReader("src/main/resources/classinfo.properties");
        //创建属性类对象Map
        Properties pro=new Properties();
        //加载
        pro.load(reader);
        //关闭流
        reader.close();
        //通过key获取value  key value都是String
        String className=pro.getProperty("className");
        System.out.println(className);
        //通过反射机制实例化对象
        Class c=Class.forName(className);
        Object obj=c.newInstance();
        System.out.println(obj);
    }

Class.forName方法

public class ReflectTest04 {
    public static void main(String[] args) throws ClassNotFoundException {
        //forName方法的执行必然导致类加载,类加载必然执行静态代码块
            Class.forName("Bean.MyClass");
    }
}
//另一个包下
public class MyClass {
    //静态代码块在类加载的时候执行,且只执行一次
    static{
            System.out.println("MyClass类的静态代码开始执行");
    }
}
  • 如果你只希望一个类的静态代码块执行,其他代码都不执行,可以使用Class.forName(“完整类名”)
  • 这个方法会导致类加载,类加载中静态代码块必然执行一次

反射获取Field

public static void main(String[] args) throws Exception{
        //获取整个类
        Class studentClass=Class.forName("Bean.Student");
         Field[] fields= studentClass.getFields();
         System.out.println(fields.length);
         Field f=fields[0];
        System.out.println(f.getName());
        Field[] fs=studentClass.getDeclaredFields();
        System.out.println(fs.length);
        System.out.println("==========");
        for(Field  field: fs){
            //获取属性的修饰符
           int i=field.getModifiers();
            System.out.println(i);
            //将这个数字转成字符串
            String modifierString= Modifier.toString(i);
            System.out.println(modifierString);
            //获取属性的类型
          Class fieldType=field.getType();
          String fName=fieldType.getName();
            System.out.println(fName);
            //获取属性的名字
            System.out.println(field.getName());
        }
    }

反编译Field

public static void main(String[] args) throws ClassNotFoundException {
        StringBuffer sb=new StringBuffer();
        Class student = Class.forName("java.lang.Thread");
      sb.append(Modifier.toString(student.getModifiers())+"class"+student.getSimpleName()+"{");
       Field[] fields=student.getDeclaredFields();
        for (Field field:fields
             ) {
            sb.append("\t");
            sb.append(Modifier.toString(field.getModifiers()));
            sb.append(" ");
            sb.append(field.getType());
            sb.append(" ");
            sb.append(field.getName());
            sb.append(";\n");
        }
      sb.append("}");
        System.out.println(sb);
    }

利用反射访问类字段

  • 如何修改字段的值
  • 如何得到字段的值
    这种写法虽然有些多,但是具有非常强的移植性,代码很灵活.

访问字段三要素:拿到对象,什么属性,设置/得到值
反射也不例外,也是按步骤得到三要素

public static void main(String[] args) throws  Exception{
        //不用反射给访问对象字段
        Student s=new Student();
        //给字段赋值
        s.no=123;
        //读取字段值
        System.out.println(s.no);
        //通过反射访问对象的字段:如何用反射给字段赋值,或者获取字段的值
        Class student=Class.forName("Bean.Student");
        Object obj=student.newInstance();//先获取一个实例,通过无参方法获取
        //获取no字段
      Field f1 = student.getDeclaredField("no");
       f1.set(obj,2222);//obj这个对象的f1设置为2222
        System.out.println(f1.get(obj));
        //访问私有字段
       Field f2=student.getDeclaredField("name");
      Object obj1=student.newInstance();
        //访问前需要打破封装 某个字段的封装
        f2.setAccessible(true);
      f2.set(obj1,"姓名");
        System.out.println(f2.get(obj1));
    }

插入知识点:可变长参数

  • 可变长参数在参数列表中只能有一个,并且是在末尾位置
  • 个数【0,n】
  • 可以当成一个数组来看待
  //可变长参数
    public static void main(String[] args) {
      //  m1(123,3);
        m2("asd",1,2,3,4,5);
    }
    public static void m1(int...args){
        System.out.println("这是可变长参数");
    }
    public static void m2(String s,int...a){
        System.out.println(s);
        for(int x:a){
            System.out.println(x);
        }
    }

使用反射机制来调用方法

 public static void main(String[] args) throws Exception{
        //不是使用反射调用方法
        UserService userService=new UserService();
        boolean logingS=userService.login("root","123");
        //使用反射调用方法  拿到类,拿到实例,拿到方法,调用方法拿到处理值
        Class user=Class.forName("Bean.UserService");
        Object obj=user.newInstance();
        Method login=user.getDeclaredMethod("login",String.class,String.class);
        //调用这个方法,传递这个参数,返回这个值
         Object retValue=login.invoke(obj,"root","123");
        System.out.println(retValue);
    }

使用反射创建对象

   public static void main(String[] args) throws Exception {
        //使用反射机制创建对象
        //反射前两步都固定了,拿到类,拿到实例
        //拿到反射类中的方法是类调用的,不是用类的实例调用的
       Class c= Class.forName("Bean.User");
       //调用无参数的构造方法 创建对象
        Object obj=c.newInstance();
        //用有参数的构造方法
        Constructor con=c.getDeclaredConstructor(int.class,String.class);
      Object newObj= con.newInstance(20,"783");
        System.out.println(newObj);
    }

版权声明:本文为Merciful_Lion原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。