前言
在java中我们使用类的方法时,一般的操作是new一个对象,然后使用这个对象直接调用方法。但是你知道吗?这种形式的调用存在着缺点,试想一下,如果我们有多个Java Bean类,里面的参数类型和个数都相同,它们都有set方法。使用这些Bean类时,我们都需要分别调用它们各自独有的set方法,这种方式的使用增加了我们的代码量,那么我们有什么方法可以减少这些代码量吗?答案肯定是有的,没错,正是我们这次的课题,java 放射,反射的使用除了可以减少不必要的代码量,还可以执行class类私有、保护的变量和方法。
反射的原理
我们知道,java程序的执行过程有两个阶段,它们分别为编译阶段和运行阶段。在编译阶段,jdk会将.java文件编译成.class字节码文件;在运行阶段,java虚拟机(jvm)会去调用业务逻辑对应需要的.class字节码文件,生成对应的class对象,然后调用其中的属性和方法完成业务逻辑。反射的执行是在运行阶段,它主动让jvm去加载.class文件,生成所需要的class对象,使用该class对象调用其属性和方法完成业务逻辑。因此反射可以动态的改变java程序中的属性
反射的使用
获取class对象
方法1://使用无参构造获取class类
Class clas = null;
try {
//获取class类
//clas = new persion().getClass();
clas = Class.forName("ObjectPackage.exercise02.persion");
//生成class对象
persion pr = (persion) clas.newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}这个方法获取到的class对象,它调用的是public修饰的无参构造方法,因此在我们自定义类中我们必须要有public修饰的无参构造方法,如果是private修饰的无参构造方法会抛出ava.lang.IllegalAccessException:Class ObjectPackage.xx.xxr can not access a member of class ObjectPackage.xx.xx with modifiers “private”
at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
at java.lang.Class.newInstance(Class.java:436)
如果没有无参构造方法则会抛出异常:
java.lang.InstantiationException:ObjectPackage.xx.xx
at java.lang.Class.newInstance(Class.java:427) 方法2://使用带参构造获取class对象
Class clas = null;
persion pr = null;
try {
//获取class类
clas = Class.forName("ObjectPackage.exercise02.persion");
//获取构造方法
//Constructor c = clas.getConstructor(String.class,String.class); //获取到的是两个String类型的参数构造方法(public)
//Constructor[] constructor = clas.getConstructors(); //获取的都是public修饰的构造方法
Constructor[] constructor = clas.getDeclaredConstructors(); //获取的全部构造方法(private、protected、public修饰)
//生成class对象
pr = (persion) constructor[0].newInstance("小明","广东xxxxxxx","2020-09-09");
System.out.println(pr);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}获取构造函数的四种方法:
1、getConstructor(Class… parameterTypes):根据构造函数的参数,返回一个具体的具有public属性的构造函数
2、getDeclaredConstructor(Class… parameterTypes):根据构造函数的参数,返回一个具体的构造函数(public和非public属性)
3、getConstructors():返回所有具有public属性的构造函数数组
4、getDeclaredConstructors():返回该类中所有的构造函数数组(public和非public属性)
注:在获取到的构造函数数组中,构造函数在数组中的存储位置和我们在编程自定类中的构造函数的顺序有关,编程的构造函数越往后,它存储的位置越靠前
获取class类的方法并执行
Class clas = null;
//获取class类
clas = Class.forName("ObjectPackage.exercise02.persion");
//生成class对象
pr = (persion) constructor[0].newInstance("小明","广东xxxxxxx","2020-09-09");
//获取class对象的方法
/* Method method = clas.getMethod("nation",String.class); //获取nation方法
//执行nation方法
method.invoke(pr,"中国");*/
//Method[] methods = clas.getMethods(); //获取全部public的方法,包括Object类的方法
Method[] methods = clas.getDeclaredMethods(); //获取class对象的public、private、protected修饰的所有方法(不包括Object类的方法)
/**methods方法存储的顺序:
* void ObjectPackage.exercise02.persion.type(java.lang.String)
* public java.lang.String ObjectPackage.exercise02.persion.toString()
* public void ObjectPackage.exercise02.persion.setTime(java.lang.String)
* public void ObjectPackage.exercise02.persion.setAddress(java.lang.String)
* public void ObjectPackage.exercise02.persion.setPersionName(java.lang.String)
* void ObjectPackage.exercise02.persion.author(java.lang.String)
* public void ObjectPackage.exercise02.persion.nation(java.lang.String)
*/
//执行方法
//打破封装,实际上setAccessible是启用和禁用访问安全检查的开关,并不是为true就能访问,为false就不能访问
//由于jdk的安全检查耗时较多,所以通过setAccessible(true)的方式关闭安全检查就可以达到提升反射速度的目的
//xxxx.setAccessible(true); //没有设置这句话时private、protected修饰的类属性不能设值,如设置抛出异常
methods[0].invoke(pr,"文学类");class类获取方法的四种方法:
getMethod(String name, Class… parameterTypes):根据方法名和参数,返回一个具体的具有public属性的方法,可以获取Object类的方法
getMethods():返回所有具有public属性的方法数组,包括Object类的方法
getDeclaredMethod(String name, Class… parameterTypes):根据方法名和参数,返回一个具体的方法(public和非public属性)
getDeclaredMethods():返回该类中的所有的方法数组(public和非public属性)
其中parameterTypes是方法参数的类型,如果是String,那么它是String.class;如果是int,那么它是int.class;如果是Integer,那么它是Integer.class
获取class类的接口和接口方法并执行方法
Class clas = null;
//获取class类
clas = Class.forName("ObjectPackage.exercise02.persion");
//获取class接口
Class[] interfaces = clas.getInterfaces();
//创建class对象
persion c = (persion) clas.newInstance();
//获取接口的方法
Method[] interfacesMethods = interfaces[0].getDeclaredMethods(); //和使用获取class的方法一样
//执行接口方法
interfacesMethods[0].invoke(c,"韩国");
获取class类的父类和父类的方法并执行方法
Class clas = null;
//获取class类
clas = Class.forName("ObjectPackage.exercise02.persion");
//获取class类的父类
Class superClass = clas.getSuperclass();
//创建class类对象
persion p = (persion) clas.newInstance();
//获取class类的父类方法
Method[] methods=superClass.getDeclaredMethods();
//执行方法
/**父类的所有方法存储顺序:
* abstract void ObjectPackage.exercise02.book.type(java.lang.String)
* public void ObjectPackage.exercise02.book.execute(java.lang.String)
* private void ObjectPackage.exercise02.book.press(java.lang.String)
* abstract void ObjectPackage.exercise02.book.author(java.lang.String)
*/
methods[1].invoke(p,"");在(3)、(4)中不能创建接口和父类的对象,因为在java中接口和抽象类不能被实例化,但是可以通过它的继承类来调用它们public方法(接口中的所有方法和属性都是public)、abstract方法,但是不能调用它们的private方法
获取class类的属性并改变属性的值
Class clas = null;
persion pr = null;
//获取class类
clas = Class.forName("ObjectPackage.exercise02.persion");
//创建class类的对象
pr = (persion) clas.newInstance();
//获取class类的属性
Field[] field = clas.getDeclaredFields();
/**属性的存储顺序:
* private java.lang.String ObjectPackage.exercise02.persion.persionName
* private java.lang.String ObjectPackage.exercise02.persion.address
* private java.lang.String ObjectPackage.exercise02.persion.time
* private java.lang.String ObjectPackage.exercise02.persion.bookName
* private int ObjectPackage.exercise02.persion.bookPrice
*/
//改变属性的值
//打破封装,实际上setAccessible是启用和禁用访问安全检查的开关,并不是为true就能访问,为false就不能访问
//由于jdk的安全检查耗时较多,所以通过setAccessible(true)的方式关闭安全检查就可以达到提升反射速度的目的
field[1].setAccessible(true);//没有设置这句话时private、protected修饰的类属性不能设值,如设置抛出异常
field[1].set(pr, "2020-8-20 16:58");
System.out.println(pr);class类获取属性的四种方法:
getField(String name):根据变量名,返回一个具体的具有public属性的成员变量
getFields():返回具有public属性的成员变量的数组
getDeclaredField(String name):根据变量名,返回一个成员变量(public和非public属性)
getDeclaredFields():返回所有成员变量组成的数组(public和非public属性)
获取方法的修饰符、参数类型
Class clas = null;
persion pr = null;
//获取class类
clas = Class.forName("ObjectPackage.exercise02.persion");
//创建class类的对象
pr = (persion) clas.newInstance();
//获取class类的方法
Method[] methods = clas.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
// methods[i].getReturnType()获取方法的返回类型
//methods[i].getExceptionTypes(); 获取这个方法执行后抛出的异常
//获取参数类型
Class[] paramenterType = methods[i].getParameterTypes();
System.out.print("方法: ");
//获取修饰符
int mo = methods[i].getModifiers();
//根据修饰符整数获取相应的修饰符
System.out.print(Modifier.toString(mo)+" ");
//获取方法名
System.out.print(methods[i].getName());
System.out.print("(");
//打印参数
for (int j = 0; j < paramenterType.length; j++) {
System.out.print(paramenterType[j]+" arg"+j);
if (j clas = null;
animal an= null;
try {
//获取class类
clas = Class.forName(backageName);
//创建class类的对象
an = (animal) clas.newInstance();
System.out.println(clas.getName()+":"); //获取类名
//获取父类和接口
Factory.getSuperAnInterface(clas);
//获取变量并改变变量的值
Factory.setFields(clas,an,modifierArray);
//获取class类的构造方法
Factory.getConstruction(clas);
//获取class类的成员方法并执行
Factory.getMethods(clas,an,parameterType);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return an;
}
/**
* 获取class类的构造方法
*/
public static void getConstruction(Class clas){
Constructor[] constructors = clas.getDeclaredConstructors();
System.out.println("构造方法:");
for (int i = 0; i < constructors.length; i++) {
System.out.println(constructors[i]);
}
}
/**
*获取class类中的全部成员方法并打印
*/
public static void getMethods(Class clas,animal an,String[] arrays){
//获取方法(public和非public)
Method[] methods = clas.getDeclaredMethods();
System.out.println("成员方法:");
for (int i = 0; i < methods.length; i++) {
//获取方法的参数
Class[] parameterTypes = methods[i].getParameterTypes();
//获取方法的修饰符的数字
int mod = methods[i].getModifiers();
//打印
System.out.print(Modifier.toString(mod)+" "); //根据数字打印修饰符
System.out.print(methods[i].getName()+"("); //打印方法名
for (int j = 0; j < parameterTypes.length; j++) {
System.out.print(parameterTypes[j]+" arg"+j); //打印参数
if (j < parameterTypes.length-1)
System.out.print(",");
}
System.out.println("){}"); //打印结束符
//修饰符判断
if (Modifier.toString(mod).equals("private")){
//打破封装
methods[i].setAccessible(true);
}
/**
* public toString(){}
* public color(class java.lang.String arg0){}
* public eat(class java.lang.String arg0){}
* private Only(){}
*/
//执行方法
try{
if (parameterTypes.length > 0){ //判断方法是否有参数
methods[i].invoke(an,arrays[index]); //执行带有参数的方法
index++;
}else {
methods[i].invoke(an); //执行没有参数的方法
}
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
/**
*获取class类的接口和父类
*/
public static void getSuperAnInterface(Class clas){
//获取父类和接口
System.out.print("父类:"+clas.getSuperclass()+"\t接口:");
Class[] interfaces = clas.getInterfaces(); //获取接口
for (int i = 0; i < interfaces.length; i++) {
System.out.print(interfaces[i]+"\t");
}
System.out.println();
}
/**
*获取class类的属性并改变属性的值
*/
public static void setFields(Class clas,animal an,String[] arg){
/**
* private java.lang.String ObjectPackage.exercise03.cat.food
* private java.lang.String ObjectPackage.exercise03.cat.color
*/
Field[] fields = clas.getDeclaredFields(); //获取属性(public和非public)
for (int i = 0; i < fields.length; i++) {
//判断属性的修饰符
if (Modifier.toString(fields[i].getModifiers()).equals("private")){
//打破封装,关闭检查装置
fields[i].setAccessible(true);
}
try {
//改变属性值
fields[i].set(an,arg[i]);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
String[] catModiferArray = new String[]{"煎鱼","黑色"}; //变量值
String[] catParameter = new String[]{"蒸鱼","亮黑色"}; //参数
String[] dogModiferArray = new String[]{"鸡骨头","黑白色"}; //变量值
String[] dogParameter = new String[]{"猪骨头","亮白色"}; //参数
System.out.println(Factory.getInstance(new cat(),catModiferArray,catParameter));
System.out.println("\n");
System.out.println(Factory.getInstance("ObjectPackage.exercise03.dog",dogModiferArray,dogParameter));
}
}
执行结果:

其中Class.forName()参数的参数是类的具体路径,即包名+类名
最后
感谢你看到这里,看完有什么的不懂的可以在评论区问我,觉得文章对你有帮助的话记得给我点个赞,每天都会分享java相关技术文章或行业资讯,欢迎大家关注和转发文章!