黑马程序员——022——反射

来源:互联网 发布:猎手软件下载 编辑:程序博客网 时间:2024/04/28 01:24
——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-

Class类
这小节开始我们开始了解Java中的反射;玩反射要导入的包是:import java.lang.reflect.Field;
反射是从Java1.2就有的一个特性,我们以后要学习的Struts,Hibernate,JUnit等框架都是用到了反射技术;
我们首先了解第一个类:Class,注意和类定义时候class开头小写区分;
—Java中的类描述这个事物该有什么属性没有什么属性;
—任何的类,都能够得到他的方法,他的接口等:如下图就是Class类的几个方法的截图:
这里写图片描述
—————————————————————————————
示例:
Person p1 = new Person();
Person p2 = new Person();
那么以下变量怎么赋值呢?
Class cls1 =
Class cls2 =
—new Class()吗? 很抱歉 没有;一个字节码表示;
——Class cls1 = 字节码1;
——Class cls2 = 字节码2;
——Class cls3 = Person.class;//第1种方法
——Class cls4 = p1.getClass();//第2种方法
——Class cls4 = Class.forName(“java.lang.String”);//第3种方法,常用这种
———这就涉及到一道面试题了:
————Class.forName的作用:内存中有这个字节码,则直接加载;内存中若没有,则使用类加载器去加载;
加载字节码文件有这三种方式,我们总结一下:
这里写图片描述
常用第三种;
——另外:void空也对应一个class:Class c = void.class;
———因此一共有九个预定义的Class实例对象:分别是8个基本类型还有一个void;
—————————————————————————————
Class的方法isPrimitive();用于验证是不是基本类型,是就会回true,否则false;
这里写图片描述
实例代码:
—————————————————————————————

public class Demo22_1{        /**         * @param args         */        public static void main(String[] args) {                String s1 = "anbc";                Class c1 = null;                try {                        c1 = Class.forName("java.lang.String");//try是为了防止类名乱写                } catch (ClassNotFoundException e) {                        e.printStackTrace();                }                System.out.println("should be true "+(String.class == s1.getClass()));//true                System.out.println("should be true "+(c1 == s1.getClass()));//true                System.out.println("should be false "+(int.class == Integer.class));//false                System.out.println("should be true "+(int.class == Integer.TYPE));//true,TYPE常量代表Integer所包装的类;                System.out.println("should be false "+(int[].class.isPrimitive()));//false,数组也是一种类型,但不是原始类型                System.out.println("should be true "+(int[].class.isArray()));//true,判断类型是否是数组,使用Class的isArray()方法;                //总之,只要是在源程序中出现的类型,都有各自Class实例对象,例如int[],void等;        }}

—————————————————————————————
构造方法的反射应用
反射就是把Java类中的各种成分映射成相应的Java类;
—这句话比许多书上讲解的都透彻,都精辟;
—所以会学习到Field、Method、Contructor、Package类;
——这些类那Method来说,Method这个类本身代表这些方法,而其的一个对象代表某个类的具体方法;
了解Contructor类:
—其一个对象代表这个字节马里面的构造方法;
—Contructor c1 = Class.forName(“..”).getConstructor();
—Contructor**[]* cs = Class.forName(“..”).getConstructor**s*();
—Contructor c2 = Class.forName(“..”).getConstructor(StringBuffer.class);
—Contructor c2 = Class.forName(“..”).getConstructor(StringBuffer.class,int.class);
——//获得了构造方法;运用到可变参数;
——//在没有可变参数以前则使用Class[]数组的方式传进来;
Class的newInstance()方法存在的意义: (查看Java源代码得到(Class.java))
—例子:String obj = (String)Class.forName(“java.lang.String”).newInstance();
—该方法内部先得到默认的构造函数;然后用该构造函数创建实例对象;
—该方法内部的具体是用到了缓存机制来保存默认构造函数的实例对象;
——因为反射比较消耗性能,得到构造方法的过程比较耗时,影响速度(针对计算机的速度),所以应该使用缓存机制来提高速度:
———即如果该无参数对象在内存中存在就不用再重新new了,直接用Class.newInstance()(不带参数的)即可了;
—————————————————————————————
成员变量的反射
实例代码:通过反射得到得到类的成员变量:
—————————————————————————————

import java.lang.reflect.Field; public class Demo22_2{        public static void main(String[] args) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {                // TODO Auto-generated method stub                Person p = new Person("halohoop",16);                System.out.println(p.getName());                Class<? extends Person> class1 = p.getClass();                //取得public的                Field fieldName = class1.getField("name");//得到了类Person字节码的name属性对象;                 //↑如果变量什么都不写修饰符默认是friendly,这里也获取不到↑                //↑要想获取到一定要携程public修饰的↑                System.out.println(fieldName.get(p));//取得p对象的name值;                //取得private的,暴力反射                Field fieldAge = class1.getDeclaredField("age");                //然而这是不够的,这仅仅是知道了字节码有age这个属性,                //还是不能够取得这个字节码对应的任何一个对象实例的age的值                //要取值还必须这么做↓                fieldAge.setAccessible(true);                System.out.println(fieldAge.get(p));//16,现在就能够取到值了                /*                   *  fieldName和fieldAge的值不是一个具体的值;                        fieldName和fieldAge不是对象身上的变量,而是类上的,要用他的get方法取那个对象的;                        可见的;public                        不可见的;private                        一个好的比喻:                        看见了拿不到,知道你身上有钱,拿不到怎么办?抢;                               因此使用 setAccessable(true);//所以也叫暴力反射                 * */        }}class Person{        public String name;        private int age;        public Person(String name, int age)        {            super();            this.name = name;            this.age = age;        }        public String getName()        {            return name;        }}

—————————————————————————————
那么我们接下来写一个成员变量反射的综合案例:
Field类:
练习:
—将任意一个对象中的所有String类型的成员变量所对应的字符串内容中的“b”改成“a”;
实例代码:
—————————————————————————————

import java.lang.reflect.Field;public class Demo22_3{        public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException {                // TODO Auto-generated method stub                ReflectObj rOpj = new ReflectObj();                Class<? extends ReflectObj> class1 = rOpj.getClass();                //得到成员变量数组                Field[] fields = class1.getFields();                System.out.println(rOpj);                for(Field f : fields){                        //采用Field的getType方法判断是否是String类型的                        if(f.getType() == String.class){                                String s = (String)f.get(rOpj);                                //set方法,必须在第一个参数指定一个对象进去,因为非静态成员变量依赖对象                                f.set(rOpj, s.replace("a", "w"));//将a全部替换成w                        }                }                System.out.println(rOpj);        }}class ReflectObj{        //4个String类型的成员变量        public String s1 = "halohoop";        public String s2 = "halohaop";        public String s3 = "halahoop";        public String s4 = "halohoap";        public int age = 12;        @Override        public String toString() {                return "ReflectObj [s1=" + s1 + ", s2=" + s2 + ", s3=" + s3 + ", s4="                                + s4 + "]";        }}

—————————————————————————————
执行结果:
这里写图片描述
提醒:字节码在内存中只有一份,因此要比较字节码相等用“==”等等号而不用equals,虽然equals也可以;
技巧:
—1.如何在开发工具中检验自己敲的覆盖方法名字有没有写对;
——加个”@Override”,如果不报错就对了!
这里写图片描述
—————————————————————————————
成员方法的反射
Method类,描述类中方法的类。导入Java包:import java.lang.reflect.Method;
实例代码:
练习:
—使用反射的方式调用String方法的charAt;
——(体会)方法的involve是Method类的involve方法;
—————————————————————————————

import java.lang.reflect.Method;public class Demo22_4{        public static void main(String[] args) throws Exception {                String s1 = "halohoop";                Class<? extends String> class1 = s1.getClass();                Method method = class1.getMethod("charAt", int.class);                System.out.println(method.invoke(s1, 1));//打印halohoop的第2个字母 a                //相当于执行了s1.charAt(2)方法;        }}

—————————————————————————————
注意:JDK1.4和JDK1.5的invoke方法的区别:
—JDK1.5:public Object invoke(Object obj, Object… args)使用了不定参数
—JDK1.4:public Object invoke(Object obj, Object[] args)使用的是数组
——即按JDK1.4的语法,需要将一个数组作为参数传递给invoke方法时,数组中的每个元素分别对应被调用方法中的一个参数,所以,调用charAt方法的代码也可以用JDK1.4改写为method.invoke(“abcde”,new Object[]{1})的形式;
—————————————————————————————
这里还有2个经验可以分享:
经验:
—1.当我们在Eclipse等开发工具中看到是com..打头的就是非标准的内部使用的,java打头的才是标准的,给我们用的;
这里写图片描述
—2.静态方法involve的第一个参数为null;
—————————————————————————————
实例演示到这里,想必大家都和我有同一个疑问,普通方法调用用的好好的,为什么还要用反射来调用咧?
原因:
—究其最终都是为了实现框架的功能;我在写框架的时候,你这个用户可能还在上小学,还不会写程序呢,那我写的框架程序怎么样才能调用到你以后写的类呢?用一个比喻来说:也就是我写框架好比盖房子,我要怎么保证以后你的生产的门窗能够安装在我的房子上呢?
—因为在写程序时无法知道要被调用的类名,所以,在程序中无法直接new某个类的实例对象了,而要用反射方式来做;
框架与框架要解决的核心问题:
—我做房子卖给用户主,由用户字节安装门窗和空调等;我做的房子就是框架,用户需要使用我们的框架,把门窗插入进我提供的框架中。
—框架与工具类的区别:工具类被用户类调用,而框架是调用用户提供的类;
—————————————————————————————
在对接收数组参数的成员方法进行反射中需要注意的:
看一个实例:反射main方法,我们知道main方法的参数是一个String类型的数组
—————————————————————————————

import java.lang.reflect.Method;public class Demo22_5 {        public static void main(String[] args) throws Exception {                // TODO Auto-generated method stub                //传统方式调用main方法;                Test5Obj.main(new String[]{"111","222","333"});                //反射方式                Class cls1 = Test5Obj.class;                //通过函数名,参数类型,就能够确定这个方法了                //因此这里就传入了两个参数,函数名main,和参数类型的字节码文件String[].class                Method method = cls1.getMethod("main", String[].class);                //我们可以这样执行main方法                method.invoke(null, new Object[]{new String[]{"111","222","333"}});                //或者我们可以这样执行main方法                method.invoke(null, (Object)new String[]{"111","222","333"});                //这两种方式都可以的原因是因为invoke方法会把第二个参数无论如何都会解析                //成为Object或者Object[]来进行拆解开来                //因此将其包装成Object数组供其拆解一次或者直接将其变成Object对象都可;        }}class Test5Obj{        public static void main(String[] args) {                for(String s : args)                        System.out.println(s);        }}

—————————————————————————————
解析:
invoke方法会把第二个参数无论如何都会解析成为Object或者Object[]来进行拆解开来 ;
——因此将其包装成Object数组供其拆解一次或者直接将其变成Object对象都可;
—————————————————————————————
数组与Object的关系及其反射类型
数组的反射:
—数组也是一种类型;
—使用反射出来的数组得到的都是同一个class;
我们调用Class类对象的getName方法就能够得到其类型对应的符号:
这里写图片描述
每一个相同的元素类型相同的维度的数组都有同一个class,字节码文件对象都是同一个(前提是相同的元素类型相同的维度);
—————————————————————————————

public class Demo22_6 {        public static void main(String[] args) {                // TODO Auto-generated method stub                int [] a1 = new int[3];                int [] a2 = new int[4];                int[][] a3 = new int[3][4];                String [] a4 = new String[3];                System.out.println(a1.getClass().getSuperclass().getName());//父类是Obejct,java.lang.Object                System.out.println(a1.getClass().getName());//[I,[表示一维数组,I表示类型是int                System.out.println(a3.getClass().getName());//[[I,[[表示二维数组,I表示类型是int                System.out.println(a4.getClass().getName());//[Ljava.lang.String;,[表示数组,L后接类名                System.out.println(a1.getClass() == a2.getClass());//true                System.out.println(a1.getClass() == a4.getClass());//如果在IDE上会报错                //说明需要相同类型,并且相同维度的数组才会是相等的字节码文件                Object aobj1 = a1;//(1)                Object aobj2 = a4;//(2)                Object[] aobj3 = a1;//(3) ,IDE会报错                Object[] aobj4 = a3;//(4)                Object[] aobj5 = a4;//(5)                //new Object[]{new Object[]{}}                //和以上这种方式一样在Obejct中包含了一个Obejct                //int [] 就是在一个数组[]中包含了一个int                //int [][]就是在一个数组[]中包含了一个int []                //而从上面的第一条打印语句可以知道数组的父类就是Object                //因此(1)就是将[]变成了Object了,这是由于[]父类是Object,(2)也是,其中包含的是int;                //(4)也将[]变成了Object[]了,只是其中包含的是int[];                //而由于int没有父类,是java的基本类型,不能将int变成Object,因此不能够将int[]变成Object[],所以IDE给(3)报错了                //这里面是存在一个向下兼容的问题,由于需要兼容1.4,                //在1.4中,反射是需要把[]变成Object,不定参数是以数组的形式传递的new Obejct[]{..},JDK1.5中才出现的不定参数,                //如果参数是int[],则会因此按照一个参数来解析,将其变成Object[],这是不允许的,因此报错;        }}

—————————————————————————————
数组的反射应用
我们看一个实例:对数据进行反射得到Class之后,如果对于int[] 想要得到其中的length属性,得到第n个值;
—这样用反射怎么做?
—————————————————————————————

import java.lang.reflect.Array;public class Demo22_7{        public static void main(String[] args) {                String[] a1 = new String[]{"a","b","c"};                String a2 = "xyz";                printObj(a1);                printObj(a2);        }        private static void printObj(Object obj) {                Class<? extends Object> class1 = obj.getClass();                //使用Class类的isArray方法判断是否是数组                if(class1.isArray()){                        System.out.println("是数组!打印出每个元素!");                        int length = Array.getLength(obj);                        for(int i=0;i<length;i++){                                System.out.println(Array.get(obj, i));                        }                }else{                        System.out.println("非数组!直接打印该元素!");                        System.out.println(obj);                }        }}

—————————————————————————————
注意:
—1.得到数组中元素的类型是不可以的,这是由于new Object[]{1,”abc”…}这里面可以是很多类型的元素;
——因此只能通过得到一个元素后对某个元素进行操作,如下↓;
———a[0].getClass().getName();
—2.自己开发框架,学这些非常重要的,学完反射再看砖头一样的书像翻小说一样,一下子翻完了!
—————————————————————————————
ArrayList_HashSet的比较及hashcode分析
集合
—Collection collections = new ArrayList();
—Collection collections = new HashSet();
内存泄露:
—程序申请了一块内存用完之后没有释放,没有指针指向它,直到程序运行结束;
只有存储的集合是HashSet哈希集合的时候你的hashCode方法覆盖了才有价值;
——我们可以参考下一小节的资料: ” hashCode方法与HashSet类
我们来看个内存泄露的实例:
—————————————————————————————

import java.util.ArrayList;import java.util.Collection;import java.util.HashSet;public class Demo22_8 {        public static void main(String[] args) {                Collection c1 = new ArrayList();                Collection c2 = new HashSet();                ForTest8Obj obj1 = new ForTest8Obj(1,2);                ForTest8Obj obj2 = new ForTest8Obj(3,4);                ForTest8Obj obj3 = new ForTest8Obj(5,6);                ForTest8Obj obj4 = new ForTest8Obj(1,2);                c1.add(obj1);                c1.add(obj2);                c1.add(obj3);                c1.add(obj4);                c2.add(obj1);                c2.add(obj2);                c2.add(obj3);                c2.add(obj4); //obj4和obj1重复,不在添加                System.out.println(c1.size()); //4                System.out.println(c2.size()); //3                obj3.a = 1;                c1.remove(obj3);                c2.remove(obj3);                System.out.println(c1.size()); //3,变成3了,删除成功了                System.out.println(c2.size()); //3,仍然是3,删除失败了,因为HashCode是根据a和b算出来的,                //而在obj3的a值被修改之前已经将obj3存在了HashSet的某一个区域内了,现在改变a的值则HashCode的值也改变了                //当执行remove方法的时候,又可能会到另一个区域里面找这个obj3,然而找不到,便删除不了了,这就导致了内存泄露的产生!        }}class ForTest8Obj{        public int a;        public int b;        @Override        public int hashCode() {                final int prime = 31;                int result = 1;                result = prime * result + a;                result = prime * result + b;                return result;        }        @Override        public boolean equals(Object obj) {                if (this == obj)                        return true;                if (obj == null)                        return false;                if (getClass() != obj.getClass())                        return false;                ForTest8Obj other = (ForTest8Obj) obj;                if (a != other.a)                        return false;                if (b != other.b)                        return false;                return true;        }        public ForTest8Obj(int a, int b) {                super();                this.a = a;                this.b = b;        }}

—————————————————————————————
框架的概念及用反射技术开发框架的原理
使用类的方式有两种:
—你调用别人的类;
—别人调用你的类;
—实例:
——框架:struts好比一个房子,买来之后,装上自己的门和窗;
——房子调用你的门和窗;
———使用框架项目完成效率高;
实例代码:利用反射的方式创建集合;
—————————————————————————————

import java.io.FileInputStream;import java.io.IOException;import java.io.InputStream;import java.util.Collection;import java.util.Properties;public class Demo22_9 {        public static void main(String[] args) throws Exception {                /*                 * config.properties:文件内容如下 ,存的是类的完整类名                 * classNameArrayList=java.util.ArrayList;                   classNameHashSet=java.util.HashSet;                 * */                InputStream fis =new FileInputStream("config.properties");                Properties props = new Properties();                props.load(fis);                fis.close(); //(1)                // 得到完整类名                String property1 = props.getProperty("classNameArrayList");                String property2 = props.getProperty("classNameHashSet");                //得到两个Class对象                Class cls1 = Class.forName(property1);                Class cls2 = Class.forName(property2);                // 调用newInstance创建两种集合对象                Collection<ForTest9Obj> collections1 = (Collection)cls1.newInstance();                Collection<ForTest9Obj> collections2 = (Collection)cls2.newInstance();                //                ForTest9Obj obj1 = new ForTest9Obj(1,2);                ForTest9Obj obj2 = new ForTest9Obj(3,4);                ForTest9Obj obj3 = new ForTest9Obj(5,6);                ForTest9Obj obj4 = new ForTest9Obj(1,2);                // 给两种集合对象添加元素                collections1.add(obj1);                collections1.add(obj2);                collections1.add(obj3);                collections1.add(obj4);                collections2.add(obj1);                collections2.add(obj2);                collections2.add(obj3);                collections2.add(obj4);                for(ForTest9Obj f : collections1){                        System.out.println(f);                }                System.out.println("========================");                for(ForTest9Obj f : collections2){                        System.out.println(f);                }        }}class ForTest9Obj{        public int a;        public int b;        @Override        public int hashCode() {                final int prime = 31;                int result = 1;                result = prime * result + a;                result = prime * result + b;                return result;        }        @Override        public boolean equals(Object obj) {                if (this == obj)                        return true;                if (obj == null)                        return false;                if (getClass() != obj.getClass())                        return false;                ForTest9Obj other = (ForTest9Obj) obj;                if (a != other.a)                        return false;                if (b != other.b)                        return false;                return true;        }        public ForTest9Obj(int a, int b) {                super();                this.a = a;                this.b = b;        }        @Override        public String toString() {                return "ForTest9Obj [a=" + a + ", b=" + b + "]";        }}

—————————————————————————————
补充:
—对于这句fis.close(); //(1),这里不是吧fis这个对象干掉了,而是把ips所申请的win资源干掉了,fis自己还在,它自己是由java虚拟机垃圾回收来管理的,所以说这里还是有一点小小的内存泄露;
—————————————————————————————
那么以上就是反射中的内容,如果真正理解了反射的应用,框架,那么对于我们看一些更加高级的书籍会非常有帮助的!

0 0
原创粉丝点击