1. Class 类的使用
1)在面向对象的世界里,万事万物皆对象(在Java中,静态的成员和普通数据类型类不是对象)
类是不是对象呢? 是;
那类是谁的对象呢? 类是 java.lang.Class类的实例对象。
2)这个类怎么表示呢?
class Foo{}
Foo这个类是Class类的一个实例对象;如何表示呢?
任何一个类都是Class的实例对象,这个实例对象有3种表示方式:
① Class c1 = Foo.class;
实际上告诉我们任何一个类都有一个隐含的静态成员class.
② 第2种表达方式,已知该类的对象通过getClass方法
Foo foo1=new Foo();
Class c2=foo1.getClass();
c1、c2表示Foo类的类类型(class type);
万事万物皆对象,类也是对象,是Class类的实例对象,这个对象我们称为该类的类类型。
③ 第3种表示方式
Class c3=null;
try{
c3=Class.forName("packageName.Foo");
}catch{}
我们完全可以通过类的类类型创建该类的对象实例。
Foo foo=(Foo)c3.newInstance();
2. 动态加载类
1)Class.forName("类的全名")
不仅表示了类的类类型,还代表了动态加载类;
编译时记得加载的类是静态加载,运行时加载的类是动态加载。
① new 创建对象是静态加载类,在编译时刻就要加载所有的可能使用到的类
World w=new World();
通过动态加载类可以解决这个问题。
② 动态加载类,在运行时刻加载
Class c=Class.forName(str);
然后通过定义一个标准(接口),用到哪个类就让那个类遵守这个标准(实现接口)就可以了。
OfficeAble oa=(OfficeAble)c.newInstance(); //Word ,Execll都实现这个接口就可以动态加载了
3. 获取方法信息
① 字节码
Class c1=int.class; // int的类类型 ,
Class c2=String.class; //String类的类类型,俗称String 类字节码
Class c3=void.class;
System.out.print(c1.getName()); //输出:int ,c2 -->java.lang.String
System.out.print(c2.getSimpleName()); //不包含包名的
基本数据类型 ,void都有字节码
② 获取方法信息
① 成员变量也是对象,是 java.lang.reflect.Field 类的对象,Field类封装了关于成员变量的操作,
getFields()方法获的是所有的public 的成员变量(包含继承父类的)的信息;
getDeclaredFilds() 获取的是该类自己声明的成员变量的信息,
1)如何获取某个方法
方法的名称和方法的参数列表才能唯一决定某个方法
2)方法反射的操作
method.invoke(对象,参数列表)
A a1=new A();
Class c=a1.getClass();
//2.获取方法 名称和参数列表来决定,getMethod获取的是public的方法
Method m=c.getMethod("print",new Class[]{int.class,int.class});
也可以这样写:
Method m=c.getMethod("print",int.class,int.class);
//方法的反射操作
//a1.print(10,20); 方法的反射操作是用Method对象来进行方法调用,和用a1.print调用的 效果完全相同
//方法如果没有返回值 返回null,有返回值就返回具体的返回值
Object o= m.invoke(a1,new Object[]{10,20}); //以前是用对象来操作方法,反射就是用方法来操作对象
也可以这样写:
Object o= m.invoke(a1,10,20);
6. 通过反射(Class,Method)了解集合泛型的本质
ArrayList list=new ArrayList(); //能放任何类型
ArrayList<String> list1=new ArrayList<String>(); //只能放String类型
// list1.add(10); 是错误的
Class c1=list.getClass();
Class c2=list1.getClass();
System.out.println(c1==c2); //结果为true
// 反射的操作都是编译之后的操作(运行时的操作)
c1==c2结果返回true,说明编译之后集合的泛型是去泛型化的
java中集合的泛型,是防止错误输入的,只在编译阶段有效,绕过编译就无效了
验证:可以通过方法的反射来操作,绕过编译
Method m=c2.getMethod("add",Object.class);
m.invoke(list1,100); //绕过编译操作 就绕过了泛型
System.out.println(list1); 结果显示 int值100加入到list1中了
7. 为什么要用反射?
①基类对任何派生类一无所知,要想使用基类的方法获取派生类的类型等信息,就需要用到反射了。
②很多场合(也许是为了通用性),要在运行期才能够决定需要调用的类型,那样就有必要用反射来取得类型的相关信息了。
1)在面向对象的世界里,万事万物皆对象(在Java中,静态的成员和普通数据类型类不是对象)
类是不是对象呢? 是;
那类是谁的对象呢? 类是 java.lang.Class类的实例对象。
2)这个类怎么表示呢?
class Foo{}
Foo这个类是Class类的一个实例对象;如何表示呢?
任何一个类都是Class的实例对象,这个实例对象有3种表示方式:
① Class c1 = Foo.class;
实际上告诉我们任何一个类都有一个隐含的静态成员class.
② 第2种表达方式,已知该类的对象通过getClass方法
Foo foo1=new Foo();
Class c2=foo1.getClass();
c1、c2表示Foo类的类类型(class type);
万事万物皆对象,类也是对象,是Class类的实例对象,这个对象我们称为该类的类类型。
③ 第3种表示方式
Class c3=null;
try{
c3=Class.forName("packageName.Foo");
}catch{}
我们完全可以通过类的类类型创建该类的对象实例。
Foo foo=(Foo)c3.newInstance();
2. 动态加载类
1)Class.forName("类的全名")
不仅表示了类的类类型,还代表了动态加载类;
编译时记得加载的类是静态加载,运行时加载的类是动态加载。
① new 创建对象是静态加载类,在编译时刻就要加载所有的可能使用到的类
World w=new World();
通过动态加载类可以解决这个问题。
② 动态加载类,在运行时刻加载
Class c=Class.forName(str);
然后通过定义一个标准(接口),用到哪个类就让那个类遵守这个标准(实现接口)就可以了。
OfficeAble oa=(OfficeAble)c.newInstance(); //Word ,Execll都实现这个接口就可以动态加载了
3. 获取方法信息
① 字节码
Class c1=int.class; // int的类类型 ,
Class c2=String.class; //String类的类类型,俗称String 类字节码
Class c3=void.class;
System.out.print(c1.getName()); //输出:int ,c2 -->java.lang.String
System.out.print(c2.getSimpleName()); //不包含包名的
基本数据类型 ,void都有字节码
② 获取方法信息
public class ClassUtils{
//打印类的信息,包括类的成员函数、成员变量
public static void printClassMessage(Object obj){
//要获取类的信息 ,首先要获取类的类类型
Class c=obj.getClass(); //传递的是哪个子类的对象
//获取类的名称
System.out.print("类的名称是:"+c.getName());
/**
* 万事万物皆对象,方法也是对象,是Method类的对象
* 一个成员方法就是一个Method对象,getMethods()方法获取的是所有public的函数
* getDeclaredMethods()获取的是所有该类自己声明的方法,不问访问权限
**/
Method[] ms=c.getMethods();
for(int i=0;i<ms.length;i++){
//得到方法的返回值类型的类类型
Class returnType=ms[i].getReturnType();
System.out.println(returnType.getName+" ");
//得到方法的名称
System.out.print(ms[i].getName()+"(");
//获取参数类型 --->得到的是参数列表的类型的类类型
Class[] paramTypes=ms[i].getParameterTypes();
for(Class class1:paramTypes ){
System.out.println(class1.getName+" ,");
}
System.out.println(")");
}
}
}
4. 获取成员变量构造函数信息① 成员变量也是对象,是 java.lang.reflect.Field 类的对象,Field类封装了关于成员变量的操作,
getFields()方法获的是所有的public 的成员变量(包含继承父类的)的信息;
getDeclaredFilds() 获取的是该类自己声明的成员变量的信息,
Field[] fs=c.getDeclaredFields();
for(Field field:fs){
//得到成员变量的类型的类类型
Class fieldType=field.getType();
String typeName=fieldType.getName();
//得到成员变量的名称
String fieldName=field.getName();
}
② 获取对象的构造函数信息
public static void printConMessage(Object obj){
Class c=obj.getClass();
/***
* 构造函数也是对象
* java.lang.Constructor 中封装了构造函数的信息
* getConstructors获取所有的public的构造函数
* getDeclaredConstructors获取所有的构造函数
**/
Constructor[] cs=c.getDeclaredConstructors();
for(Constructor constructor:cs){
System.out.println(constructor.getName+"(");
Class[] paramTypes=constructor.getParameterTypes();
for(Class class1:paramTypes){
System.out.println(class1.getName+",");
}
System.out.println(")");
}
}
5. 方法的反射1)如何获取某个方法
方法的名称和方法的参数列表才能唯一决定某个方法
2)方法反射的操作
method.invoke(对象,参数列表)
public class A{
public void print(int a,int b){
System.out.println(a+b);
}
public void print(String a,String b){
System.out.println(a+b);
}
}
//① 要获取print(int,int)方法 -->1.要获取一个方法就是获取类信息,那首先要获取类的类类型A a1=new A();
Class c=a1.getClass();
//2.获取方法 名称和参数列表来决定,getMethod获取的是public的方法
Method m=c.getMethod("print",new Class[]{int.class,int.class});
也可以这样写:
Method m=c.getMethod("print",int.class,int.class);
//方法的反射操作
//a1.print(10,20); 方法的反射操作是用Method对象来进行方法调用,和用a1.print调用的 效果完全相同
//方法如果没有返回值 返回null,有返回值就返回具体的返回值
Object o= m.invoke(a1,new Object[]{10,20}); //以前是用对象来操作方法,反射就是用方法来操作对象
也可以这样写:
Object o= m.invoke(a1,10,20);
6. 通过反射(Class,Method)了解集合泛型的本质
ArrayList list=new ArrayList(); //能放任何类型
ArrayList<String> list1=new ArrayList<String>(); //只能放String类型
// list1.add(10); 是错误的
Class c1=list.getClass();
Class c2=list1.getClass();
System.out.println(c1==c2); //结果为true
// 反射的操作都是编译之后的操作(运行时的操作)
c1==c2结果返回true,说明编译之后集合的泛型是去泛型化的
java中集合的泛型,是防止错误输入的,只在编译阶段有效,绕过编译就无效了
验证:可以通过方法的反射来操作,绕过编译
Method m=c2.getMethod("add",Object.class);
m.invoke(list1,100); //绕过编译操作 就绕过了泛型
System.out.println(list1); 结果显示 int值100加入到list1中了
7. 为什么要用反射?
①基类对任何派生类一无所知,要想使用基类的方法获取派生类的类型等信息,就需要用到反射了。
②很多场合(也许是为了通用性),要在运行期才能够决定需要调用的类型,那样就有必要用反射来取得类型的相关信息了。