查看: 591|回复: 0

[Java代码] Java进阶--------反射

发表于 2018-2-1 08:00:03
Java反射

一、类的加载
1、当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。
(1)加载
就是指将class文件读入内存,并为之创建一个Class对象(class文件的对象)。
任何类被使用时系统都会建立一个Class对象
(2)连接
验证 是否有正确的内部结构,并和其他类协调一致
准备 负责为类的静态成员分配内存,并设置默认初始化值
解析 将类的二进制数据中的符号引用替换为直接引用
(3)初始化
2、类初始化时机
(1) 创建类的实例
(2)类的静态变量,或者为静态变量赋值
(3) 类的静态方法
(4)使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
(5)初始化某个类的子类(最先执行的是object类的registerNative方法,将对象注册到本地的操作系统)
(6) 直接使用java.exe命令来运行某个主类
3、类加载器
a.负责将.class文件加载到内在中,并为之生成对应的Class对象。
b.虽然我们不需要关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行
4、类加载器的组成
a.Bootstrap ClassLoader 根类加载器
也被称为引导类加载器,负责Java核心类的加载
比如System,String等。在JDK中JRE的lib目录下rt.jar文件中
b.Extension ClassLoader 扩展类加载器
负责JRE的扩展目录中jar包的加载。
在JDK中JRE的lib目录下ext目录。
c.System ClassLoader 系统类加载器
负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径。

二、反射
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法.所以先要获取到每一个字节码文件对应的Class类型的对象。
1、Class类(lang包下的)
阅读API的Class类得知,Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的

?获取Class对象的三种方式
方式一: 通过Object类中的getObject()方法
Person p = new Person();
Class c = p.getClass();

方式二: 通过 类名.class 获取到字节码文件对象(任意数据类型都具备一个class静态属性,看上去要比第一种方式简单)。
Class c2 = Person.class;

方式三: 通过Class类中的方法(将类名作为字符串传递给Class类中的静态方法forName即可)。
Class c3 = Class.forName("Person");
?注意:第三种和前两种的区别
前两种你必须明确Person类型.
后面是指定这种类型的字符串就行.这种扩展更强.我不需要知道你的类.我只提供字符串,按照配置文件加载就可以了
示例:

  1. package com.lm;
  2. import com.bean.Person;
  3. /*
  4. * 获取一个类的class文件对象的三种方式
  5. * 1、对象获取
  6. * 2、类名获取
  7. * 3、Class类的静态方法获取
  8. */
  9. public class ReflectDemo {
  10. public static void main(String[] args) {
  11. //1、对象获取
  12. Person p=new Person();
  13. //调用Person类的父类的方法getClass()
  14. Class c=p.getClass();
  15. System.out.println(c);
  16. //2、类名获取
  17. //每个类型,包括基本和引用,都会赋予一个静态属性,属性名为class
  18. Class c1=Person.class;
  19. System.out.println(c1);
  20. System.out.println(c.equals(c1)); //true
  21. System.out.println(c==c1); //true 只在类加载的时候产生一个class文件对象
  22. //3、Class类的静态方法获取forName(全包名+类名)
  23. try {
  24. Class c2=Class.forName("com.bean.Person");
  25. System.out.println(c2);
  26. } catch (ClassNotFoundException e) {
  27. // TODO Auto-generated catch block
  28. e.printStackTrace();
  29. }
  30. }
  31. }
复制代码

附Person类

  1. public class Person {
  2. //成员变量
  3. public String name;
  4. public int age;
  5. private String address;
  6. //构造方法
  7. public Person() {
  8. System.out.println("空参数构造方法");
  9. }
  10. public Person(String name) {
  11. this.name = name;
  12. System.out.println("带有String的构造方法");
  13. }
  14. //私有的构造方法
  15. private Person(String name, int age){
  16. this.name = name;
  17. this.age = age;
  18. System.out.println("带有String,int的构造方法");
  19. }
  20. public Person(String name, int age, String address){
  21. this.name = name;
  22. this.age = age;
  23. this.address = address;
  24. System.out.println("带有String, int, String的构造方法");
  25. }
  26. //成员方法
  27. //没有返回值没有参数的方法
  28. public void method1(){
  29. System.out.println("没有返回值没有参数的方法");
  30. }
  31. //没有返回值,有参数的方法
  32. public void method2(String name){
  33. System.out.println("没有返回值,有参数的方法 name= "+ name);
  34. }
  35. //有返回值,没有参数
  36. public int method3(){
  37. System.out.println("有返回值,没有参数的方法");
  38. return 123;
  39. }
  40. //有返回值,有参数的方法
  41. public String method4(String name){
  42. System.out.println("有返回值,有参数的方法");
  43. return "哈哈" + name;
  44. }
  45. //私有方法
  46. private void method5(){
  47. System.out.println("私有方法");
  48. }
  49. @Override
  50. public String toString() {
  51. return "Person [name=" + name + ", age=" + age + ", address=" + address+ "]";
  52. }
  53. }
复制代码

2、通过反射获取构造方法并使用
在反射机制中,把类中的成员(构造方法、成员方法、成员变量)都封装成了对应的类进行表示。其中,构造方法使用类Constructor表示。可通过Class类中提供的方法获取构造方法:
?返回一个构造方法
?public Constructor getConstructor(Class... parameterTypes) 获取public修饰, 指定参数类型所对应的构造方法
?public Constructor getDeclaredConstructor(Class... parameterTypes) 获取指定参数类型所对应的构造方法(包含私有的)
?返回多个构造方法
?public Constructor[] getConstructors() 获取所有的public 修饰的构造方法
?public Constructor[] getDeclaredConstructors() 获取所有的构造方法(包含私有的)
3、通过反射获取成员变量并使用
在反射机制中,把类中的成员变量使用类Field表示。可通过Class类中提供的方法获取成员变量:
?返回一个成员变量
?public Field getField(String name) 获取指定的 public修饰的变量
?public Field getDeclaredField(String name) 获取指定的任意变量
?返回多个成员变量
?public Field[] getFields() 获取所有public 修饰的变量
?public Field[] getDeclaredFields() 获取所有的 变量 (包含私有)
4、通过反射获取成员方法并使用
在反射机制中,把类中的成员方法使用类Method表示。可通过Class类中提供的方法获取成员方法:
?返回获取一个方法:
?public Method getMethod(String name, Class... parameterTypes)
获取public 修饰的方法
?public Method getDeclaredMethod(String name, Class... parameterTypes)
获取任意的方法,包含私有的
参数1: name 要查找的方法名称; 参数2: parameterTypes 该方法的参数类型
?返回获取多个方法:
?public Method[] getMethods() 获取本类与父类中所有public 修饰的方法
public Method[] getDeclaredMethods() 获取本类中所有的方法(包含私有的)
三、练习
1、泛型擦除

  1. package com.lm;
  2. import java.lang.reflect.InvocationTargetException;
  3. import java.lang.reflect.Method;
  4. import java.util.ArrayList;
  5. /*
  6. * 反射泛型擦除
  7. * 定义集合类,泛型String
  8. * 要求向集合中添加Integer类型
  9. *
  10. * 反射方式,获取出集合ArrayList类的class文件对象
  11. * 通过class文件对象,调用add方法
  12. */
  13. public class ReflectTest {
  14. public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
  15. ArrayList<String> array=new ArrayList<String>();
  16. array.add("a");
  17. Class c=array.getClass();
  18. //获取ArrayList.class文件中的方法add(编译后的class文件里没有泛型)
  19. Method method=c.getMethod("add",Object.class);//注意这里用的是Object
  20. //使用invoke运行ArrayList方法add
  21. method.invoke(array, 2);
  22. method.invoke(array, 12);
  23. method.invoke(array, 13);
  24. method.invoke(array, 14);
  25. System.out.println(array.size());
  26. System.out.println(array);
  27. //不能用增强for循环遍历,因为类型不一样,可以考虑迭代器
  28. }
  29. }
复制代码

2、反射配置文件
(1)通过反射配置文件,运行配置文件中指定类的对应方法
读取config.properties文件中的数据,通过反射技术,来完成Person对象的创建
config.properties文件内容如下:

  1. className=com.bean.Person
  2. methodName=eat
复制代码

(2)读取配置文件,调用指定类中的对应方法
package com.lm;

import java.io.FileReader;
import java.lang.reflect.Method;
import java.util.Properties;

/*

有可能调用Person的方法,有可能调用Student的方法,有可能调用worker的方法 类不确定,方法也不不确定 解决办法: 通过配置文件实现此功能 运行的类名和方法名,以键值对的形式写在文件里 运行哪个类就读取配置文件即可 实现步骤: 1、准备配置文件,键值对 2、IO读取配置文件,Reader 3、文件中的键值对存储到集合的Properties 集合保存的键值对,就是类名和方法名 4、反射获取指定类的class文件 5、class文件对象,获取指定的方法 6、运行方法
*/

public class Test {

  1. public static void main(String[] args) throws Exception {
  2. //IO读取配置文件
  3. FileReader r=new FileReader("config.properties");
  4. //创建集合对象
  5. Properties pro=new Properties();
  6. pro.load(r);
  7. r.close();
  8. String className=pro.getProperty("className");
  9. String methodName=pro.getProperty("methodName");
  10. Class c=Class.forName(className);
  11. Object obj=c.newInstance();
  12. Method method=c.getMethod(methodName);
  13. method.invoke(obj);
  14. }
复制代码

}
四、总结
(1)如何获取.Class文件对象
a, 通过Object类 getClass()方法获取 Class对象
b, 通过类名.class 方式 获取 Class对象
c, 通过反射的方式, Class.forName(String classname) 获取Class对象
public static Class forName(String className)throws ClassNotFoundException
返回与带有给定字符串名的类或接口相关联的 Class 对象

(2)通过反射, 获取类中的构造方法,并完成对象的创建
获取指定的构造方法
public Constructor getConstructor(Class... parameterTypes)
获取指定的public修饰的构造方法
public Constructor getDeclaredConstructor(Class... parameterTypes)
获取指定的构造方法,包含私有的
获取所有的构造方法
public Constructor[] getConstructors() 获取所有的public 修饰的构造方法
public Constructor[] getDeclaredConstructors() 获取所有的构造方法,包含私有的

(3)通过反射, 获取类中的构造方法,并完成对象的创建
步骤:
a,获取字节码文件对象
b,通过字节码文件对象 ,获取到指定的构造方法
getConstructor(参数);
c,通过构造方法,创建对象
public T newInstance(Object... initargs)

(4)私有构造方法,创建对象
a,获取字节码文件对象
b,通过字节码文件对象 ,获取到指定的构造方法
getDeclaredConstructor (参数);
c,暴力访问
con.setAccessible(true);
d,通过构造方法,创建对象
public T newInstance(Object... initargs)

(5)通过反射,获取Class文件中的方法
获取指定的方法
public Method getMethod(String name, Class... parameterTypes)
获取指定的public方法
public Method getDeclaredMethod(String name, Class... parameterTypes)
获取指定的任意方法,包含私有的
获取所有的方法
public Method[] getMethods() 获取本类与父类中所有public 修饰的方法
ublic Method[] getDeclaredMethods()获取本类中所有的方法,包含私有的

(6)通过反射,调用方法
步骤:
a,获取Class对象
b,获取构造方法,创建对象
c,获取指定的public方法
d,执行方法
public Object invoke(Object obj, Object... args)

(7)私有方法的调用:
a,获取Class对象
b,获取构造方法,创建对象
c,获取指定的private方法
d,开启暴力访问
m5.setAccessible(true);
e,执行方法
public Object invoke(Object obj, Object... args)

(8)通过反射,获取成员变量(字段)
获取指定的成员变量
public Field getField(String name) 获取public修饰的成员变量
public Field getDeclaredField(String name) 获取任意的成员变量,包含私有
获取所有的成员变量
public Field[] getFields() 获取所有public修饰的成员变量
public Field[] getDeclaredFields() 获取司所有的成员变量,包含私有

(9)通过反射,获取成员 变量,并赋值使用
步骤:
a,获取字节码文件对象
b,获取构造方法,创建对象
c,获取指定的成员变量
d,对成员变量赋值\获取值操作
public void set(Object obj, Object value) 赋值
public Object get(Object obj) 获取值

(10)私有成员变量的使用
步骤:
a,获取字节码文件对象
b,获取构造方法,创建对象
c,获取指定的成员变量
d,开启暴力访问
e,对成员变量赋值\获取值操作
public void set(Object obj, Object value) 赋值
public Object get(Object obj) 获取值



回复

使用道具 举报