在正常的类操作过程中,一定要先确定使用的类,再利用关键字new 产生实例化对象后使用。 但是在反射里面,可以通过Object 类中的getClass()
==(public final Class<?> getClass() )==方法实现.
第一种:调用 Object 类中的 getClass()
方法,要使用此类必须有实例化对象
mport java.util.Date; public class Person { public static void main(String[] args) { Date date = new Date(); Class<?> clazz = date.getClass(); System.out.println(date.getClass()); System.out.println(clazz.getName()); } } 运行结果: class java.util.Date java.util.Date
第二种:使用“类.class
” 取得,此时不许用通过指定的实例化对象获取。
public class Person { public static void main(String[] args) { Class<?> clazz = java.util.Date.class; System.out.println(clazz.getName()); } } 运行结果: java.util.Date
第三种:调用 Class 类提供的方法:public static Class<?> forName(String className)Throws ClassNotFoundException
public class Person { public static void main(String[] args) throws Exception { //此方法只需要定义一个具体的名称就可以取得反射操作类对象,此类需要确实存在不存在会抛出异常 Class<?> clazz = Class.forName("java.util.Date"); System.out.println(clazz.getName()); } }
利用getClass()
方法操作一般出现简单的 Java 类与提交参数的自动赋值操作中,像Struts、Spring MVC 都会提供表单参数与简单的 Java 类的自动转换。
利用类.class
的方式往往 是将反射操作的类型设置交由用户使用,像 Hibernate 中进行数据保存以及根据ID 查询中会使用到此类操作;
利用Class.forName()
方法可以实现配置文件以及 Annotation 配置的反射操作 ,几乎所有的开发框架都是依靠此方式实现的。
接下来就可以利用Class 类来进行类的反射控制。在Class 类中提供有一些常用的方法,自己百度查看(常用 10个)
有一点需要注意的是,如果利用 Class 类中 的 newInstance()
方法反射实例化类对象,则类中一定要提供无参构造方法,否则会出现语法错误。也可以进一步深入利用反射调用指定的无参构造。
package com.cj; /* * 利用反射实例化对象 * */ //创建一个Book 书类 class Book{ // Book的无参构造方法 public Book(){ System.out.println("Book类的无参构造方法"); } // @Override 它说明了被标注的方法重载了父类的方法,起到了断言的作用,这里没有重载父类 @Override public String toString() { return "java 开发反射实例化对象学习"; } } public class Person { public static void main(String[] args) throws Exception { Class<?> clazz = Class.forName("com.cj.Book"); //设置操作对象类的名称 // 反射实例化后的对象都是Object 类型 Object obj = clazz.newInstance(); //相当于使用new 调用无参构造方法 Book book = (Book) obj ; System.out.println(book); } } 运行结果: Book类的无参构造方法 java 开发反射实例化对象学习
该代码没有通过关键字new 对Book进行实例化操作 ,而是通过Class类定义的操作类名称,并利用 newInstance()
方法进行对象实例化,此时会默认调用类中的无参构造。但是该方法返回的Object 类型,需要向下转型 ,转换为子类实例。
java 这样做有什么意义?
答:利用反射机制实例化对象可以实现 更好的解耦合操作。
利用反射实现工厂设计模式
package com.cj; /* * 利用反射实例化对象 * */ interface Fruit{ // 动作 public void eat(); } class Apple implements Fruit{ @Override public void eat(){ System.out.println("eat 苹果"); } } class Orange implements Fruit{ @Override public void eat(){ System.out.println("eat 橘子"); } } class Factory { public static Fruit getInstance(String className) { Fruit fruit = null; try { //反射实例化,子类对象可以使用Fruit 接收 fruit = (Fruit) Class.forName(className).newInstance(); } catch (Exception e) { //没有处理异常 } return fruit; } } public class Person { public static void main(String[] args) { //直接传递类名称 Fruit fa = Factory.getInstance("com.cj.Apple"); Fruit fb = Factory.getInstance("com.cj.Orange"); fa.eat(); fb.eat(); } } 运行结果: eat 苹果 eat 橘子
在实际的开发中将以上的工厂设计模式再结合一些配置文件,列如(XML格式文件),就可以利用配置文件来动态定义项目中所需要的操作类,此时的程序将变得非常灵活。
使用newInstance()
方法实现反射实例化对象有限制,就是需要提供无参构造。所以当类中只有有参构造的时候,就必须通过java.lang.reflect.Constructor
类来实现对象的反射实例化操作。
虽然有解决方案,但每一个类的构造方法中的参数数量可能不一样多,此时如果需要编写一个公共的反射实例化对象的工具类会比较麻烦。
取得类中的构造方法
取得全部构造方法:public Constructor<?> [] getConstructors() throws SecurityException
取得指定参数类型的构造方法:public Constructor<T> getConstructor(Class<?>...parameterTypes) throws NoSuchMethodException,SecurityException
Constructor 类的常用操作方法
描述(return):方法名
返回构筑方法上所有抛出异常的类型(Class<?>[]):getExceptionTypes()
取得构造方法的修饰符(int):getModifiers()
取得构造方法的名字(String):getName()
取得构造方法中的参数个数(int):getParameterCount()
取得构造方法中的参数类型Class<?>[]):getParameterTypes()
调用指定参数的构造实例化类对象(T):newInstance(Object ...initargs)
此方法使用了可变参数化
修饰符利用数字描述,所有的修饰符本质上都是数字加法操作。假如(public=1,final=16),那么(public final)=(1+16)17,在java.lang.reflect.Modifer 类中明确的定义了各个修饰符对应的常量操作。
数字转修饰符的方法:public static String toString (int mod)
package com.cj; import java.lang.reflect.Constructor; class Book{ private String title; private double price; public Book(String title,double price){ this.title = title; this.price = price ; } public String toString(){ return "图书名称:" + this.title +","+"图书价格:"+this.price ; } } public class Person { public static void main(String[] args) throws Exception{ //此类需要存在,所以需要抛出异常 Class<?> clazz = Class.forName("com.cj.Book"); // 明确找到Book类中的两个参数的构造,第一个是String ,第二个是double Constructor<?> constructor = clazz.getConstructor(String.class ,double.class); Object object = constructor.newInstance("java实战",11.2); System.out.println(object); } } 运行结果: 图书名称:java实战,图书价格:11.2
利用反射机制实现类方法的操作,可以利用Class类 完成
Class 类取得普通方法的操作
取得全部方法(Method[]):getMethods()
取得指定类中指定方法名称与参数类型的方法(Method):getMethod(String name,Class<?>...parameterTypes)
Method 类的常用方法
取得方法修饰符(int):getModifiers()
getReturnType()
getParameterCount()
getParameterTypes()
反射调用方法并且传递执行方法所需要的参数数据:invoke(Object object ,Object...args)
getExceptionTypes()
invoke()
反射调用的核心操作。
package com.cj; import java.lang.reflect.Method; import java.util.*; class Book{ private String title; public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } } public class Person { // 首字母大写第一种方法 public static String initcap(String string){ char[] chars = string.toCharArray(); if(chars[0]>='a' && chars[0]<='z'){ chars[0] = (char) (chars[0]-32); } return new String(chars); } // 第二种方法 public static String initcaps(String string){ //substring 取子串 return string.substring(0,1).toUpperCase() +string.substring(1); } public static void main(String[] args)throws Exception { String fieldName = "title"; //要操作成员的名称 Class<?> clazz = Class.forName("com.cj.Book"); //取得要操作的反射对象 Object object = clazz.newInstance(); //实例化对象 // 取得类中的setTitle方法,由于title的首字母需要大写,所以调用init处理,参数类型为String Method setMed = clazz.getMethod("set" + initcap(fieldName),String.class); // 取得类中的getTitle方法,笨方法不接受参数并且没有返回值类型声明 Method getMed = clazz.getMethod("get"+initcap(fieldName)); setMed.invoke(object,"java开发"); System.out.println("书名:"+getMed.invoke(object)); } } 运行结果: 书名:java开发
当取得了对应方法的Method 对象后就可以利用 invoke()
指定方法所在类的实例化对象以及相应的参数实现方法的反射调用。
成员的取得依然需要通过Class 类方法。
Class 类中取得成员的操作
取得本类定义的全部成员(Field[]):getDeclaredFields()
取得本类指定名称的成员(Field):getDeclaredField(String name)
取得本类继承父类的全部成员(Field[]):getFields()
取得本类继承父类中指定名称的成员(Field):getField(String name)
返回值类型都是java.lang.reflect.Field,此类可以描述类成员的信息。在Field类中也定义了一些方法。
Field 类的常用方法
getType()
get(Object obj)
set(Object obj,Object value)
还有许多setXxx()
、getXxx()
,自行查看官方文档。
package com.cj; import java.lang.reflect.Field; class Book{ private String title; //私有属性并没有定义setter,getter方法 } public class Person { public static void main(String[] args)throws Exception { Class<?> clazz = Class.forName("com.cj.Book"); //取得反射对象 Object object = clazz.newInstance(); //给出实例化对象 Field titleField = clazz.getDeclaredField("title"); //取得类中的title属性; titleField.setAccessible(true); //取消封装 titleField.set(object,"java开发"); //相当于:this.title = "java开发" System.out.println(titleField.get(object)); //相当于:this.title } } 运行结果: java开发
注意封装操作的解除,如果不使用setAccessible(true)
会报错,虽然这样做了可以直接进行成员的访问,但是这样的代码严格意义上不标准,所有的属性的访问还是通过setter、getter方法操作。
如何确定当前的软件项目运行的语言环境;实现多语言切换,必须针对每一个语言提供一个资源文件,并且可以根据语言环境选择不同的资源文件进行信息的读取。
如果要对用户的语言环境进行定义,则可以使用 java.util.Locale 类完成。
Locale 类常用的方法
设置要使用的语言以及国家编码:public Locale(String language,String country)
构造
取得当前语言环境的 Locale 类对象:public static Locale getDefault()
package com.cj; import java.util.Locale; public class Person { public static void main(String[] args)throws Exception { Locale locale = Locale.getDefault(); //取得本地默认的Local对象 System.out.println(locale); } } 运行结果: zh_CN //zh表示使用的语言是中文,CN表示的是国家是中国
资源文件一般都是以“key,value” 的形式保存文本信息,这样读取信息的时候就可以根据指定的 key 取得对应的 value 数据,资源文件的名称是有要求的,必须以,“*.properties" 作为文件的后缀。 可以利用 java.util.ResourceBundle 类完成。
ResourceBundle 类的常用方法
根据当前默认语言环境,取得资源对象(ResourceBundle ):getBundle()
根据指定的语言环境,取得资源对象(ResourceBundle ):getBundle(String baseName,Locale locale)
根据 key 取得对应的 value 数据(String):getString(Sgring key)
package com.cj; import java.util.ResourceBundle; //info = www.cj.com public class Person { public static void main(String[] args)throws Exception { // 需要自己定义一个Message.properties 文件 //此时的Message.properties 一定要放在CLASSPATH路径下 ResourceBundle resourceBundle = ResourceBundle.getBundle("com.cj.Message"); System.out.println(resourceBundle.getString("info")); } } 运行结果: www.cj.com
其实可以通告占位符在资源文件中采用动态的内容设置,而这就必须已考 java.text.MessageFormat 完成。设置占位符的方法为:public static String format(String pattern,Object...arguments)
package com.cj; import java.text.MessageFormat; import java.util.ResourceBundle; /* * info = 更多内容访问:{0},讲师:{1} * info = \sss\ssss\s\s\s\s{0}\s\s{1} * */ public class Person { public static void main(String[] args)throws Exception { // 需要自己定义一个Message.properties 文件 //此时的Message.properties 一定要放在CLASSPATH路径下 ResourceBundle resourceBundle = ResourceBundle.getBundle("com.cj.Message"); System.out.println(MessageFormat.format(resourceBundle.getString("info"),"www.cj.com","铿")); } }