Java源码解析(3) —— Class(2)

时间:2022-02-05 14:06:31

Class 源码详解续(2)

  续Class,详见:Java源码解析(2) —— Class(1)
  关于嵌套类、内部类、成员类、局部类、匿名类、静态类等知识详见:Java各种称呼类详解

源码

//以下说的类是Class而非class
//如果该Class对象位于一个方法内,返回包含这个类的方法的信息(Method对象)
public Method getEnclosingMethod() {...}
//如果该方法具有(内部)局部类或匿名类,获取其信息,包括Class对象、name以及描述。
private native Object[] getEnclosingMethod0();
//获取包含局部类或匿名类的方法的信息
private EnclosingMethodInfo getEnclosingMethodInfo() {
Object[] enclosingInfo = getEnclosingMethod0();
if (enclosingInfo == null)
return null;
else {
return new EnclosingMethodInfo(enclosingInfo);
}
}
//包含局部类或匿名类的方法的信息描述类
private final static class EnclosingMethodInfo {...}
//将参数Type对象o转化为Class对象
private static Class<?> toClass(Type o) {
if (o instanceof GenericArrayType)
return Array.newInstance(toClass(((GenericArrayType)o).getGenericComponentType()),
0)
.getClass();
return (Class<?>)o;
}
//类似getEnclosingMethod,只是这是针对构造器
@CallerSensitive
public Constructor<?> getEnclosingConstructor() {
}
//若该类是成员类,返回外部类Class对象,否则返回null
public Class<?> getDeclaringClass() {...}
private native Class<?> getDeclaringClass0();
@CallerSensitive
//类似getEnclosingMethod,只是这是针对类
public Class<?> getEnclosingClass() {}
//如果该类是*类,返回null,否则返回该类去除*类外后字符串
//非*类名称命名:*类全限定名+$+[(该种类类的,有的话)顺序]+[类名称(如果有的话)]
//成员类:com.fcc.test.OuterClass$MemberClass,调用该方法后,返回
//$MemberClass
private String getSimpleBinaryName() {
Class<?> enclosingClass = getEnclosingClass();
if (enclosingClass == null) //无外部类,即为*类
return null;
try {
return getName().substring(enclosingClass.getName().length());
} catch (IndexOutOfBoundsException ex) {
throw new InternalError("Malformed class name");
}
}
//返回简单类名称(不包含包名)
public String getSimpleName() {
if (isArray())//首先判断该类是否是数组
return getComponentType().getSimpleName()+"[]";
//是数组,返回形式:元素类名称[]
String simpleName = getSimpleBinaryName();
if (simpleName == null) { //等于null,即该类为*类
simpleName = getName();
return simpleName.substring(simpleName.lastIndexOf(".")+1);
//*类的简单名称即去掉包名即可
}
int length = simpleName.length();
//非*类即嵌套类,却有*类名称命名格式,说明这个类命名不合法
if (length < 1 || simpleName.charAt(0) != '$')
throw new InternalError("Malformed class name");
int index = 1;
while (index < length && isAsciiDigit(simpleName.charAt(index)))
index++;
// 找到$字符位置
return simpleName.substring(index);//返回嵌套类名称
}
//判断字符是否是ASCII码
private static boolean isAsciiDigit(char c) {
return '0' <= c && c <= '9';
}
//除数组外,同getName方法,数组时,getName返回的是[Ljava.lang.String之类的表现
//形式,而getCanonicalName返回的就是跟我们声明类似的形式。
public String getCanonicalName() {}
//判断是否是注释类型
public boolean isAnonymousClass() {
return "".equals(getSimpleName());
}
//判断是否是局部类
public boolean isLocalClass() {
return isLocalOrAnonymousClass() && !isAnonymousClass();
}
//判断是否是成员类
public boolean isMemberClass() {
return getSimpleBinaryName() != null && !isLocalOrAnonymousClass();
//非*类,且不是局部类也不是匿名类,即为成员类
}
//判断是否是局部类或匿名类
private boolean isLocalOrAnonymousClass() {
return getEnclosingMethodInfo() != null;
}
//获取该类中所有公有的成员类
//getDeclaredClasses则是获取所有成员类Class对象
@CallerSensitive
public Class<?>[] getClasses() {}
//获取所有公有字段
@CallerSensitive
public Field[] getFields() throws SecurityException {...}
//获取所有公有方法
@CallerSensitive
public Method[] getMethods() throws SecurityException {...}
//获取所有公有构造器
@CallerSensitive
public Constructor<?>[] getConstructors() throws SecurityException {...}
//根据名称获取字段(该字段需要为public的否则抛异常)
@CallerSensitive
public Field getField(String name)
throws NoSuchFieldException, SecurityException {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);//公有成员访问限定
Field field = getField0(name);//本地方法获取字段信息
if (field == null) {//为null,表示没有,抛不存在该字段异常
throw new NoSuchFieldException(name);
}
return field;
}
//根据方法名称获取方法信息,后面的变长参数是该方法的每一个参数的对应的Class类型
//同样需要方法是公有的
@CallerSensitive
public Method getMethod(String name, Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException {...}
//根据构造器名称获取构造器信息,后面的变长参数是该构造器的每一个参数的对应的Class类型
//同样需要构造器是公有的
@CallerSensitive
public Constructor<T> getConstructor(Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException {...}

概述

上面有很多类似的方法,总结起来需要知道:
1.类里面有这么些东西:类(Class)、字段(Field)、方法(Method)、构造器(Constructor),这样就会有类似的方法,比如:getClass()/getClasses()、getField()/getFields()、getMethod()/getMethods()等,但要注意,Class这一对方法和其他的不同。
2.另外,类似getDeclaredFields(),中间加了Declared的表示获取所有对象,而未加的则表示获取公有的所有对象。
3.enclosingMethod、enclosingConstructor、enclosingClass,enclosing表示该类是被包装起来的,那么就分为三种,分别是被包装在方法、构造器或类中。

源码详解

1.getEnclosingXxx
  这个Xxx表示某种对象,比如Method,表示如果这个类存在于某个方法内,返回这个方法(Method对象),否则返回null。(注意,构造器不是方法)
  类似的还有getEnclosingClass()/getEnclosingConstructor():分别是如果类位于类中、构造器中,返回相应的类、构造器信息。

public class {
class A;//位于类中,有enclosed class属性
public Main(){
class B;//类位于构造器中,有enclosed constructor属性
}
public static void main(String[] args) throws Exception
{
Class c1 = new InterfaceA(){}.getClass();
//类位于方法中,有enclosed method属性
}
}

2.getXxx
  这里的Xxx可以是:Class、Field、Method、Constructor,表示获取该类中的指定的(参数)对应的公有对象(Class不同,表示获取该类的Class对象),也可以是他们的复数,表示获取该类中对应的所有公有对象(getClasses表示获取所有公有成员类)。
3.getDeclaredXxx
  和2类似,只不过这次获取的是该类所有对应的对象,而不是仅仅是公有的。
4.getName、getSimpleName、getCanonicalName
  (1).getName:获取类全限定名,形如:com.fcc.test.ClassName,当这个类是数组的时候,会得到奇怪的结果:[[Ljava.lang.String;这是个二元数组。
  (2).getSimpleName:去掉包名的简单类名称,数组时候返回正常:String[][]。
  (3).getCanonicalName:同getName,但数组的时候返回正常:java.lang.String[][]。