观看尚硅谷视频做的视频笔记
一、高级-反射与动态代理(十四)
1、反射概述
1)java程序中,所有的对象都有两种类型:编译时类型,运行时类型,而很多时候对象的编译类型和运行时类型不一致。
此处就是多态性体现。
Object obj=new String(“hello”);
2)例如:某些变量或形参的声明类型是Object类型,但是程序却需要调用该对象运行时类new String(“hello”);的方法compareTo方法,该方法不是Object中的方法,那么如何解决?
方案1:多态中有用到,向下转型。在编译和运行是都完全知道类型的具体的信息,在这种情况下,我们可以直接先使用instanceof运算符进行判断是否为String类型,若是就利用强制类型转换符将其转成运行时类的变量即可,强制转换后就可以用String里面的方法。缺点:①若用这种方法会写很多instanceof这种判断。②此判断是否全,是否满足需求。此时写好了代码,形参是Object类型。判断obj是否为A类型,是就进行强转,调方法,B类型,C类型,目前只是ABC三个对象。后续可能DEF,也有可能作为参数传进来,就需要进行修改,有没有更好的方案解决此问题,自然而然就考虑方案2.
方案2:编译时根本无法预知该对象和类的真实信息,程序只能依靠运行时obj已经在内存中,信息来发现该对象和类的真实信息,这就必须使用反射。(程序运行过程中,动态获取所属类的类型obj.getClass,obj对象是哪个类造的,动态获取,及时把类转换成声明,动态的转换,再去调特有的方法就需要用到反射)
obj.getClass在Object中声明
框架中用的比较多。
3)反射是被是视为动态语言的关键,反射机制允许程序在运行期间借助于RelectionAPI取得任何类得内部信息,并能直接操作任意对象得内部属性及方法。(跟对象里面方法属性的权限都无关)
加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象。(一个类只有一个Class对象),这个对象就包含了完整的类的结构的信息。我们可以i通过这个对象看到类的结构,这个对象就像一面镜子,通过这个镜子看到类的结构,所以我们形象的称为反射。
4)代码示例(利用面向对象做的操作 、 使用反射完成调属性方法操作、使用反射完成调私有属性方法操作)
package com.zhou.java;import org.junit.Test;import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;public class ReflectionTest {/* 利用反射之前做的操作 */@Testpublic void test1() {//1.创建Person类的实例// public person()Person p1 = new Person();//2.调用属性、方法p1.age = 10;System.out.println(p1.age);//3.调用方法//public void show(){}p1.show();}//使用反射完成上诉操作@Testpublic void test2() throws Exception {
////1.创建类的实例//person.class就作为Class的实例出现Class<Person> clazz = Person.class;Person p1 = clazz.newInstance();//jdk9之后不建议用System.out.println(p1);//2.调属性、先获取当前类当中叫age名的属性。Field agefield = clazz.getField("age");agefield.set(p1, 10);System.out.println(agefield.get(p1));//3.调方法,先获取当前类当中叫show的方法。Method showmethod = clazz.getMethod("show");showmethod.invoke(p1);}//反射做其他事儿@Testpublic void test3() throws Exception {//1.调用私有的构造器,创建Person实例// private(String name,int age){}Class clazz1 = Person.class;Constructor cons = clazz1.getDeclaredConstructor(String.class, int.class);//不用填写构造器名称,只需要告诉参数就行,需要传的是String参数和int参数,构造器有了,就需要造对象 cons.setAccessible(true);Person p1 = (Person) cons.newInstance("Tom", 20);System.out.println(p1);//2.调用私有的属性Field namefield = clazz1.getDeclaredField("name");namefield.setAccessible(true);namefield.set(p1, "Jerry");System.out.println(p1);//2.调用私有的方法//private String showNation(){}Method showNation = clazz1.getDeclaredMethod("showNation", String.class);showNation.setAccessible(true);String info = (String) showNation.invoke(p1, "CHN");System.out.println(info);}
}
5)通过使用反射前后的的例子的对比
①面向对象中创建对象,请用指定结构(属性、方法)等功能,可以不使用反射,也可以使用反射,请问有什么区别?
运行时类,运行的代码中,person类就会加载到内存中,放到内存中的类就称为运行时类。
不使用反射:我们需要考虑封装性,比如,出了Person类之后,就不能再调用Person类中的私有结构。使用反射:我们可以调用运行时类中任意的构造器、属性、方法。包括了私有的属性、方法、构造器。
②以前创建对象并调用方法的方式,与现在通过反射创建对象并调用方法的方式对比的话,哪种用的多?
从我们程序员来讲,我们开发中主要是完成义务代码。对于相关的对象,方法的调用都是确定,所以我们使用非反射的方式多一些。
因为反射体现了多态性(可以再运行时动态的获取对象所属的类,动态的调用相关的方法),所以我们在设计框架的时候,会大量的使用反射。
框架=注解+反射(获取相关注解)+设计模式
③单例模式的饿汉式和懒汉式,私有化的构造器,此时通过反射,可以创建单列模式的多个对象吗?
是的,单列模式的饿汉式和懒汉式,构造器都被私有。在类的内部造对象,外部直接用就可以。通过反射可以调构造器。
④.通过反射,可以调用类中私有结构,是否与面对对象的封装有冲突》是不是java语言设计存在bug?
不存在bug
封装性:体现的是否会建议我们调用内部api的问题,比如,private声明的结构,意味着不建议调用。
反射:体现的是我们能否调用的问题,因为类的完整结构都加载到了内存中,我们就有能力进行调用。
反射理解:是假设内存中有一个内存对象,通过这个对象,可以看成是哪个类创建的,通过类可以看到是在哪个包中声明的。
6)java反射机制提供的功能
在运行时判断任意一个对象所属的类
在运行时构造任意一个类的对象
在运行时判断任意一个类所具有的成员变量和方法
在运行时获取泛型信息
在运行时
2.Class类的理解
1)如下以Java类的加载为例说明
编译:针对于编写好的.java源文件进行编译(使用Javac.exe),会生成一个或多个.class字节码文件。
接着,我们使用Java.exe命令对指定的.class字节码文件进行解释运行。这个解释运行的过程中,我们需要将.class字节码文件加载到(使用类的构造器)内存中(方法区),加载到内存的class文件对应的结构即为Class的一个实例
比如:加载到内存中的Person类或String类或User类,都作为Class的一个一个的实例
接口加载到内存中也是对应得一个Class实例
Class clazz1=Person.class;
Class clazz2=String.class;
Class clazz3=User.class;
Class clazz4=Compareable.class;
3.获取Class实例的几种方式(掌握前三种)
import com.zhou.java.Person;
import org.junit.Test;public class ClassTest {//1.调用运行时类的静态属性@Testpublic void test1() throws Exception {Class clazz1 = Person.class;System.out.println(clazz1);//2.调用运行时类的对象的getClass()Person p1 = new Person();Class clazz2 = p1.getClass();System.out.println(clazz1 = clazz2);//true//3.调用Class的静态方法forName(String className)String className = "com.zhou.java.Person";//全类名Class clazz = Class.forName(className);//4.Class systemClassLoader = ClassLoader.getSystemClassLoader().loadClass("com.zhou.java.Person");}}
4.class看作是反射源头
5.Class的实列都可以指向哪些结构呢?
class:外部类,成员(成员内部类,静态内部类)、局部内部类、匿名内部类
interface:接口
[]:数组
enum:枚举
annotation:注解
基本数据类型
void
6.类的加载过程(详细在jvm中有)
6.1过程1:loading(装载)
将类的class文件读入内存,并为之创建一个java.lang.Class对象。此过程由加载器完成
6.2过程2linking(连接)
验证、准备、解析
验证(Verify):确保加载的类信息符合JVM规范,例如:以cafebabe开头,没有安全方面的问题。
准备prepare):正式为类变量(static)分配内存并设置类变量默认初始值的阶段,这些内存都将在方法区中进行分配。
解析(Resolve):虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程。
6.3过程3Initialization(初始化)
6.4关于类的加载器(了解,JDK8版本为例)
1)作用:负责类的加载,并对应一个Class的实例
虽叫类的加载器,不仅能加载类,还能加载接口、没接、注解等
负责将字节码文件放到内存中
2) 分类(分为两种)
似乎是继承关系,其实不是继承关系,而是层级关系
①BootStrapClassLoader:引导加载器、启动加载器
使用C、c++语言编写的,不能通过java代码获取实例
负载加载java的核心库。(Java_Home/jar/lib/rt.jar)
java代码获取实例,获取的时候返回为null
②继承于ClassLoader的类加载器
扩展类加载器(jdk提供的)
系统类加载器、应用程序类加载器(jdk提供的)自定义的类默认使用的类的加载器
用户自定义类的加载器(Tomcat中也定义了一些类加载器)
③代码演示
获取系统类加载器
获取扩展加载器(获取上一层加载器)
获取引导类加载器失败,获取不到。返回值为null
用户自定义的类是系统类加载加载的
方式一
方式二
3)以上的类的加载器是否存在继承关系
4)掌握使用类的加载器获取流,并读取配置文件信息。
方案一:
propertites处理属性文件
读取配置文件时:
使用单元测试方法,默认就是使用的是当前module下面
通过propertites读取流当中文件的数据
处理的key和值都是String类型
以后会实现数据和代码的分离
数据会放在具体的配置文件中
使用Classloader也可以做这样的事儿
拿到系统加载器
作为流拿到一个资源
方案二:
7.反射的应用
7.1反射的应用1:创建运行时类的对象
1)如何实现:通过Class的实例调用newInstance()方法即可
2)要想创建对象成功,需要满足
①条件1:要求运行是类必须满足一个空参的构造器。
②条件2:要求提供的空参构造器的权限要足够。
Person类实现了两个接口

创建Person对象此处的创建运行时类对象指的是Person类
当把Person类加载到内存中时
使用Java.exe指令时,走类的加载过程:loading,linking、initilion环节,在方法区就会有一个大的实例。就称为运行时类。
首先有一个大的Class实例,获取实例。
per调的是toString方法
若Person类里没有空参构造器,则抛异常
还有一种异常是有构造器但不能用私有权限
默认权限,同包别的类能用
3)回忆:javabean中要求当前类:一个空参构造器,有什么用?
①场景1:子类对象在实例化时,子类的构造器的首行默认调用父类的空参的构造器默认有super()
②场景2:在反射中,经常用来创建运行时类的对象,那么我们要求各个运行时类都提供一个空参构造器,便于我们创建运行时类对象的代码。
Javabean的要求,当前类是公共的,提供当前类的空参构造器,有属性提供get、set方法、有toString方法、重写equals、hashcode方法,是为了防止将对象放在集合中、toString方法(打印对象变量时,打印地址、打印属性的值)
为啥要有这个提供空参构造器这个要求?
package com.zhou.java;public class Creature<T> {boolean gender;public int id;public void breath(){System.out.println("呼吸");}private void info(){System.out.println("我是一个生物");}
}
package com.zhou.java;public interface myInterface {void method();}
package com.zhou.java;import org.junit.Test;public class NewInstanceTest {@Testpublic void test() throws InstantiationException, IllegalAccessException {Class clazz = Person.class;//获取Person类的实例Person per = (Person) clazz.newInstance();System.out.println(per);//调的是toString()方法}}
package com.zhou.java;public class Person extends Creature<String> implements Comparable, myInterface {private String name;public int age = 1;private static String info;private Person(String name, int age) {this.name = name;this.age = age;}protected Person(int age) {this.age = age;}protected Person(String name) {this.name = name;}Person() {System.out.println("person()========");}public void show() {System.out.println("您好我是一个Person");}private String showNation(String nation, int age) {System.out.println("shownation");return "我的国籍是:" + nation + ",生活了" + age + "年";}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +'}';}public static void showInfo() {System.out.println("我是一个人");}@Overridepublic void method() {}@Overridepublic int compareTo(Object o) {return 0;}
}
4)在jdk标识为过时,替换成什么结构
通过Constructor类调用newInstance()
7.2反射的应用2:获取运行时类所有的结构
1)获取运行时类的内部结构1:所有属性、所有方法、所有构造器
2)获取运行时类的内部结构1:父类、接口们、包、贷泛型的父亲、父亲的泛型
package com.atguigu03.reflectapply.apply2;import com.atguigu03.reflectapply.data.Person;
import org.junit.Test;import java.lang.reflect.Field;
import java.lang.reflect.Modifier;/*** @author shkstart* @create 14:28*/
public class FieldsTest {@Testpublic void test1() {Class clazz = Person.class;//getFields():获取到运行时类本身及其所有的父类中声明为public权限的属性Field[] fields = clazz.getFields();for (Field f : fields) {System.out.println(f);}//getDeclaredFields():获取当前运行时类中声明的所有属性Field[] declaredFields = clazz.getDeclaredFields();for (Field f : declaredFields) {System.out.println(f);}}//权限修饰符 变量类型 变量名@Testpublic void test2() {Class clazz = Person.class;Field[] declaredFields = clazz.getDeclaredFields();for (Field f : declaredFields) {//1.权限修饰符/** 0x是十六进制* PUBLIC = 0x00000001; 1 1* PRIVATE = 0x00000002; 2 10* PROTECTED = 0x00000004; 4 100* STATIC = 0x00000008; 8 1000* FINAL = 0x00000010; 16 10000* ...** 设计的理念,就是用二进制的某一位是1,来代表一种修饰符,整个二进制中只有一位是1,其余都是0** mod = 17 0x00000011* if ((mod & PUBLIC) != 0) 说明修饰符中有public* if ((mod & FINAL) != 0) 说明修饰符中有final*/int modifier = f.getModifiers();System.out.print(modifier + ":" + Modifier.toString(modifier) + "\t");// //2.数据类型Class type = f.getType();System.out.print(type.getName() + "\t");
//
// //3.变量名String fName = f.getName();System.out.print(fName);
//System.out.println();}}
}
得到的都是Person类声明的,父类的没有
获取每一个具体的信息
f.getModifiers()获取修饰符
类调里面的toString方法,可以看到权限修饰符
怎么做的?因为modifer这个数在Modifer这个类中有对应的十六进制对应关系
获取方法
都是当前类中有的
package apply2;import com.zhou.java.Person;
import org.junit.Test;import java.lang.reflect.Method;public class MethodTest {@Testpublic void test1() {Class clazz = Person.class;/* //getMethods():获取到运行时类本身及其所有的父类中声明为public权限的方法Method[] methods = clazz.getMethods();for (Method m:methods) {System.out.println(m);}*///getDeclareMethods():获取当前运行时类中声明的所有方法Method[] declaredMethods = clazz.getDeclaredMethods();for (Method m : declaredMethods) {System.out.println(m);}}}
package apply2;import com.zhou.java.Person;
import org.junit.Test;import java.lang.reflect.Field;
import java.lang.reflect.Modifier;public class FieldsTest {//权限修饰符、变量类型、变量名@Testpublic void test2() {Class clazz = Person.class;Field[] fields = clazz.getDeclaredFields();for (Field f : fields) {//1.权限修饰符int modifier = f.getModifiers();System.out.print(modifier + ":" + Modifier.toString(modifier) + "\t");//2.数据类型Class type = f.getType();System.out.println(type.getName() + "\t");//3.变量名String sname = f.getName();System.out.println(sname);System.out.println();}}@Testpublic void test1() {Class clazz = Person.class;//getFields():获取到运行时类本身及其所有的父类中声明为public权限的属性/*Field[] fields = clazz.getFields();for (Field c:fields) {System.out.println(c);}*///getDeclareFields():获取当前运行时类中声明的所有属性Field[] declaredFields = clazz.getDeclaredFields();for (Field f : declaredFields) {System.out.println(f);}}
}
person里使用了注解
自定义注解,要想在反射中获取注解及注解的信息,需要求注解的生命周期长
首先有大的实例,指明全类名
person继承时带泛型
希望结果里带上泛型
一个类可以实现多个接口,所以就是数组
什么时候用到,泛型写apply时
自定义泛型时,写dao时,dao通用的增删改查操作
操作的哪个表暂时不确定,回到Java层面哪个类不确定?
有可能需要当前类中继承的方法,有一些需要重写,有一些需要继承父类调用的方法,在这里需要获取这里这个类继承的父类的泛型
获取某一个对象,就不用传大的Class
获取Person类的实例,希望打印出String
先获取带泛型的父类
也有可能这个类,父类没有泛型,
也有可能有泛型,若有泛型就进行强转。
数组就是泛型参数构成的
角标0就是想要的String
不想要class,就获取class名字
调getname()调不了
是因为目前看到的是Type
Type是Class实现的接口
强转成Class
然后再进行Class.getName
package apply2;import org.junit.Test;import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;public class OtherTest {//获取运行时类的内部结构2:父类、接口们、包、带泛型的父类、父类的泛型等//5.获取运行时类的父类的泛型(难)@Testpublic void test5() throws ClassNotFoundException {Class clazz = Class.forName("com.zhou.java.Person");//获取带泛型的父类(Type是一个接口,Class实现了此接口)Type genericSuperclass = clazz.getGenericSuperclass();System.out.println(genericSuperclass);//如果父类是带泛型的,进行强转为ParameterizedTypeParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;//调用getActualTypeArguments()获取泛型的参数,结果是一个数组,因为可能有多个泛型参数。Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();//获取泛型参数的名称System.out.println(((Class) actualTypeArguments[0]).getName());}//4.获取运行时类所在的包@Testpublic void test4() throws ClassNotFoundException {Class clazz = Class.forName("com.zhou.java.Person");Package aPackage = clazz.getPackage();System.out.println(aPackage);}//3.获取运行时类实现的接口@Testpublic void test3() throws ClassNotFoundException {Class clazz = Class.forName("com.zhou.java.Person");Class[] interfaces = clazz.getInterfaces();for (Class i : interfaces) {System.out.println(i);}}//1.获取运行时类的父类@Testpublic void test1() throws ClassNotFoundException {Class clazz = Class.forName("com.zhou.java.Person");Class superclass = clazz.getSuperclass();System.out.println(superclass);}//2.获取运行时类的带泛型的父类@Testpublic void test2() throws ClassNotFoundException {Class clazz = Class.forName("com.zhou.java.Person");Type genericSuperclass = clazz.getGenericSuperclass();System.out.println(genericSuperclass);}}
package apply2;import com.zhou.java.Person;
import org.junit.Test;import java.lang.reflect.Method;public class MethodTest {@Testpublic void test1() {Class clazz = Person.class;/* //getMethods():获取到运行时类本身及其所有的父类中声明为public权限的方法Method[] methods = clazz.getMethods();for (Method m:methods) {System.out.println(m);}*///getDeclareMethods():获取当前运行时类中声明的所有方法Method[] declaredMethods = clazz.getDeclaredMethods();for (Method m : declaredMethods) {System.out.println(m);}}}
package apply2;import com.zhou.java.Person;
import org.junit.Test;import java.lang.reflect.Field;
import java.lang.reflect.Modifier;public class FieldsTest {//权限修饰符、变量类型、变量名@Testpublic void test2() {Class clazz = Person.class;Field[] fields = clazz.getDeclaredFields();for (Field f : fields) {//1.权限修饰符int modifier = f.getModifiers();System.out.print(modifier + ":" + Modifier.toString(modifier) + "\t");//2.数据类型Class type = f.getType();System.out.println(type.getName() + "\t");//3.变量名String sname = f.getName();System.out.println(sname);System.out.println();}}@Testpublic void test1() {Class clazz = Person.class;//getFields():获取到运行时类本身及其所有的父类中声明为public权限的属性/*Field[] fields = clazz.getFields();for (Field c:fields) {System.out.println(c);}*///getDeclareFields():获取当前运行时类中声明的所有属性Field[] declaredFields = clazz.getDeclaredFields();for (Field f : declaredFields) {System.out.println(f);}}
}
7.3反射的应用3:调用指定的结构、指定的属性、方法、构造器
还是针对Person类
age非静态,获取它的值,跟对象打交道
创建当前类的对象
创建Person类的实例
获取当前类中名字叫age属性,File是java.lang包下的子类
这个属性是实例变量,得跟对象打交道
得说明是哪一个对象的值,是per对象的
因为此处是默认权限
改成public
设置属性的值
package apply3;import com.zhou.java.Person;
import org.junit.Test;import java.lang.reflect.Field;public class ReflectTest {//反射的应用:调用指定的属性// public int age = 1;@Testpublic void test() throws Exception {Class clazz = Person.class;Person person = (Person) clazz.newInstance();//1.获取运行时类指定名的属性Field agefield = clazz.getField("age");System.out.println(agefield.get(person));//2.获取或设置属性的值agefield.set(person, 2);System.out.println(agefield.get(person));}}
操作私有权限的属性
按age代码重先更改为name
package apply3;import com.zhou.java.Person;
import org.junit.Test;import java.lang.reflect.Field;public class ReflectTest {//private String name;@Testpublic void test2() throws Exception{Class clazz = Person.class;Person person = (Person)clazz.newInstance();//1.通过Class实例调用getDeclareField(String fieldName)获取运行时类指定的属性Field namefield = clazz.getDeclaredField("name");//2、确保此属性可以访问namefield.setAccessible(true);//3、通过Filed类的实例调用get(Object obj)获取// 或设置此属性的值set(Object obj,Object value)namefield.set(person,"Tom");System.out.println(namefield.get(person));}}
调用静态属性
infoField.get()里参数不写不行,因为他不是可变参数。写null是没问题的。
怎么理解此处null?获取属性时就是用Class获取的,clazz就是Person.class
若是实例变量,就必须要传对象
public class ReflectTest {//调用静态属性// private static String info;@Testpublic void test3() throws Exception{Class clazz = Person.class;Field infofield = clazz.getDeclaredField("info");infofield.setAccessible(true);/* infofield.set(Person.class,"我是一个人");System.out.println(infofield.get(Person.class));
*/infofield.set(null,"我是一个人");System.out.println(infofield.get(null));}}
这里是引用
这里为啥不能写Integer
因为不同类型的值才可以自动装箱,此处是类型
int class和Integer class 显然是不同的实例
这叫自动装箱
正常情况下,非静态方法用对象去调,传实参
发现方法有返回值,返回值怎么体现呢?
~~删除
invoke方法的返回值就是shownation的返回值
import java.lang.reflect.Field;
import java.lang.reflect.Method;public class ReflectTest {//反射应用3-2:调用指定的方法// private String showNation(String nation, int age){}@Testpublic void test4() throws Exception{Class clazz = Person.class;Person person =(Person) clazz.newInstance();//1.通过Class的实列调用getDeclareMethod(String methodName,Claass...args)获取指定的方法Method showNation = clazz.getDeclaredMethod("showNation", String.class, int.class);//2.setAccessible(true);确保此方法是可以调用的showNation.setAccessible(true);//3.通过Method实例调用invoke(Onject obj,objs)即为对Method对应的方法调用//invoke()返回值即为Method对应的返回值//特别的,如果Method对应的返回值类型为void,则invoke()返回值为bullObject invoke = showNation.invoke(person, "CHN", 10);System.out.println(invoke);}
涉及静态方法
package apply3;import com.zhou.java.Person;
import org.junit.Test;import java.lang.reflect.Field;
import java.lang.reflect.Method;public class ReflectTest {//调用静态方法// public static void showInfo() {}@Testpublic void test5() throws Exception{Class clazz = Person.class;Method declaredMethod = clazz.getDeclaredMethod("showInfo");declaredMethod.setAccessible(true);Object invoke = declaredMethod.invoke(null);System.out.println(invoke);}}
package apply3;import com.zhou.java.Person;
import org.junit.Test;import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;public class ReflectTest {//调用指定构造器@Testpublic void test6() throws Exception{Class clazz= Person.class;Constructor constructor = clazz.getDeclaredConstructor(String.class, int.class);constructor.setAccessible(true);Person per = (Person)constructor.newInstance("Tom", 12);System.out.println(per);}
}
框架大量使用注解
框架使用反射去读注解,根据注解属性值做相应处理
熟悉常用注解,知道注解怎么赋值
参照SupperssWarming,他怎么写,我们就怎么写,
Table修饰类、接口类型
注解能让我们获取就的是Runnntime
在类的声明处就可以使用
此处为什么报错
没有进行赋值
此处可以进行默认赋值
此处就不再报错
反射复习
1.反射的概述(熟悉)
Java给我们提供了一套API
运行时动态获取指定对象所属类(通过对象调getClass)
API
2.Class的源头
3.类的加载过程、类的加载器(理解)
4.反射的应用1:创建运行时类的对象
5.反射的应用2:获取运行时类所有的结构
6.反射的应用3:调用指定的结构:属性、方法、构造器
7.反射的应用4:注解的使用
8.体会反射的动态性