Java之反射机制详解「建议收藏」

Java之反射机制详解「建议收藏」反射机制反射机制在java.lang.reflect.*包下反射机制相关的重要的类有java.lang.Class:代表字节码文件java.lang.reflect.Method:代表字节码中的方法字节码java.lang.reflect.Constructor:代表字节码中的构造方法字节码java.lang.reflect.Field:代表字节码中的属性字节码反射机制有什么用通过java语言中的反射机制可以操作字节码文件。有点类似于黑客(可以读和修改字节码文件。)通过反射机制可以操作代

大家好,又见面了,我是你们的朋友全栈君。

一、反射机制

反射机制在java.lang.reflect.*包下
反射机制相关的重要的类有

  • java.lang.Class:代表字节码文件,代表一个类型,代表整个类。
  • java.lang.reflect.Method:代表字节码中的方法字节码。代表类中的方法。
  • java.lang.reflect.Constructor:代表字节码中的构造方法字节码。代表类中的构造方法
  • java.lang.reflect.Field:代表字节码中的属性字节码。代表类中的成员变量(局部变量)

二、反射机制有什么用

通过java语言中的反射机制可以操作字节码文件。有点类似于黑客(可以读和修改字节码文件。)通过反射机制可以操作代码片段。(class文件)让程序更加灵活

三、获取Class文件的三种方式

1、Class.forName()

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

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

    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.forName("完整类名");
这个方法的执行会导致类加载,类加载时,静态代码块执行

2、getClass()方法

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

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

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

        Class c1 = null;
    try { 
   
         c1 = Class.forName("java.lang.String");//c1代表的是String.class文件,或者说c1代表String类型
		}catch (ClassNotFoundException e) { 
   
        e.printStackTrace();
    }

    String s = "abc";
    Class x = s.getClass();//x代表String.class字节码文件,x代表String类型
        System.out.println(c1 == x); //true(==判断的是对象的内存地址)
    }
}

在这里插入图片描述
在这里插入图片描述

3、java类型的.class属性

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

Class z = String.class; //z代表String类型
Class k = Date.class;//k代表Date类型

四、通过反射实例化对象

获取.class能干什么?

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

使用反射机制的方式来创建对象:

public class User { 
   
    public User() { 
   
        System.out.println("无参数的构造方法");
    }
}
public class ReflectTest02 { 
   
    public static void main(String[] args) { 
   

        try { 
   
            //通过反射机制,获取Class,通过Class;来实例化对象
            Class c = Class.forName("User");//c代表User类型

            //newInstance() 这个方法会调用User这个类的无参数构造方法,完成对象的创建。
            //重点是:newInstance()调用的是无参构造,必须保证无参构造是存在的!!!!
            Object obj = c.newInstance();

            System.out.println(obj);
        } catch (ClassNotFoundException e) { 
   
            e.printStackTrace();
        } catch (InstantiationException e) { 
   
            e.printStackTrace();
        } catch (IllegalAccessException e) { 
   
            e.printStackTrace();
        }
    }
}

在这里插入图片描述

五、通过读属性文件实例化对象

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

classinfo.properties文件:

className = User
import java.io.FileReader;
import java.util.Properties;

public class ReflectTest03 { 
   
    public static void main(String[] args) throws Exception{ 
   
        //通过IO流读取classinfo.properties文件
        FileReader reader = new FileReader("classinfo.properties");
        //创建属性类对象Map
        Properties pro = new Properties();
        //加载
        pro.load(reader);
        //关闭流
        reader.close();

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

        //通过反射机制实例化对象
        Class c = Class.forName(calssName);
        Object obj = c.newInstance();
        System.out.println(obj);
    }
}

六、资源绑定器

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

import java.util.ResourceBundle;

public class ThreadTest04 { 
   
    public static void main(String[] args) { 
   
        //资源绑定器,只能绑定×××.properties文件。并且这个文件必须在src/类路径下。文件扩展名必须是properties
        //并且在写路径的时候,路径的后面扩展名不能写
        ResourceBundle bundle = ResourceBundle.getBundle("classinfo3");

        String className = bundle.getString("className");
        System.out.println(className);

    }
}

七、类加载器

1、什么是加载器

专门负责加载类的命令/工具。ClassLoader

2、JDK中自带的类加载器

  • 启动类加载器rt.jar
  • 扩展类加载器ext/*.jar
  • 应用类加载器classpath

假如执行以下代码:

String s = "abc";

代码在开始执行以前,会将所需要类全部加载到JVM当中。

通过类加载器加载,看到以上代码类加载器会找string. class文件,找到就加载,那么是怎么进行加载的呢?

  • 首先通过 “启动类加载器” 加载。
    注意:启动类加载器专门加载: C: \Program Files\Java\jdk1.8.0_ 101\jre\lib\rt.jar
    rt. jar中都是JDK最核心的类库

  • 如果通过 “启动类加载器”加载不到 的时候,会通过 “扩展类加载器” 加载。
    注意:扩展类加载器专门加载: C: \Program Files\Java\jdk1.8.0_ 101\jre\lib\ext\*.jar

  • 如果 “扩展类加载器” 没有加载到,那么会通过 “应用类加载器” 加载。
    注意:应用类加载器专门加载:classpath中的类。

3、双亲委派机制

java中为了保证类加载的安全,使用了双亲委派机制优先从启动类加载器中加载,这个称为 “父”,”父”无法加载到,再从扩展类加载器中加载,这个称为**“母”。双亲委派。如果都加载不到,才会考虑从应用类加载器**中加载。直到加载到为止。

八、Field

1、获取Field

反射属性Field,Field翻译为字段,其实就是属性/成员。
4个Field,分别采用了不同的访问控制权限修饰符

public class Student { 
   
    //4个Field,分别采用了不同的访问控制权限修饰符
    public int no;
    private String name;
    protected int age;
    boolean sex;
}
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

public class ReflectTest05 { 
   
    public static void main(String[] args) throws Exception{ 
   
        //获取整个类
        Class studentClass = Class.forName("Student");
        //获取类中所有的public修饰的Field
        Field[] fields = studentClass.getFields();
        System.out.println(fields.length);
        //取出这个Field的名
        Field f = fields[0];
        String fieldName = f.getName();
        System.out.println(fieldName);

        //获取所有的Field
        Field[] fields1 = studentClass.getDeclaredFields();
        System.out.println(fields1.length);

        for (Field field : fields1) { 
   
            //获取属性的修饰符
            int i = field.getModifiers();//返回的修饰符是一个数字,每个数字都是修饰符的代号
            System.out.println(i);
            //将代号转化为字符串
            String modifiersString = Modifier.toString(i);
            System.out.println(modifiersString);
            //获取属性的类型
            Class fieldType = field.getType();
            String fName = fieldType.getName();
            System.out.println(fName);
            //获取属性的名字
            System.out.println(field.getName());
        }
    }
}

在这里插入图片描述

2、反编译Field

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

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

public class ReflectTese06 { 
   
    public static void main(String[] args) throws Exception{ 
   
        //创建这个是为为了拼接字符串。
        StringBuilder s = new StringBuilder();

        Class studentClass = Class.forName("Student");

        s.append(Modifier.toString(studentClass.getModifiers())+ " class "+ studentClass.getSimpleName() + "Student{\n");

        Field[] fields = studentClass.getDeclaredFields();

        for (Field field : fields) { 
   
            s.append("\t");
            s.append(Modifier.toString(field.getModifiers()));
            s.append(" ");
            s.append(field.getType().getSimpleName());
            s.append(" ");
            s.append(field.getName());
            s.append(";\n");
        }
        s.append("}");
        System.out.println(s);

    }
}

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

3、通过反射机制访问java对象属性(重点掌握)

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

import java.lang.reflect.Field;

public class ReflectTest07 { 
   
    public static void main(String[] args) throws Exception { 
   
        //不使用反射机制怎样访问一个对象的属性
        Student s = new Student();

        //给属性赋值
        s.no = 1111;

        //读属性值
        System.out.println(s.no);

        //使用反射机制,访问一个对象的属性
        Class studentClass = Class.forName("Student");
        Object obj = studentClass.newInstance();//obj就是Student对象。(底层调用无参数构造方法)

        //获取no属性(根据属性的名称来获取Field)
        Field noField = studentClass.getDeclaredField("no");

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

        //读取属性的值
        //两个要素:获取obj对象的no属性的值。
        System.out.println(noField.get(obj));
    }
}

在这里插入图片描述

九、Method

1、可变长度参数

int... args这就是可变长度参数
语法是:类型... (注意:一定是3个点)

  • 1、可变长度参数要求的参数个数是:0~N个
  • 2、可变长度参数在参数列表中必须在最后一个位置上,而且可变长度参数只能有一个。
public class ArgsTest { 
   
    public static void main(String[] args) { 
   
        m();
        m(10);
        m(10,20);

    }
    public static void m(int... args){ 
   
        System.out.println("m方法执行了!");
    }
}

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

2、反射Method

public class User { 
   

	public int id;
	private String name;	
	String address;
	protected int sno;
	public User() { 
   }
	
	public User(int id, String name, String address, int sno) { 
   
		super();
		this.id = id;
		this.name = name;
		this.address = address;
		this.sno = sno;
	}
	
	public void study() { 
   
		System.out.println("study...");
	}
	
	public void eat(int a,String b) { 
   
		System.out.println("eat...");
	}
}

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public class Test { 
   
	public static void main(String[] args) throws Exception { 
   
		// 获取类
        Class usrClass = Class.forName("com.lzj.reflect.pojo.User");

        // 获取所有的Method(包括私有)
        Method[] methods = usrClass.getDeclaredMethods();
        
        // 遍历Method
        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());
            }
        }
	}
}

3、反编译Method

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public class Test { 
   
	public static void main(String[] args) throws Exception { 
   
		StringBuilder s = new StringBuilder();
        Class userClass = Class.forName("com.lzj.reflect.pojo.User");
        s.append(Modifier.toString(userClass.getModifiers()) + " class "+userClass.getSimpleName()+" {\n");

        Method[] methods = userClass.getDeclaredMethods();
        for(Method method : methods){ 
   
        	
            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(",");
            }
            
            if(parameterTypes.length > 0) { 
   
            	s.deleteCharAt(s.length() - 1);
            }
            
            s.append("){}\n");
        }
        s.append("}");
        System.out.println(s);
	}
}

4、通过反射机制调用方法(重点掌握)

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

public class Test { 
   

    public static void main(String[] args) { 
   
        try { 
   
            Class<?> clazz = Class.forName("com.lgz.Test");
            Method method = clazz.getMethod("run");
            method.invoke(clazz.newInstance());

            Method methodPrivate = clazz.getDeclaredMethod("eat");
            methodPrivate.setAccessible(true);//调private方法
            methodPrivate.invoke(clazz.newInstance());

            Method methodStatic = clazz.getMethod("work");
            methodStatic.invoke(null);

            Method method1 = clazz.getMethod("speak", int.class, String.class);
            method1.invoke(clazz.newInstance(), 22, "小明");
        } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException e) { 
   
            e.printStackTrace();
        }

    }

    public static void work() { 
   
        System.out.println("work() 方法被调用...");
    }

    private void eat() { 
   
        System.out.println("eat() 方法被调用...");
    }

    public void run() { 
   
        System.out.println("run() 方法被调用...");
    }

    public void speak(int age, String name) { 
   
        System.out.println("speak() 方法被调用.... age = " + age + " name= " + name);
    }
}

十、Constructor

1)得到某个类所有的构造方法

Constructor[] constructors = Class.forName("java.lang.String").getConstructors();
2)得到某一个构造方法

Constructor constructor1 = String.class.getConstructor(StringBuffer.class);
实例

Constructor constructor = String.class.getConstructor(StringBuffer.class);
String str = (String) constructor.newInstance(new StringBuffer("abc"));
System.out.println(str.charAt(2));

打印结果:c

十一、获取父类和父接口

public class ReflectTest08 { 
   
    public static void main(String[] args) throws Exception{ 
   
        //String举例
        Class stringClass = Class.forName("java.lang.String");

        //获取String的父类
        Class superClass = stringClass.getSuperclass();
        System.out.println(superClass);

        //获取String类实现的所有接口(一个类可以实现多个接口)
        Class[] interfaces = stringClass.getInterfaces();
        for (Class anInterface : interfaces) { 
   
            System.out.println(anInterface);
        }
    }
}

在这里插入图片描述

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

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/157036.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • JAVA中&&和&、||和|的区别?「建议收藏」

    JAVA中&&和&、||和|的区别?「建议收藏」问题一:JAVA中&amp;&amp;和&amp;、||和|(短路与和逻辑与、短路或和逻辑或)的区别?首先名称是不同的&&逻辑与  ||逻辑或  它们都是逻辑运算符& 按位与  | 按位或  它们都是位运算符if(a==1&&b==2) 这是说既要满足a=1也要满足b=2if(a==1||b==2) 这是说或者满足a=1或者要满足b=2而a&b或者a|b则是二进制的与…

  • Browserify_browses

    Browserify_browses1.Browserify是什么?“Browserifyletsyourequire(‘modules’)inthebrowserbybundlingupallofyourdependencies.”-Browserify.org出自官网的一句话。用通俗的话讲就是:browserify是一个浏览器端代码模块化工具,可以处理模块之间的依赖关系,让服务器端的

    2022年10月25日
  • seekg的应用案例

    seekg的应用案例在学习C++文件流控制时(链接)我们知道C++有一个标准库fstream该库定义了三个数据类型ofstreamifstream和fstream在练习相应的案例时,seekg()函数掌握的不是很好,后经过多次尝试,可以正常调用了代码如下:#include<fstream>#include<iostream>usingnamespacestd;intmain(){chardata[100];////以写模式打开文件

  • Mac Charles抓包配置

    Mac Charles抓包配置MacCharles抓包配置1.基本安装直接在官网下载,需要破解的同学可以使用这个,我也是借花献佛,这样你可以时刻来抓包了,RegisteredName:https://zhile.ioLicenseKey:48891cf209c6d32bf4找不到在哪设置license的同学看下图:2CA证书安装点击安装后,会自动打开钥匙串,一定要记住进入钥匙串,点击Charles…

  • .ipynb与.py文件快速互转[通俗易懂]

    .ipynb与.py文件快速互转[通俗易懂].ipynb代码转为.py:法一:在xxx.ipynb所在目录下,打开终端,并输入命令:jupyternbconvert–toscriptxxx.ipynb其中xxx.ipynb是要转换文件的名字,转换后在该目录下出现xxx.py文件(有的版本是xxx.txt文件,再改下后缀即可)。法二:在Jupyternotebook或GoogleColab中打开ipynb文件,然后选择file–downloadas–pythonfile.py代码转为.ipynb:

  • Java 如何抛出异常、自定义异常[通俗易懂]

    一、异常的抛出1、定义:一个方法不处理这个异常,而是调用层次向上传递,谁调用这个方法,这个异常就由谁来处理。2、throw:将产生的异常抛出(强调的是动作),抛出的既可以是异常的引用,也可以是异常对象。(位置:方法体内)3、throws:如果一个方法可能会出现异常,但没有能力处理这种异常,可以在方法声明处用throws子句来声明抛出异常。用它修饰的方法向调用者表明该方法可能会抛出异

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号