反射知识点梳理

1、首先定义一个Person类,在接下来的案例中会不断用到。

注意:在Person类中定义了无参和全参构造,如果一个类中没有无参也没有全参构造,则系统会默认给这个类添加一个无参构造方法。。

/**
 * 使用当前类测试反射机制
 */
public class Person {
    private String name = "张三";
    private int age = 18;

    public Person(){}

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void sayHello(){
        System.out.println(name+":hello!");
    }
    public void sayHi(){
        System.out.println(name+":Hi!");
    }
    public void watchTV(){
        System.out.println(name+":看电视");
    }
    public void sing(){
        System.out.println(name+":唱歌");
    }

    public void doSomeThing(String something){
        System.out.println(name+"正在做"+something);
    }
    public void doSomeThing(String something,int count){
        for(int i=0;i<count;i++) {
            System.out.println(name + "正在做" + something + i + "次");
        }
    }

    private void dosome(){
        System.out.println("我是Person的私有方法dosome()!!!!!!");
    }


    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

java反射机制:

反射是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:还可以通过类加载器形式完成
         */

        //获取String的类对象
//        Class cls = String.class;
//        Class cls = ArrayList.class;

//        Class cls = Class.forName("java.lang.String");

        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);



        //获取当前类对象所表示的类的完全限定名
        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()+"()");
        }
    }
}

2、使用反射机制实例化对象

public class ReflectDemo2 {
    public static void main(String[] args) throws Exception {
        Object p = new Person();
        System.out.println(p);

        //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类对象直接提供了可以通过公开的无参构造器实例化的功能
//------------Person必须要有无参的构造方法!!!!!!!
        Object obj = cls.newInstance();
        System.out.println(obj);
    }
}

3、使用有参构构造器进行实例化

public class ReflectDemo3 {
    public static void main(String[] args) throws Exception {
        Person p = new Person("李四",22);
        System.out.println(p);

        Class cls = Class.forName("reflect.Person");
        //获取Person的构造器Person(String,int)
//        cls.getConstructor();//不穿任何参数时获取的仍然是无参构造器
        Constructor c = cls.getConstructor(String.class,int.class);
        //new Person("王五",55);
        Object obj = c.newInstance("王五",55);//实例化时要传入构造器要求的实际参数
        System.out.println(obj);
    }
}

4、使用反射机制调用方法

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 methodName = scanner.nextLine();

        //1实例化
//        Class cls = Class.forName("reflect.Person");
        Class cls = Class.forName(className);
        //Object obj = new Person();
        Object obj = cls.newInstance();

        //2调用方法
        //2.1通过类对象获取要调用的方法
//        Method method = cls.getMethod("sayHello");//获取的是无参的sayHello方法
        Method method = cls.getMethod(methodName);
        //2.2通过获取的方法对象来调用该方法
//        obj.sayHello()  因为obj指向的是及一个Person对象,因此反射机制可以调用到它的sayHello()
        method.invoke(obj);


    }
}

5、调用有参方法

public class ReflectDemo5 {
    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);
    }
}

6、在外部调用类中的私有方法

public class ReflectDemo6 {
    public static void main(String[] args) throws Exception {
//        Person p = new Person();
//        p.dosome();

        Class cls = Class.forName("reflect.Person");
        Object obj = cls.newInstance();
        /*
            getMethod(),getMethods()
            它们都是获取Class所表示的类的所有公开方法,包含从超类继承的

            getDeclaredMethod(),getDeclaredMethods()
            这两个方法获取的都是Class所表示的类中当前类自身定义的方法。包含私有方法
         */
//        Method[] methods = cls.getDeclaredMethods();
//        for(Method method : methods){
//            System.out.println(method.getName());
//        }

        Method method = cls.getDeclaredMethod("dosome");
        method.setAccessible(true);//强行打开dosome方法的访问权限
        method.invoke(obj);//p.dosome()



    }
}

7、自动调用与当前类ReflectDemo7所在的同一个包中,自动调用本类自己定义的无参的公开的方法。

public class ReflectDemo7 {
   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(    //getParameterCount:返回此方法的参数个数
                        method.getParameterCount()==0
                        &&
                        //判断方法的权限修饰符是否为public
                        method.getModifiers() == Modifier.PUBLIC
                ){
                    System.out.println("自动调用"+className+"的方法:"+method.getName());
                    method.invoke(obj);
                }
            }

        }


    }
}

8、这两种方法时完全一样的:

 9、下面这两种获得包名的方法得到的结果是不同的:

应该用 : 类名.class.getPackage().getName()的方式获得报名,得到的包名是完整的。

public class Demo {
    public static void main(String[] args) throws URISyntaxException, ClassNotFoundException {
        File dir = new File(
                Demo.class.getResource(".").toURI()
        );

        System.out.println(dir.getName());
        System.out.println(Demo.class.getPackage().getName());
        //            demo.Test
//        Class.forName(dir.getName()+"."+"Test");
        //            reflect.demo.Test
        Class.forName(Demo.class.getPackage().getName()+"."+"Test");
    }
}

10、变长参数:

JDK5之后推出的新特性

该特性用于适应那些传入参数的个数不固定的使用场景,使得使用一个方法就可以解决该问题

而无需穷尽所有参数个数组合的重载。

public class ArgDemo {
    public static void main(String[] args) {
        dosome(1,"one");
        dosome(2,"one","two");
        dosome(1,"one","two","three");
    }
    /**
     * 一个方法里只能有一个变长参数,且必须是最后一个参数
     * @param arg
     */
    public static void dosome(int a,String... arg){
        System.out.println(arg.length);
        System.out.println(Arrays.toString(arg));
    }
}

11、实例化10个Person对象:

注意:这里创建类对象和创建有参构造器都能放在循环之外!

public class Test1 {
    public static void main(String[] args) throws Exception {
        List<Person> list = new ArrayList<>();
        Class cls = Class.forName("reflect.Person");
        Constructor c = cls.getConstructor(String.class,int.class);
        for(int i=1;i<=10;i++){
            Object obj = c.newInstance("text"+i,20+i);
            System.out.println(obj);
            list.add((Person) obj);
        }
        System.out.println(list);
    }
}

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

 * 提示:
 * Method类上定义了一个方法:int parameterCount(),该方法可以返回当前Method对象表示的
 * 方法的参数个数。
 */
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.getParameterCount()==0)&&method.getName().contains("say")){
                System.out.println("自动调用方法:"+method.getName());
                method.invoke(obj);
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值