13. Java基础之类型信息(RTTI和反射)

一. 背景

并不是所有的Class都能在编译时明确,因此在某些情况下需要在运行时再发现和确定类型信息(比如:基于构建编程,),这就是RTTI(Runtime Type Information,运行时类型信息)。

Java是如何让我们在运行时识别对象和类的信息的,主要有两种RTTI的方式,一种是“传统的”RTTI,即假设在编译时已经知道了所有的类型;还有一种,是利用反射机制,在运行时再尝试确定类型信息。

二. RTTI

RTTI(Run-Time Type Infomation),运行时类型信息。可以在运行时识别一个对象的类型。类型信息在运行时通过Class对象表示,Class对象包含与类有关的信息,可以使用Class对象来创建类的实例。

  每个类对应一个Class对象,这个Class对象放在.class文件中,当我们的程序中首次主动使用某类型时,会把该类型所对应的Class对象加载进内存。

     我们如何获取到Class对象呢?有三种方法

1. Class.forName("全限定名");(其中,全限定名为包名+类名)。

2. 类字面常量,如String.class,对应String类的Class对象。

3.通过getClass()方法获取Class对象,如String str = "hello";str.getClass();。

  通过一个类对应的Class对象后,我们可以做什么?我们可以获取该类的父类、接口、创建该类的对象、该类的构造器、字段、方法等等。

  下面我们通过例子来熟悉Class对象的各种用法。

 1. 三种获取对象Class对象的方法

note1:在面向对象的世界里,万事万物都是对象。类是对象,类是java.lang.Class类的实例对象。

note2:查看Class类的源码,发现构造函数是private的,只有虚拟机才可以创建class对象。

note3:任何一个类都是Class的实例对象,这个实例对象有3中标识方式:(1)(2)(3)所示。

(1)类名.class-------不会引起类初始化

这实际告诉我们任何一个类都有一个隐含的静态成员变量class

 1 public class Person {
 2     public int a=1;
 3     public static int b=2;
 4     public final static int c=3;
 5     static {
 6         System.out.println("hello");
 7     }
 8
 9 }
10
11
12 public class Test2 {
13     public static void main(String args[]) {
14          Class<?> class1=Person.class;    //未引起初始化
15     }
16
17 }
18
19
20 未输出任何东西,说明.class没有起到初始化作用

(2)对象.getClass()------会引起类初始化

 1 public class Test2 {
 2     public static void main(String args[]) {
 3         Person person=new Person();
 4         Class<?>class1=person.getClass();
 5     }
 6
 7 }
 8
 9
10
11 输出:
12 hello

(3)Class.forName(“全限定名”)--------会引起类初始化

 1 public class Test2 {
 2     public static void main(String args[]) throws ClassNotFoundException {
 3         Class<?>class1=Class.forName("com.test.a.Person");
 4     }
 5
 6 }
 7
 8
 9 输出:
10 hello

(4)一个类只可能是Class类的一个实例对象

因此上述三种方法都会得到同样的一个Class类的实例对象。

 1 package com.test.a;
 2
 3 public class Test {
 4     public static void main(String args[]) throws ClassNotFoundException {
 5
 6         Class class1 = Test.class;
 7         Test test = new Test();
 8         Class class2 = test.getClass();
 9         Class class3 = Class.forName("com.test.a.Test");
10         System.out.println(class1 == class2);
11         System.out.println(class1 == class3);
12     }
13 }
14 true
15 true

(5)可以通过类的类类型创建该类的对象实例

Foo foo=(Foo)c1.newInstance();

 2. Class类本身定义的方法使用

 1 package com.test.a;
 2
 3 public class Person {
 4     public String name;
 5     public int age;
 6
 7     public String getName() {
 8         return name;
 9     }
10
11     private void setName(String name) {
12         this.name = name;
13     }
14
15     public int getAge() {
16         return age;
17     }
18
19     private void setAge(int age) {
20         this.age = age;
21     }
22 }
23 package com.test.a;
24
25 public class Woman extends Person{
26     public Double salary;
27     private String sex;
28     public Woman(String sex) {
29         this.sex=sex;
30     }
31     private Woman() {
32
33     }
34     public void print()
35     {
36         System.out.println("hello");
37     }
38
39     private void print2() {
40         System.out.println("hello2");
41     }
42
43 }

(1)获取包名

 1 package com.test.a;
 2
 3 public class Test {
 4     public static void main(String args[]) {
 5         Class class1 = Test.class;
 6         System.out.println(class1.getName());
 7         System.out.println(class1.getSimpleName());
 8         Class c1 = double.class;
 9         Class c2 = String.class;
10         Class c3 = Void.class;
11
12         System.out.println(c1.getName());
13         System.out.println(c1.getSimpleName());
14         System.out.println(c2.getName());
15         System.out.println(c2.getSimpleName());//不包含包名
16         System.out.println(c3.getName());
17         System.out.println(c3.getSimpleName());
18     }
19 }
20
21
22 com.test.a.Test
23 Test
24 double
25 double
26 java.lang.String
27 String
28 java.lang.Void
29 Void

(2)获取方法

 1 package com.test.a;
 2
 3 import java.lang.reflect.Method;
 4
 5 public class Test {
 6     public static void main(String args[]) {
 7         Woman woman=new Woman();
 8         Class class1=woman.getClass();//传递的是哪个子类的对象,class1就是该子类的类类型
 9         //一个成员方法就是一个Method对象
10         Method[] publicMethods=class1.getMethods();//得到所有的public方法,包含从父类继承而来的Public方法
11         for(Method i:publicMethods) {
12             System.out.println(i);
13         }
14
15         System.out.println("***************");
16         Method[] declaredMethod=class1.getDeclaredMethods();//只打印该类自己定义的方法(没有权限限制)
17         for(Method i:declaredMethod) {
18             System.out.println(i);
19         }
20     }
21 }
22
23 /**
24 public void com.test.a.Woman.print()
25 public java.lang.String com.test.a.Person.getName()
26 public int com.test.a.Person.getAge()
27 public final void java.lang.Object.wait() throws java.lang.InterruptedException
28 public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
29 public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
30 public boolean java.lang.Object.equals(java.lang.Object)
31 public java.lang.String java.lang.Object.toString()
32 public native int java.lang.Object.hashCode()
33 public final native java.lang.Class java.lang.Object.getClass()
34 public final native void java.lang.Object.notify()
35 public final native void java.lang.Object.notifyAll()
36 ***************
37 public void com.test.a.Woman.print()
38 private void com.test.a.Woman.print2()
39
40 */

(3)获取成员变量

 1 package com.test.a;
 2
 3 import java.lang.reflect.Field;
 4 import java.lang.reflect.Method;
 5
 6 public class Test {
 7     public static void main(String args[]) {
 8         Woman woman=new Woman();
 9         Class class1=woman.getClass();//传递的是哪个子类的对象,class1就是该子类的类类型
10         Field fields[]=class1.getFields();
11         for(Field i:fields) {
12             System.out.println(i);
13         }
14
15         System.out.println("**************");
16         Field decFileds[]=class1.getDeclaredFields();
17         for(Field i:decFileds) {
18             System.out.println(i);
19         }
20     }
21 }
22
23 /**
24 public java.lang.Double com.test.a.Woman.salary
25 public java.lang.String com.test.a.Person.name
26 public int com.test.a.Person.age
27 **************
28 public java.lang.Double com.test.a.Woman.salary
29 private java.lang.String com.test.a.Woman.sex
30
31
32 */

(4)获取对象的构造函数信息

 1 package com.test.a;
 2
 3 import java.lang.reflect.Constructor;
 4 import java.lang.reflect.Field;
 5 import java.lang.reflect.Method;
 6
 7 public class Test {
 8     public static void main(String args[]) {
 9         Woman woman=new Woman("femal");
10         Class class1=woman.getClass();//传递的是哪个子类的对象,class1就是该子类的类类型
11
12         Constructor<Woman> constructors[]=class1.getConstructors();
13         for(Constructor<Woman> i:constructors) {
14             System.out.println(i);
15         }
16         System.out.println("********");
17
18         Constructor<Woman> constructors2[]=class1.getDeclaredConstructors();
19         for(Constructor<Woman> i:constructors2) {
20             System.out.println(i);
21         }
22     }
23 }
24
25 /**
26  public com.test.a.Woman(java.lang.String)
27 ********
28 public com.test.a.Woman(java.lang.String)
29 private com.test.a.Woman()
30
31  */

3. Java动态加载类

Class.forName(“类的全称”):不仅标识了类的类类型,还代表了动态加载类。

编译时刻加载类是静态加载类、运行时刻加载类是动态加载类。

静态加载类  VS  动态加载类??

三. 反射

与RTTI必须在编译器就知道所有类型不同,反射不必在编译期就知道所有的类型,它可以在运行过程中使用动态加载的类,而这个类不必在编译期就已经知道。反射主要由java.lang.reflect类库的Field、Method、Constructor类支持。这些类的对象都是JVM在运行时进行创建,用来表示未知的类。

  关于两者的区别更深刻表达如下:对于RTTI而言,编译器在编译时打开和检查.class文件;对于反射而言,.class文件在编译时是不可获取的,所以在运行时打开和检查.class文件。

  其实在的第一个例子中我们已经用到了Constructor、Method类,现在我们来更加具体的了解Constructor、Method、Field类。  

 1 package com.test.a;
 2
 3 import java.lang.reflect.Constructor;
 4 import java.lang.reflect.Field;
 5 import java.lang.reflect.InvocationTargetException;
 6 import java.lang.reflect.Method;
 7
 8 import javax.activation.FileDataSource;
 9
10 public class Test2 {
11     public static void main(String args[]) throws NoSuchMethodException, SecurityException, InstantiationException,
12             IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchFieldException {
13         Class<?> class1 = Person.class;
14         Constructor<?> constructor = class1.getConstructor(int.class, String.class);// 区别getConstructors,这里不是Integer.class
15         Person person1 = (Person) constructor.newInstance(23, "Tom");
16         System.out.println(person1);
17
18 //        Method method=class1.getMethod("f");//运行异常,因为该方法不适用于private方法
19         Method method2 = class1.getMethod("g");
20         method2.invoke(person1);
21         Method method3 = class1.getDeclaredMethod("f");
22         method3.setAccessible(true);
23         method3.invoke(person1);// 必须要setAccessible成true,才可以访问private方法
24
25         Field field = class1.getDeclaredField("age");
26         System.out.println(person1);
27         field.setAccessible(true);
28         field.set(person1, 34);
29         System.out.println(person1);
30     }
31
32 }

1 The name is: Tomthe age is 23
2 I am public function
3 I am private function
4 The name is: Tomthe age is 23
5 The name is: Tomthe age is 34

说明:反射可以让我们创建一个类的实例、在类外部访问类的私有方法、私有字段。

1.方法的反射

(1)如何获取某个方法

方法的名称和方法的参数列表才能唯一决定某个方法

(2)方法反射的操作

method.invoke(对象,参数列表)

note:要获取一个方法,就是要获取类的信息,要获取一个类的信息,就是获取类类型。

 1 package com.test.a;
 2
 3 import java.lang.reflect.InvocationTargetException;
 4 import java.lang.reflect.Method;
 5
 6 public class Test {
 7     public static void main(String args[]) throws NoSuchMethodException, SecurityException, IllegalAccessException,
 8             IllegalArgumentException, InvocationTargetException {
 9         // 1.获取类的信息
10         R a1 = new R();
11         Class class1 = a1.getClass();
12         // 2.获取方法:名称和参数列表来决定
13         Method method = class1.getMethod("print", new Class[] { int.class, int.class });
14         // 也可以写成Method method2=class1.getMethod("print",
15         // int.class,int.class);因为...代表可变参数,可以携程数组形式,也可以全部写出来
16
17         // 3.方法的反射操作
18         // note1:方法的反射操作是用method对象来进行方法调用,和a1.print调用的效果相同。(正常情况下是对象操作方法,反射反过来,通过print对象操作a1)
19         // note2:如果方法没有返回值,返回null,有返回值返回具体的返回值
20         Object object = method.invoke(a1, new Object[] { 10, 20 });
21
22     }
23 }
24
25 package com.test.a;
26
27 public class R {
28     public void print(int a, int b) {
29         System.out.println(a + b);
30     }
31
32     public void print(String a, String b) {
33         System.out.println(a.toUpperCase() + "," + b.toLowerCase());
34     }
35 }

30

2.Java通过反射了解集合泛型的本质

 1 package com.test.a;
 2
 3 import java.lang.reflect.InvocationTargetException;
 4 import java.lang.reflect.Method;
 5 import java.util.ArrayList;
 6
 7 public class Test {
 8     public static void main(String args[]) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
 9         ArrayList list1=new ArrayList();
10         ArrayList<String> list2=new ArrayList<>();
11         list2.add("a");//only string,can‘t list.add(20);
12         Class class1=list1.getClass();
13         Class class2=list2.getClass();
14         System.out.println(class1==class2);
15         /**
16          * class1==class2的结果为true说明编译后的集合的泛型是去泛型化的。
17          * Java中集合的泛型,是防止错误输入的,只在编译阶段有效,绕过编译就无效了。
18          * 验证:我们可以通过方法的反射来操作,绕过编译
19          */
20         Method method=class2.getMethod("add", Object.class);
21         method.invoke(list2, 20);//绕过编译操作就绕过了泛型
22         System.out.println(list2.size());
23         System.out.println(list2);
24
25     }
26 }
27
28 true
29 2
30 [a, 20]

四.动态代理

http://www.cnblogs.com/xiaoluo501395377/p/3383130.html

视频

https://www.imooc.com/learn/199

https://study.163.com/course/introduction.htm?courseId=947001#/courseDetail?tab=1

原文地址:https://www.cnblogs.com/Hermioner/p/9596335.html

时间: 2024-10-12 22:49:59

13. Java基础之类型信息(RTTI和反射)的相关文章

【Java核心技术】类型信息(Class对象 反射 动态代理)

1 Class对象 理解RTTI在Java中的工作原理,首先需要知道类型信息在运行时是如何表示的,这是由Class对象来完成的,它包含了与类有关的信息.Class对象就是用来创建所有"常规"对象的,Java使用Class对象来执行RTTI,即使你正在执行的是类似类型转换这样的操作. 每个类都会产生一个对应的Class对象,也就是保存在.class文件.所有类都是在对其第一次使用时,动态加载到JVM的,当程序创建一个对类的静态成员的引用时,就会加载这个类.Class对象仅在需要的时候才会

运行时类型信息RTTI

我们在写C++代码的时候经常碰到使用dynamic_cast进行类型转换的情况,也都知道经过dynamic_cast的转换更加安全,因为dynamic_cast进行了类型检查. 但是可能很多人不知道dynamic_cast是C++ 运行时类型信息(RTTI)机制链条上的一个节点. RTTI提供了两个操作符和一个类: dynamic_cast typeid type_info 整个RTTI, 作为一个整体,暴露给程序员的就是这三个元素.因此我们关注的焦点也就在它们身上了. 什么是RTTI 在C++

Thinking in Java -- 类型信息RTTI

Thinking in Java – 类型信息 个人感觉 java 中的比較难的部分了,在看了些netty源代码发现事实上这块很实用. 这章重点是RTTI和反射.先说下自己的理解 RTTI是执行时识别.在c++中是用virtual来实现的,在编译期会忽略对象的详细类型信息,假定我们已经知道,并在执行时详细识别. Java反射机制实在执行状态中,对于随意一个类,都能够知道这个类的全部属性和方法.对于随意一个对象.都能够调用它的随意一个方法和属性,这样的动态获取的信息以及动态调用对 象的方法的功能称

Java编程思想(十四) —— 类型信息RTTI(1)

译者翻译的时候有些奇怪,Runtime type information (RTTI) allows you to discover and use type information while a program is running. 运行时类型信息(原来的翻译没有括号这里面的内容,Runtime type information,简称RTTI,个人觉得这样注释比较好)可以让你在程序运行的时候发现和使用类型信息.后面直接出现RTTI让人疑惑. 1)为什么需要RTTI 之前的多态的例子中: p

类型信息-RTTI

运行时类型信息使你可以在程序运行时发现和使用类型信息 主要有两种方式:一种是传统的RTTI,它假定我们在编译时已经知道了所有的类型,另一种是反射机制,它允许我们在允许时发现和使用类的信息. Class对象 要理解RTTI在Java中的工作原理,首先必须知道类型信息在运行时是如何表示的. 类是程序的一部分,每个类都有一个Class对象.换言之,每当编写并且编译路一个新类,就会产生一个Class对象. 所有的类 都是在对其第一次使用时,动态加载到JVM中.当程序创建第一个对类的静态成员的引用时,就会

java 基础 浮点类型

1.浮点类型用于表示小数的数据类型. 2.浮点数原理:也就是二进制科学计数法. 3.Java的浮点类型有float和double两种. 4.Java默认浮点类型计算的结果是double类型,字面量也是double类型. 1.十进制浮点数科学计数法: 219345 = 2.19345*(10^5) 2.二进制浮点数科学计数法: 10111 = 1.0111*(2^100) 1.float类型共32位,1位为符号位, 指数8位, 尾数23位. 2.float的精度是23位(即能精确表达23位的数,超

黑马程序员--Java基础--基本类型

--Java培训.Android培训.iOS培训..Net培训 期待与您共同交流!-- 变量.JAVA基本类型.运算符和表达式 目录: 1变量 2JAVA基本类型 3运算符和表达式 1. 变量 1.1. 什么是变量 在日常生活中,人们会用到大量的数据,像去ATM机取款,首先,需要插入银行卡,这个过程其实就是ATM机的系统在获取银行卡号这个数据,而后,输入密码,这个过程也是在获取数据,也就是密码.在后续进行的业务处理中,像存钱.取钱.转帐汇款等等,银行卡号和密码会被反复的.频繁的使用, 那就需要一

java 基础 整数类型

1.Java有四种整数类型:byte.short.int和long. 2.Java默认整数计算的结果是int类型. 3.整数的字面量是int类型. 4.若字面量超过int类型的最大值,则字面量是long类型,那么后面要用L(或l)表示该值是long类型. byt b = 5; byt a = b + 5;错误 int a =  b + 5;正确 byt a = 122 + 5;正确 byt a = 123 + 5;错误,超出范围

了解运行时类型信息(RTTI)

RTTI需要引用单元TypeInfo GetPropInfo 函数用于获得属性的 RTTI 指针 PPropInfo.它有四种重载形式,后面三种重载的实现都是调用第一种形式.AKinds 参数用于限制属性的类型,如果得到的 PPropInfo 不属于指定的类型,则返回 nil. function GetPropInfo(TypeInfo: PTypeInfo; const PropName: string): PPropInfo; function GetPropInfo(Instance: T