/*
java程序员反射
*/
反射概述
Java中的类主要是描述相同特性的一类事物,比如我们用Person类来描述人的共有特性,那么Class这个类就是用来描述Java中的各个类,它主要描述Java类中所包含类所具有的共性,比如说类的名称,所属的基类,所属的包等,比如String,Integer,int,等,我们在实例化对象时,首先是将已经编译的类的字节码文件从硬盘加载到内存中,然后虚拟机用这一份字节码创建多个实例对象,而类的这份字节码文件就用Class的实例对象表示(字节码对象);
/*
*获取一个对象所对应的字节码实例对象有三种方式
1类名.class(),比如int.class
2对象.getClass(),比如person.getClass();
3Class.forName(类名),这个类名必须是所对应具体的包中的具体对象。比如Class.forName("java.util.ArrayList");
返回的方式有两种:
1种是已经被加载过,呆在虚拟机中,直接返回。
2中是还没有被加载到内存中,那么就会被类加载器加载进内存,缓存到虚拟机中,然后再返回。
九个预定义的Class实例对象
八个基本数据类型,加上一个返回类型void.class。
演示代码如下:
- class ReflectDemo1_1
- {
- public static void main(String[] args) throws ClassNotFoundException
- {
- Person p=new Person("xt",12);
- Class c1=Integer.class;//用于获取Integer包装类所对应的字节码文件
- Class c2=p.getClass();//用person实例对象的getClass()方法获取实例对象p所对应的字节码文件。
- Class c3=int.class;
- System.out.println(Class.forName("java.lang.Integer"));//使用Class类的静态方法forName()来描述一个对象所对应的字节码文件。
- String s1="xtiongtao";
- System.out.println(s1.getClass());
- System.out.println(c1);
- System.out.println(c2);
- System.out.println(c3);
- //使用isPrimitive()来判断对象所对应的字节码文件是否是基本类型的字节码文件
- System.out.println(int.class.isPrimitive());//返回是true
- System.out.println(Integer.class.isPrimitive());//基本类型包装类所对应的字节码文件不是基本类型的字节码
- System.out.println(int.class==Integer.TYPE);//包装类的常量字段TYPE是用来返回所包装的基本类型的字节码。
- System.out.println(int[].class.isArray())//用来判断一个int类型数组所对应的字节码文件是否是数组类型的。
- System.out.println("******************************");
- fun();
- }
- //下面的方法用来同一个类所对应的不同对象是否是用的同一个字节码对象。
- public static void fun()throws ClassNotFoundException
- {
- String s1="xt";
- Class c1=s1.getClass();
- Class c2=String.class;
- Class c3=Class.forName("java.lang.String");
- System.out.println(c1==c2);
- System.out.println(c2==c3);
- //两个返回的都是true,这样我们这个发现相同类所对应的不同的实例对象所公用的是一份字节码对象,
- }
- }
- class Person
- {
- private String name;
- private int age;
- Person(String name,int age)
- {
- this.name=name;
- this.age=age;
- }
- }
比如Person中的有成员变量,成员函数,构造函数等成分,而Class类中定义了一些方法来描述这些成分,Method, Contructor,Field,Package等
Constructor表示一份字节码中的构造方法。
程序在编译时,只检查语法错误,不会去进行运算。
构造函数的反射
演示代码如下:
- import java.lang.reflect.*;
- class ReflectDemo1_2
- {
- public static void main(String[] args) throws Exception
- {
- //比如我们要使用的String构造方法创建一个字符串,是接受的参数是一个,并且参数类型是StringBuilder的,那么我们怎么将这个想法让JVM知道呢。那么我们可以这样定义
- Constructor constructor=Class.forName("java.lang.String").getConstructor(StringBuilder.class);//获取String类型并且参数类型是StringBuilder类型的构造方法。
- //注意:java编译器在编译时只是严格的检查语法,不会去执行等号左右两边的赋值,只知道是Constructor类型,但是具体的是什么类型的构造方法,参数是几个,java编译器都不知道,而在运行的时候才知道。
- String s=(String)constructor.newInstance(new StringBuilder("xiongtao"));
- System.out.println(s.charAt(2));
- //通过查阅Java文档我们发现Class类有一个方法:
- //newInstance()使用这个方法可以创建一个Class对象所表示的类的一个无参的实例
- String s1=(String)Class.forName("java.lang.String").newInstance();//获取String 类型的默认的无参实例对象。
- s1="xiongtao";
- System.out.println(s1.charAt(2));
- //同样用到了缓存的机制来保存默认构造方法的实例对象,下一次在调用该对象的无参构造方法创建该对象就可以直接使用,不需要再去加载,因此这样会降低程序的性能。
- }
- }
- class Person
- {
- private String name;
- private int age;
- Person(String name,int age)
- {
- this.name=name;
- this.age=age;
- }
- public void showMsg()
- {
- System.out.println(name+"……"+age);
- }
- }
- <span style="font-size:10px;">import java.lang.reflect.*;
- class ReflectDemo1_4
- {
- public static void main(String[] args) throws Exception
- {
- TextClass tc=new TextClass();
- letterReplace(tc);
- }
- public static void letterReplace(Object obj)throws Exception
- {
- Field[] fields=obj.getClass().getDeclaredFields();//获取实例对所对应的字节码文件所对应的所有的成员变量
- for(Field field :fields)//
- {
- if(field.getType()==String.class)//注意这里是为了判断成员变量中的类型是否是String类型,注意这里必须使用==,因为多个String变量公用的是一份字节码文件。不能使用equals()方法
- {
- String s=(String)field.get(obj);
- String temp=s.replace('b','a');
- field.set(obj,temp);
- }
- }
- System.out.println(obj);
- }
- }
- class TextClass
- {
- String str1="bball";
- String str2="basketball";
- String str3="itcast";
- public String toString()
- {
- return str1+"\n"+str2+"\n"+str3;
- }
- }</span>
演示代码如下:
- /*
- *
- *接下来我们来了解java中方法的反射的使用
- */
- import java.lang.reflect.*;
- class ReflectDemo1_6
- {
- public static void main(String[] args) throws Exception
- {
- String s="xiongtao";
- methodFun(s);
- }
- public static void methodFun(String s)throws Exception
- {
- Method m=Class.forName("java.lang.String").getMethod("charAt",int.class);
- //利用反射的方式,得到字节码中的方法,然后在拿到这个方法去作用实例对象上。
- System.out.println(m.invoke(s,2));
- }
- }
演示代码如下:
- import java.lang.reflect.*;
- class ReflectDemo1_7
- {
- public static void main(String[] args)throws Exception
- {
- /*TextDemo1_1.main(new String[]{"xiongtao","zhangsan","lisi"});
- */
- //平时我们调用时是直接根据类名来调用一个指定类的main方法,
- //接下来我们将使用反射的原理来完成这个功能。
- //获取用户传入的类名的字符串
- String clsName=args[0];
- //根据用户传入的类名获取主函数
- Method m=Class.forName(clsName).getMethod("main",String[].class);
- //获得方法,然后将该方法作用到具体的对象上去,因为这个方法时静态方法,所以就不用指定具体的对象。然后传入参数。因为在传入参数时,Java编译器就会自动的拆一次包,所以不能直接传入参数new String[]{"xt","ctt","non"};因为直接传入的字符串数组将会拆成三个字符串,
- //下面第一种就是告诉编译器,这个数组是一个对象,是一个整体,不能够将其拆分
- //第二种就是相当于对这个字符串数组中再进行一次包装,那么编译器解封时也只会解封一次new Object[]这个数组,所以留下的也是一个字符串数组。
- m.invoke(null,(Object)new String[]{"xt","ctt","non"});
- m.invoke(null,new Object[]{(new String[]{"xt","ctt","non"})});
- }
- }
- class TextDemo1_1
- {
- public static void main(String[]args)
- {
- for(String arg:args)
- {
- System.out.println(arg);
- }
- }
- }
接下来,我们将使用一个方法。如果传入的是一个数组,那么就将数组中的每个元素都打印出来,如果是一个单个元素,那么就直接将这个元素打印出来.演示实例如下:
- /*
- *
- */
- import java.lang.reflect.*;
- class ReflectDemo1_9
- {
- public static void main(String[] args)
- {
- int[] arr=new int[]{1,23,4,5};
- String s="xiongtao";
- printFun(arr);
- printFun(s);
- }
- public static void printFun(Object obj)
- {
- if(obj.getClass().isArray())//如果对象对应的字节码是数组类型,
- {
- int len=Array.getLength(obj);//使用java.lang.reflect类中的一个Array来获数组中的每个元素。
- for(int i=0;i<len;i++)
- {
- System.out.println(Array.get(obj,i));//
- }
- }
- else
- {
- System.out.println(obj);
- }
- }
- }
- import java.util.*;
- class ReflectDemo1_10
- {
- public static void main(String[] args)
- {
- Person p1=new Person("xt",12);
- Person p2=new Person("ctt",12);
- Person p3=new Person("ctt",12);
- Person p4=new Person("xt",12);
- Collection c=new ArrayList();
- c.add(p1);
- c.add(p2);
- c.add(p3);
- c.add(p4);
- System.out.println(c.size());//List集合中允许存入相同的元素
- RerefPoint p5=new RerefPoint(1,2);
- RerefPoint p6=new RerefPoint(3,4);
- Collection s=new HashSet();
- s.add(p5);
- s.add(p6);
- System.out.println(s.size());
- p5.x=4;
- s.remove(p5);//这里我们已经移除了p5这个对象,但是为什么集合的长度仍然是2呢,原因是对象中的这个x,y都参与了hashCode值的计算,如果修改以后就会导致元素存储集合中的位置区域发生了变化,再也无法找到以前的对象存储的位置。所以删除就会失败,这样也就会存在隐患,导致内存泄露。所以存储到集合中的对象的元素一旦存储成功以后就不要去修改,
- System.out.println(s.size());
- //哈希算法就是将集合根据哈希值进行若干个区域的划分,每一个区域对应不同的哈希值,然后在存储对象时,先根据对象的hash值将其存储到对应的区域中,这样每次存一个对象时,就不会一个一个的去遍历整个集合,去看是否有重复的,而是只在这一片区域中查找是否有相同的元素,这样也可以提高查找的性能。
- //Set集合中不能存入相同的元素,先是根据传入的对象算出一个hash值,而这个值一般都是内存地址,然后根据这个值将对象存储在,如果地址相同,那么在比较对象本身的equals()这个方法。
- }
- }
- class RerefPoint
- {
- public int x;
- public int y;
- RerefPoint(int x,int y)
- {
- this.x=x;
- this.y=y;
- }
- public int hashCode()
- {
- int prim=31;
- return prim*x+y;
- }