Java——反射机制详解及实操

Java——反射机制详解及实操

作用

通过java语言中的反射机制可以操作(读/改)字节码文件

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

反射机制的相关的重要的类

反射机制的相关类均在 java.lang.reflect.*;

  1. java.lang.class :代表整个字节码文件,代表一个类型
  2. java.lang.reflect.Method :代表字节码中的方法字节码
  3. java.lang.reflect.Constructor :代表字节码中的构造方法字节码
  4. java.lang.reflect.Field :代表字节码中的属性字节码
    java.lang.Class:
        public class User{
            //Field
            int no;
            
            //Constructor
            public User() {
            }
            public User(int no) {
                this.no = no;
            }
            
            //Method
            public void setNo(int no) {
                this.no = no;
            }
            public int getNo() {
                return no;
            }
        }

获取java.lang.Class实例的三种方式

注:字节码文件装载到JVM中的时候,只装载一份

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

  1. 静态方法
  2. 方法的参数是一个字符串
  3. 字符串需要的是一个完整类名
  4. 完整类名必须带有包名。java.lang包也不能省略。
    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类型
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

方式二:Class c = 引用getClass();

java中任何一个对象都有一个方法:getClass

    public static void main(String[] args) {
        String s = "abc";
        Class x = s.getClass();//x代表String.class字节码文件;x代表String类型
    }

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

java语言中任何一种类型,包括基本数据类型,它都有.class属性

public static void main(String[] args) {
    Class i = String.class;//i代表String类型
    Class j = int.class;//j代表int类型
    Class k = Date.class;//k代表Date类型
    Class l = double.class;//l代表double类型
}

注:字节码文件装载到JVM中的时候,只装载一份

如:方式一和方式二获得的类型对象的内存是相同的

image-20210203230529863

通过反射实例化对象——newInstance();

注:newInstance()方法内部实际上调用了无参构造方法,必须保证无参构造存在才可以。如果没有这个无参数构造方法会出现“实例化”异常

public static void main(String[] args) {
    try {
        //通过反射机制,获取Class,通过Class来实例化对象
        Class c = Class.forName("com.xiaoli.User");//c代表User类型
        Object obj = c.newInstance();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InstantiationException e) {
        e.printStackTrace();
    }
}

通过反射实例化对象的优点——更加灵活

java代码写一遍,在不改变java源代码的基础之上,可以做到不同对象的实例化。

非常灵活。(符合OCP开闭原则:对扩张开放,对修改关闭)

例(验证):

创建一个属性配置文件

源码:

    public static void main(String[] args) throws IOException {
        //通过IO流读取ClassName文件
        FileReader reader = new FileReader("D:\\IDEA\\untitled02\\ClassName");
        //创建属性类对象Map
        Properties pro = new Properties();//key value都是String
        //加载
        pro.load(reader);
        //关闭流
        reader.close();

        //通过key获取value
        String className = pro.getProperty("classname");

        //通过反射实例化对象
        try {
            Class c = Class.forName(className);
            Object obj = c.newInstance();
            System.out.println(obj);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
    }

灵活性:代码不需要改动,可以修改配置文件,配置文件修改之后,可以创建出不同的实例对象

forName();——只让静态代码块执行

    public static void main(String[] args) {
        try {
            Class.forName("com.busying.Myclass");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    class Myclass{

    //静态代码块在类加载时执行,并且只执行一次
    static {
        System.out.println("MyClass的静态代码块执行了");
    }
}

运行结果:
在这里插入图片描述

文件路径

String path = Thread.currentThread().getContextClassLoader().getResource("写相对路径,但是这个相对路径从src出发开始找").getPath();

例:

String path = Thread.currentThread().getContextClassLoader().getResource("com/jintianxuejavalema/xuele").getPath();
//必须保证src下有com目录,com目录下有jintianxuejavalema目录,jintianxuejavalema目录下有xuele文件

这种方式是为了获取一个文件的绝对路径(通用方式,不会受环境移植性影响)

但是该文件要求放在类路径下(即:放在src下面,src下的类是根路径)

//直接以流的方式返回
InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream("com/jintianxuejavalema/xuele");

快速绑定属性资源文件——IO + Properties

要求:

  1. 这个文件必须在类路径下(src下)
  2. 这个文件必须是以Properties结尾
//com.jintianxuejavalema/xuele.properties
ResourceBundle bundle = ResourceBundle.getBundle("com.jintianxuejavalema/xuele");
        String Value = bundle.getString(key);

实操(例子)

编写一个student类,通过反射机制获取其实例对象并将实例对象中的所有数据输出到student.txt文件中,

源码中创建student类对象的时不允许通过new关键字创建对象。

    public static void main(String[] args){
        try {
            FileOutputStream writer = new FileOutputStream("D:\\IDEA\\untitled02\\src\\student.txt");
            //获取Class
            Class c = Class.forName("com.busying.student");

            //构造一个对象
            //student student01 =(student) c.newInstance();

            //通过构造器创建对象
            Constructor constructor = c.getDeclaredConstructor(String.class,int.class,double.class);
            student student01 =(student) constructor.newInstance("小李",18,99);
            String text = student01.toString();
            byte[] bytes = text.getBytes();
            writer.write(bytes);
            writer.close();

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
class student{
    private String name;
    private int age;
    private double score;

    public student() {
    }

    public student(String name,int age, double score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public double getScore() {
        return score;
    }

    public void setScore(double score) {
        this.score = score;
    }

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

运行结果:
在这里插入图片描述

posted @ 2021-02-05 15:32  许君  阅读(91)  评论(0编辑  收藏  举报