黑马程序员——反射
2015-06-29 21:16
633 查看
——- android培训、java培训、期待与您交流! ———-
对于任意一个对象,都能够调用它任意一个方法和属性。
这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制
简单的讲:反射就是动态获取类中信息。可以理解为对类的解剖
如何得到各个字节码对应的实例对象(class类型)
1,类名.class; 例如,System.class
2,对象.getClass(); 例如,new Date.getClass()
3,Class.forName(“类名”); 例如,Class.forName(“java.util.Date”);
基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也表示为 Class 对象。
总之:只要在源程序中出现的类型,都有各自的Class实例对象,例如:int[], void ….
反射简单的讲,就是把java类中的各种成分映射成相应的java类
什么是反射
java反射机制就是在运行状态中,对于任意一个类(class文件),都能够知道这个类的所有属性和方法对于任意一个对象,都能够调用它任意一个方法和属性。
这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制
简单的讲:反射就是动态获取类中信息。可以理解为对类的解剖
如何得到各个字节码对应的实例对象(class类型)
1,类名.class; 例如,System.class
2,对象.getClass(); 例如,new Date.getClass()
3,Class.forName(“类名”); 例如,Class.forName(“java.util.Date”);
基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也表示为 Class 对象。
总之:只要在源程序中出现的类型,都有各自的Class实例对象,例如:int[], void ….
反射简单的讲,就是把java类中的各种成分映射成相应的java类
使用反射获取一个类中的构造方法
import java.io.*; import java.lang.reflect.Constructor; import java.net.*; public class Demo { public static void main(String[] args) throws Exception { //使用反射来获取String str = new String(new StringBuffer("abc")); Constructor constructor1 = String.class.getConstructor(StringBuffer.class);//此方法是获取String的构造方法,并将该构造方法封装成一个对象Constructor, //StringBuffer.class代表要获取哪个构造方法 String str1 = (String)constructor1.newInstance(new StringBuffer("abc"));//此方法相当于用构造方法创建了一个String类型的对象,参数是StringBuffer("abc"); //由于在编译时,编译器只知道它是一个构造方法对象,并不知道它是什么类型的构造方法对象,所以需要加上强制转换。 //编译器编译时,只看左边,既:只看constructor,只知道它是一个构造方法对象而以,并不知道是什么类型 //String str2 = (String)constructor1.newInstance("abc");//此方法是错误的。因为构造coustructor1是能接收StringBuffer参数的构造方法,不是接收String类型的构造方法。 System.out.println(str1.charAt(2));//c } }
使用反射获取一个类中的变量
import java.io.*; import java.lang.reflect.Field; import java.net.*; class Person{ private int x; public int y; public Person(int x, int y) { super(); this.x = x; this.y = y; } } public class Demo { public static void main(String[] args) throws Exception { Person p = new Person(3,5); Field fieldY = p.getClass().getField("y");//fieldY的值是多少?是5,错!fieldY只是封装了某个基本类型的对象而已,想要知道该基本类型的值,需要用对象中的方法get获取 System.out.println(fieldY.get(p));// 5 获取p这个对象中的y的值 // Field fieldX = p.getClass().getField("x");//此处会报错,因为Person类中的x是私有的,不可获取 // System.out.println(fieldX.get(p)); // Field fieldX =p.getClass().getDeclaredField("x");//能够获取到类中私有变量的方法 // System.out.println(fieldX.get(p));//此处会报错。虽然fieldX获取到了私有的变量,但是fieldX.get(p)不可以获取到 Field fieldX = p.getClass().getDeclaredField("x"); fieldX.setAccessible(true);//设置fieldX中的get方法能够获取得到Person类中的私有变量 System.out.println(fieldX.get(p));//3 } }
使用反射获取一个类中的方法
import java.io.*; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.net.*; public class Demo { public static void main(String[] args) throws Exception { String str1 = "abcdefgh"; Method methodCharAt = String.class.getMethod("charAt", int.class); System.out.println(methodCharAt.invoke(str1, 1));//b 获取str1这个字符串中,角标为1的字符 System.out.println(methodCharAt.invoke(str1, new Object[]{2}));//c 这一种接收参数的形式也可以,这是在jdk1.4版本的时候用法,因为当时没有可变参数 Method methodIndexOf = String.class.getMethod("indexOf", int.class,int.class); System.out.println(methodIndexOf.invoke(str1, 'c', 0));//2 获取str1这个字符串中,字符'c'出现的角标 Method methodValueOf = String.class.getMethod("valueOf",boolean.class); System.out.println(methodValueOf.invoke(null, true));//true 将boolean类型的true,变成字符串"true" //invock第一个参数为null,代表valueOf这个方法是静态的,因为静态方法不需要对象调用,所以参数为null } }
数组反射
/* 数组反射:Array,该类中的方法全是静态的 该类提供了动态创建和访问 Java 数组的方法。 */ import java.lang.reflect.Array; import java.lang.reflect.Field; import java.util.Arrays; public class Demo { public static void main(String[] args) throws Exception { String[] strs = new String[]{"abc","aaa","kkk"}; Object obj = strs; method(obj); method("123"); } public static void method(Object obj) { if(obj.getClass().isArray()){//判读obj是否为一个数组 int len = Array.getLength(obj);//通过反射的Array方法,获取obj对象中数组的长度 for(int i=0;i<len;i++){ //返回指定数组对象中索引组件的值。 String str = (String) Array.get(obj, i); System.out.println(str); } } else{ System.out.println(obj); } } }反射的练习
/* 需求: 用反射的方法,将Person对象中的字符串的'b'字符全部改为'a', 如:"aabbcc"修改后的值为"aaaacc"。 */ import java.io.*; import java.lang.reflect.Field; import java.net.*; class Person { public int a = 2; public int b = 5; public String str1 = "aabbcc"; public String str2 = "abcabbcc"; public String str3 = "qwert"; @Override // 此处的意思代表,如果toString写错了,编译时会出现提示,因为自己并不能保证覆盖父类的方法写对了 public String toString() { return str1 + "..." + str2 + "..." + str3; } } public class Demo { public static void main(String[] args) throws Exception { Person p = new Person(); System.out.println(p);//aabbcc...abcabbcc...qwert Field[] fields = p.getClass().getFields();// 获取对象中所有的公共变量,并存放到Field类中 for (Field field : fields) {//遍历fields对象中的所有public类型 //if(field.getType().equals(String.class)) //字节码都是一个份的,干嘛用equals去比较 if (field.getType() == String.class) {//通过getType方法,获取Person中变量的类型,如果获取到的类型是String类型,那么就执行以下代码 String str1 = (String) field.get(p);//通过Field类中的get方法,获取Person类中的字符串 String str2 = str1.replace('b', 'a');//将Person类中的字符串中的'b'修改为'a' field.set(p, str2);//将修改后的字符串再写入Person类中 } } System.out.println(p);//aaaacc...aacaaacc...qwert } }/* 需求:用反射方式执行某个类中的main方法 */ import java.io.*; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.net.*; public class Demo { public static void main(String[] args) throws Exception { // 普通的调用方式 // String[] strs = new String[]{"111","222","333"}; // Demo2.main(strs); // 反射的调用方式 String[] strs = new String[] { "111", "222", "333" }; Method methodMain = Class.forName("Demo2").getMethod("main", String[].class); //methodMain.invoke(null, strs);//此处报异常,因为传递strs数组进来时,invoke自动将数组拆成了3个参数,那么传递给Demo2类中main方法的 //参数就不是一个数组了,而已3个String类型的参数,所以会出现异常 methodMain.invoke(null, new Object[]{strs});//此方法是正确的,因为invoke将Object数组拆成了1个参数,而此参数却是一个数组,所以, //将此数传递给Demo2类中的main方法是可行的 methodMain.invoke(null, (Object)strs);//此方法正确,通过反射的形式调用了Demo2类中的main方法, //invoke中第一个参数为null的原因是,main方法为静态的,不需要对象去调用。而第二个参数是(Object)strs是因为, //告诉invoke方法,此方法不是一个数组,是一个Object类型的对象,这样invoke方法就不会将strs数组拆成3个String类型 } } class Demo2 { public static void main(String[] args) throws Exception { for (String arg : args) System.out.println(arg); } }/* 练习:将集合的名称以键值的形式存入一个配置文件中, 通过键获取集合的名称,并在该集合中存入数据,并打印集合的长度 */ import java.io.FileInputStream; import java.io.InputStream; import java.lang.reflect.Constructor; import java.util.*; class Person { private int x, y; public Person(int x, int y) { this.x = x; this.y = y; } @Override public int hashCode() { return x + y * 32; } @Override public boolean equals(Object obj) { if (!(obj instanceof Person)) throw new RuntimeException(); Person p = (Person) obj; return x == p.x && y == p.y; } } public class Demo { public static void main(String[] args) throws Exception { InputStream is = new FileInputStream("c:\\properties.txt"); Properties prop = new Properties(); prop.load(is); is.close(); Person p1 = new Person(3,3); Person p2 = new Person(5,5); Person p3 = new Person(3,3); String className = prop.getProperty("className");//获取键为className的值 Constructor con = Class.forName(className).getConstructor();//获取className这个字符串的构造方法 Collection coll = (Collection) con.newInstance();//调用这个字符串的构造方法 coll.add(p1); coll.add(p2); coll.add(p3); coll.add(p1); System.out.println(coll.size());//4 /* 此程序的使用方法: 在c盘中创建一个properties.txt文件。 在文件中写 className=java.util.ArrayList 这样程序的运行结果为4 如果在文件中写 className=java.util.HashSet 那么程序的运行结果为2 */ } }package cn.itcast.reflect; import java.io.*; import java.util.*; /* * 练习:电脑运行基于主板。 * * 如:有一个声卡,声卡想要运行,就必须要通过主板调用。 * 后期又来了个网卡,网卡想要运行,也必须通过主板调用。 */ interface PCI { public abstract void open(); public abstract void close(); } class ZhuBan {//主板 public void run() { System.out.println("ZhuBan...run"); } public void pciInvoke(PCI pci) { pci.open(); pci.close(); } } class WangKa implements PCI {//网卡实现PCI接口 public void open() { System.out.println("WangKa....open"); } public void close() { System.out.println("WangKa....close"); } } class ShengKa implements PCI {//声卡实现PCI接口 public void open() { System.out.println("ShengKa....open"); } public void close() { System.out.println("ShengKa....close"); } } public class Demo { public static void main(String[] args) throws Exception { File file = new File("D:\\peizhi.txt"); if (!file.exists()) { throw new RuntimeException("文件不存在"); } ZhuBan zb = new ZhuBan(); zb.run(); Properties prop = new Properties();//创建一个Properties对象 FileReader fr = new FileReader(file);//创建一个流和peizhi.txt文件相关联 prop.load(fr);//将peizhi.txt文件中的键值存入prop集合中 //循环遍历peizhi.txt文件中的键值 for (int i = 0; i < prop.size(); i++) { //获取peizhi.txt文件中的值,该值是一个对象的名称 String className = prop.getProperty("peizhi" + (i + 1)); //获取peizhi.txt文件中名称的字节码文件对象 Class clazz = Class.forName(className); //创建获取到的名称 的对象。例如:获取到的名称是WangKa。那么相当于PCI pci = new WangKa(); PCI pci = (PCI) clazz.newInstance(); //调用主板中的方法 zb.pciInvoke(pci); } } } /* * 此程序用法:在D盘中创建一个peizhi.txt文件。并在文件中写入一下数据 * peizhi1=cn.itcast.reflect.WangKa * peizhi2=cn.itcast.reflect.ShengKa * * 此程序这么设计的扩展性极强。以后如果想加入一个内存卡,那么就写一个内存卡的类,并实现PCI接口, * 当类写完了以后,再将类名存入peizhi.txt文件中。 当程序运行时,就会自动获取peizhi.txt文件中的信息。 */总结
/* 反射方法的总结。 看完该例子就会找到反射的使用规律 例如: 1,获取某类的构造函数,想都不用想,直接就上,Constructor constructor = 字节码文件对象.getConstructor(); 这样就拿到了构造方法的Constructor对象。相对此构造方法怎么宰割,查API找方法去 2,获取某类的方法,想都不用想,直接就上,Method method = 字节码文件对象.getMethod("方法名",参数类型.class); 这样也拿到了方法的Method。想对此方法怎么宰割,继续查API去 3,如果方法是私有时,Method method = 字节码文件对象.getDeclaredMethod("方法名",参数类型.class); 然后再加上 method.setAccessible(true); 那么此私有的方法也获取到了,爱怎么使用再查API去。 */ import java.io.*; import java.lang.reflect.*; import java.net.*; import java.util.*; class Person { public int x = 3; private int y = 5; public Person() { System.out.println("person()....run"); } public Person(int x, int y) { System.out.println("Person(int x,int y).....run"); } public void method() { System.out.println("method()......run"); } public void method(String str, int num) { System.out.println("method(String str,int num)....run"); } public static void staticMethod() { System.out.println("staticMethod().....run"); } private void privateMethod() { System.out.println("privateMethod.....run"); } } public class Demo { public static void main(String[] args) throws Exception { // 获取无参构造函数 Constructor constructor1 = Class.forName("Person").getConstructor(); Person p = (Person) constructor1.newInstance();// person()....run // 获取有参构造函数 Constructor constructor2 = Class.forName("Person").getConstructor( int.class, int.class); constructor2.newInstance(5, 6);// Person(int x,int y).....run // 获取 公有 的字段 Field field1 = Class.forName("Person").getField("x"); int x = (Integer) field1.get(p); System.out.println(x);// 3 // 获取 私有 的字段 ,getDeclaredField代表能够获取Person类中私有的字段 Field field2 = Class.forName("Person").getDeclaredField("y"); field2.setAccessible(true);// 想要获取就必须加上这一句 int y = (Integer) field2.get(p); System.out.println(y);// 5 // 获取无参方法 Method method1 = p.getClass().getMethod("method", null); method1.invoke(p, null);// method()......run // 获取有参方法 Method method2 = p.getClass().getMethod("method", String.class, int.class); method2.invoke(p, "abc", 2);// method(String str,int num)....run // 获取无参的静态方法 Method method3 = p.getClass().getMethod("staticMethod", null); method3.invoke(p, null);// staticMethod().....run // 获取 私有 的方法 ,getDeclaredMethod代表能够获取Person类中的私有方法 Method method4 = p.getClass().getDeclaredMethod("privateMethod", null); method4.setAccessible(true);// 如果要获取私有的,就必须加上这一句 method4.invoke(p, null);// privateMethod.....run } }
相关文章推荐
- 黑马程序员——Java基础——反射
- 黑马程序员——Java基础——多线程
- 自己收集逻辑面试题及解法-不断补充
- 视觉直观感受 7 种常用的排序算法
- 常见排序算法小结
- 黑马程序员---区分线程和进程
- 黑马程序员---ArrayList、Vector、LinkedList的区别及其优缺点?
- 黑马程序员---Java中常见的排序
- 秒杀多线程第一篇 多线程笔试面试题汇总
- 黑马程序员——OC基础学习(三)---从传统setter方法和getter方法到@property增强型使用(体验代码的优化过程)
- 黑马程序员---java反射机制的实现原理
- 黑马程序员---java多线程的一些常见问题
- 黑马程序员---Java多线程的用法详解
- 黑马程序员---面向对象
- 黑马程序员——Java基础——集合
- 黑马程序员——Java中的异常和包
- 黑马程序员——Java基础---IO(一)---IO流概述、字符流、字节流、流操作规律
- 《C程序员:从校园到职场》出版预告(4):从“散兵游勇”到“正规部队”
- 黑马程序员-java基础-基础小知识
- 【剑指Offer学习】【面试题17 ::合并两个排序的链表】