一、反射的基石->Class类

  定义一个类使用 class

  有一个类叫Class

  Java程序中的各个Java类属于同一类事务,描述这类事物的Java类名就是Class。

  Person类代表人,它的实例对象就是张三,李四这样一个个具体的人,Class类代表Java类,

  它的各个实例对象又分别对应什么呢?

    ->对应各个类在内存中的字节码,例如,Person的字节码,ArrayList类的字节码...

    ->一个类被类加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,不同的类的字节码是不同的

      所以他们在内存中的内容是不同的,这一个个的空间可分别用一个个的对象类表示,这些对象显然具有相同的类型

1.类的字节码

  Class cls1 = 字节码1;

  Class cls2 = 字节码2;

  Class的实例对象代表内存里的一份字节码;

  什么叫字节码?

    Person p1 = new Person();

    当我们在源程序中用到了Person这个类的时候,会把编译好的二进制代码(.class)加载到内存中,接着才能用它创建一个个的对象。

    加入用到了 Person Date Math 三个类,就会在内存中存在3份字节码,即每一份字节码都是一个Class类的实例对象。

    Class cls1 = Date.class

    Class cls2 = Person.class

获取字节码的方式有3种:

  ① 类名.class 例如:  Date.class;  //使用Date.class获取类的字节码(类.class)

  ② 对象.getClass() 例如:Person p1 = new Person();

               p1.getClass();  //获取类的字节码

  ③ Class.forName("类名"),例如:Class.forName("java.lang.String") //获取类的字节码

  面试题:Class.forName()的作用

  使用Class.forName() 返回类的字节码,获取时有两种情况:

    ①.类的字节码已经被加载过,在java虚拟机中,不需要加载可以直接返回

    ②.JVM中还没有字节码,用类加载器加载,把加载进来的字节码缓存到JVM中,以后要得到字节码就不用再加载了

每一个字节码都是一个Class的实例对象

2.有九个预定义的Class实例对象

   8个基本类型(byte, short, boolean ,char, int, long, float,double)

 和void

3.数组类型的Class实例对象

  Class.isArray()

  总之,只要是在源程序中出现的类型,都有各自的Class实例对象,例如,int[] ,void...

  1. public static void main(String[] args) throws Exception{
  2. String str1 = "abc";
  3. Class cls1 = str1.getClass();
  4. Class cls2 = String.class;
  5. Class cls3 = Class.forName("java.lang.String");
  6. //3种方式都是同一份字节码
  7. System.out.println(cls1 == cls2);
  8. System.out.println(cls1 == cls3);
  9.  
  10. //isPrimitive 是否是基本类型
  11. System.out.println(cls1.isPrimitive()); //flase
  12. System.out.println(int.class.isPrimitive()); //true
  13. System.out.println(int.class == Integer.class); //false
  14. //Integer.TYPE 代表包装类的基本类型的字节码
  15. System.out.println(int.class == Integer.TYPE); //true
  16. System.out.println(int[].class.isPrimitive()); // flase
  17. System.out.println(int[].class.isArray()); //true
  18. }

二、反射

  反射就是把Java类中的各种成分映射成相应的java类(Field,Method,Contructor,Package...)。

  Method 的对象就是一个具体的方法

  我们通过写程序可以得到这个类里面的各个成分所对应的类的对象

1.构造方法的反射应用

一个Class代表一个字节码,一个Method代表这个字节码里的一个方法,一个Contructor就代表一份自己码里的构造方法

得到某个类所有的构造方法:

  例子:Constructor[] constructors = Class.forName("java.lang.String").getConstructors();

获取指定构造方法,并调用构造方法:

  1. //new String(new StringBuffer("abc"))
  2. Constructor constructor = String.class.getConstructor(StringBuffer.class);
  3. String str2 = (String)constructor.newInstance(new StringBuffer("abc"));
  4. System.out.println(str2.charAt(2));

如果改成:

  1. //new String(new StringBuffer("abc"))
  2. Constructor constructor = String.class.getConstructor(StringBuffer.class);//获取构造方法时需要类型
  3. String str2 = (String)constructor.newInstance("abc"); //调用构造方法时需要传递同样类型的对象
  4. System.out.println(str2.charAt(2));

会报如下错误:

  1. Exception in thread "main" java.lang.IllegalArgumentException: argument type mismatch
  2. at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
  3. at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
  4. at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
  5. at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
  6. at com.java.enhance.ReflectTest.main(ReflectTest.java:23)

表示类型不匹配,获取构造方法时传的是StringBuffer.class

Class.newInstance()方法:

  例子:String obj = (String)Class.forName("java.lang.String").newInstacne();

  该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象.

  该方法内部的具体代码是怎样写的呢? 用到了缓存机制来保存默认的构造方法的实例对象。

2.成员变量的反射

ReflectPoint.java

  1. public class ReflectPoint {
  2.  
  3. private int x;
  4. public int y;
  5.  
  6. public ReflectPoint(int x, int y) {
  7. super();
  8. this.x = x;
  9. this.y = y;
  10. }
  11.  
  12. }

ReflectTest.java

  1. ReflectPoint pt1 = new ReflectPoint(3, 5);
  2. Field fieldY = pt1.getClass().getField("y");
  3. //fieldY不是对象身上的变量,而是类上,要用它去取某个对象对应的值
  4. System.out.println(fieldY.get(pt1));
  5.  
  6. //由于x是private修饰,会报错 java.lang.NoSuchFieldException: x,
  7. // Field fieldX = pt1.getClass().getField("x");
  8. // System.out.println(fieldX.get(pt1));
  9.  
  10. //获取私有变量 暴力反射
  11. Field fieldX = pt1.getClass().getDeclaredField("x");
  12. fieldX.setAccessible(true);
  13. System.out.println(fieldX.get(pt1));

3.成员变量反射的综合案例

  1. public class ReflectPoint {
  2.  
  3. private int x;
  4. public int y;
  5. public String z = "basketball";
  6. public String z1 = "abc";
  7.  
  8. public ReflectPoint(int x, int y) {
  9. super();
  10. this.x = x;
  11. this.y = y;
  12. }
  13.  
  14. /*
  15. * (non-Javadoc)
  16. *
  17. * @see java.lang.Object#toString()
  18. */
  19. @Override
  20. public String toString() {
  21. return "ReflectPoint [x=" + x + ", y=" + y + ", z=" + z + ", z1=" + z1
  22. + "]";
  23. }
  24.  
  25. }
  1. 将对象中的String类型的成员变量所有的字符b换为字符a
  1. public static void main(String[] args){
         exchangeCharValue(pt1);
  2. System.out.println(pt1);
  3. }
  4.  
  5. //将对象中的String类型的成员变量所有的字符b换为字符a
  6. private static void exchangeCharValue(Object obj) throws Exception {
  7. Field[] fields = obj.getClass().getFields();
  8. for(Field field : fields){
  9. // if(field.getType().equals(String.class)){
  10. if(field.getType() == String.class){ //这里用 == 比equals更准确,字节码只有一份自己和自己比较
  11. String oldValue = (String)field.get(obj);
  12. String newValue = oldValue.replace('b', 'a');
  13. field.set(obj, newValue);
  14. }
  15. }
  16. }

4.成员方法的反射

  1. String str1 = "abc";
    Method chartAtMethod = Class.forName("java.lang.String").getMethod("charAt", int.class);
  2. //通常方式
  3. System.out.println(str1.charAt(1));//b
  4. //反射方式
  5. System.out.println(chartAtMethod.invoke(str1, 1));
    System.out.println(methodCharAt.invoke(str1, new Object[]{2})); //c

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是没有可变参数

如果传递给Method对象的invoke()方法的第一个参数为null,这有着什么意义呢?

说明该Method对象对应的是一个静态方法!

5.对接受数组参数的成员方法进行反射

  目标:写一个程序,这个程序能够根据用户提供的类名,去执行该类中的main方法。

     用普通法师调用完后,要明白为什么要用反射方式去调用?

  1. public static void main(String[] args) throws Exception{
  2. String startingClassName = args[0];
  3. Method mainMethod = Class.forName(startingClassName).getMethod("main", String[].class);
  4. mainMethod.invoke(null, (Object)new String[]{"111","222","333"});
         mainMethod.invoke(null, new Object[]{new String[] { "111", "222", "333" }});
  5. }

 添加参数

 问题:  

  启动Java程序的main方法的参数是一个字符串数组,及public static void main(String[] args),

  通过反射方式来调用这个main方法时,如何为invoke方法传递参数呢?按jdk1.5的语法,整个数组是一个参数,

  而按jdk1.4的语法,数组中的每个元素对应一个参数,当把一个字符串数组作为参数传递给invoke方法时,javac会

  到底按照那种语法进行处理?jdk1.5肯定要兼容jdk1.4的语法,会按jdk1.4的语法进行处理,即把数组打散成为若干个单独的参数。

由于

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中传了一个数组作为实参,对呗拆包成为多个参数。

所以,在给main方法传递参数时,不能使用代码 mainMethod.invoke(null,new String[]{"xxx}),javac

  只会把他当做jdk1.4的语法进行理解,而不是把它当做jdk1.5的语法解释,因此会出现参数类型不对的问题。

解决办法:

  mainMethod.invoke(null,new Object[]{new String[]{"xxx"}});

  mainMethod.invoke(null,(Object)new String[]{"xxx"});

  编译器会做特殊处理,编译时不把参数当做数组看待,也就不会把数组打散成若干个参数了。

6.数组的反射

  1. int[] a1 = new int[]{1,2,3};
  2. int[] a2 = new int[4];
  3. int[][] a3 = new int[2][3];
  4. String [] a4 = new String[]{"a","b","c"};
  5. System.out.println(a1.getClass() == a2.getClass()); //true
  6. System.out.println(a1.getClass().getName()); //[I
  7. System.out.println(a1.getClass().getSuperclass().getName());//java.lang.Object
  8. System.out.println(a4.getClass().getSuperclass().getName());//java.lang.Object
  9.  
  10. System.out.println(Arrays.asList(a1));//[[I@206c2ea3]
  11. System.out.println(Arrays.asList(a4)); //[a, b, c]
  12.  
  13. Object aObj1 = a1;
  14. Object aObj2 = a4;
  15. Object[] aObj4 = a3;
  16. Object[] aObj5 = a4;

  具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象。

  代表数组的Class实例对象的getSuperClass()方法返回的父类为Object类对应的Class

  基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用,非基本类型的一维数组,即可以当作Object类型使用,又可以当作Object[]类型使用。

  Arrays.asList()方法处理int[] 和String[]时的差异

    Arrays.asList(new String[]{"a","b","c"})  => [a,b,c]

    Arrays.asList(new int[]{1,2,3})  =>[I@xxxxxxx(哈希Code码)

    处理String[] 时使用的是jdk1.4的语法(当作Object[],1.4没有可变参数的概念,使用的是Object[]当作参数),处理int[] 时用的jdk1.5的语法(当作Object一个参数来处理,有可变参数的概念,使用的是Object...args)

7.数组反射的应用

  1. private void printObject(Object obj){
  2. Class clazz = obj.getClass();
  3. if(class.isArray){
  4. int len = Array.getLength(obj); //反射的方式获取长度
  5. for(int i = 0; i<len;i++){
  6. System.out.println(Array.get(obj,i); //反射的方式获取数组元素
  7. }
  8. }else{
  9. System.out.println(obj);
  10. }
  11. }

思考:

  怎么得到数组中的元素类型?

    没有办法得到数组中元素类型。

    int[] a = new int[3];

    Object[] a = new Object[]{"a",1}

    只能得到某一个元素的类型 a[0].getClass().getName();

8. ArrayList_HashSet的比较及Hashcode分析

  hashCode()方法的作用

  

   

  1. public static void main(String[] args) {
  2. Collection collection = new HashSet<>();
  3. ReflectPoint pt1 = new ReflectPoint(3, 3);
  4. ReflectPoint pt2 = new ReflectPoint(5, 5);
  5. ReflectPoint pt3 = new ReflectPoint(3, 3);
  6. collection.add(pt1);
  7. collection.add(pt2);
  8. collection.add(pt3);
  9. collection.add(pt1);
  10. // pt1.y = 7; //修改后hashCode值就不同了,导致找不到这个对象了,也无法删除
  11. collection.remove(pt1);
  12. /**
          1.如果实体类只重写了equals(),没有重写hashCode() 则collection总的个数为3
           因为,在存储一个对象进HashSet中时,如果没有重写hashCode方法,导致两个对象计算出来的hashCode值是不相同的(hashCode值是根据对象在内存中的地址计算出来的)
           我在我的区域里边找,不在那个已经存储同样对象的区域里边找,所以这个对象还是会被存储进去
  13.       为了让相等的对象找到在相同的区域,就有一个说法,如果两个对象的equals相等时,应该让他们的hashCode也应该相等
          如果对象没有要存到hash集合里边,就没必要重写hashCode();
  14. * 如果要放入HashSet中的对象没有重写hashCode方法和equals方法,
  15. * 两个对象的引用不同,还是会存入HashSet中,默认的equals方法使用 == 比较两个对象的内存地址值
  16. * 如果重写hashCode与equals方法,会认为是相同的对象,后边要存入的存入不了
  17. *
  18. * 当一个对象被存进HashSet集合中后,就不能修改这个对象中参与计算哈希值的字段了,否则,对象
  19. * 修改后的哈希值与最初存进HashSet集合中的哈希值就不同了,在这种情况下,即使在
  20. * contains方法使用该对象的当前引用作为的参数去HashSet集合中检索对象,也将返回
  21. * 不到对象的结果,这也会导致无法从HashSet集合中单独删除当前对象,从而造成内存泄漏。
  22. */
  23. System.out.println(collection.size());
  24. /**
  25. * 通常来说,一个类的两个实例对象用equals()方法比较的结果相等时,他们的哈希吗也必须相等,但
  26. * 反之则不成立,及equals方法比较结果不相等的对象可以有相同的哈希码,或者说哈希吗相同的两个对象,
  27. * equals方法比较的结果可以不相同,例如 字符串"BB"和"Aa"的equals比较结果不相同,但hashCode()结果相等。
  28. */
  29. System.out.println("BB".hashCode()); //
  30. System.out.println("Aa".hashCode());
  31. }

内存泄漏的问题也可以用此例子举例,

所谓内存泄漏,就是这个对象不再使用了可一直占用内存空间,无法释放掉。

9.反射的作用 -> 实现框架的功能

  例如:要调用某个类的main方法,但是不知道要调用哪个类,这个类是动态传过来的,就类似一个小的框架。

  你调用别人写的类,与别人调用你写的类,一个叫工具,一个叫框架(框架调用你的类)

  框架与框架要解决的核心问题

    -> 我做房子卖给用户住,由用户自己安装门窗和空调,我做的房子就是框架,用户需要使用的框架,把门窗插入进我提供的框架中。框架与工具类有区别,工具类被用户的类调用,而框架则是调用用户提供的类。

  框架要解决的核心问题

    -> 因为在写程序时无法知道要调用的类名,所以,在程序中无法直接 new 某个类的实例对象了,而要用反射(Class.forName())的方式类做。

  1. public static void main(String[] args) throws Exception{
  2. InputStream is = new FileInputStream("config.properties"); //尽量面向父类或面向接口编程
  3. /**
  4. * Perperties类似于HashMap,比HashMap多了点功能,可以把内存中的键值对存到硬盘的文件中去,
  5. * 也可以在初始化时把硬盘中的键值对文件加载到内存中
  6. */
  7. Properties props = new Properties();
  8. props.load(is);
  9. /*
  10. * 马上关闭资源,否则会有一点小小的内存泄漏
  11. * 这个内存泄漏不是对象没有被释放,而是对象关联的系统资源没被释放
  12. */
  13. /*
  14. * 告诉系统把关联的系统资源干掉,本对象有可能还在,后续由垃圾回收释放掉
  15. * 自己在垃圾回收之前,先把自己关联的系统资源释放掉
  16. */
  17. is.close();
  18. String className = props.getProperty("className");
  19. Collection collection = (Collection)Class.forName(className).newInstance();
  20. }

config.properties

  className=java.util.ArrayList

10.用类加载器的方式管理资源和配置文件

  上述代码中的 InputStream ips = new FileInputStream("config.properties");在项目开中不会这么搞,一般要用完整的路径,但完整的路径不是硬编码,而是运算出来的。

  .class 需要被类加载器加载到内存中,类加载器会提供一个方法加载普通文件。

  InputStream is = ReflectTest2.class.getClassLoader().getResourceAsStream("config.properties");

  使用getClassLoader()这种方式时,需要把配置文件放在classpath目录下,因为getClassLoader加载.class文件就是在classpath目录下加载,所以当然加载配置文件时也需放到classpath目录下

  getResourceAsStream(),是在在classpath指定的目录下逐一查找需要加载的文件。

  此时config.properties文件需放在src目录下,如果放在包下,需要写上目录名getResourceAsStream("com/java/config.properties"),在使用eclipse时会自动将配置文件复制到classpath目录下,真正在运行时用的是classpath目录下

也可以使用:

  InputStream is = ReflectTest2.class.getResourceAsStream("config.properties"); //表示与当前类所在目录的相对路径下,即应该与当前类在同一个目录,用自己的类加载的时候可以用相对也可以用绝对,其内部还是使用的getClassLoader(),大部分框架内部都是使用类加载器加载配置文件,如果使用Eclipse开发,放在src目录下会自动拷贝的classpath目录下。

 

java高新技术-反射的更多相关文章

  1. Java高新技术 反射机制

     Java高新技术 反射机制 知识概要:                   (1)反射的基石 (2)反射 (3)Constructor类 (4)Field类 (5)Method类 (6)用反射方 ...

  2. 黑马程序猿——JAVA高新技术——反射

    ----------android培训.java培训.java学习型技术博客.期待与您交流!------------ 一.对于反射的概念 对于JAVA反射机制是在执行状态中,对于随意一个类.都可以知道 ...

  3. Java高新技术 注解

      Java高新技术 注解 知识概要:                  (1)了解注解 (2)注解的应用结构图 (3)@Retention(RetentionPolicy.RUNTIME)    ...

  4. JAVA的反射理解

    1----------------------------反射的概念----------------------------------------------- JAVA的反射机制是在运行状态中,对 ...

  5. java的反射

    JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意方法和属性:这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制. ...

  6. iOS运行时编程(Runtime Programming)和Java的反射机制对比

    运行时进行编程,类似Java的反射.运行时编程和Java反射的对比如下:   1.相同点   都可以实现的功能:获取类信息.属性设置获取.类的动态加载(NSClassFromString(@“clas ...

  7. Java 类反射机制分析

    Java 类反射机制分析 一.反射的概念及在Java中的类反射 反射主要是指程序可以访问.检测和修改它本身状态或行为的一种能力.在计算机科学领域,反射是一类应用,它们能够自描述和自控制.这类应用通过某 ...

  8. java的反射机制

    一.java的反射机制浅谈 最近研究java研究得很给力,主要以看博文为学习方式.以下是我对java的反射机制所产生的一些感悟,希望各位童鞋看到失误之处不吝指出.受到各位指教之处,如若让小生好好感动, ...

  9. Java:反射

    初识Java反射机制: 从上面的描述可以看出Java的反射机制使得Java语言可以在运行时去认识在编译时并不了解的类/对象的信息,并且能够调用相应的方法或修改属性的值.Java反射机制的核心就是允许在 ...

随机推荐

  1. 5 个最好的3D游戏开发工具(转)

    转自:http://www.open-open.com/news/view/33a4f0 5 个最好的3D游戏开发工具 jopen 2012-11-19 22:56:21 • 发布 摘要:UDK(th ...

  2. Oracle中使用Entity Framework 6.x Code-First方式开发

    去年写过一篇EF的简单学习笔记,当时EF还不支持Oracle的Code-First开发模式,今天无意又看了下Oracle官网,发现EF6.X已经支持了,并且给出了二篇教程(英文版): 1.Using ...

  3. Orchard CMS中如何打包不带源码的模块

    在Orchard CMS的官网已经提供了文档说明如何打包,但是如果使用它的打包方式,打好的nuget包是带源代码的.如果是为开源系统写模块,不需要关注源代码是否可见.但是如果是用Orchard CMS ...

  4. Entity Framework6 with Oracle(可实现code first)

    Oracle 与2个月前刚提供对EF6的支持.以前只支持到EF5.EF6有很多有用的功能 值得升级.这里介绍下如何支持Oracle   一.Oracle 对.net支持的一些基础知识了解介绍. 1.早 ...

  5. android animation中的参数interpolator详解

      android:interpolator interpolator 被用来修饰动画效果,定义动画的变化率,可以使存在的动画效果可以 accelerated(加速),decelerated(减速), ...

  6. Matlab中的向量

    1.             向量的创建 1)直接输入: 行向量:a=[1,2,3,4,5] 列向量:a=[1;2;3;4;5] 2)用“:”生成向量 a=J:K 生成的行向量是a=[J,J+1,…, ...

  7. 【JavaEE企业应用实战学习记录】requestListener

    package sanglp.servlet; import javax.servlet.*; import javax.servlet.annotation.WebListener; import ...

  8. android开发------响应用户事件

    今天的内容有点简单,不难,就是为按钮添加onClick事件.  新知识点: Intent类的简单使用 startActivity方法 一般事件都由按钮触发,现在我们要实现的是当用户点击按钮的时候,启动 ...

  9. android 一条线

    还在为布局的时候做不出来一条细细的线而烦恼么? 哈哈,自从知道了写法腰也不酸了,腿也不疼了!一口气写100行!! <View android:layout_height="1px&qu ...

  10. 【BZOJ 3053】The Closest M Points

    KDTree模板,在m维空间中找最近的k个点,用的是欧几里德距离. 理解了好久,昨晚始终不明白那些“估价函数”,后来才知道分情况讨论,≤k还是=k,在当前这一维度距离过线还是不过线,过线则要继续搜索另 ...