【Java高级】利用反射获取非静态内部类有参(无参)构造方法 创建内部类对象 调用内部类方法 内部类全限定类名规律 getConstructor getMethod getField getClass

一、直接看如何使用:

(一)通过反射获取非静态内部类有参(无参)构造方法:

1、 获取无参构造方法+创建内部类对象:

Constructor<DogAndStudent.Student> studentConstructor = clazz.getConstructor(DogAndStudent.class);
DogAndStudent.Student studentForTest = (DogAndStudent.Student)studentConstructor.newInstance(new DogAndStudent());

说明:

1、DogAndStudent为外部类,Dog,Student为内部类。
2、利用getConstructor()获取非静态内部类构造方法时需要传入参数【外部类.class】
3、使用newInstance创建非静态内部类对象时需要传入外部类对象

参考:

Java内部类的全限定名规律
Java反射获取内部类的实例

2、获取有参构造方法:

Constructor<DogAndStudent.Student> studentMethod1 = clazz.getDeclaredConstructor(DogAndStudent.class, String.class, Integer.class, String.class, Date.class, dog1.getClass());

说明:

1、比无参构造方法多了所需参数的class,以及在首个参数位置的【父类.class】
2、这个父类.class为什么要写?具体原因我也不知道,某度也没有搜到获取有参数的构造方法的文章,所以是自己摸索的。可以通过getDeclaredConstructors()方法看到如下内容:

//public com.itqidian.cc.Homework_4.DogAndStudent$Student(com.itqidian.cc.Homework_4.DogAndStudent,java.lang.String,java.lang.Integer,java.lang.String,java.util.Date,com.itqidian.cc.Homework_4.DogAndStudent$Dog)

(二)通过反射得到的有参构造器创建非静态内部类对象

DogAndStudent.Student student = studentMethod1.newInstance(new DogAndStudent(), "888", (Integer)18, "18818818888", new Date(), dog1);

或者:

Object[] initargs = {new DogAndStudent(), "888", (Integer)18, "18818818888", new Date(), dog1};
DogAndStudent.Student student = studentMethod1.newInstance(initargs);//也可以

说明:

1、需要在所需要的参数前加上外部类对象new DogAndStudent()

(三)利用反射调用内部类对象的方法(非public及public)

//使用反射获取showInfo方法并调用
Method showInfoMethod = clazz.getMethod("showInfo");
showInfoMethod.invoke(student);
            
//使用反射获取walkAdog方法并调用
Method walkAdogMethod = clazz.getDeclaredMethod("walkAdog");
DogAndStudent.Dog doge = (DogAndStudent.Dog)walkAdogMethod.invoke(student);
Field ageField = doge.getClass().getDeclaredField("age");//★★★★注意要Declared★★★★
Field nameField = doge.getClass().getDeclaredField("name");//★★★★注意要Declared★★★★
ageField.setAccessible(true);
nameField.setAccessible(true);
System.out.println("这只狗芳龄:"+ageField.get(doge));
System.out.println("昵称是:"+nameField.get(doge));

说明:

1、getMethod()能获取public修饰方法。
2、getMethods()能获取所有的public修饰方法(包括继承的方法)。
3、getDeclaredMethod()能获取所有的方法(包括private修饰的)。
4、getDeclaredMethods()能获取所有的方法(但不包括继承的方法)。
5、利用反射调用private修饰的方法或者属性时,要先设置方法或属性的可见性为true:xxxx.setAccessible(true)

二、作业描述:

定义如下的类:

public class Dog{
    private int age;
    private String name;
}
public class Student{
    private String no;
    private Integer age;
    private String tel;
    private Date birth;
    private Dog dog;
    //无参数构造
    public Student(){}
    //全参构造方法
    public Student(.....){....}
    void showInfo(){ 输出所有信息 }
    
    Dog walkAdog(){return dog;}
    
    // setter和getter省略
}

[1]使用反射创建一个Student对象。
[2]给创建的对象的所有属性赋值。
[3]使用反射获取shwoInfo方法,并且执行。
[4]使用反射获取walkAdog方法, 并且执行。将执行结果转换为Dog类型。
[5]利用全参构造方法创建一个Student对象。

三、代码

DogAndStudent类:

/**
 * @author cc
 * @description
 */
package com.itqidian.cc.Homework_4;

import java.util.Date;

public class DogAndStudent {
    public DogAndStudent() {
    }

    public class Dog{
        private int age;
        private String name;
    }
    
    public class Student{
        private String no;
        private Integer age;
        private String tel;
        private Date birth;
        private Dog dog;

        Dog walkAdog(){
            return this.dog;
        }
        
        //无参数构造方法
        public Student() {
        }

        //全参构造方法
        public Student(String no, Integer age, String tel, Date birth, Dog dog) {
            this.no = no;
            this.age = age;
            this.tel = tel;
            this.birth = birth;
            this.dog = dog;
        }

        @Override
        public String toString() {
            return "Student{" +
                    "no='" + no + '\'' +
                    ", age=" + age +
                    ", tel='" + tel + '\'' +
                    ", birth=" + birth +
                    ", dog=" + dog +
                    '}';
        }

        public void showInfo(){
            System.out.println(this.toString());
        }

        // setter和getter
        public String getNo() {
            return no;
        }

        public void setNo(String no) {
            this.no = no;
        }

        public Integer getAge() {
            return age;
        }

        public void setAge(Integer age) {
            this.age = age;
        }

        public String getTel() {
            return tel;
        }

        public void setTel(String tel) {
            this.tel = tel;
        }

        public Date getBirth() {
            return birth;
        }

        public void setBirth(Date birth) {
            this.birth = birth;
        }

        public Dog getDog() {
            return dog;
        }

        public void setDog(Dog dog) {
            this.dog = dog;
        }
    }
}

测试类:

/**
 * @author cc
 * @description
 */
package com.itqidian.cc.Homework_4;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Date;

public class Ex {
    public static void main(String[] args) {
        //[1]使用反射创建一个Student对象。
        //[2]给创建的对象的所有属性赋值。
        //[3]使用反射获取shwoInfo方法,并且执行。
        //[4]使用反射获取walkAdog方法, 并且执行。将执行结果转换为Dog类型。
        //[5]利用全参构造方法创建一个Student对象。
        Class clazz = null;
        Class clazz1 = null;
        Class clazz2 = null;

        //获取Class对象:
        try {
            //法一:通过Class的forName获取 需要知道成员内部类 匿名内部类 局部内部类的命名规则。
            clazz = Class.forName("com.itqidian.cc.Homework_4.DogAndStudent$Student");
            //法二:通过类的静态成员变量class获取
            clazz1 = DogAndStudent.Student.class;
            //法三:通过类对象的getClass()获取
            clazz2 = new DogAndStudent().new Student().getClass();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        System.out.println(clazz == clazz1 && clazz1 == clazz2 && clazz == clazz2);//true
//        System.out.println(clazz.toString());
        
        //创建类对象
//        法一:newInstance();通过无参数的构造方法创建这个类的一个对象。
//          在没有指定泛型的情况下创建的对象类型是Object
//          这个类必须有无参数的构造方法,如果没有就报错。
        try {
//            ★★★★★反射产生非静态内部类的实例化对象
//            ★★★★★要实例化非静态的内部类对象,必须先实例化外部类的对象。
//            ★★★★★而法一直接利用Class对象的newInstance()创建实例对象,括号中无法传入外部类对象
//            ★★★★★所以只能通过Constructor对象的newInstance()创建
            //★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★这里要传入一个外部类★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
//            1、由于成员内部类对象的创建依赖于外部类对象,持有指向外部类对象的引用。
//            所以在反射构造成员内部类的时候一定要通过获取构造器再调用构造器的newInstance方法,
//            其中必须要传入外部类的Class和实例。
// https://blog.csdn.net/ldstartnow/article/details/52782420?spm=1001.2101.3001.6650.3&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-3.pc_relevant_antiscanv2&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-3.pc_relevant_antiscanv2&utm_relevant_index=5
            Constructor<DogAndStudent.Student> studentConstructor = clazz.getConstructor(DogAndStudent.class);
            //★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
            DogAndStudent.Student studentForTest = (DogAndStudent.Student)studentConstructor.newInstance(new DogAndStudent());
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
            e.printStackTrace();
        }
        //法二:我们可以通过Constructor提供的API获取构造方法的访问权限和参数列表信息。
        try {
            //创建dog类对象:dog1
            Class DogClass = Class.forName("com.itqidian.cc.Homework_4.DogAndStudent$Dog");
            Constructor<DogAndStudent.Dog> dogConstructor = DogClass.getDeclaredConstructor(DogAndStudent.class);
            DogAndStudent.Dog dog1 = dogConstructor.newInstance(new DogAndStudent());
            //初始化dog1
            Field dogAge = DogClass.getDeclaredField("age");
            Field dogName = DogClass.getDeclaredField("name");
            dogAge.setAccessible(true);
            dogName.setAccessible(true);
            dogAge.set(dog1, 3);
            dogName.set(dog1, "豆豆");

            //获取Student类的构造方法
            //★★★★★★★★原来如此少了一个父类的Class:com.itqidian.cc.Homework_4.DogAndStudent★★★★★★★★👇
            Constructor<DogAndStudent.Student> studentMethod1 = clazz.getDeclaredConstructor(DogAndStudent.class, String.class, Integer.class, String.class, Date.class, dog1.getClass());

            //★★★★forTest★★★★
//            Constructor<DogAndStudent.Student>[] studentMethod = clazz.getDeclaredConstructors();
            //public com.itqidian.cc.Homework_4.DogAndStudent$Student(com.itqidian.cc.Homework_4.DogAndStudent,java.lang.String,java.lang.Integer,java.lang.String,java.util.Date,com.itqidian.cc.Homework_4.DogAndStudent$Dog)
            //原来如此少了一个父类的Class:com.itqidian.cc.Homework_4.DogAndStudent
//            System.out.println(studentMethod[0]);
                    
            //创建Student对象
            //★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
//            Object[] initargs = {new DogAndStudent(), "888", (Integer)18, "18818818888", new Date(), dog1};
//            DogAndStudent.Student student = studentMethod1.newInstance(initargs);//也可以
            DogAndStudent.Student student = studentMethod1.newInstance(new DogAndStudent(), "888", (Integer)18, "18818818888", new Date(), dog1);

            //使用反射获取showInfo方法并调用
            Method showInfoMethod = clazz.getMethod("showInfo");
            showInfoMethod.invoke(student);
            
            //使用反射获取walkAdog方法并调用
            Method walkAdogMethod = clazz.getDeclaredMethod("walkAdog");
            DogAndStudent.Dog doge = (DogAndStudent.Dog)walkAdogMethod.invoke(student);
            Field ageField = doge.getClass().getDeclaredField("age");//★★★★注意要Declared★★★★
            Field nameField = doge.getClass().getDeclaredField("name");//★★★★注意要Declared★★★★
            ageField.setAccessible(true);
            nameField.setAccessible(true);
            System.out.println("这只狗芳龄:"+ageField.get(doge));
            System.out.println("昵称是:"+nameField.get(doge));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

四、总结

这道题完全可以将两个类拆分成单独的类,但是我自讨苦吃,整了半天才整明白,网上相关也只能搜到如何利用无参构造函数创建非静态内部类对象,自己摸索了很久才解决,以供大家学习。

  • 5
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
1. 反射Class 类的实例表示正在运行的 Java 应用程序中的类和接口; 枚举是一种类,注解(指的是注解Annotation)是一种接口; 每个数组都是 Class字节码类中的一个具体 对象 基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也表示为 Class 对象; 注意 : 1、 Class类 和它的实例的产生: Class的对象是已经存在的类型, 所以不能够直接new一个Class对象出来,是通过Class类中的一个方法获取到的。 例如:通过限定路径类名 2、同一种类型不管通过什么方式得到Class的实例都是相等的;一个类型的字节码对象只有一份,在元空间。 3、Class的实例就看成是Java中我们学过的所有的数据类型在JVM中存在的一种状态(字节码对象) String.class int.class List.class int[].class 1.概念:通过一个限定类名获取字节码文件 2.作用: 1. 提高开发灵活度,提高程序的扩展性 2. 框架(提高开发效率的别人封装好的代码)底层都是使用反射技术。例如:Tomcat、Spring... 3. 缺点:破坏封装性,性能低下(以后,能不用反射技术就不用) 3. 使用:(重点) 1. 获取字节码文件 1. 1.1 Class clazz = Class.forName(限定路径名) (最多使用)默认就是调用下面的方法 1.2 static 类<?> forName(String name, boolean initialize, ClassLoader loader) name:限定路径名 initialize:表示是否初始化,默认是false loader:可以指定一个类加载器加载字节码文件 2. 限定类名.class 3. 对象名.getClass() Class类中方法newInstance():创建当前字节码对象(只能调用无参且是public修饰的构造方法) 2. 根据字节码文件获取构造方法、普通方法、字段等 构造方法 Constructor[] constructors = clazz.getConstructors() 获取public修饰的构造方法数组 Constructor[] constructors = clazz.getDeclaredConstructors() 获取任意权限的所有造方法数组 Constructor constructor = clazz.getConstructor(Class 数字节码)根据数类型获取public修饰的指定的的构造方法 Constructor constructor = clazz.getDeclearConstructor(Class 数字节码) 获取任意访问权限指定的构造方法 //通过构造方法对象去用构造方法创建对象 => 相当于new 一个对象 Object instance = constructor.newInstance(Object 实);//可以创建任意访问权限的有或者无参构造 普通方法 Method[] methods = clazz.getMethods() 获取public修饰的构造方法数组,有父类中的方法 Method[] methods = clazz.getDeclaredMethods() 获取任意访问权限所有造方法数组,并且都是自己的方法 Method method = clazz.getMethod(String methodName,Class... 数字节码)根据方法名和数类型获取指定的的方法 methodName:方法Class:形类型。如果方法没有形,则Class可变数不用写 Method method = clazz.getDeclaredMethod(String methodName,Class... 数字节码)根据方法名和数类型获取指定的的方法 methodName:方法Class:形类型。如果方法没有形,则Class可变数不用写 //通过普通方法对象调用执行方法 method.invoke(Object obj,Object... args); obj:对象。如果是对象的方法,就传入一个当前字节码创建的对象,如果是static方法,则写null args:就是具体实 字段 Field[] fields = clazz.getFields() 获取public修饰的字段 Field[] fields = clazz.getDeclaredFields() 获取任意权限所有字段 Field field = clazz.getDeclaredField(String fieldName) 根据字段名获取任意访问权限的指定字段 Field field = clazz.Field(String fieldName)根据字段名获取public的指定字段 //通过当前的字段对象,给某一个字段赋值取值 field.get(Object obj);//如果是属于static,就传入一个对象,如果是静态的,就传入null obj:对象 field.set(Object obj, Object value);//如果是属于static,就传入一个对象,如果是静态的,就传入null obj:对象 value:值 String getName() 获取限定类名限定,包含包名) Class类中方法 String getSimpleName() 获取类名简称 Class类中方法 Package getPackage() 获取包名 Class类中方法 T newInstance() 根据当前字节码创建对应的对象 Class类中方法 注意: 1. Class类中方法newInstance():创建当前字节码对象(只能调用无参且是public修饰的构造方法) 2. Constructor类中方法newInstance(Object 实);//可以创建任意访问权限的有或者无参构造 3. 私有化方法、字段、构造方法都必须破坏封装才能使用: public void setAccessible(boolean flag) true表示破坏封装,false是不破坏 是哪个private修饰的方法、字段、构造方法需要执行,就需要用这个对象破坏哪一个的封装 例如: //获取cn.itsource.reflect.User字节码文件 Class<?> clazz = Class.forName("cn.itsource.reflect.User"); //获取User的有String构造 Constructor<?> constructor = clazz.getConstructor(String.class); //调用String构造,创建一个User对象 Object newInstance = constructor.newInstance("某文"); //获取private修饰的方法:testPrivate Method method2 = clazz.getDeclaredMethod("testPrivate"); method2.setAccessible(true);//破坏普通方法Method封装 //破坏封装后就可以执行了 Object invoke2 = method2.invoke(newInstance);//没有形就不用写 System.out.println(invoke2); 4. 调用static方法、字段: 例如: Field field = clazz.getDeclaredField("country");//获取任意访问权限静态变量country field.set(null, "中国");//因为字段country是static修饰,所以不用对象调用,就传入null。字段country赋值:中文 Object object = field.get(null);//字段country取值 System.out.println(object); 2. 注解: 1.概念: 就是一个标签,有标签后,就具有某一些标签的特性。 本质就是跟类、接口、枚举平级的新结构 2.作用: 1. 帮助程序员校验代码 2. 可以提高开发效率和程序的扩展性 @Test @Before @After 3. 可以生成文档说明 api 操作步骤: 1.选中项目/代码,右键选中Export 2.输入Javadoc 3.第一个next(可以设置生成文档注释的目录),第二个next,设置字符集 如果是UTF-8编码,且有中文,请输入-encoding UTF-8 -charset UTF-8 4. 勾选一个生成完毕后,直接通过浏览器打开的选项 5. finish 3.使用:(重点) 1. 使用jdk或者别人定义好的标签 @ + 注解的名称 -- 比如@Override ->注解 2. 使用自定义的标签 1.JDK的元注解:就是专门用来声明其他注解的注解 作用:通过元注解了解其他注解的使用特点,还可以自定义注解 2.元注解: 1. @Target @Target 作用 用来限制被修饰注解的使用范围,即注解可以在类的哪些成员上使用 @Target 取值使用ElementType.() 1. CONSTRUCTOR:可以在构造器上使用注解 2. FIELD:可以在字段上使用注解 3. LOCAL_VARIABLE:可以在局部变量上使用注解 4. METHOD:可以在普通方法上使用注解 5. PACKAGE:可以在包上使用注解 6. PARAMETER:可以在数列表上使用注解 7. TYPE:可以在类、接口(包括注解类型) 或enum上使用注解 例如:@Target(ElementType.METHOD)//意味着@Override只能在普通方法上使用 public @interface Override { } 2. @Retention

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_小鹰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值