------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
黑马程序员——java基础---反射
一、反射概念
反射就是把Java类中的各种成分映射相成Java类。
例如:众多的人用一个Person类来表示,那么众多的Java类就用一个Class类来表示。
反射也称为对类的解剖。把类的各个组成部分映射成一个个相应的Java类。
例如:一个类有:成员变量,方法,构造方法,包等等信息。
利用反射技术可以对一个类进行解剖。
其实只要拿到Java类的字节码对应的Class对象,就等于拿到了Java类中的各个成分。
反射的基石就是Class。
Class类
Class类用于表示.class文件,是所有加载进内存的字节码对象的父类。
所以可以通过Class得到运行时的类。
如何得到某个class文件对应的class对象呢?
方法有3种:
1)类名.class 例如,Person.class。
2)对象.getClass() 例如,new Data().getClass()。
3)Class.forName("包名.类名"); 例如,Class.forName("java.lang.String");
代码示例:
class ClassTest
{
public static void main(String...args)throws Exception
{
String str = "黑马程序员";
Class cls1 =String.class;
Class cls2 = str.getClass();
Class cls3 = Class.forName("java.lang.String");
System.out.println(cls1 == cls2);//true
System.out.println(cls2 == cls3);//true
}
}
九个预定义Class实例对象(八大原始类型+void)
Class 类的实例表示正在运行的 Java 应用程序中的类和接口。枚举是一种类,注释是一种接口。
每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该Class 对象。
基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void也表示
为 Class 对象。
注意:
1、int.class == Integer.TYPE。
2、数组类型的Class实例对象Class.isArray()为true。
3、反射并不是Java 5.0的新特性
Java 5.0 代码示例:
class ReflectTest
{
public static void main(String[] args)
{
System.out.println("判断int基本数据类型和Integer的基本数据类型" + "是不是同一个Class对象 结果为:"+(int.class == Integer.TYPE));
System.out.println("判断int.class是不是基本数据类型 结果为:"+int.class.isPrimitive());
System.out.println("判断是不是数组类型 结果为:"+int[].class.isArray());
}
}
一个类中的每个成员都可以用相应的反射API类的一个实例对象来表示,通过调用Class类的方法可以得到
这些实例对象后,得到这些实例对象后有什么用呢?怎么用呢?这正是学习和应用反射的要点。
二、构造方法的反射应用(Coustructor类)
Constructor类的实例对象代表类的一个构造方法。 反射公共,私有和保护的构造方法:
反射公共的需要的方法是:getConstructor();
反射私有的需要的方法是:getDeclaredConstructor();
Constructor对象代表一个构造方法,Constructor对象有的方法:得到构造方法名字,得到所属于的类,
产生实例对象。
得到某个类空参数构造方法,
例:Constructor constructor = Class.forName("java.lang.String").getConstructor();
得到某个类所有的构造方法,
例: Constructor [] constructors= Class.forName("java.lang.String").getConstructors();
得到某一个带参数的构造方法,
例Constructor constructor =Class.forName("java.lang.String").getConstructor(StringBuffer.class);
注意:一个类有多个构造方法,用什么方式可以区分清楚想得到其中的哪个方法呢?
根据参数的个数和类型,例如,Class.getMethod(name,Class... args)中的args参数就代表所要获取
的那个方法的各个参数的类型的列表。重点:参数类型用什么方式表示?用Class实例对象。
利用构造方法创建实例对象:
通常方式:String instance = new String(new StringBuffer("黑马程序员"));
反射方式:String instance = (String)constructor.newInstance("黑马程序员");
调用获得的方法时要用到上面相同类型的实例对象
通过Class类中的newInstance()方法也可创建类的实例,其内部工作原理是先得无参的构造方法,
再用构造方法创建实例对象。
代码示例:
import java.lang.reflect.Constructor;
public class ReflectTest
{
public static void main(String[] args) throws Exception
{
Class clszz = Class.forName("java.lang.String");
Constructor constructor = clszz.getConstructor(StringBuffer.class);
String str = (String) constructor.newInstance(new StringBuffer("黑马程序员"));
char [] chs = str.toCharArray();
for(int x = 0; x
Field类代表反射某个类中的一个成员变量。
问题:得到的Field对象是对应到类上面的成员变量,还是对应到对象上的成员变量?类只有一个,
而该类的实例对象有多个,如果是与对象关联,那关联的是哪个对象呢?所以字段fieldAge 代表的是Age
的定义,而不是具体的Age变量。(注意访问权限的问题)也就是说,定义的是类对象,而非对象的对象。
当我们需要对其操作的时候,需要确定是那个具体的对象。
代码示例:
class Person
{
public int age;
private int height;
public Person(int age,int height)
{
this.age=age;
this.height=height;
}
}
import java.lang.reflect.Field;
class ReflectTest
{
public static void main(String...args) throws Exception
{
Person p = new Person(20,30);
Field fieldAge = p.getClass().getField("age");
System.out.println(fieldAge.get(p));
}
}
射改成"黑马论坛"。
public class ReflectPoint
{
public String str1 = "CSDN社区";
public String str2 = "黑马程序员";
public String str3 = "黑马程序员——新长城";
@Override
public String toString()
{
return str1 + "\n" + str2 + "\n" + str3;
}
}
import java.lang.reflect.Field;
public class ReflectTest
{
public static void main(String[] args) throws Exception
{
ReflectPoint rp = new ReflectPoint();
changeStringValue(rp);
System.out.println(rp);
}
public static void changeStringValue(Object obj) throws Exception
{
Field[] fields = obj.getClass().getFields();
for(Field field : fields)
{
if(field.getType() == String.class)
{
String oldValue = (String)field.get(obj);
String newValue = oldValue.replace("CSDN社区", "黑马论坛" );
field.set(obj, newValue);
}
}
}
}
Method类代表某个类中的一个成员方法 得到类中的某一个方法:
例子:Method charAt =Class.forName("java.lang.String").getMethod("charAt",int.class);
调用方法:
通常方式:System.out.println(str.charAt(1));
反射方式:System.out.println(charAt.invoke(str, 1));
如果传递给Method对象的invoke()方法的第一个参数为null,这有着什么样的意义呢?
说明该Method对象对应的是一个静态方法!
JDK1.4和JDK1.5的invoke方法的区别:
JDK1.4:public Object invoke(Object obj,Object[] args)
JDK1.5:public Object invoke(Object obj,Object... args)
即按JDK1.4的语法,需要将一个数组作为参数传递给invoke方法时,这时它会把一个数组作为一个元素。
这时如果我们要取出其中的元素,需要将数组中的元素通过数组一个个的取出。
所以,调用charAt方法的代码也可以用JDK 1.4改写为 charAt.invoke("str", new Object[]{1})形式。
代码示例:
import java.lang.reflect.Method;
public class ReflectTest
{
public static void main(String[] args) throws Exception
{
String str = "黑马程序员";
Method method = str.getClass().getMethod("charAt", int.class);
//1.4写法。
System.out.println(method.invoke(str,new Object[]{1}));
//1.5写法。
System.out.println(method.invoke(str, 0));
}
}
目标:写一个程序,这个程序能够根据用户提供的类名,去执行该类中的main方法。
import java.lang.reflect.Method;
public class ReflectTest
{
public static void main(String[] args) throws Exception
{
Class clszz = Class.forName(args[0]);
Method main = clszz.getMethod("main", String[].class);
main.invoke(null,"黑马程序员","黑马论坛","CSDN社区");
}
}
代码示例:
import java.lang.reflect.Array;
public class ReflectTest
{
public static void main(String[] args)throwsException
{
String[] arrOne =newString[] {"黑马程序员", "黑马论坛", "CSDN社区"};
String arrTo ="黑马程序员";
printObject(arrOne);
printObject(arrTo);
}
public static void printObject(Object obj)
{
Class clazz = obj.getClass();
if (clazz.isArray())
{
int len = Array.getLength(obj);
for (inti = 0; i < len; i++)
{
System.out.println(Array.get(obj, i));
}
}
else
{
System.out.println(obj);
}
}
}
框架与框架要解决的核心问题
我做房子卖给用户住,由用户自己安装门窗和空调,我做的房子就是框架,用户需要使用我的
框架,把门窗插入进我提供的框架中。框架与工具类有区别,工具类被用户的类调用,而框架则是调用用
户提供的类。
框架要解决的核心问题
我在写框架(房子)时,你这个用户可能还在上小学,还不会写程序呢?我写的框架程序怎样
能调用到你以后写的类(门窗)呢?因为在写才程序时无法知道要被调用的类名,所以,在程序中无法直
接new某个类的实例对象了,而要用反射方式来做。
七、用类加载器的方式管理资源和配置文件
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Collection;
import java.util.Properties;
public class ReflectTest
{
public static void main(String[] args)throws Exception
{
//InputStream is = new FileInputStream("config.properties");
//方式一:采用类加载器进行加载,使用相对路径的方式
/*InputStream is=ReflectTest.class.getClassLoader().
getResourceAsStream("com/itheima/day01/config.properties");*/
//方式二:利用Class方式进行加载,使用相对路径的方式
//InputStream is = ReflectTest.class.getResourceAsStream("config.properties");
//方式三:利用Class方式进行加载,使用绝对路径的方式
InputStream is = ReflectTest.class.getResourceAsStream("/com/itheima/ReSource/config.properties");
Properties p = new Properties();
p.load(is);
is.close();
String className = p.getProperty("className");
Collection collection = (Collection)Class.forName(className) .newInstance();
collection.add("黑马程序员");
collection.add("黑马论坛");
collection.add("CSDN社区");
collection.add("黑马程序员");
System.out.println(collection.size());
}
}