反射(reflect):通过类的Class对象来获取类的相关信息,动态操作类中的字段、调用类中的方法。
第二种方式最好。
性能更优。第一种、第三种是调用方法,要为方法开辟内存区,为方法中的变量分配内存,方法执行完毕还要回收此方法的内存区,时间、空间开销大。安全性更好。第一种是通过字符串指定类名,编译时不会检查这个类存不存在,如果这个类不存在,执行时会抛出ClassNotFoundException。第二种编译时会检查类存不存在,如果不存在,通不过编译,其实写代码时IDE就会红色报错。最简便
但有一个问题:使用第二种不能动态创建对象,使用第一种可以。
示例:
1 Class<?> Class1 = Class.forName("test.Student"); //必须写成全类名。因为类名是String形式,编译时并不知道Class对象的类型,所以只能写成?,?相当于Object 2 Class<Student> Class2 = (Class<Student>) Class.forName("test.Student"); //强转后可以写成具体类型 3 4 Class<Student> class3=Student.class; //编译时知道Class对象的类型 5 6 Student student=new Student(); 7 Class<?> class4=student.getClass(); 8 Class<Student> class5= (Class<Student>) student.getClass(); //需要强转
类是对对象的抽象,Class类是对所有类的抽象,Class类的实例称为Class对象。
类都有构造器、字段(成员变量+类变量)、方法(成员方法+类方法),Class对象提供了获取构造器、字段、方法的一系列方法。
T表示可以写成具体的类型,后面不用强转。
?相当于Object,后面需要强转。
Student类如下:
1 class Student{ 2 private int id; 3 private String name; 4 private int age; 5 private int score; 6 7 public Student() { 8 } 9 10 public Student(int id, String name, int age, int score) { 11 this.id = id; 12 this.name = name; 13 this.age = age; 14 this.score = score; 15 } 16 17 public int getId() { 18 return id; 19 } 20 21 public void setId(int id) { 22 this.id = id; 23 } 24 25 public String getName() { 26 return name; 27 } 28 29 public void setName(String name) { 30 this.name = name; 31 } 32 33 public int getAge() { 34 return age; 35 } 36 37 public void setAge(int age) { 38 this.age = age; 39 } 40 41 public int getScore() { 42 return score; 43 } 44 45 public void setScore(int score) { 46 this.score = score; 47 } 48 49 @Override 50 public String toString() { 51 return "Student{" + 52 "id=" + id + 53 ", name='" + name + '\'' + 54 ", age=" + age + 55 ", score=" + score + 56 '}'; 57 } 58 }
java.lang.reflect包下有一个Array类,此类有许多静态方法,可以动态创建数组,动态获取、设置数组元素的值。
1 //动态创建数组 2 Object obj = Array.newInstance(String.class, 10); //第一个参数指定数组的元素类型,第二参数指定元素个数 3 String[] arr= (String[]) obj; //动态创建数组的返回值返回值是Object,有时候需要强转 4 5 //动态设置数组元素的值 6 Array.set(obj, 0, "张三"); //第一个参数指定数组,第二个参数指定数组下标,第三个参数指定元素值 7 Array.set(obj,1,"李四"); //虽然obj是Object类型,但要求第一个参数是数组,会自动向下转型 8 9 //动态获取数组元素的值 10 Object first = Array.get(obj, 0); //第一个参数是数组,第二个参数是数组下标 11 Object second=Array.get(obj, 1); 12 System.out.println(first); //会自动向下转型。张三 13 System.out.println(second); //李四 14 15 /* 16 这三个方法均为静态方法,通过Array直接调用。 17 基本数据类型用setXxx()、getXxx(),比如setInt()、getInt(),获取时返回的就是该类型。 18 引用数据类型用set()、get(),获取值时返回值类型是Object。 19 */
原来我们创建对象:Student student=new Student(1,"chy",20,100);
操作字段、调用函数:student.setAge(22);
操作数组:arr[0]=1
把代码写死了。
使用反射:把数据作为参数传入。
比如调用方法:
Method method = studentClass.getMethod("setName",String.class); method.invoke(student,"李四");把方法名、参数类型作为参数传入,把对象、实参作为方法传入。
传入什么方法,就调用什么方法。比如传入"setName",它就调用setName(),传入"getName",就调用getName()。它是动态调用的,传入什么,就调用什么。
不像原来student.setName("张三");把方法写死了,这句代码只会调用setName(),调用不了其他方法。
比如操作数组:
Array.set(arr, 0, "张三");把数组、索引、元素值作为参数传入,传入哪个数组就操作哪个数组,传入哪个索引就操作哪个索引,传入什么值,就使用什么值,根据传入的东西来动态操作。
不像原来arr[0]="张三",都写死了,全是固定的,最多索引、元素值使用变量,但数组仍是固定的。
原来我们创建对象、操作字段、调用方法、操作数组,都是要有这个东西才能使用。
比如IDE下,我们写 new Student(),要有Student这个类才可以,
我们写student.getName() ,要有student这个对象,且这个对象要有getName()这个方法才可以,
我们写 arr[0]="张三",要有arr这个数组才可以。
没有就通不过编译。
反射相当于从代码中映射出一张表,JVM按照这张映射表来进行相关操作。
比如调用方法:
Method method = studentClass.getMethod("setName",String.class); method.invoke(student,"李四"); Method method = studentClass.getMethod("getName"); method.invoke(student); 方法名要操作的对象形参类型实参表setNamestudentString.class"李四"getNamestudent无无不管方法名有没有,不管这个操作可不可行,都可以通过编译。运行时,JVM按照这张从代码中映射出来的表来调用相关方法。当然,这张表是虚构的,实际并不存在。
有泛型检测,不能通过编译。
Class alClass = Class.forName("java.util.ArrayList"); Method add = alClass.getMethod("add", Object.class); //将add()的参数类型设置为Object ArrayList<Integer> arrayList=new ArrayList<>(); add.invoke(arrayList,"hello"); //调用arrayList的add()方法,添加字符串"hello" System.out.println(arrayList); //[hello]。成功加入String,绕过了泛型检测。
JVM中没有泛型的概念,创建Class对象时会扔掉泛型。比如ArrayList<Integer>,创建Class对象时创建的是ArrayList,是不带泛型的。没有ArrayList<Interger>这种数据类型(Class对象),ArrayList才是数据类型。
转载于:https://www.cnblogs.com/chy18883701161/p/11385253.html