java反射机制
参考博客:https://blog.csdn.net/sinat_38259539/article/details/71799078
一、反射的概述
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法(指的是所有,任何的私有,静态,等等……都可以被获取到);对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法,所以先要获取到每一个字节码文件对应的Class类型的对象。
以上的总结就是什么是反射
反射就是把java类中的各种成分映射成一个个的Java对象——很重要,后面的事情都是根据这个目标来进行的。
例如:一个类有:成员变量、方法、构造方法、包等等信息,利用反射技术可以对一个类进行解剖,把一个个组成部分映射成一个个对象。
(其实:一个类中这些成员方法、构造方法、在加入类中都有一个类来描述)
如图是类的正常加载过程:反射的原理在与class对象。
熟悉一下加载的时候:Class对象的由来是jvm虚拟机通过去本地磁盘找到相应从class文件,将class文件读入内存中并为之创建一个Class对象。
如图:
二、下面,我们先来介绍一下Class类(基于1.7的API)
Class
类的实例表示正在运行的 Java 应用程序中的类和接口。也就是jvm中有N多的实例每个类都有该Class对象。(包括基本数据类型)
Class
没有公共构造方法。Class
对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的defineClass
方法自动构造的。也就是这不需要我们自己去处理创建,JVM已经帮我们创建好了。
Class类中有64个方法,具体用到哪个再详细介绍哪个。
三.获取一个类的Class对象的3种方法
3.1 Object类下的getClas()方法
3.2任何的数据类型(包括基本数据类型)都有一个“静态的”calss属性
3.3通过Class类的静态方法: Class.forName(String className) //className是全路径名——包名+类名,但是并不是磁盘的绝对路径 (常用)
public void f1() throws ClassNotFoundException {
//获取class对象
/*
* 1.通过对象来获取字节码对象
* 缺点:我对象都有了。。。还要字节码对象做啥子*/
Student student=new Student(); //这样一创建 其实创建了2个对象 student对象 以及Student类对应的class对象
Class stuClass1=student.getClass();
/*
* 2.通过每个类型都有的“静态的”class属性来获取
* 缺点:依赖性太强,得导入相应的类的包;否则编译不过*/
Class stuClass2=Student.class;
/*
* 常用,需要掌握
* Class forName(String className) 注意:全限类名*/
Class stuClass3=Class.forName("com.kylin.FanShe.Student");
System.out.println(stuClass1==stuClass2);
System.out.println(stuClass1==stuClass3);
System.out.println(stuClass2==stuClass3);
//结果3个都是true,说明——一个类只有一个class对象
}
四,通过class对象获取一个类的构造方法以及通过构造方法创建对象
通过calss对象来获取构造器,主要分获取一批构造器和获取单个构造器; 1.获取一批构造器
1.1获取所有 “共有的”构造器
public Constructor[] getConstructors();所有"公有的"构造方法;这个Constructor是 反射包里的 java.lang.reflect.Constructor
1.2获取所有的(任何访问类型--包访问/共有/保护/私有)的构造器
public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)
2.获取一个构造器(如果是无参构造器,那么参数写null或者不写)
2.1获取单个 共有的构造器
public Constructor getConstructor(Class ... paramterTypes) ;//获取单个的“公有的”构造方法(参数是构造器参数的类型;注意是构造器参数的类型!)
2.2.获取单个任何类型的构造器
public Constructor getDeclaredConstructor(Class... parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有
3.通过构造方法对象来创建对象
Object newInstance(构造器的参数) //构造方法的一个方法,用于创建对象,返回Object类型 需要自己手动的类型转换一下
Student.java
package com.kylin.FanShe; public class Student { private String name; //姓名 private String sex; //性别 private String id; //学号 private int age; //年龄 //4种访问类型的构造器 包访问类型 共有 保护 私有 Student(){ System.out.println("包访问类型的构造器"); } //共有 public Student(String name){ this.name=name; System.out.println("共有访问类型的构造器,且名字为:"+name); } public Student(String id, int age){ this.id=id; this.age=age; System.out.println("共有的构造器,id,年龄"+id+" "+age); } //保护 protected Student(String name,String sex){ this.name=name; this.sex=sex; System.out.println("保护类型的构造器,且名字为:"+name+"性别:"+sex); } //私有 private Student(String name,String id,String sex){ this.name=name; this.id=id; this.sex=sex; System.out.println("私有的构造器:"+name+" "+id+" "+sex); } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public String getId() { return id; } public void setId(String id) { this.id = id; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
获取构造方法代码: (一开始引入了:import java.lang.reflect.Constructor)
public static void main(String []args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { // 获取class对象 Class clazz=Class.forName("com.kylin.FanShe.Student"); //1.1获取所有的公有的构造器 Constructor []constructors=clazz.getConstructors(); for (Constructor constructor:constructors){ System.out.println(constructor); } /* * 结果: * public com.kylin.FanShe.Student(java.lang.String,int) public com.kylin.FanShe.Student(java.lang.String) * */ System.out.println("-----------------------------美丽的分割线-------------------------------"); //1.2获取所有任何类型的构造方法 Constructor []allConstructors=clazz.getDeclaredConstructors(); for (Constructor constructor:allConstructors){ System.out.println(constructor); } /* * 结果: * private com.kylin.FanShe.Student(java.lang.String,java.lang.String,java.lang.String) protected com.kylin.FanShe.Student(java.lang.String,java.lang.String) public com.kylin.FanShe.Student(java.lang.String,int) public com.kylin.FanShe.Student(java.lang.String) com.kylin.FanShe.Student()*/ System.out.println("-----------------------------美丽的分割线-------------------------------"); //2.1获取某个公有的 构造器 Constructor myPubConstructor=clazz.getConstructor(String.class); //参数是:构造器参数的类型 System.out.println(myPubConstructor); // 输出:public com.kylin.FanShe.Student(java.lang.String) System.out.println("-----------------------------美丽的分割线-------------------------------"); //2.2获取某个任何类型的构造器 Constructor myEveConstructor=clazz.getDeclaredConstructor(String.class,String.class,String.class); System.out.println(myEveConstructor); // 输出:private com.kylin.FanShe.Student(java.lang.String,java.lang.String,java.lang.String) //通过构造方法来创建对象 Student student= (Student) myPubConstructor.newInstance("name"); System.out.println(student.getName()); // 这样会抛出异常,因为一个私有的构造器不能被用来创建对象 所以需要设置访问权限 // Student student1= (Student) myEveConstructor.newInstance("1","22","333"); // System.out.println(student1.getName()+" "+student1.getId()+" "+student1.getSex()); myEveConstructor.setAccessible(true);//暴力访问 Student student1= (Student) myEveConstructor.newInstance("111","222","333"); System.out.println(student1.getName()+" "+student1.getSex()+" "+student1.getId()); }
输出结果:
public com.kylin.FanShe.Student(java.lang.String,int) public com.kylin.FanShe.Student(java.lang.String) -----------------------------美丽的分割线------------------------------- private com.kylin.FanShe.Student(java.lang.String,java.lang.String,java.lang.String) protected com.kylin.FanShe.Student(java.lang.String,java.lang.String) public com.kylin.FanShe.Student(java.lang.String,int) public com.kylin.FanShe.Student(java.lang.String) com.kylin.FanShe.Student() -----------------------------美丽的分割线------------------------------- public com.kylin.FanShe.Student(java.lang.String) -----------------------------美丽的分割线------------------------------- private com.kylin.FanShe.Student(java.lang.String,java.lang.String,java.lang.String) 共有访问类型的构造器,且名字为:name name 私有的构造器:111 222 333 111 333 222
五,通过class对象获取成员变量并调用
teacher.java
package com.kylin.FanShe; public class Teacher { public String name; public String name1; String school; protected String sex; private int age; public Teacher(){ } @Override public String toString(){ return "Teacher[name="+name+",school=" +school+ ",sex="+sex+",age="+age+",name1="+name1; } }
测试获取成员变量并且调用
package com.kylin.FanShe; import org.junit.Test; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; //获取成员变量 public class GetFiled { /* * 获取成员变量并调用: * * 1.批量的 * 1).Field[] getFields():获取所有的"公有字段" * 2).Field[] getDeclaredFields():获取所有字段,包括:私有、受保护、默认、公有; * 2.获取单个的: * 1).public Field getField(String fieldName):获取某个"公有的"字段; * 2).public Field getDeclaredField(String fieldName):获取某个字段(可以是私有的) * * 设置字段的值: * Field --> public void set(Object obj,Object value): * 参数说明: * 1.obj:要设置的字段所在的对象; * 2.value:要为字段设置的值*/ public static void main(String []args) throws ClassNotFoundException, NoSuchFieldException,
NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { //获取class对象 Class clazz=Class.forName("com.kylin.FanShe.Teacher"); //获取所有的共有的属性 Field []fields=clazz.getFields(); for (Field field:fields){ System.out.println(field); } System.out.println("-----------------------------美丽分割线-------------------------"); //获取所有的属性 (任何访问类型) Field []allFields=clazz.getDeclaredFields(); for (Field field:allFields){ System.out.println(field); //这也是一个对象来的 } System.out.println("-----------------------------美丽分割线-------------------------"); //获取指定的共有属性并且赋值给对象 Field nameField=clazz.getField("name"); System.out.println(nameField); Teacher teacher= (Teacher) clazz.getConstructor(null).newInstance(); nameField.set(teacher,"苏桃"); System.out.println(teacher); System.out.println("-----------------------------美丽分割线-------------------------"); //获取指定的任何类型(这里获取私有来测试)的属性 并且赋值给对象 Field priAgeField=clazz.getDeclaredField("age"); //女人的年龄是秘密嘛 System.out.println(priAgeField); //私有的需要设置权限 这里是暴力访问 priAgeField.setAccessible(true); priAgeField.set(teacher,20); System.out.println(teacher); } }
输出:
public java.lang.String com.kylin.FanShe.Teacher.name public java.lang.String com.kylin.FanShe.Teacher.name1 -----------------------------美丽分割线------------------------- public java.lang.String com.kylin.FanShe.Teacher.name public java.lang.String com.kylin.FanShe.Teacher.name1 java.lang.String com.kylin.FanShe.Teacher.school protected java.lang.String com.kylin.FanShe.Teacher.sex private int com.kylin.FanShe.Teacher.age -----------------------------美丽分割线------------------------- public java.lang.String com.kylin.FanShe.Teacher.name Teacher[name=苏桃,school=null,sex=null,age=0,name1=null -----------------------------美丽分割线------------------------- private int com.kylin.FanShe.Teacher.age Teacher[name=苏桃,school=null,sex=null,age=20,name1=null
六,使用class对象获取成员方法并且调用(调用肯定得是该类型的对象或者子类才可以调用)
people.java
package com.kylin.FanShe; public class People { public void show1(String s){ System.out.println("调用了:公有的,String参数的show1(): s = " + s); } protected void show2(){ System.out.println("调用了:受保护的,无参的show2()"); } void show3(){ System.out.println("调用了:默认的,无参的show3()"); } private String show4(int age) { System.out.println("调用了,私有的,并且有返回值的,int参数的show4(): age = " + age); return "abcd"; } }
获取成员方法并且调用
/* 获取成员方法并调用 * 1.批量的: * public Method[] getMethods():获取所有"公有方法";(包含了父类的方法也包含Object类) * public Method[] getDeclaredMethods():获取所有的成员方法,包括私有的(不包括继承的) 2.获取单个的: * public Method getMethod(String name,Class<?>... parameterTypes): * 参数: * name : 方法名; * Class ... : 形参的Class类型对象 public Method getDeclaredMethod(String name,Class<?>... parameterTypes) * 调用方法: * Method --> public Object invoke(Object obj,Object... args): * 参数说明: * obj : 要调用方法的对象,这个对象一定要是和方法同一类/或者子类也应该可以的; * args:调用方式时所传递的实参; ): */ public static void main(String[] args) throws Exception { //1.获取Class对象 Class clazz = Class.forName("com.kylin.FanShe.People"); //2.获取所有公有方法 System.out.println("***************获取所有的”公有“方法*******************"); Method[] methodArray = clazz.getMethods(); for (Method m : methodArray) { System.out.println(m); } System.out.println("***************获取所有的方法,包括私有的*******************"); methodArray = clazz.getDeclaredMethods(); for (Method m : methodArray) { System.out.println(m); } System.out.println("***************获取公有的show1()方法*******************"); Method pubMethod = clazz.getMethod("show1", String.class); System.out.println(pubMethod); //实例化一个Student对象 People people= (People) clazz.getConstructor().newInstance(); pubMethod.invoke(people,"刘德华"); //方法对象被调用执行,则输出了那句话 System.out.println("***************获取私有的show4()方法******************"); Method priMethod = clazz.getDeclaredMethod("show4", int.class); System.out.println(priMethod); priMethod.setAccessible(true);//解除私有限定 System.out.println("11111111111111111111111"); //抛出异常 可能是得该类的对象才可以调用 // Object object=new Object(); People people1= (People) clazz.getConstructor().newInstance(); priMethod.invoke(people1, 20);//需要两个参数,一个是要调用的对象(获取有反射),一个是实参 // System.out.println("返回值:" + result); }
总结:
反射可以获取到一个类的所有的东西——静态的非静态的,私有的公有的……只要是在类中的,或者继承于父类的东西,都可以被获取成一个对象,然后进行调用……当然也可以获取main方法等等,这里主要介绍了通过类的字节码对象获取构造方法,成员变量,方法等……反射同时也是很多框架实现的基础,要好好琢磨……