元数据:描述数据的描述数据
反射:得到类的元数据的过程
在运行时期,动态的去获取某一个类中的成员信息(构造器,方法,字段,内部类,接口,父类等)
并把类中的每一种成员都描述成一个新的类
Class:表示一个类
Constructor:表示所有的构造器
Method:表示所有的方法
Fleid:表示所有的字段
一、通过反射来获取某一个类的构造器
1.获取所有构造器
(1)获取构造器所在类的字节码对象:Class clz = T.class;
(2)获取clz对象中所有的构造器
- public Constructor<?>[] getConstructors():该方法只能获取public修饰的构造器
- public Constructor<?>[] getDeclaredConstructors():该方法能获取所有构造器,与访问权限无关
2.获取指定构造器
(1)获取构造器所在类的字节码对象:Class clz = T.class;
(2)获取clz对象中指定的构造器:
- Constructor con = clz.getConstructor();该方法只能获取public修饰的构造器
- Constructor con = clz.getDeclaredConstructor(String.class,int.class);该方法获取构造器与访问权限无关
相关实例:
class Person {
public Person() {
}
public Person(String name) {
}
private Person(String name, int age) {
}
}
public class GetConstructorDemo {
public static void main(String[] args) throws Exception {
getAll();
System.out.println("------------------------------------------------");
getOne();
}
//获取指定的一个构造器
private static void getOne() throws Exception {
//获取构造器所在类的字节码对象
Class<Person> clz = Person.class;
//1.获取public Person()
Constructor<Person> con = clz.getConstructor();
System.out.println(con);
//2.获取public Person(String name)
con = clz.getConstructor(String.class);
System.out.println(con);
//3.获取private Person(String name, int age)
con = clz.getDeclaredConstructor(String.class,int.class);
System.out.println(con);
}
//获取所有构造器
private static void getAll(){
//1.获取构造器所在类的字节码对象
Class<Person> clz = Person.class;
//2.获取clz对象中所有的构造器
//public Constructor<?>[] getConstructors():该方法只能获取public修饰的构造器
Constructor<?>[] cs = clz.getConstructors();//只能获取public修饰的构造器,private修饰的构造器不能访问
System.out.println(cs.length);
for (Constructor<?> constructor : cs) {
System.out.println(constructor);
}
System.out.println("------------------------------------------------");
//public Constructor<?>[] getDeclaredConstructors():该方法能获取所有构造器,与访问权限无关
cs = clz.getDeclaredConstructors();
System.out.println(cs.length);
for (Constructor<?> constructor : cs) {
System.out.println(constructor);
}
}
}
二、使用反射调用构造器
1.创建对象:Class clz = T.class;
2.调用对象:Constructor con = clz.getConstructor(Class<?> parameterType);
3.调用构造器的newInstance方法来创建对象,并传入实参:con.newInstance(Object…args);
4.当对象是私有成员时,用
Constructor con = clz.getDeclaredConstructor(Class<?> parameterType),
再设置当前构造器可以访问con.setAccessible(true),
最后调用newInstance方法来创建对象,并传入实参
相关实例:
class User {
public User() {
System.out.println("无参数构造器");
}
public User(String name) {
System.out.println("构造器" + name);
}
private User(String name, int age) {
System.out.println("私有构造器" + name + age);
}
}
public class CreateObjectDemo {
public static void main(String[] args) throws Exception {
// 传统方式创建对象
new User();
new User("hello");
//new User("hello",12);//private修饰的构造器不能访问
System.out.println("----------------------------------");
//反射机制调用
Class<User> clz = User.class;
//调用public User()
Constructor<User> con = clz.getConstructor();
//调用构造器的newInstance方法来创建对象,并传入实参
con.newInstance();
//public User(String name)
con = clz.getConstructor(String.class);
con.newInstance("hello");
//private User(String name, int age),使用反射可以调用私有的成员
con = clz.getDeclaredConstructor(String.class,int.class);
//设置当前构造器可以访问
con.setAccessible(true);
con.newInstance("hello",17);
}
}
三、使用反射获取方法
1.获取方法所在类的字节码对象
2.获取方法
3.调用方法
Class类中常用的方法
-
public Method[] getMethoss():获取包括自身和继承过来的所有的piblic方法
-
public Method[] getDeclaredMethods():获取自身所有的方法(不包括继承的,和访问权限无关)
-
public Method getMethod(String methodName,Class<?> parameterType):表示调用指定的一个公共的方法(包括继承的)
参数:
methodName:表示被调用的方法的名字
parameterType:表示被调用方法的参数的Class类型,如String.class -
public Method getDeclaredMethod(String methodName,Class<?> parameterType):表示调用指定的一个公共的方法(不包括继承的)
参数:
methodName:表示被调用的方法的名字
parameterType:表示被调用方法的参数的Class类型,如String.class
相关实例
class User {
public void doWork() {
}
public static void doWork(String name) {
}
private String sayHello(String name, int age) {
return name + "," + age;
}
}
public class MethodDemo {
public static void main(String[] args) throws Exception {
getAll();
System.out.println("-------------------------------------------------------");
getOne();
}
//获取单个指定的方法
private static void getOne() throws Exception{
Class clz = User.class;
//获取dowork()
Method m = clz.getMethod("doWork");
System.out.println(m);
//获取doWork(String name)
m = clz.getMethod("doWork", String.class);
System.out.println(m);
//获取private String sayHello(String name, int age)
m = clz.getDeclaredMethod("sayHello", String.class,int.class);
System.out.println(m);
}
//获取所有方法
private static void getAll() {
Class clz = User.class;
//public Method[] getMethoss():获取包括自身和继承过来的所有的piblic方法
Method[] ms = clz.getMethods();
System.out.println(ms.length);//11
for (Method method : ms) {
System.out.println(method);
}
System.out.println("-------------------------------------------------------");
//public Method[] getDeclaredMethods():获取自身所有的方法(不包括继承的,和访问权限无关)
ms = clz.getDeclaredMethods();
System.out.println(ms.length);//3
for (Method method : ms) {
System.out.println(method);
}
}
}
四、使用反射调用方法
1.获取方法所在类的字节码对象
2.获取方法
3.调用方法
-
在Method类中有方法
public Object invoke(Object obj,Object…args):表示调用当前Method所表示的方法
参数
obj:表示被调用的方法底层所属对象
args:表示调用方法时传递的实际参数
返回
底层方法的返回结果 -
调用私有方法:
在调用私有方法之前:应该设置该方法为可访问的
又因为Method是AccessibleObject的子类,所以Method中具有该方法:setAccessible(true); -
调用静态方法
静态方法不属于任何对象,静态方法属于类的本身
此时把invoke方法的第一个参数设置为null即可
相关实例
class Person {
public void doWork() {
System.out.println("Person.doWork()");
}
public static void doWork(String name) {
System.out.println("Person.doWork()"+name);
}
private String sayHello(String name, int age) {
System.out.println("Person.sayHello()"+name+","+age);
return name + "," + age;
}
public static void sayNo(String name){
System.out.println("Person.sayNo()" + name);
}
}
public class MethodInvokeDemo {
public static void main(String[] args) throws Exception, Exception {
//不使用反射
new Person().doWork();
//使用反射
Class clz = Person.class;
//调用public void doWork()
Method m = clz.getMethod("doWork");
//public Object invoke(Object obj,Object...args):表示调用当前Method所表示的方法
Object ret =m.invoke(clz.newInstance());
System.out.println(ret);
//调用public static void doWork(String name)
m = clz.getMethod("doWork", String.class);
ret = m.invoke(clz.newInstance(), "hello");
System.out.println(ret);
//调用private String sayHello(String name, int age)
m = clz.getDeclaredMethod("sayHello", String.class,int.class);
//设置可访问私有的成员
m.setAccessible(true);
//调用方法
ret = m.invoke(clz.newInstance(), "hello",17);
System.out.println(ret);
//使用反射调用Date的toLocalString()
Object obj = new java.util.Date();
Method method = obj.getClass().getMethod("toLocaleString");
Object d = method.invoke(obj);
System.out.println(d);
//使用反射调用静态方法
//静态方法不属于任何对象,静态方法属于类的本身
//此时把invoke方法的第一个参数设置为null即可
m = clz.getMethod("sayNo", String.class);
ret = m.invoke(null, "nono");
System.out.println(ret);
}
}
五、使用反射调用数组参数
调用方法的时候把实际参数统统作为Object数组的元素即可
Method对象.invoke.(方法底层所属对象,new Object[]{所有实参})
相关实例
class Employe {
public static void doWork1(int...arr){
System.out.println("Employe.doWork1()" + Arrays.toString(arr));
}
public static void doWork2(String...arr){
System.out.println("Employe.doWork2()" + Arrays.toString(arr));
}
public static <T> List<T> asList(T... a){
return null;
}
}
public class MethodInvokeDemo2 {
public static void main(String[] args) throws Exception, Exception {
Class clz = Employe.class;
//1.数组的元素类型是基本数据类型
Method method = clz.getMethod("doWork1", int[].class);
method.invoke(null, new int[]{1,2,3,4,5});
method.invoke(null, new Object[]{new int[]{1,2,3,4,5}});
//2.数组的元素类型是引用类型
method = clz.getMethod("doWork2", String[].class);
System.out.println(method);
method.invoke(null, new Object[]{new String[]{"A","B","C"}});
//3.泛型要提升到最高类型
method = clz.getMethod("asList", Object[].class);
System.out.println(method);
}
}
六、操作反射相关的API
1.获取类的修饰符
2.获取类的名称:全限定名称 getName();简易名称 getSimpleName();
3.获取包名:getPackage().getName()
4.获取父类:.getSuperclass()
等等(API文档中)
相关实例
public class OtherAPI {
public static void main(String[] args) {
// 获取类的修饰符
Class clz = OtherAPI.class;
int mod = clz.getModifiers();
String m = Modifier.toString(mod);
System.out.println(m);
// 获取类的名称
String name = OtherAPI.class.getName();
System.out.println(name);//other.OtherAPI
String simpleName = OtherAPI.class.getSimpleName();
System.out.println(simpleName);//OtherAPI
//获取包名
String packageName = OtherAPI.class.getPackage().getName();
System.out.println(packageName);
//获取父类
System.out.println(OtherAPI.class.getSuperclass());
}
}
七、反射中的Array
相关实例
public class ArrayDemo {
public static void main(String[] args) {
Object arr = new int[] {1,3,5,7,9};
//获取arr数组中索引为2的元素
Object val = Array.getInt(arr, 2);
System.out.println(val);
//设置arr数组中索引为2的新元素为100
Array.set(arr, 2, 100);
System.out.println(Array.getInt(arr, 2));
//数组复制
int[] src = new int[] {1,2,3,4,5,6,7,8,9,10};
int[] dest = new int[10];
System.out.println(Arrays.toString(src));
arraycopy(src,3,dest,2,5);
System.out.println(Arrays.toString(dest));
}
public static void arraycopy(Object src, int srcPos, Object dest,
int destPos, int length) {
if (src == null || dest == null) {
throw new NullPointerException("源数组和目标数组都不能为空");
}
if (!src.getClass().isArray() || !dest.getClass().isArray()) {
throw new ArrayStoreException("源和目标必须都是数组");
}
if (src.getClass().getComponentType() != dest.getClass().getComponentType()) {
throw new ArrayStoreException("源和目标元素类型必须相同");
}
if (srcPos < 0 || destPos < 0 || length < 0
|| srcPos + length > Array.getLength(src)
|| destPos + length > Array.getLength(dest)) {
throw new IndexOutOfBoundsException("索引越界");
}
for (int index = srcPos;index <srcPos + length;index ++) {
//获取需要拷贝的元素
Object val = Array.get(src, index);
//给目标数组设置元素
Array.set(dest, destPos, val);
destPos++;
}
}
}