Java——Class对象(反射及其应用)

定义

java.lang.Class类型:
所有的Java类型(包括基本数据类型、引用数据类型、void)
被加载到内存后,或者是编译器自动编译生成的class字节码,最终都会用一个Class对象来表示。 即,所有的Java类型,在内存中都表示为一个Class对象。

如何获取Class对象

(1)类名.class

(1)类型名.class
优点:最简洁
缺点:要求编译期这个类型就要存在

@Test
	public void test01(){
		Class c1 = int.class;//基本数据类型
		
		Class c2 = void.class;//特殊的空类型
		
		Class c3 = String.class;//系统定义的类类型
		Class c4 = TestClass.class;//自定义的类的类类型
		
		Class c5 = Serializable.class;//接口类型
		Class c6 = ElementType.class;//枚举类型    
		Class c7 = Override.class;//注解类型
		
		Class c8 = int[].class;		
//		Class c9 = Student.class;//错误的,因为编译期间不存在
		
	}

(2)对象.getClass()

这个方法在java.lang.Object类型中声明的,返回对象的运行时类型
适用于:你的先有对象,适用于引用数据类型

@Test
	public void test02(){
		Class c2 = String.class;
		Class c1 = "".getClass();
		
		System.out.println(c1 == c2);//true
	}

(3)Class.forName(“类型全名称”)

类型全名称:包.类名
优势:这个类型可以在编译期间未知,这个类名称可以在代码中出现,也可以配置在配置文件中,或者键盘输入等方式来指定。

@Test
	public void test03() throws ClassNotFoundException{
		Class c2 = String.class;
		Class c1 = "".getClass();		
		Class c3 = Class.forName("java.lang.String");		
		System.out.println(c1 == c2);//true
		System.out.println(c1 == c3);//true		
	}

(4)类加载器对象.loadClass(“类型全名称”)

一般都是用在自定义类加载器对象去加载指定路径下的类

 @Test
    public void test05() throws ClassNotFoundException {
        Class c = TestClass.class;
        ClassLoader loader = c.getClassLoader();

        Class c2 = loader.loadClass("main.Employee");
        Class c3 = Employee.class;
        System.out.println(c2 == c3);//true
    }

反射

声明类 --> 创建对象

反射: 获取Class对象 --> 创建对象

Java是静态语言,如果没有反射,Java必须在编译期间确定所有类型。Student stu= new Student(); 为了使Java也支持动态——在运行期间,确定某个类型的功能,才引入了反射机制。(只是延迟了确定对象的类型)

反射的应用

运行时获取任意类型的详细信息

以扩展包下的CustommizedClass类为例演示:
在这里插入图片描述
在这里插入图片描述

public class TestClass {
    private Class clazz;

    @Before
    public void test01() throws Exception {
        //如果这个类名是在配置文件中,先获取类名
        Properties pro = new Properties();
        pro.load(TestClass.class.getClassLoader().getResourceAsStream("demo.txt"));
        String className = pro.getProperty("className");//key就是demo.txt文件中=左边的属性名

        //(1)获取这个类的Class对象
        clazz = Class.forName(className);
        System.out.println(clazz);//class javaextra.guochao.demo.CustomizedClass
    }

    @Test
    public void test02() {
        //(2)获取类的详细信息
        // clazz代表class javaextra.guochao.demo.CustomizedClass这个类

        //获取包名
        Package pkg = clazz.getPackage();
        System.out.println("包名:" + pkg.getName());//包名:javaextra.guochao.demo

        //获取类名
        System.out.println("类名:" + clazz.getName());//类名:javaextra.guochao.demo.CustomizedClass

        //类的修饰符
        int mod = clazz.getModifiers();
        System.out.println("修饰符的值:" + mod);//修饰符的值:1
        System.out.println("修饰符:" + Modifier.toString(mod));//修饰符:public

        //获取父类
        Class sc = clazz.getSuperclass();
        System.out.println("父类的名称:" + sc.getName());//父类的名称:java.lang.Object

        //获取接口,因为接口可能有多个,所以用数组表示
        Class[] interfaces = clazz.getInterfaces();
        System.out.println("父接口们:");
        for (Class inter :
                interfaces) {
            System.out.println(inter.getName());//父接口们:
        }

        //每一个属性就是一个Field的对象
        /*
         * (1)Field[] getFields() 得到所有公共的属性
         * (2)Field[] getDeclaredFields() 得到所有声明的属性
         */
        Field[] fields = clazz.getDeclaredFields();
        int count = 0;
        for (Field field : fields) {
            count++;
            int fMod = field.getModifiers();
            System.out.print(count + ":属性的修饰符:" + Modifier.toString(fMod));

            System.out.print(count + ":属性的数据类型:" + field.getType().getName());

            System.out.println(count + ":属性的名称:" + field.getName());
        }

        /*
         * Constructor[]  getConstructors():得到所有的公共的构造器
         * Constructor[]  getDeclaredConstructors()():得到所有的声明的构造器
         */
        count = 0;
        Constructor[] constructors = clazz.getDeclaredConstructors();
        for (Constructor constructor : constructors) {
            count++;
            int cMod = constructor.getModifiers();
            System.out.println(count + ":构造器的修饰符:" + Modifier.toString(cMod));
            System.out.println(count + ":构造器的名称:" + constructor.getName());
            Class[] parameterTypes = constructor.getParameterTypes();
            System.out.println(count + ":构造器的形参列表:" + Arrays.toString(parameterTypes));
        }

        /* (1)Method[] getMethods(); 得到所有公共的方法
         * (2)Method[] getDeclaredMethods(); 得到所有声明的方法
         */
        count=0;
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method method : declaredMethods) {
            count++;
            int mMod = method.getModifiers();
            System.out.println(count + ":方法的修饰符:" + Modifier.toString(mMod));
            System.out.println(count +":方法的返回值类型:" + method.getReturnType());
            System.out.println(count + ":方法的名称:" + method.getName());
            System.out.print(count + ":抛出的异常类型们:");
            Class<?>[] exceptionTypes = method.getExceptionTypes();
            System.out.println(Arrays.toString(exceptionTypes));
            Class[] parameterTypes = method.getParameterTypes();
            System.out.println(count + ":方法的形参列表:" + Arrays.toString(parameterTypes));
        }
    }
}

运行期间创建任意引用数据类型的对象

方式一:使用Class对象直接new对象(通过无参构造创建)
步骤:
(1)获取某个类型的Class对象
(2)通过Class对象来创建这个Class所代表的类型的对象

 @Test
    public void test01() throws ClassNotFoundException, InstantiationException, IllegalAccessException{
        //(1)获取某个类型的Class对象
        Class clazz = Class.forName("javaextra.guochao.demo.CustomizedClass");
        //(2)创建对象
        //obj的编译时类型,Object类型
        //obj的运行时类型是CustomizedClass类型
        Object obj = clazz.newInstance();//这里的newInstance()没有参数,因为它是用无参构造创建实例
        System.out.println(obj);//CustomizedClass [id=0, info=null, num=0]
    }

缺点:如果这个类没有无参构造,则会报错: java.lang.InstantiationException: javaextra.guochao.demo.CustomizedClass
Caused by: java.lang.NoSuchMethodException: javaextra.guochao.demo.CustomizedClass.< init >()

建议:
(1)创建对象方便
(2)继承时方便
子类构造器默认调用父类的无参构造
(3)反射创建方便

方式二:通过Class对象先获取有参构造,然后再创建对象
步骤:
(1)获取某个类型的Class对象
(2)通过Class对象来获取Constructor对象
(2)通过Constructor对象来创建这个Class所代表的类型的对象

 @Test
    public void test02() throws Exception{
        //(1)获取某个类型的Class对象
        Class clazz = Class.forName("javaextra.guochao.demo.CustomizedClass");

        /*
         * (1)Constructor clazz.getConstructor(Class<?>... parameterTypes)某个(public)公共的构造器
         * (2)Constructor clazz.getDeclaredConstructor(Class<?>... parameterTypes)某个声明的构造器(所有权限)
         * 一个类中可能存在多个构造器,但是多个构造器重载的话,形参列表一定不一样,所以通过形参列表就可以唯一的定位到一个构造器
         * 如果Class<?>... parameterTypes,一个都不传,即获取无参构造
         */
        //(2)获取有参构造对象
        Constructor c = clazz.getDeclaredConstructor(int.class,String.class,int.class);

         //如果是private构造器,也可以创造对象
        c.setAccessible(true);

        //(3)通过Constructor对象来创建实例对象
        Object obj = c.newInstance(1,"郭超",10);//这里的newInstance(实参列表),因为它用有参构造创建对象
        System.out.println(obj);//CustomizedClass [id=1, info=郭超, num=10]
    }

运行时设置或获取属性的值

@Test
    public void test01() throws Exception {
        //(1)获取某个类型的Class对象
        Class clazz = Class.forName("javaextra.guochao.demo.CustomizedClass");
        //(2)创建对象
        //obj的编译时类型,Object类型
        //obj的运行时类型是CustomizedClass类型
        Object obj = clazz.newInstance();//通过无参构造创建CustomizedClass的一个对象obj
        Object obj2 = clazz.newInstance();//创建另一个对象 obj2
        System.out.println(obj);//CustomizedClass [id=0, info=null, num=0]

        //(3)为属性赋值
        //①获取id属性的Field对象
        Field idField = clazz.getDeclaredField("id");
        //设置id属性可访问,因为id为private
        idField.setAccessible(true);
        //②为id属性赋值
        //在为属性赋值时,要说为哪个对象的属性赋值
        idField.set(obj, 3);
        System.out.println(obj);//CustomizedClass [id=3, info=null, num=0]

        //(5)获取num属性的值
        Field numfield= clazz.getDeclaredField("num");
        numfield.setAccessible(true);
        Object value= numfield.get(obj);//获取obj对象的num属性值
        System.out.println(value);//0
    }

运行时调用对象的任意方法

@Test
    public  void  test09() throws Exception{
        Class clazz =Class.forName("javaextra.guochao.demo.CustomizedClass");

        //调用非静态方法:setinfo
        //一个类中方法是可能重载,如何定位到某一个方法 :   方法名 + 形参列表
         Method method=clazz.getMethod("setInfo",String.class);
         Object obj = clazz.newInstance();//非静态方法需先创建对象调用
         method.invoke(obj, "郭超");
         System.out.println(obj);

        //调用静态方法 :public static void test(int a)
        Method testMethod = clazz.getDeclaredMethod("test", int.class);
        testMethod.invoke(null, 10);//如果obj位置传入null,表示调用静态方法
    }

在运行时读取某个类的泛型实参

步骤:
(1)获取Class对象
(2)获取泛型父类
Type type = clazz.getGenericSuperclass();
ParameterizedType type = (ParameterizedType) clazz.getGenericSuperclass();
(3)获取类型实参

Type:代表Java的所有类型
(1)Class:代表的是普通的类型,没有泛型信息的
(2)ParameterizedType:参数化类型 例如:Father<Integer, String>
(3)GenericArrayType:泛型数组类型 例如:T[]
(4)TypeVariable:类型变量 例如:T
(5)WildcardType:带?通配符的泛型的类型 例如:ArrayList<?>

 public class TestClass {
        @Test
         public void test01() {
            //获取Son类的泛型父类的类型实参
            //(1)获取Class对象
            Class clazz = Son.class;

            //(2)获取泛型父类
            //获取普通父类
            //Class fu = clazz.getSuperclass();
             //System.out.println(fu);

            ParameterizedType type = (ParameterizedType) clazz.getGenericSuperclass();

            //(3)获取类型实参
            Type[] types = type.getActualTypeArguments();
            for (Type t : types) {
                System.out.println(t);
            }
            /*class java.lang.Integer
            class java.lang.String*/
        }
    }
    //泛型类型形参:<T,U>
    class Father<T,U>{
    }
    //泛型类型实参:<Integer, String>
    class Son extends Father<Integer, String>{
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

GuochaoHN

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

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

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

打赏作者

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

抵扣说明:

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

余额充值