Java基础-反射(reflect)技术详解

时间:2021-06-05 06:27:11

            Java基础-反射(reflect)技术详解

                                  作者:尹正杰

版权声明:原创作品,谢绝转载!否则将追究法律责任。

一.类加载器

1>.JVM 类加载机制

   如下图所示,JVM类加载机制分为五个部分:加载,验证,准备,解析,初始化,下面我们就分别来看一下这五个过程。(下图引用自:http://www.importnew.com/25295.html)

Java基础-反射(reflect)技术详解

加载过程

  答:就是指将class文件读入内存(JVM的方法区),并在堆内存中为之创建一个Class对象。任何类被使用时系统都会自动建立一个Class字节码对象(这个字节码对象是给JVM看的)。

连接过程

  答:链接过程如上图所示,分为验证,准备和解析。具体解释如下:

>.验证
  是否有正确的内部结构(构造器,方法,成员变量等等),并和其他类协调一致
>.准备
  负责为类的静态成员(包括静态成员变量和静态方法)分配内存(这些数据被放在方法区的数据共享区中,还会给这些变量做一个标记,即这些变量属于哪个类),并设置默认初始化值
>.解析
  将类的二进制数据中的符号引用替换为直接引用(举个例子,String name = “尹正杰”; 在内存中存储形式可能就只存储”尹正杰”这个对象了。)

下面我们解释一下符号引用和直接引用的概念:
  符号引用与虚拟机实现的布局无关,引用的目标并不一定要已经加载到内存中。各种虚拟机实现的内存布局可以各不相同,但是它们能接受的符号引用必须是一致的,因为符号引用的字面量形式明确定义在Java虚拟机规范的Class文件格式中。
  直接引用可以是指向目标的指针,相对偏移量或是一个能间接定位到目标的句柄。如果有了直接引用,那引用的目标必定已经在内存中存在。

初始化过程

  答:初始化阶段是类加载最后一个阶段,前面的类加载阶段之后,除了在加载阶段可以自定义类加载器以外,其它操作都由JVM主导。到了初始阶段,才开始真正执行类中定义的Java程序代码。

初始化阶段是执行类构造器<client>方法的过程。<client>方法是由编译器自动收集类中的类变量的赋值操作和静态语句块中的语句合并而成的。虚拟机会保证<client>方法执行之前,父类的<client>方法已经执行完毕。p.s: 如果一个类中没有对静态变量赋值也没有静态语句块,那么编译器可以不为这个类生成<client>()方法。

注意以下几种情况不会执行类初始化:
  1>.通过子类引用父类的静态字段,只会触发父类的初始化,而不会触发子类的初始化。
  2>.定义对象数组,不会触发该类的初始化。
  3>.常量在编译期间会存入调用类的常量池中,本质上并没有直接引用定义常量的类,不会触发定义常量所在的类。
  4>.通过类名获取Class对象,不会触发类的初始化。
通过Class.forName加载指定类时,如果指定参数initialize为false时,也不会触发类初始化,其实这个参数是告诉虚拟机,是否要对类进行初始化。
通过ClassLoader默认的loadClass方法,也不会触发初始化动作。

2>.类的加载时机

  在我们之前学习的Java类中,类加载的时机分为以下几种情况:

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.java; public class Demo {
public static void main(String[] args) {
System.out.println("I'm yinzhengjie !");
}
} /*
以上代码输出结果如下:
I'm yinzhengjie !
*/

直接使用java.exe命令来运行某个主类

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.java; public class Demo{
public static void main(String[] args) {
Test obj = new Test();//此时的Test类虽然没有main方法,但是依然是会被加载到内存中!因为我们创建了类的实例
obj.sayHello();
}
} class Test {
public void sayHello(){
System.out.println("I'm yinzhengjie !");
}
} /*
以上代码输出结果如下:
I'm yinzhengjie !
*/

创建类的实例

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.java; public class Demo{
public static void main(String[] args) {
System.out.println(Test.name);//类的静态变量,或者为静态变量赋值都会加载Test这个类!
}
} class Test {
public static String name = "尹正杰";
} /*
以上代码输出结果如下:
尹正杰
*/

调用类的静态变量,或者为静态变量赋值

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.java; public class Demo{
public static void main(String[] args) {
Test.sayHello(); //调用类的静态方法,也会加载Test这个类!
}
} class Test {
public static void sayHello(){
System.out.println("I'm yinzhengjie !");
}
} /*
以上代码输出结果如下:
I'm yinzhengjie !
*/

调用类的静态方法

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.java; public class Demo{
public static void main(String[] args) {
new Sun(); //初始化Father类的子类Sun,其实会先加载其父类,然后再去加载其子类,我们此处忽略Object这个类!
}
} class Father {
{
System.out.println("I'm father");
}
public static void sayHello(){
System.out.println("I'm yinzhengjie !");
}
} class Sun extends Father{
{
System.out.println("I'm sun");
}
} /*
以上代码输出结果如下:
I'm father
I'm sun
*/

初始化某个类的子类

  上述几种方式相信大家都很熟悉,处理以上的5中方式可以加载类之外,还有一种方式也可以加载类,即:使用反射方式来强制创建某个类或接口对应的java.lang.Class对象。当然,如果使用反射稍后我们会一一揭晓!

3>.什么是类加载器

  答:负责将.class文件加载到内在中,并为之生成对应的Class对象。虽然我们不需要关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行。详情请参考:https://www.cnblogs.com/yinzhengjie/p/9280564.html

4>.类加载器的组成

根类加载器(Bootstrap ClassLoader)

  答:根类加载器也被称为引导类加载器,负责Java核心类的加载。负责加载 JAVA_HOME\lib 目录中的,或通过-Xbootclasspath参数指定路径中的,且被虚拟机认可(按文件名识别,如rt.jar)的类。比如System,String,Math.Integer类等等。

Java基础-反射(reflect)技术详解

扩展类加载器(Extension ClassLoader)

  答:负责JRE的扩展目录中jar包的加载。在JDK中JRE的lib目录下ext目录。比如我本地存储的JRE目录如下:

Java基础-反射(reflect)技术详解

系统类加载器(System ClassLoader)

  答:负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径。换句话说,就是执行咱们程序员自己写的Java代码!当然也包括第三方库也是由系统类加载器完成的。

 5>.四级类加载机制

Java基础-反射(reflect)技术详解

  上图阐述了类加载器的加载机制,想要了解验证过程请参考:https://www.cnblogs.com/yinzhengjie/p/9280564.html。类加载器作用就是加载类到JVM中。主要做的寻找类,通过将完整类名映射成相应的目录,按照目录进行搜索。尝试使用当前的类加载器,如果加载不到,再使用父加载器加载,否则再向上找。

二.反射

 1>.反射的概念以及作用

   JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。我们可以举一个简单的例子来方便理解,假如你是一名医生,你可以解剖一具尸体,在你没有解剖这具尸体之前,你是看不到心脏,肺,肝等器官,但是在你解剖之后,你就可以看到人具有的所有器官,你可以把这些器官拿出来,移植到别人的身体上,比如骨髓移植(当然移植骨髓的话应该不需要解剖整个尸体吧,哈哈哈,并非讽刺医生啊)。而反射和这个例子类似,反射处理的就不是尸体了,而是在JVM中的一个Class类,反射可以把这个类解剖,得到里面的所有方法和属性,包括私有的,共有的,静态的,受保护的等等各种,当拿到这些属性或者方法时,你可以直接去执行这些方法,而不需要通过类的方式去调用!

  要想解剖一个类,必须先要获取到该类的字节码文件对象(*.class)。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象。这个“java.lang.Class”类就是用来描述字节码文件对象的!阅读API的Class类得知,Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的。

Java基础-反射(reflect)技术详解

2>.获取class文件对象三种方式

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.myReflect; public class Person {
//定义私有属性
private String name;
private int age;
//定义静态代码块,每次new Person类时,都会触发静态代码块
{
System.out.println("Welcome to use the Yin Zhengjie custom Person class !");
}
//定义空参构造
public Person(){}
//定义有参构造
public Person(String name,int age){
this.name = name;
this.age = age;
}
//为name和age变量定义get和set方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}

Person.java 文件内容

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.myReflect; /**
* 此类用于演示获取class文件对象的三种方式:
*   方式一:
*       通过Object类中的getObject()方法
*   方式二:
*       通过 类名.class 获取到字节码文件对象(任意数据类型都具备一个class静态属性,看上去要比第一种方式简单)。
*   方式三:
*       通过Class类中的方法(将类名作为字符串传递给Class类中的静态方法forName即可)。
*   第三种和前两种的区别:
* 前两种你必须明确Person类型.后面是指定这种类型的字符串就行.这种扩展更强.我不需要知道你的类.我只提供字符串,按照配置文件加载就可以了。
*/
public class Demo {
public static void main(String[] args) throws ClassNotFoundException {
//获取class文件对象的方式一:通过某个对象获取
Person p = new Person();
Class c1 = p.getClass(); //通过调用Person类的父类的方法getClass获取文件对象
System.out.println(c1); //获取class文件对象的方式二:通过类名获取
Class c2 = Person.class; //每个类型,包括基本和引用数据类型,都会赋予这个类型一个静态的属性,属性名字为class。
System.out.println(c2); //获取class文件对象的方式三:通过Class类的静态方法获取
Class c3 = Class.forName("cn.org.yinzhengjie.myReflect.Person"); //里面传入的字符串必须是你想要获取class文件对象的完整路径 //c1和c2还有c3使用的都是同一个文件对象,因为这个文件对象在执行之前,只加载一次!因此下面的输出全为真!
System.out.println(c1==c2);
System.out.println(c1==c3);
System.out.println(c2==c3);
System.out.println(c1.equals(c2));
System.out.println(c1.equals(c3));
System.out.println(c2.equals(c3));
}
}

3>.反射获取构造方法

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.myReflect; public class Person {
//定义私有属性
private String name;
private int age;
//定义空参构造
public Person(){}
//定义有参构造
public Person(String name,int age){
this.name = name;
this.age = age;
}
//定义私有的构造方法
private Person(int age,String name){
this.age = age;
this.name = name;
} //定义默认的权限的构造方法
Person(int age){
this.age = age;
this.name = "yinzhengjie";
} //定义Person特有的方法
public void study(){
System.out.println(this.name + " 正在学习!!!");
} //为name和age变量定义get和set方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
} //重写toString方法。
public String toString() {
return "Person{" + " name = " + name + ", age = " + age + '}';
}
}

Person.java 文件内容

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.myReflect; import java.lang.reflect.Constructor; /**
* 此类用于演示反射获取所有公共的构造方法.
*/
public class Demo {
public static void main(String[] args) throws ClassNotFoundException {
//通过Class类的静态方法获取获取class文件对象
Class c1 = Class.forName("cn.org.yinzhengjie.myReflect.Person"); //里面传入的字符串必须是你想要获取class文件对象的完整路径。 //使用Class文件对象,获取类中的构造方法,通过getConstructors()获取class文件对象中的所有公共的构造方法。
Constructor[] constructors = c1.getConstructors(); //Constructor是描述构造方法的对象类
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
}
} /*
以上代码执行结果如下:
public cn.org.yinzhengjie.myReflect.Person(java.lang.String,int)
public cn.org.yinzhengjie.myReflect.Person()
*/

通过getConstructors()获取class文件对象中的所有公共的构造方法。

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.myReflect; import java.lang.reflect.Constructor; /**
* 此类用于演示反射获取class文件中的空参构造方法并运行构造方法
*/
public class Demo {
public static void main(String[] args) throws Exception {
//通过Class类的静态方法获取获取class文件对象
Class c1 = Class.forName("cn.org.yinzhengjie.myReflect.Person"); //里面传入的字符串必须是你想要获取class文件对象的完整路径。 //获取指定的构造方法,空参数的构造方法
Constructor constructor = c1.getConstructor();
System.out.println(constructor);
//运行空参构造方法,Constructor类方法newInstance()运行获取到的构造方法
Object obj = constructor.newInstance();
//我们调用obj对象的toString()方法
System.out.println(obj.toString());
}
} /*
以上代码执行结果如下:
public cn.org.yinzhengjie.myReflect.Person()
Person{ name = null, age = 0}
*/

反射获取空参数构造方法并运行

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.myReflect; import java.lang.reflect.Constructor; /**
* 此类用于演示反射获取class文件中的有参数的构造方法并运行
*/
public class Demo {
public static void main(String[] args) throws Exception {
//通过Class类的静态方法获取获取class文件对象
Class c1 = Class.forName("cn.org.yinzhengjie.myReflect.Person"); //里面传入的字符串必须是你想要获取class文件对象的完整路径。 //获取指定的构造方法,获取带有String和int参数的构造方法
Constructor constructor = c1.getConstructor(String.class, int.class);
System.out.println(constructor);
//运行有参的构造方法
Object yzj = constructor.newInstance("尹正杰", 18);
System.out.println(yzj);
}
} /*
以上代码执行结果如下:
public cn.org.yinzhengjie.myReflect.Person(java.lang.String,int)
Person{ name = 尹正杰, age = 18}
*/

反射获取有参数的构造方法并运行

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.myReflect; import java.lang.reflect.Constructor; /**
* 此类用于演示反射获取构造方法并运行的快速的方式
* 这种快捷方式是有前提的:
* 1>.被反射的类,必须具有空参构造方法;
* 2>.构造方法权限必须为最大权限,即使用public关键字修饰;
*/
public class Demo {
public static void main(String[] args) throws Exception {
//通过Class类的静态方法获取获取class文件对象
Class c1 = Class.forName("cn.org.yinzhengjie.myReflect.Person"); //里面传入的字符串必须是你想要获取class文件对象的完整路径。 //Class类中定义方法,直接使用newInstance()方法直接创建被反射类的对象实例。由于newInstance无法传参,因此要求class文件对象必须有空参构造!
Object obj = c1.newInstance();
System.out.println(obj.toString());
}
} /*
以上代码执行结果如下:
Person{ name = null, age = 0}
*/

反射获取构造方法并运行的快速的方式

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.myReflect; import java.lang.reflect.Constructor; /**
* 此类用于演示反射获取私有构造方法并运行(不推荐使用,因为它破坏了程序的封装性和安全性 )
*/
public class Demo {
public static void main(String[] args) throws Exception {
//通过Class类的静态方法获取获取class文件对象
Class c1 = Class.forName("cn.org.yinzhengjie.myReflect.Person"); //里面传入的字符串必须是你想要获取class文件对象的完整路径。 //通过getDeclaredConstructors()获取所有的构造方法,包括私有的
Constructor[] declaredConstructors = c1.getDeclaredConstructors();
for (Constructor item : declaredConstructors) {
System.out.println(item);
}
}
} /*
以上代码执行结果如下:
cn.org.yinzhengjie.myReflect.Person(int)
private cn.org.yinzhengjie.myReflect.Person(int,java.lang.String)
public cn.org.yinzhengjie.myReflect.Person(java.lang.String,int)
public cn.org.yinzhengjie.myReflect.Person()
*/

通过getDeclaredConstructors()获取class文件对象中所有的构造方法(包括私有的)

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.myReflect; import java.lang.reflect.Constructor; /**
* 此类用于演示反射获取指定私有构造方法并运行(不推荐使用,因为它破坏了程序的封装性和安全性 )
*/
public class Demo {
public static void main(String[] args) throws Exception {
//通过Class类的静态方法获取获取class文件对象
Class c1 = Class.forName("cn.org.yinzhengjie.myReflect.Person"); //里面传入的字符串必须是你想要获取class文件对象的完整路径。
//获取指定列表参数的构造方法,可以指定私有的构造方法!
Constructor constructor = c1.getDeclaredConstructor(int.class,String.class);
//调用Constructor父类AccessibleObject中定义的setAccessible方法,目的是取消权限检查。这样就可以访问私有的,这种方式叫做暴力私有。如果不指定为true,默认是无法访问"private"修饰的方法,即会抛异常:IllegalAccessException
constructor.setAccessible(true);
Object obj = constructor.newInstance(18, "尹正杰");
System.out.println(obj.toString()); }
} /*
以上代码执行结果如下:
Person{ name = 尹正杰, age = 18}
*/

反射获取指定私有构造方法并运行(不推荐使用,因为它破坏了程序的封装性和安全性 )

4>.反射获取成员变量并改值

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.myReflect; public class Person {
//定义私有属性
private String name;
private int age;
public String sex;
//定义空参构造
public Person(){}
//定义有参构造
public Person(String name, int age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
} //定义私有的构造方法
private Person( int age, String name,String sex) {
this.name = name;
this.age = age;
this.sex = sex;
} //定义默认的权限的构造方法
Person(int age,String sex){
this.age = age;
this.sex = sex;
this.name = "yinzhengjie";
} //定义Person特有的方法
public void study(){
System.out.println(this.name + " 正在学习!!!");
} private void playGame(){
System.out.println(this.name + "正在打游戏!!");
} //为name和age变量定义get和set方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
} //重写toString方法。 @Override
public String toString() {
return "Person{" + "name =" + name + ", age = " + age + ", sex = " + sex + '}';
}
}

Person.java 文件内容

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.myReflect; import java.lang.reflect.Field; /**
* 此类用于演示反射获取所有的公用的成员变量
*/
public class Demo {
public static void main(String[] args) throws Exception {
//通过Class类的静态方法获取获取class文件对象
Class c1 = Class.forName("cn.org.yinzhengjie.myReflect.Person"); //里面传入的字符串必须是你想要获取class文件对象的完整路径。 //获取成员变量Class类的getFields()方法可以获取所有的公用的成员变量
Field[] fields = c1.getFields();
for (Field field : fields) {
System.out.println(field);
} }
} /*
以上代码执行结果如下:
public java.lang.String cn.org.yinzhengjie.myReflect.Person.sex
*/

反射获取所有的公用的成员变量

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.myReflect; import java.lang.reflect.Field; /**
* 此类用于演示反射获取所有的成员变量,包括私有的
*/
public class Demo {
public static void main(String[] args) throws Exception {
//通过Class类的静态方法获取获取class文件对象
Class c1 = Class.forName("cn.org.yinzhengjie.myReflect.Person"); //里面传入的字符串必须是你想要获取class文件对象的完整路径。 //获取成员变量Class类的getDeclaredFields()方法可以获取所有的成员变量,包括私有的成员变量
Field[] fields = c1.getDeclaredFields();
for (Field field : fields) {
System.out.println(field);
}
}
} /*
以上代码执行结果如下:
private java.lang.String cn.org.yinzhengjie.myReflect.Person.name
private int cn.org.yinzhengjie.myReflect.Person.age
public java.lang.String cn.org.yinzhengjie.myReflect.Person.sex
*/

反射获取所有的成员变量,包括私有的

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.myReflect; import java.lang.reflect.Field; /**
* 此类用于演示反射获取指定的public修饰的成员变量
*/
public class Demo {
public static void main(String[] args) throws Exception {
//通过Class类的静态方法获取获取class文件对象
Class c1 = Class.forName("cn.org.yinzhengjie.myReflect.Person"); //里面传入的字符串必须是你想要获取class文件对象的完整路径。 //获取指定的公共的(即public关键字修饰)成员变量,需要以字符串的形式传入成员变量的名称。
Field field = c1.getField("sex");
System.out.println(field);
//通过反射的方法创建一个Person类。
Object obj = c1.newInstance();
System.out.println(obj);
//Field的类的set()方法可以修改成员变量的值,第一个参数需要传入需要修改的对象,第二个参数是修改后后的值
field.set(obj,"boy");
System.out.println(obj);
}
} /*
以上代码执行结果如下:
public java.lang.String cn.org.yinzhengjie.myReflect.Person.sex
Person{name =null, age = 0, sex = null}
Person{name =null, age = 0, sex = boy}
*/

反射获取指定的public修饰的成员变量,并修改其值

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.myReflect; import java.lang.reflect.Field; /**
* 此类用于演示反射获取指定的private修饰的成员变量,并修改其值
*/
public class Demo {
public static void main(String[] args) throws Exception {
//通过Class类的静态方法获取获取class文件对象
Class c1 = Class.forName("cn.org.yinzhengjie.myReflect.Person"); //里面传入的字符串必须是你想要获取class文件对象的完整路径。 //获取指定的成员变量,包括私有的(即private关键字修饰的)成员变量,需要以字符串的形式传入成员变量的名称。
Field field = c1.getDeclaredField("name");
System.out.println(field);
//通过反射的方法创建一个Person类。
Object obj = c1.newInstance();
System.out.println(obj);
//设置取消权限检查,否在在访问"private"修饰的成员变量时,会抛异常: "java.lang.IllegalAccessException".
field.setAccessible(true);
//Field的类的set()方法可以修改成员变量的值,第一个参数需要传入需要修改的对象,第二个参数是修改后后的值
field.set(obj,"尹正杰");
System.out.println(obj);
}
} /*
以上代码执行结果如下:
private java.lang.String cn.org.yinzhengjie.myReflect.Person.name
Person{name =null, age = 0, sex = null}
Person{name =尹正杰, age = 0, sex = null}
*/

反射获取指定的private修饰的成员变量,并修改其值

5>.反射获取成员方法并运行

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.myReflect; public class Person {
//定义私有属性
private String name;
private int age;
public String sex;
//定义空参构造
public Person(){}
//定义有参构造
public Person(String name, int age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
} //定义私有的构造方法
private Person( int age, String name,String sex) {
this.name = name;
this.age = age;
this.sex = sex;
} //定义默认的权限的构造方法
Person(int age,String sex){
this.age = age;
this.sex = sex;
this.name = "yinzhengjie";
} //定义Person特有的方法
public void study(String name){
System.out.println(name + " 正在学习!!!");
} public void eat(){
System.out.println("人是铁,饭是钢,一顿不吃饿得慌!");
} void work(){
System.out.println("正在工作中!");
} private void playGame(String name){
System.out.println(name + "正在打游戏!!");
} //为name和age变量定义get和set方法 public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} public String getSex() {
return sex;
} public void setSex(String sex) {
this.sex = sex;
} //重写toString方法。 @Override
public String toString() {
return "Person{" + "name =" + name + ", age = " + age + ", sex = " + sex + '}';
}
}

Person.java 文件内容

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.myReflect; import java.lang.reflect.Method; /**
* 此类用于演示反射获取的是Class文件中所有公共成员方法
*/
public class Demo {
public static void main(String[] args) throws Exception {
//通过Class类的静态方法获取获取class文件对象
Class c1 = Class.forName("cn.org.yinzhengjie.myReflect.Person"); //里面传入的字符串必须是你想要获取class文件对象的完整路径。 //获取Class对象中的成员方法,getMethods()方法获取的是Class文件中所有公共成员方法,包括继承来的方法公共权限方法
Method[] methods = c1.getMethods(); for (Method method : methods) {
System.out.println(method);
} }
} /*
以上代码执行结果如下:
public java.lang.String cn.org.yinzhengjie.myReflect.Person.toString()
public java.lang.String cn.org.yinzhengjie.myReflect.Person.getName()
public void cn.org.yinzhengjie.myReflect.Person.setName(java.lang.String)
public int cn.org.yinzhengjie.myReflect.Person.getAge()
public void cn.org.yinzhengjie.myReflect.Person.eat()
public void cn.org.yinzhengjie.myReflect.Person.study(java.lang.String)
public void cn.org.yinzhengjie.myReflect.Person.setAge(int)
public java.lang.String cn.org.yinzhengjie.myReflect.Person.getSex()
public void cn.org.yinzhengjie.myReflect.Person.setSex(java.lang.String)
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
*/

反射获取的是Class文件中所有公共成员方法

/*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.myReflect; import java.lang.reflect.Method; /**
* 此类用于演示反射获取的是Class文件中空参数成员方法并与运行
*/
public class Demo {
public static void main(String[] args) throws Exception {
//通过Class类的静态方法获取获取class文件对象
Class c1 = Class.forName("cn.org.yinzhengjie.myReflect.Person"); //里面传入的字符串必须是你想要获取class文件对象的完整路径。
Object obj = c1.newInstance();
//通过Class类中getMethod方法的获取public修饰的空参数成员方法,需要以字符串的形式传入方法名
Method method = c1.getMethod("eat");
//使用Method类中的方法,运行获取到的study方法,这里我们需要传入一个Person类
method.invoke(obj); }
} /*
以上代码执行结果如下:
人是铁,饭是钢,一顿不吃饿得慌!
*/

反射获取的是Class文件中空参数成员方法并与运行

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.myReflect; import java.lang.reflect.Method; /**
* 此类用于演示反射获取有参数的成员方法并运行
*/
public class Demo {
public static void main(String[] args) throws Exception {
//通过Class类的静态方法获取获取class文件对象
Class c1 = Class.forName("cn.org.yinzhengjie.myReflect.Person"); //里面传入的字符串必须是你想要获取class文件对象的完整路径。
Object obj = c1.newInstance();
//通过Class类中getMethod方法的获取public修饰的有参数成员方法,需要以字符串的形式传入方法名,以及传入的参数类型,如果有多个参数用逗号分割即可。
Method method = c1.getMethod("study",String.class);
//使用Method类中的方法,运行获取到的study方法,这里我们需要传入一个Person类
method.invoke(obj,"尹正杰"); }
} /*
以上代码执行结果如下:
尹正杰 正在学习!!!
*/

反射获取有参数的成员方法并运行

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.myReflect; import java.lang.reflect.Method; /**
* 此类用于演示反射获取所有的成员方法,包括私有的
*/
public class Demo {
public static void main(String[] args) throws Exception {
//通过Class类的静态方法获取获取class文件对象
Class c1 = Class.forName("cn.org.yinzhengjie.myReflect.Person"); //里面传入的字符串必须是你想要获取class文件对象的完整路径。
Object obj = c1.newInstance();
//通过Class类中getDeclaredMethods()方法获取所有的成员方法,包括私有的
Method[] declaredMethods = c1.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod);
} }
} /*
以上代码执行结果如下:
public java.lang.String cn.org.yinzhengjie.myReflect.Person.toString()
public java.lang.String cn.org.yinzhengjie.myReflect.Person.getName()
public void cn.org.yinzhengjie.myReflect.Person.setName(java.lang.String)
public void cn.org.yinzhengjie.myReflect.Person.setSex(java.lang.String)
public void cn.org.yinzhengjie.myReflect.Person.study(java.lang.String)
public java.lang.String cn.org.yinzhengjie.myReflect.Person.getSex()
public void cn.org.yinzhengjie.myReflect.Person.setAge(int)
public int cn.org.yinzhengjie.myReflect.Person.getAge()
private void cn.org.yinzhengjie.myReflect.Person.playGame(java.lang.String)
void cn.org.yinzhengjie.myReflect.Person.work()
public void cn.org.yinzhengjie.myReflect.Person.eat()
*/

反射获取所有的成员方法,包括私有的

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.myReflect; import java.lang.reflect.Method; /**
* 此类用于演示反射获取(private关键字修饰)私有的成员方法
*/
public class Demo {
public static void main(String[] args) throws Exception {
//通过Class类的静态方法获取获取class文件对象
Class c1 = Class.forName("cn.org.yinzhengjie.myReflect.Person"); //里面传入的字符串必须是你想要获取class文件对象的完整路径。
Object obj = c1.newInstance();
//通过Class类中getDeclaredMethod()方法获取指定的成员方法,包括私有的。
Method playGame = c1.getDeclaredMethod("playGame", String.class);//第一个参数指定获取的方法名称,后面的一顿参数都是参数的类型,如果有多个参数类型用逗号进行分割。
//Method类的setAccessible方法取消权限检查。
playGame.setAccessible(true);
playGame.invoke(obj,"尹正杰");
}
} /*
以上代码执行结果如下:
尹正杰正在打游戏!!
*/

反射获取(private关键字修饰)私有的成员方法

三.内省(Introspector)

1>.成员变量和属性的区别

  答:成员变量和字段是等效的,指的就是非方法字段。而属性针对的成员方法,主要针对的是getter和setter方法。一般的做法是通过类Introspector来获取某个对象的BeanInfo信息,然后通过BeanInfo来获取属性的描述器(PropertyDescriptor),通过这个属性描述器就可以获取某个属性对应的getter/setter方法,然后我们就可以通过反射机制来调用这些方法。

在计算机科学中,内省是指计算机程序在运行时(Run time)检查对象(Object)类型的一种能力,通常也可以称作运行时类型检查。
不应该将内省和反射混淆。相对于内省,反射更进一步,是指计算机程序在运行时(Run time)可以访问、检测和修改它本身状态或行为的一种能力。

内省在wiki上的解释

2>.什么是内省

  答:内省是一种特殊的反射,被用于JavaBean API。通过使用内省,我们可以确定一个对象中哪些方法适用于被其他对象访问。例如:getter和setter方法。

3>.内省和反射区别

  答: 反射式在运行状态把Java类中的各种成分映射成相应的Java类,可以动态的获取所有的属性以及动态调用任意一个方法,强调的是运行状态。 内省机制是通过反射来实现的,BeanInfo用来暴露一个bean的属性、方法和事件,以后我们就可以操纵该JavaBean的属性

4>.测试内省代码

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.myReflect; import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method; public class Demo {
public static void main(String[] args) throws Exception{
//通过内省(Introspector)得到bean信息
BeanInfo bi = Introspector.getBeanInfo(Dog.class) ;
//得到属性描述符集合
PropertyDescriptor[] pps = bi.getPropertyDescriptors(); //遍历通过
for(PropertyDescriptor pp :pps){
//获取PropertyDescriptor变量的名称
String pname = pp.getName();
//获取PropertyDescriptor的数据类型
Class ptype = pp.getPropertyType() ;
//获取getter方法
Method getter = pp.getReadMethod();
//获取setter方法
Method setter = pp.getWriteMethod() ;
System.out.printf("%s : %s : %s : %s\r\n" , pname , ptype.toString() , getter!=null?getter.toString():null, setter!=null?setter.toString():null);
}
}
} /*
以上代码执行结果如下:
age : int : public int cn.org.yinzhengjie.myReflect.Dog.getAge() : public void cn.org.yinzhengjie.myReflect.Dog.setAge(int)
class : class java.lang.Class : public final native java.lang.Class java.lang.Object.getClass() : null
name : class java.lang.String : public java.lang.String cn.org.yinzhengjie.myReflect.Animal.getName() : public void cn.org.yinzhengjie.myReflect.Animal.setName(java.lang.String)
sex : int : public int cn.org.yinzhengjie.myReflect.Dog.getSex() : null
*/

四.小试牛刀

1>.定义集合类,泛型String,要求向集合中添加Integer类型(反射泛型擦除)

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.myReflect; import java.lang.reflect.Method;
import java.util.ArrayList; public class Demo {
public static void main(String[] args) throws Exception{
//定义的泛型只允许传入字符
ArrayList<String> array = new ArrayList<String>();
array.add("尹正杰");
//反射方式,获取出集合ArrayList类的class文件对象
Class c = array.getClass();
//获取ArrayList.class文件中的方法add,通过反射的方法,我们可以指定传入的类型是Object.class.
Method method = c.getMethod("add",Object.class);
//使用invoke运行ArrayList方法add,擦除了之前只允许传入字符串的泛型!
method.invoke(array, 2018);
method.invoke(array, 30000);
method.invoke(array, 25000);
System.out.println(array);
}
} /*
以上代码执行结果如下:
[尹正杰, 2018, 30000, 25000]
*/

2>.调用Person方法,调用Student方法,调用Worker方法,通过配置文件实现此功能(反射通过配置文件运行功能实现)

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.myReflect; public class Person {
public void playGame(){
System.out.println("尹正杰今天在玩LOL时拿了3个五杀!!!");
}
}

Person.java 文件内容

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.myReflect; public class Student {
public void study(){
System.out.println("学生在学习");
}
}

Student.java 文件内容

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.myReflect; public class Worker {
public void job(){
System.out.println("上班族在工作");
}
}

Worker.java 文件内容

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.myReflect; import java.io.FileReader;
import java.lang.reflect.Method;
import java.util.Properties; /*
*
*运行的类名和方法名字,以键值对的形式,写在文本中,运行哪个类,读取配置文件即可,实现步骤如下:
* 1>.准备配置文件,键值对
* 2>.IO流读取配置文件 Reader
* 3>.文件中的键值对存储到集合中 Properties,集合保存的键值对,就是类名和方法名
* 4>.反射获取指定类的class文件对象
* 5>.class文件对象,获取指定的方法
* 6>.运行方法
*/
public class Demo {
public static void main(String[] args) throws Exception{
//IO流读取配置文件,
FileReader r = new FileReader("D:\\10.Java\\IDE\\yhinzhengjieData\\config.properties");
//创建集合对象
Properties pro = new Properties();
//调用集合方法load,传递流对象
pro.load(r);
r.close();
//通过键获取值
String className = pro.getProperty("className");
String methodName = pro.getProperty("methodName");
//反射获取指定类的class文件对象
Class c = Class.forName(className);
Object obj = c.newInstance();
//获取指定的方法名
Method method = c.getMethod(methodName);
method.invoke(obj);
}
} /*
以上代码执行结果如下:
尹正杰今天在玩LOL时拿了3个五杀!!!
*/

  注意,在运行以上代码时,需要指定配置文件,我们通过配置文件就可以轻松实现调用某个类了,并不需要修改源码,具体配置如下:

className=cn.org.yinzhengjie.myReflect.Person
methodName=playGame

D:\\10.Java\\IDE\\yhinzhengjieData\\config.properties

3>.通过反射实现属性浅度复制(相同类之间)

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.myReflect; public class Person {
//定义私有属性
private String name;
private int age;
public String sex;
//定义空参构造
public Person(){}
//定义有参构造
public Person(String name, int age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
} //定义私有的构造方法
private Person( int age, String name,String sex) {
this.name = name;
this.age = age;
this.sex = sex;
} //定义默认的权限的构造方法
Person(int age,String sex){
this.age = age;
this.sex = sex;
this.name = "yinzhengjie";
} //定义Person特有的方法
public void study(String name){
System.out.println(name + " 正在学习!!!");
} public void eat(){
System.out.println("人是铁,饭是钢,一顿不吃饿得慌!");
} void work(){
System.out.println("正在工作中!");
} private void playGame(String name){
System.out.println(name + "正在打游戏!!");
} //为name和age变量定义get和set方法 public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} public String getSex() {
return sex;
} public void setSex(String sex) {
this.sex = sex;
} //重写toString方法。 @Override
public String toString() {
return "Person {" + " name =" + name + ", age = " + age + ", sex = " + sex + '}';
}
}

Person.java 文件内容

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.myReflect; import java.lang.reflect.Field;
import java.lang.reflect.Modifier; public class Demo {
public static void main(String[] args) throws Exception{
//生成两个不同的对象
Person p1 = new Person("尹正杰",18,"boy");
System.out.println("p1: " + p1);
Person p2 = new Person();
System.out.println("复制前的p2: " + p2);
copyProperties1(p1,p2);
System.out.println("复制后的p2: " + p2);
} /**
* 属性复制
*/
public static void copyProperties1(Object o1, Object o2) throws Exception {
Class c1 = o1.getClass() ;
Class c2 = o2.getClass() ;
if(c1 != c2){
throw new RuntimeException("类型不一致异常!!") ;
}
Field[] fields = c1.getDeclaredFields() ;
for(Field f : fields){
//取消权限检查
f.setAccessible(true);
//获取修饰符总和得到一个int值
int mod = f.getModifiers() ;
//判断成员变量是否是常量,如果是常量,将上面的mod变量传给Modifier.isFinal()方法即可!
if(!Modifier.isFinal(mod)){
//如果不是常量我们就将c1的属性取出
Object value = f.get(o1) ;
//将c1的属性赋值给c2对象
f.set(o2,value);
}
}
} } /*
以上代码执行结果如下:
p1: Person { name =尹正杰, age = 18, sex = boy}
复制前的p2: Person { name =null, age = 0, sex = null}
复制后的p2: Person { name =尹正杰, age = 18, sex = boy}
*/

4>.不同性对象属性复制-时间复杂度

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.myReflect; public class Person {
//定义私有属性
private String name;
private int age;
public String sex;
//定义空参构造
public Person(){}
//定义有参构造
public Person(String name, int age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
} //定义私有的构造方法
private Person( int age, String name,String sex) {
this.name = name;
this.age = age;
this.sex = sex;
} //定义默认的权限的构造方法
Person(int age,String sex){
this.age = age;
this.sex = sex;
this.name = "yinzhengjie";
} //定义Person特有的方法
public void study(String name){
System.out.println(name + " 正在学习!!!");
} public void eat(){
System.out.println("人是铁,饭是钢,一顿不吃饿得慌!");
} void work(){
System.out.println("正在工作中!");
} private void playGame(String name){
System.out.println(name + "正在打游戏!!");
} //为name和age变量定义get和set方法 public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} public String getSex() {
return sex;
} public void setSex(String sex) {
this.sex = sex;
} //重写toString方法。 @Override
public String toString() {
return "Person {" + " name =" + name + ", age = " + age + ", sex = " + sex + '}';
}
}

Person.java 文件内容

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.myReflect; public class Student {
private String name;
private int age;
public String sex; public Student() { } public Student(String name, int age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} public String getSex() {
return sex;
} public void setSex(String sex) {
this.sex = sex;
} @Override
public String toString() {
return "Student {" + " name = " + name + ", age = " + age + ", sex = " + sex + '}';
}
}

Student.java 文件内容

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.myReflect; import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map; public class Demo {
public static void main(String[] args) throws Exception{
//生成两个不同的对象
Person p1 = new Person("尹正杰",18,"boy");
System.out.println(p1);
Student s1 = new Student();
System.out.println("复制之前的s1: " + s1);
copyProperties(p1,s1);
System.out.println("复制之后的s1: " + s1);
} /**
* 属性复制
*/
public static void copyProperties(Object o1, Object o2) throws Exception {
Class c2 = o2.getClass() ;
Field[] fs2 = c2.getDeclaredFields();
//将c2的字段名称和对应的字段对象放在一个map中。
Map<String, Field> map = new HashMap<String, Field>() ;
for (Field f2 : fs2) {
f2.setAccessible(true);
String f2Name = f2.getName();
map.put(f2Name , f2) ;
} Class c1 = o1.getClass() ;
Field[] fields = c1.getDeclaredFields() ; for(Field f : fields){
Class f1Type = f.getType() ;
String f1Name = f.getName() ;
f.setAccessible(true);
Object value = f.get(o1) ;
//在o2中找出和f相同名称并相同类型的字段
Field f2 = map.get(f1Name);
if(f2 != null && f2.getType() == f1Type){
f2.setAccessible(true);
f2.set(o2 , value);
}
}
c1.getInterfaces();
} } /*
以上代码执行结果如下:
p1: Person { name =尹正杰, age = 18, sex = boy}
复制前的p2: Person { name =null, age = 0, sex = null}
复制后的p2: Person { name =尹正杰, age = 18, sex = boy}
*/

5>.内省实现属性复制

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.myReflect; public class Person {
//定义私有属性
private String name;
private int age;
public String sex;
//定义空参构造
public Person(){}
//定义有参构造
public Person(String name, int age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
} //定义私有的构造方法
private Person( int age, String name,String sex) {
this.name = name;
this.age = age;
this.sex = sex;
} //定义默认的权限的构造方法
Person(int age,String sex){
this.age = age;
this.sex = sex;
this.name = "yinzhengjie";
} //定义Person特有的方法
public void study(String name){
System.out.println(name + " 正在学习!!!");
} public void eat(){
System.out.println("人是铁,饭是钢,一顿不吃饿得慌!");
} void work(){
System.out.println("正在工作中!");
} private void playGame(String name){
System.out.println(name + "正在打游戏!!");
} //为name和age变量定义get和set方法 public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} public String getSex() {
return sex;
} public void setSex(String sex) {
this.sex = sex;
} //重写toString方法。 @Override
public String toString() {
return "Person {" + " name =" + name + ", age = " + age + ", sex = " + sex + '}';
}
}

Person.java 文件内容

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.myReflect; public class Student {
private String name;
private int age;
public String sex; public Student() { } public Student(String name, int age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} public String getSex() {
return sex;
} public void setSex(String sex) {
this.sex = sex;
} @Override
public String toString() {
return "Student {" + " name = " + name + ", age = " + age + ", sex = " + sex + '}';
}
}

Student.java 文件内容

 /*
@author :yinzhengjie
Blog:http://www.cnblogs.com/yinzhengjie/tag/Java%E5%9F%BA%E7%A1%80/
EMAIL:y1053419035@qq.com
*/
package cn.org.yinzhengjie.myReflect; import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method; public class Demo {
public static void main(String[] args) throws Exception{
Person p1 = new Person("尹正杰",18,"boy");
Student s1 = new Student();
System.out.println(p1);
System.out.println("复制之前的s1: " + s1);
copyProperties(p1 ,s1);
System.out.println("复制之后的s1: " + s1);
} /**
* 通过内省方式复制属性
*/
public static void copyProperties(Object o1 , Object o2) throws Exception {
//得到a的bean信息
BeanInfo bi_a = Introspector.getBeanInfo(o1.getClass()) ;
//b的信息
BeanInfo bi_b = Introspector.getBeanInfo(o2.getClass()) ;
PropertyDescriptor[] pps_b = bi_b.getPropertyDescriptors();
//得到a的所有属性
PropertyDescriptor[] pps_a = bi_a.getPropertyDescriptors();
for(PropertyDescriptor pp_a : pps_a){
String aname = pp_a.getName() ;
Class atype = pp_a.getPropertyType() ;
Method agetter = pp_a.getReadMethod();
if(agetter != null){
Object value = agetter.invoke(o1) ;
//
for(PropertyDescriptor pp_b : pps_b){
String bname = pp_b.getName();
Class btype = pp_b.getPropertyType() ;
Method bsetter = pp_b.getWriteMethod() ;
if(aname.equals(bname)
&& atype == btype
&& bsetter != null){
bsetter.invoke(o2 , value) ;
}
} }
}
}
} /*
以上代码执行结果如下:
Person { name =尹正杰, age = 18, sex = boy}
复制之前的s1: Student { name = null, age = 0, sex = null}
复制之后的s1: Student { name = 尹正杰, age = 18, sex = boy}
*/