1、什么是反射?
➢Reflection (反射)是Java被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。 Class C = Class.forName(“java.lang.String”) ➢加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一 个类只有一-个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到 类的结构。这个对象就像一面镜子, 透过这个镜子看到类的结构,所以,我们形象的称之为:反射
2、反射机制能做些什么?
➢在运行时判断任意一个对象 所属的类
➢在运行时构造任意一个类的对象
➢在运行时判断任意一个类所具有的成员变量和方法
➢在运行时获取泛型信息
➢在运行时调用任意一个对象的成员变量和方法
➢在运行时处理注解
➢生成动态代理
3、如何获取反射对象
3.1、class详解,反射操作的源头,获取class的几种方法
所有反射得来源都是操作class类,通过获取class类对反射进行操作
3.1.1、class类描述
对象照镜子后可以得到的信息:某个类的属性、方法和构造器、某个类到底实现了哪些接口。对于每个类而言,JRE都为其保留一-个不变的Class类型的对象。一个Class对象包含了特定某个结构(class/interface/enum/annotation/primitive type/void/[)的有关信息。
➢Class 本身也是一个类
➢Class 对象只能由系统建立对象
➢加载的类在JVM中只会有一个Class实例
➢一个Class对象对应的是- -个加载到JVM中的一个.class文件
➢每个类的实例都会记得自己是由哪个Class实例所生成
➢通过Class可以完整地得到一个类中的所有被加载的结构
➢Class类 是Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象
3.1.2、class类常用方法
3.1.3、获取class类得常用几种方法
package com.haoker.reflection;
/**
* @ClassName GetClass
* @Description TODO
* @Author 豪
* @Date 2021/8/2 21:38
* @Version I. 0
**/
public class GetClass {
public static void main(String[] args) throws ClassNotFoundException {
//1、通过类名.class获得
Class c1 = User.class;
System.out.println(c1);
//2、通过forName获得
Class c2 = Class.forName("com.haoker.reflection.User");
System.out.println(c2);
//3、通过new对象后getClass方法获得
Class c3 = new User().getClass();
System.out.println(c3);
//获取父类
Class superclass = c1.getSuperclass();
System.out.println(superclass);
}
}
class User{
private String empName;
private String empNo;
public String getEmpName() {
return empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
public String getEmpNo() {
return empNo;
}
public void setEmpNo(String empNo) {
this.empNo = empNo;
}
}
4、反射具体操作
4.1、获取运行时类得详细信息
package com.haoker.reflection;
import com.haoker.annotations.ColCopy;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* @ClassName GetClass
* @Description 反射获取类得名称、属性、方法、构造方法、注解和泛型
* @Author 豪
* @Date 2021/8/2 21:38
* @Version I. 0
**/
public class GetClassInfo {
public static void main(String[] args) throws Exception, IllegalAccessException {
//1、通过forName获得
Class c2 = Class.forName("com.haoker.reflection.User2");
System.out.println(c2);
//2、获取类全路径名称
String name = c2.getName();
System.out.println(name);
//3、获取类名,不包含路径
String simpleName = c2.getSimpleName();
System.out.println(simpleName);
//获取类所有得属性,只获取共有属性
Field[] fields = c2.getFields();
for (Field field : fields) {
System.out.println(field);
}
//获取类所有得属性,包括私有属性
Field[] declaredFields = c2.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
//获取类指定属性
Field empName = c2.getDeclaredField("empName");
System.out.println(empName);
//获取类指定属性,共有
Field empName2 = c2.getField("age");
System.out.println("获取类指定属性,共有" + empName2);
//获取本类和父类得全部方法
Method[] methods = c2.getMethods();
for (Method method : methods) {
System.out.println("获取本类和父类所有方法" +method);
}
//获取本类所有方法
Method[] methods2 = c2.getDeclaredMethods();
for (Method method : methods2) {
System.out.println("获取本类所有方法" + method);
}
//获取类指定方法,方法名和入参
Method methods3 = c2.getMethod("getEmpName",null);
System.out.println(methods3);
Method methods4 = c2.getMethod("setEmpName",String.class);
System.out.println(methods4);
//获取全部得构造器
Constructor[] constructors = c2.getConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
//获取全部得构造器
Constructor[] declaredConstructors = c2.getDeclaredConstructors();
for (Constructor constructor : declaredConstructors) {
System.out.println(constructor);
}
//获取指定名称得构造器
Constructor constructor = c2.getDeclaredConstructor();
System.out.println(constructor);
Constructor constructor2 = c2.getDeclaredConstructor(String.class,String.class,String.class);
System.out.println(constructor2);
//操作注解,看注解得位置再哪里,再属性上,就需要先获取到属性
Field empName3 = c2.getDeclaredField("empName");
if(empName3.isAnnotationPresent(ColCopy.class)){
ColCopy empName1 = empName3.getAnnotation(ColCopy.class);
System.out.println(empName1.name());
}
//操作泛型,泛型一般都是再方法中,所以先要操作方法,再获取泛型对应得实体,因为泛型再程序编译完成后,会编译成具体对应得类
}
}
class User2{
@ColCopy(name ="empName")
private String empName;
private String empNo;
public String age;
public User2() {
}
public User2(String empName, String empNo, String age) {
this.empName = empName;
this.empNo = empNo;
this.age = age;
}
public String getEmpName() {
return empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
public String getEmpNo() {
return empNo;
}
public void setEmpNo(String empNo) {
this.empNo = empNo;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
}
4.2、对方法、属性、构造函数进行操作
package com.haoker.reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* @ClassName GetClass
* @Description 反射获取类得方法,然后对方法进行执行
* 反射获取属性值,然后对属性进行赋值
* @Author 豪
* @Date 2021/8/2 21:38
* @Version I. 0
**/
public class GetClassIovoke {
public static void main(String[] args) throws Exception, IllegalAccessException {
//1、通过forName获得
Class c2 = Class.forName("com.haoker.reflection.User3");
System.out.println(c2);
//2、对方法、属性进行操作得时候,入参是类
User3 o = (User3) c2.newInstance();
//操作构造函数
Constructor declaredConstructor = c2.getDeclaredConstructor(String.class, String.class, String.class);
User3 lin = (User3) declaredConstructor.newInstance("lin", "999", "27");
System.out.println(lin.getEmpName());
//操作方法
Method methods5 = c2.getMethod("setEmpName",String.class);
methods5.invoke(o,"123");
System.out.println(o.getEmpName());
//操作属性得时候
Field empName1 = c2.getDeclaredField("empName");
empName1.setAccessible(true);
empName1.set(o,"333");
System.out.println(o.getEmpName());
}
}
class User3{
private String empName;
private String empNo;
public String age;
public User3() {
}
public User3(String empName, String empNo, String age) {
this.empName = empName;
this.empNo = empNo;
this.age = age;
}
public String getEmpName() {
return empName;
}
public void setEmpName(String empName) {
this.empName = empName;
}
public String getEmpNo() {
return empNo;
}
public void setEmpNo(String empNo) {
this.empNo = empNo;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
}
4.3、对注解进行获取操作
//操作注解,看注解得位置再哪里,再属性上,就需要先获取到属性
Field empName3 = c2.getDeclaredField("empName");
if(empName3.isAnnotationPresent(ColCopy.class)){
ColCopy empName1 = empName3.getAnnotation(ColCopy.class);
System.out.println(empName1.name());
}
4.4、对泛型进行操作
package com.haoker.reflection;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
/**
* @ClassName GetClass
* @Description 反射操作泛型
* @Author 豪
* @Date 2021/8/2 21:38
* @Version I. 0
**/
public class GetClassT {
public void test1(Map<String,User3> maps,List<String> lists){
System.out.println(maps);
}
public static void main(String[] args) throws Exception, IllegalAccessException {
//1、通过forName获得
Class c2 = Class.forName("com.haoker.reflection.GetClassT");
System.out.println(c2);
//获取方法
Method test1 = c2.getMethod("test1", Map.class, List.class);
//获取方法得参数类型
Type[] genericParameterTypes = test1.getGenericParameterTypes();
for (Type genericParameterType : genericParameterTypes) {
//循环继续获取集合里面得真正类型得参数
if(genericParameterType instanceof ParameterizedType){
Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
for (Type parameterType : actualTypeArguments) {
System.out.println(parameterType);
}
}
}
}
}
5、反射性能和普通new方法对比,优缺点
优点:
➢可以实现动态创建对象和编译,体现出很大的灵活性
优点:
➢对性能有影响。使用反射一般比使用普通得new要慢一点
6、内加载内存分析
6.1、类加载的过程,主要第一步类得加载得时候会把class文件生成
6.2、分析类初始化
6.3、类加载器
6.4、双亲委派机制
6.4.1、双亲委派机制原理
白话文得说法:遇到事情得时候,儿子先不处理问题,先向父亲反馈,然后父亲再向爷爷反馈,看爷爷能不能处理,爷爷不能处理,再告诉父亲,看父亲能不能处理,不能再让儿子处理。意思就是,类加载得时候,类加载器先一级一级向父级请求,然后父级再一级一级往下查找由谁加载,就好像金字塔那样子得走向。
6.4.2、如何破环双亲委派机制
双亲委派,就是一级一级往上找,然后再往下找。为了能够保证唯一性而不错乱。只要把唯一性不错乱给破坏了,自然而然把双亲委派破坏了。
1、自定义类加载器 ,重写loadclass方法,这样就会存在多个相同得类,导致双亲委派破坏
7、思考题
7.1、能不能自己写-一个限定名为java.lang.String的类,并在程序中调用它?
分情况,如果只是单纯得再java项目中,新建一个java.lang得包,然后再新建这个string类,然后main函数启动,是不行得,因为根据双亲委派机制,会一级一级往上找,找到得不是我们自己建得类,而找到得类中,是没有main方法得,所以,无法运行程序!但是,如果是再web项目中,可以在WEB-INF/clsssess目录下创建对应得string类,此时程序加载得时候,读取得是我们自己建得string类,但是这样原来得string类得方法都不能用了。