前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java反射机制及其使用

Java反射机制及其使用

作者头像
全栈程序员站长
发布2022-09-08 10:37:19
5370
发布2022-09-08 10:37:19
举报
文章被收录于专栏:全栈程序员必看

大家好,又见面了,我是你们的朋友全栈君。

文章目录

1 Java反射机制概述

Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。

加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射。

Java反射机制提供的功能

  • 在运行时判断任意一个对象所属的类
  • 在运行时构造任意一个类的对象
  • 在运行时判断任意一个类所具有的成员变量和方法
  • 在运行时获取泛型信息
  • 在运行时调用任意一个对象的成员变量和方法
  • 在运行时处理注解
  • 生成动态代理

反射相关的主要API

  • java.lang.Class:代表一个类
  • java.lang.reflect.Method:代表类的方法
  • java.lang.reflect.Field:代表类的成员变量
  • java.lang.reflect.Constructor:代表类的构造器

关于动态语言和静态语言

1、动态语言

是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。通俗点说就是在运行时代码可以根据某些条件改变自身结构。主要动态语言有:Object-C、C#、JavaSpript、PHP、Python、Erlang。

2、静态语言

与动态语言相对应的,运行时结构不可变的语言就是静态语言。如Java、C、C++。Java不是动态语言,但Java可以称之为“准动态语言”。即Java有一定的动态性,我们可以利用反射机制、字节码操作获得类似动态语言的特性。Java的动态性让编程的时候更加灵活!

2 反射及其使用

2.1 关于类 java.lang.Class

java中,Java源文件xxx.java经过javac.exe命令编译以后,会生成一个或多个字节码文件xxx.class,接着使用java.exe命令对某个字节码文件xxx.class进行解释运行,相当于将某个字节码文件加载到内存中,此过程就称为类的加载。加载到内存中的类,我们就称为运行时类,此运行时类,就作为Class的一个实例。简言之,Class的实例就对应着一个运行时类

哪些类型可以有Class对象? (1)class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类 (2)interface:接口 (3)[]:数组 (4)enum:枚举 (5)annotation:注解@interface (6)primitive type:基本数据类型 (7)void

加载到内存中的运行时类,会缓存一定的时间。在此时间之内,我们可以通过不同的方式来获取此运行时类。

获取Class类实例的四种方式

代码语言:javascript
复制
public void test3() throws ClassNotFoundException { 
   
        // 1 调用运行时类的属性 .class
        Class clazz1 = Person.class;
        System.out.println(clazz1);

        // 2 通过运行时类的对象,调用getClass()
        Person person = new Person();
        Class clazz2 = person.getClass();
        System.out.println(clazz2);

        // 3 调用Class的静态方法 forName(String classPath)
        Class clazz3 = Class.forName("reflection.Person");
        System.out.println(clazz3);
        System.out.println(clazz3);

        // 4 使用类的加载器ClassLoader(不常用)
        ClassLoader classLoader = ReflectionDemo.class.getClassLoader();
        Class clazz4 = classLoader.loadClass("reflection.Person");
        System.out.println(clazz4);

        // 每个类对应的Class的实例在运行过程中都是唯一的
        System.out.println(clazz2 == clazz1);//true
        System.out.println(clazz3 == clazz1);//true
        System.out.println(clazz4 == clazz1);//true
    }
代码语言:javascript
复制
class reflection.Person
class reflection.Person
class reflection.Person
class reflection.Person
class reflection.Person
true
true
true

Process finished with exit code 0

2.2 类的加载

2.2.1 类的加载过程

类加载的作用:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口。

类缓存:标准的JavaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这些Class对象。

当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过如下三个步骤来对该类进行初始化。

加载:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口(即引用地址)。所有需要访问和使用类数据只能通过这个Class对象。这个加载的过程需要类加载器参与。

链接:将Java类的二进制代码合并到JVM的运行状态之中的过程。

  • 验证:确保加载的类信息符合JVM规范,例如:以cafe开头,没有安全方面的问题。
  • 准备:正式为类变量(static)分配内存并设置类变量默认初始值的阶段,这些内存都将在方法区中进行分配。
  • 解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程。

初始化

  • 执行类构造器<clinit>()方法的过程。类构造器<clinit>()方法是由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。(类构造器是构造类信息的,不是构造该类对象的构造器)。
  • 当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。
  • 虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确加锁和同步。
代码语言:javascript
复制
/** * 第一步:加载,将A.class加载到内存中 * 第二步:链接结束后m=0 * 第三步:初始化后,m的值由 <clinit>() 方法执行决定。这个A的类构造器 <c0init>() 方法由类变量 * 的赋值和静态代码块中的语句按照顺序合并产生,类似于: * <clinit>(){ * m = 300; * m = 100; * } */
class A { 
   
    static { 
    m = 300; }
    static int m = 100;
}

public class ClassLoadingTest { 
   
    public static void main(String[] args) { 
   
        System.out.println(A.m);//100
    }
}

2.2.2 类加载器

类加载器作用是用来把类(class)装载进内存的。JVM规范定义了如下类型的类的加载器。

双亲委派机制/父类委托机制

当某个类加载器需要加载某个.class文件时,它首先把这个任务委托给他的父加载器,只有当父加载器反馈自己无法完成该加载请求(该加载器的搜索范围中没有找到对应的类)时,子加载器才会尝试自己去加载。每一个层次的加载器都是如此。

采用这种机制主要是从安全方面考虑的,假设某人定义了一个与系统类重名的类,该类可能含有恶意或不健全的代码,如果先加载自定义类的话可能会影响程序的运行,所以只能先加载系统的类,由于每个类在运行时只存在一个Class类的实例,当系统类加载后,自定义类就不会被加载了。而实现这种加载顺序的方法就是双亲委派机制,先让顶层的类加载器加载相应的类,因为它们会先加载系统类库中的类,而自定义的类是由系统类加载器或自定义类加载器加载的,这样就保证了加载过程的安全性。

代码语言:javascript
复制
public class ClassLoaderTest { 
   
    public static void main(String[] args) { 
   
        // 系统类加载器
        ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
        System.out.println(classLoader);

        // 扩展类加载器
        ClassLoader classLoader1 = classLoader.getParent();
        System.out.println(classLoader1);

        // 引导类加载器。(引导类加载器无法直接获取,会输出null)
        ClassLoader classLoader2 = classLoader1.getParent();
        System.out.println(classLoader2);
    }
}
代码语言:javascript
复制
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@677327b6
null

Process finished with exit code 0

2.3 反射的使用

代码目录结构

代码语言:javascript
复制
└─reflection1
      Creature.java
      MyAnnotation.java
      MyInteface.java
      Person.java
      Test1.java
代码语言:javascript
复制
public class Creature<T> implements Serializable { 
   
    private char sex_private;
    public double weight_public;

    private void breadth_private(){ 
   
        System.out.println("生物呼吸");
    }
    public void eat_public() { 
   
        System.out.println("生物吃东西");
    }
}
代码语言:javascript
复制
@Target({ 
   ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.CONSTRUCTOR, ElementType.LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation { 
   
    String value() default "hello";
}
代码语言:javascript
复制
public interface MyInteface { 
   
    void info_public();
}
代码语言:javascript
复制
@MyAnnotation(value = "class_person")
public class Person extends Creature<String> implements Comparable<String>, MyInteface { 
   
    private String name_private;
    int age_default;
    public int id_public;

    public Person() { 
   }

    @MyAnnotation(value = "private_cons")
    private Person(String name_private) { 
   
        this.name_private = name_private;
    }

    Person(String name_private, int age_default) { 
   
        this.name_private = name_private;
        this.age_default = age_default;
    }

    @MyAnnotation
    private String show_private(String nation) throws IOException, ClassCastException { 
   
        System.out.println("我的国籍是:" + nation);
        return nation;
    }

    public String display_public(String interests, int years){ 
   
        return interests + ", " + years;
    }

    @Override
    public int compareTo(String o) { 
   
        return 0;
    }


    @Override
    public void info_public() { 
   
        System.out.println("我是一个人");
    }
}

2.3.1 创建运行时类的对象

代码语言:javascript
复制
   // 通过反射创建运行时类的对象
    public void test() throws Exception { 
   
        Class<Person> clazz = Person.class;
        /* 调用newInstance(),创建运行时类的对象。实际上对象还是通过调用类的无参构造函数创建的, 若没有无参构造函数,则报错。 要想此方法正常的创建运行时类的对象,要求: 1.运行时类必须提供空参的构造器 2.空参的构造器的访问权限得够。通常,设置为public。 在javabean中要求提供一个public的空参构造器。原因: 1.便于通过反射,创建运行时类的对象 2.便于子类继承此运行时类时,默认调用super()时,保证父类有此构造器 */
        Person person = clazz.newInstance();
        System.out.println(person);
    }

2.3.2 获取运行时类的属性结构

代码语言:javascript
复制
	// 获取运行时类的属性结构
    public void getFields() { 
   
        Class<Person> clazz = Person.class;

        // 1 getFields():获取当前运行时类及其父类中声明为public访间权限的属性
        Field[] fields = clazz.getFields();
        for (Field field : fields) { 
   
            System.out.println(field);
        }
        System.out.println("********************");

        // 2 getDecLaredFields():获取当前运行时类中声明的所有属性。(不包含父类中声明的属性)
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field declaredField : declaredFields) { 
   
            System.out.println(declaredField);
        }
        System.out.println("*********************");

        int i = 0;
        for (Field declaredField : declaredFields) { 
   
            System.out.println("------属性 " + (++i) + ":" + declaredField);

            // 3 获取属性的访问修饰符
            int modifiers = declaredField.getModifiers();
            System.out.println(Modifier.toString(modifiers));

            // 4 获取属性的数据类型
            Class type = declaredField.getType();
            System.out.println(type.getName());

            // 5 获取变量名
            String name = declaredField.getName();
            System.out.println(name);
        }
    }
代码语言:javascript
复制
public int reflection1.Person.id_public
public double reflection1.Creature.weight_public
********************
private java.lang.String reflection1.Person.name_private
int reflection1.Person.age_default
public int reflection1.Person.id_public
*********************
------属性 1:private java.lang.String reflection1.Person.name_private
private
java.lang.String
name_private
------属性 2:int reflection1.Person.age_default

int
age_default
------属性 3:public int reflection1.Person.id_public
public
int
id_public

Process finished with exit code 0

2.3.3 获取运行时类的方法结构

代码语言:javascript
复制
	/** * 获取运行时类的方法结构。 * * @xxxAnnotation * 访问修饰符 返回值类型 方法名(参数列表) throws xxxException {} */
    public void test2() { 
   
        Class<Person> clazz = Person.class;

        // 1 getMethods():获取当前运行时类及其所有父类中声明为public权限的方法
        Method[] methods = clazz.getMethods();
        for (Method method : methods) { 
   
            System.out.println(method);
        }
        System.out.println("********************************");

        // 2 getDeclaredMethods():获取当前运行时类中声明的所有方法。(不包含父类中声明的方法)
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) { 
   
            System.out.println(declaredMethod);
        }
        System.out.println("********************************");

        int i = 0;
        for (Method mtd : declaredMethods) { 
   
            System.out.println("\n---------------方法 " + (++i) + ":" + mtd);

            // 3 获取方法声明的注解
            Annotation[] annotations = mtd.getAnnotations();
            for (Annotation anno : annotations) { 
   
                System.out.println(anno);
            }

            // 4 获取方法的访问修饰符
            System.out.println(Modifier.toString(mtd.getModifiers()));

            // 5 获取方法返回值类型
            System.out.println(mtd.getReturnType().getName());

            // 6 获取方法形参列表
            Class[] parameterTypes = mtd.getParameterTypes();
            if (parameterTypes.length > 0) { 
   
                for (Class parameterType : parameterTypes) { 
   
                    System.out.print(parameterType + ", ");
                }
                System.out.println();
            }

            // 7 获取方法抛出的异常
            Class[] exceptionTypes = mtd.getExceptionTypes();
            if (exceptionTypes.length > 0) { 
   
                for (Class exceptionType : exceptionTypes) { 
   
                    System.out.print(exceptionType + ", ");
                }
            }
        }
    }
代码语言:javascript
复制
public int reflection1.Person.compareTo(java.lang.String)
public int reflection1.Person.compareTo(java.lang.Object)
public void reflection1.Person.info_public()
public java.lang.String reflection1.Person.display_public(java.lang.String,int)
public void reflection1.Creature.eat_public()
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
********************************
public int reflection1.Person.compareTo(java.lang.String)
public int reflection1.Person.compareTo(java.lang.Object)
private java.lang.String reflection1.Person.show_private(java.lang.String) throws java.io.IOException,java.lang.ClassCastException
public void reflection1.Person.info_public()
public java.lang.String reflection1.Person.display_public(java.lang.String,int)
********************************

---------------方法 1:public int reflection1.Person.compareTo(java.lang.String)
public
int
class java.lang.String, 

---------------方法 2:public int reflection1.Person.compareTo(java.lang.Object)
public volatile
int
class java.lang.Object, 

---------------方法 3:private java.lang.String reflection1.Person.show_private(java.lang.String) throws java.io.IOException,java.lang.ClassCastException
@reflection1.MyAnnotation(value=hello)
private
java.lang.String
class java.lang.String, 
class java.io.IOException, class java.lang.ClassCastException, 
---------------方法 4:public void reflection1.Person.info_public()
public
void

---------------方法 5:public java.lang.String reflection1.Person.display_public(java.lang.String,int)
public
java.lang.String
class java.lang.String, int, 

Process finished with exit code 0

2.3.4 获取运行时类的构造器结构

代码语言:javascript
复制
	// 获取运行时类的构造器结构
	public void test3() { 
   
        Class<Person> clazz = Person.class;

        // 1 getConstructors():获取当前运行时类中声明public的构造器
        Constructor<?>[] constructors = clazz.getConstructors();
        for (Constructor<?> constructor : constructors) { 
   
            System.out.println(constructor);
        }
        System.out.println("*****************************");

        // 2 getDeclaredConstructors():获取当前运行时类中声明的所有的构造器
        Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors();
        for (Constructor<?> declaredConstructor : declaredConstructors) { 
   
            System.out.println(declaredConstructor);
        }
    }
代码语言:javascript
复制
public reflection1.Person()
*****************************
reflection1.Person(java.lang.String,int)
private reflection1.Person(java.lang.String)
public reflection1.Person()

Process finished with exit code 0

2.3.5 获取运行时类的父类信息

代码语言:javascript
复制
	public void test4() { 
   
        Class<Person> clazz = Person.class;

        // 1 获取父类名
        Class<? super Person> superclass = clazz.getSuperclass();
        System.out.println(superclass);

        // 2 获取带泛型的父类
        Type genericSuperclass = clazz.getGenericSuperclass();
        System.out.println(genericSuperclass);

        // 3 获取带泛型的父类的 泛型类型
        ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
        Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
        // 父类可能有多个泛型类型
        for (Type actualTypeArgument : actualTypeArguments) { 
   
            System.out.println(actualTypeArgument.getTypeName());
        }
    }
代码语言:javascript
复制
class reflection1.Creature
reflection1.Creature<java.lang.String>
java.lang.String

Process finished with exit code 0

2.3.6 获取运行时类实现的接口

代码语言:javascript
复制
	// 获取运行时类实现的接口
    public void test5(){ 
   
        Class<Person> clazz = Person.class;

        Class<?>[] interfaces = clazz.getInterfaces();
        for (Class<?> c : interfaces) { 
   
            System.out.println(c);
        }

        // 获取父类实现的接口
        Class<?>[] interfaces1 = clazz.getSuperclass().getInterfaces();
        for (Class<?> c : interfaces1) { 
   
            System.out.println(c);
        }
    }
代码语言:javascript
复制
interface java.lang.Comparable
interface reflection1.MyInteface
interface java.io.Serializable

Process finished with exit code 0

2.3.7 获取运行时类声明的注解

代码语言:javascript
复制
	// 获取运行时类声明的注解
    public void test6(){ 
   
        Class<Person> clazz = Person.class;
        Annotation[] annotations = clazz.getAnnotations();
        for (Annotation annotation : annotations) { 
   
            System.out.println(annotation);
        }
    }
代码语言:javascript
复制
@reflection1.MyAnnotation(value=class_person)

Process finished with exit code 0

2.3.7 获取运行时类所在的包

代码语言:javascript
复制
    // 获取运行时类所在的包
    public void test7(){ 
   
        Class<Person> clazz = Person.class;
        Package aPackage = clazz.getPackage();
        System.out.println(aPackage);
    }
代码语言:javascript
复制
package reflection1

Process finished with exit code 0

2.3.8 调用类的构造函数、操作类的属性、调用类中方法

代码语言:javascript
复制
class Person { 
   
    public String name_public;
    private int age_private;

    public Person() { 
   
    }

    private Person(String name_public) { 
   
        this.name_public = name_public;
    }

    public Person(String name_public, int age_private) { 
   
        this.name_public = name_public;
        this.age_private = age_private;
    }

    private static void show_static(String name, int age) { 
   
        System.out.println("my name is " + name + ", my age is " + age);
    }

    @Override
    public String toString() { 
   
        return "Person{" +
                "name_public='" + name_public + '\'' +
                ", age_private=" + age_private +
                '}';
    }

    public void show_public(){ 
   
        System.out.println("我是一个人");
    }
    private String show_private(String name){ 
   
        return name;
    }
}
代码语言:javascript
复制
	// 通过常规方法创建一个类的对象,并对其进行操作
    public void test1() { 
   
        // 1 创建类对象
        Person jack = new Person("jack", 88);
        System.out.println(jack);

        // 2 通过对象,调用类内部属性
        jack.name_public = "jack1";
        System.out.println(jack);

        // 3 通过对象,调用其方法
        jack.show_public();

        // 在一个类外部,不能通过其对象调用内部的私有结构
    }
代码语言:javascript
复制
Person{name_public='jack', age_private=88}
Person{name_public='jack1', age_private=88}
我是一个人

Process finished with exit code 0
代码语言:javascript
复制
	// 通过反射对类进行操作
    public void test2() throws Exception { 
   
        Class<Person> pClass = Person.class;

        // 1 调用类的构造函数
        /** * reflect.Constructor<T> getConstructor(@Nullable Class<?>... parameterType) * 参数:构造函数的参数列表 */
        Constructor<Person> cons_public = pClass.getConstructor(String.class, int.class);
        Person lisa = cons_public.newInstance("lisa", 33);
        System.out.println(lisa);

        // 2 操作类内部属性
        /** * reflect.Field getField(@NonNls @NotNull String name) * 参数:属性名 * * void set(Object obj, Object value) * 参数一:指明设置哪个对象的属性 * 参数二:将此属性值设置为多少 */
        Field name_public = pClass.getField("name_public");
        name_public.set(lisa, "lisa1");
        System.out.println(lisa);

        // 3 调用类中方法
        /** * reflect.Method getMethod(@NonNls @NotNull String name, @Nullable Class<?>... parameterTypes) * 参数一:方法名 * 参数二:形参列表,若有多个参数,则写多个 * * Object invoke(Object obj, Object... args) * 参数一:方法的调用者 * 参数二:给方法形参赋值的实参,若有多个参数,则写多个 * 返回值:被调用方法的返回值。如:原方法返回 18,则invoke返回 18;原方法返回 "abc",则invoke返回 "abc"…… */
        Method show_public = pClass.getMethod("show_public");
        show_public.invoke(lisa);

        // ---------通过反射,操作类内部的私有结构-----------------
        // 1 调用私有构造方法
        Constructor<Person> cons_private = pClass.getDeclaredConstructor(String.class);
        cons_private.setAccessible(true);
        Person michael = cons_private.newInstance("michael");
        System.out.println(michael);

        // 2 操作类的私有属性
        Field age_private = pClass.getDeclaredField("age_private");
        age_private.setAccessible(true);
        age_private.set(michael, 55);
        System.out.println(michael);

        // 3 调用类的私有方法
        Method show_private = pClass.getDeclaredMethod("show_private", String.class);
        show_private.setAccessible(true);
        String res = (String) show_private.invoke(michael, "我的名字是:michael");
        System.out.println(res);

        // 4 调用静态方法
        Method show_static = pClass.getDeclaredMethod("show_static", String.class, int.class);
        show_static.setAccessible(true);
        show_static.invoke(Person.class, "jim", 20);
    }
代码语言:javascript
复制
Person{name_public='lisa', age_private=33}
Person{name_public='lisa1', age_private=33}
我是一个人
Person{name_public='michael', age_private=0}
Person{name_public='michael', age_private=55}
我的名字是:michael
my name is jim, my age is 20

Process finished with exit code 0

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/157087.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • 1 Java反射机制概述
  • 2 反射及其使用
    • 2.1 关于类 java.lang.Class
      • 2.2 类的加载
        • 2.2.1 类的加载过程
        • 2.2.2 类加载器
      • 2.3 反射的使用
        • 2.3.1 创建运行时类的对象
        • 2.3.2 获取运行时类的属性结构
        • 2.3.3 获取运行时类的方法结构
        • 2.3.4 获取运行时类的构造器结构
        • 2.3.5 获取运行时类的父类信息
        • 2.3.6 获取运行时类实现的接口
        • 2.3.7 获取运行时类声明的注解
        • 2.3.7 获取运行时类所在的包
        • 2.3.8 调用类的构造函数、操作类的属性、调用类中方法
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档