JAVA反射

JAVA反射

class类

获取一个类的实例有三种方法
方法一:直接通过一个class的静态变量class获取
Class cls = String.class

方法二:如果我们有一个实例变量,可以通过该实例变量提供的getClass()方法获取
String s = "Hello";
Class cls = s.getClass();

方法三:如果知道一个class的完整类名,可以通过静态方法Class.forName()获取
Class cls = Class.forName("java.lang.String");


通过实例获取类的信息
System.out.println("Class name: " + cls.getName());
System.out.println("Simple name: " + cls.getSimpleName());
if (cls.getPackage() != null) {
    System.out.println("Package name: " + cls.getPackage().getName());
}
System.out.println("is interface: " + cls.isInterface());
System.out.println("is enum: " + cls.isEnum());
System.out.println("is array: " + cls.isArray());
System.out.println("is primitive: " + cls.isPrimitive());

// 获取String的Class实例:
Class cls = String.class;
// 创建一个String实例:
String s = (String) cls.newInstance();
上述代码相当于new String()。
通过Class.newInstance()可以创建类实例,
它的局限是:只能调用public的无参数构造方法。
带参数的构造方法,或者非public的构造方法都无法通过Class.newInstance()被调用


动态加载
JVM在执行Java程序的时候,并不是一次性把所有用到的class全部加载到内存,而是第一次需要用到class时才加载


动态加载class的特性对于Java程序非常重要。利用JVM动态加载class的特性,
我们才能在运行期根据条件加载不同的实现类。例如,Commons Logging总是优先使用Log4j,
只有当Log4j不存在时,才使用JDK的logging。利用JVM动态加载特性,大致的实现代码如下:

// Commons Logging优先使用Log4j:
LogFactory factory = null;
if (isClassPresent("org.apache.logging.log4j.Logger")) {
    factory = createLog4j();
} else {
    factory = createJdkLog();
}

boolean isClassPresent(String name) {
    try {
        Class.forName(name);
        return true;
    } catch (Exception e) {
        return false;
    }
}
这就是为什么我们只需要把Log4j的jar包放到classpath中,Commons Logging就会自动使用Log4j的原因。

访问字段

getField(name):根据字段名获取某个public的field(包括父类)
getDeclaredField(name):根据字段名获取当前类的某个field(不包括父类)
Field[] getFields():获取所有public的field(包括父类)
Field[] getDeclaredFields():获取当前类的所有field(不包括父类)

getName():返回字段名称
getType():返回字段类型
getModifiers():返回字段的修饰符

Field f = String.class.getDeclaredField("value");
f.getName(); // "value"
f.getType(); // class [B 表示byte[]类型
int m = f.getModifiers();
Modifier.isFinal(m); // true
Modifier.isPublic(m); // false
Modifier.isProtected(m); // false
Modifier.isPrivate(m); // true
Modifier.isStatic(m); // false

获取字段的值
Object p = new Person("Xiao Ming");
Class c = p.getClass();
Field f = c.getDeclaredField("name");
// 注意name要是private是要写这句
f.setAccessible(true);
Object value = f.get(p);
System.out.println(value); // "Xiao Ming"


通过Field实例可以读取或设置某个对象的字段,如果存在访问限制,要首先调用setAccessible(true)来访问非public字段。

通过反射读写字段是一种非常规方法,它会破坏对象的封装。

调用方法

Method getMethod(name, Class...):获取某个public的Method(包括父类)
Method getDeclaredMethod(name, Class...):获取当前类的某个Method(不包括父类)
Method[] getMethods():获取所有public的Method(包括父类)
Method[] getDeclaredMethods():获取当前类的所有Method(不包括父类)

getName():返回方法名称,例如:"getScore";
getReturnType():返回方法返回值类型,也是一个Class实例,例如:String.class;
getParameterTypes():返回方法的参数类型,是一个Class数组,例如:{String.class, int.class};
getModifiers():返回方法的修饰符,它是一个int,不同的bit表示不同的含义。

调用方法
String s1 = "hello world";
Method substring = String.class.getMethod("substring", int.class);
Object invoke = substring.invoke(s1, 6);
System.out.println(invoke);

调用静态方法
如果获取到的Method表示一个静态方法,调用静态方法时,由于无需指定实例对象,
所以invoke方法传入的第一个参数永远为null。我们以Integer.parseInt(String)为例:
// 获取Integer.parseInt(String)方法,参数为String:
Method m = Integer.class.getMethod("parseInt", String.class);
// 调用该静态方法并获取结果:
Integer n = (Integer) m.invoke(null, "12345");
// 打印调用结果:
System.out.println(n);


调用非public方法
Person p = new Person();
Method m = p.getClass().getDeclaredMethod("setName", String.class);
m.setAccessible(true); //跟访问非public的字段是一样的
m.invoke(p, "Bob");
System.out.println(p.name);

多态 针对某个类型的方法调用,其真正执行的方法取决于运行时期实际类型的方法
Method m = Person.class.getMethod("hello");
m.invoke(new Student());
实际上相当于:
Person p = new Student();
p.hello();


调用构造函数
getConstructor(Class...):获取某个public的Constructor;
getDeclaredConstructor(Class...):获取某个Constructor;
getConstructors():获取所有public的Constructor;
getDeclaredConstructors():获取所有Constructor。
注意Constructor总是当前类定义的构造方法,和父类无关,因此不存在多态的问题。

调用非public的Constructor时,
必须首先通过setAccessible(true)设置允许访问。

旧:Person p = Person.class.newInstance();
新:Constructor cons1 = Person.class.getConstructor();  Person p = cons1.newInstance();

获取继承关系
Class getSuperclass():获取父类类型;

Class i = Integer.class;
Class n = i.getSuperclass();
System.out.println(n);
Class o = n.getSuperclass();
System.out.println(o);
System.out.println(o.getSuperclass());

Class[] getInterfaces():获取当前类实现的所有接口。
Class s = Integer.class;
Class[] is = s.getInterfaces();
for (Class i : is) {
    System.out.println(i);
}
通过Class对象的isAssignableFrom()方法可以判断一个向上转型是否可以实现
// Integer i = ?
Integer.class.isAssignableFrom(Integer.class); // true,因为Integer可以赋值给Integer
// Number n = ?
Number.class.isAssignableFrom(Integer.class); // true,因为Integer可以赋值给Number
// Object o = ?
Object.class.isAssignableFrom(Integer.class); // true,因为Integer可以赋值给Object
// Integer i = ?
Integer.class.isAssignableFrom(Number.class); // false,因为Number不能赋值给Integer

动态代理

什么是动态代理:Java标准库提供了动态代理功能,允许在运行期动态创建一个接口的实例

通常情况我们写的就是静态代理的代码:
public interface Hello {
    void morning(String name);
}
编写实现类:
public class HelloWorld implements Hello {
    public void morning(String name) {
        System.out.println("Good morning, " + name);
    }
}

这是提前就编译好的。动态代理就是运行时候才生成一个实现接口的实例
动态代理步骤:
1. 定义一个InvocationHandler实例,它负责实现接口的方法调用
2. 通过Proxy.newProxyInstance()创建interface实例,它需要3个参数:
使用的ClassLoader,通常就是接口类的ClassLoader
需要实现的接口数组,至少需要传入一个接口进去
用来处理接口方法调用的InvocationHandler实例
将返回的Object强制转型为接口

InvocationHandler dynamic_proxy =
    new InvocationHandler() {
      @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if ("hello".equals(method.getName())) {
          System.out.println("dynamic proxy");
        }
        return null;
      }
    };

Person o =
    (Person)
        Proxy.newProxyInstance(
            Person.class.getClassLoader(), new Class[] {Person.class}, dynamic_proxy);
o.hello();
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

冰明子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值