Java匹马行天下之JavaSE核心技术——反射机制

Java反射机制

一、什么是反射?

    在运行状态中,对于任意一个类,都能够获取到这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性(包括私有的方法和属性),这种动态获取的信息以及动态调用对象的方法的功能就称为java语言的反射机制。通俗点讲,通过反射,该类对我们来说是完全透明的,想要获取任何东西都可以。

    想要使用反射机制,就必须要先获取到该类的字节码文件对象(.class),通过字节码文件对象,就能够通过该类中的方法获取到我们想要的所有信息(方法,属性,类名,父类名,实现的所有接口等等),每一个类对应着一个字节码文件也就对应着一个Class类型的对象,也就是字节码文件对象。

JAVA反射机制是在运行状态中,
  对于任意一个类,都能够知道这个类的所有属性和方法(动态获取的信息);
  对于任意一个对象,都能够调用它的任意一个方法和属性(动态调用对象的方法);
  这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制

  简言之:通过字节码文件对象,去使用该文件中的成员变量、构造方法、成员方法。

    获取字节码文件对象的三种方式。

       1、Class class1 = Class.forName("全限定类名");  //通过Class类中的静态方法forName,直接获取到一个类的字节码文件对象,此时该类还是源文件阶段,并没有变为字节码文件。

       2、Class class2  = Person.class;    //当类被加载成.class文件时,此时Person类变成了.class,在获取该字节码文件对象,也就是获取自己, 该类处于字节码阶段。

       3、Person p = new Person();

                              Class class3 = p.getClass();    //通过类的实例获取该类的字节码文件对象,该类处于创建对象阶段 

第一种和后两种的区别 
后两种你必须明确Person类型。
第一种需要这种类型的字符串就行(开发中用)。
这种扩展更强,不需要知道具体的类,只提供字符串,按照配置文件加载就可以了。

反射的特点:
        1.Class类中的静态方法forName()传入的字符串将来会做成配置信息文件,所以以后你不知道程序运行的是谁(是哪个类)
        2.反射是不会看到类的任何信息的。即通过构造方法对象Constructor、成员方法对象Method,调用他们的方法返回值都是Object类型。
          (因为任何类型都可以用Object类型接收,基本数据类型会自动装箱为引用数据类型)。
        3.反射可以访问私有的东西(前提是class文件未被加密)。

三种获取字节码文件对应的Class类型的对象的方式

  要想解剖一个类,必须先要获取到该类的字节码文件对象。
  而解剖使用的就是Class类中的方法,所以先要获取到每一个字节码文件对应的Class类型的对象。

  .class文件   --> Class类
    成员变量    --> Field类
    构造方法    --> Constructor类
    成员方法    --> Method类

    有了字节码文件对象才能获得类中所有的信息,我们在使用反射获取信息时,也要考虑使用上面哪种方式获取字节码对象合理,视不同情况而定。下面介绍Class类的功能。

二、具体操作实例

实例一:

package com.my.fanshe;
2
3 public class Person {
4 private String name;
5 int age;
6 public String address;
7
8 public Person() {
9 }
10
11 private Person(String name) {
12 this.name = name;
13 }
14
15 Person(String name, int age) {
16 this.name = name;
17 this.age = age;
18 }
19
20 public Person(String name, int age, String address) {
21 this.name = name;
22 this.age = age;
23 this.address = address;
24 }
25
26 public void show() {
27 System.out.println("show");
28 }
29
30 public void method(String s) {
31 System.out.println("method " + s);
32 }
33
34 public String getString(String s, int i) {
35 return s + "---" + i;
36 }
37
38 private void function() {
39 System.out.println("function");
40 }
41
42 @Override
43 public String toString() {
44 return "Person [name=" + name + ", age=" + age + ", address=" + address + "]";
45 }
46
47 }

  


package com.my.fanshe;
   /*
4 * 反射:就是通过class文件对象,去使用该文件中的成员变量,构造方法,成员方法。
5 *
6 * Person p = new Person();
7 * p.使用;
8 *
9 * 要想这样使用,首先你必须得到class文件对象,其实也就是得到Class类的对象。
10 * .class文件 --> Class类
11 * 成员变量 --> Field类
12 * 构造方法 --> Constructor类
13 * 成员方法 --> Method类
14 *
15 * 获取class文件对象的方式:
16 * A:Object类的getClass()方法
17 * B:数据类型的静态属性class(任意数据类型都具备一个class静态属性)
18 * C:Class类中的静态方法(将类名作为字符串传递给Class类中的静态方法forName)
19 * public static Class forName(String className)
20 *
21 * 一般我们到底使用谁呢?
22 * A:自己玩 任选一种,第二种比较方便
23 * B:开发时 第三种
24 * 为什么呢?因为第三种是一个字符串,而不是一个具体的类名。这样我们就可以把这样的字符串配置到配置文件中。
25 */
26 public class ReflectDemo {
27 public static void main(String[] args) throws ClassNotFoundException {
28 // 方式A
29 Person p = new Person();
30 Class c = p.getClass();
31
32 Person p2 = new Person();
33 Class c2 = p2.getClass();
34
35 System.out.println(p == p2); // false
36 System.out.println(c == c2); // true
37
38 // 方式B
39 Class c3 = Person.class;
40 // int.class;
41 // String.class;
42 System.out.println(c == c3); // true
43
44 // 方式C
45 // ClassNotFoundException 需要类的全路径(带包名的路径)
46 Class c4 = Class.forName("com.my.fanshe.Person");
47 System.out.println(c == c4); // true
48 }
49 }

  

实例二:

package com.my.fanshe;

public interface JieKou {
public void print();
}

  

package com.my.fanshe;

public class A implements JieKou {
private String name;
private int age; public A(String name, int age) {
this.name = name;
this.age = age;
} public String getName() {
return name;
} @Override
public void print() { System.out.println("实现A接口方法");
} @Override
public String toString() {
return "A{" +
"name='" + name + '\'' +
", age=" + age +
'}';
} public int getAge() {
return age;
}
}

  

package com.my.fanshe;

public class B implements JieKou {

    @Override
public void print() {
System.out.println("实现B接口方法");
}
}

  

package com.my.fanshe;

public class C {
public void show(){
System.out.println("实现C方法");
}
}

  

package com.my.fanshe;

public class FansheDemo {
public static void main(String[] args) throws Exception{
//方法一:通过对象获得Class类类型(静态加载)
A a = new A("张三",20);
Class c = a.getClass();
System.out.println("Class类类型为:"+c);
//方法二:通过类名获得Class类类型
Class c1 = B.class;
System.out.println("Class类类型为:"+c1);
//方法三:通过动态加载
Class c2 = Class.forName("com.my.fanshe.C");
System.out.println("Class类类型为:"+c2);
}
}

  

运行结果:

Class类类型为:class com.my.fanshe.A
Class类类型为:class com.my.fanshe.B
Class类类型为:class com.my.fanshe.C

  

2、获取类的信息

package com.my.fanshe;

public interface JieKou {
public void print();
}

  

package com.my.fanshe;

public class A implements JieKou {
public String name;
private int age; public A(String name, int age) {
this.name = name;
this.age = age;
} public String getName() {
return name;
} @Override
public void print() { System.out.println("实现A接口方法");
} @Override
public String toString() {
return "A{" +
"name='" + name + '\'' +
", age=" + age +
'}';
} public int getAge() {
return age;
} }

  

package com.my.fanshe;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method; public class FansheDemo {
public static void main(String[] args) throws Exception{
//动态加载类,Class是表示当前类的类类型
//获取字节码文件
Class c = Class.forName("com.my.fanshe.A");
// 通过Class类类型获得类的实例
Constructor constructor = c.getConstructor(String.class,int.class);
A a = (A)constructor.newInstance("张三",20);
//获取公共属性的成员变量
Field f = c.getField("name");
System.out.println(f.get(a));
//获取私有属性的成员变量
Field f1 = c.getDeclaredField("age");
System.out.println(f1.getType()+" "+f1.getName());
//打开权限
f1.setAccessible(true);
System.out.println(f1.get(a));
//获取方法并执行
Method method = c.getMethod("getName");
System.out.println(method.invoke(a)); Method method1 = c.getMethod("getAge");
System.out.println(method1.invoke(a)); Method method2 = c.getMethod("print");
method2.invoke(a); Method method3 = c.getMethod("toString");
System.out.println(method3.invoke(a)); }
}

  

运行结果:

张三
int age
20
张三
20
实现A接口方法
A{name='张三', age=20}

  

3、反射思维导图

Java匹马行天下之JavaSE核心技术——反射机制

三.反射相关理解的知识

1:反射(理解)
(1)类的加载
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载连接初始化三步来实现对这个类进行初始化。 加载
就是指将class文件读入内存,并为之创建一个Class对象。
任何类被使用时系统都会建立一个Class对象。
连接
验证:是否有正确的内部结构,并和其他类协调一致。
准备:负责为类的静态成员分配内存,并设置默认初始化值。
解析:将类的二进制数据中的符号引用替换为直接引用
初始化
就是我们以前讲过的初始化步骤。 注意:Object类的方法
public final Class getClass() 返回对象的字节码文件对象
Class类的方法
public String getName() 以 String 的形式返回此 Class 对象所表示的实体名称。(实体包括:类、接口、数组名、基本类型或 void)
即:可以通过Class类中的一个方法,获取对象的真实类的全名称

(2)类的初始化时机
1.创建类的实例时。
2.访问类的静态变量,或者为静态变量赋值时。
3.调用类的静态方法时。
4.使用反射方式来强制创建某个类或接口对应的java.lang.Class对象时。
5.初始化某个类的子类时。
6.直接使用java.exe命令来运行某个主类时。

(3
)类加载器
负责将.class文件加载到内存中,并为之生成对应的Class对象。
虽然我们不需要关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行。
(4)类加载器的组成
Bootstrap ClassLoader 类加载器
Extension ClassLoader 扩展类加载器
Sysetm ClassLoader 系统类加载器 Bootstrap ClassLoader 类加载器
也被称为引导类加载器,负责Java核心类的加载。
比如System类,String类等。在JDK中JRE的lib目录下rt.jar文件中(JDK8以前版本中的位置,JDK9/10位置变化了)。 Extension ClassLoader 扩展类加载器
负责JRE的扩展目录中jar包的加载。
在JDK中JRE的lib目录下ext目录。 Sysetm ClassLoader 系统类加载器
负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径。
一般我们自己写的类通过系统类加载器来加载的。

如若对你有用,记得推荐,如若有误,欢迎指正!

本人原创,转载请说明出处https://www.cnblogs.com/zyx110/

上一篇:Linux安装php的Redis扩展


下一篇:springboot之web工程RedisTemplate的哨兵模式配置