zoukankan      html  css  js  c++  java
  • 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();
            }
            
        }
    View Code

    六、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只是表示,你是这个了性吗,不考虑继承。

  • 相关阅读:
    第三十章 货币增长与通货膨胀
    第二十九章 货币制度
    第二十八章 失业
    第二十七章 金融学的基本工具
    第二十六章 储需、投资和金融体系
    第二十五章 生产与增长
    第二十四章 生活费用的衡量
    第二十三章 一国收入的衡量
    第二十二章 微观经济学前沿
    Struts2 文件上传进度条的实现
  • 原文地址:https://www.cnblogs.com/rookiechen/p/5521453.html
Copyright © 2011-2022 走看看