反射基础

在应用反射机制之前,首先我们先来看一下如何获取一个对象对应的反射类Class,在Java中我们有三种方法可以获取一个对象的反射类。

通过getClass方法

在Java中,每一个Object都有一个getClass()方法,通过getClass方法我们可以获取到这个对象对应的反射类:

1

2

String s = "ziwenxie";

Class<?> c = s.getClass();

通过forName方法

我们也可以调用Class类的静态方法forName()

1

Class<?> c = Class.forName("java.lang.String");

使用.class

或者我们也可以直接使用.class

1

Class<?> c = String.class;

获取类型信息

在文章开头我们就提到反射的一大好处就是可以允许我们在运行期间获取对象的类型信息,下面我们通过一个例子来具体看一下。

首先我们在typeinfo.interfacea包下面新建一个接口A

1

2

package typeinfo.interfacea;

public interface A { void f(); }

接着我们在typeinfo.packageaccess包下面新建一个接口C,接口C继承自接口A,并且我们还另外创建了几个用于测试的方法,注意下面几个方法的权限都是不同的。

1

2

3

4

5

6

7

8

9

10

11

12

package typeinfo.packageaccess;

import typeinfo.interfacea.A;

class implements A {

    public void f() { System.out.println("public C.f()"); }

    public void g() { System.out.println("public C.g()"); }

    protected void v () { System.out.println("protected C.v()"); }

    void u() { System.out.println("package C.u()"); }

    private void w() { System.out.println("private C.w()"); }

}

public class HiddenC {

    public static A makeA() { return new C(); }

}

callHiddenMethod()方法中我们用到了几个新的API,其中getDeclaredMethod()根据方法名用于获取Class类指代对象自己声明的某个方法,然后我们通过调用invoke()方法就可以触发对象的相关方法:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

package typeinfo;

import typeinfo.interfacea.A;

import typeinfo.packageaccess.HiddenC;

import java.lang.reflect.Method;

public class HiddenImplementation {

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

        A a = HiddenC.makeA();

        a.f();

        System.out.println(a.getClass().getName());

        // Oops! Reflection still allows us to call g():

        callHiddenMethod(a, "g");

        // And even methods that are less accessible!

        callHiddenMethod(a, "u");

        callHiddenMethod(a, "v");

        callHiddenMethod(a, "w");

    }

    static void callHiddenMethod(Object a, String methodName) throws Exception {

        Method g = a.getClass().getDeclaredMethod(methodName);

        g.setAccessible(true);

        g.invoke(a);

    }

}

从输出结果我们可以看出来,不管是publicdefaultprotect还是pricate方法,通过反射类我们都可以自由调用。当然这里我们只是为了显示反射的强大威力,在实际开发中这种技巧还是不提倡。

1

2

3

4

5

6

public C.f()

typeinfo.packageaccess.C

public C.g()

package C.u()

protected C.v()

private C.w()

上面我们只是测试了Method对象,感兴趣的读者在熟悉了反射的API之后,不妨测试一下Filed,这里我们就不重复了。

与注解相结合

在单元测试框架比如Junit中反射机制也得到了广泛的应用,即通过注解的方式。下面我们简单地来了解一下如何通过反射机制来获取相关方法的注解信息,比如说我们有下面这样一个业务场景,当用户在修改自己密码的时候,为了保证密码的安全性,我们要求用户的新密码要满足一些条件,比如说至少要包含一个非数字字符,不能与以前的密码相同之类的条件等。

1

2

3

4

5

6

7

import java.lang.annotation.*

@Target(ElementType.METHOD)

@Retention(RetentionPolicy.RUNTIME)

public @interface UserCase {

    public int id();

    public String description() default "no description";

}

下面是我们检测密码的工具类的实现:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

public class PasswordUtils {

    @UserCase(id=47, description="Password must contain at least one numeric")

    public boolean validatePassword(String password) {

        return (password.matches("\\w*\\d\\w*"));

    }

    @UserCase(id=48)

    public String encryptPassword(String password) {

        return new StringBuilder(password).reverse().toString();

    }

    @UserCase(id=49, description="New passwords can't equal previously used ones")

    public boolean checkForNewPassword(List<String> prevPasswords, String password) {

        return !prevPasswords.contains(password);

    }

}

利用反射我们可以写出更加清晰的测试代码,其中getDeclaredMethods()方法可以获取相关对象自己声明的相关方法,而getAnnotation()则可以获取Method对象的指定注解。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

public class UseCaseTracker {

    public static void trackUseCases(List<Integer> useCases, Class<?> cl) {

        for(Method m : cl.getDeclaredMethods()) {

            UseCase uc = m.getAnnotation(UseCase.class);

            if(uc != null) {

                System.out.println("Found Use Case: " + uc.id() + " " + uc.description());

                useCases.remove(new Integer(uc.id()));

            }

        }

        for(int i : useCases) {

            System.out.println("Warning: Missing use case-" + i);

        }

    }

    public static void main(String[] args) {

        List<Integer> useCases = new ArrayList<Integer>();

        Collections.addAll(useCases, 47484950);

        trackUseCases(userCases, PasswordUtils.class);

    }

}

解决泛型擦除

现在有下面这样一个业务场景,我们有一个泛型集合类List<Class<? extends Pet>>,我们需要统计出这个集合类中每种具体的Pet有多少个。由于Java的泛型擦除,注意类似List<? extends Pet>的做法肯定是不行的,因为编译器做了静态类型检查之后,到了运行期间JVM会将集合中的对象都视为Pet,但是并不会知道Pet代表的究竟是Cat还是Dog,所以到了运行期间对象的类型信息其实全部丢失了。

为了实现我们上面的例子,我们先来定义几个类:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

public class Pet extends Individual {

    public Pet(String name) { super(name); }

    public Pet() { super(); }

}

public class Cat extends Pet {

    public Cat(String name) { super(name); }

    public Cat() { super(); }

}

public class Dog extends Pet {

    public Dog(String name) { super(name); }

    public Dog() { super(); }

}

public class EgyptianMau extends Cat {

    public EgyptianMau(String name) { super(name); }

    public EgyptianMau() { super(); }

}

public class Mutt extends Dog {

    public Mutt(String name) { super(name); }

    public Mutt() { super(); }

}

上面的Pet类继承自IndividualIndividual类的的实现稍微复杂一点,我们实现了Comparable接口,重新自定义了类的比较规则,如果不是很明白的话,也没有关系,我们已经将它抽象出来了,所以不理解实现原理也没有关系。

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

public class Individual implements Comparable<Individual> {

    private static long counter = 0;

    private final long id = counter++;

    private String name; // name is optional

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

    public Individual() {}

    public String toString() {

        return getClass().getSimpleName() + (name == null "" " " + name);

    }

    public long id() { return id; }

    public boolean equals(Object o) {

        return instanceof Individual && id == ((Individual)o).id;

    }

    public int hashCode() {

        int result = 17;

        if (name != null) {

            result = 37 * result + name.hashCode();

        }

        result = 37 * result + (int) id;

        return result;

    }

    public int compareTo(Individual arg) {

        // Compare by class name first:

        String first = getClass().getSimpleName();

        String argFirst = arg.getClass().getSimpleName();

        int firstCompare = first.compareTo(argFirst);

        if (firstCompare != 0) {

            return firstCompare;

        }

        if (name != null && arg.name != null) {

            int secendCompare = name.compareTo(arg.name);

            if (secendCompare != 0) {

                return secendCompare;

            }

        }

        return (arg.id < id ? -1 : (arg.id == id ? 0 1));

    }

}

下面创建了一个抽象类PetCreator,以后我们通过调用arrayList()方法便可以直接获取相关Pet类的集合。这里使用到了我们上面没有提及的newInstance()方法,它会返回Class类所真正指代的类的实例,这是什么意思呢?比如说声明new Dog().getClass().newInstance()和直接new Dog()是等价的。

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

public abstract class PetCreator {

    private Random rand = new Random(47);

    // The List of the different getTypes of Pet to create:

    public abstract List<Class<? extends Pet>> getTypes();

    public Pet randomPet() {

        // Create one random Pet

        int n = rand.nextInt(getTypes().size());

        try {

            return getTypes().get(n).newInstance();

        catch (InstantiationException e) {

            throw new RuntimeException(e);

        catch (IllegalAccessException e) {

            throw new RuntimeException(e);

        }

    }

    public Pet[] createArray(int size) {

        Pet[] result = new Pet[size];

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

           result[i] = randomPet();

        }

        return result;

    }

    public ArrayList<Pet> arrayList(int size) {

        ArrayList<Pet> result = new ArrayList<Pet>();

        Collections.addAll(result, createArray(size));

        return result;

    }

}

接下来我们来实现上面这一个抽象类,解释一下下面的代码,在下面的代码中,我们声明了两个集合类,allTypestypes,其中allTypes中包含了我们呢上面所声明的所有类,但是我们具体的类型实际上只有两种即MuttEgypianMau,所以我们真正需要new出来的宠物只是types中所包含的类型,以后我们通过调用getTypes()便可以得到types中所包含的所有类型。

1

2

3

4

5

6

7

8

9

10

public class LiteralPetCreator extends PetCreator {

    @SuppressWarnings("unchecked")

    public static final List<Class<? extends Pet>> allTypes = Collections.unmodifiableList(

        Arrays.asList(Pet.class, Dog.class, Cat.class, Mutt.class, EgyptianMau.class));

    private static final List<Class<? extends Pet>> types = allTypes.subList(

        allTypes.indexOf(Mutt.class), allTypes.size());

    public List<Class<? extends Pet>> getTypes() {

        return types;

    }

}

总体的逻辑已经完成了,最后我们实现用来统计集合中相关Pet类个数的TypeCounter类。解释一下isAssignalbeFrom()方法,它可以判断一个反射类是某个反射类的子类或者间接子类。而getSuperclass()顾名思义就是得到某个反射类的父类了。

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

public class TypeCounter extends HashMap<Class<?>, Integer> {

    private Class<?> baseType;

    public TypeCounter(Class<?> baseType) {

        this.baseType = baseType;

    }

    public void count(Object obj) {

        Class<?> type = obj.getClass();

        if (!baseType.isAssignableFrom(type)) {

            throw new RuntimeException(

                obj + " incorrect type " + type + ", should be type or subtype of " + baseType);

        }

        countClass(type);

    }

    private void countClass(Class<?> type) {

        Integer quantity = get(type);

        put(type, quantity == null 1 : quantity + 1);

        Class<?> superClass = type.getSuperclass();

        if (superClass != null && baseType.isAssignableFrom(superClass)) {

            countClass(superClass);

        }

    }

    @Override

    public String toString() {

        StringBuilder result = new StringBuilder("{");

        for (Map.Entry<Class<?>, Integer> pair : entrySet()) {

            result.append(pair.getKey().getSimpleName());

            result.append("=");

            result.append(pair.getValue());

            result.append(", ");

        }

        result.delete(result.length() - 2, result.length());

        result.append("} ");

        return result.toString();

    }

}

喜欢的朋友可以点赞关注,一起学习进步

Java反射机制应用实践的更多相关文章

  1. Java 反射机制应用实践

    反射基础 p.s: 本文需要读者对反射机制的API有一定程度的了解,如果之前没有接触过的话,建议先看一下官方文档的Quick Start(https://docs.oracle.com/javase/ ...

  2. 一个例子让你了解Java反射机制

    本文来自:blog.csdn.net/ljphhj JAVA反射机制: 通俗地说,反射机制就是可以把一个类,类的成员(函数,属性),当成一个对象来操作,希望读者能理解,也就是说,类,类的成员,我们在运 ...

  3. (转)个例子让你了解Java反射机制

    个例子让你了解Java反射机制   原文地址:http://blog.csdn.net/ljphhj/article/details/12858767 JAVA反射机制:   通俗地说,反射机制就是可 ...

  4. (转)JAVA反射机制理解

    JAVA反射机制: 通俗地说,反射机制就是可以把一个类,类的成员(函数,属性),当成一个对象来操作,希望读者能理解,也就是说,类,类的成员,我们在运行的时候还可以动态地去操作他们. 理论的东东太多也没 ...

  5. 浅谈java反射机制

    目录 什么是反射 初探 初始化 类 构造函数 属性 方法 总结 思考 什么是反射 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意 ...

  6. 一文带你了解Java反射机制

    想要获取更多文章可以访问我的博客 - 代码无止境. 上周上班的时候解决一个需求,需要将一批数据导出到Excel.本来公司的中间件组已经封装好了使用POI生成Excel的工具方法,但是无奈产品的需求里面 ...

  7. 第28章 java反射机制

    java反射机制 1.类加载机制 1.1.jvm和类 运行Java程序:java 带有main方法的类名 之后java会启动jvm,并加载字节码(字节码就是一个类在内存空间的状态) 当调用java命令 ...

  8. Java反射机制

    Java反射机制 一:什么事反射机制 简单地说,就是程序运行时能够通过反射的到类的所有信息,只需要获得类名,方法名,属性名. 二:为什么要用反射:     静态编译:在编译时确定类型,绑定对象,即通过 ...

  9. java基础知识(十一)java反射机制(上)

    java.lang.Class类详解 java Class类详解 一.class类 Class类是java语言定义的特定类的实现,在java中每个类都有一个相应的Class对象,以便java程序运行时 ...

随机推荐

  1. open-falcon-agent插件使用

    说明 Plugin可以看做是对agent功能的扩充.使用插件可以对采集脚本进行统一管理,方便定制修改,也可以免去在crontab中添加计划任务. 开启plugin功能 # 修改agent配置文件 &q ...

  2. php+redis 学习 三 乐观锁

    <?php header('content-type:text/html;chaeset=utf-8'); /** * redis实战 * * 实现乐观锁机制 * * @example php ...

  3. 【Tools】Pycharm 2018专业版 linux安装教程 附2018专业版密钥

    Linux安装pycharm2018专业版 1. 下载安装包 Pycharm下载地址:http://www.jetbrains.com/pycharm/download/ 2.终端打开你的安装包所在路 ...

  4. CocosCreator游戏开发---菜鸟学习之路(一)

    PS(废话): 辞职后在家好久好久了,久到经济不允许了,接着就准备再次出去找工作了,然而工作哪有那么好找,特别是像我这种菜鸟.而且我还准备转行,准备去做游戏,技能等级接近于0,那工作就更难找了.既然如 ...

  5. IDEA的配置文件访问

    问题起源 IDEA中当前模块的配置文件无法被访问,只能够访问到外层的Project的配置文件.具体情形可表示如下: Project --------------- project.properties ...

  6. 数据库之mac上mysql root密码忘记或权限错误的解决办法

    [转自  http://blog.csdn.net/u014410695/article/details/50630233] 以下方法亲测有效,过程使用的工具只有mac的终端无需workbench 当 ...

  7. APACHE服务器出现No input file specified.解决方案

    thinkcmf程序默认的.htaccess里面的规则: <IfModule mod_rewrite.c> RewriteEngine on RewriteCond %{REQUEST_F ...

  8. Java中的双重检查锁(double checked locking)

    最初的代码 在最近的项目中,写出了这样的一段代码 private static SomeClass instance; public SomeClass getInstance() { if (nul ...

  9. 在Mac下配置Maven环境

    下载Maven安装文件,(http://maven.apache.org/download.html)如:apache-maven-3.5.0-bin.zip,然后解压到本地目录. 打开 .bash_ ...

  10. hdu 2046递推

    递推公式   F[N]=F[N-1]+F[N-2]; AC代码: #include<cstdio> const int maxn=55; long long ans[maxn]; void ...