在正常情况下,需要先有一个类的完整路径引入之后才可以按照固定的格式产生实例话对象,但是在Java中也允许通过一个实例话对象找到一个类的完整信息。那么这就是Class类的功能。
实际上类是Java反射的源头,实际上所谓反射从程序的运行结果来看也很好理解,即可以通过对象的反射求出类的名称。
实例化Class类,获得字节码文件的方法有三种:
第一种:通过forName()方法;第二种:类.class第三种:对象.getClass()
package toto.learn;
class X1{}
publicclass GetClassDemo02 {
publicstaticvoid main(String[] args) {
Class c1=null;//指定泛型
Class c2=null;//指定泛型
Class c3=null;//指定泛型
try{
c1=Class.forName("toto.learn.X");//最常用的形式,这种方式将字节码文件加载到内存中。
}catch(ClassNotFoundException e){
e.printStackTrace();
}
c2 = new X1().getClass();//通过Object类中的方法实例
c3 = X1.class;//通过类class实例化
System.out.println("类名称:"+c1.getName());//得到类的名称
System.out.println("类名称:"+c2.getName());//得到类的名称
System.out.println("类名称:"+c3.getName());//得到类的名称
}
}
通过以上方法获得类名称的方式得到的是包名+类名
如果要想通过Class类本身实例化其他类的对象,则可以使用newInstance()方法,但是必须要保证被实例化的类中必须存在一个无参够造方法。
被实例化对象的类中必须存在无参构造方法,如果不存在的话,则肯定是无法实例化的。
1、 通过Class类中的getConstructors()取得本类中的全部构造方法
2、 向构造方法中传递一个对象数组进去,里面包含了构造方法中所需的各个参数
3、 之后通过Constructor实例化对象。
package org.lxh.demo15.instancedemo;
import java.lang.reflect.Constructor;
publicclass InstanceDemo03 {
publicstaticvoid main(String[] args) {
Class c = null;
try {
c = Class.forName("org.lxh.demo15.instancedemo.Person"); // 声明Class对象
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Person per = null; // 声明Person对象
Constructor cons[] = null; // 声明一个表示构造方法的数组
cons = c.getConstructors(); // 通过反射,取得全部构造
try {// 向构造方法中传递参数,此方法使用可变参数接收,并实例化对象
per = (Person) cons[0].newInstance("李兴华", 30);
} catch (Exception e) { // 因为只有一个构造,所以数组下标为0
e.printStackTrace();
}
System.out.println(per); // 输出对象
}
}
per = (Person)cons[0].newInstance("李兴华", 30); //此处是调用并使用构造方法的部分。
在声明对象数组的时候,必须考虑到构造方法中参数的类型顺序,所以第一个参数的类型为Stirng,第二个参数的类型WieInteger(在使用是可以自动拆箱)
Constructorcons[]=null;//实例化构造方法的数组
Cons =c.getConstructors();//取得全部构造
//向构造方法中传递参数,此方使用可变参数接收,并实例化对象
Per = (Person)cons[0].newInstance(“李兴华”,30);
设置构造方法的参数内容
publicPerson(String name,int age){//通过构造设置属性内容
}
反射的应用
可以使用反射取得实现的全部接口
可以使用反射取得一个类所继承的父类
可以使用反射取得一个类中的全部构造方法
可以使用反射取得一个类中的全部方法
可以使用反射取得一个类中的全部属性
在实际开发中发,以上的程序就是反射应用最多的地方,当然反射机制所提供的功能远不如此,还可以通过反射得到一个类中的完整构造,那么这就要使用到java.lang.reflect包中的一下几个类。
Constructor:表示类中的构造方法
Field:表示类中的属性
Method:表示类中的方法
这三个类都是AccessibleObject类中的子类。
要想取得一个类中所实现的全部接口,则必须使用Class类中的getInterfaces()方法。此方法定义如下:
publicClass[] getInterfaces();
此方法返回一个Class类的对象数组,之后就可以直接利用Class类中的getName()方法输出即可。
通过反射取得实现的全部接口
package org.lxh.demo15;
publicclass GetInterfaceDemo {
publicstaticvoid main(String[] args) {
Class c1 =null;//声明Class对象
try{
c1 = Class.forName("org.lxh.demo15.Person");//实例化Class对象
}catch(ClassNotFoundException e){
e.printStackTrace();
}
Class c[] = c1.getInterfaces();//取得实现的全部接口
for(int i=0;i
System.out.println("实现的接口名称:"+c[i].getName());//输出接口名称
}
}
}
一个类中可以实现多个接口,但是只能继承一个父类,所以如果要想取得一个类的父类,可以直接使用Class类中的getSuperclass()方法。此方法定义如下:
PublicClass getSuperclass()
此方法返回的是Class实例,和之前的得到接口一样,可以通过getName()方法取得名称。
取得构造方法的例子:
package org.lxh.demo15;
import java.lang.reflect.Constructor;//导入反射操作包
publicclass GetConstructorDemo01 {
publicstaticvoid main(String[] args) {
Class c1 = null;//声明Class对象
try{
c1 = Class.forName("org.lxh.demo15.Person");
}catch(ClassNotFoundException e){
e.printStackTrace();
}
Constructor con[]=c1.getConstructors();//得到全部构造方法
for(int i=0;i
System.out.println("构造方法:"+con[i]);//直接打印输出
}
}
}
还原修饰符
在整个Java中对于方法的修饰符使用一定的数字表示出来的,而如果要想把这个数字还原成用户可以看懂的关键字,则必须依靠Modifier类完成,此类定义在java.lang.reflect包中。直接使用Modifer类的一下方法可修饰符:
publicstatic String toString(int mod)
int mo = con[i].getModifiers();
System.out.print(Modifier.toString(mo)+””); //还原权限
getDeclaredMethods()方法,此方法返回一个Method类的对象数组,而如果要想进一步取得方法具体信息,例如:方法的参数,抛出的异常声明等等,则就是必须依靠Method类
再反射操作中同样可以取得一个类中的全部属性,但是在取得属性的时候有以下两种不同的操作:
得到实现的接口或父类中的公共属性:public Field[] getFields() throwsSecurityException
得到本类中自己定义的的全部属性:public Field[] getDeclaredFields() throws SecurityException
如果要使用反射调用类中的方法可以通过Method类完成,操作步骤如下:
1、 通过Class类的getMethod(Stringname,Class…parameterTypes)方法取得一个Method的对象,并设置此方法操作时所需的参数类型。
2、 之后才可以使用invoke进行调用,并向方法中传递要设置的参数。
在Proxy类中的newProxyInstance()方法中,需要一个ClassLoader类的实例,ClassLoader实际上对应的是类加载器,在Java中主要有以下三种类加载器:
BootstrapClassLoader:此加载器采用C++ 编写,一般开发中是看不到的;
ExtensionClassLoader:用来进行扩展类的加载,一般对应的是jrelibext目录中的类;
AppClassLoader:加载classpath指定的类,是最常用使用的一种加载器。
通过forName()加载类两次时,此时的类只加载了一次
如果有以下代码:
Object obj= new VipUser();//这里VipUser是User的子类,它继承了User
if(obj instanceof User){
System.out.println("instanceof判断是一个user");
User user = (User)obj;
}
当使用以上代码中的instanceof来判断时,此时obj就是一个User了,但实际上它不是。它只是User的一个子类。
总结:使用instanceof时主要用来判断是否实现了后面的接口。
if(obj.getClass()==User.class){
System.out.println("getClass判断,是一个user");
User user = (User)obj;
}
而getClass()用于判断对象的准确类型。
在后面用到的类:
package toto.learn1;
publicclass User {
private String name;
private String password;
private String gender;
public User(String name, String password, String gender) {
super();
this.name = name;
this.password = password;
this.gender = gender;
}
public User() {
super();
// TODO Auto-generatedconstructor stub
}
public User(String name, String password) {
super();
this.name = name;
this.password = password;
}
public String getName() {
returnname;
}
publicvoid setName(String name) {
this.name = name;
}
public String getPassword() {
returnpassword;
}
publicvoid setPassword(String password) {
this.password = password;
}
public String getGender() {
returngender;
}
publicvoid setGender(String gender) {
this.gender = gender;
}
publicvoid run(String str, int num){
System.out.println("run");
}
publicvoid run(String name){
System.out.println("hello:"+name);
}
}
以下是关于反射的部分。
package toto.learn1;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
publicclass Demo2 {
/**
* 反射
* @throwsIllegalAccessException
* @throwsInstantiationException
* @throwsNoSuchMethodException
* @throwsSecurityException
*/
publicstaticvoid main(String[] args) throws Exception {
Class clazz = User.class;
//构造方法
//获得构造方法
Constructor[] cons = clazz.getConstructors();
for(Constructor con : cons){
//参数列表,获得的约定是完整的参数类型,包括这种类型坐在的包。
Class[] types = con.getParameterTypes();
System.out.println("参数类型为:");
for(Class type:types){
System.out.println(type.getName()+"...");
}
System.out.println();
}
//获得指定的构造方法 创建对象
try {
Object obj = clazz.newInstance();//默认调用无参的构造方法
System.out.println(obj.getClass());
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
/**参数类型为:
java.lang.String...
java.lang.String...
参数类型为:
java.lang.String...
java.lang.String...
java.lang.String...
class toto.learn1.User*/
System.out.println("-------------------------");
//获得指定的构造方法2 创建对象
Object obj = clazz.newInstance();//默认调用无参的构造方法
Constructor constructor = clazz.getConstructor(String.class,String.class);//此处调用有两个参数的构造方法
//String.class是因为构造方法中有带有年个参数的构造方法的形参都是String类的。
User usr = (User)constructor.newInstance("toto","查看");//传递两个参数。
System.out.println(usr.getName()+" "+usr.getPassword());//结果:toto 查看
//方法
//获得类所有的方法
Method [] methods = clazz.getMethods();
for(Method method:methods){
System.out.println(method.getName());
}
/**通过getMethods()方法获得的类中包含有父类的方法,并且拿到的是共有的方法
* run
run
getName
setName
getPassword
setPassword
getGender
setGender
wait
wait
wait
hashCode
getClass
equals
toString
notify
notifyAll
* */
//如果想获得声明的所有方法,包括非public的,不包括继承的,可以使用一下途径实现
System.out.println("-----------------------");
Method [] methods2 = clazz.getDeclaredMethods();
for(Method method:methods2){
System.out.println(method.getName());
}
/**结果是:
* run
run
getName
setName
getPassword
setPassword
getGender
setGender*/
//获得指定的方法,调用方法
Method runMethod = clazz.getMethod("run", String.class); /**第一个表示要调用那个方法
第二个参数是可变参数,表示调用的润方法的参数类型。*/
//获得了方法后,要执行时,得给出一个对象,在这里要执行的对象是User,故作如下实例
User usr1 = new User();
runMethod.invoke(usr1, "toto");//表示执行usr1对象中的runMethod方法,并且向方法中传递了参数。
/**
* 运行结果:hello:toto 此为User类中run(String name);方法输出的结果,其中name即为toto*/
//属性
//获得对象的属性,先创建一个对象
Object object = new User("toto","123","male");//调用User中的构造方法:
//由于获得对象的私有属性,得获得get方法,故得先获得对象的字节码文件,通过这个文件获得get方法
Class objectClazz = object.getClass();
//获得get方法
Method runMethod2 = clazz.getMethod("getName"); //由于该方法没有参数,故不用在向里面传递参数
//执行此方法,输出参数
Object name = runMethod2.invoke(object);
System.out.println(name);//由于上面传递了name值,故此处返回的结果值为:toto
//获得所有属性
System.out.println("-------------------------------");
//Field[] fields =clazz.getFields();//此方法拿到的是public的属性,由于User类中没有共有的属性
Field[] fields = clazz.getDeclaredFields();//拿到声明的属性
for(Field fd : fields){
System.out.println(fd.getName()+"属性类型: "+fd.getType());
}
/** 结果为:
* name属性类型: class java.lang.String
password属性类型: class java.lang.String
gender属性类型: class java.lang.String*/
//为对象的password属性赋值:
User user2 = new User();
String fieldName = "password";//作为变量来处理的,可以传递任意的属性名称
//getpw是通过反射拿到的属性,其实就是password
Field getpw = user2.getClass().getDeclaredField(fieldName);
//判断属性是否为私有:
int num = getpw.getModifiers();//返回的是整型的值。
if(num == Modifier.PRIVATE){//获得的是
System.out.println("属性是私有的");
//让java虚拟机不检查访问权限
//getpw.setAccessible(true);
}
//System.out.println(getpw);//结果private java.lang.Stringtoto.learn1.User.password
getpw.set(user2, 234567);//前一个参数表示的是为那个对象赋密码。
System.out.println(user2.getPassword());
}
}
l 通过Field类访问对象属性时,需要注意访问权限的问题,若对象
的属性被声明为私有,则无法访问,此时需要打开java语言检查
filed.setAccessible(true);
l Method类代表某个类中的一个成员方法
l 通过invoke方法可以调用Method表示的方法,此时同样需要传递
对象给Method,若被调用的方法为静态,则传入一个null值
l 注意:jdk1.4和jdk1.5的invoke方法的区别:
l Jdk1.5:public Object invoke(Objectobj,Object... args)
l Jdk1.4:public Object invoke(Objectobj,Object[] args)
l 当传入一个数组时,为了向后兼容,虚拟机会优先考虑考虑
调用JDK1.4的方法