1.什么是Java反射
指在JAVA程序运行状态中
- 给定一个类(Class)对象,通过反射获取这个类(Class)对象的所有成员结构
- 给定一个具体的对象,能够动态的调用它的方法及任意属性值进行获取和赋值
这种动态获取类的内容,创建对象,以及动态调用对象的方法及操作属性的机制就是Java反射机制 。
示例
public class Dog {
public String color;
public void bark() {
System.out.println("Dog barking");
}
public static void main(String[] args) throws Exception {
Class clazz = Dog.class;
// 创建对象
Dog dog = (Dog) clazz.newInstance();
// 获取包的结构
System.out.println(clazz.getSuperclass()); // 父类
System.out.println(clazz.getClassLoader());// 类的classlader
System.out.println(clazz.getPackage());// 包名
System.out.println(clazz.getName());//类名
Method method = clazz.getDeclaredMethod("bark");
method.invoke(clazz.newInstance());
}
}
运行结果:
class java.lang.Object
sun.misc.Launcher$AppClassLoader@18b4aac2
package com.liulin.reflect.demo1
com.liulin.reflect.demo1.Dog
Dog barking
2.反射的优劣势
优势
增加程序的灵活性,避免将固有的逻辑程序写死到代码里
代码简洁,可读性强,可提高代码的复用率
缺点
相较直接调用在量大的情况下反射性能下降
存在一些内部暴露和安全隐患
反射慢在哪里
- 寻找类Class 字节码的过程
- 安全管理机制的权限验证等等
- 若需要调用native 方法调用时JNI 接口的使用
3.反射初体验
Office接口
public interface Office {
public void toPDF();
}
Excel类
public class Excel implements Office {
@Override
public void toPDF() {
System.out.println("Excel to PDF");
}
}
PPT类
public class PPT implements Office {
@Override
public void toPDF() {
System.out.println("PPT to PDF");
}
}
Word类
public class Word implements Office {
@Override
public void toPDF() {
System.out.println("Word to PDF");
}
}
测试类
public class Main {
public static void main(String[] args) {
String key = "PPT";
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
Office office = getInstanceReflectByKey(key);
}
long endTime = System.currentTimeMillis();
System.out.println("总计花费时间:" + (endTime - startTime));
}
/**
* 通过传入key 创建不同的对象
*
* @param key
* @return
*/
public static Office getInstanceByKey(String key) {
if ("Word".equals(key)) {
return new Word();
}
if ("Excel".equals(key)) {
return new Excel();
}
if ("PPT".equals(key)) {
return new PPT();
}
return null;
}
/**
* 通过反射机制动态的创建
*
* @param key
* @return
*/
public static Office getInstanceReflectByKey(String key) {
String packageName = "com.gpseven.corejava.demo2";
Office office = null;
try {
Class clazz = Class.forName(packageName + "." + key);
office = (Office) clazz.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return office;
}
}
4.反射技术的重要组成
万物皆对象,我们定义的类其实从面向对象的角度来分析,它其实也是一个具体的对象,它是
一个描述类的实例.描述这个类中有哪些属性,行为等等内容.
我们可以通过定义类,来描述一组具有相同属性,行为的实例对象.比如我们创建Person 类
Class Person {
String ID;
int age;
void talk(){
//TODO
}
}
我们可以基于这个类创建具体不同身份证号和姓名的Person 实例(new Person).
每一个实例都具有身份证号,年龄,说话的行为.
通过上面的简单案例,我们可以这么理解在Java 语言中Class 的定义,是创建对象的统一模板.
那么我们可以思考这样一个问题,既然不管是Java 语言默认的类还是我们自定义创建的类都
是为了创建具有相同行为属性的对象的模板…
那么每一个类我们在定义的时候,是不是也可以抽取共性的东西,比如,每一个类都有包名,属
性定义,行为(方法),构造器等等.
那么既然每一个类都会具备这样的内容,那么这些类对象实例,应该也可以抽取成一个公有的
模板,用于创建类对象实例的模板.
所以在java 中,这个类定义的创建模板就是我们java 语言中的java.lang.Class 类.在Class 的模
板中,我们也可以找到大家耳熟能详的模板类如Method,Constructor,Field …
通过上面的内容,我们已经了解到我们创建的每一个自定义的Class 实例都是基于他的模板类
java.lang.Class 类.
在大家每一个编写的类实例中,都会定义这个类的包名,类名,访问域,特征符,构造器,字段,函
数,父类,接口等等内容.
5.反射-API
基本信息操作
- int modifier = clazz.getModifiers(); //获取类的修饰符
若类的定义为,public abstract Class ClassXXX{ }
int modify = clazz.getModifers();
返回值为public 值+ abstract 的值= 1025
- Package package= clazz.getPackage(); //获取类的包名
- String fullClassName = clazz.getName(); //获取类的全路径名称
- String simpleClassName = clazz.getSimpleName(); //获取类的简单名称
- ClassLoader classLoader = clazz.getClassLoader(); //获取类的类加载器
- Class[] interfacesClasses = clazz.getInterfaces(); //获取类实现的接口列表
- Class fc= clazz.getSuperclass(); //获取类的父类
- Annotation[] annotations= clazz.getAnnotations(); //获取类的注解列表
字段操作
- Field[] fields = clazz.getFields(); //获取类中所有的公有字段 包含继承
- Field[] declaredFields=clazz.getDeclaredFields(); //获取类中定义的字段 内部
- Field nameField=clazz.getField(“name”); //获取指定名称的公有字段
- Field likeDescField=clazz.getDeclaredField(“likeDesc”); //获取指定名称类中定义的字段
- int modifersFiled = likeDescField.getModifiers(); //获取字段的修饰
- nameField.setAccessible(true); //指定字段强制访问
- nameField.set(person,“小皮皮”); //成员字段赋值(需指定对象)
- descriptionField.set(null,“没有结婚的都是男孩!”); //静态字段赋值
方法操作
- Method[] methods = clazz.getMethods(); //获取类中所有的公有方法 继承
- Method[] declaredMethods = clazz.getDeclaredMethods(); //获取类中定义的方法
- Method talkMethod = clazz.getMethod(“talk”, String.class); //获取类中指定名称和参数的公有方法
- Method pugMethod = clazz.getDeclaredMethod(“pickUpGirls”) //获取类中定义指定名称和参数的方法
- int modifers = pugMethod .getModifiers(); //获取方法的修饰符
- talkMethod.invoke(boy,“I LOVE SEVEN”); //指定对象进行成员方法的调用
- pugMethod .setAccessible(true); //指定方法的强制访问
- pickUpGirlsMethod.invoke(null); //静态方法的调用
构造器操作
- Constructor[] cons = clazz.getConstructors(); //获取类中所有的公有构造器
- Constructor[] cons = clazz.getDeclaredConstructors(); //获取类中所有的构造器
- Constructor conNoParam= clazz.getDeclaredConstructor(); //获取类中无参的构造器
- Constructor con= clazz.getDeclaredConstructor(String.class,String.class); //获取类中有参构造
- int modifers = con.getModifiers(); //获取构造器的修饰符
- conNoParam.newInstance(); //构造器实例对象
- con.setAccessible(true); //指定方法的强制访问
- con.newInstance(‘abc’,‘def’); //有参构造调用
- class.newInstacne(); //class直接调用默认无参构造
代码示例
Boy类
public class Boy extends Person {
//身高
public int height;
private int weight;
public static String desc;
public Boy() {}
private Boy(int height) {
this.height = height;
}
public Boy(int height,int weight) {
this.height = height;
this.weight = weight;
}
public static void playBasketball(){
System.out.println("play basketball !");
}
public void playball(String ballType){
System.out.println("play "+ballType+" !");
}
//追女孩
private void pickUpGirl(){
System.out.println("pick up girl !");
}
public int getWeight() {
return weight;
}
}
Person类
public class Person {
//名字
public String name;
//年龄
private int age;
//说话
public void talk(){
System.out.println(name +",正在说话");
}
}
测试类
public class Main {
public static void main(String[] args) throws Exception {
basicOper();// 基本信息操作
filedOper();// 字段操作
methodOper(); // 方法操作
constructorOper();// 构造器操作
}
/**
* 类的基本操作
*/
public static void basicOper() {
// 获取一个Class的对象实例方法1
Class clazz = Boy.class;
/* // 获取一个Class的对象实例方法2
Class clazz2 = new Boy().getClass();
// 获取一个Class的对象实例方法3
try {
Class clazz3 = Class.forName("com.gpseven.corejava.demo3.Boy");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
// 获取一个Class的对象实例方法4
try {
Class clazz4 = Main.class.getClassLoader().loadClass("com.gpseven.corejava.demo3.Boy");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}*/
// 获取类修饰符
int modifers = clazz.getModifiers();
System.out.println("modifers:" + modifers);
// 获取类的包名
Package boyPkg = clazz.getPackage();
System.out.println("boyPkg:" + boyPkg);
// 获取类的全路径名称
System.out.println("clazz.getName():" + clazz.getName());
//获取类的简单名称
System.out.println("clazz.getSimpleName():" + clazz.getSimpleName());
//获取类的类加载器
System.out.println("clazz.getClassLoader():" + clazz.getClassLoader());
//获取类实现的接口列表
System.out.println("clazz.getInterfaces().length:" + clazz.getInterfaces());
//获取类的父类
System.out.println("clazz.getSuperclass():" + clazz.getSuperclass());
//获取类的注解列表
System.out.println("clazz.getAnnotations().length:" + clazz.getAnnotations().length);
}
public static void filedOper() throws Exception {
Class clazz = Boy.class;
Boy boy = (Boy) clazz.newInstance();
Field[] fields = clazz.getFields(); //获取当前类当中所有的公有字段,包含继承
for (int i = 0; i < fields.length; i++) {
System.out.println(fields[i]);
}
Field nameField = clazz.getField("name");
System.out.println(nameField.getName());
Field[] fields2 = clazz.getDeclaredFields();
System.out.println(fields2.length);
for (int i = 0; i < fields2.length; i++) {
System.out.println(fields2[i]);
}
Field weightField = clazz.getDeclaredField("weight");
System.out.println(weightField.getModifiers());
//让私有字段进行强制访问
weightField.setAccessible(true);
weightField.set(boy, 180);
System.out.println(boy.getWeight());
Field descField = clazz.getField("desc");
descField.set(null, "这是一个静态的属性内容!!");
System.out.println(Boy.desc);
}
public static void methodOper() throws Exception {
Class clazz = Boy.class;
Boy boy = (Boy) clazz.newInstance();
Method[] methods = clazz.getMethods();
//包含父类的方法 Object
System.out.println("public method 一共有" + methods.length + "个");
for (int i = 0; i < methods.length; i++) {
System.out.println(methods[i]);
}
Method talkMethod = clazz.getMethod("talk");
System.out.println(talkMethod.getName());
Method playBallMethod = clazz.getMethod("playball", String.class);
System.out.println(playBallMethod.getName());
System.out.println(playBallMethod.getModifiers());
playBallMethod.invoke(boy, "乒乓球");
Method[] methods2 = clazz.getDeclaredMethods();
System.out.println(methods2.length);
Method pickUpGirlMethod = clazz.getDeclaredMethod("pickUpGirl");
System.out.println(pickUpGirlMethod.getModifiers());
// private方法强制访问
pickUpGirlMethod.setAccessible(true);
pickUpGirlMethod.invoke(boy);
// 静态方法调用
Method playBasketballMethod = clazz.getDeclaredMethod("playBasketball");
playBasketballMethod.invoke(null);
}
public static void constructorOper() throws Exception {
Class clazz = Boy.class;
clazz.newInstance(); // ---> constructor 进行对象的构建.
System.out.println(clazz.getConstructors().length);
System.out.println(clazz.getDeclaredConstructors().length);
//获取clazz中无参的构造器
Constructor constructor = clazz.getDeclaredConstructor();
Constructor constructor1 = clazz.getDeclaredConstructor(int.class);
Constructor constructor2 = clazz.getDeclaredConstructor(int.class, int.class);
System.out.println(constructor.getModifiers());
System.out.println(constructor1.getModifiers());
System.out.println(constructor2.getModifiers());
Boy boy = (Boy) constructor.newInstance();
System.out.println(boy);
boy = (Boy) constructor2.newInstance(176, 130);
System.out.println(boy.getWeight());
constructor1.setAccessible(true);
boy = (Boy) constructor1.newInstance(176);
System.out.println(boy.height);
}
}
6. Class 中newInstance 函数的本质
通过查看源码,我们发现,他底层还是调用的类的Constructor 对象进行newInstance 函数的调
用他会默认调用无参的构造函数
Class 类中newInstance 部分源码: