day35(java反射机制:在程序[运行期间]进行操作)

day35(java反射机制:在程序[运行期间]进行操作)

1.java反射机制

1.定义:

反射是java的动态机制,可以允许我们在程序[运行期间]再确定实例化,调用某个方法,操作某个属性。

  • 反射机制大大的提高了代码的灵活度,但是会有更高的系统开销和较慢的运行效率。

  • 因此反射机制不能被过度的使用

2.获取一个类的类对象

1:类名.class( 注意:基本类型获取类对象只有这一种方式。)

2:Class.forName(String className)

3:还可以通过类加载器形式完成

package reflect;

import java.lang.reflect.Method;
import java.util.Scanner;

/**
* java反射机制
* <p>
* 反射是java的动态机制,可以允许我们在程序[运行期间]再确定实例化,调用某个方法,操作某个属性。
* 反射机制大大的提高了代码的灵活度,但是会有更高的系统开销和较慢的运行效率。
* 因此反射机制不能被过度的使用
*/
public class ReflectDemo1 {
   public static void main(String[] args) throws ClassNotFoundException {
             /*
           类对象 Class的实例
           JVM在加载一个类的class文件时,就会同时创建一个Class的实例,使用该实例记录加载的
           类的一切信息(类名,有哪些属性,哪些方法,哪些构造器等)。并且每个被JVM加载的类都有
           且只有一个Class的实例与之对应。
           反射的第一步就是获取要操作的类的类对象,以便程序在运行期间得知要操作的类的一切信息
           然后对其进行响应的操作。

           获取一个类的类对象的常见方式:
           1:类名.class
           例如:
               Class cls = String.class;
               Class cls = int.class;
               注意:基本类型获取类对象只有这一种方式。

           2:Class.forName(String className)
           例如:
               Class cls = Class.forName("java.lang.String");
               这里传入的类名必须是类的完全限定名,即:包名.类名

           3:还可以通过类加载器形式完成
        */
       Scanner scanner=new Scanner(System.in);
       System.out.println("请输入类名");
       String className=scanner.nextLine();
         /*
           java.util.ArrayList
           java.util.HashMap
           java.io.ObjectInputStream
           java.lang.String
        */
       Class cls=Class.forName(className);
//       Class cls = String.class;
       String name = cls.getName();
       System.out.println(name);
       name = cls.getSimpleName();
       System.out.println(name);

 /*
           Package getPackage()
           获取当前类对象所表示的类的包,返回的Package实例表示该包信息
        */
       Package pack = cls.getPackage();
       String packName = pack.getName();
       System.out.println("包名:" + packName);
       /*
           Method[] getMethods()
           获取当前类对象所表示的类中定义的所有公开方法,包含从超类继承下来的方法

           java.lang.reflect.Method类,方法对象
           该类的每一个实例用于表示某个类中定义的一个方法,通过它可以获取其表示的方法中的
           相关信息(方法名,参数个数,参数类型,返回值类型等等,并且还可以调用这个方法)
        */
       Method[] methods = cls.getMethods();
       for (Method method : methods) {
           System.out.println(method.getName() + "()");
      }
  }
}

3.利用反射机制实例化对象

package reflect;
/**
* 使用反射机制实例化对象
*/
import java.util.Scanner;

public class ReflectDemo2 {
   public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
       Person person=new Person();
       System.out.println(person);
       //1获取要实例化的类的类对象
//       Class cls = Class.forName("reflect.Person");
       System.out.println("请输入类名");
       Scanner scanner=new Scanner(System.in);
       String className=scanner.nextLine();
       Class cls=Class.forName(className);
       //2类对象直接提供了可以通过公开的无参构造器实例化的功能
       Object obj=cls.newInstance();
       System.out.println(obj);
  }
}

4.灵活调用类名,方法名(前提是得有无参函数以及无参方法)

package reflect;

import java.lang.reflect.Method;
import java.util.Scanner;

public class ReflectDemo4 {
   public static void main(String[] args) throws Exception {
       Person p = new Person();
       p.sayHello();
//       类名
       Scanner scanner = new Scanner(System.in);
       System.out.println("请输入类名");
       String className = scanner.nextLine();
//       方法名
       System.out.println("请输入方法名");
       String methods = scanner.nextLine();
//实例化
       Class cls = Class.forName(className);
       Object obj = cls.newInstance();
//调用方法
//       通过类对象获取要调用的方法
       Method method = cls.getMethod(methods);
//       通过获取的方法对象来调用该方法
       method.invoke(obj);
  }
}

5.获取某些类中的某些方法(无参)

package reflect;

import java.lang.reflect.Method;

/**自动调用reflect.Person中所有的无参方法,包含say的方法
* */
public class Test2 {
   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);
          }

      }


  }
}

6.变长参数(有参:传入参数的个数不固定的)

package reflect;

import java.util.Arrays;
/**
* JDK5之后推出了一个特性:变长参数
* 该特性用于适应那些传入参数的个数不固定的使用场景,使得使用一个方法就可以解决该问题,而无需穷尽
* 所有参数个数组合的重载。
*/
public class ArgDemo {
   public static void main(String[] args) {
       dosome(1,"one");
       dosome(2,"one","two");
       dosome(5,"one","three","four","apex" );
  }
   /*一个方法里只能有一个变长参数,且必须是最后一个参数*/
   public static void dosome(int a,String...arg){
       System.out.println(arg.length);
       System.out.println(Arrays.toString(arg));
  }
}

 

7.开发中两种常用的相对路径(编译后的目录)

package reflect;

import java.io.File;

public class ReflectDemo7 {
   public static void main(String[] args) throws Exception {
       /*两个开发中常用的相对路径*/
       /*1.表示ReflectDemo7所在的这个类所在的最外层的上一级目录*/
       File file = new File(
               ReflectDemo7.class.getClassLoader().getResource(".").toURI()
      );
       System.out.println(file.getAbsolutePath());//D:\CGB2202_SE\out\production\CGB2202_SE
       /*当前类所在的目录*/
       File file1 = new File(
               ReflectDemo7.class.getResource(".").toURI()
      );
       System.out.println(file1.getAbsolutePath());//D:\CGB2202_SE\out\production\CGB2202_SE\reflect
  }
}

8.自动调用与当前类ReflectDemo7所在同一个包中,自动调用本类自己定义的无参方法

package reflect;
/**
* 自动调用与当前类ReflectDemo7所在同一个包中,自动调用本类自己定义的无参方法
*/

import java.io.File;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public class ReflectDemo7 {
      /**
    * main方法上的参数String[] args的作用是
    * 在命令行上使用java命令指定当前类时,可以传递参数进来,此时会被main上的String[] args接收
    *
    * 例如:
    * java ReflectDemo7 arg1 arg2 arg3
    * main方法执行后,args数组就有三个元素,对应的就是"arg1","arg2","arg3"
    *
    */
   public static void main(String[] args) throws Exception {
       /*两个开发中常用的相对路径*/
       /*1.表示ReflectDemo7所在的这个类所在的最外层的上一级目录*/
       File file = new File(
               ReflectDemo7.class.getClassLoader().getResource(".").toURI()
      );
       System.out.println(file.getAbsolutePath());//D:\CGB2202_SE\out\production\CGB2202_SE
       /*2.当前类所在的目录*/
       File file1 = new File(
               ReflectDemo7.class.getResource(".").toURI()
      );
       System.out.println(file1.getAbsolutePath());//D:\CGB2202_SE\out\production\CGB2202_SE\reflect
//       通过当前类ReflectDemo7的类对象获取所在的包名
       String packName = ReflectDemo7.class.getPackage().getName();
       System.out.println("ReflectDemo7类的包名是:" + packName);
   //获取ReflectDemo7.class文件所在的目录中所有.class文件
       File[] subs = file1.listFiles(f -> f.getName().endsWith("class"));
       for (File data : subs) {
           //获取字节码文件的文件名
           String fileName = data.getName();
        //   System.out.println(fileName);
             /*
               由于java命名要求,文件名必须与类名一致,所以我们可以通过文件名得知该字节码
               文件中保存的类的类名
            */
           String className = fileName.substring(0, fileName.indexOf("."));
        //   System.out.println("类名:" + className);
//           加载该类的类对象
           Class cls = Class.forName(packName + "." + className);
          // System.out.println("加载的类为:" + cls.getName());


           Object obj = cls.newInstance();
           //通过类对象获取本类定义的所有方法
           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);
              }
          }
      }


  }
}

9.面试题补充(main方法上的参数String[] args的作用)

  • main方法上的参数String[] args的作用是 在命令行上使用java命令指定当前类时,可以传递参数进来,此时会被main上的String[] args接收

  • 例如: java ReflectDemo7 arg1 arg2 arg3 main方法执行后,args数组就有三个元素,对应的就是"arg1","arg2","arg3"

  •  
posted @ 2022-04-18 17:52  约拿小叶  阅读(163)  评论(0编辑  收藏  举报