好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

Java反射机制基础详解

1、什么是Java反射机制?

在程序运行中动态地获取类的相关属性,同时调用对象的方法和获取属性,这种机制被称之为Java反射机制

下面给出一个反射的简单例子:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

import lombok.Data;

 

@Data

public class User {

 

     public String username;

     private String password;

 

     @Override

     public String toString() {

         return "User{" +

                 "username='" + username + '\ '' +

                 ", password='" + password + '\ '' +

                 '}' ;

     }

}

?

1

2

3

4

5

6

7

8

9

10

11

12

public static void reflectionSimpleExample() throws Exception{

     User user = new User();

     System.out.println(user.toString());

     // 获取对应类的class

     Class<?> cls = Class.forName( "com.example.core.example.reflection.domain.User" );

     Object obj = cls.newInstance();

     System.out.println(obj);

     // 获取成员变量,注意要是public的

     Field field = cls.getField( "username" );

     System.out.println(field.get(obj));

 

}

2、反射机制原理

Java反射是Java实现动态语言的关键,也就是通过反射实现类动态加载

静态加载: 在编译时加载相关的类,如果找不到类就会报错,依赖性比较强动态加载:在运行时加载需要的类,在项目跑起来之后,调用才会报错,降低了依赖性

例子:静态加载,如下代码,如果找不到类的情况,代码编译都不通过

?

1

User user = new User();

而动态加载,就是反射的情况,是可以先编译通过的,然后在调用代码时候,也就是运行时才会报错

?

1

2

Class<?> cls = Class.forName("com.example.core.example.reflection.User");

Object obj = cls.newInstance();

Exception in thread [main] java.lang.ClassNotFoundException: com.example.core.example.reflection.User

java中的反射允许程序在执行期借助jdk中Reflection API来获取类的内部信息,比如成员变量、成员方法、构造方法等等,并能操作类的属性和方法

java中反射的实现和jvm和类加载机制有一定的关系,加载好类之后,在jvm的堆中会产生一个class类型的对象,这个class类包括了类的完整结构信息,通过这个class对象就可以获取到类的结构信息,所以形象地称之为java反射

3、Class类介绍

3.1、Class类基本介绍

然后这个Class类是什么?看下uml类图:

Class也是类,因此也继承Object类

Class不是直接new出来的,而是经过系统创建的

?

1

User user = new User();

打个断点,debug进行看看源码,可以看到传统的直接new一个对象也是通过类加载器loadClass拿到的

java反射方式,通用通过调试看看:

?

1

Class<?> cls = Class.forName("com.example.core.example.reflection.domain.User");

同样本质也是通过ClassLoad再通过类的全类名

3.2、Class类对象的获取方法 Class.forname()

?

1

Class<?> cls = Class.forName("com.example.core.example.reflection.domain.User");

应用场景:多用于读取类全路径,加载类

具体类.class
已经知道具体类的情况,通过具体类的class属性获取

?

1

Class u = User.class;

应用场景:多用于用于参数传递

对象.getClass
已经创建好对象的情况,直接通过对象实例获取class对象

?

1

Class cls = user.getClass();

应用场景:通过创建好的对象,获取Class对象

ClassLoader获取

?

1

2

ClassLoader cl = user.getClass().getClassLoader();

Class cls = cl.loadClass( "com.example.core.example.reflection.domain.User" );

基本数据类型

?

1

Class cls = int . class ;

包装类

基本数据类型对应的包装类可以通过.TYPE得到Class类对象

?

1

Class cls = Integer.TYPE;

3.3 、可以获取Class对象的类型

1、外部类,成员内部类,静态内部类,局部内部类,匿名内部类

2、interface:接口

3、数组

4、enum:枚举

5、annotation:注解

6、基本数据类型

7、void8、Class… ,等等

?

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

import com.example.core.example.reflection.domain.User;

import lombok.ToString;

 

import java.util.List;

 

public class GetClassObjectExample {

 

     public static void main(String[] args) {

         // 外部类

         Class<User> cls1 = User. class ;

         // 接口

         Class<List> cls2 = List. class ;

         // 数组

         Class<Integer[]> cls3 = Integer[]. class ;

         // 二维数组

         Class<String[][]> cls4 = String[][]. class ;

         // 注解

         Class<lombok.ToString> cls5 = ToString. class ;

 

         // 枚举

         Class<Thread.State> cls6 = Thread.State. class ;

         // 基本数据类型

         Class<Long> cls7 = Long. class ;

         // void 数据类型

         Class<Void> cls8 = Void. class ;

         // Class

         Class<Class> cls9 = Class. class ;

 

         System.out.println(cls1);

         System.out.println(cls2);

         System.out.println(cls3);

         System.out.println(cls4);

         System.out.println(cls5);

         System.out.println(cls6);

         System.out.println(cls7);

         System.out.println(cls8);

         System.out.println(cls9);

     }

}

4、java反射的作用?

可以通过外部类的全路径名创建对象,并使用这些类可以枚举出类的全部成员,包括构造函数、属性、方法利用反射 API 访问类的私有成员

5、反射API主要类

1、java.lang.Class:代表一个类,表示某个类在jvm堆中的对象

2、 java.lang.reflect.Method:代表类的方法

3、 java.lang.reflect.Field:代表类的成员变量

4、 java.lang.reflect.Constructor:代表类额构造方法

6、Java反射的优缺点

优点:使用Java反射可以灵活动态地创建和使用对象,反射是框架的底层支撑缺点:使用Java反射,基本就是解释执行的,对执行速度是有影响的

7、反射调用的优化方法

前面介绍了Java反射虽然很灵活,但是缺点就是调用时候比较慢,相对直接new对象来说,情况是怎么样的?下面通过例子进行试验:

?

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

import com.example.core.example.reflection.domain.User;

 

import java.lang.reflect.Method;

 

public class TestReflectionExample {

 

     private static final Integer TOTAL_COUNT = 1000000 ;

 

     public static void main(String[] args) throws Exception{

         test0();

         test1();

         test2();

     }

 

     public static void test0() {

         long start = System.currentTimeMillis();

         User user = new User();

         for ( int i = 0 ; i < TOTAL_COUNT; i++) {

             user.hello();

         }

         System.out.println(String.format( "传统调用方法执行时间:%d" , System.currentTimeMillis() - start));

     }

 

     public static void test1() throws Exception{

         long start = System.currentTimeMillis();

         Class<?> cls = Class.forName( "com.example.core.example.reflection.domain.User" );

         Object obj = cls.newInstance();

         Method hello = cls.getMethod( "hello" );

         for ( int i = 0 ; i < TOTAL_COUNT; i++) {

             hello.invoke(obj);

         }

         System.out.println(String.format( "反射方法调用执行时间:%d" , System.currentTimeMillis() - start));

     }

 

     public static void test2() throws Exception{

         long start = System.currentTimeMillis();

         Class<?> cls = Class.forName( "com.example.core.example.reflection.domain.User" );

         Object obj = cls.newInstance();

         Method hello = cls.getMethod( "hello" );

         // 关键,取消调用反射方法时的安全检查

         hello.setAccessible( true );

         for ( int i = 0 ; i < TOTAL_COUNT; i++) {

             hello.invoke(obj);

         }

         System.out.println(String.format( "优化后的反射方法调用执行时间:%d" , System.currentTimeMillis() - start));

     }

}

传统调用方法执行时间:19
反射方法调用执行时间:112
优化后的反射方法调用执行时间:50

8、反射的基本使用例子

java.lang.Class类

方法名 作用
getName 获取全类名
getSimpleName 获取简单类名
getFields 获取所有 public 修饰的属性、包括本类以及父类的
getDeclaredFields 获取本类中所有的属性
getMethods 获取所有的 public 修饰的方法,包括本类以及父类的
getDeclaredMethod 获取本类中所有方法
getConstructors 获取所有 public 修饰的构造器,只有本类
getDeclaredConstructors 获取本类中所有构造器
getPackage 以 Package 形式返回 包信息
getSuperClass 以 Class 形式返回父信息
getInterfaces 以 Class[] 形式返回父接口信息
getAnnotations 以 Annotation[] 形式返回注解信息

?

1

2

3

4

5

6

7

8

9

10

11

12

13

String allClassName = "com.example.core.example.reflection.domain.User" ;

// 通过全类名获取class对象

Class<?> cls = Class.forName(allClassName);

System.out.println(cls);

// 通过对象获取class

System.out.println(cls.getClass());

// 获取全类名

System.out.println(cls.getName());

// 获取包名

System.out.println(cls.getClass().getPackage().getName());

// 获取对象实例

Object obj = cls.newInstance();

System.out.println(obj);

java.lang.reflect.Field类

方法名 作用
getModifiers 以int形式返回修饰符,默认修饰符是0,public是1,private是2,protected是4,static是8,final是16
getType 以Class形式返回类型
getName 返回属性名称

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

// 获取类属性

Field field = cls.getField( "username" );

field.set(obj , "admin" );

System.out.println(field.get(obj));

// 获取所有类属性,private的成员变量没有权限访问

Field[] fields = cls.getFields();

for (Field field1 : fields) {

     System.out.println(field1.get(obj));

}

// 获取所有类属性包括private成员变量

Field[] allFields = cls.getDeclaredFields();

for (Field afield : allFields) {

     // 开放权限,私有的成员变量也能打印出来

     afield.setAccessible( true );

     System.out.println(afield.get(obj));

}

java.lang.reflect.Method类

方法名 作用
getModifiers 以int形式返回修饰符,默认修饰符是0,public是1,private是2,protected是4,static是8,final是16
getName 返回方法名
getReturnType 以class形式返回类型
getParmeterTypes 以Class[] 返回参数类型数组

?

1

2

3

// 获取class方法,同样默认情况不能获取private的方法

Method method = cls.getMethod( "hello" );

System.out.println(method.invoke(obj));

java.lang.reflect.Constructor类

方法名 作用
getModifiers 以int形式返回修饰符,默认修饰符是0,public是1,private是2,protected是4,static是8,final是16
getName 返回方法名
getParmeterTypes 以Class[] 返回参数类型数组

?

1

2

3

4

5

Class<?> cls = Class.forName( "com.example.core.example.reflection.domain.User" );

Constructor<?> con = cls.getDeclaredConstructor();

con.setAccessible( true );

Object obj = con.newInstance();

System.out.println(obj);

9、反射开放权限操作

在我们使用Java反射获取class的private成员变量或者方法时,这种情况是不允许获取的,不过可以通过开放权限的方式来处理,通过设置setAccessible(true);即可

?

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

import java.lang.reflect.Constructor;

import java.lang.reflect.Field;

import java.lang.reflect.Method;

 

/**

  * <pre>

  *      开放java反射权限,允许调用类的private属性或者方法

  * </pre>

  * <p>

  * <pre>

  * @author mazq

  * 修改记录

  *    修改后版本:     修改人:  修改日期: 2021/08/09 19:10  修改内容:

  * </pre>

  */

public class AccessibleReflectionExample {

 

     public static void main(String[] args) throws Exception{

 

         test();

     }

 

     public static void test() throws Exception{

         // 创建class实例对象

         Class<?> cls = Class.forName( "com.example.core.example.reflection.domain.User" );

         Constructor<?> con = cls.getDeclaredConstructor();

         con.setAccessible( true );

         Object obj = con.newInstance();

 

         // 获取私有成员变量

         Field pwd = cls.getDeclaredField( "password" );

         // 开放私有变量的访问权限

         pwd.setAccessible( true );

         pwd.set(obj , "123456" );

 

         // 私有方法调用

         Method method = cls.getDeclaredMethod( "priToString" );

         method.setAccessible( true );

         System.out.println(method.invoke(obj));

     }

}

总结

本篇文章就到这里了,希望能给你带来帮助,也希望您能够多多关注的更多内容!

原文链接:https://blog.csdn.net/u014427391/article/details/119518788

查看更多关于Java反射机制基础详解的详细内容...

  阅读:19次