JavaSE基础视频28_反射机制

来源:互联网 发布:最热网络词条 编辑:程序博客网 时间:2024/06/07 07:15

一、反射机制——概述&应用场景

概念:1、java反射机制是在运行状态中,对于任意一个类(class文件),都能够知道这个类的所有属性和方法;2、对于任意一个对象,都能够调用它的任意方法和属性;3、这种动态获取的信息以及动态调用对象的方法和功能称为java语言的反射机制。4、反射技术出现于JDK1.2版本。5、反射应用一般需要有接口和配置文件。6、反射技术也可以理解为对类的解剖。反射一般用于框架的开发及应用学习框架:1、看这个框架是干什么的;2、看这个框架的配置文件怎么用;3、学框架中常用对象的用法。

二、反射机制——细节&Class 对象

1、什么是字节码文件?例如:当我们在程序中使用到Person这个类的时候,首先要从硬盘上把Person这个类的二进制代码加载到内存中,然后通过这个字节码创建出一个一个的对象。当我们用到Date这个类,Math这个类,那么这两个类也会加载进内存。那么这时,内存中就有了3个字节码。那么,每一个字节码就是 Class 的实例对象。例如:Person p1 = new Person();Person p2 = new Person();Date MathClass cls1 = Date.class //Date.class就是一个字节码Class cls2 = Person.class //Person.class就是一个字节码Class cls3 = Math.class2、 Class 类描述java.lang.Object -java.lang.Class<T> public final class Class<T> extends Object implements Serializable, GenericDeclaration, Type, AnnotatedElement Class 类的实例表示正在运行的 Java 应用程序中的类和接口。该类可以获取.class 字节码文件中的所有内容。3、九个预定义Class实例对象基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也表示为 Class 对象。 例如:Class cls1 = boolean.class Class cls2 = int.class Class cls3 = void.class ......只要是在源程序中出现的类型,都有各自的Class实例对象。

三、反射机制——获取 Class 对象的三种方式以及一些小细节

想要对字节码文件进行解剖,必须要有字节码文件对象。如何获取字节码文件对象呢?字节码文件可以理解为这个 Class 对象。获取 Class 对象的三种方式:1、获取字节码对象的方式一:Object类中的getClass()方法;想要用这种方式,必须要明确具体的类,并创建对象。2、方式二:任何数据类型都具备都具备一个静态的属性 .class ,可以用它来获取其对应的 Class 对象。相对简单,但还是需要明确用到类中的静态成员。3、方式三:只要通过给定的类的字符串名称就可以获取该类,这样更为扩展。可以用 Class类中的方法来完成。该方法就是 Class 类中的静态方法: static Class forName(String className);4、示例:String str = "abc";Class cls1 = str.getClass();//方式一Class cls2 = String.class;//方式二Class cls3 = Class.forName("java.lang.String");//方式三System.out.println(cls1 == cls2);//trueSystem.out.println(cls2 == cls3);//truecls1、cls2、cls3都表示Class的一个实例对象。都是 String 类加载进内存内存后的一份字节码,而且该字节码只有一个。5、 Class 类中有一些方法:boolean isPrimitive():判定指定的 Class 对象是否表示一个基本类型。 例如:System.out.println(String.class.isPrimitive());//结果是falseSystem.out.println(int.class.isPrimitive());//结果是trueSystem.out.println(int.class == Integer.class);//结果是false;因为int是基本数据类型,Integer是类类型System.out.println(int[].class.isPrimitive());//结果是false;因为数组是引用数据类型boolean isArray():判定此 Class 对象是否表示一个数组类。示例:System.out.println(int[].class.isArray());//结果是true6、 Integer 类中有一个字段摘要:static Class<Integer> TYPE :表示基本类型 int 的 Class 实例。 示例: System.out.println(int.class == Integer.TYPE);//结果是true 示例代码:
class ClassDemo {public static void main(String[] args) throws ClassNotFoundException {getClassObject_3();}/*获取字节码对象的方式一:Object类中的getClass()方法;想要用这种方式,必须要明确具体的类,并创建对象。*/public static void getClassObject_1(){Person p = new Person();Class clazz = p.getClass();Person p1 = new Person();Class clazz1 = p1.getClass();System.out.println(clazz==clazz1);//true}/*方式二:任何数据类型都具备都具备一个静态的属性 .class 来获取其对应的 Class 对象。相对简单,但还是需要明确用到类中的静态成员。*/public static void getClassObject_2(){Class clazz = Person.class;Class clazz1 = Person.class;System.out.println(clazz==clazz1);//true}/*方式三:只要通过给定的类的字符串名称就可以获取该类,这样更为扩展。可以用 Class类中的方法来完成。该方法就是forName(String className);*/public static void getClassObject_3() throws ClassNotFoundException {String className = "Person";Class clazz = Class.forName(className);System.out.println(clazz);//class Person }}

四、反射机制——获取 Class 中的构造函数 

1、 Class 类中获取指定字节码文件中构造函数的方法(该方法只能获取 public 修饰的):
Constructor<T> getConstructor(Class<?>... parameterTypes); 获取单个

2、获取所有 public 构造方法:
Constructor<?>[] getConstructors(); 

3、获取指定的构造方法,包括私有的:
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes);

4、获取所有的构造方法,包括私有的:
Constructor<?>[] getDeclaredConstructors();

5、获取一个 Class 对象后,可以调用 Class 对象的 newInstance();方法创建该Class所表示的类的空参数的实例对象。
如果要创建一个非空参数的实例对象,只能先获取到对应的构造方法对象后,才能通过指定的构造方法对象创建对象。
步骤:
(1)获取Class对象;
(2)调用Classd对象的getConstructor方法(以上四个中的一个),并返回 Constructor 对象;
(3)调用Constructor对象的newInstance方法。

6、示例代码:
import java.lang.reflect.*;class ReflectDemo {public static void main(String[] args) throws Exception {createNewObject_2();}public static void createNewObject() throws ClassNotFoundException, IllegalAccessException, InstantiationException {String name = "Person";Class clazz = Class.forName(name);//创建空参数的对象Object obj = clazz.newInstance();}public static void createNewObject_2() throws Exception {String name = "Person";Class clazz = Class.forName(name);//获取指定构造函数对象。Constructor constructor = clazz.getConstructor(String.class, int.class);//通过该构造器的newInstance方法进行对象的初始化。Object obj = constructor.newInstance("小明", 32);}}

五、反射机制——获取 Class 中的字段

Class 类中获取字段(包括私有)的方法:
Field getDeclaredField(String name); 获取指定字段

Field[] getDeclaredFields(); 获取所有字段对象

Field getField(String name); 和  Field[] getFields(); 只能获取 public 修饰的字段。

1、获取字段,并给字段赋值
步骤:
1、获取Class对象;
2、调用Class对象的getField方法(以上四个中的一个),并返回Field对象;
3、创建字节码文件所表示的类对象obj;
4、给指定对象obj字段赋值。

2、暴力反射:
当字节码文件中某些字段被private修饰后,通常不可以被访问;
这时,可以调用该字段对象的setAccessible方法,并设置为true;
这时就可以访问该字段了。

3、setAccessible方法:
void setAccessible(boolean flag) 将此对象的 accessible 标志设置为指示的布尔值。值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。
该方法是 AccessibleObject 类的一个方法。 AccessibleObject 类是 Constructor 、 Field 、 Method 的共同父类。
那么对于私有构造函数和私有函数也可以调用该方法进行暴力反射。

示例代码:
import java.lang.reflect.*;class ReflectDemo2{public static void main(String[] args) throws Exception {getFieldDemo();}public static void getFieldDemo() throws Exception {Class clazz = Class.forName("Person");//获取私有字段Field field = clazz.getDeclaredField("age");//对私有字段的访问取消权限检查(暴力访问)field.setAccessible(true);//创建空参数的对象Object obj = clazz.newInstance();//设置字段的值field.set(obj,89);//获取obj对象中的field字段。Object o = field.get(obj);System.out.println("field: "+field); //private int Person.ageSystem.out.println("obj: "+obj); //Person@6d06d69cSystem.out.println("o: "+o); //89}}

六、反射机制——获取 Class 中的方法 

Class 类中同样有获取字节码文件所表示的类的方法,获取方式与 Constructor 和 Field 相同。
Method getDeclaredMethod(String name, Class<?>... parameterTypes) 

Method[] getDeclaredMethods() 

Method getMethod(String name, Class<?>... parameterTypes) 

Method[] getMethods() 

不同的是,获取到 Method 对象后,调用的是 Method 对象的 invoke()方法去调用字节码文件类的方法。

代码:
import java.lang.reflect.*;class ReflectDemo3 {public static void main(String[] args) throws Exception {getMethodDemo_3();}public static void getMethodDemo() throws Exception {Class clazz = Class.forName("Person");//Method[] methods = clazz.getMethods();//获取所有公有的方法(包括继承的方法)Method[] methods = clazz.getDeclaredMethods();//只获取本类中的所有方法,包括私有方法。不包括继承的方法。for(Method method : methods){System.out.println(method);}}public static void getMethodDemo_2() throws Exception {Class clazz = Class.forName("Person");Method method = clazz.getMethod("show",null);System.out.println(method);//Object obj = clazz.newInstance();Constructor constructor = clazz.getConstructor(String.class, int.class);Object obj = constructor.newInstance("小明",22);method.invoke(obj,null);//调用show()方法}public static void getMethodDemo_3() throws Exception {Class clazz = Class.forName("Person");Method method = clazz.getMethod("paramMethod",String.class,int.class);Object obj = clazz.newInstance();method.invoke(obj,"小强",55);}}

七、反射机制——反射练习

加入反射技术,优化day08电脑主板示例代码:
配置文件信息:
pci1=SoundCardpci2=NetCard
import java.io.File;import java.io.FileInputStream;import java.util.Properties;public class ReflectTest {public static void main(String[] args) throws Exception {MainBoard mb = new MainBoard();mb.run();//mb.usePCI(new SoundCard());//以前是直接new一个PCI传入方法参数。//利用反射技术,首先加载进一个配置文件File configFile = new File("pci.properties");Properties prop = new Properties();FileInputStream fis = new FileInputStream(configFile);prop.load(fis);//扫描这个配置文件,并获取配置文件信息,也就是类名for (int x = 0; x < prop.size(); x++) {String className = prop.getProperty("pci"+(x+1));//创建Class对象Class clazz = Class.forName(className);//创建PCI对象PCI p = (PCI)clazz.newInstance();//调用方法mb.usePCI(p);}fis.close();}}

八、对接收数组参数的成员方法进行反射

示例代码:
import java.lang.reflect.*;class Demo{public static void main(String[] args)throws Exception{String className = args[0];Method mainMethod = Class.forName(className).getMethod("naim",String[].class);mainMethod.invoke(null,new String[]{"111","222","333"});//这样传入参数会出现参数个数异常//mainMethod.invoke(null,new Object[]{new String[]{"111","222","333"}});//正确写法//mainMethod.invoke(null,(Object)new String[]{"111","222","333"});//正确写法二}}class Test{public static void main(String[] args){for (int x=0 ;x<args.length ;x++ ){System.out.println(args);}}}
该代码存在问题点:
1、运行时需要传入一个字符串参数: java Demo Test
2、JDK1.5后,增加了可变参数的新特性,但是还需要兼容JDK1.4版本;当调用一个参数为数组类型的方法时,传入的数组将会自动拆包为一个一个的元素。
该例中,mainMethod.invoke(null,new String[]{"111","222","333"});参数中的String类型的数组会被拆包成一个一个的String类型的元素;
所以运行结果会出现参数个数异常。
应该改为:mainMethod.invoke(null,new Object[]{new String[]{"111","222","333"}});
或者改为:mainMethod.invoke(null,(Object)new String[]{"111","222","333"});

九、数组与Object的关系及其反射类型

如果两个数组的元素类型,以及数组的维数都是相同的,则这两个数值的字节码文件是同一个。
代码示例:
class Demo2{public static void main(String[] args) {int[] a1 = new int[3];int[] a2 = new int[4];int[][] a3 = new int[2][3];String[] a4 = new String[4];System.out.println(a1.getClass() == a2.getClass());//true//System.out.println(a1.getClass() == a3.getClass());//编译失败,因为不能进行比较//System.out.println(a1.getClass() == a4.getClass());//编译失败,因为不能进行比较System.out.println(a1.getClass().getSuperclass());//class java.lang.Object}}

十、数组的反射应用

import java.lang.reflect.*;class Demo3 {public static void main(String[] args) {int[] arr = {2,5,8};printObject(arr);}public static void printObject(Object obj){Class cls = obj.getClass();if(cls.isArray()){int len = Array.getLength(obj);for (int x=0 ;x<len ;x++ ){System.out.println(Array.get(obj,x));}}else{System.out.println(obj);}}}
0 0
原创粉丝点击