Java编程思想——类型信息(RTTI)
一、概念
编译时已知的到所有的类型:就是在写代码阶段就确定是这个类型了,当运行程序的时候,类型是不可改变的
举例:List<String> str = new ArrayList(); //运行时就无法改变其类型
运行时使用其他类型:就是运行程序的时候,可以根据代码改变其类型
Class c = Class.fromName(String className);//传入不同的className获取不同的对象
二、RTTI
定义:
RTTI(Run-Time Type Identification,通过运行时类型识别)的含义
就是在运行时识别一个对象的类型,其对应的类是Class对象,每个java里面的类都对应一个Class对象(在编写并且编译后),这个对象被保存在这个类的同名class文件里。
2、支持向上转型和向下转型:如“(Apple)Fruit”,由RTTI确保类型转换的正确性,如果执行了一个错误的类型转换,就会抛出一个 ClassCastException异常。
3、判定是否为同一类别:通过关键字instanceof。
所以说:在编译时必须知道一个非常重要的东西:类名(甚至是全类名)
举例:
- //假设Shape类,含有子类Circle类、Rectange类
- List<Shape> list = new ArrayList();
- list.add(new Circle());
- list.add(new Rectange());
- //根据RTTI会先识别Circle类,然后寻找对应的Class,进行编译
- //因为容器都是将类型当做Object类持有,当取出对象的时候RTTI会将Object转换为泛型的类,也就是Shape,而不是转换为更彻底的Cirlcle类
类加载器在类被第一次static调用(比如一个静态方法,一个静态代码块或者new关键字调用构造器,注意构造器contructors其实都是静态的)时会把那个对应的Class对象加载到内存中。(运行时创建对象,而不是在编译时创建对象,这是和其他语言不一样的地方——比如说PHP就是先将类创建完成之后再运行的,所以类是先创建还是后创建的不影响逻辑顺序)
三、Class对象
JAVA可以使用Class对象执行RTTI,Class拥有大量使用RTTI的其他方法:
通过Class对象来获取对象的类型。如
Class c = Class.forName(“Apple”);
Object o = c.newInstance();
3.通过关键字instanceof或Class.isInstance()方法来确定对象是否属于某个特定类型的实例
1、java编译顺序详解
①、当编译了一个新类的时候,就会创建.class文件,为了生成这个类的对象,就运行程序的“JVM”(Java虚拟机)称为类加载器的子系统
②、程序中那么如何生成这个类的对象:
所有的类都是第一次被使用的时候,就会动态加载到JVM上,类加载器在类被第一次static调用的时候就会被加载(构造方法也是一个静态方法 所以 new A()就是调用static)
所以说,java程序是在需要的时候,才会加载,而不是在运行前完全加载。
③、类加载器的操作:类加载器首先会检查这个类是否被加载,如果未被加载就根据类名查找.class文件,然后经Class对象载入内存,之后就创建这个类中的所有对象。
注:
- public class A{
- static {
- //static 初始化 是在类加载时进行的
- }
- }
static 初始化
2、Class类的使用
主要类:
- public class UseClass {
- static void printfIn(Class cc){
- String name = cc.getName();//获取Class类加包的名字
- Boolean isInterface = cc.isInterface();
- String simplyName = cc.getSimpleName();//获取类的名字
- }
- public static void main(String [] args){
- try {
- Class newClass = Class.forName("ClassLoaderTest");//获取相应对象
- //获取该类继承的接口
- for(Class face : newClass.getInterfaces()){
- System.out.println(face.getName());
- }
- Class classSuper = newClass.getSuperclass();//获取该类父类
- Object object = newClass.newInstance();
- //将Class对象创建为其所对应的对象(这里为ClassLoaderTest),不过返回的是Object类型,需要向下转型为ClassLoaderTest类型
- //原理:调用ClassLoader的默认的构造器,创建ClassLoaderTest对象。
- printfIn(newClass);
- } catch (ClassNotFoundException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }//加载类
- catch (InstantiationException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
UseClass
创建类:
- public class ClassLoaderTest {
- static {
- //当被加载的时候调用
- System.out.println("My name is ClassLoader");
- }
- }
ClassLoaderTest
根据得到的结果,当调用Class.forName("ClassLoaderTest");,会调用ClassLoaderTest的static{}域。
四、类字面常量
第三种生成Class对象的方法,举例:Class c = ClassLoaderTest.class;
与其他生成方法的区别:不会自动初始化该class对象。
延生(使用类而做的准备工作):
①、加载。类加载器创建Class对象 ②、链接。分配存储空间 ③、初始化:初始化其父类,静态初始化块。
所以说:不会自动初始化意思就是,不会执行上诉的初始化工作,当只有第一次调用该类的静态域的时候才会被调用。
执行类:
- public static void main(String [] args){
- System.out.println(ClassLoaderTest.DATA+"");//没有进行初始化
- System.out.println(ClassLoaderTest.TEST+"");//强制进行了初始化
- //说明了加上了final表示,直接调用不会进行初始化。但也有例外比如说
- System.out.println(ClassLoaderTest.DATA_ONE+"");//强制初始化,因为值不是编译期常量(是运行时的)
- try {
- Class c = ClassLoaderTest.class;//没有进行初始化
- Class c1 = Class.forName("ClassLoaderTest");//强制进行初始化
- new ClassLoaderTest();//调用默认构造器,强制进行初始化
- } catch (ClassNotFoundException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
ThirdWays
辅助类:
- public class ClassLoaderTest {
- static final int DATA= 333;
- static final int DATA_ONE = new Random().nextInt(3);
- static int TEST = 333;
- static {
- //当被加载的时候调用
- System.out.println("My name is ClassLoader");
- }
- }
ClassLoaderTest
五、泛型的Class引用
使用:Class<?> class = int.class;
作用:在编译器进行类型检查。
注:?代表通配符,表示使用一个非具体的类型,Class<?>等价于Class,那么他的作用在哪里呢,需要加上extends才能体现的出来
- public class ClassReferences {
- public static void main(String[]args){
- //Integer继承自Number
- Class<? extends Number> bound = int.class;//这样就能够声明放入的是Number的子类
- //但是错误的是:该泛型不支持向上转型,不像List那样。
- Class<Number> intClass = int.class;//这种方式是会报错的
- }
- }
ClassReferences
第二个作用:class.newInstance()返回的是具体的类型,而不是Object类
- public static void main(String[]args){
- Class<ClassLoader> loader = ClassLoader.class;//类字面常量才能这么用
- try {
- ClassLoader cl = loader.newInstance();//返回的是具体类型,而不是Object
- } catch (InstantiationException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
六、RTTI的第三种用法:instanceof
使用 :if (xx instanceof Dog){
((Dog)x).bark();
}
实例,动态创建不同类型的宠物
假设:所有父类为Pet,所有动物为其子类
步骤:①、获取所有子类的Class对象放在List数组中,注意最好使用泛型,判定该动物是否继承Pet类 ②、通过Random随机获取List中的Class对象,然后通过newInstance()方法生成具体动物的对象。 ③、再放入新的List数组中。
实例:
模型类:
- ublic abstract class PetCreator {
- //这里用到的模型模式,利用重写抽象方法获取种类的类型
- public abstract List<Class<? extends Pet >> type () throws ClassNotFoundException;
- //随机取出List中的Class对象,初始化成具体类
- public Pet randomPet() throws InstantiationException, IllegalAccessException, ClassNotFoundException{
- Random random = new Random();
- int index = random.nextInt(type().size());
- return type().get(index).newInstance();
- }
- //输入具体生成多少个动物,然后将random生成的对象,装入数组中
- public Pet[] createPets(int size){
- Pet[] pets = new Pet[size];
- try {
- for (int i=0; i<size; ++i){
- pets[i] = randomPet();
- }
- } catch (InstantiationException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (ClassNotFoundException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- return pets;
- }
- //将数组中的东西,转换成List
- public ArrayList<Pet> getPetsList(int size){
- ArrayList<Pet> arrList = new ArrayList();
- Collections.addAll(arrList, createPets(size));
- return arrList;
- }
- }
PetCreator
实现类:
- public class ForNameCreator extends PetCreator{
- private String[] forName = {"1","2","3","4"};
- private List<Class<? extends Pet>> types;
- @Override
- public List<Class<? extends Pet>> type() throws ClassNotFoundException {
- // TODO Auto-generated method stub
- types = new ArrayList();
- for (int i=0; i<forName.length; ++i){
- types.add((Class<? extends Pet>)Class.forName(forName[i]));
- }
- return types;
- }
- }
ForNameCreator
创建计数器,计算生成的Pet的具体种类多少
步骤:①、获取装有Pet对象的容器 ②、获取容器中的数据,并使用instanceof判断其属于的具体类型 ③、如果匹配,创建Map,将该类型的名字作为键,每当有一个匹配,就让该键所对应的值加一。
- public class PetCount {
- private Map<String,Integer> petCounts;
- private List<Pet> petsList;
- public PetCount(List<Pet> list){
- petCounts = new HashMap();
- petsList = list;
- }
- //将动物分类
- private void classify(){
- for(int i=0; i<petsList.size();++i){
- Pet pet = petsList.get(i);
- if (pet instanceof Dog){
- count("Dog");
- }
- else if (pet instanceof Cat){
- count("Cat");
- }
- else if (pet instanceof Bird){
- count("Bird");
- }
- else if (pet instanceof Chicken){
- count("Chicken");
- }
- }
- }
- //计数
- private void count(String className){
- //应该使用Integer,不应该使用int类型
- Integer count = petCounts.get(className);
- if(count != null){
- petCounts.replace(className, count+1);
- }
- else{
- petCounts.put(className,1);
- }
- }
- }
PetCount
动态的instanceof重写计数器:
步骤:①、创建PetCount1类,在初始化前获取全部子类的对象,存入Map<Pet,Integer>中(与静态的instanceof相比,将所有的子类不用数组形式展现,以放入map的形式出现) ②、获取Pet对象的容器 ③、获取map中的对象,调用isInstance()与pet容器中的对象进行比较,一致则加一。
- public class PetCount1 {
- private Map<Pet,Integer> allType;//获取所有Pet的子类并初始化。
- public void count(Pet pet){
- for (Map.Entry<Pet, Integer> map:allType.entrySet()){
- Pet newPet = map.getKey();//获取键
- Class c = newPet.getClass();//获取class
- //动态使用instanceof
- if (c.isInstance(pet)){
- map.setValue(map.getValue()+1);//如果相等就坐加法
- }
- }
- }
- }
PetCount1
小知识:Class.isAssignableFrom()是用来判断一个类Class1和另一个类Class2是否相同或是另一个类的子类或接口。
获取该对象的继承结构(核心 Class.getSuperClass(),递归)
- public class PetCount3 {
- public static void main(String[]args){
- Dog dog = new Dog();
- Class c = dog.getClass();
- getConstruct(c);
- }
- public static void getConstruct(Class c){
- Class superClass = c.getSuperclass();//获取该类的父类
- if (superClass != null){
- System.out.println(superClass.toString());
- getConstruct(superClass);//进行递归
- superClass = null;
- }
- }
- }
PetCount3
七、注册工厂
在继承结构中的问题是:当我向Pet结构中添加了一个新的类的时候,就需要需改ForNameCreator.java中的项,那么这样就很可能出问题。
所以采用工厂方法,使用一个接口,继承这个接口就表示成为了Pet的一个结构,然后由工厂创建该类。
八、instanceof与Class的关系
1、instanceof与isInstanceof()生成的结果完全一样
2、equal与==结果也一样。
3、但是意义不同:instanceof表示:你是这个类型的吗,或者你是这个类的派生类吗
equal只是表示,你是这个了性吗,不考虑继承。
Java编程思想——类型信息(RTTI)的更多相关文章
- JAVA编程思想——类型信息(反射)
一.反射与RTTI RTTI:这个类型必须在编译的时候已知或者存在,如果不知道对象的确切类型,RTTI可以告诉你. 反射(个人认为就是能够利用Class获取或者调用.class这个文件中的数据):当我 ...
- Java 编程思想 Chapter_14 类型信息
本章内容绕不开一个名词:RTTI(Run-time Type Identification) 运行时期的类型识别 知乎上有人推断作者是从C++中引入这个概念的,反正也无所谓,理解并能串联本章知识才是最 ...
- JAVA编程思想读书笔记(三)--RTTI
接上篇JAVA编程思想读书笔记(二) 第十一章 运行期类型判定 No1: 对于作为程序一部分的每个类,它们都有一个Class对象.换言之,每次写一个新类时,同时也会创建一个Class对象(更恰当的说, ...
- java编程思想-枚举类型思维导图
- Java编程思想重点笔记(Java开发必看)
Java编程思想重点笔记(Java开发必看) Java编程思想,Java学习必读经典,不管是初学者还是大牛都值得一读,这里总结书中的重点知识,这些知识不仅经常出现在各大知名公司的笔试面试过程中,而 ...
- java编程思想
Java编程思想,Java学习必读经典,不管是初学者还是大牛都值得一读,这里总结书中的重点知识,这些知识不仅经常出现在各大知名公司的笔试面试过程中,而且在大型项目开发中也是常用的知识,既有简单的概念理 ...
- Java编程思想(11~17)
[注:此博客旨在从<Java编程思想>这本书的目录结构上来检验自己的Java基础知识,只为笔记之用] 第十一章 持有对象 11.1 泛型和类型安全的容器>eg: List<St ...
- Java编程思想(后)
Java编程思想(后) 持有对象 如果一个程序只包含固定数量的且其生命期都是已知的对象,那么这是一个非常简单的程序. Java中的库基本类型: List, Set, Queue和Map --- 称为集 ...
- 【Java核心技术】类型信息(Class对象 反射 动态代理)
1 Class对象 理解RTTI在Java中的工作原理,首先需要知道类型信息在运行时是如何表示的,这是由Class对象来完成的,它包含了与类有关的信息.Class对象就是用来创建所有“常规”对象的,J ...
随机推荐
- 视差滚动(Parallax Scrolling)效果的原理和实现
视差滚动(Parallax Scrolling)是指让多层背景以不同的速度移动,形成立体的运动效果,带来非常出色的视觉体验.作为今年网页设计的热点趋势,越来越多的网站应用了这项技术. 一.什么是视差滚 ...
- Jquery表单与表格的运用
1,表单的应用: a. 单行文本框的应用 多行文本框的应用 b.复选框的框的应用 c.下拉框的应用 d.表单验证 2,表格的应用: a. 表格变色 b.表格展开关闭 d.表格内容筛选 3,多行文本框 ...
- 如何成为一个真正在路上的Linuxer
Linux 是工具,却更像一个信仰. 写在前面: 本文目的不是教你如何成为一个真正的Linuxer,也没有能力教你成为一个真正的linuxer,而是通过笔者的一些想法试图指引你真正踏上学习linux之 ...
- information_schema.referential_constraints 学习
information_schema.referential_constraints 表用于查看外键约束 1.information_schema.referential_constraints表的常 ...
- 正则表达式小试牛刀--匹配我的csdn博文标题
正则表达式小试牛刀--匹配我的博文标题 作者:vpoet 邮箱:vpoet_sir@163.com 正则匹配,我以我的博客页面的博客标题为例:http://blog.csdn.net/u0130187 ...
- Wine --- Linux上运行 Windows 应用
https://www.winehq.org/ Wine (“Wine Is Not an Emulator” 的首字母缩写)是一个能够在多种 POSIX-compliant 操作系统(诸如 Linu ...
- MyCat 主键ID自增长配置
在实现分库分表的情况下,数据库自增主键已无法保证自增主键的全局唯一.为此,MyCat 提供了全局sequence,并且提供了包含本地配置和数据库配置等多种实现方式,实现方式主要有三种:本地文件方式.数 ...
- C++ Primer 有感(异常处理)
1.异常是通过抛出对象而引发的.该对象的类型决定应该激活哪个处理代码.被选中的处理代码是调用链中与该对象类型匹配且离抛出异常位置最近的那个. 2.执行throw的时候,不会执行跟在throw后面的语句 ...
- Oracle官方版Entity Framework
千呼萬喚始出來! Oracle官方版Entity Framework問市,邁入開發新時代 自從我得了一種"不用LINQ就不會寫資料庫程式"的病,為了滿足工作上要搭配Oracle(雖 ...
- Andrord问题小结
问题描述:Gradle version 2.10 is required. Current version is 2.8.Gradle版本由2.8升为2.10后,发现所有依赖play-services ...