thinking in java学习笔记:14章 类型信息

14.2

https://github.com/zhaojiatao/javase

1、什么是Class对象,Class对象是用来做什么的?

Class对象是java程序用来创建类的所有常规对象用的;每个类都有一个Class对象;

2、Class对象是如何创建的?

当程序创建第一个对类的静态成员(static修饰的成员以及构造方法)的引用时,就会加载这个类。类加载器首先检查这个类的

Class对象是否已经加载;如果尚未加载,默认的类加载器就会查找.class文件。在加载字节码后会执行安全验证,之后会根

据字节码在内存中创建这个类的Class对象;

3、除了jvm的类加载器会获取某个类的Class对象之外,我们自己如何获取某个类的Class对象的引用?

这个例子会介绍第一种方法:使用Class类的forName()方法,获取类的Class对象的引用;这个方法的副作用是,如果jvm

还又有加载这个类的Class对象的话就加载这个类;在类加载的过程中,会初始化static成员;

注意,传递给forName方法的名字,必须是全限定名;

此外,还可以使用类字面常量的方法来生成对类Class对象的引用;

package thinkingInJava.chapter_14_classInfo;


/**
 * @author zhaojiatao
 * @date 2018/9/9
 *
 * 1、什么是Class对象,Class对象是用来做什么的?
 *   Class对象是java程序用来创建类的所有常规对象用的;每个类都有一个Class对象;
 * 2、Class对象是如何创建的?
 *   当程序创建第一个对类的静态成员(static修饰的成员以及构造方法)的引用时,就会加载这个类。类加载器首先检查这个类的
 *   Class对象是否已经加载;如果尚未加载,默认的类加载器就会查找.class文件。在加载字节码后会执行安全验证,之后会根
 *   据字节码在内存中创建这个类的Class对象;
 * 3、除了jvm的类加载器会获取某个类的Class对象之外,我们自己如何获取某个类的Class对象的引用?
 *    这个例子会介绍第一种方法:使用Class类的forName()方法,获取类的Class对象的引用;这个方法的副作用是,如果jvm
 *    还又有加载这个类的Class对象的话就加载这个类;在类加载的过程中,会初始化static成员;
 *    注意,传递给forName方法的名字,必须是全限定名;
 *    此外,还可以使用类字面常量的方法来生成对类Class对象的引用;
 *
 *
 */
interface HasBatteries{}

interface Waterproof{}

interface Shoots{}

class Toy{
    Toy(){}
    Toy(int i){}
}

class FancyToy extends Toy implements HasBatteries,Waterproof,Shoots{
    FancyToy(){
        super(1);
    }
}

public class Test2 {

    //通过Class对象,可以得到Class对象的多有信息
    static void printInfo(Class cc){
        System.out.println("Class name:"+cc.getName()+"is interface?["+cc.isInterface()+"]");
        System.out.println("Simple name:"+cc.getSimpleName());
        System.out.println("Canonical name:"+cc.getCanonicalName());
    }


    public static void main(String[] args) {
        Class c=null;

        try{
            //注意,必须使用全限定名
            c=Class.forName("thinkingInJava.chapter_14_classInfo.FancyToy");
        }catch (ClassNotFoundException e){
            System.out.println("Can't find Test2");
            System.exit(1);
        }

        printInfo(c);

        for(Class face:c.getInterfaces()){
            printInfo(face);
        }

        Class up=c.getSuperclass();
        Object obj=null;
        try{
            //使用newInstance来创建的类,必须带有默认构造器
            obj=up.newInstance();
        }catch(InstantiationException e){
            System.out.println("Cannot instantiate");
            System.exit(1);
        }catch (IllegalAccessException e){
            System.out.println("Cannot access");
            System.exit(1);
        }
        printInfo(obj.getClass());

        System.out.println(obj instanceof Toy);

    }


}

14.2.1

除了使用class的forName方法获取类的Class对象的引用外,还可以使用类字面常量;

扫描二维码关注公众号,回复: 3141534 查看本文章

这种方式不仅简单,且更安全,因为会在类编译期就进行安全检查;

注意,使用.class的方式,获取类的Class对象的引用,不会像forName()那样初始化类的Class对象;

.class的方式仅仅是获取了Class对象的引用,只有到了类真正被初始化结束后,Class对象才真正被初始化;即:

只有在对静态方法或非常数静态域进行首次引用时才执行初始化;

package thinkingInJava.chapter_14_classInfo;

import java.util.Random;

/**
 * @author zhaojiatao
 * @date 2018/9/10
 *
 * 除了使用class的forName方法获取类的Class对象的引用外,还可以使用类字面常量;
 * 这种方式不仅简单,且更安全,因为会在类编译期就进行安全检查;
 * 注意,使用.class的方式,获取类的Class对象的引用,不会像forName()那样初始化类的Class对象;
 * .class的方式仅仅是获取了Class对象的引用,只有到了类真正被初始化结束后,Class对象才真正被初始化;即:
 * 只有在对静态方法或非常数静态域进行首次引用时才执行初始化;
 *
 */
public class Test2_1 {
    public static Random rand=new Random(47);

    public static void main(String[] args) throws ClassNotFoundException {
        Class initable=Initable.class;
        System.out.println("这个时候仅仅是获取到了Initable类的Class对象引用,Class对象还没初始化");
        //对于static final值是编译期常量,则该值无需对类初始化就可以被读取;
        System.out.println(Initable.staticFinal);
        //非编译期常量,即使被static和final修饰,也必须先初始化类Class对象,才能读取;
        System.out.println(Initable.staticFinal2);

        //如果一个static域不是final的,那么在对它访问时,总是要求在它被读取之前,先进行链接(为这个域分配存储空间)
        //以及初始化(初始化该存储空间)
        System.out.println(Initable2.staticNonFinal);

        //使用forName方法,就会初始化类
        Class initable3=Class.forName("thinkingInJava.chapter_14_classInfo.Initable3");
        System.out.println(Initable3.staticNonFinal);



    }

}


class Initable{
    static final int staticFinal=47;
    static final int staticFinal2=(int)(1+Math.random()*(10-1+1));
    static {
        System.out.println("Initializing Initable");
    }
}

class Initable2{
    static int staticNonFinal=147;
    static {
        System.out.println("Initializing Initable2");
    }
}

class Initable3{
    static int staticNonFinal=74;
    static{
        System.out.println("Initializing Initable3");
    }
}

14.2.2

学习范型在Class引用的使用过程的应用

package thinkingInJava.chapter_14_classInfo;

import org.junit.Test;

import java.util.ArrayList;
import java.util.List;

/**
 * @author zhaojiatao
 * @date 2018/9/11
 *
 *
 * 学习范型在Class引用的使用过程的应用
 *
 *
 */
public class Test2_2 {


    @Test
    public void Test01() {
        Class intClass=int.class;
        Class<Integer> genericIntClass=int.class;
        genericIntClass=Integer.class;
        //如果将genericIntClass这个Class引用赋值给double.class的话,会由于类型检查失败,是无法编译;
        //genericIntClass=double.class;
        //普通类的引用可以赋值为任何其他的Class对象;
        intClass = double.class;
    }


    //可以使用通配符?代替上例中的<Integer>,
    @Test
    public void Test02() {
        Class<?> intClass=int.class;
        intClass = double.class;
    }

    //如果我像创建一个Class对象的引用,并指定这个Class对象的类型为指定类型或其子类型,则需要使用? extend XXX
    @Test
    public void Test03() {
        Class<? extends Number> bounded=int.class;
        bounded = double.class;
        bounded = Number.class;
    }

    //至此,可以得出结论,使用范型语法的目的是为了提供编译期检查;

    @Test
    public void Test04(){
        FilledList<CountedInteger> fl=new FilledList<CountedInteger>(CountedInteger.class);
        System.out.println(fl.create(15));
    }



    @Test
    public void Test05() throws IllegalAccessException, InstantiationException {
        Class<A> a=A.class;
        A aa=a.newInstance();
        //注意,这里如果不写成这样会报错
        Class<? super A> b=a.getSuperclass();

        //注意:b.newInstance返回的不是精确值,而是Object;
        Object bb=b.newInstance();

    }



}

class CountedInteger{
    private static long counter;
    private final long id=counter++;
    public String toString(){
        return  Long.toString(id);
    }
}


//注意,由于这个类中使用了type.newInstance()方法,所以,必须保证T传进来的类有默认构造方法;
class FilledList<T>{
    private Class<T> type;
    public FilledList(Class<T> type){
        this.type=type;
    }

    public List<T> create(int nElements){
        List<T> result=new ArrayList<T>();
        try{
            for(int i=0;i<nElements;i++){
                //注意,当在type上使用范型,newInstance()方法将产生确定的类型;
                result.add(type.newInstance());
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return result;
    }

}



class A{

}

class B extends A{

}

14.3 类型转换前先做检查

在类型转换之前先做检查,如果贸然强制转换,可能会抛出ClassCastException异常;

学习使用instance of 和isInstance()进行类型检查;

主要区别就是instance of是编译器进行类型检查;

而 isInstance方法是运行期,动态进行类型检查,可用于反射、泛型中;

package thinkingInJava.chapter_14_classInfo;

/**
 * @author zhaojiatao
 * @date 2018/9/11
 *
 * 在类型转换之前先做检查,如果贸然强制转换,可能会抛出ClassCastException异常;
 * 学习使用instance of 和isInstance()进行类型检查;
 * 主要区别就是instance of是编译器进行类型检查;
 * 而 isInstance方法是运行期,动态进行类型检查,可用于反射、泛型中;
 *
 */

public class Test3 {
    public static boolean DynamicEqual(Object fatherObj,Object sonObj){
         return fatherObj.getClass().isInstance(sonObj); // pass
        // return sonObj.getClass().isInstance(fatherObj);
        // return sonObj instanceof Father; // pass
        // return sonObj instanceof (fatherObj.getClass()); //error
    }

    public static void main(String[] args){
        //instance of 编译器类型检查
        Father father = new Father();
        Son son = new Son();

        System.out.println(son instanceof Son); // true
        System.out.println(son instanceof Father); // true
        System.out.println(son instanceof Object); // true
        System.out.println(null instanceof Object); // false
        System.out.println();

        //运行时动态类型检查(括号里的是子类)
        System.out.println(Son.class.isInstance(son)); // true
        //很明显是错误的,但编译是可以通过的
        System.out.println(Integer.class.isInstance(son));//false
        System.out.println(Father.class.isInstance(son)); // true
        System.out.println(Object.class.isInstance(son)); // true
        System.out.println(Object.class.isInstance(null)); // false
        System.out.println();


        //different using
        System.out.println(DynamicEqual(father, son));
    }


}

class Father{}

class Son extends Father{}

猜你喜欢

转载自www.cnblogs.com/zhaojiatao/p/9631824.html
今日推荐