• Java——类型信息


    1、Class对象

      Class对象是一个特殊的对象,它包含了与类有关的信息。Class对象就是用来创建类的所有常规对象的。

      类是程序的一部分,每个类都有一个Class对象,每当编写并且编译一个新类,就会产生一个Class对象。为了生成这个对象,运行这个程序的Java虚拟机(JVM)的使用被称为 类加载器 的子系统。

      所有的类都是在对其第一次使用时,动态加载到JVM中的,当程序创建第一个对类的静态的引用时,就会加载这个类。当我们使用new关键字创建一个类的第一个对象的时候,JVM会帮助我们加载该类Class对象

      类加载器首先检查这个类的CLASS对象是否被加载,如果尚未加载,默认的类加载器就会根据类名查找.class文件

      想自己加载这个类的Class对象有三个办法:

      a、Class.forName("类名字符串") (注意:类名字符串必须是全称,包名+类名)

      b、类字面常量法:类名.class

      c、实例对象.getClass()

        class Candy{
            static {System.out.println("Candy");}
        }
        class Sun{
            static {System.out.println("Sun");}
        }
    
    public class PP {
        public static void main(String[] args) {
            new Candy();
            try {
                Class.forName("Sun");
            }catch(ClassNotFoundException e) {
                System.out.println("Candy can't find");
            }
            
            try {
                Class.forName("SS");
            }catch(ClassNotFoundException e) {
                System.out.println("Candy can't find");
            }
        }
    }

    输出:

    Candy
    Sun
    Candy can't find

    这里有两个类,两个类都有一个static语句,每当类在第一次加载的时候就会执行打印出类的名字。

    Class.forName("Sun");这个.forName()方法是一个Class类的static成员,这是一种取得Class对象的引用的方法,这里的主要作用是加载Sun类,加载的过程中static子句将被执行。

    如果Class.forName()找不到想要加载的类,它就会抛出ClassNotFoundException

        interface Ss{}
        interface Ss2{}
        
        class Candy{
        }
        class Sun extends Candy implements Ss,Ss2 {
            static {System.out.println("Sun");}
        }
        
    public class PP {
        static void pr(Class cc) {
            System.out.println("cc.getname:" + cc.getName());
            System.out.println("is interface?:" + cc.isInterface());
            System.out.println("cc SimpleName:" + cc.getSimpleName());
            System.out.println("cc CanonicalName:" + cc.getCanonicalName());
            
        }
        public static void main(String[] args) {
            Class c = null;
            
            try {
                c = Class.forName("Sun");
            }catch(ClassNotFoundException e) {
                System.out.println("Sun can't find");
            }
            
            pr(c);
            System.out.println();
            
            for(Class face: c.getInterfaces()) {
                pr(face);
            }
            System.out.println();
            
            
            Class up = c.getSuperclass();
            pr(up);
            
        }
    }

    这里有两个接口 Ss Ss2 和两个类Candy和Sun,其中Candy是Sun的子类并实现了Ss Ss2 两个接口。

    输出:

    Sun
    cc.getname:Sun
    is interface?:false
    cc SimpleName:Sun
    cc CanonicalName:Sun
    
    cc.getname:Ss
    is interface?:true
    cc SimpleName:Ss
    cc CanonicalName:Ss
    cc.getname:Ss2
    is interface?:true
    cc SimpleName:Ss2
    cc CanonicalName:Ss2
    
    cc.getname:Candy
    is interface?:false
    cc SimpleName:Candy
    cc CanonicalName:Candy

    pr()方法可传入一个Class对象作为参数,这个方法将调用Class的getName()产生全限定的类名(我这不知道为什么不是==),isInterface()判断是不是一个接口,getSimpleName()产生不含包名的类名,getCanonicalName()也是产生全限定的类名(我这也是不知道为什么不是==)

    main方法中,.getInterfaces()获得该对像所包含的接口,.getSuperclass()可以获得基类。

    import java.util.*;
    
    class Initable {
            static final int staticfinal = 41;
            static final int staticfinal2 = PP.rand.nextInt(1000);
            static {System.out.println("Initable");}
        }
    class Initable2 {
        static int staticfinal = 141;
        static {System.out.println("Initable2");}
    }
    class Initable3 {
        static int staticfinal = 74;
        static {System.out.println("Initable3");}
    }
    public class PP {
        public static Random rand = new Random(47);
        public static void main(String[] args) {
            Class initable = Initable.class;
            System.out.println(Initable.staticfinal);
            System.out.println(Initable.staticfinal2);
            System.out.println(Initable2.staticfinal);
            try {
                Class initable3 = Class.forName("Initable3");
            } catch (ClassNotFoundException e) {
                System.out.println("Initable3 can't find");
            }
            System.out.println(Initable3.staticfinal);
        }
    }

    输出:

    41
    Initable
    258
    Initable2
    141
    Initable3
    74

    这里的输出证明:

      1、Class initable = Initable.class;这样获取一个Class对象,并不会引起初始化。

      2、static final的变量是编译器常量不需要初始化就能被读取。

      3、如果这是static final还不能保证2的结论,就像staticfinal2这个变量的值来自于PP类的rand对象的nextInt()方法,这样将会被强制进行初始化。

      4、Class.forName()会引起初始化。

    2、泛化的Class引用

      Class的引用总是指向某个Class对象,它可以制造类的实例,并包含可作用于这些实例的所有方法代码。它还包含该类的静态成员,因此,Class引用表示的就是它所指向的对象的确切类型,而该对象便是Class类的一个对象。

    public class Gener {
        public static void main(String[] args) {
            Class intClass = int.class;
            Class<Integer> genIntClass = int.class;
            genIntClass = Integer.class;
            intClass = double.class;
            //genIntClass = double.class; Type mismatch: cannot convert from Class<Double> to Class<Integer>
        }
    }

      这里创建了两个Class对象,普通的类引用并不会产生警告信息,泛型类引用只能赋值为其声明的类型,但是普通的类引用可以被重新赋值为指向任何其他的Class对象。

      为了使在使用泛化的Class引用放松限制,可以使用通配符,通配符“?”,表示任何事物。

    public class Gener {
        public static void main(String[] args) {
            Class intClass = int.class;
            Class<?> genIntClass = int.class;
            genIntClass = Integer.class;
            intClass = double.class;
            genIntClass = double.class;
        }
    }

      这样就不会产生编译器警告信息。

      为了创建为某种类型,或是该类型的任意子类型,需要将通配符与extends关键字相结合,

    public class Gener {
        public static void main(String[] args) {
            Class<? extends Number> genIntClass = int.class;
            genIntClass = double.class;
            genIntClass = Number.class;
        }
    }

      向Class引用添加泛型语法的原因仅仅是为了提供编译期的类型检查,因此如果你操作错误,稍后就会发现这一点,使用普通Class引用时,直到运行才会发现这个错误

    class coun{
            private static long counter;
            private final long id = counter++;
            public String toString() { return Long.toString(id);}
            
        }
    class FilledList<T>{
        private Class<T> type;
        public FilledList(Class<T> type) {
            this.type = type;
        }
        public List<T> create(int eElements){
            List<T> result = new ArrayList<T>();
            try {
                for(int i = 0; i < eElements; i++ ) {
                    result.add(type.newInstance());
                }
            }catch(Exception e) {
                throw new RuntimeException(e);
            }
            return result;
        }
    }
    public class Gener {
    
        public static void main(String[] args) {
            FilledList<coun> fl = 
                    new FilledList<coun>(coun.class);
                    System.out.println(fl.create(15));
        }
    }

    这里构建了一个FilledList类,拥有一个泛型的Class类字段,随后又产生了一个List,使用.newInstance()来填充这个List,.newInstance()方法是实现“虚拟构造器”的一种途径,虚拟构造器允许你声明:"我不知道你的确切类型,但是无论如何我都要创建你",这里在填充List的时候因为是使用的泛型,所以add()的时候并不知道添加的对象是什么类型的,所以这里使用Class的.newInstance()方法。

    输出:

    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]

    注意:这个类必须假设与他一同工作的任何类型都具有一个默认的构造器(无参构造器),如果不符合这个条件,你将得到一个异常。因为.newInstance()是没有参数的,想要生成一个需要传入参数才能创建的对象必然是失败的。

    class toy{
        
    }
    class fancy extends toy {
        
    }
    public class Gener {
    
        public static void main(String[] args) throws Exception {
            Class<fancy> ftclass = fancy.class;
            fancy ft = ftclass.newInstance();
            
            Class<? super fancy> up = ftclass.getSuperclass();
            
            //Class<toy> up = ftclass.getSuperclass();
            
            Object obj = up.newInstance();
        }
    }

       这里定义了这个类,toy是fancy的父类,如果你想要通过子类的Class对象创建一个其父类的Class对象,编译器将只允许你声明父类引用是“某个类,它是fancy的父类”,将像Class<? super fancy>这样的表达,Class<toy>这样的声明是不会被允许的。

      因为getSuperclass()方法返回的是基类(不是接口),并且编译器在编译器就知道它是什么类型了。

    3、Class引用的转型语法

      

    class toy{
        
    }
    class fancy extends toy {
        
    }
    public class Gener {
    
        public static void main(String[] args) throws Exception {
            toy t = new toy();
            Class<fancy>  fancyType = fancy.class;
            fancy f = fancyType.cast(t);        
        }
    }

      cast()方法接受参数对象,并将器转型为Clsss引用的类型。这里一开始创建了一个toy类型的对象t,然后创建了一个fancy的Class对象fancyType,然后调用fancyType这个Classd对象的cast()方法传入t,将这个t转型为fancyType这个Classd对象引用的类型fancy。

    toy t = new toy();
    fancy f = (fancy)t;  

      其实实现的功能和这两句一样,。只是cast这种转型语法用在对于无法使用普通转型的情况,在编写泛型代码的时候,如果存储了Class引用,并希望以后通过这个引用来执行转型。

    4、类型转换前检查

      instanceof是在java中RTTI的一种形式的关键字,还有传统的“(fancy)”类型转换,然后就是代表对象的类型的Class对象。

      instanceof返回一个布尔值,告诉我们对象是不是某个特定类型的实例。

    if((x instanceof Dog)
        ((Dog)x).bark();

      在将x转型成一个Dog之前,上面的if语句会先检查x对象是不是属于Dog类。

    5、instanceof与Class的等价性

    class base{}
    class Derived extends base{}
    
    public class Gen {
    
        static void test(Object x) {
            System.out.println("x getclass," + x.getClass());
            System.out.println("x instanceof base," + (x instanceof base));
            System.out.println("x instanceof Derived," + (x instanceof Derived));
            System.out.println("base.isInstance(x)," + base.class.isInstance(x));
            System.out.println("Derived.isInstance(x)," + Derived.class.isInstance(x));
            System.out.println("x.getClass() == base.class," + (x.getClass() == base.class));
            System.out.println("x.getClass() == Derived.class," + (x.getClass() == Derived.class));      
            System.out.println("x.getClass().equals(base.class)," + (x.getClass().equals(base.class)));  
            System.out.println("x.getClass().equals(Derived.class)," + (x.getClass().equals(Derived.class)));  
        }
        public static void main(String[] args) {
            test(new base());
            test(new Derived());
        }
    
    }

    输出:

    x getclass,class base
    x instanceof base,true
    x instanceof Derived,false
    base.isInstance(x),true
    Derived.isInstance(x),false
    x.getClass() == base.class,true
    x.getClass() == Derived.class,false
    x.getClass().equals(base.class),true
    x.getClass().equals(Derived.class),false
    x getclass,class Derived
    x instanceof base,true

    从输出可以看出来,instanceof和isInstance()得到的结果是一样的,equals和==也是一样的,但是instanceof考虑的是“你是这个类,或者是这个类的派生类吗”,== 就没有考虑继承

  • 相关阅读:
    easy Html5 Jquery Mobile之ToolBars(Header and Footer)
    Windows Phone7 性能
    easy Html5 Jquery Mobile之Buttons
    开篇,从这里开始。
    SkyDrive APIs——用户登录和APP授权(2)
    Windows Phone的强大语音控制,让你的Phone大秀一把
    SkyDrive APIs之——操作文件和文件夹(3)
    Mango in my mind 之 Live Tile
    SkyDrive APIs——搭建环境(1)
    windows10安装redis
  • 原文地址:https://www.cnblogs.com/xxbbtt/p/7653034.html
Copyright © 2020-2023  润新知