一、我们来讲Class类的使用。首先我们要理解两个方面
1. 理解类类型。类类型实际上就是类的类,什么意思,我们知道万事万物皆对象,那么类也是对象,那么这个类的对象是什么谁呢,是Class。创建类类型有三种方式:
a. 直接调用类的class()方法如:
Class c1 = Foo.class;
b. 用对象的getClass()方法。如:
Foo foo = new Foo();
Class c2 = foo.getClass();
c. 使用Class类的 forName()方法
Class c3 = null;
c3=Class.forName("test.Foo"); //注意要有try catch ,此处忽略
2. 理解如何通过类类型创建对象
try {
Foo foo = (Foo)c1.newInstance();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
这里使用 Foo foo = (Foo)c1.newInstance();来创建对象
二、如何理解Class加载类,即Class.forName
上一节我们讲到了创建类类型的三种方式,第三种是通过Class的加载类实现的。那么怎么理解呢?
Class.forName("类的全称");
a. 不仅表示了类的类类型,还代表了动态加载类。
b. 在运行时加载。怎么理解在运行时加载呢?在我们的IDE工具下,我们不能分清哪是编译哪是运行时,因为工具已经帮你做了。
c. 编译时刻加载类是静态加载类,运行时刻加载类是动态加载类
举例,加入有如下程序:
class Office
{
public static void main(String[] args)
{
if("Word".equals(args[0])){
//System.out.println("word")
Word word = new Word();
word.start();
}
if(args[0].equals("exel")){
//system.out.println("word")
Exel exel = new Exel();
exel.start();
}
}
}
class Word
{
public void start(){
System.out.println("word start");
}
}
分析:我们说首先编译是不能通过的,因为没有Exel类。但是,假如一个软件只需要Word启动呢,那就不好办了。运用编译加载的缺点是:只要有一个出错,则都不能通过使用,而我们在实际
使用过程中,当然是希望Word能用,而当使用Exel时才给我们报错的。也就是说,如果我们的软件如果有100个功能,加入一个有问题,整个功能就会有问题。
而动态加载可以解决这个问题。如改成下面的程序:
class Office
{
public static void main(String[] args)
{
try
{
Class c = Class.forName(args[0]);
//通过类类型创建对象,这里有个问题:即要求根据输入参数创建类,不能写死。为此我们要用到接口,即符合这个标准的类我才认为你是Office的一个功能。(很重要,接口的重要作用)
Ioffice ioffice =(Ioffice)c.newInstance();
ioffice.start();
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
interface Ioffice
{
public void start();
}
class Word implements Ioffice
{
public void start(){
System.out.println("word start");
}
}
class Exel implements Ioffice
{
public void start(){
System.out.println("exel start");
}
}
结论:以后功能型的创建,尽量使用动态加载三、通过反射获取方法信息(属性,方法,构造方法)
写一个方法,获取类的全部信息
public class DemoUtil {
/**
* 打印类的信息,包括类的成员函数,成员变量
* @param obj
*/
public static void printClassInfo(Object obj){
Class c = obj.getClass();// 它是native方法,JNI,这中方法用Java语言来声明,用C语音实现
//传递的是哪个子类的对象,c就是该子类的类类型
//1.获取类的名称
System.out.println("类的名称是:"+c.getName());
//2.获取类的方法
Method[] methods= c.getMethods(); //获取类中所有public的函数,包括父类继承而来的
methods= c.getDeclaredMethods();//获取所有该类的自己声明的方法,不问访问权限
for(int i=0;i<methods.length;i++){
//2.1 得到返回值类的类类型
Class returnType=methods[i].getReturnType();
System.out.println(returnType.getName()+" ");
//2.2 得到方法名称
String methodName=methods[i].getName();
System.out.println(methodName+" ");
//2.3 得到方法参数类型---》得到的是参数列表类型的类类型
Class[] paramTypes=methods[i].getParameterTypes();
for(Class class1 : paramTypes){
System.out.print(class1.getName()+",");
}
}
//3.获取类的属性,包含所有访问权限的属性,public private proteced
System.out.println();
for(Field f:c.getDeclaredFields()){
System.out.println(f);
}
//4. 获取构造函数
for(Constructor f:c.getConstructors()){
System.out.println(f);
}
}
public static void main(String[] args) {
//Demo demo = new Demo();
DemoUtil.printClassInfo(new String());
}
}
class Demo{
public int a=0;
private String b="jkj";
protected int c=10;
public void starts(int a,int b,String d) {
System.out.println("XXXX");
}
}
四、 方法的反射
1.如何获取某个方法
获取 方法名称和方法参数列表才能唯一决定某个方法
2. 方法反射的操作
method.invoke(对象,参数列表)
如下代码:
method m =c.getMethod("print",new Class[]{int.class,int.class});
或者: method m =c.getMethod("print",int.class,int.class);
//调用print方法 a1.print(10,20)
Object o =m.invoke(a1,new Object[]{10,20})
Object o =m.invoke(a1,10,20)
五、通过Class,Method 认识泛型的本质
ArrayList list = new ArrayList<>();
ArrayList<String> list1=new ArrayList<String>();
//list1.add(100);在这里是出错的。因为类型不一致,但是在后面运用反射是可以的。
Class c1=list.getClass();
Class c2=list1.getClass();
System.out.println(c1==c2); //为true,说明编程之后,集合的泛型是去泛型化的。
//Java中集合的泛型,是防止错误输入的,只在编译阶段有效。
//验证:我们可以通过方法的反射来操作,绕过编译
try {
Method m = c1.getMethod("add", Object.class);
m.invoke(list1, 100);
System.out.println(list1.size());//大小为1,说明加进去了
} catch (Exception e) {
e.printStackTrace();
}
总结:定义:泛型只是编译时的概念,是供编译器进行语法检查用的。所谓泛型,就是在定义(类型的定义,方法的定义,形式参数的定义,成员变量的定义等等)的时候,指定它为通用类型,也就是数据类型可以是任意的类型,如List<?> list = null,具体调用时,要将通用类型转换成指定的类型。泛型提高了大型程序的类型安全和可维护性。