java基础-反射1(类型信息,Class对象简介,Class对象初始化)
2017-11-30 15:18
1041 查看
Java基础-反射1(反射基础,运行时类型信息,Class对象简介)
在学习反射之前,先来了解一下类型信息,类型信息就是JAVA类的一些信息,包括(包,方法,属性等),运行时类型信息可以让你在程序运行时了解类的信息并且使用类,那么如何在运行时获取类的信息?在JAVA中有两种方式:传统的RTTI
反射
RTTI (Run-Time Type identification)
传统的RTTI的使用是假定我们在编译时就已经知道了类的信息。先来看一个例子:有一个People的抽象类,然后American类和Chinese类继承至People如下:public abstract class People { public abstract void say(); }
public class American extends People { @Override public void say() { System.out.println("Americaen say: hello"); } }
public class Chinese extends People { @Override public void say() { System.out.println("中国人 说: 你好"); } }
public class Test { public static void main(String agrs[]){ List<People> peoples= new ArrayList<>(); peoples.add(new American()); peoples.add(new Chinese()); for (int i = 0 ;i <peoples.size();i++){ peoples.get(i).say(); } } }
这是一个简单的类的继承例子,People是父类,American 和Chinese 是子类,在Test中我们创建一个List,放入American和Chinese,然后我们取出,调用say()方法.
在这里我们List中定义需要放的类型是父类People,而不是子类,面向对象的目的是让程序只操作父类而不是子类,但是我们实际上往List中添加的类型是子类(American,Chinese),这里就发生了向上转型(子类对象赋值给父类引用)但是我们在调用People的say()方法时却又打印出子类的信息(这就是多态)
下面我们来看把对象从List中取出来的代码:
ArrayList底层是通过数组实现的 下面是ArryList源码中的其中一个构造方法
transient Object[] elementData; public ArrayList(int initialCapacity) { if (initialCapacity > 0) { this.elementData = new Object[initialCapacity]; } else if (initialCapacity == 0) { this.elementData = EMPTY_ELEMENTDATA; } else { throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); } }
通过源码可以看出 elementData 是一个Object 数组。所以
List peoples= new ArrayList<>(); 其实是储存的Object[]数组,但是当我们从List中取出来时,如下面代码:
for (int i = 0 ;i <peoples.size();i++){ peoples.get(i).say(); }
当我们从List中取出来时却又表现出People的特点,那是因为我们从
List<People> peoples中取出来时自动将Object转化为了People(向下转型,相当于People people = (People)object) 这就是RTTI的最基本的使用形式:在运行时进行类型检查因为我们在编译时已经知道
List<People> peoples储存的都是People。所以在我们从容器中取出元素时系统自动会将Object转化为People。
那么RTTI是如何工作的喃?要理解RTTI的工作原理就必须要类型信息在运行时如何表示的。
Class对象简介
java 在运行时表示类型信息是通过class这个特殊对象来完成的,class对象属于Class类,Class类用来表示一般类的信息,所以class对象包含了与类相关的信息,它主要有以下几个特点:
每个类都有一个与其相关的Class对象。
Class对象含有与其相关类的类信息。
Class对象用来创建与其相关类的“常规对象“
Class对象保存在与其同名的.class文件中。
那么Class对象是如何产生的喃?
Class对象是通过类加载器加载的,并且只有在第一次使用类时才会被加载。
当使用一个类时,JVM的类加载器会先检查这个类的class对象是否已经被加载,如果没有被加载,则查找同名的.class文件。如果一旦发现类的class对象已经被加载到内存了,则使用这个class对象来创建这个类的所有对象。
那么如何获取Class对象?
Class对象的获取
下面看下这个例子public class Dog { static { System.out.println("狗"); } }
public class Cat { static { System.out.println("猫"); } }
public class People { static { System.out.println("人"); } }
public class Test { public static void main(String agrs[]){ try { //获取Class 的第一种方法 Class catClass= Class.forName("com.hbcode.Test.Cat"); Dog dog1 = new Dog(); //获取Class 的第二种方法 Class dog =dog1.getClass(); //获取Class 的第三种方法 Class people =People.class; } catch (ClassNotFoundException e) { e.printStackTrace(); } } }
上面就是获取类的class对象的三种方法,下面我们看下打印的信息
我们发现没有打印出来“人”那是因为使用.class来获取类的class对象并不会初始化类的class对象,实际上在获取class对象时做了一下三步:
类的加载:由类加载器执行,根据类名查找.class文件,并从中创建一个class对象
类的链接:验证.class文件(字节码文件)看其结构是否完整,并且为静态域分配空间。
类的初始化:如果类有父类则初始化父类,执行父类的静态初始化块,和静态初始化器,然后执行自己的。
延伸:笔试题,变量的初始化顺序
根据《java编程思想》一书上面的讲解,类的初始化其实是被延迟到了对静态方法的引用(构造函数也是隐式的静态方法)或者是非常数的静态域进行引用。
从上面可以看出
Class.forName()其实就是调用的是类的静态函数可以被初始化。创建对象 new 对象时其实也是对构造函数(隐式的静态方法)的调用。
现在我们获取到了class对象,就可以获取其相对应的类型信息了,下一篇文章我们就来通过class对象来获取类的类型信息,并且通过反射来操作类
相关文章推荐
- java基础-反射2(反射,反射操作对象,Class对象的使用,类型信息的获取)
- 【Java核心技术】类型信息(Class对象 反射 动态代理)
- 深入理解Java类型信息(Class对象)与反射机制
- 深入理解Java类型信息(Class对象)与反射机制
- 先码后看 Class对象(Java类型信息)与反射机制,超详细 侵立删
- 深入理解Java类型信息(Class对象)与反射机制
- 深入理解Java类型信息(Class对象)与反射机制
- 深入理解Java类型信息(Class对象)与反射机制
- 黑马程序员--Java基础加强--14.利用反射操作泛型III【解析关于泛型类型的细节信息的获取方法】【Method与泛型相关的方法】【个人总结】
- Objective-C(五、@class,id类型,类对象构造方法,加载和初始化,description,Logging宏)——iOS开发基础
- Java反射基础(一)--Class对象获取
- java语言反射的概述以及三种获取字节码文件对应的Class类型的对象的方式
- Java基础之类型信息(反射)
- java类型信息—class对象
- Java反射---Class对象,获得类的方法、成员变量和构造函数信息
- Java反射基础(一) — ClassLoader简介
- Java基础——反射之获取对象信息
- Java 类型信息 —— 获取泛型类型的类对象(.class)
- 黑马程序员--Java基础加强--17.利用反射操作泛型VI【泛型类型变量的语义】【GenericDeclaration接口】【泛型接口TypeVariable】【通过Class反射解析泛型类】
- JAVA类型信息——Class对象