java 自学日志【十九】---基础加强DAY26下
2013-05-15 19:14
302 查看
------android培训,java培训,期待与您交流------
反射:就是把java类中的各种成员映射成相应的java类;
反射的基石:Class类
class类是对对象的抽象,而Class是对class的抽象,或者说类是对对象的共性的描述,
而Class又是对类的共性的描述,但它本身又是一个类,它的具体实例是内存中某个类的字节码。
得到类的字节码的方式有三种:
(1)对象.getClass();
(2)类名.class;
(3)Class.forName("完整类名“);
Class.forName()的作用:
如果有要获得的字节码存在于内存中就直接返回,如果不存在,则通过类加载器将其 .clas文件加载后返回。
九种预定义的Class实例对象:
表示八个基本类型和void。这些类对象由 Java 虚拟机创建,与其表示的基本类型同名,即boolean、byte、char、short、int、long、float 和double。
数组的Class实例对象:类型[ ].class,可以用isArray()方法判断一个Class实例是否为数组。
Integer.TYPE代表的就是他们所包装的基本数据类型的字节码;
Class.isPrimitive():判断是否是原始类型;
总之,只要是在源程序中出现的类型,都有自己的Class实例对象。
Construtcor类:构造器类,构造函数类,得到某个类的构造方法:
得到某个类所有的构造方法:get.Constructors();
Constructor[ ] constructors = String.class.getConstructors();
Constructor[ ] constructors = String.class.getDeclaredConstructors();//包括私有的
得到某个类的某个构造方法:会用到参数类型的Class对象
Constructor constructor1 = String.class.getConstructor(StringBuffer.class);
创建实例对象:参数要用获得构造方法时指定的类型。
普通方式:String str = new String(new StringBuffer("abc"));
反射方式:String str1 =(String) constructor1.newInstance(new StringBuffer("abc"));
Class也提供了newInstance,其内部是利用Constructor的newInstance的无参方法。
用Class类的newInstance创建实例对象;
String s = (String)Class.forName("java.lang.String").newInstance();
Field类:类的成员变量,它的具体实例代表的的是类中某个字段的定义,而非具体的变量。
对于上边的类ReflectPoint如何获取其字段x,和y的值?
访问public成员变量:
ReflectPoint ref = new ReflectPoint(4,8);
Field fieldY =ref.getClass().getField("y");
System.out.println(fieldY.get(ref));
访问private成员变量:
暴力反射,用getDeclaredField()取出包括私有在内的所有变量
Field fieldX =ref.getClass().getDeclaredField("x");
将私有变量设为可访问的
fieldX.setAccessible(true);
System.out.println(fieldX.get(ref));
将RefectPoint中的所有String成员变量的值中的“a”,替换成“w”
Method类:代表某个类中的一个方法
String str = "abc";
Method methodCharAt = str.getClass().getMethod("charAt", int.class);
System.out.println(methodCharAt.invoke(str, 2));//如果方法是静态的,invoke的第一次参数可以写成null
System.out.println(methodCharAt.invoke(str, new Object[]{2}));//JDK1.4的语法,封箱成Integer
JDK1.4和JDK1.5的invoke方法的区别:
jdk1.4:public Object invoke(Object obj,Object[ ] args);用的是数组
jdk1.5:public Object invoke(Object obj,Object... args);用的是可变参数
用反射方式执行某个类中的main方法
数组的反射:如果两个数组的元素类型和维度相同,则它们用的是同一份字节码,即具有相同的字节码对象实例;
代表数组的Class实例对象的getSuperclass()方法返回的是父类Object类对应的class;
注意:基本数据类型的一维数组可以被当做Object类型使用,但是不能当做Object【】类型使用;
Array工具类用于完成对数组的反射动作;
打印一个对象,如果是数组则打印数组中每个元素的值
不可能得到,例如:对应new Object[]{"adc",105,'ch'}这个数组,因为里边的数据类型不一致, 所以只能得到具体某个元素的类型。
反射的作用:实现框架的功能
框架与工具类的区别:工具类被用户的类调用,而框架则是调用用户提供的类。
框架要解决的核心问题:因为在写框架时,你无法知道要调用的类名,所以无法直接new某个类的实例对象,而要用反射的方式来做了。
hashcode把集合分成若干个区域,每个区域都有对应的哈希值,当某个对象想进入集合中时,会先算出这个对象的哈希值,然后看它属于某个区域,再和相应区域里的元素进行比较,而不需要和所有元素比较,前提是要存进hash集合中,哈希值才有价值,当一个对象呗存进hashset集合中,就不能修改这个对象中的那些参与计算哈希值的字段了,否则对象修改后的哈希值与最初存储进集合中时的哈希值就不同了,这样,即使在contains方法使用该对象的当前引用作为参数去集合中检索
对象,也检索不到,这也会导致无法从集合中单独删除当前对象,从而造成内存泄露;
将上边的例子模拟成一个小的框架:即Collection collection的值以获取配置文件的形式传入。
需要先建立一个config.propertie文件,其内容为:className= ArrayList。
实际开发中管理配置文件的方法:
方法一:先获取程序安装目录,然后拼上配置文件的相对路径,获取绝对路径(不是硬编码获取绝对路径)。
这样就可以既读取,有能写入(即保存更改后的配置信息)。它是读写资源文件的唯一方法。
方法二:通过类加载器,来加载资源文件,但只能加载,不能修改配置文件。框架多是这样加的。
(在MyEclipse中,会把除源文件之外的所有资源文件都在bin执行文件目录下拷贝一份。)这种方式有两个方法可以调用,最终都是利用类加载器来完成的。
使用类加载器的getResourceAsStream()方法
InputStream ips=ReflectTest2.class.getClassLoader().getResourceAsStream("cn/itcast/day1/config.properties");
上边这句是从执行文件的根目录(bin目录,也即package所在的目录)下去找资源文件。
使用类的getResourceAsStream()方法加载时,如果配置文件前没有"/"表示在在包中与.class同级,如果有"/”则表示与包同级,在根目录。
InputStream ips = ReflectTest2.class.getResourceAsStream("config.properties");
InputStream ips = ReflectTest2.class.getResourceAsStream("resource/config.properties");
反射:就是把java类中的各种成员映射成相应的java类;
反射的基石:Class类
class类是对对象的抽象,而Class是对class的抽象,或者说类是对对象的共性的描述,
而Class又是对类的共性的描述,但它本身又是一个类,它的具体实例是内存中某个类的字节码。
得到类的字节码的方式有三种:
(1)对象.getClass();
(2)类名.class;
(3)Class.forName("完整类名“);
Class.forName()的作用:
如果有要获得的字节码存在于内存中就直接返回,如果不存在,则通过类加载器将其 .clas文件加载后返回。
九种预定义的Class实例对象:
表示八个基本类型和void。这些类对象由 Java 虚拟机创建,与其表示的基本类型同名,即boolean、byte、char、short、int、long、float 和double。
数组的Class实例对象:类型[ ].class,可以用isArray()方法判断一个Class实例是否为数组。
Integer.TYPE代表的就是他们所包装的基本数据类型的字节码;
Class.isPrimitive():判断是否是原始类型;
总之,只要是在源程序中出现的类型,都有自己的Class实例对象。
class Demo { public static void main(String[] args) { String str = "abc"; Class cls1 = str.getClass(); Class cls2 = String.class; Class cls3 = Class.forName("java.lang.String"); System.out.println(cls1 == cls2); System.out.println(cls1 == cls3); System.out.println(cls1.isPrimitive()); System.out.println(int.class.isPrimitive()); System.out.println(Integer.class.isPrimitive()); System.out.println(Integer.TYPE.isPrimitive()); System.out.println(int[].class.isArray()); } }
Construtcor类:构造器类,构造函数类,得到某个类的构造方法:
得到某个类所有的构造方法:get.Constructors();
Constructor[ ] constructors = String.class.getConstructors();
Constructor[ ] constructors = String.class.getDeclaredConstructors();//包括私有的
得到某个类的某个构造方法:会用到参数类型的Class对象
Constructor constructor1 = String.class.getConstructor(StringBuffer.class);
创建实例对象:参数要用获得构造方法时指定的类型。
普通方式:String str = new String(new StringBuffer("abc"));
反射方式:String str1 =(String) constructor1.newInstance(new StringBuffer("abc"));
Class也提供了newInstance,其内部是利用Constructor的newInstance的无参方法。
用Class类的newInstance创建实例对象;
String s = (String)Class.forName("java.lang.String").newInstance();
Field类:类的成员变量,它的具体实例代表的的是类中某个字段的定义,而非具体的变量。
public class ReflectPoint { private int x; public int y; public String str1 = "Master"; public String str2 = "Basketball"; public String str3 = "itcast"; public ReflectPoint(int x ,int y) { super(); this.x=x; this.y = y; } }
对于上边的类ReflectPoint如何获取其字段x,和y的值?
访问public成员变量:
ReflectPoint ref = new ReflectPoint(4,8);
Field fieldY =ref.getClass().getField("y");
System.out.println(fieldY.get(ref));
访问private成员变量:
暴力反射,用getDeclaredField()取出包括私有在内的所有变量
Field fieldX =ref.getClass().getDeclaredField("x");
将私有变量设为可访问的
fieldX.setAccessible(true);
System.out.println(fieldX.get(ref));
将RefectPoint中的所有String成员变量的值中的“a”,替换成“w”
class Demo { private static void changeStringValue(Object obj) throws Exception { Field [] fields = obj.getClass().getFields(); for(Field field:fields) { if(field.getType()==String.class) { //字节码的比较用==更精确,因为比较的字节码是同一份文件; String str =(String) field.get(obj); String str1 = str.replace('a','w'); field.set(obj, str1); } } } public static void main(String[] args) { changeStringValue(ref); System.out.println(ref); } }
Method类:代表某个类中的一个方法
String str = "abc";
Method methodCharAt = str.getClass().getMethod("charAt", int.class);
System.out.println(methodCharAt.invoke(str, 2));//如果方法是静态的,invoke的第一次参数可以写成null
System.out.println(methodCharAt.invoke(str, new Object[]{2}));//JDK1.4的语法,封箱成Integer
JDK1.4和JDK1.5的invoke方法的区别:
jdk1.4:public Object invoke(Object obj,Object[ ] args);用的是数组
jdk1.5:public Object invoke(Object obj,Object... args);用的是可变参数
用反射方式执行某个类中的main方法
class TestArguments { public static void main(String [] args) { for(String arg:args) { System.out.println(arg); } } } String startingClassName =args[0];//注意,要在该程序的运行环境中配置args为TestArguments; Method mainMethod =Class.forName(startingClassName).getMethod("main",String[].class); //new Object[]{new String[]{"ball","wife"} mainMethod.invoke(null,new Object[]{new String[]{"ball","wife"}}); //这句为什么?JDK1.5向下兼容1.4版本,而1.4版本中,会把传递进的数组当一个个参数的组成的数组,而进行拆分。这句调用的是1.4的方法。 mainMethod.invoke(null,(Object)new String[]{"ball","wife"});//这句是调用了1.5的方法。
数组的反射:如果两个数组的元素类型和维度相同,则它们用的是同一份字节码,即具有相同的字节码对象实例;
代表数组的Class实例对象的getSuperclass()方法返回的是父类Object类对应的class;
注意:基本数据类型的一维数组可以被当做Object类型使用,但是不能当做Object【】类型使用;
Array工具类用于完成对数组的反射动作;
int[] a1 =new int [3]; int[] a2 = new int [4]; int[][] a3 = new int[2][3]; String[] a4 = new String[]{"111","222","333"}; Class cl1 =a1.getClass(); System.out.println(a1.getClass() == a2.getClass());//true; // System.out.println(a1.getClass() == a3.getClass());//MyEclipse会报错 // System.out.println(a1.getClass() == a4.getClass());//MyEclipse会报错 System.out.println("a1:"+a1.getClass().getName()); System.out.println("a2:"+a2.getClass().getName()); System.out.println("a3:"+a3.getClass().getName()); System.out.println("a4:"+a4.getClass().getName()); System.out.println(a1.getClass().getSuperclass().getName()); System.out.println(a4.getClass().getSuperclass().getName()); // Object[] obj1 = a1;//这句是错误的 Object obj2 = a2; Object[] obj3 = a3; Object[] obj4 = a4; //Arrays工具类,可以把数组当成集合来打印,但如果数组元素的类型是基本数据类型,那么打印的是数组引用的地址,而不是每个元素的值 System.out.println(Arrays.asList(a1)); System.out.println(Arrays.asList(a4));
打印一个对象,如果是数组则打印数组中每个元素的值
class Demo { private static void printObject(Object obj) { Class cla = obj.getClass(); if(cla.isArray()) { int len = Array.getLength(obj); for(int x=0;x<len;x++) { System.out.println(Array.get(obj, x)); } } else { System.out.println(obj); } } public static void main(String[] args) { printObject(a4); printObject("zyx"); } }问题:怎样得到数组的元素类型?
不可能得到,例如:对应new Object[]{"adc",105,'ch'}这个数组,因为里边的数据类型不一致, 所以只能得到具体某个元素的类型。
反射的作用:实现框架的功能
框架与工具类的区别:工具类被用户的类调用,而框架则是调用用户提供的类。
框架要解决的核心问题:因为在写框架时,你无法知道要调用的类名,所以无法直接new某个类的实例对象,而要用反射的方式来做了。
Collection collection= new ArrayList();//new HashSet(); ReflectPoint p1 = new ReflectPoint(4,4); ReflectPoint p2 = new ReflectPoint(2,5); ReflectPoint p3 = new ReflectPoint(7,6); ReflectPoint p4 = new ReflectPoint(4,4); collection.add(p1); collection.add(p2); collection.add(p3); collection.add(p1); //重写ReflectPoint的hashCode和equals方法,让x和y参与其重新方法内。 /* @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + x; result = prime * result + y; return result; } @Override//如果把这句去掉,下边写成equals(ReflectPoint obj)不会报错 public boolean equals(Object obj) {//Object不能改成ReflectPoint,否则就不是重写,而是重载了 if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; ReflectPoint other = (ReflectPoint) obj; if (x != other.x) return false; if (y != other.y) return false; return true; } */ System.out.println(collection.size())//ArrayList值为4,HashSet则值为3; p1.y= 8;//HashSet集合中,p1元素的y参与了hashcode值的运算,所以改变y后,导致无法删除和重复加入该元素。 System.out.println(collection.remove(p1));//ArrayList的remove 方法,删除列表中第一次出现的p1 System.out.println(collection.size());//ArrayListz值减少一个为3,HashSet无法删除,所以值不变仍为3;知识点:hashcode的作用:
hashcode把集合分成若干个区域,每个区域都有对应的哈希值,当某个对象想进入集合中时,会先算出这个对象的哈希值,然后看它属于某个区域,再和相应区域里的元素进行比较,而不需要和所有元素比较,前提是要存进hash集合中,哈希值才有价值,当一个对象呗存进hashset集合中,就不能修改这个对象中的那些参与计算哈希值的字段了,否则对象修改后的哈希值与最初存储进集合中时的哈希值就不同了,这样,即使在contains方法使用该对象的当前引用作为参数去集合中检索
对象,也检索不到,这也会导致无法从集合中单独删除当前对象,从而造成内存泄露;
将上边的例子模拟成一个小的框架:即Collection collection的值以获取配置文件的形式传入。
需要先建立一个config.propertie文件,其内容为:className= ArrayList。
InputStream ips = ReflectTest2.class.getResourceAsStream("config.properties"); Properties prop = new Properties(); prop.load(ips); ips.close(); String className = prop.getProperty("className"); Collection collection = (Collection) Class.forName(className).newInstance() //注意:用newInstance时,必须该类有空参构造方法,否则会出现初始化异常; ReflectPoint p1 = new ReflectPoint(4,4); ReflectPoint p2 = new ReflectPoint(2,5); ReflectPoint p3 = new ReflectPoint(7,6); ReflectPoint p4 = new ReflectPoint(4,4); collection.add(p1); collection.add(p2); collection.add(p3); collection.add(p1); System.out.println(collection.size())//配置文件中className为ArrayList时,值为4,为HashSet时值为3;注意:在实际开发中,配置文件获取不是用相对路径,而是绝对路径。
实际开发中管理配置文件的方法:
方法一:先获取程序安装目录,然后拼上配置文件的相对路径,获取绝对路径(不是硬编码获取绝对路径)。
这样就可以既读取,有能写入(即保存更改后的配置信息)。它是读写资源文件的唯一方法。
方法二:通过类加载器,来加载资源文件,但只能加载,不能修改配置文件。框架多是这样加的。
(在MyEclipse中,会把除源文件之外的所有资源文件都在bin执行文件目录下拷贝一份。)这种方式有两个方法可以调用,最终都是利用类加载器来完成的。
使用类加载器的getResourceAsStream()方法
InputStream ips=ReflectTest2.class.getClassLoader().getResourceAsStream("cn/itcast/day1/config.properties");
上边这句是从执行文件的根目录(bin目录,也即package所在的目录)下去找资源文件。
使用类的getResourceAsStream()方法加载时,如果配置文件前没有"/"表示在在包中与.class同级,如果有"/”则表示与包同级,在根目录。
InputStream ips = ReflectTest2.class.getResourceAsStream("config.properties");
InputStream ips = ReflectTest2.class.getResourceAsStream("resource/config.properties");
相关文章推荐
- java 自学日志【十八】---基础加强DAY26上
- java 自学日志【二十】---基础加强DAY27上
- JAVA 自学日志【二十一】---基础加强DAY27下
- java基础视频25days(毕老师)学习日志(day23、24)
- 黑马程序员Java基础加强9-16学习日记
- 黑马程序员----java基础加强(1)
- Java基础加强
- 【黑马程序员】java基础加强_枚举
- 05_java基础加强(数组、判断语句、循环结构、进制转换、常用类等)
- 黑马程序员_Java基础10天日志
- java基础加强--注解的入门与使用
- Java基础加强总结之动态代理(Proxy)
- java 自学日志【十七】---正则表达式
- 【黑马程序员】java基础加强---------类加载器(classLoader)
- 大龄屌丝自学笔记--Java零基础到菜鸟--037
- 黑马程序员--Java基础加强(三)
- 黑马程序员-张孝祥Java基础加强(PART1)
- java基础视频25days(毕老师)学习日志(day14、day15、day16)
- 黑马程序员_java基础自学6
- 黑马程序员-------Java基础加强1