反射基础之Class

Java中每个类型要么是引用类型,要么是原生类型。类,枚举,数组(他们都继承于java.lang.Object)和接口都是引用类型。例如:java.lang.String,所有原生类型的包装类java.lang.Double,接口java.io.Serializable和枚举javax.swing.Sortorder,都是引用类型。原生类型的数量是固定的:boolean,byte,short,int,long,char,float,double。 对于每个对象类型,JVM都会为其初始化一个java.lang.Class的实例,可以检查包括属性和方法在内的对象运行时的属性。Class同样也可以创建一个新的类和对象。最重要的是,他是所有反射API的入口。

获取类对象

java.lang.Class是所有反射操作的的入口。java.lang.reflect中的所有类都没有公共的构造函数,所以为了实例化其中的类,需要通过调用合适的Class函数。

Object.getClass()

如果可以获取到一个类的实例,最简单获取Class的方法就是调用Object.getClass()。当然这个只适应于全部都继承与Object的引用类型。例如:返回StringClass

Class c = "foo".getClass();

System.console() 返回的是一个java.io.Console类,所以如下代码然会了ConsoleClass

Class c = System.console().getClass();

对于枚举类型,E,A是E的实例,所以A.getClass() 返回的是E的Class例如:

Enum E {A,B}
Class c = A.getClass();

因为数组也是对象,所以也可以通过调用其getClass()获取Class,返回的Class带有数组的类型信息。

byte[] bytes = new byte[1024];
Class c = bytes.getClass();

对于集合类,同样s.getClass() 返回的是与java.util.HashSet相关的Class

import java.util.Set
import java.util.HashSet

Set<String> s = new HashSet<String>();
Class c = s.getClass();
.class 语法

如果类型可用,但是没有实例,可以通过.class获取Class。同样,也可以使用这个方法获取原生类型的Class

boolean b;
Class = b.getClass(); //编译错误

Class = boolean.class; //正确

注意直接使用boolean.getClass()会发生编译错误,因为boolean是一个原生类型,而且不可以被反引用。

Class c = java.io.PrintStream.class;

获取java.io.PrintStream相关的Class

Class c = int[][].class;

获取二位int数组相关的Class

Class.forName()

使用这个方法需要知道类的全限定名。只能应用于引用类型。数组类型的全限定名可以由Class.getName()获取。

Class c = Class.forName("com.duke.MyLocaleServiceProvider");

通过使用全限定名创建class

Class cDoubleArray = Class.forName("[D");

Class cStringArray = Class.forName("[[Ljava.lang.String;");

cDoubleArraydouble[]Classdouble[].class相同。cStringArray 则与String[][].class相同。

原生类型包装类的TYPE字段

获取原生类型的Class既可以通过.class方式,也可以通过原生类型的包装类的TYPE字段,对于void,java.lang.Void同样提供了这样的一个字段。

Class c = Double.TYPE;

Double.TYPE 就相当于double.class

Class c = Void.TYPE;

等同于void.class

返回Class的方法

如果已经得到了Class,那么可以使用Class中的API来获取其他的类 Class.getSuperClass() 返回指定类的父类

Class c = javax.swing.JButton.class.getSupperClass();

返回其父类javax.swing.AbstractButton Class.getClasses() 获取所有内部所有public类,接口,枚举,包括本身和继承的成员类。

Class<?>[] c = Character.class.getClasses();

返回Character的两个成员类Character.SubsetCharacter.UnicodeBlock Class.getDeclearedClasses()返回类中所有的类,接口,枚举。

Class<?>[] c Character.class.getDeclearedClasses();

返回Character的两个public类Character.SubsetCharacter.UnicodeBlock和一个private类Character.CharacterCacheClass.getDeclearingClass(),java.lang.reflect.Field.getDeclearingClass(),java.lang.reflect.Method.getDeclearingClass(),java.lang.reflect.Constructor.getDeclaringClass()返回声明这些成员的类返回声明这些成员的类。匿名类不会有声明类,有一个外围类(enclosing class)。

import java.lang.reflect.Field;

Field f = System.class.getField("out");
Class c = f.getDeclaringClass();

out 是在System中声明的。

public class MyClass{
    static Object o = new Object() {
        public void m(){}
    };
    static Class<c> = o.getClass().getEnclosingClass();
}

Class.getEnclosingClass() 返回其外围类

Class c = Thread.State.class.getEnclosingClass();

Thread.State的外围类Thread

public class MyClass {
    static Object o = new Object() { 
        public void m() {} 
    };
    static Class<c> = o.getClass().getEnclosingClass();
}

匿名类的外围类是MyClass.

获取Class主要三种方法对比:

方式

使用范围

getClass()

需要获取对象实例,仅能用于引用类型

.class

无需获取对象实例,既可以是引用类型也可以是原生类型

forName()

只需要类的全限定名

检查类修饰符和类型

类声明时会包含一个或多能够影响他运行时行为的修饰符:

  • 访问修饰符:public,private,protected
  • 抽象修饰符:abstract
  • 静态修饰符:static
  • 不可变修饰符: final
  • 强制严格浮点修饰符: strictfp
  • 注解

不是所有的修饰符都可以应用到所有的类上,比如,interface不能使用final,enum不能使用abstract。java.lang.reflect.Modifier 包含了所有的修饰符声明。方法Class.getModifiers()可以返回所有的修饰符。 下面这个例子演示了如果获取一个类的修饰符,泛型参数,实现接口,和集成链。因为Class继承了java.lang.reflect.AnnotatedElement所以可以在运行时获取注解信息。

import java.lang.annotation.Annotation;
import java.lang.reflect.Modifier;
import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import java.util.List;

import static java.lang.System.out;
public class ClassDeclarationSpy {
    public static void main(String[] args) {
        try{
            Class<?> c = Class.forName(args[0]);
            out.format("Class:%n %s%n%n", c.getCanonicalName());
            out.format("Modifier: %n %s%n%n", Modifier.toString(c.getModifiers()));

            out.format("Type Parameters:%n");
            TypeVariable[] tv = c.getTypeParameters();
            if (tv.length != 0){
                out.format("  ");
                for (TypeVariable t : tv)
                    out.format("%s ", t.getName());
                out.format("%n%n");
            }else{
                out.format("-- No TypeParameter --%n%n");
            }
            out.format("Inheritance Path:%n");
            List<Class> l = new ArrayList<>();
            printAncestor(c, l);
            if (l.size() != 0) {
                for (Class<?> cl : l)
                    out.format("  %s%n", cl.getCanonicalName());
                out.format("%n");
            } else {
                out.format("  -- No Super Classes --%n%n");
            }
            out.format("Annotations:%n");
            Annotation[] ann = c.getAnnotations();
            if (ann.length != 0) {
                for (Annotation a : ann)
                    out.format("  %s%n", a.toString());
                out.format("%n");
            } else {
                out.format("  -- No Annotations --%n%n");
            }

        }catch (Exception e){
            e.printStackTrace();
        }
    }
    private static void printAncestor(Class<?> c, List<Class> l){
        Class<?> ancestor =c.getSuperclass();
        if (ancestor != null){
            l.add(ancestor);
            printAncestor(ancestor, l);
        }
    }
}

运行:

$ java ClassDeclarationSpy java.util.concurrent.ConcurrentNavigableMap
Class:
  java.util.concurrent.ConcurrentNavigableMap

Modifiers:
  public abstract interface

Type Parameters:
  K V

Implemented Interfaces:
  java.util.concurrent.ConcurrentMap<K, V>
  java.util.NavigableMap<K, V>

Inheritance Path:
  -- No Super Classes --

Annotations:
  -- No Annotations --

java.util.concrrent.ConcurrentNavigableMap的源码为:

public interface ConcurrentNavigableMap<K,V>
    extends ConcurrentMap<K,V>, NavigableMap<K,V>

注意,以为他是接口,编译器默认对所有接口加abstract。有两个泛型K,V。 运行:

$ java ClassDeclarationSpy "[Ljava.lang.String;"
Class:
  java.lang.String[]

Modifiers:
  public abstract final

Type Parameters:
  -- No Type Parameters --

Implemented Interfaces:
  interface java.lang.Cloneable
  interface java.io.Serializable

Inheritance Path:
  java.lang.Object

Annotations:
  -- No Annotations --]
获取类成员

Class中有两种获取field,method,constructor的方式,一种是获取全部成员,一种是获取指定成员。同样获取成员是也分为从当前类查找和从继承链中查找。总结如下: Field 方法

API

列出所有成员

列出继承成员

私有成员

getDeclaredField()

n

n

y

getField()

n

y

n

getDeclaredFields()

y

n

y

getFields()

y

y

n

Method 方法

API

列出所有成员

列出继承成员

私有成员

getDeclaredMethod()

n

n

y

getMethod()

n

y

n

getDeclaredMethods()

y

n

y

getMethods()

y

y

n

constructor 方法:

API

列出所有成员

列出继承成员

私有成员

getDeclaredConstructor()

n

n

y

getConstructor()

n

n

n

getDeclaredConstructors()

y

n

y

getConstructors()

y

n

n

构造函数并不继承。

示例如下:

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Member;
import static java.lang.System.out;

enum ClassMember { CONSTRUCTOR, FIELD, METHOD, CLASS, ALL }

public class ClassSpy {
    public static void main(String... args) {
    try {
        Class<?> c = Class.forName(args[0]);
        out.format("Class:%n  %s%n%n", c.getCanonicalName());

        Package p = c.getPackage();
        out.format("Package:%n  %s%n%n",
               (p != null ? p.getName() : "-- No Package --"));

        for (int i = 1; i < args.length; i++) {
        switch (ClassMember.valueOf(args[i])) {
        case CONSTRUCTOR:
            printMembers(c.getConstructors(), "Constructor");
            break;
        case FIELD:
            printMembers(c.getFields(), "Fields");
            break;
        case METHOD:
            printMembers(c.getMethods(), "Methods");
            break;
        case CLASS:
            printClasses(c);
            break;
        case ALL:
            printMembers(c.getConstructors(), "Constuctors");
            printMembers(c.getFields(), "Fields");
            printMembers(c.getMethods(), "Methods");
            printClasses(c);
            break;
        default:
            assert false;
        }
        }

        // production code should handle these exceptions more gracefully
    } catch (ClassNotFoundException x) {
        x.printStackTrace();
    }
    }

    private static void printMembers(Member[] mbrs, String s) {
    out.format("%s:%n", s);
    for (Member mbr : mbrs) {
        if (mbr instanceof Field)
        out.format("  %s%n", ((Field)mbr).toGenericString());
        else if (mbr instanceof Constructor)
        out.format("  %s%n", ((Constructor)mbr).toGenericString());
        else if (mbr instanceof Method)
        out.format("  %s%n", ((Method)mbr).toGenericString());
    }
    if (mbrs.length == 0)
        out.format("  -- No %s --%n", s);
    out.format("%n");
    }

    private static void printClasses(Class<?> c) {
    out.format("Classes:%n");
    Class<?>[] clss = c.getClasses();
    for (Class<?> cls : clss)
        out.format("  %s%n", cls.getCanonicalName());
    if (clss.length == 0)
        out.format("  -- No member interfaces, classes, or enums --%n");
    out.format("%n");
    }
}

使用如下:

 java ClassSpy ClassMember FIELD METHOD
Class:
  ClassMember

Package:
  -- No Package --

Fields:
  public static final ClassMember ClassMember.CONSTRUCTOR
  public static final ClassMember ClassMember.FIELD
  public static final ClassMember ClassMember.METHOD
  public static final ClassMember ClassMember.CLASS
  public static final ClassMember ClassMember.ALL

Methods:
  public static ClassMember ClassMember.valueOf(java.lang.String)
  public static ClassMember[] ClassMember.values()
  public final int java.lang.Enum.hashCode()
  public final int java.lang.Enum.compareTo(E)
  public int java.lang.Enum.compareTo(java.lang.Object)
  public final java.lang.String java.lang.Enum.name()
  public final boolean java.lang.Enum.equals(java.lang.Object)
  public java.lang.String java.lang.Enum.toString()
  public static <T> T java.lang.Enum.valueOf
    (java.lang.Class<T>,java.lang.String)
  public final java.lang.Class<E> java.lang.Enum.getDeclaringClass()
  public final int java.lang.Enum.ordinal()
  public final native java.lang.Class<?> java.lang.Object.getClass()
  public final native void java.lang.Object.wait(long) throws
    java.lang.InterruptedException
  public final void java.lang.Object.wait(long,int) throws
    java.lang.InterruptedException
  public final void java.lang.Object.wait() hrows java.lang.InterruptedException
  public final native void java.lang.Object.notify()
  public final native void java.lang.Object.notifyAll()
故障排除

下面这些例子是在用注解是会遇到到的一些常见问题:

Compiler Warning: “Note: … uses unchecked or unsafe operations”

调用方法的时候会检查参数类型比做类型转换,如下示例:

import java.lang.reflect.Method;

public class ClassWarning {
    void m() {
    try {
        Class c = ClassWarning.class;
        Method m = c.getMethod("m");  // warning

        // production code should handle this exception more gracefully
    } catch (NoSuchMethodException x) {
            x.printStackTrace();
        }
    }
}

运行 结果:

javac ClassWarning.java
Note: ClassWarning.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
$ javac -Xlint:unchecked ClassWarning.java
ClassWarning.java:6: warning: [unchecked] unchecked call to getMethod
  (String,Class<?>...) as a member of the raw type Class
Method m = c.getMethod("m");  // warning
                      ^
1 warning

可以通过使用泛型或者使用注解@SuppressWarning("unchecked")解决:

Class<?> c = warn.getClass();
Class c = ClassWarning.class;
@SupressWarning("unchecked")
Method m = c.getMethod();
InstantiationException when the Constructor is Not Accessible

如果通过Class.newInstance()创建类,但是没有0参数的构造器,会抛出InstantiationException异常。例如:

class Cls {
    private Cls() {}
}

public class ClassTrouble {
    public static void main(String... args) {
    try {
        Class<?> c = Class.forName("Cls");
        c.newInstance();  // InstantiationException

        // production code should handle these exceptions more gracefully
    } catch (InstantiationException x) {
        x.printStackTrace();
    } catch (IllegalAccessException x) {
        x.printStackTrace();
    } catch (ClassNotFoundException x) {
        x.printStackTrace();
    }
    }
}

运行结果:

java ClassTrouble
java.lang.IllegalAccessException: Class ClassTrouble can not access a member of
  class Cls with modifiers "private"
        at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:65)
        at java.lang.Class.newInstance0(Class.java:349)
        at java.lang.Class.newInstance(Class.java:308)
        at ClassTrouble.main(ClassTrouble.java:9)

原文发布于微信公众号 - 代码拾遗(gh_8f61e8bcb1b1)

原文发表时间:2018-05-02

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏前端儿

JS 对象属性相关--检查属性、枚举属性等

delete只是断开属性和宿主对象的联系,而不会去操作属性中的属性  看到delete a.p之后b.x仍然为1

12820
来自专栏C/C++基础

C++11 变参模板

版权声明:感谢您对博文的关注!校招与社招,有需要内推腾讯的可以QQ(1589276509)or 微信(louislvlv)联系我哈,期待您的加入。 ...

52320
来自专栏代码拾遗

​Java反射详解

反射的概念是软件可以在运行时,检查,分析,修改代码。例如:在Junit中,使用@Test注解等。 在Java中,通过反射可以在运行时检查字段,方法,类,接口,注...

30130
来自专栏西安-晁州

golang学习之闭包

匿名函数不能够独立存在,但可以被赋值于某个变量,即保存函数的地址到变量中:fplus := func(x, y int) int { return x + y ...

20600
来自专栏微信公众号:Java团长

Java反射机制详解

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

11120
来自专栏jojo的技术小屋

原 三、基本概念

作者:汪娇娇 时间:2017年11月4日 一、语法 1、区分大小写 2、标识符 指变量、函数、属性的名字,采用驼峰大小写格式。 3、注释 单行:// 多行:/*...

27050
来自专栏JMCui

Java 反射机制

    Java 反射(Reflection)就是 Java 程序在运行时可以加载一个才知道类名的类,获得类的完整构造方法,并实例化出对象,给对象属性设定值或者...

9920
来自专栏C++

python笔记:#005#算数运算符

19520
来自专栏阮一峰的网络日志

Ramda 函数库参考教程

学习函数式编程的过程中,我接触到了 Ramda.js。 我发现,这是一个很重要的库,提供了许多有用的方法,每个 JavaScript 程序员都应该掌握这个工具。...

93980
来自专栏二进制文集

JDK源码分析 反射

对于JDK源码分析的文章,仅仅记录我认为重要的地方。源码的细节实在太多,不可能面面俱到地写清每个逻辑。所以我的JDK源码分析,着重在JDK的体系架构层面,具体源...

28360

扫码关注云+社区

领取腾讯云代金券