一、反射原理的概念
反射:将类中的各个成分映射成不同的对象
反射原理举例:
java.lang.Class:将java中的任何一个类都可以统一使用
反射的深层原理:
如果对于一个类,在编译期间不知道其中的结构,而希望在运行期间去探索、得知内部的结构,则需要使用反射原理
如果要使用反射原理去探索,则需要先加载这个类
获取Class对象的方式
Class.forName("类的全局限定名")
类名.class
调用对象实例中的getClass方法
通过类加载器获取
注意:
方法一不执行静态块和动态构造块
方法二执行静态快、不执行动态构造块
方法三需要创建对象,静态快和动态快都会执行
四种方式得到的Class对象都是同一个对象,因为类只会被加载一次,因此要判断是否为某一类型,用==比较即可
java反射原理中有哪些核心类?分别有什么作用?
通配符和泛型
无限定通配符?
使用一个问号表示类型参数,虽然我们可以在用到的使用再指定具体的类型,但是如果在用到的时候还不能确定具体的类型,就需要依靠通配符来解决。即引入了通配符,使得泛型变成协变的。
上边界限定通配符? extends ……
List<? extends Number> list1 = new ArrayList<Number>();
List<? extends Number> list2 = new ArrayList<Integer>();
List<? extends Number> list3 = new ArrayList<Long>();
下边界限定通配符? super ……
List<? super Integer> list4 = new ArrayList<Integer>();
List<? super Integer> list5 = new ArrayList<Number>();
T、E、V、K等
以List或Map为例,其中的泛型参数定义为<E>或者<K,V>,类中的方法中甚至将E、K、V等作为参数类型使用。此处和?最大的区别在于,这个类型其实是一个确定的类型,调用时指定什么类型就是什么类型,例如List<String>,编译期间会将E自动擦除,替换成String,类中所有出现E的地方,也替换成String。
这一类参数相当于实现了类中一些类型的同统一(包括参数、返回值等)
/***
* extends
*
* super
*/
public class Demo02 {
public static void main(String[] args) {
// List<Integer> list1=new ArrayList<Integer>();
// List<Double> list2=new ArrayList<Double>();
//? extends Number:类型可以是Number类型或其子类型
List<? extends Number> list1 = new ArrayList<Number>();
List<? extends Number> list2 = new ArrayList<Integer>();
List<? extends Number> list3 = new ArrayList<Double>();
//List<? extends Number> list4=new ArrayList<String>();
//? super Integer:Integer类型本身或其父类
List<? super Number> list5 = new ArrayList<Number>();
List<? super Integer> list6 = new ArrayList<Number>();
}
}
import java.util.LinkedList;
import java.util.List;
/**
* T/E/V/K:泛型参数,
*/
public class Demo03 {
public static void main(String[] args) {
MyStack<Integer> myStack = new MyStack<>();
myStack.add(1);
myStack.add(2);
myStack.add(3);
myStack.print();
MyStack<String> myStack2 = new MyStack<>();
myStack2.add("1");
myStack2.add("2");
myStack2.add("3");
myStack2.print();
}
}
/**
* 表示一个栈
*/
class MyStack<E> {
/**
* E:表示的是一个泛型,类中使用的泛型要在类上声明出来
* 泛型参数在运行期间是不存在的,在编译成字节码的时候,会使用具体的类型来擦除泛型参数
* 泛型参数其实是一种固定的数据类型
*/
private List<E> list = new LinkedList<>();
// private List<F> list2 = new LinkedList<>();
public void add(E o) {
//泛型参数不能当做类去使用,也不能直接实例化
//E e=new E();
list.add(o);
}
/**
* 该方法实现的操作:打印出当前集合中的元素值
* <p>
* 如果元素是整数则,每个+5
* 如果是字符串,则前面加上str前缀
*/
public void print() {
for (E ele : list) {
if (ele.getClass() == Integer.class) {
int num=(Integer) ele;
System.out.println(num+5);
}
if (ele.getClass() == String.class) {
String str=(String) ele;
System.out.println("str"+str);
}
}
}
/**
* 如果泛型参数只是某个方法用到,可以不用在类中声明这个泛型参数
* <p>
* 可以直接在方法中声明这个泛型参数
*
* @param param1
*/
public <T, R, S> S method(T param1, R param2) {
return null;
}
}
java.lang.Class:将java中的任何一个类都可以统一使用Class类型来表示,表示的是一个类的完整信息,所有反射操作的开始都是在这里开始的。
import com.sy.reflect.entity.Emp;
/**
* java.lang.Class:当一个类被虚拟机加载以后,会得到一个Class对象
* Class对象是反射中其它操作的起源,通过Class对象可以得到类中的属性、方法、构造器等对象
*/
public class Demo01 {
public static void main(String[] args) {
try {
//Class.forName("类的全局限定名")
//全局限定名=类所在的包+类名
//注意:不能使用具体的类型作为泛型,应该使用?
//?:类型不确定的时候,使用?解决
//通过字符串指定类名,虚拟机不知道最后会得到什么类型
Class<?> clazz1 = Class.forName("com.sy.reflect.entity.Emp");
System.out.println(clazz1);
//类名.class
Class<Emp> clazz2 = Emp.class;
System.out.println(clazz2);
//对象实例.getClass();
//由于在编译期间还没产生对象,虚拟机也不知道会产生什么对象
//所以类型不确定
// Class<?> clazz3 = new Emp().getClass();
// System.out.println(clazz3);
//由于Class对象在第一次加载以后会进行缓存,所以
//重复加载不会得到新的对象
System.out.println(clazz1 == clazz2);
// System.out.println(clazz3 == clazz3);
// System.out.println(clazz1 == clazz3);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
import com.sy.reflect.entity.Emp;
import java.lang.reflect.Modifier;
import java.util.Arrays;
/**
* Class对象的用法
*/
public class Demo04 {
public static void main(String[] args) {
try {
Class<?> clazz = Class.forName("com.sy.reflect.entity.Emp");
//获取访问修饰符
//(1)获取的是访问修饰符对应的整数值
System.out.println(clazz.getModifiers());
//(2)可以将访问修饰符对应的数值转换为字符串
System.out.println(Modifier.toString(clazz.getModifiers()));
//获取类名
//完整类名=包名+类名
System.out.println(clazz.getName());
//类名
System.out.println(clazz.getSimpleName());
//获得了Class对象以后,可以调用Class对象的newInstance方法来创建对象
//权限足够的前提之下,调用的其实是类的无参构造方法
Object emp = clazz.newInstance();
System.out.println(emp);
//判断clazz是否为基本数据类型
System.out.println(Integer.class.isPrimitive());
System.out.println(int.class.isPrimitive());
System.out.println(void.class.isPrimitive());
//判断是否为数组类型
System.out.println(new int[]{1, 2, 3}.getClass().isArray());
//isArray()方法不能用于集合
System.out.println(Arrays.asList(1, 2, 3).getClass().isArray());
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
}
java.lang.reflect.Field:将类中的属性映射成该类型
import com.sy.reflect.entity.Emp;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
/**
* java.lang.reflect.Field:表示类中的属性
* 通过Field对象可以获取属性的信息,也可以对属性的值进行修改
*/
public class Demo06 {
public static void main(String[] args) {
try {
Class<?> clazz = Class.forName("com.sy.reflect.entity.Emp");
//获取所有的属性
//clazz.getFields():只返回公有属性
Field[] fields = clazz.getFields();
for (Field field : fields) {
System.out.println(field);
}
System.out.println("========================");
//clazz.getDeclaredFields():返回所有的属性
fields = clazz.getDeclaredFields();
for (Field field : fields) {
System.out.println(field);
}
System.out.println("========================");
//获取某一个属性
//clazz.getField只能获取公有的属性
Field field = clazz.getField("gender");
System.out.println(field);
//clazz.getDeclaredField可以返回任何一个属性
field = clazz.getDeclaredField("name");
System.out.println(field);
System.out.println("========================");
//Field对象的用法
//获取访问修饰符
System.out.println(Modifier.toString(field.getModifiers()));
//获取属性的数据类型,返回的是Class<?>
System.out.println(field.getType());
//获取属性名
System.out.println(field.getName());
//也可以使用Filed对象对属性进行赋值操作
// Emp emp=new Emp();
// emp.name="";
//emp.属性名=值;
//第一个参数:要被修改属性的对象,第二个参数:要修改进去的属性值
Object obj=clazz.newInstance();
System.out.println(obj);
//打破封装,访问私有属性
field.setAccessible(true);
field.set(obj,"Smith" );
//用完后,还原封装
field.setAccessible(false);
System.out.println(obj);
//Emp emp=new Emp();
//emp.属性名="";
//emp.setxxx();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
}
java.lang.reflect.Construtor:将类中的构造器映射成该类型
import com.sy.reflect.entity.Emp;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
/**
* java.lang.reflect.Constructor:
* 构造器对象,获取构造器对象以后,可以对类进行实例化操作
*/
public class Demo05 {
public static void main(String[] args) {
try {
Class<?> clazz = Class.forName("com.sy.reflect.entity.Emp");
//获取所有的构造器
//getConstructors:获取所有的公有构造方法
Constructor<?>[] constructors = clazz.getConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
System.out.println("=================================");
//getDeclaredConstructors:获取所有的构造方法
constructors = clazz.getDeclaredConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
System.out.println("=================================");
//获取某一个构造器
//getConstructor方法的参数应该要执行构造器的参数列表的类型
Constructor<?> constructor = clazz.getConstructor();
System.out.println(constructor);
constructor = clazz.getConstructor(String.class, int.class, char.class, String.class, double.class);
System.out.println(constructor);
//getConstructor只能获取公有的构造方法
// constructor = clazz.getConstructor(String.class);
// System.out.println(constructor);
//getDeclaredConstructor();获取任何修饰词修饰的构造方法
constructor = clazz.getDeclaredConstructor(String.class);
System.out.println(constructor);
System.out.println("==================");
//获取构造器的访问修饰符
System.out.println(Modifier.toString(constructor.getModifiers()));
//通过构造器进行实例化操作
constructor = clazz.getConstructor(String.class, int.class, char.class, String.class, double.class);
//通过newInstance进行实例化,方法参数就是构造器中要传入的属性的初始值
//调用public构造
Object obj = constructor.newInstance("Tom", 1001, 'F', "Java开发", 6000);
System.out.println(obj);
System.out.println("============================");
//调用非public构造
constructor = clazz.getDeclaredConstructor(String.class);
//打破封装,使得原来权限不够的构造方法可以被调用
constructor.setAccessible(true);
obj = constructor.newInstance("Jack");
//用完以后,再将封装还原
constructor.setAccessible(false);
System.out.println(obj);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
java.lang.reflect.Method:将类中的成员方法映射成该类型
import com.sy.reflect.entity.Emp;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
/**
* java.lang.reflect.Method:用来表示成员方法
* 可以用来获取方法信息,通过Method对象可以调用类中的方法
*/
public class Demo07 {
public static void main(String[] args) {
try {
Class<?> clazz = Class.forName("com.sy.reflect.entity.Emp");
//获取类中的所有方法
//获取当前类的所有公有方法 clazz.getMethods();
Method[] methods = clazz.getMethods();
for (Method method : methods) {
System.out.println(method);
}
System.out.println("=========================");
//获取当前类中无论何种修饰符的方法
methods = clazz.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method);
}
System.out.println("=========================");
// getMethod(本类及父类中public的)
//一个类中同名方法可能有多个(重载)
//所以在指定方法名的同时要指定方法的参数列表
Method method = clazz.getMethod("method01", int.class, String.class);
System.out.println(method);
// getDeclaredMethod(本类中无论何种访问修饰符)
method = clazz.getDeclaredMethod("method01", Integer.class, String.class);
System.out.println(method);
System.out.println("=========================");
//获取的方法的访问修饰符
System.out.println(Modifier.toString(method.getModifiers()));
//获取方法的返回类型
Class<?> returnType = method.getReturnType();
System.out.println(returnType.getName());
System.out.println(returnType.getSimpleName());
//获取方法名
System.out.println(method.getName());
//获取方法的参数类型
Class<?>[] parameterTypes = method.getParameterTypes();
for (Class<?> parameterType : parameterTypes) {
System.out.println(parameterType);
}
//通过Method对象调用方法
// Emp emp=new Emp();
// emp.method01(, );
//emp.方法名(参数1,参数2,参数3,……,参数N);
Object obj = clazz.newInstance();
//invoke返回的结果就是方法的返回值
//打破封装后,调用私有方法
method.setAccessible(true);
Object result = method.invoke(obj, 3, "Hello");
//封装还原
method.setAccessible(false);
System.out.println(result);
System.out.println("==========================");
//注意:void返回方法的invoke返回的值
method = clazz.getDeclaredMethod("method03", int.class);
System.out.println(method);
result = method.invoke(obj, 1);
//空参方法的invoke返回为null
System.out.println(result);
//需要区分是带返回类型的方法返回null还是方法本身就是void方法
if (method.getReturnType() == void.class) {
System.out.println("方法返回类型为void");
} else {
System.out.println("方法有指定返回类型");
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
类加载器的缓存机制:
类装载器还使用了Cache(缓存)机制,如果缓存中有这个Class则直接返回它,如果没有,则从文件中读取并转换成Class对象,同时再将它Cache起来。这样做的目的是保证Class对象只被包装一次。这也就是为什么在修改了java代码之后,需要重新启动才会生效的原因。
结合同步代码块中的类名.class全局锁 记忆。