目录
基本概念
反射的进一步理解:
将类的各个组成部分封装为其他对象(将一个类的组成部分封装为其他对象)
就是反射。
Java代码经历的三个阶段:
1. 写好代码.java后使用javac,编译为字节码文件:xxx.class
这个阶段称之为source源代码阶段。
3. 当程序要new一个对象时
这个阶段称之为Runtime运行时阶段
2. 把字节码文件.class加载进内存里面(使用类加载器ClassLoader)
在内存里面会存在一个对象用于描述字节码文件。这个对象为Class对象。
所以java里面有个Class类。专门用来搞这个字节码文件的
如:
成员变量(Fields)、构造方法(Constructor)、成员方法(Method)
这个阶段称之为Class类对象阶段
第三阶段都是同过第二阶段创建起来的。第二个阶段需要第一个阶段。
反射好处:
1. 在程序运行过程中操作这些对象。(IDEA这个程序的 某个对象方法的提示,就是反射)
2. 可以解耦。提高程序可扩展性。
获取Class对象的方式:
有3种方式,对应java代码经历的3个阶段;
第一个阶段只有.class字节码文件,并没有在内存中,要把.class加载到内存中生成对象。对应的API为:Class.forName("全类名") 返回class对象;
第二个阶段:这个.class字节码文件已经到内存了,这个时候,只要获取他就可以了,类名.class,这种方式。
第三个阶段:在Runtime时,已经有对象了,这个时候只要 对象.getClass()就可以了。这个getClass()类是在Object里面
的。
总结下:
1.Class.forName("全类名");将字节码文件加载到内存,返回class对象。
2.类名.class;通过类名的属性class获取。
3.对象.getClass();getClass()方法在Object类中定义
代码与实例
如下代码:
Person.java
package Object;
public class Person {
private String name;
private Integer age;
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
public Person(){
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
Main1.java
import Object.Person;
public class Main1 {
public static void main(String[] args) throws ClassNotFoundException {
//全类名
Class cls1 = Class.forName("Object.Person");
System.out.println(cls1);
//类名.class
Class cls2 = Person.class;
System.out.println(cls2);
//对象.getClass()
Person p = new Person();
Class cls3 = p.getClass();
System.out.println(cls3);
//比较下对象:
System.out.println(cls1 == cls2);
System.out.println(cls1 == cls3);
}
}
程序运行截图如下:
结论:
同一个字节码文件.class在一次程序运行过程中,只会被加载一次,不论通过哪种方式获取的class对象都是同一个。
第一种方式,多用于配置文件中,读取文件,加载类
第二种方式,多用于参数的传递;
第三种方式,多用于对象的获取字节码
下面是
Class对象功能:
1.获取成员变量;
Field[] getFields()
Field getField(String name)
Field[] getDeclaredFields()
Field getDeclaredField(String name)
2.获取构造方法;
Constructor<?>[] getConstructors()
Constructor<T> getConstructor(类<?>... parameterTypes)
Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)
Constructor<?>[] getDeclaredConstructors()
3.获取成员方法;
Method[] getMethods()
Method[] getMethod(String name, 类<?>... parameterTypes)
Method[] getDeclaredMethods()
Method getDeclaredMethod(String name, 类<?>... parameterTypes)
4.获取类名;
String getName()
下面先给出变量名的实例
Person.java
package Object;
public class Person {
private String name;
private Integer age;
public String a;
protected String b;
String c;
private String d;
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
public Person(){
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", a='" + a + '\'' +
", b='" + b + '\'' +
", c='" + c + '\'' +
", d='" + d + '\'' +
'}';
}
}
Main2.java
import Object.Person;
import java.lang.reflect.Field;
public class Main2 {
public static void main(String[] args) throws Exception {
//Field[] getFields()获取所有public修饰的成员变量
Class aClass = Class.forName("Object.Person");
Field[] fields = aClass.getFields();
for(Field field : fields){
System.out.println(field);
}
//获取及设置a的值
Field a = aClass.getField("a");
Person person = new Person();
Object value = a.get(person);
System.out.println("the a is : " + value);
a.set(person, "你妹");
System.out.println(person);
System.out.println("----------华丽的分割线----------");
//Field getDeclaredField(String name)
Field[] declaredFields = aClass.getDeclaredFields();
for(Field field : declaredFields){
System.out.println(field);
}
//忽略修饰符暴力反射,设置值
Field d = aClass.getDeclaredField("d");
d.setAccessible(true);
d.set(person, "猪小明");
System.out.println(person);
}
}
运行截图如下:
下面是关于构造函数的反射:
Person还是与上面的一样。
import Object.Person;
import java.lang.reflect.Constructor;
public class Main3 {
public static void main(String[] args) throws Exception {
Class personClass = Person.class;
Constructor constructor = personClass.getConstructor(String.class, Integer.class);
System.out.println(constructor);
//创建对象
Object p = constructor.newInstance("王二麻子", 99);
System.out.println(p);
System.out.println("----------华丽的分割线----------");
Constructor constructor2 = personClass.getConstructor();
Object p2 = constructor2.newInstance();
System.out.println(p2);
}
}
程序运行截图如下:
下面是获取成员方法的实例。
Person.java
package Object;
public class Person {
private String name;
private Integer age;
public String a;
protected String b;
String c;
private String d;
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
public Person(){
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", a='" + a + '\'' +
", b='" + b + '\'' +
", c='" + c + '\'' +
", d='" + d + '\'' +
'}';
}
public void eat(){
System.out.println("eat... ...");
}
//带参数的
public void eat(String food){
System.out.println("eat... ..." + food);
}
}
Main4.java
import Object.Person;
import java.lang.reflect.Method;
public class Main4 {
public static void main(String[] args) throws Exception {
Class personClass = Person.class;
//获取指定名称的方法
Method eat = personClass.getMethod("eat");
Person p = new Person();
eat.invoke(p);
System.out.println("----------华丽的风格线----------");
Method eat1 = personClass.getMethod("eat", String.class);
eat1.invoke(p, "西北风");
//获取所有Public修饰的方法
Method[] methods = personClass.getMethods();
for(Method method : methods){
System.out.println(method);
String name = method.getName();
System.out.println(name);
//method.setAccessible(true);
}
}
}
程序运行截图如下:
下面是关于获取类名的实例:
Main5.java
import Object.Person;
public class Main5 {
public static void main(String[] args){
Class personClass = Person.class;
System.out.println(personClass.getName());
}
}
程序运行截图如下:
import Object.Person;
public class Main5 {
public static void main(String[] args){
Class personClass = Person.class;
System.out.println(personClass.getName());
}
}
程序运行截图如下:
下面来仿一个案例,像Spring Boot那样的通过配置application.properties既可以调用相应的类,和函数
这里应用到的就是反射
Main6.java
import Object.Person;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Properties;
public class Main6 {
public static void main(String[] args) throws Exception {
/***
* 加载配置文件
* 创建Properties对象
* 加载配置文件,转换为集合
* 获取class目录下的配置文件
*/
Properties pro = new Properties();
ClassLoader classLoader = Person.class.getClassLoader();
InputStream resourceAsStream = classLoader.getResourceAsStream("application.properties");
pro.load(resourceAsStream);
//配置文件中定义的数据
String className = pro.getProperty("className");
String methodName = pro.getProperty("methodName");
//加载该类到内存
Class aClass = Class.forName(className);
Object o = aClass.newInstance();
//获取方法并执行
Method method = aClass.getMethod(methodName);
method.invoke(o);
}
}
通过这种配置文件,即可调用对应的类和方法:
程序运行截图如下:
源码打包下载地址: