目录
一.基本了解
1.作用
像Java 在运行时结构不可变的语言 有了反射体现了动态的特性
运行时获取任何类的内部信息,并能直接操作任意对象的内部属性及方法
进行动态代理
java.lang.reflect包下
框架 = 反射+注解+设计模式
2.类的加载过程
类的加载 java.exe 指令执行过后将类的class文件读入内存
并为之创建一个java.lang.Class对象 由类加载器完成此过程
类的链接 将类的二进制数据合并到JRE中
类的初始化 JVM负责对类进行初始化
程序经过javac.exe命令之后会生成多个或一个字节码文件,然后使用java.exe命令对某个字节码文件进行解释运行,相当于将某个字节码文件加载 到内存中。加载到内存中的类叫运行时类,此运行时类就作为Class的实例。
注:之前通过类造对象,类本身也是Class的对象
3.类的加载器
类的加载器
Bootstrap引导类加载器 负责Java核心库
Extension扩展类加载器 负责jre/lib/etx目录下的jar包
System系统类加载器 负责java.class.path 的类与jar包装入工作,最常用的加载器
@Test
public void test6() {
ClassLoader classLoader = T1.class.getClassLoader();
// 自定义类 系统类加载器
System.out.println(classLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2
ClassLoader parent = classLoader.getParent();
// 系统类加载器getParent() 扩展类加载器
System.out.println(parent);//sun.misc.Launcher$ExtClassLoader@28a418fc
ClassLoader parent1 = parent.getParent();
// 扩展类加载器getParent(); null不代表没有 其上层是引导类加载器只不过获取不到
System.out.println(parent1);//null
}
4.类加载器的实例(通过jcbc.properties)
// 实例1
@Test
public void ex1() throws Exception {
// 配置properties文件
Properties properties = new Properties();
// 方式一 路径为当前module下
FileInputStream fis = new FileInputStream("src//reflect//jdbc.properties");
properties.load(fis);
String pwd = properties.getProperty("pwd");
System.out.println(pwd);
fis.close();
// 方式二 用类加载器 文件路径为module下面的src下
ClassLoader classLoader = Main.class.getClassLoader();
InputStream resourceAsStream = classLoader.getResourceAsStream("reflect//jdbc.properties");
properties.load(resourceAsStream);
System.out.println(properties.getProperty("pwd"));
}
5.获取Class的实例方式(4种)
@Test
public void test2() throws ClassNotFoundException {
// 1.调用运行时类的属性 .class
Class<T1> clazz = T1.class;
// 2.通过运行时类的对象
T1 t1 = new T1();
Class clazz2 = t1.getClass();
// 3.(常用)调用Class的静态方法 Class.forName("reflect.T1") 全类名 抛异常 ClassNotFoundException
Class<?> clazz3 = Class.forName("reflect.T1");
// 加载到内存中的运行时类,会缓存一定时间 在此时间之内 这三种方式 获取的是同一个运行时类
System.out.println(clazz == clazz2);//true
System.out.println(clazz == clazz3);//true
// 4.使用类的加载器 Main.class.getClassLoader(); Main此类的类名
ClassLoader classLoader = Main.class.getClassLoader();
Class clazz4 = classLoader.loadClass("reflect.T1");
}
6. Class实例可以是那些结构
接口数组,基本数据类型,包括void都可以
@Test
public void test5() {
Class b = int.class;
Class a = int[].class;
Class c = void.class;
Class d = Class.class;
}
7. 通过反射创建运行时类的对象
@Test
public void test8() throws Exception {
// 创建class的实例
Class<T1> clazz = T1.class;
// 返回的是object 这里强转为T1
// newInstance(); 创建对应的运行时类对象
// 这里底层也是new出来的 调用空参的构造器(权限至少大于private)
T1 t1 = (T1) clazz.newInstance();
}
// 获取指定类的对象
public Object getInstance(String classpath) throws Exception {
Class clazz = Class.forName(classpath);
return clazz.newInstance();
}
8.测试 反射的动态性
@Test
public void ex2() throws Exception {
String classPath = " ";
int num = new Random().nextInt(3);//0,1,2
switch (num) {
// 要有空参构造器
case 0:
classPath = "java.util.Date";
break;
case 2:
classPath = "java.lang.Object";
break;
case 1:
// 注意路径是.不是/
classPath = "reflect.Main";
break;
}
Object obj = getInstance(classPath);
System.out.println(obj);
}
9.获取运行类的完整结构
@Test
public void test10() {
// getFields() 获取当前运行时类及其父类声明为public访问权限的属性
// getDeclaredFields(); 获取当前运行时类的所有属性
Class clazz = T1.class;
Field[] df = clazz.getDeclaredFields();
for (Field f :
df) {
// 1.权限修饰符 2代表public
int modifiers = f.getModifiers();
// toString() 返回String类型的权限修饰符
System.out.print(Modifier.toString(modifiers) + "\t");
// 2.数据类型
Class type = f.getType();
System.out.print(type.getName() + "\t");
// 3.变量名
String fName = f.getName();
System.out.print(fName + "\t");
System.out.println();
// private java.lang.String name
// private int age
}
System.out.println("!!!!!!!!!!!!!!!!");
// 获取方法的内部结构
Method[] methods = clazz.getDeclaredMethods();
for (Method m:
methods) {
// 获取注解
Annotation[] annotations = m.getAnnotations();
for (Annotation a:
annotations) {
System.out.println(a);
}
// 获取方法权限
System.out.print(Modifier.toString(m.getModifiers())+" ");
// 获取返回值类型
System.out.print(m.getReturnType().getName()+" ");
// 获取方法名
System.out.print(m.getName());
// 获取形参列表
System.out.print("(");
Class<?>[] parameterTypes = m.getParameterTypes();
int i =0;
if (!(parameterTypes==null || parameterTypes.length==0)){
for (Class p:
parameterTypes) {
System.out.print(p.getName()+" args"+i+" ");
i++;
}
}
System.out.print(") ");
// 抛出的异常
Class<?>[] exceptionTypes = m.getExceptionTypes();
if (!(exceptionTypes == null || exceptionTypes.length==0)) {
System.out.print("throws ");
for (int j = 0; j < exceptionTypes.length; j++) {
System.out.print(exceptionTypes[i].getName()+" ");
}
}
System.out.println();
}
// public java.lang.String toString()
// public java.lang.String getName()
// public void setName(java.lang.String args0 )
// public void show()
// public int getAge() throws java.lang.InstantiationException
// public void setAge(int args0 )
// private void say()
}
10.获取运行时类带泛型的父类
@Test
public void test11(){
Class clazz = Main.class;
Type genericSuperclass = clazz.getGenericSuperclass();
System.out.println(genericSuperclass);
}
11.获取运行时类带泛型的父类的泛型
@Test
public void test12(){
Class clazz = Main.class;
Type genericSuperclass = clazz.getGenericSuperclass();
ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
System.out.println(actualTypeArguments[0].getTypeName());
}
12.获取运行时类父类实现的接口
@Test
public void test13(){
Class clazz = Main.class;
// clazz.getSuperclass()获取运行时类的父类 获取父类的接口getInterfaces()
Class[] interfaces = clazz.getSuperclass().getInterfaces();
for (Class c :
interfaces) {
System.out.println(c);
}
}
13.获取运行时类所在的包
@Test
public void test14(){
Class clazz = Main.class;
Package aPackage = clazz.getPackage();
System.out.println(aPackage);
}
14.获取运行时类声明的注解
@Test
public void test15(){
Class clazz = Main.class;
Annotation[] annotations = clazz.getAnnotations();
for (Annotation a:
annotations) {
System.out.println(a);
}
}
二 获取运行时类的指定结构
1.获取运行时类的指定属性
@Test
public void test20() throws Exception {
Class clazz = T1.class;
// 创建运行时类的对象
T1 t =(T1) clazz.newInstance();
// 获取指定的属性 clazz.getField() 获取到的时声明为public的
// getDeclaredField("age"); 可以获取private但需要setAccessible(true);
Field age = clazz.getDeclaredField("age");
// 设置age是可访问的
age.setAccessible(true);
// 给获取的属性赋值 set(Object obj 运行时类的对象, Object value 设置的值)
age.set(t,12);
System.out.println(t);
}
2.获取运行时类的指定方法
@Test
public void test21() throws Exception {
Class clazz = T1.class;
// 创建运行时类的对象
T1 t =(T1) clazz.newInstance();
// 获取指定的方法 参数1方法名 参数二 形参列表
Method say = clazz.getDeclaredMethod("say", String.class);
// 设置age是可访问的
say.setAccessible(true);
// 调用获取的方法 invoke(Object obj 运行时类的对象, Object value 设置的值)
// invoke的返回值 即对应方法的返回值
say.invoke(t,"para");
System.out.println("!!!!!!");
// 调用静态方法 private static void st()
Method st = clazz.getDeclaredMethod("st");
st.setAccessible(true);
// 方法没有返回值 填null也行 因为是静态的 所以newInstance();这一步时已经获取到方法了
Object invoke = st.invoke(null);
System.out.println(invoke);//null
}
3.获取运行时类的指定构造器
@Test
public void test23() throws Exception {
Class clazz = T1.class;
// 注意 int 和 Integer不一样
Constructor dc = clazz.getDeclaredConstructor(String.class,int.class);
dc.setAccessible(true);
// 用此构造器创建运行类的对象
T1 t = (T1) dc.newInstance("luck", 22);
System.out.println(t);
}
三 静态代理和动态代理
1.静态代理
特点
被代理类和代理类在编译期间就确定下来了
代理类和被代理类要实现相同的接口
步骤1.创建普通的接口
interface ClothFactory{
void produceCloth();
}
步骤2.创建被代理类
class NikeClothFactory implements ClothFactory{
@Override
public void produceCloth() {
System.out.println("生产的Nike");
}
}
步骤3.创建代理类
class ProxyCloth implements ClothFactory{
private ClothFactory clothFactory;
// 初始话的时候拿被代理的对象进行初始化
public ProxyCloth(ClothFactory clothFactory){
this.clothFactory = clothFactory;
}
@Override
public void produceCloth() {
System.out.println("代理对象启动");
clothFactory.produceCloth();
System.out.println("代理结束");
}
}
步骤4.测试
public class StaticProxyTest {
public static void main(String[] args) {
// 1.声明被代理对象
NikeClothFactory ncf = new NikeClothFactory();
// 2.声明代理类对象
ProxyCloth pc = new ProxyCloth(ncf);
pc.produceCloth();
// 代理对象启动
// 生产的Nike
// 代理结束
}
}
2.动态代理
步骤1.创建普通的接口
interface Human{
String getBelief();
void say(String hello);
}
步骤2.创建被代理类
//被代理类
class SuperMan implements Human{
@Override
public String getBelief() {
return "No fair in the word!";
}
@Override
public void say(String hello) {
System.out.println("I am SuperMan"+hello);
}
}
步骤3.创建动态代理类(Proxy.newProxyInstance()这个方法需要步骤4)
//代理类
class ProxyFac {
public static Object getInstance(Object o) throws Exception {
MyInvocationHandler h = new MyInvocationHandler();
// 将被代理类对象传进去
h.bind(o);
// o 被代理类的对象 借用此方法返回一个代理对象
// public static Object newProxyInstance
// (被代理类加载器, 被代理类实现的接口,InvocationHandler h)
// 调用方法生成一个代理类对象
return Proxy.newProxyInstance(o.getClass().getClassLoader(),
o.getClass().getInterfaces(),h);
}
}
步骤4.创建一个类实现 InvocationHandler接口 (动态调用被代理类中的同名方法实现)
class MyInvocationHandler implements InvocationHandler{
private Object o;//method.invoke(o, args);赋值时
// 需要使用被代理类的对象赋值 所以要把该对象传进来
public void bind(Object o){
this.o = o;
}
@Override
// 当我们通过代理类的对象,调用方法A时,就会自动的调用如下方法
// 将被代理类要执行的方法功能就声明在invoke方法中
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// invoke(Object proxy, 要调用的方法, 参数)
// 其方法的返回值即被代理类方法的返回值
// method即为代理类对象调用的方法 此
// invoke(对象, 参数)
Object invoke = method.invoke(o, args);
return invoke;
}
}
步骤5.测试
public class ProxyTest {
public static void main(String[] args) throws Exception {
SuperMan sm = new SuperMan();
// 为什么时是human 如果superMan的话,代理无意义
Human hm =(Human) ProxyFac.getInstance(sm);
System.out.println(hm.getBelief());
// hm.say("好麻烦")
// 此时就会进入我们重写的invoke method=say args="好麻烦"
hm.say("好麻烦");
}
}
输出结果
No fair in the word!
I am SuperMan好麻烦