黑马程序员--Java学习日记之类的加载,反射,动态代理,枚举
2015-06-27 11:22
1056 查看
------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
1、类的加载相关概述
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载、连接、初始化三步来对这个类进行初始化。
①加载
就是指将class文件读入内存,并为之创建一个Class对象
任何类被使用时系统都会建立一个Class对象。
②连接
验证 是否有正确的内部结构,并和其他类协调一致
准备 负责为类的静态成员分配内存,并设置默认初始化值
解析 将类的二进制数据中的符号引用替换为直接引用
③初始化:跟以前的初始化动作一样。
②类加载器的组成:
Bootstrap Classloader根类加载器。也称为引导类加载器负责java核心类的加载,比如System.String等。在JDK中JRE的lib目录下rt.jar文件中。
Extension ClassLoader扩展类加载器。负责JRE的扩展目录中jar包的加载,在JDK中JRE的lib目录下的ext目录。
System CkassLoader 系统类加载器,负责在JVM启动时加载来自java命令的class文件,以及负责classpath环境变量所指定的jar包和路径。主要是用来加载我们写的文件。
Class类中就包含属性有field(字段)、method(方法)、construction(构造函数)。
而field中有修饰符、类型、变量名等复杂的描述内容,因此也可以将字段封装称为一个对象。用来获取类中field的内容,这个对象的描述叫Field。同理方法和构造函数也被封装成对象Method、Constructor。要想对一个类进行内容的获取,必须要先获取该字节码文件的对象。该对象是Class类型。
Class类描述的信息:类的名字,类的访问属性,类所属于的包名,字段名称的列表,方法名称的列表等。每一个字节码就是class的实例对象。如:classcls=Data.class;
②Class和class的区别
class:Java中的类用于描述一类事物的共性,该类事物有什么属性,没有什么属性,至于这个属性的值是什么,则由此类的实例对象确定,不同的实例对象有不同的属性值。
Class:指的是Java程序中的各个Java类是属于同一类事物,都是Java程序的类,这些类称为Class。例如人对应的是Person类,Java类对应的就是Class。Class是Java程序中各个Java类的总称;它是反射的基石,通过Class类来使用反射。
要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用就是Class类中方法
获取Class文件对象的方式:
①Object类的getClass()方法。获取字节码文件对象。
Personp = new Person();
Classc = p.getClass();
②数据类型的静态属性class:比第一种方式简单,不用创建对象,也不会调用getClass方法,但是还是要使用具体的类对象和该类的一个静态属性完成。
Classc2 = Person.class;
③Class类中的静态方法forName()方法。注意:要拿带包名的全路径名称:这种方式比较简单,不用创建对象,也不用调用方法,也不需要去调用具体的属性和行为,就能获取到Class对象了。
Classc3 = Class.forName("Person");
一般我们到底使用哪种方法呢?
自己玩:任选一种,第二种较为方便
开发:第三种。为什么呢?因为第三种是一个字符串,而不是一个具体的类名,这样我们就可以把这样的字符串配置到配置文件中。
三种获取Class文件对象的方式演示:
①获取构造方法
getConstructor(Class<?>... parameterTypes):返回一个
Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。
getDeclaredConstructor(Class<?>... parameterTypes):返回一个
Constructor 对象,该对象反映此 Class 对象所表示的类或接口的指定构造方法。
②创建对象
newInstance():创建此 Class 对象所表示的类的一个新实例。
③获取所有成员
getFields():返回一个包含某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的所有可访问公共字段。
getDeclaredFields():返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段。
④获取单个成员
getField(String name):返回一个
Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段。
getDeclaredField(String name):返回一个
Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。
⑤获取所有方法
getMethods():返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法。
getDeclaredMethods(): 返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。
⑥获取单个方法
getMethod(String name,Class<?>... parameterTypes): 返回一个
Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。
getDeclaredMethod(String name,Class<?>... parameterTypes): 返回一个
Method 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法。
方法演示代码如下:
②Constructor代表的是某个类的一个构造方法。提供关于类的单个构造方法的信息以及对它的访问权限。
③获取构造方法
获取所有的构造方法:Constructor[] cons = Class.forName(“cn.itheima.Person”).getConstructors();
获取单个构造方法:Constructor con=Person.class.getConstructor(String.class,int.class);
④创建实例对象
反射方式:Person p= (Person)con.newInstance(“lisi”,30);
通常方式:Person p = new Person(“lisi”,30);
注意:创建实例时newInstance方法中的参数列表必须与获取Constructor的方法getConstructor方法中的参数列表一致。
②方法:
Field getField(String s);只能获取公有和父类中公有
Field getDeclaredField(String s);获取该类中任意成员变量,包括私有
setAccessible(ture);如果是私有字段,要先将该私有字段进行取消权限检查的能力。也称暴力访问。
set(Object obj, Object value);将指定对象变量上此Field对象表示的字段设置为指定的新值。
Object get(Object obj);返回指定对象上Field表示的字段的值。
代码演示如下:
②举一个例子:获得String类的charAt方法。
Method methodCharAt=Class.forName("java.lang.String").getMethod("charAt",int.class);
System.out.println(methodCharAt.invoke(str,1));//调用str对象中charAt(1)
③调用方法
通常方式:System.out.println(str.charAt(1));
反射方式:System.out.println(charAt.invoke(str,1));//str是一个对象,这里str可以是null,说明invoke()方法是一个静态方法。
④实例应用:用反射方式执行某个类中的main方法
a、目的:写一个程序,这个程序能够根据用户提供的类名,去执行该类中的Main方法。
b、问题:启动java程序的main方法的参数是一个字符串数组, 即public static void main(String[]args),通过反射方式来调用这个main方法时,按jdk1.5的语法,整个数组是一个参数,而jdk1.4的语法,数组的每个元素对应一个参数,当做把一个字符串数组作为参数传递给invoke方法如何处理(注意兼容)。所以,在给main方法传递参数时,不能使用代码mainMthod.invoke(null,newString[]{“xxxx”}),javac只把它当做JDK1.4的语法进行理解,。而不能把它当做JDK1.5的语法解释,因此,会出现参数类型不对的问题。
c、解决办法:
方法一:mainMethod.invoke(null,newObject[]{new String[]{xxxx}});
方法二:mainMethod.invoke)((Object)newString[]{"xxxx"});编译器会做特殊处理,编译时不把参数当做数组看待,也就不会数组达三成若干参数
实例一:Method方法演示
实例二: 写一个程序,这个程序能够根据用户提供的类名,去执行该类中的main方法。
②代表数组的Class实例对象的getSuperClass()方法,返回的父类为Object类对应的Class
③基本类型的一维数组可以被当做Object类型使用,不能作为Object[]类型使用,不能当做Object[]类型使用,非基本类型的一维数组,既可以当做Object类型使用,又可以当做Object[]类使用
④ 注意区别 Array.asList()方法处理int[]和String[]时的差异
⑤ Array工具类用于完成对数组的反射操作
Array.getLength(Object obj);获取数组的长度
Array.get(Object obj,int x);获取数组中的元素
四.反射的重要应用
1.通过反射运行配置文件的内容
3.通过泛型设置某个对象的某个属性为指定值
在程序运行过程中产生的这个对象,而程序运行过程中产生对象其实就是我们刚才反射讲解的内容,所以,动态代理其实就是通过反射来生成一个代理
2).测试类中如果需要coding()方法,需要直接实例化一个Student,并调用方法;
如果想在coding()方法的前面或者后面添加一些其他功能,可以不用修改Student类,而为Student类添加
一个"代理类",代理类会调用Student类中的方法。
3).增加了代理类后,测试类不需要直接面对Student,转而使用代理类。代理类中为coding方法添加了新的功能。
实际上就是不直接使用基础类,而是使用第三方代理,并且代理可以添加一些基本功能
2).在需要使用代理类的时候,使用Proxy类中的newProxyInstance()方法获取代理类的对象
3).JDK只能为接口做动态代理,所以要为所要代理的类定义一个接口,并去实现接口
例如:骰子:需要2个实例;
扑克:54个实例;
现在说的枚举,就是基于"多例模式":
例如:我们的程序运行期间,需要三个颜色(红\绿\蓝),所以我们使用一个类MyColor来表示颜色。因为我们只需要三个颜色,所以这个类的对象,只能有三个;
2).直接定义枚举项。注意:可以有其它成员,但枚举项必须在第一行;
3).枚举中可以包含抽象方法;
4).任何的枚举类,都继承自:Enum类;
String name()//枚举项的名字fieldname
int ordinal()//枚举项的索引值
String toString()//枚举对象所对对应的fieldname
<T> T valueOf(Class<T> type,String name)//将一个字符串转换为type类型的对象
values()
此方法虽然在JDK文档中查找不到,但每个枚举类都具有该方法,它遍历枚举类的所有枚举值非常方便
------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
一、类的加载
1、类的加载相关概述
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载、连接、初始化三步来对这个类进行初始化。①加载
就是指将class文件读入内存,并为之创建一个Class对象
任何类被使用时系统都会建立一个Class对象。
②连接
验证 是否有正确的内部结构,并和其他类协调一致
准备 负责为类的静态成员分配内存,并设置默认初始化值
解析 将类的二进制数据中的符号引用替换为直接引用
③初始化:跟以前的初始化动作一样。
2、类加载器
①概念:负责将.class文件加载到内存中,并为之生成对应的Class对象。②类加载器的组成:
Bootstrap Classloader根类加载器。也称为引导类加载器负责java核心类的加载,比如System.String等。在JDK中JRE的lib目录下rt.jar文件中。
Extension ClassLoader扩展类加载器。负责JRE的扩展目录中jar包的加载,在JDK中JRE的lib目录下的ext目录。
System CkassLoader 系统类加载器,负责在JVM启动时加载来自java命令的class文件,以及负责classpath环境变量所指定的jar包和路径。主要是用来加载我们写的文件。
二、反射
1、反射的基石——Class类
①所有的类文件都有共同属性,所以可以向上抽取,把这些共性内容封装成一个类,这个类就叫Class(描述字节码文件的对象)。Class类中就包含属性有field(字段)、method(方法)、construction(构造函数)。
而field中有修饰符、类型、变量名等复杂的描述内容,因此也可以将字段封装称为一个对象。用来获取类中field的内容,这个对象的描述叫Field。同理方法和构造函数也被封装成对象Method、Constructor。要想对一个类进行内容的获取,必须要先获取该字节码文件的对象。该对象是Class类型。
Class类描述的信息:类的名字,类的访问属性,类所属于的包名,字段名称的列表,方法名称的列表等。每一个字节码就是class的实例对象。如:classcls=Data.class;
②Class和class的区别
class:Java中的类用于描述一类事物的共性,该类事物有什么属性,没有什么属性,至于这个属性的值是什么,则由此类的实例对象确定,不同的实例对象有不同的属性值。
Class:指的是Java程序中的各个Java类是属于同一类事物,都是Java程序的类,这些类称为Class。例如人对应的是Person类,Java类对应的就是Class。Class是Java程序中各个Java类的总称;它是反射的基石,通过Class类来使用反射。
2、反射机制
JAVA反射机制是在运行状态中,对于任意一个类,都能知道这个类的所有属性和方法,对于任意一个对象,都能调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。简而言之,反射就是通过class文件对象,去使用该文件中的成员变量,构造方法,成员方法。要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用就是Class类中方法
获取Class文件对象的方式:
①Object类的getClass()方法。获取字节码文件对象。
Personp = new Person();
Classc = p.getClass();
②数据类型的静态属性class:比第一种方式简单,不用创建对象,也不会调用getClass方法,但是还是要使用具体的类对象和该类的一个静态属性完成。
Classc2 = Person.class;
③Class类中的静态方法forName()方法。注意:要拿带包名的全路径名称:这种方式比较简单,不用创建对象,也不用调用方法,也不需要去调用具体的属性和行为,就能获取到Class对象了。
Classc3 = Class.forName("Person");
一般我们到底使用哪种方法呢?
自己玩:任选一种,第二种较为方便
开发:第三种。为什么呢?因为第三种是一个字符串,而不是一个具体的类名,这样我们就可以把这样的字符串配置到配置文件中。
三种获取Class文件对象的方式演示:
package cn.itcast_01; 02. 03./* 04. *获取Class文件对象的三种方式 05. */ 06.public class ReflectDemo { 07. public static void main(String[] args) throws ClassNotFoundException { 08. //第一种 09. Person p = new Person(); 10. Class c = p.getClass(); 11. 12. Person p2 = new Person(); 13. Class c2 = p2.getClass(); 14. 15. System.out.println(p == p2);// false 16. System.out.println(c == c2);// true 17. 18. //第二种 19. Class c3 = Person.class; 20. // int.class; 21. // String.class; 22. System.out.println(c == c3); 23. 24. // 第三种 25. // ClassNotFoundException 26. Class c4 = Class.forName("cn.itcast_01.Person"); 27. System.out.println(c == c4); 28. } 29.}
3、Class类中的常用方法
通过反射获取和使用构造方法和类中成员变量即成员方法①获取构造方法
getConstructor(Class<?>... parameterTypes):返回一个
Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。
getDeclaredConstructor(Class<?>... parameterTypes):返回一个
Constructor 对象,该对象反映此 Class 对象所表示的类或接口的指定构造方法。
②创建对象
newInstance():创建此 Class 对象所表示的类的一个新实例。
③获取所有成员
getFields():返回一个包含某些 Field 对象的数组,这些对象反映此 Class 对象所表示的类或接口的所有可访问公共字段。
getDeclaredFields():返回 Field 对象的一个数组,这些对象反映此 Class 对象所表示的类或接口所声明的所有字段。
④获取单个成员
getField(String name):返回一个
Field 对象,它反映此 Class 对象所表示的类或接口的指定公共成员字段。
getDeclaredField(String name):返回一个
Field 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明字段。
⑤获取所有方法
getMethods():返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法。
getDeclaredMethods(): 返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。
⑥获取单个方法
getMethod(String name,Class<?>... parameterTypes): 返回一个
Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。
getDeclaredMethod(String name,Class<?>... parameterTypes): 返回一个
Method 对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法。
方法演示代码如下:
package cn.itcast_02; 02. 03.import java.lang.reflect.Constructor; 04. 05.import cn.itcast_01.Person; 06. 07./* 08. * 通过反射获取构造方法并使用。 09. */ 10.public class ReflectDemo { 11. public static void main(String[] args) throws Exception { 12. // 获取字节码文件对象 13. Class<?> c = Class.forName("cn.itcast_01.Person"); 14. 15. // 获取构造方法 16. // public Constructor[] getConstructors():所有公共构造方法 17. // public Constructor[] getDeclaredConstructors():所有构造方法 18. Constructor<?> [] cons = c.getDeclaredConstructors(); 19. for (Constructor<?> con : cons) { 20. System.out.println(con); 21. } 22. 23. // 获取单个构造方法 24. // public Constructor<T> getConstructor(Class<?>... parameterTypes) 25. // 参数表示的是:你要获取的构造方法的构造参数个数及数据类型的class字节码文件对象 26. Constructor<?> con = c.getConstructor();// 返回的是构造方法对象 27. 28. Person p = new Person(); 29. System.out.println(p); 30. // public T newInstance(Object... initargs) 31. // 使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例, 32. //并用指定的初始化参数初始化该实例。 33. Object obj = con.newInstance(); 34. System.out.println(obj); 35. 36. Person p1 = (Person) obj; 37. p1.show(); 38. } 39.}
4、Constructor<T>类
①概述:当指定的类中没有空参数构造函数时,或者创建的类对象需要通过指定的构造函数进行初始化,这时候就不能使用Class类中的newInstance方法。这时候我们就需要通过其它的方法来实现了。②Constructor代表的是某个类的一个构造方法。提供关于类的单个构造方法的信息以及对它的访问权限。
③获取构造方法
获取所有的构造方法:Constructor[] cons = Class.forName(“cn.itheima.Person”).getConstructors();
获取单个构造方法:Constructor con=Person.class.getConstructor(String.class,int.class);
④创建实例对象
反射方式:Person p= (Person)con.newInstance(“lisi”,30);
通常方式:Person p = new Person(“lisi”,30);
注意:创建实例时newInstance方法中的参数列表必须与获取Constructor的方法getConstructor方法中的参数列表一致。
5、Field类
①Field代表的是某个类的某个成员变量。②方法:
Field getField(String s);只能获取公有和父类中公有
Field getDeclaredField(String s);获取该类中任意成员变量,包括私有
setAccessible(ture);如果是私有字段,要先将该私有字段进行取消权限检查的能力。也称暴力访问。
set(Object obj, Object value);将指定对象变量上此Field对象表示的字段设置为指定的新值。
Object get(Object obj);返回指定对象上Field表示的字段的值。
代码演示如下:
package cn.itcast_03; 02. 03.import java.lang.reflect.Constructor; 04.import java.lang.reflect.Field; 05. 06./* 07. * 通过发生获取成员变量并使用 08. */ 09.public class ReflectDemo { 10. public static void main(String[] args) throws Exception { 11. // 获取字节码文件对象 12. Class<?> c = Class.forName("cn.itcast_01.Person"); 13. 14. // 获取所有的成员变量 15. // Field[] fields = c.getFields(); 16. // Field[] fields = c.getDeclaredFields(); 17. // for (Field field : fields) { 18. // System.out.println(field); 19. // } 20. 21. /* 22. * Person p = new Person(); p.address = "北京"; System.out.println(p); 23. */ 24. 25. // 通过无参构造方法创建对象 26. Constructor<?> con = c.getConstructor(); 27. Object obj = con.newInstance(); 28. System.out.println(obj); 29. 30. // 获取单个的成员变量 31. // 获取address并对其赋值 32. Field addressField = c.getField("address"); 33. // public void set(Object obj,Object value) 34. // 将指定对象变量上此 Field 对象表示的字段设置为指定的新值。 35. addressField.set(obj, "北京"); // 给obj对象的addressField字段设置值为"北京" 36. System.out.println(obj); 37. 38. // 获取name并对其赋值 39. // NoSuchFieldException 40. Field nameField = c.getDeclaredField("name"); 41. // IllegalAccessException 42. nameField.setAccessible(true); 43. nameField.set(obj, "林青霞"); 44. System.out.println(obj); 45. 46. // 获取age并对其赋值 47. Field ageField = c.getDeclaredField("age"); 48. ageField.setAccessible(true); 49. ageField.set(obj, 27); 50. System.out.println(obj); 51. } 52.}
6、Method类
①概述:Method类代表类中的一个成员方法,调用某个对象身上的方法,要先得到方法,再针对某个对象调用。②举一个例子:获得String类的charAt方法。
Method methodCharAt=Class.forName("java.lang.String").getMethod("charAt",int.class);
System.out.println(methodCharAt.invoke(str,1));//调用str对象中charAt(1)
③调用方法
通常方式:System.out.println(str.charAt(1));
反射方式:System.out.println(charAt.invoke(str,1));//str是一个对象,这里str可以是null,说明invoke()方法是一个静态方法。
④实例应用:用反射方式执行某个类中的main方法
a、目的:写一个程序,这个程序能够根据用户提供的类名,去执行该类中的Main方法。
b、问题:启动java程序的main方法的参数是一个字符串数组, 即public static void main(String[]args),通过反射方式来调用这个main方法时,按jdk1.5的语法,整个数组是一个参数,而jdk1.4的语法,数组的每个元素对应一个参数,当做把一个字符串数组作为参数传递给invoke方法如何处理(注意兼容)。所以,在给main方法传递参数时,不能使用代码mainMthod.invoke(null,newString[]{“xxxx”}),javac只把它当做JDK1.4的语法进行理解,。而不能把它当做JDK1.5的语法解释,因此,会出现参数类型不对的问题。
c、解决办法:
方法一:mainMethod.invoke(null,newObject[]{new String[]{xxxx}});
方法二:mainMethod.invoke)((Object)newString[]{"xxxx"});编译器会做特殊处理,编译时不把参数当做数组看待,也就不会数组达三成若干参数
实例一:Method方法演示
package cn.itcast_04; 02. 03.import java.lang.reflect.Constructor; 04.import java.lang.reflect.Method; 05. 06.public class ReflectDemo { 07. public static void main(String[] args) throws Exception { 08. // 获取字节码文件对象 09. Class<?> c = Class.forName("cn.itcast_01.Person"); 10. 11. // 获取所有的方法 12. // Method[] methods = c.getMethods(); // 获取自己的包括父亲的公共方法 13. // Method[] methods = c.getDeclaredMethods(); // 获取自己的所有的方法 14. // for (Method method : methods) { 15. // System.out.println(method); 16. // } 17. 18. Constructor<?> con = c.getConstructor(); 19. Object obj = con.newInstance(); 20. 21. /* 22. * Person p = new Person(); p.show(); 23. */ 24. 25. // 获取单个方法并使用 26. // public void show() 27. // public Method getMethod(String name,Class<?>... parameterTypes) 28. // 第一个参数表示的方法名,第二个参数表示的是方法的参数的class类型 29. Method m1 = c.getMethod("show"); 30. // obj.m1(); // 错误 31. // public Object invoke(Object obj,Object... args) 32. // 返回值是Object接收,第一个参数表示对象是谁,第二参数表示调用该方法的实际参数 33. m1.invoke(obj); // 调用obj对象的m1方法 34. 35. System.out.println("----------"); 36. // public void method(String s) 37. Method m2 = c.getMethod("method", String.class); 38. m2.invoke(obj, "hello"); 39. System.out.println("----------"); 40. 41. // public String getString(String s, int i) 42. Method m3 = c.getMethod("getString", String.class, int.class); 43. Object objString = m3.invoke(obj, "hello", 100); 44. System.out.println(objString); 45. // String s = (String)m3.invoke(obj, "hello",100); 46. // System.out.println(s); 47. System.out.println("----------"); 48. 49. // private void function() 50. Method m4 = c.getDeclaredMethod("function"); 51. m4.setAccessible(true); 52. m4.invoke(obj); 53. } 54.}
实例二: 写一个程序,这个程序能够根据用户提供的类名,去执行该类中的main方法。
package cn.itheima; 02.//定义一个测试类 03.class Test{ 04. public static void main(String[] args){ 05. for(String arg : args){ 06. System.out.println(arg); 07. } 08. } 09.} 10.//用反射方式根据用户提供的类名,去执行该类中的main方法。 11.import java.lang.reflect.Method; 12. 13.public class PerformedMain{ 14. 15. public static void main(String[] args) throws Exception { 16. //普通方式 17. Test.main(new String[]{"123","456","789"}); 18. System.out.println("-----------------------------"); 19. 20. //反射方式 21. String className=args[0]; 22. Class clazz=Class.forName(className); 23. 24. Method methodMain=clazz.getMethod("main",String[].class); 25. //方式一:强制转换为超类Object,不用拆包 26. methodMain.invoke(null, (Object)new String[]{"123","456","789"}); 27. //方式二:将数组打包,编译器拆包后就是一个String[]类型的整体 28. methodMain.invoke(null, new Object[]{new String[]{"123","456","789"}}); 29. }
三、数组的反射
①具有相同维数和元素类型的数组属于同一个类型,即具有相同的Class实例对象。②代表数组的Class实例对象的getSuperClass()方法,返回的父类为Object类对应的Class
③基本类型的一维数组可以被当做Object类型使用,不能作为Object[]类型使用,不能当做Object[]类型使用,非基本类型的一维数组,既可以当做Object类型使用,又可以当做Object[]类使用
④ 注意区别 Array.asList()方法处理int[]和String[]时的差异
⑤ Array工具类用于完成对数组的反射操作
Array.getLength(Object obj);获取数组的长度
Array.get(Object obj,int x);获取数组中的元素
四.反射的重要应用
1.通过反射运行配置文件的内容
public class Demo { 02. public static void main(String[] args) throws Exception{ 03. 04. /* 05. //获取一个class类对象,使用三种方法中那一中,class类中静态方法 06. Class aclass =Class.forName("cn.itcast.Test反射读取配置文件.line2"); 07. //由获得Class对象再得到一个该类的一个对象 08. Object obj=aclass.getConstructor().newInstance(); 09. //得到的该类的一个成员方法 10. Method m=aclass.getMethod("move", int.class); 11. m.invoke(obj, 20); 12. */ 13. 14. //定义一个class类对象 15. Class aclass=Class.forName(getvalue("classname")); 16. //通过反射,并调用构造方法,创建一个该类的对象 17. Object obj=aclass.getConstructor(String.class).newInstance("好方法"); 18. //通过反射调用其成员方法 19. aclass.getMethod(getvalue("methodname"), int.class).invoke(obj, 50); 20. } 21. //通过定义一个方法来获取几种东西 22. public static String getvalue(String str)throws IOException{ 23. //定义一个properties集合map集合,以便于读写文件 24. Properties pro=new Properties(); 25. //定义一个输入流 26. FileReader filein=new FileReader(new File("pro.properties")); 27. //读取文件中内容到集合中 28. pro.load(filein); 29. //关闭资源 30. filein.close(); 31. return pro.getProperty(str); 32. } 33.} 34.public class line2 { 35. private String name; 36. public line2(String name){ 37. this.name =name; 38. } 39. public void move(int n){ 40. System.out.println("名字是:"+name+"小伙的年龄是"+n); 41. } 42.}
2.通过泛型越过泛型检查
泛型只是在编译期间有作用,可以通过泛型直接加载其class,往声明泛型的集合添加东西public class Demo { 02. public static void main(String[] args) throws Exception { 03. //定义一个集合 04. ArrayList<Integer> list=new ArrayList<>(); 05. list.add(10); 06. //使用反射创建此类的class对象,调用其方法 07. Class aclass=ArrayList.class;//list.getclass(); 08. //获取其方法的对象,使用的参数为object.class,通用的类型 09. Method m=aclass.getMethod("add", Object.class); 10. m.invoke(list, "nihaoma "); 11. System.out.println(list); 12. } 13.}
3.通过泛型设置某个对象的某个属性为指定值
public class Demo { 02. public static void main(String[] args) throws Exception { 03. Cat c=new Cat(); 04. setValue(c,"name","bosimao"); 05. setValue(c, "age", 2); 06. System.out.println("年龄:"+c.getAge()+"名字:"+c.getName()); 07. } 08. 09. 10. private static void setValue(Object obj, String fieldName, Object value) throws Exception { 11. Class aclass=obj.getClass(); 12. Field field=aclass.getDeclaredField(fieldName); 13. //由于是私有属性,需要暴力访问 14. field.setAccessible(true); 15. field.set( obj, value); 16. } 17.} 18.public class Cat { 19. private String name; 20. private int age=0; 21. 22. public String getName(){ 23. return this.name; 24. } 25. public int getAge(){ 26. return this.age; 27. } 28.}
五.动态代理
1.动态代理的概述
本来应该自己做的事情,却请了别人来做,被请的人就是代理对象。在程序运行过程中产生的这个对象,而程序运行过程中产生对象其实就是我们刚才反射讲解的内容,所以,动态代理其实就是通过反射来生成一个代理
2.代理模式:
1).Student类中有一个coding()方法,写程序;2).测试类中如果需要coding()方法,需要直接实例化一个Student,并调用方法;
如果想在coding()方法的前面或者后面添加一些其他功能,可以不用修改Student类,而为Student类添加
一个"代理类",代理类会调用Student类中的方法。
3).增加了代理类后,测试类不需要直接面对Student,转而使用代理类。代理类中为coding方法添加了新的功能。
实际上就是不直接使用基础类,而是使用第三方代理,并且代理可以添加一些基本功能
public class Demo {//测试类 02. public static void main(String[] args) { 03. /* 04. Student stu = new Student(); 05. stu.coding(); 06. */ 07. //使用代理模式 08. StudentProxy proxy = new StudentProxy(); 09. proxy.coding(); 10. } 11.} 12. 13. 14. 15. 16.public class StudentProxy {//代理类 17. public void coding(){ 18. check(); 19. new Student().coding(); 20. zj(); 21. } 22. //先期检查 23. public void check(){ 24. System.out.println("先期检查......"); 25. } 26. //后期总结 27. public void zj(){ 28. System.out.println("后期总结......."); 29. } 30.} 31. 32. 33. 34. 35.public class Student {//基础类 36. public void coding(){ 37. System.out.println("做项目,写程序......"); 38. } 39.}
3.动态代理的步骤;
1).自定义一个类,实现InvocationHander接口,并重写接口中的invoke()方法2).在需要使用代理类的时候,使用Proxy类中的newProxyInstance()方法获取代理类的对象
3).JDK只能为接口做动态代理,所以要为所要代理的类定义一个接口,并去实现接口
public class Demo {//测试类 02. public static void main(String[] args) throws Exception { 03. //多态的子类对象,使用proxy类中的方法newproxyInstace 04. 05. //Idao idaostu=(Idao) Proxy.newProxyInstance(Class.forName("cn.itcast.Test动态代理.Student").getClassLoader(), 06. // Student.class.getInterfaces(),new MyInvocationhander(new Student())); 07. 08. Idao idaostu=(Idao) Proxy.newProxyInstance(Student.class.getClassLoader(), 09. Student.class.getInterfaces(), 10. new MyInvocationhander(new Student())); 11. idaostu.write();//需要使用的方法 12. } 13.} 14. 15. 16. 17. 18.public class MyInvocationhander implements InvocationHandler {//实现接口,代理类 19. 20. private Object obj;//定义需要代理的对象 21. public MyInvocationhander(Object obj) { 22. this.obj=obj; 23. } 24. 25. 26. @Override 27. public Object invoke(Object proxy, Method method, Object[] args) 28. throws Throwable { 29. Object objmethod=method.invoke(obj); 30. show(); 31. return objmethod; 32. } 33. 34. 35. private void show() { 36. System.out.println("真是牛叉啊!!"); 37. } 38.} 39. 40. 41.public interface Idao {//自己定义接口 42. public void write(); 43.} 44. 45. 46.public class Student implements Idao {//自定义类,需要代理的类 47. 48. @Override 49. public void write() { 50. System.out.println("你好,动态代理!!"); 51. } 52.}
六.枚举
1.枚举概述
一种模式"多例模式":在整个应用程序运行期间,有些类的实例,只允许有固定的几个,这种模式就是"多例模式"例如:骰子:需要2个实例;
扑克:54个实例;
现在说的枚举,就是基于"多例模式":
例如:我们的程序运行期间,需要三个颜色(红\绿\蓝),所以我们使用一个类MyColor来表示颜色。因为我们只需要三个颜色,所以这个类的对象,只能有三个;
public abstract class MyColor3 {//抽象类也是可以定义多例模式 02. public static final MyColor3 RED = new MyColor3("红"){ 03. @Override 04. void show() { 05. System.out.println("我是红色的!"); 06. }}; 07. public static final MyColor3 GREEN = new MyColor3("绿"){ 08. @Override 09. void show() { 10. System.out.println("我是绿色的!"); 11. }}; 12. public static final MyColor3 BLUE = new MyColor3("蓝"){ 13. @Override 14. void show() { 15. System.out.println("我是蓝色的!"); 16. }}; 17. private String colorName; 18. 19. private MyColor3(String colorName){ 20. this.colorName = colorName; 21. } 22. 23. public String toString(){ 24. return this.colorName; 25. } 26. 27. abstract void show(); 28.}
2.制作枚举步骤:
1).定义枚举:public enum Xxxxx2).直接定义枚举项。注意:可以有其它成员,但枚举项必须在第一行;
3).枚举中可以包含抽象方法;
4).任何的枚举类,都继承自:Enum类;
public enum MyColor3 { 02. RED("红") { 03. @Override 04. void show() { 05. System.out.println("我是红色的!"); 06. } 07. },GREEN("绿") { 08. @Override 09. void show() { 10. System.out.println("我是绿色的!"); 11. } 12. },BLUE("蓝") { 13. @Override 14. void show() { 15. System.out.println("我是蓝色的!"); 16. } 17. }; 18. private String colorName; 19. private MyColor3(String n){ 20. this.colorName = n; 21. } 22. 23. public String getColorName(){ 24. return this.colorName; 25. } 26. 27. abstract void show(); 28.}
3.枚举类中常用方法,使用枚举对象调用
int compareTo(E o)//比较的是枚举项的索引值,做减法String name()//枚举项的名字fieldname
int ordinal()//枚举项的索引值
String toString()//枚举对象所对对应的fieldname
<T> T valueOf(Class<T> type,String name)//将一个字符串转换为type类型的对象
values()
此方法虽然在JDK文档中查找不到,但每个枚举类都具有该方法,它遍历枚举类的所有枚举值非常方便
public class Demo { 02. public static void main(String[] args) { 03. MyColor3 c1 = MyColor3.RED; 04. MyColor3 c2 = MyColor3.BLUE; 05. 06. System.out.println(c2.compareTo(c1));//索引值的减法 07. System.out.println("name = " + c2.name());//BLUE 08. //int ordinal() 09. System.out.println("c2.ordinal() : " + c2.ordinal()); 10. System.out.println("c1.ordinal() : " + c1.ordinal()); 11. //String toString() 12. System.out.println("c2.toString():" + c2.toString()); 13. //<T> T valueOf(Class<T> type,String name) 14. MyColor3 c3 = c1.valueOf("RED");//将一个字符串转换为MyColor3对象 15. MyColor3 c4 = MyColor3.valueOf(MyColor3.class,"BLUE"); 16. 17. System.out.println(c3); 18. System.out.println(c4); 19. 20. //values() 21. MyColor3[] result = c1.values(); 22. System.out.println("循环遍历:"); 23. for(MyColor3 c : result){ 24. System.out.println(c); 25. } 26. } 27.}
------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
相关文章推荐
- 黑马程序员——构造函数与set方法
- 程序员修炼之道摘要
- 面试总结二
- 黑马程序员-【java基础】-多线程
- 黑马程序员——内部类的理清
- 黑马程序员--构造函数的理解和怎么去使用
- 【BAT(百度,阿里,腾迅)面试题】
- 黑马程序员——Java基础--面向对象(一)
- 如何成为一名真正专业的程序员
- 《程序员的自我修养》系列技术文章整理收藏
- 面试需要的基础知识-替换空格
- 面试需要的基础知识-合并排序数组
- 黑马程序员-IOS学习笔记(六)Category(类别)
- [置顶] 程序员技术练级攻略
- 黑马程序员——Java基础--抽象
- Axis Aligned Rectangles (Google 面试题, 2016网易游戏校招笔试)
- 黑马程序员——Java基础:工具类、for循环、可变参数、其他类
- 黑马程序员--流程控制
- 黑马程序员--枚举类型和结构体
- 高级程序员离职或跳槽可以像公司申请一个月工资