1. 反射
1.概述
Java类用于描述一类事物的共性,其类有什么属性,不同的实例对象有不同的属性值。就是把java类中的各种成分映射成相应的java类。
Class:表示的是各个java类是否属于同一类事物。描述的是:类的名字,类的访问属性,类所属于的包名,字段名称的列表,方法名称的列表。
比较记忆:
Person类代表人,他的实例是:张三,李四。。等具体的人,
Class类代表java类,他的实例对象对应的是:
对象各个类在内存中的字节码,例如:Person类的字节码,ArrayList类的字节码
一个类被加载器加载到内存中,占用一片存储空间,这个空间里面的内容就是类的字节码,不同的类的字节码是不同的,所以他们在内存中的内容是不同的,这一个个的空间分别用一个个的对象来表示,
2.基本方法
格式:
格式1:类名.class;
格式2:对象.getClass()
格式3:Class.forName(“包名.类名”);
九个预定义对象 基本数据类型(boolean、byte、char、short、int、long、float 和double。)和void
只有这写基本类型的包装类可以通过包装类.TYPE获得所包装的基本类型的Class对象。
1. public class reflectDemo {
2. public static void main(String[] args) throws Exception {
3. String str = "hello";
4. Class strClass= String.class;// 类名.class
5. Class strClass1 = str.getClass();// 对象.getClass
6. Class strClass2 = Class.forName("java.lang.String");// 使用Class.forName("包名.类名")
7. System.out.println("String的字节码:" + strClass);
8. System.out.println(strClass == strClass1);// true
9. System.out.println(strClass == strClass2);// true
10. // 从而可以证明,String在内存的字节码是唯一的
11. /*
12. * boolean isPrimitive()判断Class对象是否是表示一个基本类型
13. */
14. System.out.println("String是否是基本类型:" + strClass.isPrimitive());
15. System.out.println("int是否是基本类型:" + int.class.isPrimitive());
16. /* 包装类通过包装类.TYPE获得所封装的基本类型的Class字节码对象 */
17. System.out.println("Integer.TYPE是否是基本类型:" + Integer.TYPE.isPrimitive());
18. /*
19. * public boolean isArray()判定此 Class 对象是否表示一个数组类。
20. */
21. System.out.println("int []是否是数组类型:" + int[].class.isArray());
22.
23. }
24.
25. }
26. 结果:
27. String的字节码:class java.lang.String
28. true
29. true
30. String是否是基本类型:false
31. int是否是基本类型:true
32. Integer.TYPE是否是基本类型:true
33. int []是否是数组类型:true
3.构造函数(反射)
通过Constructor类来获得构造函数,然后获得对象。
也可以通过Class中的newInstance方法获得无参的构造函数的对象,
1. import java.lang.reflect.Constructor;
2.
3. public class reflectDemo {
4. public static void main(String[] args) throws Exception {
5. Class strClass = String.class;// 类名.class
6. /*
7. * 使用Constructor类获得构造方法,调用Class的getConstructor(Class...para)方法,
8. * 调用此方法的时候,参数是是Class,通过参数的类型指定获得那一个构造方法。
9. * newInstance(和指定参数类型的那个一样的对象)来获得对象
10. */
11. Constructor con=strClass.getConstructor(StringBuffer.class);
12. String str=(String) con.newInstance(new StringBuffer("abc"));
13. System.out.println(str);
14. /**
15. * 也可以通过Class的newInstance()获得无参的构造方法的对象
16. */
17. String str1=(String)strClass.newInstance();
18. str1="bdc";
19. System.out.println(str1);
20. }
21.
22. }
23. 结果:
24. abc
25. bdc
用获得的构造方法创建实例,此时创建的时候的参数和你获得构造方法时的参数类型要一样
4.字段(Filed)反射
4.1字段反射
1. import java.lang.reflect.Field;
2. class Person{
3. public String name;
4. private int age;
5. public Person(String name,int age){
6. this.name=name;
7. this.age=age;
8. }
9. }
10. public class FiledDemo {
11. public static void main(String[] args) throws Exception {
12. Class pC=Person.class;//获得Person类的字节码
13. Person p=new Person("abc",23);
14. /**
15. * 获得公共字段(getField),这只是获得字段对象,不含有值,要是获得某个对象的值
16. * 那么需要get(对象)来获得
17. */
18. Field fieldName=pC.getField("name");//获得字段
19. String name=(String)fieldName.get(p);//获得对象中字段的值
20. System.out.println("name="+name);
21. /**
22. * 获得私有字段(getDeclaredField()),这里也只是获得只是简单的字段,不包含值
23. * setAccessible()设置可操作
24. */
25. Field filedAge=pC.getDeclaredField("age");
26. filedAge.setAccessible(true);//可操作
27. Objectage=filedAge.get(p);//获得此对象的值
28. System.out.println("age="+age);
29. }
30.
31. }
32. 结果:
33. name=abc
34. age=23
4.2更改字段的值
将对象中的字段是String类型的,那么就取对象的值,将值中的字符b更改为字符0,然后输出
1. import java.lang.reflect.Field;
2.
3.
4. class Person{
5. public String name;
6. private int age;
7. public Person(String name,int age){
8. this.name=name;
9. this.age=age;
10. }
11. public String toString(){
12. return name+":"+age;
13. }
14. }
15. public class FiledDemo {
16. public static void main(String[] args) throws Exception {
17. Class pC=Person.class;//获得Person类的字节码
18. Person p=new Person("abc",23);
19. /**
20. * getFields()获得所有字段
21. */
22. Field [] fileds=pC.getFields();
23. for(Field f :fileds){//遍历
24. if(String.class==f.getType()){//判断f的类型是否是String类型
25. String oldValue=(String)f.get(p);
26. String newValue=oldValue.replace('b', '0');//将字段上的值的b替换成0
27. f.set(p, newValue);//重新传给对象
28.
29. }
30. }
31. System.out.println(p);
32. }
33.
34. }
35. 结果:
36. a0c:23
5.方法反射(Method)
5.1普通方法反射
1. import java.lang.reflect.Method;
2. public class MethodDemo {
3. public static void main(String[] args)throws Exception {
4. /**
5. * 通过getMethod(方法名,参数类型)获得方法对象
6. * invoke(对象,参数)来调用获得的方法,
7. */
8. String str="abcd";
9. Method methodcharAt=String.class.getMethod("charAt",int.class);
10. System.out.println(methodcharAt.invoke(str, 3));
11. System.out.println(methodcharAt.invoke("defh", 3));
12.
13. }
14.
15. }
5.2,静态方法反射
1. import java.lang.reflect.Method;
2.
3. public class MethodDemo {
4. public static void main(String[] args)throws Exception {
5. /**
6. * 如果invoke(null,参数),那么就表示调用此方法的方法是静态方法
7. */
8.
9. Method methosMax=Math.class.getMethod("max", int.class,int.class);
10. System.out.println(methosMax.invoke(null, 23, 57));
11. }
12.
13. }
5.3主函数(main)反射
1. import java.lang.reflect.Method;
2.
3. public class mainDemo {
4. public static void main(String[] args) throws Exception {
5.
6. Method mianMethod = Class.forName("wang.fuxi.jiaqiang.text").getMethod(
7. "main", String[].class);
8. mianMethod.invoke(null, new Object[] { new String[] { "123", "456",
9. "789" } });
10. }
11.
12. }
13.
14. class text {
15. public static void main(String[] agrs) {
16. System.out.println(agrs.length);
17. }
18. }
19. 结果:
20. 3
注意:在调用主函数的时候,给主函数传数组参数的时候,程序会先拆包,然后取里面的参数,所以我们要先把数组封装起来,否则,报异常,
在运行主调类的主函数的时候,也可以在在类中右击—run configurations----arguments----然后把被调用类的位置传进去(包名.类名),然后调用的时候就可以使用 Method mianMethod =Class.forName(argus[0]).getMethod("main", String[].class);来获得被调类的主函数。
传参的方式:
invoke(null,new Object[] { new String[] {"123", "456", "789" } });
或者:
invoke(null, (Object)(new String[] {"123", "456","789" } ));
6.数组反射
数组类型相同并且维数也相同,那么他们的Class字节码对象才相等,还有对于AsList(T…. ts)方法来说,在1.5之后,那么int数组把其当作一个参数来处理,如果是String数组,那么就分来作为参数。
6.1基本判断
1. import java.util.Arrays;
2.
3. public class SuZuDemo {
4. public static void main(String[] args) {
5. int[] a = new int[3];
6. int[] b = new int[4];
7. int[][] c = new int[3][4];
8. String[] s1 = new String[3];
9. System.out.println(a.getClass() == b.getClass());// true
10. //System.out.println(a.getClass()==c.getClass());//编译失败
11. //System.out.println(a.getClass()==s1.getClass());//编译失败
12. /**
13. * 获得父类的字节码,从结果知道,他们的父类的是Object
14. */
15. System.out.println(a.getClass().getSuperclass().getName());
16. System.out.println(s1.getClass().getSuperclass().getName());
17. int[] a1 = new int[] { 1, 2, 3 };
18. String[] s2 = new String[] { "a", "b", "c" };
19. System.out.println(Arrays.asList(a1));// 在1.5后,此此方法会把整形数组当作一个参数处理
20. System.out.println(Arrays.asList(s2));// 会把String数组拆分,分别作为参数来处理
21. }
22.
23. }
24. 结果:
25. true
26. java.lang.Object
27. java.lang.Object
28. [[I@1f66cff]
29. [a, b, c]
6.2,反射数组
使用Array类。
1. import java.lang.reflect.Array;
2. import java.util.Arrays;
3.
4. public class SuZuDemo {
5. public static void main(String[] args) throws Exception {
6. String[] s = { "abc", "def", "hig" };
7. String ss = "hello";
8. print(s);
9. print(ss);
10. }
11.
12. public static void print(Object ob) {
13. Class classzz = ob.getClass();// 反射
14. if (classzz.isArray()) {// 判断的是否是数组
15. int len = Array.getLength(ob);// 获取长度
16. for (int i = 0; i < len; i++) {
17. System.out.println(Array.get(ob, i));// 获得数组中的元素
18. }
19. } else {
20. System.out.println(ob);
21. }
22. }
23.
24. }
25. 结果:
26. abc
27. def
28. hig
29. hello
30.
2. 反射的应用
1. HashCode()方法和HashSet的联系
HashSet集合中,要是存放了对象,那么就不要修改在对象中的字段的值,否则会出现内存泄漏,溢出,只要一修改,那么哈希值会改变,就不会在那个区域找到此对象,
HashCode(),是为了比较对象,然后就是把不相同的对象存储到集合中,在集合中存放早集合中的对象,不要修改,否则会出现内存泄漏。只用于Set集合中,在List集合中没有起到作用。
1. import java.util.ArrayList;
2. import java.util.Collection;
3. import java.util.HashSet;
4.
5. public class SetDemo {
6. public static void main(String[] args) {
7. Collection con = new HashSet();
8. TextDemo t1 = new TextDemo("zhangsan", 12);
9. TextDemo t2 = new TextDemo("lisi", 12);
10.
11. con.add(t1);
12. con.add(t2);
13. con.add(t2);
14. //t1.age = 23;// 更改了字段的值,那么hashCode值也变了,那么再移除修改的对象,那么就会找不到
15. t1.setAge(22);//这样修改了,那么也会找不到原来的元素
16. System.out.println(con.remove(t1));//fasle
17. System.out.println(con.size());
18. }
19.
20. }
21.
22. class TextDemo {
23. String name;
24. int age;
25.
26. public TextDemo(String name, int age) {
27. this.name = name;
28. this.age = age;
29. }
30.
31. @Override
32. public int hashCode() {
33. final int prime = 31;
34. int result = 1;
35. result = prime * result + age;
36. result = prime * result + ((name == null) ? 0 : name.hashCode());
37. return result;
38. }
39.
40. @Override
41. public boolean equals(Object obj) {
42. if (this == obj)
43. return true;
44. if (obj == null)
45. return false;
46. if (getClass() != obj.getClass())
47. return false;
48. TextDemo other = (TextDemo) obj;
49. if (age != other.age)
50. return false;
51. if (name == null) {
52. if (other.name != null)
53. return false;
54. } else if (!name.equals(other.name))
55. return false;
56. return true;
57. }
58.
59. public void setAge(int age) {
60. this.age = age;
61. }
62. }
2. 框架案例
框架,框架就是调用你写的类,工具类:就是你调用工具类来完善你的。在你没有写出你的类的时候,
框架已经产生,现在使用把类名存储在文件中,然后利用反射来创建此对象,当你更配置的时候,框架中相应的额跟着改变。
现在nane.properties文件中存放的是:
className=java.util.HashSet
1. public static void main(String[] args) throws Exception {
2. InputStream in=new FileInputStream("nane.properties");
3. Properties pro=new Properties();
4. pro.load(in);
5. in.close();
6. String className=pro.getProperty("className");
7. Class c=Class.forName(className);
8. Collection con=(Collection)c.newInstance();
9. con.add(newPerson(1,2));
10. con.add(newPerson(4,2));
11. con.add(newPerson(3,2));
12. con.add(newPerson(1,2));
13. System.out.println("集合长度:"+con.size());
14. }结果:
15. 集合长度:3
也可以打开配置文件,然后进行更改里面的值,那么结果就会跟着改变。
className=java.util.ArrayList
1. public static void main(String[] args) throws Exception {
2. InputStream in=new FileInputStream("nane.properties");
3. Properties pro=new Properties();
4. pro.load(in);
5. in.close();
6. String className=pro.getProperty("className");
7. Class c=Class.forName(className);
8. Collection con=(Collection)c.newInstance();
9. con.add(new Person(1,2));
10. con.add(new Person(4,2));
11. con.add(new Person(3,2));
12. con.add(new Person(1,2));
13. System.out.println("集合长度:"+con.size());
14.
15. }结果:
16. 集合长度:4
3. 加载资源的位置
1.可是使用绝对路径,但是那么就使用getRealPath,然后进行存数,
2.使用加载器
加载器可以加载java文件,那么也可以加载配置文件,但是有一点缺点,就是配置文件只能读取不能修改,可以使用相对路径,那么最前面要加”/”,否则就不用加”/”,表示的是根目录。
InputStream in=Text.class.getClassLoader().getResourceAsStream("/包名/nane.properties");
Text是运行类的类名,nane.properties是配置的文件