Java笔记--反射
本站字数:108k 本文字数:4k 预计阅读时长:18min 访问次数:次
Java笔记–反射
Java反射机制概述
Reflection (反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射。
正常方式和反射方式的对比:
Java反射机质研究及其应用
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时获取泛型信息
- 在运行时调用任意一个对象的成员变量和方法
- 在运行时处理注解
- 生成动态代理
反射相关的主要API
- java.lang.Class: 代表一个类
- java.lang.reflect.Method:代表类的方法
- java.lang.reflect.Field:代表类的成员变量
- java.lang.reflect.Constructor:代表类的构造器
反射初体验
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| @Test public void test1() { Person p1 = new Person("ABC", 21);
p1.age = 18; System.out.println(p1.toString()); p1.show();
}
@Test public void test2() throws Exception { Class clazz = Person.class; Constructor cons = clazz.getConstructor(String.class, int.class); Object obj = cons.newInstance("Tom", 12); System.out.println(obj.toString()); Person p = (Person)obj; System.out.println(p.toString());
Field age = clazz.getDeclaredField("age"); age.set(p, 10); System.out.println(p.toString()); Method show = clazz.getDeclaredMethod("show"); show.invoke(p);
System.out.println("*********************************");
Constructor pS = clazz.getDeclaredConstructor(String.class); pS.setAccessible(true); Object obj1 = pS.newInstance("Toms"); Person p1 = (Person)obj1; System.out.println(p1.toString());
Field name = clazz.getDeclaredField("name"); name.setAccessible(true); name.set(p1, "Thompson"); System.out.println(p1.toString());
Method showNation = clazz.getDeclaredMethod("showNation", String.class); showNation.setAccessible(true); String nation = (String)showNation.invoke(p, "中国"); System.out.println(nation); }
|
反射机制和封装性辨析
Q1:通过直接new的方式或反射的方式都可以调用公共的结构,开发中用哪个?
A1:直接使用new的方式
Q2:什么时候会用到反射机制?
A2:不确定创建一个什么样的对象时,需要时用反射机制。
Q3:反射机制一面向对象中的封装性是不是矛盾的?如何看待两个技术?
A3:不矛盾。
Class类的理解和获取Class实例
Class类的理解
类的加载过程
程序经过javac.exe命令以后,会生成一个或多个字节码文件(.class结尾),此过程是编译过程。接着我们使用java.exe对某个字节码文件,进行解释运行。相当于讲某个字节码文件加载到内存中,此过程叫做类的加载。加载到内存中的类,叫做运行时类,就作为Class的一个实例。
换句话说,Class的实例就对应着一个运行时类。
加载到内存中的运行时类,会缓存一段时间。在此时间之内,我们可以通过不同的方式来获取此运行时类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
@Test public void test3() throws ClassNotFoundException { Class<Person> clazz1 = Person.class; System.out.println(clazz1.toString()); Person p1 = new Person(); Class<? extends Person> clazz2 = p1.getClass(); System.out.println(clazz2.toString()); Class<?> clazz3 = Class.forName("xyz.klenkiven.reflection.Person");
System.out.println("clazz1 == clazz2\t" + (clazz1 == clazz2)); System.out.println("clazz1 == clazz3\t" + (clazz1 == clazz3));
ClassLoader classLoader = ReflectionTest.class.getClassLoader(); Class<?> clazz4 = classLoader.loadClass("xyz.klenkiven.reflection.Person"); System.out.println(clazz4.toString());
}
|
那些类型可以有Class对象
class:
内部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类
interface:接口
[]:数组
enmu:枚举
annotation:注解@interface
基本数据类型
void
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| @Test public void test4() { Class<?> c1 = Object.class; Class<?> c2 = Comparable.class; Class<?> c3 = String[].class; Class<?> c4 = int[][].class; Class<?> c5 = ElementType.class; Class<?> c6 = Override.class; Class<?> c7 = int.class; Class<?> c8 = void.class; Class<?> c9 = Class.class;
int[] a = new int[10]; int[] b = new int[100]; Class<?> c10 = a.getClass(); Class<?> c11 = b.getClass(); System.out.println(c10 == c11); }
|
了解类的加载器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @Test public void test1() { ClassLoader classLoader = ClassLoaderTest.class.getClassLoader(); System.out.println(classLoader); ClassLoader parent = classLoader.getParent(); System.out.println(parent); ClassLoader parent1 = parent.getParent(); System.out.println(parent1);
ClassLoader classLoader1 = String.class.getClassLoader(); System.out.println(classLoader); }
|
使用ClassLoader加载配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| @Test public void test2() throws Exception {
Properties pros = new Properties();
ClassLoader classLoader = ClassLoaderTest.class.getClassLoader(); InputStream resourceAsStream = classLoader.getResourceAsStream("jdbc.properties"); pros.load(resourceAsStream);
String user = pros.getProperty("user"); String password = pros.getProperty("password");
System.out.println("user = " + user + " password = " + password);
}
|
创建运行时类的对象
使用 newInstance()
创建要求:
- 使用这个方法一定需要有一个空参构造器
- 使用这个方法一定需要一个合适的访问权限,通常权限为
public
在javabean中要求提供一个public的空参构造器,原因:
- 便于通过反射,创建运行时类的对象
- 便于子类继承此运行时类时,默认调用
super()
时,保证父类由此构造器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| @Test public void test02() { int num = new Random().nextInt(3); String classPath = ""; switch (num) { case 0: classPath = "java.util.Date"; break; case 1: classPath = "java.util.EnumSet"; break; case 2: classPath = "xyz.klenkiven.reflection.Person"; break; } try { Object obj = getInstance(classPath); System.out.println(obj.toString()); } catch (Exception e) { e.printStackTrace(); } }
public Object getInstance(String classPath) throws Exception { Class<?> clazz = Class.forName(classPath); return clazz.newInstance(); }
|
获取运行时类的完整结构
获取当前运行时类的属性结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| @Test public void test1() {
Class<Animal> clazz = Animal.class; System.out.println("getDeclaredFields()"); Field[] fields = clazz.getFields(); for (Field f : fields) { System.out.println(f); } System.out.println(); System.out.println("getDeclaredFields()"); Field[] declaredField = clazz.getDeclaredFields(); for (Field f: declaredField) { System.out.println(f); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| @Test public void test2() { Class<Animal> clazz = Animal.class; Field[] declaredFields = clazz.getDeclaredFields(); for (Field f: declaredFields) {
int modifiers = f.getModifiers(); System.out.print(Modifier.toString(modifiers) + "\t");
Class<?> type = f.getType(); System.out.print(f + "\t");
String name = f.getName(); System.out.print(name);
System.out.println(); } }
|
获取运行时类的方法结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| @Test public void test1() { Class<?> clazz = Animal.class;
Method[] methods = clazz.getMethods(); for (Method m: methods) { System.out.println(m); } System.out.println();
Method[] declaredMethods = clazz.getDeclaredMethods(); for (Method m: declaredMethods) { System.out.println(m); } }
|
- @Xxxx 权限修饰符 返回值类型 方法名 (数据类型 参数名,…) throws XxxException
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| @Test public void test2() { Class<?> clazz = Animal.class; Method[] declaredMethods = clazz.getDeclaredMethods(); for (Method m: declaredMethods) { Annotation[] annotations = m.getAnnotations(); for (Annotation a: annotations) { System.out.println(a); }
System.out.print(Modifier.toString(m.getModifiers()) + "\t");
System.out.print(m.getReturnType().getName() + "\t");
System.out.print(m.getName());
System.out.print("("); Class<?>[] parameterTypes = m.getParameterTypes(); if (!(parameterTypes == null || parameterTypes.length == 0)){ for (int i = 0; i < parameterTypes.length; i++) { if (i != 0) System.out.println(", "); System.out.print(parameterTypes[i].getName() + " args_" + i); } } System.out.print(")\t");
Class<?>[] exceptionTypes = m.getExceptionTypes(); if (!(exceptionTypes == null || exceptionTypes.length == 0)){ System.out.print("throws\t"); for (int i = 0; i < exceptionTypes.length; i++) { if (i != 0) System.out.print(" ,"); System.out.print(exceptionTypes[i].getName()); } }
System.out.println(); } }
|
获取运行时类的构造器结构
获取运行时类的构造器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| @Test public void test1() {
Class<?> clazz = Animal.class; Constructor<?>[] constructors = clazz.getConstructors(); for (Constructor c: constructors){ System.out.println(c); }
System.out.println();
Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors(); for (Constructor c: declaredConstructors) { System.out.println(c); } }
|
其他的属性的获取,可以参考方法部分的获取方法
获取运行时类的带有泛型的父类
1 2 3 4 5 6 7
| @Test public void test2() { Class<?> clazz = Animal.class;
Class<?> superclass = clazz.getSuperclass(); System.out.println(superclass); }
|
1 2 3 4 5 6 7
| @Test public void test3() { Class<?> clazz = Animal.class;
Type genericSuperclass = clazz.getGenericSuperclass(); System.out.println(genericSuperclass.getTypeName()); }
|
1 2 3 4 5 6 7 8 9 10 11 12
| @Test public void test4() { Class<?> clazz = Animal.class;
Type genericSuperclass = clazz.getGenericSuperclass(); ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass; Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); System.out.println(((Class)actualTypeArguments[0]).getName()); }
|
获取运行时类实现的接口,所在包,注解等等
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| @Test public void test5() { Class<?> clazz = Animal.class;
Class<?>[] interfaces = clazz.getInterfaces(); for (Class<?> c: interfaces) { System.out.println(c); }
System.out.println();
Class<?> superclass = clazz.getSuperclass(); Class<?>[] interfaces1 = superclass.getInterfaces(); for (Class<?> c: interfaces1) { System.out.println(c); } }
|
1 2 3 4 5 6 7
| @Test public void test6() { Class<?> clazz = Animal.class;
Package aPackage = clazz.getPackage(); System.out.println(aPackage); }
|
1 2 3 4 5 6 7 8 9
| @Test public void test7() { Class<?> clazz = Animal.class;
Annotation[] annotations = clazz.getAnnotations(); for (Annotation a: annotations) { System.out.println(a); } }
|
调用运行时类的完整结构
调用运行时类中指定的属性
- 获取Class类
- 获取指定的属性
- 修改指定的属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| @Test public void testField() throws Exception { Class<?> clazz = Animal.class;
Animal a = (Animal)clazz.newInstance();
Field id = clazz.getField("id"); id.set(a, 1001); int aid = (int)id.get(a); System.out.println(aid); }
|
上面的这种方法已经是过时或者是用处比较小的方法,一般的话使用下面的getDeclaredField()
方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| @Test public void testField1() throws Exception { Class<Animal> clazz = Animal.class;
Animal a = clazz.newInstance();
Field declaredField = clazz.getDeclaredField("name");
declaredField.setAccessible(true); declaredField.set(a, "Dog");
String name = (String)declaredField.get(a); System.out.println(name);
}
|
调用运行时类中指定的方法
- 获取指定的方法
- 保证方法一定是可访问的
setAccessable(true)
- 调用方法的
invoke()
invoke()
方法会返回一个Object的返回值
,但是可以强转为原方法对应的返回值类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| @Test public void testMethod() throws Exception { Class<Animal> clazz = Animal.class;
Animal animal = clazz.newInstance();
Method show = clazz.getDeclaredMethod("show", String.class); show.setAccessible(true);
show.invoke(animal, "犬类"); String breed = (String)show.invoke(animal, "犬类"); System.out.println(breed);
Method showDesc = clazz.getDeclaredMethod("showDesc"); showDesc.setAccessible(true); Object returnVal1 = showDesc.invoke(animal); Object returnVal2 = showDesc.invoke(null); System.out.println(returnVal1); System.out.println(returnVal2); }
|
调用运行时类中的指定构造器
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @Test public void testConstructor() throws Exception { Class<Animal> clazz = Animal.class; Constructor<Animal> declaredConstructor = clazz.getDeclaredConstructor(String.class); declaredConstructor.setAccessible(true); Animal dog = declaredConstructor.newInstance("Dog");
System.out.println(dog); }
|
附录:测试中使用到的类
Person.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| public class Person {
private String name; public int age;
public Person(String name, int age) { this.name = name; this.age = age; } private Person(String name) { this.name = name; } public Person() { System.out.println("Person()"); }
public void show() { System.out.println("我是一个人"); } private String showNation(String nation) { System.out.println("我是一个" + nation + "人"); return nation; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
@Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
|
Animal一系列的类
- Creature.java
- MyAnnotation.java
- MyInterface.java
Animal.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| @MyAnnotation public class Animal extends Creature<String> implements Comparable<String>, MyInterface{
private String name; int age; public int id;
public Animal() {}
@MyAnnotation(value = "abc") public Animal(String name) { this.name = name; }
Animal(String name, int age) { this.name = name; this.age = age; }
@MyAnnotation private String show(String breed) { System.out.println("动物品种是" + breed); return breed; }
public String display(String sample) throws NullPointerException, ClassNotFoundException { return sample; }
private static void showDesc() { System.out.println("我是一只可爱的动物"); }
@Override public int compareTo(String o) { return 0; }
@Override public void info() { System.out.println("这是一只动物"); }
@Override public String toString() { return "Animal{" + "name='" + name + '\'' + ", age=" + age + ", id=" + id + '}'; } }
|
Creature.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import java.io.Serializable;
public class Creature<T> implements Serializable { private char gender; public double weight;
private void breath() { System.out.println("生物呼吸"); }
private void eat() { System.out.println("生物吃东西"); } }
|
MyAnnotaion.java
1 2 3 4 5 6 7 8 9 10 11 12
| import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*; import static java.lang.annotation.ElementType.LOCAL_VARIABLE;
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { String value() default "hello"; }
|
MyInterface.java
1 2 3
| public interface MyInterface { void info(); }
|