Java基础进阶反射

导读:本篇文章讲解 Java基础进阶反射,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

反射机制有什么用?

通过java语言中的反射机制可以操作字节码文件。
优点类似于黑客。(可以读和修改字节码文件。)
通过反射机制可以操作代码片段。(class文件。)
在这里插入图片描述

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

java.lang.reflect.*;

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

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

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

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

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

获取Class的三种方式:

java中获取Class的三种方式?要操作一个类的字节码,需要首先获取到这个类的字节码,怎么获取java.lang.Class实例
第一种:

Class c = Class.forName("完整类名");

第二种:

Class c = 对象.getClass();

第三种:

Class c = int.class;
Class c = String.class;

示例代码01:

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

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

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

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

        // 第三种方式,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类型

        System.out.println(x == z);// true
    }
}

运行结果:

在这里插入图片描述

获取Class,能干什么?

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

示例代码02:

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

        //不使用反射创建对象
        User user = new User();
        System.out.println(user);
        Class c;

        {
            try {
                //使用反射创建对象,通过反射机制,获取Class,通过Class来实例化对象
                c = Class.forName("com.newstudy.javase.bean.User");//c代表User类型
                //newInstance()这个方法会调用User这个类的无参构造方法,完成对象的创建!
                //重点是:newInstance()调用的是无参构造方法,必须保证无参构造是存在的
                Object o = c.newInstance();
                System.out.println(o);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }
}
class User {

    public User(){
        System.out.println("无参构造方法执行!");
    }
    /*public User(){

    }*/
}

运行结果:

在这里插入图片描述

反射机制的灵活性。

java代码写一遍,再不改变java源代码的基础之上,可以做到不同对象的实例化。
非常之灵活。(符合OCP开闭原则:对扩展开放,对修改关闭。)

示例代码02:

public class ReflectTest03 {
    public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException {

        // 这种方式代码就写死了。只能创建一个User类型的对象
        //User user = new User();

        // 以下代码是灵活的,代码不需要改动,可以修改配置文件,配置文件修改之后,可以创建出不同的实例对象。
        // 通过IO流读取classinfo.properties文件
        FileReader reader = new FileReader("reflect/src/classinfo.properties");

        //在创建properties对象
        // 创建属性类对象Map
        Properties pro = new Properties();

        //把文件通过流方式加载到properties集合中
        pro.load(reader);

        // 关闭流
        reader.close();

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

        //通过反射创建对象,实例化对象
        Class c = Class.forName(classname);
        Object o = c.newInstance();
        System.out.println(o);


    }
}

运行结果:

在这里插入图片描述

Class.forName()发生了什么?

记住,重点:
如果你只是希望一个类的静态代码块执行,其它代码一律不执行,
你可以使用:
Class.forName(“完整类名”);
这个方法的执行会导致类加载,类加载时,静态代码块执行。

示例代码03:

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

        try {
            // Class.forName()这个方法的执行会导致:类加载。
            Class c = Class.forName("com.newstudy.javase.reflect.MyClass");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
public class MyClass {

    static{

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

在这里插入图片描述

运行结果:

在这里插入图片描述

怎么获取一个文件的绝对路径。

Thread.currentThread() 当前线程对象
getContextClassLoader() 是线程对象的方法,可以获取到当前线程的类加载器对象。
getResource() 【获取资源】这是类加载器对象的方法,当前线程的类加载器默认从类的根路径下加载资源。

示例代码04:

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

        // 这种方式的路径缺点是:移植性差,在IDEA中默认的当前路径是project的根。
        // 这个代码假设离开了IDEA,换到了其它位置,可能当前路径就不是project的根了,这时这个路径就无效了。
        //FileReader reader = new FileReader("chapter25/classinfo2.properties");

        // 接下来说一种比较通用的一种路径。即使代码换位置了,这样编写仍然是通用的。
        // 注意:使用以下通用方式的前提是:这个文件必须在类路径下。
        // 什么类路径下?方式在src下的都是类路径下。【记住它】
        // src是类的根路径。
        /*
        解释:
            Thread.currentThread() 当前线程对象
            getContextClassLoader() 是线程对象的方法,可以获取到当前线程的类加载器对象。
            getResource() 【获取资源】这是类加载器对象的方法,当前线程的类加载器默认从类的根路径下加载资源。
         */
        // 这种方式获取文件绝对路径是通用的。
        String path1 = Thread.currentThread().getContextClassLoader().getResource("classinfo2.properties").getPath();
        // 采用以上的代码可以拿到一个文件的绝对路径。
        // /C:/Users/Administrator/IdeaProjects/javase/out/production/chapter25/classinfo2.properties
        System.out.println(path1);

        // 获取db.properties文件的绝对路径(从类的根路径下作为起点开始)
        String path2 = Thread.currentThread().getContextClassLoader().getResource("com/newstudy/javase/bean/db.properties").getPath();
        System.out.println(path2);
    }
}

在这里插入图片描述

在这里插入图片描述

运行结果:

在这里插入图片描述

文件路径直接以流的形式返回:

InputStream in = Thread.currentThread().getContextClassLoader()
.getResourceAsStream(“com/bjpowernode/test.properties”);

示例代码05:

public class IoPropertiesTest {

    public static void main(String[] args) throws IOException {

        // 获取一个文件的绝对路径了!!!!!
        /*String path = Thread.currentThread().getContextClassLoader()
                .getResource("classinfo2.properties").getPath();
        FileReader reader = new FileReader(path);*/

        //直接以流的方式返回
        InputStream reader = Thread.currentThread().getContextClassLoader()
                .getResourceAsStream("classinfo2.properties");
        Properties pro = new Properties();
        pro.load(reader);
        reader.close();
        //通过key获取value
        String classname = pro.getProperty("classname");
        System.out.println(classname);

    }
}

运行结果:

在这里插入图片描述

通过反射获取属性值Field(以Student类举例)

示例代码06:

public class ReflectTest05 {
    public static void main(String[] args) throws ClassNotFoundException {

        // 获取整个类
        Class student = Class.forName("com.newstudy.javase.bean.Student");
        //获取完整类名
        String studentName = student.getName();
        System.out.println("完整类名:" + studentName);
        //获取简单类名
        String studentSimpleName = student.getSimpleName();
        System.out.println("简单类名:" + studentSimpleName);

        //获取类属性
        /*Field[] fields = student.getFields();
        System.out.println(fields.length); // 测试数组中只有1个元素
        Field field = fields[0];
        System.out.println(field.getName());*/

        //获取所有属性字段
        Field[] fs = student.getDeclaredFields();
        for(Field fieldo : fs) {
            //获取字段修饰符列表
            int i = fieldo.getModifiers();// 返回的修饰符是一个数字,每个数字是修饰符的代号!!!
            // 可以将这个“代号”数字转换成“字符串”吗?
            String s = Modifier.toString(i);
            System.out.println(s);
            //获取字段类型名字
            /*Class type = fieldo.getType();
            System.out.println(type.getName());*/
            //获取字段类型简单名字
            Class type1 = fieldo.getType();
            System.out.println(type1.getSimpleName());
            //获取字段名字
            System.out.println(fieldo.getName());
        }
    }
}
public class Student {
    public int no;
    private String name;
    protected int age;
    boolean sex;
}

运行结果:

在这里插入图片描述

通过反射机制,反编译一个类的属性Field

示例代码07:

public class ReflectTest06 {
    public static void main(String[] args) throws ClassNotFoundException {

        //获取整个类
        Class student = Class.forName("java.lang.Thread");

        //创建字符串拼接对象// 创建这个是为了拼接字符串。
        StringBuilder sb = new StringBuilder();
        sb.append(Modifier.toString(student.getModifiers()) + " class " + student.getSimpleName() + "{\n");
        Field[] fields = student.getDeclaredFields();
        for(Field field : fields) {
            sb.append("\t");
            sb.append(Modifier.toString(field.getModifiers()));
            sb.append(" ");
            sb.append(field.getType().getSimpleName());
            sb.append(" ");
            sb.append(field.getName());
            sb.append(";\n");
        }
        sb.append("}");
        System.out.println(sb);
    }
}

运行结果:

在这里插入图片描述

怎么通过反射机制访问一个java对象的属性?

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

示例代码08:

public class ReflectTest07 {

    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException {

        //不使用反射修改获取对象值
        Student student1 = new Student();
        // 给属性赋值
        student1.no = 123456789;//三要素:给s对象的no属性赋值1111
                                //要素1:对象s
                                //要素2:no属性
                                //要素3:1111
        // 读属性值
        // 两个要素:获取s对象的no属性的值。
        System.out.println(student1.no);

        //获取Student对象// 使用反射机制,怎么去访问一个对象的属性。(set get)
        Class student = Class.forName("com.newstudy.javase.bean.Student");

        //实例化对象
        Object o = student.newInstance();

        //获取其中一个属性值// 获取no属性(根据属性的名称来获取Field)
        Field nofield = student.getDeclaredField("no");

        //给属性设置值
        // 给obj对象(Student对象)的no属性赋值
        /*
        虽然使用了反射机制,但是三要素还是缺一不可:
            要素1:obj对象
            要素2:no属性
            要素3:2222值
        注意:反射机制让代码复杂了,但是为了一个“灵活”,这也是值得的。
         */
        nofield.set(o,382155);// 给obj对象的no属性赋值382155

        //获取修改后的属性值
        // 读取属性的值
        // 两个要素:获取obj对象的no属性的值。
        System.out.println(nofield.get(o));

        //可以访问私有属性吗
        Field namefield = student.getDeclaredField("name");

        //打破封装
        namefield.setAccessible(true);
        namefield.set(o,"wangyilin");
        System.out.println(namefield.get(o));//此处如果不打破分装,会报权限访问异常

    }
}
public class Student {
    public int no;
    private String name;
    protected int age;
    boolean sex;
}

运行结果:

在这里插入图片描述

通过反射获取Method

示例代码09:

public class ReflectTest08 {
    public static void main(String[] args) throws ClassNotFoundException {

        //获取UserService类型
        Class userService = Class.forName("com.newstudy.javase.bean.UserService");

        //获取方法名字// 获取所有的Method(包括私有的!)
        Method[] methods = userService.getDeclaredMethods();

        for(Method method :methods){
            //获取方法的修饰符
            System.out.println(Modifier.toString(method.getModifiers()));
            //获取方法的返回类型
            System.out.println(method.getReturnType().getSimpleName());
            //获取方法的名字
            System.out.println(method.getName());
            //获取方法的参数列表(方法可以有多个参数)
            Class[] parameterTypes = method.getParameterTypes();
            for(Class parameterType : parameterTypes){
                System.out.println(parameterType.getSimpleName());
            }

        }

    }
}

运行结果:

在这里插入图片描述

通过反射反编译对象方法Method

示例代码10:

public class ReflectTest09 {
    public static void main(String[] args) throws ClassNotFoundException {

        StringBuilder s = new StringBuilder();
        //Class userServiceClass = Class.forName("com.bjpowernode.java.service.UserService");
        Class userServiceClass = Class.forName("java.lang.String");
        s.append(Modifier.toString(userServiceClass.getModifiers()) + " class "+userServiceClass.getSimpleName()+" {\n");

        Method[] methods = userServiceClass.getDeclaredMethods();
        for(Method method : methods){
            //public boolean login(String name,String password){}
            s.append("\t");
            s.append(Modifier.toString(method.getModifiers()));
            s.append(" ");
            s.append(method.getReturnType().getSimpleName());
            s.append(" ");
            s.append(method.getName());
            s.append("(");
            // 参数列表
            Class[] parameterTypes = method.getParameterTypes();
            for(Class parameterType : parameterTypes){
                s.append(parameterType.getSimpleName());
                s.append(",");
            }
            // 删除指定下标位置上的字符
            s.deleteCharAt(s.length() - 1);
            s.append("){}\n");
        }

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

运行结果:

在这里插入图片描述

重点:通过反射机制怎么调用一个对象的方法?

反射机制,让代码很具有通用性,可变化的内容都是写到配置文件当中,
将来修改配置文件之后,创建的对象不一样了,调用的方法也不同了,
但是java代码不需要做任何改动。这就是反射机制的魅力。

示例代码11:

public class ReflectTest10 {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {

        // 不使用反射机制,怎么调用方法
        // 创建对象
        UserService userService1 = new UserService();
        // 调用方法
        /*
        要素分析:
            要素1:对象userService
            要素2:login方法名
            要素3:实参列表
            要素4:返回值
         */
        boolean loginSuccess = userService1.login("admin","123");
        //System.out.println(loginSuccess);
        System.out.println(loginSuccess ? "登录成功" : "登录失败");
        //获取类型
        Class userService = Class.forName("com.newstudy.javase.bean.UserService");

        //实例化对象
        Object o = userService.newInstance();

        //获取方法
        Method login = userService.getMethod("login", String.class, String.class);
        //Method login = userService.getMethod("login", int.class);
        // 调用方法
        // 调用方法有几个要素? 也需要4要素。
        // 反射机制中最最最最最重要的一个方法,必须记住。
        /*
        四要素:
        loginMethod方法
        obj对象
        "admin","123" 实参
        retValue 返回值
         */

        //调用方法
        Object reValue = login.invoke(o, "admin", "123");
        System.out.println(reValue);
    }
}
public class UserService {

    public boolean login(String username,String password){
        if("123".equals(username) && "admian".equals(password)){
            return true;
        }
        return false;
    }

    public  void loginout(){
        System.out.println("退出系统,欢迎下次光临!");
    }

    public void login(int age){

    }
}

运行结果:

在这里插入图片描述

反编译一个类的Constructor构造方法。

示例代码12:

public class ReflectTest11 {
    public static void main(String[] args) throws Exception{
        StringBuilder s = new StringBuilder();
        Class vipClass = Class.forName("java.lang.String");
        s.append(Modifier.toString(vipClass.getModifiers()));
        s.append(" class ");
        s.append(vipClass.getSimpleName());
        s.append("{\n");

        // 拼接构造方法
        Constructor[] constructors = vipClass.getDeclaredConstructors();
        for(Constructor constructor : constructors){
            //public Vip(int no, String name, String birth, boolean sex) {
            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);
    }
}

运行结果:

在这里插入图片描述

通过反射机制调用构造方法实例化java对象。

示例代码13:

public class ReflectTest12 {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {

        //不使用反射通过构造器创建对象
        Vip vip1 = new Vip();
        Vip vip = new Vip(110,"zhagnsan","1997-09-19",true);

        //获取类型
        Class Vip2 = Class.forName("com.newstudy.javase.bean.Vip");
        //通过无参构造方法反射实例化Vip对象
        Object o = Vip2.newInstance();
        System.out.println(o);

        //通过反射调用构造器创建方法
        //调用有参构造方法
        //先获取有参构造方法
        Constructor constructor = Vip2.getDeclaredConstructor(int.class, String.class, String.class, boolean.class);
        Object o1 = constructor.newInstance(100,"jiameihong","2022-19-9",false);
        System.out.println(o1);

    }
}
public class Vip {
    private int no;
    private String name;
    private String birth;
    boolean sex;

    public Vip() {
    }

    public Vip(int no, String name, String birth, boolean sex) {
        this.no = no;
        this.name = name;
        this.birth = birth;
        this.sex = sex;
    }

    public int getNo() {
        return no;
    }

    public void setNo(int no) {
        this.no = no;
    }

    public String getName() {
        return name;
    }

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

    public String getBirth() {
        return birth;
    }

    public void setBirth(String birth) {
        this.birth = birth;
    }

    public boolean isSex() {
        return sex;
    }

    public void setSex(boolean sex) {
        this.sex = sex;
    }

    @Override
    public String toString() {
        return "Vip{" +
                "no=" + no +
                ", name='" + name + '\'' +
                ", birth='" + birth + '\'' +
                ", sex=" + sex +
                '}';
    }
}

运行结果:

在这里插入图片描述

通过反射机制怎么获取这个类的父类,和它自己已经实现的接口

示例代码14:

public class RefectTest13 {
    public static void main(String[] args) throws ClassNotFoundException {

        //获取类型
        Class stringclass = Class.forName("java.lang.String");

        //获取类型的父类
        Class superclass = stringclass.getSuperclass();
        System.out.println(superclass.getName());
        // 获取String类实现的所有接口(一个类可以实现多个接口。)
        Class[] interfaces = stringclass.getInterfaces();
        for(Class in : interfaces){
            //输出实现接口的名字
            System.out.println(in.getName());
        }

    }
}

运行结果:

在这里插入图片描述

资源绑定器

java.util包下提供了一个资源绑定器,便于获取属性配置文件中的内容。
使用以下这种方式的时候,属性配置文件xxx.properties必须放到类路径下。

示例代码15:

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

        // 资源绑定器,只能绑定xxx.properties文件。并且这个文件必须在类路径下。文件扩展名也必须是properties
        // 并且在写路径的时候,路径后面的扩展名不能写。
        ResourceBundle bundle = ResourceBundle.getBundle("classinfo2");

        ResourceBundle bundle1 = ResourceBundle.getBundle("com/newstudy/javase/bean/db");
        //通过key获取value
        String classname = bundle.getString("classname");
        System.out.println(classname);
        String classname1 = bundle1.getString("classname");
        System.out.println(classname1);
    }
}

在这里插入图片描述

在这里插入图片描述

运行结果:

在这里插入图片描述

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/94238.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!