|
十二章 反射与注解
一、加载类
在一个程序中可能会用到许多类,一般在使用时才会加载它们,而非程序启动时。
想要显式加载类的话就要借助一个叫Class的类,它可以代表一个程序运行所加载的类或接口实例。枚举、数组和基本Java数据类型都可以表示为Class对象,动态加载类的操作也可以通过它完成。
(1)通过Class对象取得类基本信息
类Class提供了许多方法,通过这些方法可以获得一个类的详细信息,也可以通过这些方法产生一个类的实例对象,然后执行对象中的方法。
方法 | 功能 |
Package getPackage() | 获取类所在的包 |
String getName | 获取当前对象所表示的实例名称 |
Field[] getDeclaredFields() | 获取当前对象所包含的公共字段(不含继承字段) |
Field[] getFields() | 获取当前对象的公共字段(包含继承公共字段) |
Method[] getDeclaredMethods() | 获取当前对象所包含的方法(不含继承方法) |
Method[] getMethods() | 获取当前对象所包含的公共方法(包含继承的公共方法) |
getModifiers | 获得方法的访问修饰符 |
getClass |
|
import java.lang.reflect.*;
class MyClass
{
public String myName;
public int age;
public String sayHello(String name)
{
String ret = "Hello"+name+"My name is"+myName;
return ret;
}
public String introduce()
{
String ret = "My name is "+ myName +"my age is"+age;
return ret;
}
}
public class TestGetClassInfo {
public static void main(String[] args)
{
Class cls = MyClass.class;
System.out.println("类名:"+cls.getName());
System.out.println();
System.out.println("数据成员信息:");
Field[] fd = cls.getDeclaredFields();
for(int i = 0;i<fd.length;i++)
{
System.out.print(fd[i]+" ");
}
System.out.println();
System.out.println("获取方法成员信息:");
Method[] mt = cls.getDeclaredMethods();
for(int i = 0;i < mt.length; i++)
{
System.out.println(mt[i].getReturnType());
System.out.println(" ");
System.out.println(mt[i].getName());
System.out.println("(");
Class[] parac = mt[i].getParameterTypes();//因为存储到Class[]里面本是要存基本类型名的,这里是不是将基本类型名称转为字符串存储?
for(int j = 0 ;j< parac.length;j++)
{
if(j>0)
{
System.out.println(",");
}
System.out.println(parac[j].getName());
System.out.println(")");
}
System.out.println("方法信息整合:"+mt[i]);
}
}
}
(2)类的加载时机
import java.util.*;
class LoadClass
{
public static void fun()//静态方法在载入时启动
{
System.out.println("Loading....");
}
public LoadClass()//构造函数在实例化时启动
{
System.out.println("Instantializing....");
}
}
public class TestLoad {
public static void main(String[] args)
{
System.out.println("code1:");
System.out.println("声明LoadClass");
LoadClass lc;
System.out.println("实例化LoadClass");
lc = new LoadClass();
System.out.println("code2:");
System.out.println("通过class常量获得LoadedClass对象");
Class c = LoadClass.class;
System.out.println("code3:");
System.out.println("用LoadClass实例调用getClasss方法");
lc.getClass();//调用对象的方法也不会导致载入。
}
}
(3)使用Class.ForName加载类
import java.lang.reflect.*;
import java.util.Scanner;
public class TestClassForName {
public static void main(String[] args)
{
try
{
String str = new Scanner(System.in).next();
Class cls = Class.forName(str);//Class cls = Class.forName(args[0]);这个本应表示本类的类名,但是eclipse,总之只有在CMD环境下 应该这样运行:java test.Test 参数 貌似才可以
System.out.println("class name:"+cls.getName());
System.out.println("当前类是数组:"+cls.isArray());
System.out.println("当前类是枚举:"+cls.isEnum());
System.out.println("当前类是接口:"+cls.isInterface());
}
catch(ArrayIndexOutOfBoundsException aoe)
{
System.out.println(aoe);
}
catch(ClassNotFoundException cnfe)
{
System.out.println(cnfe);
}
}
二、使用反射生成与操作对象
(1)生成对象
要想调用类的非静态方法或者访问其非静态数据需要先生成指定类的对象。
动态生成对象有两种情况:1知道类的类型;2对类的类型及其内部的方法和数据成员不知情。
下面的代码是在知情的情况下。第二种情况见调用方法(第二小节)
import java.util.Scanner;
interface MyInterface
{
void say();
}
class MyClass1 implements MyInterface
{
public void say()
{
System.out.print("i am classone!");
}
}
class MyClass2 implements MyInterface
{
public void say()
{
System.out.print("i am classtwo!");
}
}
public class Test {
public static void main(String[] args)
{
try
{
String str = new Scanner(System.in).next();
Class cls = Class.forName(str);
MyInterface mf = (MyInterface)cls.newInstance();//如果是用这个方式产生对象然后再赋值的话就会需要进行强制类型转换,必须保证给出的参数和强制转换后的类型是相容的,否则会产生异常
mf.say();
}
catch(ArrayIndexOutOfBoundsException ioe)//程序没有给出参数,会使得使用args[0]时产生数组越界的异常。主要针对这句代码:(但是我不知道怎么在eclipse的环境下给出参数)Class cls = Class.forName(args[0]);所以改成上面那句了。
{
System.out.println(ioe);
}
catch(ClassNotFoundException ioe)//没有找到由参数指定的类时产生的异常
{
System.out.println(ioe);
}
catch(InstantiationException ioe)//调用newInstance实例化对象失败时产生的异常
{
System.out.println(ioe);
}
catch(IllegalAccessException ioe)//参数指定的类或者这个类的无参数的构造器是不可访问时会产生的异常
{
System.out.println(ioe);
}
}
}
(2)调用方法
如果是不知道类的类型则先要用newInstance取得类实例,然后通过getMethod取得特定的方法对象,最后使用方法对象的invoke方法完成对被动态载入的类的相应方法的调用。
类 | 方法 | 功能 |
Class | getMethod(方法名,(参数类型列表)) | 取得有指定方法的对象 |
Method | invoke(指定一个对象,调用方法时所需要的参数)(如果Method对象所代表的底层方法是一个静态方法,则第一个参数为null) | 可以令当前方法对象所代表的底层方法执行 |
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Scanner;
interface MyInterface
{
void say();
}
class MyClass1 implements MyInterface
{
public void say()
{
System.out.print("i am classone!");
}
}
class MyClass2 implements MyInterface
{
public void say()
{
System.out.print("i am classone!");
}
public void say(String name,int age)
{
System.out.print("i am "+name+"in classtwo! and age is"+age);
}
}
public class Test {
public static void main(String[] args)
{
try
{
String str = new Scanner(System.in).next();
Class<?> cls = Class.forName(str);//问号是代表位置类型
Object obj = cls.newInstance();
Class[] para ={String.class,int.class};//方法参数列表,这里表示没有参数
Method m = cls.getMethod("say", para);
Object[] arg = {"Tom",24};
m.invoke(obj, arg);
}
catch(ArrayIndexOutOfBoundsException ioe)//程序没有给出参数,会使得使用args[0]时产生数组越界的异常。主要针对这句代码:(但是我不知道怎么在eclipse的环境下给出参数)Class cls = Class.forName(args[0]);所以改成上面那句了。
{
System.out.println(ioe);
}
catch(ClassNotFoundException ioe)//没有找到由参数指定的类时产生的异常
{
System.out.println(ioe);
}
catch(InstantiationException ioe)//调用newInstance实例化对象失败时产生的异常
{
System.out.println(ioe);
}
catch(IllegalAccessException ioe)//参数指定的类或者这个类的无参数的构造器是不可访问时会产生的异常
{
System.out.println(ioe);
}
catch(InvocationTargetException it)
{
System.out.println(it);
}
catch( NoSuchMethodException nsm)
{
System.out.println(nsm);
}
}
}
(3)访问数据成员
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Scanner;
class PersonInfo
{
public String name;
public int age;
public void say()
{
System.out.println("名字是:"+this.name+" 年龄是:"+this.age);
}
}
public class TestInfo {
public static void main(String[] args)
{
try
{
String str = new Scanner(System.in).next();
Class<?> cls = Class.forName(str);
Object obj = cls.newInstance();
Field fd1 = cls.getField("name");
fd1.set(obj, "Tom");
Field fd2 = cls.getField("age");
fd2.set(obj,24);
Class[] para =null;
Method m = cls.getMethod("say", para);
Object[] arg = null;
m.invoke(obj, arg);
}
catch(ArrayIndexOutOfBoundsException ioe)
{
System.out.println(ioe);
}
catch(ClassNotFoundException ioe)//没有找到由参数指定的类时产生的异常
{
System.out.println(ioe);
}
catch(InstantiationException ioe)//调用newInstance实例化对象失败时产生的异常
{
System.out.println(ioe);
}
catch(IllegalAccessException ioe)//参数指定的类或者这个类的无参数的构造器是不可访问时会产生的异常
{
System.out.println(ioe);
}
catch(InvocationTargetException it)
{
System.out.println(it);
}
catch( NoSuchMethodException nsm)
{
System.out.println(nsm);
}
catch(NoSuchFieldException nofe)
{
System.out.println(nofe);
}
}
}
三、注解
(1)说明重写父类---@Override
告诉编译器,它下面的方法是一个覆盖方法
(2)说明不建议使用---@Deprecated
如果调用了一个过时的方法编译器会给出警告,如果想让别人在调用某个自定义的方法也有警告信息的话可以在方法前面加上该注解
(3)抑制编译警告---@SuppressWarnig
该注释告诉编译器那些警告是可以容忍的,在编译时不必报错。在报出可以忽略警告的方法上一行添加一个
@SuppressWarnig(value={“unchecked”})或
@SuppressWarnig(“unchecked”)
告诉编译器同时忽略unchecked和deprecation的警告信息。
@SuppressWarnings(value={"unchecked", "deprecation"})
等同于@SuppressWarnings("unchecked", "deprecation")
该注解支持同时指定多个警告,此时只需在花括号中列出多个警告,警告之间用逗号隔开。
小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)
GMT+8, 2024-5-17 10:05
Powered by Discuz! X3.4
© 2001-2023 Discuz! Team.