02-反射
反射
一、反射的机制概念
(1)图示

(2)优点和缺点
- 1、优点:
- 可以实现动态创建对象和编译,体现出很大的灵活性
 
- 2、缺点:
- 对性能有影响,使用反射基本上是一种解释操作,我们告诉JVM我们希望干什么,然后它满足我们的要求。这种操作慢于直接执行相同的操作
 
(3)反射的主要API

(4)反射获取类的Class对象
- 
1、创建一个实体类 //创建实体类:pojo,entity class User{...}
- 
2、通过反射获取类的Class对象 public class Test1 { public static void main(String[] args) throws ClassNotFoundException { //通过反射获取类的Class对象 //Class.forName("类的路径") Class c1=Class.forName("Reflection.User"); System.out.println(c1); } }
- 
3、一个类只有一个Class对象 public class Test1 { public static void main(String[] args) throws ClassNotFoundException { Class c2=Class.forName("Reflection.User"); Class c3=Class.forName("Reflection.User"); //一个类在内存中只有一个Class对象 //一个类被加载后,类的整个结构都会被封装在class对象中 //因此c2和c3的哈希值一样 System.out.println(c2.hashCode()); System.out.println(c3.hashCode()); } }
二、Class类
(1)Class类概述
- 1、Class本身也是一个类
- 2、Class对象只能由系统建立对象
- 3、一个加载的类在JVM中只会有一个Class实例【无论创建多少个,他们的哈希值都是一样的】
- 4、针对任何你想动态加载、运行的类,唯有先获得相应的Class对象才行
(2)Class类的常用方法

(3)获取Class类的实例
- 
1、创建目标类的一个对象 public class Test2 { public static void main(String[] args) { //创建一个Student对象 Person person=new Student(); System.out.println("24岁,是"+person.name); } } class Person{...} class Student extends Person{...}
- 
2、通过对象获得 Class c1=person.getClass(); System.out.println(c1.hashCode());
- 
3、通过forName获得 Class c2=Class.forName("Reflection.Student");
- 
4、通过类名获得 Class c3=Student.class;
- 
5、基本内置类型的包装类都有一个Type属性 Class c4=Integer.class; System.out.println(c4); /** * 输出结果 * int */
- 
6、获得父类类型 Class c5=c1.getSuperclass(); System.out.println(c5);
(4)哪些类型可以有Class对象
- 1、图示

- 
2、示例代码 public class Test3 { public static void main(String[] args) { Class c1=Object.class;//类 Class c2= Comparable.class;//接口 Class c3=String[].class;//一维数组 Class c4=int[][].class;//二维数组 Class c5=Override.class;//注解 Class c6=Element.class;//枚举 Class c7=Integer.class;//基本数据类型 Class c8=void.class;//void Class c9=Class.class;//class } }
(5)类的加载
- 1、加载到内存,会产生一个类对应的Class对象
- 2、链接,链接结束后,为static修饰的变量分配内存
- 3、初始化:< clinit>(){.......},里面放着所有类变量的赋值动作以及静态代码块内的语句
(6)类什么时候初始化
- 
1、主动引用(会发生类的初始化) - ①图示
 

- 
②new一个对象 package Reflection; public class Test4 { static { System.out.println("main函数被加载"); } public static void main(String[] args) { //1、new一个类的对象 Son son=new Son(); } } class Father{ static int b=2; static { System.out.println("父类被加载"); } } class Son extends Father{ static { System.out.println("子类被加载"); m=300; } static int m=100; static final int M=1; } /** * main函数被加载 * 父类被加载 * 子类被加载 */
- 
③反射 //反射也会产生主动引用 Class.forName("Reflection.Son");
- 
2、被动引用(不会发生类的初始化) - ①图示
 

- 
②调用父类的静态变量 //子类调用父类的静态变量,子类不会被加载 System.out.println(Son.b); /** * main函数被加载 * 父类被加载 * 2 */
- 
③定义该类的数组 //子类和父类都不会加载 Son[] array=new Son[5]; /** * main函数被加载 */
- 
④引用常量 System.out.println(Son.M); /** * main函数被加载 */
(7)类加载器(加载阶段)
- 
(1)作用:类加载器作用是用来把类(class)装载进内存的。 
- 
(2)类加载器分类 - 1、引导类加载器:JVM自带的,负责Java平台核心库(该加载器无法直接获取)
- 2、扩展类加载器:负责jre/lib/ext目录下的jar包装入工作库
- 3、系统类加载器:负责java -classpath目录下的类与jar包装入工作,最常用的加载器
 
- 
(3)类加载器关系图(系统类->扩展类->引导类) 

- 
(4)示例代码 public class Test5 { public static void main(String[] args) throws ClassNotFoundException { //获取系统类加载器 ClassLoader c1=ClassLoader.getSystemClassLoader(); //获取系统类加载器的父类加载器-->扩展类加载器 ClassLoader c2=c1.getParent(); //获取扩展类加载器的父类加载器-->根加载器(因为无法获取,所以为null) ClassLoader c3=c2.getParent(); //测试当前类是哪个加载器加载的(系统类加载器) ClassLoader c4=Class.forName("Reflection.Test5").getClassLoader(); //测试JDK内置类是谁加载的(根加载器) ClassLoader c5=Class.forName("java.lang.Object").getClassLoader(); } }
三、创建运行时类的对象
### (1)获取运行时类的完整结构
- 
0、创建User类的Class对象 Class c1=Class.forName("Reflection.User");
- 
1、getName和getSimpleName【获得类名】 //获得类的名字 System.out.println(c1.getName());//获得包名+类名 System.out.println(c1.getSimpleName());//获得类名
- 
2、getFields和getDeclaredFields【获得属性】 //获得类的属性 Field[] fields=c1.getFields();//只能找到public的属性 fields=c1.getDeclaredFields();//找到全部的属性 for (Field field : fields) { System.out.println(field); }
- 
3、getDeclaredField【获得指定属性】 //获取指定属性的值 Field name=c1.getDeclaredField("name"); System.out.println(name);
- 
4、getMethods和getDeclaredMethods【获得方法】 //获取类的方法 Method[] methods=c1.getMethods();//获得本类及其父类的public方法 for (Method method : methods) { System.out.println(method); } methods=c1.getDeclaredMethods();//获得本类的所有方法 for (Method method : methods) { System.out.println(method); }
- 
5、getDeclaredMethod【获得指定方法】 //获得指定方法 Method getName=c1.getDeclaredMethod("getName",null); Method setName=c1.getDeclaredMethod("setName", String.class); System.out.println(getName); System.out.println(setName);
- 
6、getConstructors和getDeclaredConstructors【获得构造器】 //获得类的构造器 Constructor[] constructors=c1.getConstructors();//获得public的构造器 System.out.println(constructors); constructors=c1.getDeclaredConstructors();//获得全部的构造器 System.out.println(constructors);
- 
7、getDeclaredConstructor【获得指定构造器】 //获得指定的构造器 Constructor declaredConstructor=c1.getDeclaredConstructor(String.class,int.class,int.class); System.out.println(declaredConstructor);
四、通过反射实例化对象
(1)通过newInstance()方法创建对象
- 
1、该类的条件 - ①类必须有一个无参的构造器(否则会报错)
- ②类的构造器的访问权限需要足够
 
- 
2、示例代码 public class Test7 { public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException { //获得Class对象 Class c1=Class.forName("Reflection.User"); //构造一个对象 User user=(User) c1.newInstance();//本质是调用了无参构造 System.out.println(user); } }
(2)通过构造器创建对象
- 
1、示例代码 public class Test7 { public static void main(String[] args) throws InstantiationException, IllegalAccessException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException { //获得Class对象 Class c1=Class.forName("Reflection.User"); //通过构造器创建对象 Constructor constructor=c1.getDeclaredConstructor(String.class,int.class,int.class); User user=(User) constructor.newInstance("我修院",114514,24); System.out.println(user); } }
(3)扩展一:通过反射调用普通方法
- 
1、示例代码 //如何通过反射调用普通方法 //1.构造一个实例化对象user User user=(User) c1.newInstance(); //2.通过反射获取目标方法 Method setName=c1.getDeclaredMethod("setName", String.class); //3.通过【目标方法.invoke(实例化对象,"方法的值")】来激活目标方法 setName.invoke(user,"我修院"); Method getName=c1.getDeclaredMethod("getName",null); System.out.println(getName.invoke(user,null));
(4)扩展二:通过反射操作属性
- 
1、示例代码 //通过反射操作属性 User user2=(User)c1.newInstance(); Field name=c1.getDeclaredField("name"); name.setAccessible(true);//name为私有属性,用setAccessible(true)关闭程序的安全检测 name.set(user2,"淳平"); System.out.println(user2.getName());
(5)setAccessible()方法
- 1、不能直接操作私有属性和私有方法,我们需要通过setAccessible(true)来关闭程序的安全检测
- 2、setAccessible()为true时可以提高反射的效率,也可以使私有成员可以访问
五、反射操作泛型【了解】
https://blog.csdn.net/qq_45017999/article/details/106047261
六、反射操作注解【重点】
(1)编写注解阶段
- 
1、类名的注解 //类名的注解 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @interface tableDJ{ String value(); }
- 
2、属性的注解 //属性的注解 @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @interface fieldDJ{ String columnName();//表名 String type();//类型 int length();//长度 }
(2)目标类编写
- 
1、原始状态 class Student2{ private int id; private int age; private String name; public Student2() { } public Student2(int id, int age, String name) { this.id = id; this.age = age; this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Worker{" + "id=" + id + ", age=" + age + ", name='" + name + '\'' + '}'; } }
- 
2、加入类注解 @tableDJ("db_student") class Student2{...}
- 
3、加入属性注解 @fieldDJ(columnName ="db_id",type = "int",length = 10) private int id; @fieldDJ(columnName ="db_age",type = "int",length = 10) private int age; @fieldDJ(columnName ="db_name",type = "String",length = 3) private String name;
(3)通过反射操作注解
- 
1、获取注解 public class Test10 { public static void main(String[] args) throws Exception { //创建Class对象 Class c1=Class.forName("Reflection.Student2"); //通过反射获取注解 Annotation[] annotations=c1.getAnnotations(); for (Annotation annotation : annotations) { System.out.println(annotation); } } }
- 
2、获得注解的value的值 //获得注解的value的值 //1.获取指定的注解 tableDJ a1=(tableDJ)c1.getAnnotation(tableDJ.class); //2.用value()获得该注解的value值 String v1=a1.value(); System.out.println(v1);
- 
3、获得指定属性的注解 //获得类指定属性的注解 //1.获得该属性 Field f=c1.getDeclaredField("id"); //获得该属性的注解 fieldDJ annotation=f.getAnnotation(fieldDJ.class); //逐个打印注解值 System.out.println(annotation.columnName()); System.out.println(annotation.length()); System.out.println(annotation.type());
 
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号