java反射机制

一,Java 反射机制概述

本质上Java反射是在 运行中 可以动态的加载某一个类,并且能知道这个类的所有的方法;对于任意一个 对象都可以动态的调用这个对象的某一个方法 ,这种动态获取信息及动态调用方法的功能称为Java的反射机制

二,问题

我们需要动态的获取某一个类的信息,以及这个它的 属性方法,能否调用它的任意的一个方法?

答案:当然是可以的

三,反射的基础

要先理解Class 这个类,因为Class 这个类是反射的基础:

3.1:那么 Class 算是一个对象吗?

Java 是一项面对对象语言,万物皆对象,对象是一个类的实例,所以类是java.lang.Class类的实例对象,而 Class 是所有类的类(This is a class named Class),Class 类是类的类型,也就是类的类型。

3.2:字节码文件:

.class 被称为字节码文件,这个文件会被 load 到内存中,也可以看做是一个对象

3.3:字节码文件的对象的类型是什么?

Class 类型

3.4:获取类字节码文件的三种方式。

1,三种方式代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public static void main(String[] args) throws ClassNotFoundException {

// Object类中的getClass()方法
// 字节码加载时机: new一个类的时候

Person person = new Person(); // 只会加载一次 所以是相同的 创建对象根据 对象.class 来创建
Person person1 = new Person();


// 一个类的字节码文件对象 在整个运行过程当中 只会加载一次
Class<? extends Person> aClass = person.getClass();
Class<? extends Person> aClass1 = person1.getClass();

System.out.println(aClass == aClass1); //true

System.out.println(person.equals(person1));

// 类型 Class 属性
Class<Integer> integerClass = int.class;

// 使用forName方法
Class.forName("com.bjq.reflection.Student");



}

2,字节码文件加载时机:

  • new 一个类的时候
  • 访问一个类的静态成员的时候
  • 调用一个类的静态方法的时候
  • 通过反射的方式床 个类的字节码对象的时候
  • 创建 个子类对象的时候
  • java 命令执行 个字节码文件的时候

3,字节码文件对象的组成:

  • .class 文件对应的是 –> class 类来的

  • 反射就 是用来 获取字节码文件对象里面的内容

四、获取类的信息

4:获取字节码文件对象中的构造方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package com.bjq.reflection;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class TestConstructor {

public static void main(String[] args) {

Person person = new Person("你好",18);
// 1. 获取字节码文件对象
Class clazz = Person.class;
// 2.获取里面的构造方法
Constructor[] constructors = clazz.getConstructors();

// 3. 获取构造方法
// for (Constructor constructor : constructors) {
//
// System.out.println(constructor);
//
// }

// 4.获取指定的构造方法对象
try {
Constructor constructor = clazz.getConstructor(String.class);
// 用构造器对象创建类的实例 里面的参数和字节码文件对象对应的类型
Object object = constructor.newInstance("李四");

System.out.println(object);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}

}

}

Person.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package com.bjq.reflection;

import java.util.ArrayList;
import java.util.List;

public class Person {

// 单例模式
//private static final Person per = new Person();
// 多例模式
//private static List<Person> personList = new ArrayList<Person>();

private String name;
private int age;

// 设置为private 防止外部类 new 这个类


public Person(String name, int age) {
this.name = name;
this.age = age;
}

public Person(String name) {
this.name = name;
}

public Person(int age) {
this.age = age;
}

public Person() {
}

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 void name(){
System.out.println("你好,这是我的名字");
}


@Override
public String toString() {
return "haha";
}
}

4.1:获取字节码文件对象中的私有构造方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.bjq.reflection.v1;


import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

// 用反射完整的创建一个类的实例
public class TestDeclaredConstructor {

public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {

//Person person = new Person();

// 得到字节码文件对象
Class clazz = Person.class;

// 得到对象类中的私有构造方法对象
Constructor declaredConstructor = clazz.getDeclaredConstructor();

// 设置暴力访问
declaredConstructor.setAccessible(true);

// 用反射的方式创建该类对象
Object object = declaredConstructor.newInstance();

System.out.println(object);

}

}

Person.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
package com.bjq.reflection.v1;

public class Person {

// 单例模式
//private static final Person per = new Person();
// 多例模式
//private static List<Person> personList = new ArrayList<Person>();

private String name;
private int age;

// 设置为private 防止外部类 new 这个类


public Person(String name, int age) {
this.name = name;
this.age = age;
}

public Person(String name) {
this.name = name;
}

public Person(int age) {
this.age = age;
}

private Person() {
}

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 void name(){
System.out.println("你好,这是我的名字");
}


@Override
public String toString() {
return "haha";
}
}

4.2:除了上面这种方式呢还有一种便携的方式(不直接使用构造器对象) JDK 会自动帮我们使用:

使用下面这种方式呢,有几个缺陷,都写在了 注释当中, 当然也可以动手实验 即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.bjq.reflection.v2;


import com.bjq.reflection.v1.Person;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

// 还有便携的一种方式 也可以获取
public class TestDemo {

public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {

/**
* 以这种方式获取有以下的缺陷
* 1. 无法获取构造方法为 private
* 2. 只能获取无参的构造方法
*/
Class clazz = Student.class;
Object object = clazz.newInstance();
System.out.println(object);

}

}

Student.java

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.bjq.reflection.v2;

public class Student {


private Student() {
}

@Override
public String toString() {
return "哈哈";
}
}

4.3:利用反射调用Student类的 Method:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package com.bjq.reflection.v3;


import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;


//利用反射调用Student类的method
public class TestDemo {

public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {

// 得到文件字节码文件对象
Class clazz = Student.class;
// 得到字节码对象中所有的Method对象
/**
Method[] methods = clazz.getMethods();

for (Method method : methods) {
System.out.println(method);
}
**/
// 得到特定的方法
// 第一个参数 : 方法的名字
// 第二个参数: 参数类型的字节码文件对象 如果是String 可以写String.class 如果没有写成 null
Method name = clazz.getDeclaredMethod("eat",null);


/**
* 正常的方法调用
* 对象名.方法名(参数)
* 反射的调用方式是反过来的
* 方法名.对象 + (方法对象.invoke(对象,参数))
*/
// 暴力访问
name.setAccessible(true);

name.invoke(clazz.newInstance(),null);

//System.out.println(name);

}

}

Student.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package com.bjq.reflection.v3;

public class Student {

private String name;

private int age;

public Student(String name, int age) {
this.name = name;
this.age = age;
}

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 Student() {
}

// 换成private
private void eat(){
System.out.println("this is myname");
}

@Override
public String toString() {
return "哈哈";
}
}

4.4:利用反射调用Student类的Field对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package com.bjq.reflection.v4;


import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

//利用反射调用Student类的Field对象
public class TestDemo {

public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {

// 得到文件字节码文件对象
Class clazz = Student.class;
Object object = clazz.newInstance();
Student student = new Student();
// 得到Field对象
/**
* Field[] fields = clazz.getFields();
* for (Field field : fields) {
System.out.println(field);
* }
*/
// 得到指定Field对象
Field field1 = clazz.getField("name"); // 得到 public的 值
Field field2 = Student.class.getDeclaredField("age"); // 得到 private 的值
// 给Field 设置值
field2.setAccessible(true);
field1.set(student,"你好");

field2.set(student,18);

System.out.println(field2.get(student));
}
}

Student.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.bjq.reflection.v4;

//利用反射访问Student类的 parameter
public class Student {

public String name;

private int age;

public Student(){

}

// 换成private
private void eat(){
System.out.println("this is myname");
}

@Override
public String toString() {
return "哈哈";
}
}

五,总结

反射是用来干嘛的,反射可以简单理解为一个程序中 外挂反射打破了 Java语言的封装性 (这样做有好处,当然也有坏处)在反射面前所有的 类 方法,属性都是裸奔的。

反射的特点就是:

在运行期间可以动态的加载某一个类进行,动态的new 一个对象 ,动态的去调用这个对象某一个方法。

ENd