API进阶反射

反射

java反射机制

 1. 反射是java中的动态机制,它允许我们在程序运行期间再确定对象的实例化,调用某个方法,操作某个属性
 2. 使得程序的灵活度大大提升,但是同时也带来了更多的资源开销和较低的
 3. 运行效率。
 4. 程序不能过度的依赖反射机制。 

获取Class 类对象(Class的实例)

  1. Class cls = String.class
  2. Class cls = Class.forName("java.lang.String")
Class 类对象
3.  JVM在加载一个类的class文件时,就会同时创建一个Class的实例,使用该实例记录加载的类的一切信息(类名,有哪些属性,哪些方法,哪些构造器等)。
4. 并且每个被JVM加载的类都有且只有一个Class的实例与之对应。
5. 反射的第一步就是获取要操作的类的类对象,以便程序在运行期间得知要操作的类的一切信息,然后对其进行响应的操作


获取一个类的类对象方式有:
1:类名.class 例如:
Class cls = String.class;
Class cls = int.class;
注:基本类型只能通过上述方式获取类对象

2:Class.forName(String className)
使用Class的静态方法forName传入要加载的类的完全限定名(包名.类名),需要抛出异常,例如:
Class cls = Class.forName("java.lang.String")

3:类加载器ClassLoader类加载器形式
Class cls = String.class;
Class cls1 = ArrayList.class;
Class cls2 = Class.forName("java.lang.String");	//处理异常

System.out.println(cls);	//class java.lang.String
System.out.println(cls1);	//class java.util.ArrayList
System.out.println(cls2);	//class java.lang.String

获取类对象的相关信息

获取当前类的完全限定名:

Class cls = String.class;
System.out.println("cls");	//class java.lang.String

String name = cls.getName();
System.out.println(name); //java.lang.String

name = cls.getSimpleName();
System.out.println(name); //String

利用扫描仪获取控制台输入的类的类对象

 public static void main(String[] args) throws ClassNotFoundException {
        Scanner scan = new Scanner(System.in);
        System.out.println("请输入类名:");
        String className = scan.nextLine();
        Class cls = Class.forName(className);
        System.out.println(cls);

    }

获取当前类对象所表示的类的完全限定名

  • String name = cls.getName()
Class cls = Class.forName("java.lang.String");
String name = cls.getName();
System.out.println(name);	//

获取当前类对象的类名(不包含包名)

  • String name = cls.getSimpleName()
Class cls = Class.forName("java.lang.String");
String name = cls.getSimpleName();
System.out.println(name);

获取当前类所在包Package的信息

  • Package pack = cls.getPackage();获取当前类对象所表示的类的包,返回的Package实例表示该包的信息
public static void main(String[] args) throws ClassNotFoundException {
        Class cls = Class.forName("java.lang.String");
        Package pack = cls.getPackage();
        System.out.println("包名"+pack.getName());//java.lang
    }

获取当前类的所有方法(公开方法),也包括从超类继承下来的方法

  • Method[] methods = cls.getMethods()
- Method[] getMethods()
- 获取当前类对象所表示的类中定义的所有公开方法,包含从超类继承下来的方法
- java.lang.reflect.Method类,方法对象
- 该类的每一个实例用于表示某个类中定义的一个方法,通过它可以获取其表示的方法中的相关信息(方法名,参数个数,参数类型,返回值类型等等,并且还可以调用这个方法)

 Method[] methods = cls.getMethods();
        System.out.println(cls.getSimpleName()+":一共有"+methods.length+"个公开方法");
         for(Method method : methods){
            System.out.println(method.getName());
        }
        

反射机制使用无参构造器实例化对象

注意:流不能利用反射机制实例化,因为没有无参构造

  • Object obj = cls.newInstance()
//1获取要实例化的类的类对象
//Class cls = Class.forName("reflect.Person");
Scanner scanner = new Scanner(System.in);
System.out.println("请输入要实例化的类名:");
String className = scanner.nextLine();
Class cls = Class.forName(className);

//2类对象直接提供了可以通过公开的无参构造器实例化的功能
Object obj = cls.newInstance();
System.out.println(obj);

反射机制使用有参构造器实例化对象

  • Constructor c = cls.getConstructor(参数类型1.class,参数类型2.class...)
  • 不传任何参数时获取的仍然是无参构造器
//1获取类对象
Class cls = Class.forName("reflect.Person");
//2获取Person的构造器Person(String,int)
Constructor c = cls.getConstructor(String.class,int.class);
//3实例化时传入构造器要求的实际参数
Object obj = c.newIntance("王五",22);
System.out.println(obj);

练习:使用反射机制实例化10个Person对象,Person对象中的name顺序为:test1–test10,age顺序为:21-30,存入一个集合

public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        List<Person> list = new ArrayList<>();
        Class cls = Class.forName("reflect.Person");
        Constructor c = cls.getConstructor(String.class,int.class);
        
        for(int i =0;i<10;i++){
            String a = "test"+(i+1);
            int b = 21+i;
            Object obj = c.newInstance(a,b);
            System.out.println(obj);
            list.add((Person) obj);
        }
    }

使用反射机制调用无参方法(分两步)

  1. 通过类对象获取要调用的方法:Method method = cls.getMethod(methodName)
  2. 通过获取的方法对象来调用该方法method.invoke(cls.newInstance())
Scanner scanner = new Scanner(System.in);
System.out.println("请输入类名:");
String className = scanner.nextLine();
System.out.println("请输入方法名:");
String methodName = scanner.nextLine();

//1获取类对象
//Class cls = Class.forName("reflect.Person");
Class cls = Class.forName(className);

//2使用无参构造实例化对象
//Object obj = new Person();
Object obj = cls.newInstance();

 //3通过类对象获取要调用的方法
 //Method method = cls.getMethod("sayHello");//获取的是无参的sayHello方法
Method method = cls.getMethod(methodName);

//4通过获取的方法对象来调用该方法
//obj.sayHello()  因为obj指向的是及一个Person对象,因此反射机制可以调用到它的sayHello()
method.invoke(obj);

自动调用reflect.Person类中所有无参且方法名中含有say的方法

  • int parameterCount(),该方法可以返回当前Method对象表示的方法的参数个数
public static void main(String[] args) throws Exception {
        Class cls = Class.forName("reflect.Person");
        Object obj = cls.newInstance();
        //获取所有方法
        Method[] methods = cls.getMethods();
        for (Method method : methods){
            if(method.getName().contains("say")
                    &&
                method.getParameterCount()==0
            ){
                System.out.println("自动调用方法:"+method.getName());
                method.invoke(obj);
            }
        }
    }

使用反射机制调用有参方法

  • Method method = cls.getName("方法名",参数类型.class)
  • method.invoke(cls.newInstance,传参)
public static void main(String[] args) throws Exception {
        Class cls = Class.forName("reflect.Person");
        Object obj = cls.newInstance();
        //doSomeThing(String)
        Method method = cls.getMethod("doSomeThing", String.class);
        method.invoke(obj,"玩游戏");//p.doSomeThing("玩游戏");

        //doSomeThing(String,int)
        Method method1 = cls.getMethod("doSomeThing", String.class,int.class);
        method1.invoke(obj,"作业",5);//p.doSomeThing("作业",5);
    }

获取当前类对象中所有自身定义的方法(包括私有方法,不包括继承超类的方法)

  • Method method = cls.getDeclaredMethod("方法名")获取所有的自身定义方法(包括私有方法,不包括继承超类的方法)
- getMethod(),getMethods()
- 它们都是获取Class所表示的类的所有公开方法,包含从超类继承的
- getDeclaredMethod(),getDeclaredMethods()
- 这两个方法获取的都是Class所表示的类中当前类自身定义的方法。包含私有方法
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        Class cls = Class.forName("reflect.Person");
        Object obj = cls.newInstance();
		
		Method[] methods = cls.getDeclaredMethods();
for(Method method : methods){
    System.out.println(method.getName());//获取所有的自身定义的方法
}
		//获取当前类对象中指定的方法
        Method method = cls.getDeclaredMethod("doSome");
        //强行打开dosome方法的访问权限(此时可以访问私有方法)
        method.setAccessible(true);
        method.invoke(obj);
    }

两个开发中常用的相对路径(都是编译后.class所在包)

当前类所在最外层包的上一级目录

File dir = new File(
				类名.class.getClassLoader().getResource(".").toURI()
        );
File dir = new File(
         ReflectDemo7.class.getClassLoader().getResource(".").toURI()
        );
        System.out.println(dir.getName());

当前类所在的目录

File dir = new File(
				类名.class.getResource(".").toURI()
        );
File dir = new File(
                ReflectDemo7.class.getResource(".").toURI()
        );

        System.out.println(dir.getName());

综合项目:

    public static void main(String[] args) throws Exception {
        /*
            两个开发中常用的相对路径
         */
        //1这里的当前目录表示的是当前ReflectDemo7这个类所在最外层包的上一级目录
//        File dir = new File(
//                ReflectDemo7.class.getClassLoader().getResource(".").toURI()
//        );

        //2这里的当前目录就是当前类所在的目录
        File dir = new File(
                ReflectDemo7.class.getResource(".").toURI()
        );

        System.out.println(dir.getName());

        //通过当前类ReflectDemo7的类对象获取所在的包名
        String packageName = ReflectDemo7.class.getPackage().getName();
        System.out.println("ReflectDemo7类的包名是:"+packageName);

        //获取ReflectDemo7.class文件所在的目录中所有.class文件
        File[] subs = dir.listFiles(f->f.getName().endsWith(".class"));
        for(File sub : subs){
            //获取字节码文件的文件名
            String fileName = sub.getName();
            System.out.println(fileName);
            /*
                由于java命名要求,文件名必须与类名一致,所以我们可以通过文件名得知该字节码
                文件中保存的类的类名
             */
            String className = fileName.substring(0,fileName.indexOf("."));
//            System.out.println("类名:"+className);

            //加载该类的类对象
            Class cls = Class.forName(packageName+"."+className);
            Object obj = cls.newInstance();
//            System.out.println("加载的类为:"+cls.getName());
            //通过类对象获取本类定义的所有方法
            Method[] methods = cls.getDeclaredMethods();
            //遍历每个方法,检查哪个方法是无参的
            for(Method method : methods){
                if(
                        method.getParameterCount()==0
                        &&
                        method.getModifiers() == Modifier.PUBLIC
                ){
                    System.out.println("自动调用"+className+"的方法:"+method.getName());
                    method.invoke(obj);
                }
            }

        }


    }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值